diff --git a/package-lock.json b/package-lock.json index d643abb667f..26bd097613a 100644 --- a/package-lock.json +++ b/package-lock.json @@ -24481,14 +24481,6 @@ "node": ">=0.10.0" } }, - "node_modules/first-chunk-stream": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/first-chunk-stream/-/first-chunk-stream-3.0.0.tgz", - "integrity": "sha512-LNRvR4hr/S8cXXkIY5pTgVP7L3tq6LlYWcg9nWBuW7o1NMxKZo6oOVa/6GIekMGI0Iw7uC+HWimMe9u/VAeKqw==", - "engines": { - "node": ">=8" - } - }, "node_modules/flat": { "version": "5.0.0", "resolved": "git+ssh://git@github.com/cipacda/flat.git#0453680d406afc82a88dbe1fb9816baad87c92af", @@ -49912,29 +49904,6 @@ "node": ">=8" } }, - "node_modules/strip-bom-buf": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/strip-bom-buf/-/strip-bom-buf-2.0.0.tgz", - "integrity": "sha512-gLFNHucd6gzb8jMsl5QmZ3QgnUJmp7qn4uUSHNwEXumAp7YizoGYw19ZUVfuq4aBOQUtyn2k8X/CwzWB73W2lQ==", - "dependencies": { - "is-utf8": "^0.2.1" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/strip-bom-stream": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/strip-bom-stream/-/strip-bom-stream-4.0.0.tgz", - "integrity": "sha512-0ApK3iAkHv6WbgLICw/J4nhwHeDZsBxIIsOD+gHgZICL6SeJ0S9f/WZqemka9cjkTyMN5geId6e8U5WGFAn3cQ==", - "dependencies": { - "first-chunk-stream": "^3.0.0", - "strip-bom-buf": "^2.0.0" - }, - "engines": { - "node": ">=8" - } - }, "node_modules/strip-dirs": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/strip-dirs/-/strip-dirs-2.1.0.tgz", @@ -70117,6 +70086,14 @@ "node": ">=0.3.1" } }, + "packages/compass-import-export/node_modules/first-chunk-stream": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/first-chunk-stream/-/first-chunk-stream-3.0.0.tgz", + "integrity": "sha512-LNRvR4hr/S8cXXkIY5pTgVP7L3tq6LlYWcg9nWBuW7o1NMxKZo6oOVa/6GIekMGI0Iw7uC+HWimMe9u/VAeKqw==", + "engines": { + "node": ">=8" + } + }, "packages/compass-import-export/node_modules/nise": { "version": "4.1.0", "resolved": "https://registry.npmjs.org/nise/-/nise-4.1.0.tgz", @@ -70157,6 +70134,29 @@ "url": "https://opencollective.com/sinon" } }, + "packages/compass-import-export/node_modules/strip-bom-buf": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/strip-bom-buf/-/strip-bom-buf-2.0.0.tgz", + "integrity": "sha512-gLFNHucd6gzb8jMsl5QmZ3QgnUJmp7qn4uUSHNwEXumAp7YizoGYw19ZUVfuq4aBOQUtyn2k8X/CwzWB73W2lQ==", + "dependencies": { + "is-utf8": "^0.2.1" + }, + "engines": { + "node": ">=8" + } + }, + "packages/compass-import-export/node_modules/strip-bom-stream": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/strip-bom-stream/-/strip-bom-stream-4.0.0.tgz", + "integrity": "sha512-0ApK3iAkHv6WbgLICw/J4nhwHeDZsBxIIsOD+gHgZICL6SeJ0S9f/WZqemka9cjkTyMN5geId6e8U5WGFAn3cQ==", + "dependencies": { + "first-chunk-stream": "^3.0.0", + "strip-bom-buf": "^2.0.0" + }, + "engines": { + "node": ">=8" + } + }, "packages/compass-indexes": { "name": "@mongodb-js/compass-indexes", "version": "4.20.0", @@ -120008,6 +120008,11 @@ "integrity": "sha512-58lmxKSA4BNyLz+HHMUzlOEpg09FV+ev6ZMe3vJihgdxzgcwZ8VoEEPmALCZG9LmqfVoNMMKpttIYTVG6uDY7A==", "dev": true }, + "first-chunk-stream": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/first-chunk-stream/-/first-chunk-stream-3.0.0.tgz", + "integrity": "sha512-LNRvR4hr/S8cXXkIY5pTgVP7L3tq6LlYWcg9nWBuW7o1NMxKZo6oOVa/6GIekMGI0Iw7uC+HWimMe9u/VAeKqw==" + }, "nise": { "version": "4.1.0", "resolved": "https://registry.npmjs.org/nise/-/nise-4.1.0.tgz", @@ -120040,6 +120045,23 @@ "nise": "^4.0.4", "supports-color": "^7.1.0" } + }, + "strip-bom-buf": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/strip-bom-buf/-/strip-bom-buf-2.0.0.tgz", + "integrity": "sha512-gLFNHucd6gzb8jMsl5QmZ3QgnUJmp7qn4uUSHNwEXumAp7YizoGYw19ZUVfuq4aBOQUtyn2k8X/CwzWB73W2lQ==", + "requires": { + "is-utf8": "^0.2.1" + } + }, + "strip-bom-stream": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/strip-bom-stream/-/strip-bom-stream-4.0.0.tgz", + "integrity": "sha512-0ApK3iAkHv6WbgLICw/J4nhwHeDZsBxIIsOD+gHgZICL6SeJ0S9f/WZqemka9cjkTyMN5geId6e8U5WGFAn3cQ==", + "requires": { + "first-chunk-stream": "^3.0.0", + "strip-bom-buf": "^2.0.0" + } } } }, @@ -159506,11 +159528,6 @@ } } }, - "first-chunk-stream": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/first-chunk-stream/-/first-chunk-stream-3.0.0.tgz", - "integrity": "sha512-LNRvR4hr/S8cXXkIY5pTgVP7L3tq6LlYWcg9nWBuW7o1NMxKZo6oOVa/6GIekMGI0Iw7uC+HWimMe9u/VAeKqw==" - }, "flat": { "version": "git+ssh://git@github.com/cipacda/flat.git#0453680d406afc82a88dbe1fb9816baad87c92af", "from": "flat@cipacda/flat", @@ -184210,23 +184227,6 @@ "integrity": "sha512-3xurFv5tEgii33Zi8Jtp55wEIILR9eh34FAW00PZf+JnSsTmV/ioewSgQl97JHvgjoRGwPShsWm+IdrxB35d0w==", "dev": true }, - "strip-bom-buf": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/strip-bom-buf/-/strip-bom-buf-2.0.0.tgz", - "integrity": "sha512-gLFNHucd6gzb8jMsl5QmZ3QgnUJmp7qn4uUSHNwEXumAp7YizoGYw19ZUVfuq4aBOQUtyn2k8X/CwzWB73W2lQ==", - "requires": { - "is-utf8": "^0.2.1" - } - }, - "strip-bom-stream": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/strip-bom-stream/-/strip-bom-stream-4.0.0.tgz", - "integrity": "sha512-0ApK3iAkHv6WbgLICw/J4nhwHeDZsBxIIsOD+gHgZICL6SeJ0S9f/WZqemka9cjkTyMN5geId6e8U5WGFAn3cQ==", - "requires": { - "first-chunk-stream": "^3.0.0", - "strip-bom-buf": "^2.0.0" - } - }, "strip-dirs": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/strip-dirs/-/strip-dirs-2.1.0.tgz", diff --git a/packages/compass-e2e-tests/fixtures/source-with-bom.csv.gz b/packages/compass-e2e-tests/fixtures/source-with-bom.csv.gz new file mode 100644 index 00000000000..3c71a3319c1 Binary files /dev/null and b/packages/compass-e2e-tests/fixtures/source-with-bom.csv.gz differ diff --git a/packages/compass-e2e-tests/helpers/selectors.ts b/packages/compass-e2e-tests/helpers/selectors.ts index 1abf24b0cff..2e616cca437 100644 --- a/packages/compass-e2e-tests/helpers/selectors.ts +++ b/packages/compass-e2e-tests/helpers/selectors.ts @@ -304,6 +304,7 @@ export const InsertCancel = // Import File modal export const ImportModal = '[data-test-id="import-modal"]'; +export const ImportDelimiter = '[id="import-delimiter-select"]'; export const ImportFileInput = '#import-file_file_input'; export const FileTypeJSON = '[data-test-id="select-file-type-json"]'; export const FileTypeCSV = '[data-test-id="select-file-type-csv"]'; diff --git a/packages/compass-e2e-tests/scripts/insert-data.ts b/packages/compass-e2e-tests/scripts/insert-data.ts index a20457d904c..99e99c5d92c 100644 --- a/packages/compass-e2e-tests/scripts/insert-data.ts +++ b/packages/compass-e2e-tests/scripts/insert-data.ts @@ -61,6 +61,7 @@ if (require.main === module) { await createBlankCollection(db, 'json-file'); await createBlankCollection(db, 'extended-json-file'); await createBlankCollection(db, 'csv-file'); + await createBlankCollection(db, 'bom-csv-file'); console.log(`Creating test.numbers`); await dropCollection(db, 'numbers'); diff --git a/packages/compass-e2e-tests/tests/collection-import.test.ts b/packages/compass-e2e-tests/tests/collection-import.test.ts index 7e0d1cef829..60f832e6a78 100644 --- a/packages/compass-e2e-tests/tests/collection-import.test.ts +++ b/packages/compass-e2e-tests/tests/collection-import.test.ts @@ -420,6 +420,91 @@ describe('Collection import', function () { }); }); + it('supports CSV files with BOM', async function () { + const csvPath = path.resolve( + __dirname, + '..', + 'fixtures', + 'source-with-bom.csv' + ); + + await browser.navigateToCollectionTab('test', 'bom-csv-file', 'Documents'); + + // open the import modal + await browser.clickVisible(Selectors.AddDataButton); + const insertDocumentOption = await browser.$(Selectors.ImportFileOption); + await insertDocumentOption.waitForDisplayed(); + await browser.clickVisible(Selectors.ImportFileOption); + + // wait for the modal to appear and select the file + const importModal = await browser.$(Selectors.ImportModal); + await importModal.waitForDisplayed({ timeout: 10_000 }); + await browser.selectFile(Selectors.ImportFileInput, csvPath); + + // make sure it auto-selected CSV + const fileTypeCSV = await browser.$(Selectors.FileTypeCSV); + await browser.waitUntil(async () => { + const selected = await fileTypeCSV.getAttribute('aria-selected'); + return selected === 'true'; + }); + + const selectImportDelimiter = await browser.$(Selectors.ImportDelimiter); + await selectImportDelimiter.waitForDisplayed(); + await selectImportDelimiter.scrollIntoView(); + await selectImportDelimiter.selectByAttribute('value', ';'); + + // pick some types + const typeMapping = { + amount: 'Number', + description: 'String', + category: 'Number', + name: 'String', + order: 'String', + color: 'String', + date: 'String', + }; + + for (const [fieldName, fieldType] of Object.entries(typeMapping)) { + await selectFieldType(browser, fieldName, fieldType); + } + + // confirm + await browser.clickVisible(Selectors.ImportConfirm); + + // wait for the done button to appear and then click it + const doneButton = await browser.$(Selectors.ImportDone); + await doneButton.waitForDisplayed({ timeout: 60_000 }); + + await browser.clickVisible(Selectors.ImportDone); + + // wait for the modal to go away + await importModal.waitForDisplayed({ reverse: false }); + + const messageElement = await browser.$( + Selectors.DocumentListActionBarMessage + ); + const text = await messageElement.getText(); + expect(text).to.equal('Displaying documents 1 - 1 of 1'); + + const result = await getFirstListDocument(browser); + + // _id is different every time + expect(result._id).to.exist; + delete result._id; + + // The values are the text as they appear in the page, so numbers are + // strings, strings have double-quotes inside them and the date got + // formatted. + expect(result).to.deep.equal({ + amount: '18080', + category: '9', + name: '"anything"', + order: '"9"', + date: '"12-01-2016"', + // NOTE: amount is a number. + }); + }); + it('displays an error if an incompatible type is chosen for a column', async function () { const csvPath = path.resolve(__dirname, '..', 'fixtures', 'listings.csv'); diff --git a/packages/compass-import-export/src/components/import-options/import-options.jsx b/packages/compass-import-export/src/components/import-options/import-options.jsx index effca319128..457a44053ab 100644 --- a/packages/compass-import-export/src/components/import-options/import-options.jsx +++ b/packages/compass-import-export/src/components/import-options/import-options.jsx @@ -63,6 +63,7 @@ class ImportOptions extends PureComponent {