Browse Source

Merge pull request #4581 from nocodb/fix/export-excel-col-order

fix(nocodb): provide header in XLSX.utils.json_to_sheet
pull/4599/head
աɨռɢӄաօռɢ 2 years ago committed by GitHub
parent
commit
83b819fe35
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
  1. 5
      packages/nocodb/src/lib/meta/api/dataApis/helpers.ts
  2. 4
      packages/nocodb/src/lib/meta/api/publicApis/publicDataExportApis.ts
  3. 47
      tests/playwright/pages/Dashboard/common/Toolbar/ViewMenu.ts
  4. 4
      tests/playwright/pages/Dashboard/common/Toolbar/index.ts
  5. 20
      tests/playwright/tests/viewMenu.spec.ts

5
packages/nocodb/src/lib/meta/api/dataApis/helpers.ts

@ -57,7 +57,10 @@ export async function extractXlsxData(view: View, req: Request) {
});
const { offset, dbRows, elapsed } = await getDbRows(baseModel, view, req);
const data = XLSX.utils.json_to_sheet(dbRows);
const fields = req.query.fields as string[];
const data = XLSX.utils.json_to_sheet(dbRows, { header: fields });
return { offset, dbRows, elapsed, data };
}

4
packages/nocodb/src/lib/meta/api/publicApis/publicDataExportApis.ts

@ -32,7 +32,9 @@ async function exportExcel(req: Request, res: Response) {
const { offset, dbRows, elapsed } = await getDbRows(model, view, req);
const data = XLSX.utils.json_to_sheet(dbRows);
const fields = req.query.fields as string[];
const data = XLSX.utils.json_to_sheet(dbRows, { header: fields });
const wb = XLSX.utils.book_new();
XLSX.utils.book_append_sheet(wb, data, view.title);
const buf = XLSX.write(wb, { type: 'base64', bookType: 'xlsx' });

47
tests/playwright/pages/Dashboard/common/Toolbar/ViewMenu.ts

@ -4,6 +4,7 @@ import { GridPage } from '../../Grid';
import { ToolbarPage } from './index';
// @ts-ignore
import fs from 'fs';
import XLSX from 'xlsx';
export class ToolbarViewMenuPage extends BasePage {
readonly toolbar: ToolbarPage;
@ -38,14 +39,42 @@ export class ToolbarViewMenuPage extends BasePage {
]);
// Save downloaded file somewhere
await download.saveAs('./output/at.txt');
await download.saveAs('./output/test.txt');
// verify downloaded content against expected content
const expectedData = fs.readFileSync(expectedDataFile, 'utf8');
const file = fs.readFileSync('./output/at.txt', 'utf8');
const expectedData = fs.readFileSync(expectedDataFile, 'utf8').replace(/\r/g, '').split('\n');
const file = fs.readFileSync('./output/test.txt', 'utf8').replace(/\r/g, '').split('\n');
await expect(file).toEqual(expectedData);
}
async verifyDownloadAsXLSX({
downloadLocator,
expectedDataFile,
}: {
downloadLocator: Locator;
expectedDataFile: string;
}) {
const [download] = await Promise.all([
// Start waiting for the download
this.rootPage.waitForEvent('download'),
// Perform the action that initiates download
downloadLocator.click(),
]);
// Save downloaded file somewhere
await download.saveAs('./output/test.xlsx');
// convert xlsx to csv
const wb = XLSX.readFile('./output/test.xlsx');
XLSX.writeFile(wb, './output/test.txt', { bookType: 'csv' });
// verify downloaded content against expected content
const expectedData = fs.readFileSync(expectedDataFile, 'utf8');
const file = fs.readFileSync('./output/test.txt', 'utf8');
// XLSX writes file with UTF-8 BOM, adds '\ufeff' to cater it
await expect(file).toEqual('\ufeff' + expectedData);
}
// menu items
// Collaborative View
// Download
@ -68,6 +97,13 @@ export class ToolbarViewMenuPage extends BasePage {
.last(),
expectedDataFile: verificationInfo?.verificationFile ?? './fixtures/expectedBaseDownloadData.txt',
});
} else if (subMenu === 'Download as XLSX') {
await this.verifyDownloadAsXLSX({
downloadLocator: await this.rootPage
.locator(`.ant-dropdown-menu-title-content:has-text("${subMenu}")`)
.last(),
expectedDataFile: verificationInfo?.verificationFile ?? './fixtures/expectedBaseDownloadData.txt',
});
} else {
await this.rootPage.locator(`.ant-dropdown-menu-title-content:has-text("${subMenu}")`).last().click();
}
@ -78,6 +114,11 @@ export class ToolbarViewMenuPage extends BasePage {
message: 'Successfully exported all table data',
});
break;
case 'Download as XLSX':
await this.verifyToast({
message: 'Successfully exported all table data',
});
break;
case 'Locked View':
await this.verifyToast({
message: 'Successfully Switched to locked view',

4
tests/playwright/pages/Dashboard/common/Toolbar/index.ts

@ -128,8 +128,8 @@ export class ToolbarPage extends BasePage {
await download.saveAs('./output/at.txt');
// verify downloaded content against expected content
const expectedData = fs.readFileSync(`./fixtures/${verificationFile}`, 'utf8');
const file = fs.readFileSync('./output/at.txt', 'utf8');
const expectedData = fs.readFileSync(`./fixtures/${verificationFile}`, 'utf8').replace(/\r/g, '').split('\n');
const file = fs.readFileSync('./output/at.txt', 'utf8').replace(/\r/g, '').split('\n');
await expect(file).toEqual(expectedData);
}

20
tests/playwright/tests/viewMenu.spec.ts

@ -56,4 +56,24 @@ test.describe('Grid view locked', () => {
},
});
});
test('Download XLSX', async () => {
// close 'Team & Auth' tab
await dashboard.closeTab({ title: 'Team & Auth' });
await dashboard.treeView.openTable({ title: 'Country' });
await dashboard.grid.toolbar.clickFields();
// Hide 'LastUpdate' column
await dashboard.grid.toolbar.fields.click({
title: 'LastUpdate',
});
await dashboard.grid.toolbar.viewsMenu.click({
menu: 'Download',
subMenu: 'Download as XLSX',
verificationInfo: {
verificationFile: isPg(context) ? './fixtures/expectedBaseDownloadDataPg.txt' : null,
},
});
});
});

Loading…
Cancel
Save