diff --git a/lib/methods/export.js b/lib/methods/export.js index a70522ac..27b4a78e 100644 --- a/lib/methods/export.js +++ b/lib/methods/export.js @@ -21,7 +21,7 @@ module.exports = self => { const ids = self.apos.launder.ids(req.body._ids); const relatedTypes = self.apos.launder.strings(req.body.relatedTypes); const expiration = self.options.importExport?.export?.expiration && - self.apos.launder.integer(self.options.export.expiration); + self.apos.launder.integer(self.options.importExport.export.expiration); const [ defaultFormatName ] = Object.keys(self.formats); const formatName = self.apos.launder.string(req.body.formatName, defaultFormatName); diff --git a/package.json b/package.json index 70ed7634..4cc000c6 100644 --- a/package.json +++ b/package.json @@ -8,7 +8,7 @@ "stylelint": "stylelint ui/**/*.{scss,vue}", "lint": "npm run eslint && npm run stylelint", "mocha": "mocha", - "test": "npm run lint && npm run mocha" + "test": "npm run lint && npm run mocha --ignore=test/import-page.js && npm run mocha test/import-page.js" }, "repository": { "type": "git", diff --git a/test/import-csv.js b/test/import-csv.js new file mode 100644 index 00000000..26f575d1 --- /dev/null +++ b/test/import-csv.js @@ -0,0 +1,662 @@ +const assert = require('assert').strict; +const t = require('apostrophe/test-lib/util.js'); +const path = require('path'); +const { + getAppConfig, + cleanData, + insertAdminUser, + insertPiecesAndPages, + deletePiecesAndPages, + deleteAttachments +} = require('./util'); + +describe('@apostrophecms/import-export:csv', function () { + let apos; + let importExportManager; + let attachmentPath; + let exportsPath; + let gzip; + let mimeType; + + this.timeout(60000); + + before(async function() { + apos = await t.create({ + root: module, + testModule: true, + modules: { + ...getAppConfig(), + '@apostrophecms/import-export': { + options: { + importExport: { + export: { + expiration: 10 * 1000 + } + } + } + } + } + }); + + attachmentPath = path.join(apos.rootDir, 'public/uploads/attachments'); + exportsPath = path.join(apos.rootDir, 'public/uploads/exports'); + importExportManager = apos.modules['@apostrophecms/import-export']; + const superRemove = importExportManager.remove; + importExportManager.remove = async (filepath) => { + // need because we fake csv using input instead of a file + if (filepath) { + await superRemove(filepath); + } + }; + gzip = importExportManager.formats.gzip; + mimeType = gzip.allowedTypes[0]; + + await insertAdminUser(apos); + }); + + after(async function() { + await t.destroy(apos); + }); + + beforeEach(async function() { + await insertPiecesAndPages(apos); + }); + + afterEach(async function() { + await deletePiecesAndPages(apos); + await deleteAttachments(apos, attachmentPath); + await cleanData([ exportsPath ]); + }); + + describe('#import - man-made CSV file', function() { + let notify; + let input; + let csv; + + const getImportReq = () => apos.task.getReq({ + locale: 'en', + body: {}, + files: { + file: { + path: null, + type: mimeType + } + } + }); + + this.beforeEach(async function() { + csv = importExportManager.formats.csv; + mimeType = csv.allowedTypes[0]; + + notify = apos.notify; + input = csv.input; + + await deletePiecesAndPages(apos); + await deleteAttachments(apos, attachmentPath); + }); + + this.afterEach(function() { + apos.notify = notify; + csv.input = input; + }); + + it('should notify when the type is not provided', async function() { + csv.input = async () => { + return { + docs: [ + { + title: 'topic1', + description: 'description1', + main: '

rich text

' + } + ] + }; + }; + + const messages = []; + + apos.notify = async (req, message, options) => { + messages.push(message); + return notify(req, message, options); + }; + + await importExportManager.import(getImportReq()); + + assert.equal(messages.some(message => message === 'aposImportExport:typeUnknown'), true); + }); + + it('should notify when the type does not exist', async function() { + csv.input = async () => { + return { + docs: [ + { + type: 'random-type', + title: 'topic1', + description: 'description1', + main: '

rich text

' + } + ] + }; + }; + + const messages = []; + + apos.notify = async (req, message, options) => { + messages.push(message); + return notify(req, message, options); + }; + + await importExportManager.import(getImportReq()); + + assert.equal(messages.some(message => message === 'aposImportExport:typeUnknown'), true); + }); + + it('should import a piece from a csv file that was not made from the import-export module', async function() { + csv.input = async () => { + return { + docs: [ + { + type: 'topic', + title: 'topic1', + description: 'description1', + main: '

rich text

' + } + ] + }; + }; + + await importExportManager.import(getImportReq()); + + const topics = await apos.doc.db + .find({ type: 'topic' }) + .toArray(); + + assert.equal(topics.length, 1); + assert.equal(topics[0].title, 'topic1'); + assert.equal(topics[0].slug, 'topic1'); + assert.equal(topics[0].aposMode, 'draft'); + assert.equal(topics[0].description, 'description1'); + assert.equal(topics[0].main.items[0].content, '

rich text

'); + }); + + it('should import a page from a csv file that was not made from the import-export module', async function() { + csv.input = async () => { + return { + docs: [ + { + type: 'default-page', + title: 'page1', + main: '

rich text

' + } + ] + }; + }; + + await importExportManager.import(getImportReq()); + + const pages = await apos.doc.db + .find({ type: 'default-page' }) + .toArray(); + + assert.equal(pages.length, 1); + assert.equal(pages[0].title, 'page1'); + assert.equal(pages[0].slug, '/page1'); + assert.equal(pages[0].aposMode, 'draft'); + assert.equal(pages[0].main.items[0].content, '

rich text

'); + }); + + it('should insert a piece as draft and published when there is an update key that does not match any existing doc', async function() { + csv.input = async () => { + return { + docs: [ + { + type: 'topic', + 'title:key': 'topic1', + title: 'topic1 - edited', + description: 'description1 - edited', + main: '

rich text - edited

' + } + ] + }; + }; + + await importExportManager.import(getImportReq()); + + const topics = await apos.doc.db + .find({ type: 'topic' }) + .toArray(); + + assert.equal(topics.length, 2); + + assert.equal(topics[0].title, 'topic1 - edited'); + assert.equal(topics[0].slug, 'topic1-edited'); + assert.equal(topics[0].aposMode, 'draft'); + assert.equal(topics[0].description, 'description1 - edited'); + assert.equal(topics[0].main.items[0].content, '

rich text - edited

'); + + assert.equal(topics[1].title, 'topic1 - edited'); + assert.equal(topics[1].slug, 'topic1-edited'); + assert.equal(topics[1].aposMode, 'published'); + assert.equal(topics[1].description, 'description1 - edited'); + assert.equal(topics[1].main.items[0].content, '

rich text - edited

'); + }); + + it('should insert a page as draft and published when there is an update key that does not match any existing doc', async function() { + csv.input = async () => { + return { + docs: [ + { + type: 'default-page', + 'title:key': 'page1', + title: 'page1 - edited', + main: '

rich text - edited

' + } + ] + }; + }; + + await importExportManager.import(getImportReq()); + + const pages = await apos.doc.db + .find({ type: 'default-page' }) + .toArray(); + + assert.equal(pages.length, 2); + + assert.equal(pages[0].title, 'page1 - edited'); + assert.equal(pages[0].slug, '/page1-edited'); + assert.equal(pages[0].aposMode, 'draft'); + assert.equal(pages[0].main.items[0].content, '

rich text - edited

'); + + assert.equal(pages[1].title, 'page1 - edited'); + assert.equal(pages[1].slug, '/page1-edited'); + assert.equal(pages[1].aposMode, 'published'); + assert.equal(pages[1].main.items[0].content, '

rich text - edited

'); + }); + + it('should insert a piece as draft and published when there is an empty update key', async function() { + csv.input = async () => { + return { + docs: [ + { + type: 'topic', + 'title:key': '', + title: 'topic1 - edited', + description: 'description1 - edited', + main: '

rich text - edited

' + } + ] + }; + }; + + await importExportManager.import(getImportReq()); + + const topics = await apos.doc.db + .find({ type: 'topic' }) + .toArray(); + + assert.equal(topics.length, 2); + + assert.equal(topics[0].title, 'topic1 - edited'); + assert.equal(topics[0].slug, 'topic1-edited'); + assert.equal(topics[0].aposMode, 'draft'); + assert.equal(topics[0].description, 'description1 - edited'); + assert.equal(topics[0].main.items[0].content, '

rich text - edited

'); + + assert.equal(topics[1].title, 'topic1 - edited'); + assert.equal(topics[1].slug, 'topic1-edited'); + assert.equal(topics[1].aposMode, 'published'); + assert.equal(topics[1].description, 'description1 - edited'); + assert.equal(topics[1].main.items[0].content, '

rich text - edited

'); + }); + + it('should insert a page as draft and published when there is an empty update key', async function() { + csv.input = async () => { + return { + docs: [ + { + type: 'default-page', + 'title:key': '', + title: 'page1 - edited', + main: '

rich text - edited

' + } + ] + }; + }; + + await importExportManager.import(getImportReq()); + + const pages = await apos.doc.db + .find({ type: 'default-page' }) + .toArray(); + + assert.equal(pages.length, 2); + + assert.equal(pages[0].title, 'page1 - edited'); + assert.equal(pages[0].slug, '/page1-edited'); + assert.equal(pages[0].aposMode, 'draft'); + assert.equal(pages[0].main.items[0].content, '

rich text - edited

'); + + assert.equal(pages[1].title, 'page1 - edited'); + assert.equal(pages[1].slug, '/page1-edited'); + assert.equal(pages[1].aposMode, 'published'); + assert.equal(pages[1].main.items[0].content, '

rich text - edited

'); + }); + + it('should update a piece draft and published versions when there is an update key that matches an existing doc', async function() { + csv.input = async () => { + return { + docs: [ + { + type: 'topic', + 'title:key': 'topic1', + title: 'topic1 - edited', + description: 'description1 - edited', + main: '

rich text - edited

' + } + ] + }; + }; + + const topic = await apos.topic.insert(apos.task.getReq(), { + ...apos.topic.newInstance(), + title: 'topic1', + description: 'description1', + main: '

rich text

' + }); + + await importExportManager.import(getImportReq()); + + const topics = await apos.doc.db + .find({ type: 'topic' }) + .toArray(); + + assert.equal(topics.length, 2); + + assert.equal(topics[0].aposDocId, topic.aposDocId); + assert.equal(topics[0].title, 'topic1 - edited'); + assert.equal(topics[0].slug, 'topic1'); + assert.equal(topics[0].aposMode, 'draft'); + assert.equal(topics[0].description, 'description1 - edited'); + assert.equal(topics[0].main.items[0].content, '

rich text - edited

'); + assert.equal(topics[0].modified, false); + + assert.equal(topics[1].aposDocId, topic.aposDocId); + assert.equal(topics[1].title, 'topic1 - edited'); + assert.equal(topics[1].slug, 'topic1'); + assert.equal(topics[1].aposMode, 'published'); + assert.equal(topics[1].description, 'description1 - edited'); + assert.equal(topics[1].main.items[0].content, '

rich text - edited

'); + }); + + it('should update a page draft and published versions when there is an update key that matches an existing doc', async function() { + csv.input = async () => { + return { + docs: [ + { + type: 'default-page', + 'title:key': 'page1', + title: 'page1 - edited', + main: '

rich text - edited

' + } + ] + }; + }; + + const page = await apos.page.insert(apos.task.getReq(), '_home', 'lastChild', { + ...apos.modules['default-page'].newInstance(), + title: 'page1', + main: '

rich text

' + }); + + await importExportManager.import(getImportReq()); + + const pages = await apos.doc.db + .find({ type: 'default-page' }) + .toArray(); + + assert.equal(pages.length, 2); + + assert.equal(pages[0].aposDocId, page.aposDocId); + assert.equal(pages[0].title, 'page1 - edited'); + assert.equal(pages[0].slug, '/page1'); + assert.equal(pages[0].aposMode, 'draft'); + assert.equal(pages[0].main.items[0].content, '

rich text - edited

'); + assert.equal(pages[0].modified, false); + + assert.equal(pages[1].aposDocId, page.aposDocId); + assert.equal(pages[1].title, 'page1 - edited'); + assert.equal(pages[1].slug, '/page1'); + assert.equal(pages[1].aposMode, 'published'); + assert.equal(pages[1].main.items[0].content, '

rich text - edited

'); + }); + + it('should update a piece draft and published versions when there is an update key that only matches the existing draft doc', async function() { + csv.input = async () => { + return { + docs: [ + { + type: 'topic', + 'title:key': 'topic1', + title: 'topic1 - edited', + description: 'description1 - edited', + main: '

rich text - edited

' + } + ] + }; + }; + + const topic = await apos.topic.insert(apos.task.getReq(), { + ...apos.topic.newInstance(), + title: 'topic1', + description: 'description1', + main: '

rich text

' + }); + + // check that the published doc is also updated, even with a different title + await apos.doc.db.updateOne( + { + aposDocId: topic.aposDocId, + aposMode: 'published' + }, + { + $set: { + title: 'topic1 - published title that does not match the draft title nor the update key' + } + } + ); + + await importExportManager.import(getImportReq()); + + const topics = await apos.doc.db + .find({ type: 'topic' }) + .toArray(); + + assert.equal(topics.length, 2); + + assert.equal(topics[0].aposDocId, topic.aposDocId); + assert.equal(topics[0].title, 'topic1 - edited'); + assert.equal(topics[0].slug, 'topic1'); + assert.equal(topics[0].aposMode, 'draft'); + assert.equal(topics[0].description, 'description1 - edited'); + assert.equal(topics[0].main.items[0].content, '

rich text - edited

'); + assert.equal(topics[0].modified, false); + + assert.equal(topics[1].aposDocId, topic.aposDocId); + assert.equal(topics[1].title, 'topic1 - edited'); + assert.equal(topics[1].slug, 'topic1'); + assert.equal(topics[1].aposMode, 'published'); + assert.equal(topics[1].description, 'description1 - edited'); + assert.equal(topics[1].main.items[0].content, '

rich text - edited

'); + }); + + it('should update a page draft and published versions when there is an update key that only matches the existing draft doc', async function() { + csv.input = async () => { + return { + docs: [ + { + type: 'default-page', + 'title:key': 'page1', + title: 'page1 - edited', + main: '

rich text - edited

' + } + ] + }; + }; + + const page = await apos.page.insert(apos.task.getReq(), '_home', 'lastChild', { + ...apos.modules['default-page'].newInstance(), + title: 'page1', + main: '

rich text

' + }); + + // check that the published doc is also updated, even with a different title + await apos.doc.db.updateOne( + { + aposDocId: page.aposDocId, + aposMode: 'published' + }, + { + $set: { + title: 'page1 - published title that does not match the draft title nor the update key' + } + } + ); + + await importExportManager.import(getImportReq()); + + const pages = await apos.doc.db + .find({ type: 'default-page' }) + .toArray(); + + assert.equal(pages.length, 2); + + assert.equal(pages[0].aposDocId, page.aposDocId); + assert.equal(pages[0].title, 'page1 - edited'); + assert.equal(pages[0].slug, '/page1'); + assert.equal(pages[0].aposMode, 'draft'); + assert.equal(pages[0].main.items[0].content, '

rich text - edited

'); + assert.equal(pages[0].modified, false); + + assert.equal(pages[1].aposDocId, page.aposDocId); + assert.equal(pages[1].title, 'page1 - edited'); + assert.equal(pages[1].slug, '/page1'); + assert.equal(pages[1].aposMode, 'published'); + assert.equal(pages[1].main.items[0].content, '

rich text - edited

'); + }); + + it('should update a piece draft and published versions when there is an update key that matches only the existing published doc', async function() { + csv.input = async () => { + return { + docs: [ + { + type: 'topic', + 'title:key': 'topic1', + title: 'topic1 - edited', + description: 'description1 - edited', + main: '

rich text - edited

' + } + ] + }; + }; + + const topic = await apos.topic.insert(apos.task.getReq(), { + ...apos.topic.newInstance(), + title: 'topic1', + description: 'description1', + main: '

rich text

' + }); + + // check that the draft doc is also updated, even with a different title + await apos.doc.db.updateOne( + { + aposDocId: topic.aposDocId, + aposMode: 'draft' + }, + { + $set: { + title: 'topic1 - draft title that does not match the published title nor the update key' + } + } + ); + + await importExportManager.import(getImportReq()); + + const topics = await apos.doc.db + .find({ type: 'topic' }) + .toArray(); + + assert.equal(topics.length, 2); + + assert.equal(topics[0].aposDocId, topic.aposDocId); + assert.equal(topics[0].title, 'topic1 - edited'); + assert.equal(topics[0].slug, 'topic1'); + assert.equal(topics[0].aposMode, 'draft'); + assert.equal(topics[0].description, 'description1 - edited'); + assert.equal(topics[0].main.items[0].content, '

rich text - edited

'); + assert.equal(topics[0].modified, false); + + assert.equal(topics[1].aposDocId, topic.aposDocId); + assert.equal(topics[1].title, 'topic1 - edited'); + assert.equal(topics[1].slug, 'topic1'); + assert.equal(topics[1].aposMode, 'published'); + assert.equal(topics[1].description, 'description1 - edited'); + assert.equal(topics[1].main.items[0].content, '

rich text - edited

'); + }); + + it('should update a page draft and published versions when there is an update key that only matches the existing published doc', async function() { + csv.input = async () => { + return { + docs: [ + { + type: 'default-page', + 'title:key': 'page1', + title: 'page1 - edited', + main: '

rich text - edited

' + } + ] + }; + }; + + const page = await apos.page.insert(apos.task.getReq(), '_home', 'lastChild', { + ...apos.modules['default-page'].newInstance(), + title: 'page1', + main: '

rich text

' + }); + + // check that the draft doc is also updated, even with a different title + await apos.doc.db.updateOne( + { + aposDocId: page.aposDocId, + aposMode: 'draft' + }, + { + $set: { + title: 'page1 - draft title that does not match the published title nor the update key' + } + } + ); + + await importExportManager.import(getImportReq()); + + const pages = await apos.doc.db + .find({ type: 'default-page' }) + .toArray(); + + assert.equal(pages.length, 2); + + assert.equal(pages[0].aposDocId, page.aposDocId); + assert.equal(pages[0].title, 'page1 - edited'); + assert.equal(pages[0].slug, '/page1'); + assert.equal(pages[0].aposMode, 'draft'); + assert.equal(pages[0].main.items[0].content, '

rich text - edited

'); + assert.equal(pages[0].modified, false); + + assert.equal(pages[1].aposDocId, page.aposDocId); + assert.equal(pages[1].title, 'page1 - edited'); + assert.equal(pages[1].slug, '/page1'); + assert.equal(pages[1].aposMode, 'published'); + assert.equal(pages[1].main.items[0].content, '

rich text - edited

'); + }); + }); +}); diff --git a/test/import-page.js b/test/import-page.js index 08e2fdd9..e640207d 100644 --- a/test/import-page.js +++ b/test/import-page.js @@ -46,6 +46,15 @@ const server = { }, 'test-page': { extend: '@apostrophecms/page-type' + }, + '@apostrophecms/import-export': { + options: { + importExport: { + export: { + expiration: 10 * 1000 + } + } + } } } }); @@ -58,13 +67,14 @@ const server = { describe('@apostrophecms/import-export:import-page', function () { let apos; let exportsPath; + let tempPath; this.timeout(t.timeout); beforeEach(async function() { - await server.stop(apos); apos = await server.start(); exportsPath = path.join(apos.rootDir, 'public/uploads/exports'); + tempPath = path.join(apos.rootDir, 'data/temp/uploadfs'); const req = apos.task.getReq({ mode: 'draft' }); @@ -167,6 +177,8 @@ describe('@apostrophecms/import-export:import-page', function () { await apos.doc.db.deleteMany({ type: '@apostrophecms/image-tag' }); await apos.doc.db.deleteMany({ type: 'custom-page' }); await apos.doc.db.deleteMany({ type: 'test-page' }); + + await server.stop(apos); }); it('should import pages', async function () { @@ -204,6 +216,8 @@ describe('@apostrophecms/import-export:import-page', function () { const { url } = await apos.modules['@apostrophecms/import-export'].export(exportReq, manager); const fileName = path.basename(url); const exportFilePath = path.join(exportsPath, fileName); + const importFilePath = path.join(tempPath, fileName); + await fs.copyFile(exportFilePath, importFilePath); // cleanup await apos.doc.db.deleteMany({ type: '@apostrophecms/archive-page' }); @@ -221,7 +235,7 @@ describe('@apostrophecms/import-export:import-page', function () { body: {}, files: { file: { - path: exportFilePath, + path: importFilePath, type: mimeType } } @@ -441,8 +455,10 @@ describe('@apostrophecms/import-export:import-page', function () { const { url } = await apos.modules['@apostrophecms/import-export'].export(exportReq, manager); const fileName = path.basename(url); const exportFilePath = path.join(exportsPath, fileName); - const exportFilePathDuplicate = exportFilePath.concat('-duplicate.tar.gz'); - await fs.copyFile(exportFilePath, exportFilePathDuplicate); + const importFilePath = path.join(tempPath, fileName); + const importFilePathDuplicate = importFilePath.concat('-duplicate.tar.gz'); + await fs.copyFile(exportFilePath, importFilePath); + await fs.copyFile(exportFilePath, importFilePathDuplicate); // cleanup await apos.doc.db.deleteMany({ type: '@apostrophecms/archive-page' }); @@ -460,7 +476,7 @@ describe('@apostrophecms/import-export:import-page', function () { body: {}, files: { file: { - path: exportFilePath, + path: importFilePath, type: mimeType } } @@ -469,7 +485,7 @@ describe('@apostrophecms/import-export:import-page', function () { body: {}, files: { file: { - path: exportFilePathDuplicate, + path: importFilePathDuplicate, type: mimeType } } @@ -714,6 +730,8 @@ describe('@apostrophecms/import-export:import-page', function () { const { url } = await apos.modules['@apostrophecms/import-export'].export(exportReq, manager); const fileName = path.basename(url); const exportFilePath = path.join(exportsPath, fileName); + const importFilePath = path.join(tempPath, fileName); + await fs.copyFile(exportFilePath, importFilePath); // cleanup await apos.doc.db.deleteMany({ type: '@apostrophecms/archive-page' }); @@ -742,7 +760,7 @@ describe('@apostrophecms/import-export:import-page', function () { body: {}, files: { file: { - path: exportFilePath, + path: importFilePath, type: mimeType } } diff --git a/test/index.js b/test/index.js index d655fcb0..d1389c05 100644 --- a/test/index.js +++ b/test/index.js @@ -30,7 +30,18 @@ describe('@apostrophecms/import-export', function () { apos = await t.create({ root: module, testModule: true, - modules: getAppConfig() + modules: { + ...getAppConfig(), + '@apostrophecms/import-export': { + options: { + importExport: { + export: { + expiration: 10 * 1000 + } + } + } + } + } }); tempPath = path.join(apos.rootDir, 'data/temp/uploadfs'); @@ -38,7 +49,6 @@ describe('@apostrophecms/import-export', function () { exportsPath = path.join(apos.rootDir, 'public/uploads/exports'); importExportManager = apos.modules['@apostrophecms/import-export']; importExportManager.removeFromUploadFs = () => {}; - importExportManager.remove = () => {}; gzip = importExportManager.formats.gzip; mimeType = gzip.allowedTypes[0]; @@ -56,7 +66,7 @@ describe('@apostrophecms/import-export', function () { afterEach(async function() { await deletePiecesAndPages(apos); await deleteAttachments(apos, attachmentPath); - await cleanData([ tempPath, exportsPath, attachmentPath ]); + await cleanData([ tempPath, exportsPath ]); }); it('should generate a zip file for pieces without related documents', async function () { @@ -263,8 +273,9 @@ describe('@apostrophecms/import-export', function () { const { url } = await importExportManager.export(req, manager); const fileName = path.basename(url); - - piecesTgzPath = path.join(exportsPath, fileName); + const exportFilePath = path.join(exportsPath, fileName); + const importFilePath = path.join(tempPath, fileName); + await fs.copyFile(exportFilePath, importFilePath); await deletePiecesAndPages(apos); await deleteAttachments(apos, attachmentPath); @@ -272,7 +283,7 @@ describe('@apostrophecms/import-export', function () { req.body = {}; req.files = { file: { - path: piecesTgzPath, + path: importFilePath, type: mimeType } }; @@ -349,13 +360,14 @@ describe('@apostrophecms/import-export', function () { const { url } = await importExportManager.export(req, manager); const fileName = path.basename(url); - - piecesTgzPath = path.join(exportsPath, fileName); + const exportFilePath = path.join(exportsPath, fileName); + const importFilePath = path.join(tempPath, fileName); + await fs.copyFile(exportFilePath, importFilePath); req.body = {}; req.files = { file: { - path: piecesTgzPath, + path: importFilePath, type: mimeType } }; @@ -460,8 +472,9 @@ describe('@apostrophecms/import-export', function () { const { url } = await importExportManager.export(req, apos.page); const fileName = path.basename(url); - - pageTgzPath = path.join(exportsPath, fileName); + const exportFilePath = path.join(exportsPath, fileName); + const importFilePath = path.join(tempPath, fileName); + await fs.copyFile(exportFilePath, importFilePath); await deletePiecesAndPages(apos); await deleteAttachments(apos, attachmentPath); @@ -469,7 +482,7 @@ describe('@apostrophecms/import-export', function () { req.body = {}; req.files = { file: { - path: pageTgzPath, + path: importFilePath, type: mimeType } }; @@ -513,13 +526,14 @@ describe('@apostrophecms/import-export', function () { const { url } = await importExportManager.export(req, apos.page); const fileName = path.basename(url); - - pageTgzPath = path.join(exportsPath, fileName); + const exportFilePath = path.join(exportsPath, fileName); + const importFilePath = path.join(tempPath, fileName); + await fs.copyFile(exportFilePath, importFilePath); req.body = {}; req.files = { file: { - path: pageTgzPath, + path: importFilePath, type: mimeType } }; @@ -618,13 +632,14 @@ describe('@apostrophecms/import-export', function () { const { url } = await importExportManager.export(req, apos.page); const fileName = path.basename(url); - - pageTgzPath = path.join(exportsPath, fileName); + const exportFilePath = path.join(exportsPath, fileName); + const importFilePath = path.join(tempPath, fileName); + await fs.copyFile(exportFilePath, importFilePath); req.body = {}; req.files = { file: { - path: pageTgzPath, + path: importFilePath, type: mimeType } }; @@ -746,8 +761,9 @@ describe('@apostrophecms/import-export', function () { const { url } = await importExportManager.export(req, apos.page); const fileName = path.basename(url); - - pageTgzPath = path.join(exportsPath, fileName); + const exportFilePath = path.join(exportsPath, fileName); + const importFilePath = path.join(tempPath, fileName); + await fs.copyFile(exportFilePath, importFilePath); // Now that it's exported as draft, PUBLISH the page again const { lastPublishedAt } = await apos.page.publish(req, draftPage); @@ -756,7 +772,7 @@ describe('@apostrophecms/import-export', function () { req.body = {}; req.files = { file: { - path: pageTgzPath, + path: importFilePath, type: mimeType } }; @@ -898,595 +914,4 @@ describe('@apostrophecms/import-export', function () { }); }); - describe('#import - man-made CSV file', function() { - let req; - let notify; - let input; - let csv; - - this.beforeEach(async function() { - csv = importExportManager.formats.csv; - mimeType = csv.allowedTypes[0]; - - req = apos.task.getReq({ - locale: 'en', - body: {}, - files: { - file: { - path: '/some/path/to/file', - type: mimeType - } - } - }); - notify = apos.notify; - input = csv.input; - - await deletePiecesAndPages(apos); - await deleteAttachments(apos, attachmentPath); - }); - - this.afterEach(function() { - apos.notify = notify; - csv.input = input; - }); - - it('should notify when the type is not provided', async function() { - csv.input = async () => { - return { - docs: [ - { - title: 'topic1', - description: 'description1', - main: '

rich text

' - } - ] - }; - }; - - const messages = []; - - apos.notify = async (req, message, options) => { - messages.push(message); - return notify(req, message, options); - }; - - await importExportManager.import(req); - - assert.equal(messages.some(message => message === 'aposImportExport:typeUnknown'), true); - }); - - it('should notify when the type does not exist', async function() { - csv.input = async () => { - return { - docs: [ - { - type: 'random-type', - title: 'topic1', - description: 'description1', - main: '

rich text

' - } - ] - }; - }; - - const messages = []; - - apos.notify = async (req, message, options) => { - messages.push(message); - return notify(req, message, options); - }; - - await importExportManager.import(req); - - assert.equal(messages.some(message => message === 'aposImportExport:typeUnknown'), true); - }); - - it('should import a piece from a csv file that was not made from the import-export module', async function() { - csv.input = async () => { - return { - docs: [ - { - type: 'topic', - title: 'topic1', - description: 'description1', - main: '

rich text

' - } - ] - }; - }; - - await importExportManager.import(req); - - const topics = await apos.doc.db - .find({ type: 'topic' }) - .toArray(); - - assert.equal(topics.length, 1); - assert.equal(topics[0].title, 'topic1'); - assert.equal(topics[0].slug, 'topic1'); - assert.equal(topics[0].aposMode, 'draft'); - assert.equal(topics[0].description, 'description1'); - assert.equal(topics[0].main.items[0].content, '

rich text

'); - }); - - it('should import a page from a csv file that was not made from the import-export module', async function() { - csv.input = async () => { - return { - docs: [ - { - type: 'default-page', - title: 'page1', - main: '

rich text

' - } - ] - }; - }; - - await importExportManager.import(req); - - const pages = await apos.doc.db - .find({ type: 'default-page' }) - .toArray(); - - assert.equal(pages.length, 1); - assert.equal(pages[0].title, 'page1'); - assert.equal(pages[0].slug, '/page1'); - assert.equal(pages[0].aposMode, 'draft'); - assert.equal(pages[0].main.items[0].content, '

rich text

'); - }); - - it('should insert a piece as draft and published when there is an update key that does not match any existing doc', async function() { - csv.input = async () => { - return { - docs: [ - { - type: 'topic', - 'title:key': 'topic1', - title: 'topic1 - edited', - description: 'description1 - edited', - main: '

rich text - edited

' - } - ] - }; - }; - - await importExportManager.import(req); - - const topics = await apos.doc.db - .find({ type: 'topic' }) - .toArray(); - - assert.equal(topics.length, 2); - - assert.equal(topics[0].title, 'topic1 - edited'); - assert.equal(topics[0].slug, 'topic1-edited'); - assert.equal(topics[0].aposMode, 'draft'); - assert.equal(topics[0].description, 'description1 - edited'); - assert.equal(topics[0].main.items[0].content, '

rich text - edited

'); - - assert.equal(topics[1].title, 'topic1 - edited'); - assert.equal(topics[1].slug, 'topic1-edited'); - assert.equal(topics[1].aposMode, 'published'); - assert.equal(topics[1].description, 'description1 - edited'); - assert.equal(topics[1].main.items[0].content, '

rich text - edited

'); - }); - - it('should insert a page as draft and published when there is an update key that does not match any existing doc', async function() { - csv.input = async () => { - return { - docs: [ - { - type: 'default-page', - 'title:key': 'page1', - title: 'page1 - edited', - main: '

rich text - edited

' - } - ] - }; - }; - - await importExportManager.import(req); - - const pages = await apos.doc.db - .find({ type: 'default-page' }) - .toArray(); - - assert.equal(pages.length, 2); - - assert.equal(pages[0].title, 'page1 - edited'); - assert.equal(pages[0].slug, '/page1-edited'); - assert.equal(pages[0].aposMode, 'draft'); - assert.equal(pages[0].main.items[0].content, '

rich text - edited

'); - - assert.equal(pages[1].title, 'page1 - edited'); - assert.equal(pages[1].slug, '/page1-edited'); - assert.equal(pages[1].aposMode, 'published'); - assert.equal(pages[1].main.items[0].content, '

rich text - edited

'); - }); - - it('should insert a piece as draft and published when there is an empty update key', async function() { - csv.input = async () => { - return { - docs: [ - { - type: 'topic', - 'title:key': '', - title: 'topic1 - edited', - description: 'description1 - edited', - main: '

rich text - edited

' - } - ] - }; - }; - - await importExportManager.import(req); - - const topics = await apos.doc.db - .find({ type: 'topic' }) - .toArray(); - - assert.equal(topics.length, 2); - - assert.equal(topics[0].title, 'topic1 - edited'); - assert.equal(topics[0].slug, 'topic1-edited'); - assert.equal(topics[0].aposMode, 'draft'); - assert.equal(topics[0].description, 'description1 - edited'); - assert.equal(topics[0].main.items[0].content, '

rich text - edited

'); - - assert.equal(topics[1].title, 'topic1 - edited'); - assert.equal(topics[1].slug, 'topic1-edited'); - assert.equal(topics[1].aposMode, 'published'); - assert.equal(topics[1].description, 'description1 - edited'); - assert.equal(topics[1].main.items[0].content, '

rich text - edited

'); - }); - - it('should insert a page as draft and published when there is an empty update key', async function() { - csv.input = async () => { - return { - docs: [ - { - type: 'default-page', - 'title:key': '', - title: 'page1 - edited', - main: '

rich text - edited

' - } - ] - }; - }; - - await importExportManager.import(req); - - const pages = await apos.doc.db - .find({ type: 'default-page' }) - .toArray(); - - assert.equal(pages.length, 2); - - assert.equal(pages[0].title, 'page1 - edited'); - assert.equal(pages[0].slug, '/page1-edited'); - assert.equal(pages[0].aposMode, 'draft'); - assert.equal(pages[0].main.items[0].content, '

rich text - edited

'); - - assert.equal(pages[1].title, 'page1 - edited'); - assert.equal(pages[1].slug, '/page1-edited'); - assert.equal(pages[1].aposMode, 'published'); - assert.equal(pages[1].main.items[0].content, '

rich text - edited

'); - }); - - it('should update a piece draft and published versions when there is an update key that matches an existing doc', async function() { - csv.input = async () => { - return { - docs: [ - { - type: 'topic', - 'title:key': 'topic1', - title: 'topic1 - edited', - description: 'description1 - edited', - main: '

rich text - edited

' - } - ] - }; - }; - - const topic = await apos.topic.insert(req, { - ...apos.topic.newInstance(), - title: 'topic1', - description: 'description1', - main: '

rich text

' - }); - - await importExportManager.import(req); - - const topics = await apos.doc.db - .find({ type: 'topic' }) - .toArray(); - - assert.equal(topics.length, 2); - - assert.equal(topics[0].aposDocId, topic.aposDocId); - assert.equal(topics[0].title, 'topic1 - edited'); - assert.equal(topics[0].slug, 'topic1'); - assert.equal(topics[0].aposMode, 'draft'); - assert.equal(topics[0].description, 'description1 - edited'); - assert.equal(topics[0].main.items[0].content, '

rich text - edited

'); - assert.equal(topics[0].modified, false); - - assert.equal(topics[1].aposDocId, topic.aposDocId); - assert.equal(topics[1].title, 'topic1 - edited'); - assert.equal(topics[1].slug, 'topic1'); - assert.equal(topics[1].aposMode, 'published'); - assert.equal(topics[1].description, 'description1 - edited'); - assert.equal(topics[1].main.items[0].content, '

rich text - edited

'); - }); - - it('should update a page draft and published versions when there is an update key that matches an existing doc', async function() { - csv.input = async () => { - return { - docs: [ - { - type: 'default-page', - 'title:key': 'page1', - title: 'page1 - edited', - main: '

rich text - edited

' - } - ] - }; - }; - - const page = await apos.page.insert(req, '_home', 'lastChild', { - ...apos.modules['default-page'].newInstance(), - title: 'page1', - main: '

rich text

' - }); - - await importExportManager.import(req); - - const pages = await apos.doc.db - .find({ type: 'default-page' }) - .toArray(); - - assert.equal(pages.length, 2); - - assert.equal(pages[0].aposDocId, page.aposDocId); - assert.equal(pages[0].title, 'page1 - edited'); - assert.equal(pages[0].slug, '/page1'); - assert.equal(pages[0].aposMode, 'draft'); - assert.equal(pages[0].main.items[0].content, '

rich text - edited

'); - assert.equal(pages[0].modified, false); - - assert.equal(pages[1].aposDocId, page.aposDocId); - assert.equal(pages[1].title, 'page1 - edited'); - assert.equal(pages[1].slug, '/page1'); - assert.equal(pages[1].aposMode, 'published'); - assert.equal(pages[1].main.items[0].content, '

rich text - edited

'); - }); - - it('should update a piece draft and published versions when there is an update key that only matches the existing draft doc', async function() { - csv.input = async () => { - return { - docs: [ - { - type: 'topic', - 'title:key': 'topic1', - title: 'topic1 - edited', - description: 'description1 - edited', - main: '

rich text - edited

' - } - ] - }; - }; - - const topic = await apos.topic.insert(req, { - ...apos.topic.newInstance(), - title: 'topic1', - description: 'description1', - main: '

rich text

' - }); - - // check that the published doc is also updated, even with a different title - await apos.doc.db.updateOne( - { - aposDocId: topic.aposDocId, - aposMode: 'published' - }, - { - $set: { - title: 'topic1 - published title that does not match the draft title nor the update key' - } - } - ); - - await importExportManager.import(req); - - const topics = await apos.doc.db - .find({ type: 'topic' }) - .toArray(); - - assert.equal(topics.length, 2); - - assert.equal(topics[0].aposDocId, topic.aposDocId); - assert.equal(topics[0].title, 'topic1 - edited'); - assert.equal(topics[0].slug, 'topic1'); - assert.equal(topics[0].aposMode, 'draft'); - assert.equal(topics[0].description, 'description1 - edited'); - assert.equal(topics[0].main.items[0].content, '

rich text - edited

'); - assert.equal(topics[0].modified, false); - - assert.equal(topics[1].aposDocId, topic.aposDocId); - assert.equal(topics[1].title, 'topic1 - edited'); - assert.equal(topics[1].slug, 'topic1'); - assert.equal(topics[1].aposMode, 'published'); - assert.equal(topics[1].description, 'description1 - edited'); - assert.equal(topics[1].main.items[0].content, '

rich text - edited

'); - }); - - it('should update a page draft and published versions when there is an update key that only matches the existing draft doc', async function() { - csv.input = async () => { - return { - docs: [ - { - type: 'default-page', - 'title:key': 'page1', - title: 'page1 - edited', - main: '

rich text - edited

' - } - ] - }; - }; - - const page = await apos.page.insert(req, '_home', 'lastChild', { - ...apos.modules['default-page'].newInstance(), - title: 'page1', - main: '

rich text

' - }); - - // check that the published doc is also updated, even with a different title - await apos.doc.db.updateOne( - { - aposDocId: page.aposDocId, - aposMode: 'published' - }, - { - $set: { - title: 'page1 - published title that does not match the draft title nor the update key' - } - } - ); - - await importExportManager.import(req); - - const pages = await apos.doc.db - .find({ type: 'default-page' }) - .toArray(); - - assert.equal(pages.length, 2); - - assert.equal(pages[0].aposDocId, page.aposDocId); - assert.equal(pages[0].title, 'page1 - edited'); - assert.equal(pages[0].slug, '/page1'); - assert.equal(pages[0].aposMode, 'draft'); - assert.equal(pages[0].main.items[0].content, '

rich text - edited

'); - assert.equal(pages[0].modified, false); - - assert.equal(pages[1].aposDocId, page.aposDocId); - assert.equal(pages[1].title, 'page1 - edited'); - assert.equal(pages[1].slug, '/page1'); - assert.equal(pages[1].aposMode, 'published'); - assert.equal(pages[1].main.items[0].content, '

rich text - edited

'); - }); - - it('should update a piece draft and published versions when there is an update key that matches only the existing published doc', async function() { - csv.input = async () => { - return { - docs: [ - { - type: 'topic', - 'title:key': 'topic1', - title: 'topic1 - edited', - description: 'description1 - edited', - main: '

rich text - edited

' - } - ] - }; - }; - - const topic = await apos.topic.insert(req, { - ...apos.topic.newInstance(), - title: 'topic1', - description: 'description1', - main: '

rich text

' - }); - - // check that the draft doc is also updated, even with a different title - await apos.doc.db.updateOne( - { - aposDocId: topic.aposDocId, - aposMode: 'draft' - }, - { - $set: { - title: 'topic1 - draft title that does not match the published title nor the update key' - } - } - ); - - await importExportManager.import(req); - - const topics = await apos.doc.db - .find({ type: 'topic' }) - .toArray(); - - assert.equal(topics.length, 2); - - assert.equal(topics[0].aposDocId, topic.aposDocId); - assert.equal(topics[0].title, 'topic1 - edited'); - assert.equal(topics[0].slug, 'topic1'); - assert.equal(topics[0].aposMode, 'draft'); - assert.equal(topics[0].description, 'description1 - edited'); - assert.equal(topics[0].main.items[0].content, '

rich text - edited

'); - assert.equal(topics[0].modified, false); - - assert.equal(topics[1].aposDocId, topic.aposDocId); - assert.equal(topics[1].title, 'topic1 - edited'); - assert.equal(topics[1].slug, 'topic1'); - assert.equal(topics[1].aposMode, 'published'); - assert.equal(topics[1].description, 'description1 - edited'); - assert.equal(topics[1].main.items[0].content, '

rich text - edited

'); - }); - - it('should update a page draft and published versions when there is an update key that only matches the existing published doc', async function() { - csv.input = async () => { - return { - docs: [ - { - type: 'default-page', - 'title:key': 'page1', - title: 'page1 - edited', - main: '

rich text - edited

' - } - ] - }; - }; - - const page = await apos.page.insert(req, '_home', 'lastChild', { - ...apos.modules['default-page'].newInstance(), - title: 'page1', - main: '

rich text

' - }); - - // check that the draft doc is also updated, even with a different title - await apos.doc.db.updateOne( - { - aposDocId: page.aposDocId, - aposMode: 'draft' - }, - { - $set: { - title: 'page1 - draft title that does not match the published title nor the update key' - } - } - ); - - await importExportManager.import(req); - - const pages = await apos.doc.db - .find({ type: 'default-page' }) - .toArray(); - - assert.equal(pages.length, 2); - - assert.equal(pages[0].aposDocId, page.aposDocId); - assert.equal(pages[0].title, 'page1 - edited'); - assert.equal(pages[0].slug, '/page1'); - assert.equal(pages[0].aposMode, 'draft'); - assert.equal(pages[0].main.items[0].content, '

rich text - edited

'); - assert.equal(pages[0].modified, false); - - assert.equal(pages[1].aposDocId, page.aposDocId); - assert.equal(pages[1].title, 'page1 - edited'); - assert.equal(pages[1].slug, '/page1'); - assert.equal(pages[1].aposMode, 'published'); - assert.equal(pages[1].main.items[0].content, '

rich text - edited

'); - }); - }); }); diff --git a/test/modules/@apostrophecms/log/index.js b/test/modules/@apostrophecms/log/index.js new file mode 100644 index 00000000..0005f162 --- /dev/null +++ b/test/modules/@apostrophecms/log/index.js @@ -0,0 +1,14 @@ +module.exports = { + options: { + filter: { + // By module name, or *. We can specify any mix of severity levels and specific event types, + // and entries are kept if *either* criterion is met + '*': { + severity: [ 'warn', 'error' ] + }, + '@apostrophecms/login': { + events: [ 'incorrect-user', 'incorrect-password' ] + } + } + } +};