UNPKG

apostrophe

Version:
718 lines (642 loc) • 21.4 kB
const t = require('../test-lib/test.js'); const assert = require('assert'); describe('Draft / Published', function() { let apos; this.timeout(t.timeout); after(async function () { return t.destroy(apos); }); /// /// // EXISTENCE /// /// it('should initialize with a schema', async function() { apos = await t.create({ root: module, modules: { '@apostrophecms/page': { options: { park: [], types: [ { name: '@apostrophecms/home-page', label: 'Home' }, { name: 'test-page', label: 'Test Page' } ] } }, 'test-page': { extend: '@apostrophecms/page-type' }, product: { extend: '@apostrophecms/piece-type', options: { alias: 'product' }, fields: { add: { body: { type: 'area', options: { widgets: { '@apostrophecms/rich-text': {}, '@apostrophecms/image': {} } } }, color: { type: 'select', choices: [ { label: 'Red', value: 'red' }, { label: 'Blue', value: 'blue' } ] }, photo: { type: 'attachment', group: 'images' }, addresses: { type: 'array', fields: { add: { street: { type: 'string' } } } }, _articles: { type: 'relationship', withType: 'article', builders: { project: { _url: 1, title: 1 } }, fields: { add: { relevance: { // Explains the relevance of the article to the // product in 1 sentence type: 'string' } } } } } } }, article: { extend: '@apostrophecms/piece-type', options: { alias: 'article' }, fields: { add: { name: { type: 'string' } } } } } }); }); let testDraftProduct; it('should be able to create and insert a draft product', async function() { const product = apos.product.newInstance(); product.title = 'Test Product'; testDraftProduct = await apos.product.insert(apos.task.getReq({ mode: 'draft' }), product); assert(testDraftProduct.modified); }); it('published should not exist yet', async function() { assert(!await apos.doc.db.findOne({ _id: testDraftProduct._id.replace(':draft', ':published') })); }); it('should be able to publish the product', async function() { await apos.product.publish(apos.task.getReq({ mode: 'draft' }), testDraftProduct); }); it('published product should exist and be the same', async function() { const product = await apos.product.find(apos.task.getReq({ mode: 'published' }), { _id: testDraftProduct._id.replace(':draft', ':published') }).toObject(); assert(product); assert(product.aposDocId === testDraftProduct.aposDocId); assert(product.aposLocale === 'en:published'); assert(product.title === testDraftProduct.title); }); it('original product should no longer be modified', async function() { testDraftProduct = await apos.product.find(apos.task.getReq({ mode: 'draft' }), { _id: testDraftProduct._id }).toObject(); assert(testDraftProduct); assert(!testDraftProduct.modified); }); it('original product still shows as unmodified if we update it with no changes', async function() { testDraftProduct = await apos.product.update(apos.task.getReq({ mode: 'draft' }), testDraftProduct); assert(!testDraftProduct.modified); }); it('original product shows as modified if we make a change to it', async function() { testDraftProduct.title = 'Another Title'; testDraftProduct = await apos.product.update(apos.task.getReq({ mode: 'draft' }), testDraftProduct); assert(testDraftProduct.modified); }); it('can revert the draft to published', async function() { testDraftProduct = await apos.product.revertDraftToPublished(apos.task.getReq({ mode: 'draft' }), testDraftProduct); assert(testDraftProduct); assert(!testDraftProduct.modified); assert(testDraftProduct.title === 'Test Product'); }); it('cannot revert the draft again', async function() { assert(!await apos.product.revertDraftToPublished(apos.task.getReq({ mode: 'draft' }), testDraftProduct)); }); it('original product shows as modified if we make another change to it', async function() { testDraftProduct.title = 'Title 3'; testDraftProduct = await apos.product.update(apos.task.getReq({ mode: 'draft' }), testDraftProduct); assert(testDraftProduct.modified); }); it('should be able to publish the product again', async function() { await apos.product.publish(apos.task.getReq({ mode: 'draft' }), testDraftProduct); }); it('"previous published" should be deduplicated at this point, and previous have the right props', async function() { const previous = await apos.doc.db.findOne({ _id: testDraftProduct._id.replace(':draft', ':previous') }); assert(previous); assert(previous.aposMode === 'previous'); assert.strictEqual(previous.slug, `deduplicate-${previous.aposDocId}-test-product`); }); it('original product shows as modified if we make a third change to it', async function() { testDraftProduct.title = 'Title 4'; testDraftProduct = await apos.product.update(apos.task.getReq({ mode: 'draft' }), testDraftProduct); assert(testDraftProduct.modified); }); it('can revert the draft to Title 3', async function() { testDraftProduct = await apos.product.revertDraftToPublished(apos.task.getReq({ mode: 'draft' }), testDraftProduct); assert(testDraftProduct); assert(!testDraftProduct.modified); assert(testDraftProduct.title === 'Title 3'); }); it('can revert the published version to Test Product (previous publication)', async function() { const req = apos.task.getReq({ mode: 'published' }); let published = await apos.product.findOneForEditing(req, { aposDocId: testDraftProduct.aposDocId }); assert(published && published.aposLocale === 'en:published'); published = await apos.product.revertPublishedToPrevious(req, published); assert(published); // Make sure the slug is no longer deduplicated assert(published.slug === 'test-product'); assert(published.title === 'Test Product'); testDraftProduct = await apos.product.findOneForEditing(req.clone({ mode: 'draft' }), { _id: testDraftProduct._id }); assert(testDraftProduct); assert(testDraftProduct.title === 'Title 3'); assert(testDraftProduct.modified); }); it('cannot revert published to previous again', async function() { const req = apos.task.getReq({ mode: 'published' }); const published = await apos.product.findOneForEditing(req, { aposDocId: testDraftProduct.aposDocId }); try { await apos.product.revertPublishedToPrevious(apos.task.getReq({ mode: 'draft' }), published); assert(false); } catch (e) { assert(e.name === 'invalid'); } }); let parent; it('should be able to create and insert a draft page', async function() { parent = { type: 'test-page', title: 'Parent', slug: '/parent' }; const req = apos.task.getReq({ mode: 'draft' }); parent = await apos.page.insert(req, '_home', 'lastChild', parent); const home = await apos.page.find(req, { slug: '/', level: 0 }).toObject(); assert.strictEqual(parent.path, `${home.path}/${parent.aposDocId}`); assert(parent.modified); }); it('published should not exist yet again', async function() { assert(!await apos.doc.db.findOne({ _id: parent._id.replace(':draft', ':published') })); }); it('should be able to publish the page', async function() { await apos.page.publish(apos.task.getReq({ mode: 'draft' }), parent); }); it('published page should exist and be the same', async function() { const publishedParent = await apos.page.find(apos.task.getReq({ mode: 'published' }), { _id: parent._id.replace(':draft', ':published') }).toObject(); assert(publishedParent); assert(publishedParent.aposDocId === parent.aposDocId); assert(publishedParent.aposLocale === 'en:published'); assert(publishedParent.title === parent.title); }); it('original page should no longer be modified', async function() { parent = await apos.page.find(apos.task.getReq({ mode: 'draft' }), { _id: parent._id }).toObject(); assert(parent); assert(!parent.modified); }); it('original page still shows as unmodified if we update it with no changes', async function() { parent = await apos.page.update(apos.task.getReq({ mode: 'draft' }), parent); assert(!parent.modified); }); it('original page shows as modified if we make a change to it', async function() { parent.title = 'Parent Title 2'; parent = await apos.page.update(apos.task.getReq({ mode: 'draft' }), parent); assert(parent.modified); }); it('can revert the page draft to published', async function() { parent = await apos.page.revertDraftToPublished(apos.task.getReq({ mode: 'draft' }), parent); assert(parent); assert(!parent.modified); assert(parent.title === 'Parent'); }); it('cannot revert the draft again (2)', async function() { assert(!await apos.page.revertDraftToPublished(apos.task.getReq({ mode: 'draft' }), parent)); }); // TODO convert more tests, add tests involving the page tree more, // write another test file with REST tests, implement the RESTfulness it('original page shows as modified if we make another change to it', async function() { parent.title = 'Parent Title 3'; parent = await apos.page.update(apos.task.getReq({ mode: 'draft' }), parent); assert(parent.modified); }); it('should be able to publish the page again', async function() { await apos.page.publish(apos.task.getReq({ mode: 'draft' }), parent); }); it('original page shows as modified if we make a third change to it', async function() { parent.title = 'Parent Title 4'; parent = await apos.page.update(apos.task.getReq({ mode: 'draft' }), parent); assert(parent.modified); }); it('can revert the draft to parent Title 3', async function() { parent = await apos.page.revertDraftToPublished(apos.task.getReq({ mode: 'draft' }), parent); assert(parent); assert(!parent.modified); assert(parent.title === 'Parent Title 3'); }); it('can revert the published version to previous', async function() { const req = apos.task.getReq({ mode: 'published' }); let published = await apos.page.findOneForEditing(req, { aposDocId: parent.aposDocId }); assert(published && published.aposLocale === 'en:published'); published = await apos.page.revertPublishedToPrevious(apos.task.getReq({ mode: 'draft' }), published); assert(published); assert(published.title === 'Parent'); parent = await apos.page.findOneForEditing(req.clone({ mode: 'draft' }), { _id: parent._id }); assert(parent); assert(parent.title === 'Parent Title 3'); assert(parent.modified); }); it('cannot revert published to previous again (2)', async function() { const req = apos.task.getReq({ mode: 'published' }); const published = await apos.page.findOneForEditing(req, { aposDocId: parent.aposDocId }); try { await apos.page.revertPublishedToPrevious(apos.task.getReq({ mode: 'draft' }), published); assert(false); } catch (e) { assert(e.name === 'invalid'); } }); let sibling; it('should be able to create and insert a sibling draft page', async function() { sibling = { type: 'test-page', title: 'Sibling', slug: '/sibling' }; sibling = await apos.page.insert(apos.task.getReq({ mode: 'draft' }), '_home', 'lastChild', sibling); assert(sibling.modified); }); it('should be able to publish the sibling page', async function() { await apos.page.publish(apos.task.getReq({ mode: 'draft' }), sibling); }); let grandchild; it('should be able to create and insert a grandchild page', async function() { grandchild = { type: 'test-page', title: 'Grandchild', // At insert time, a good slug is up to the caller slug: '/parent/grandchild' }; grandchild = await apos.page.insert(apos.task.getReq({ mode: 'draft' }), parent._id, 'lastChild', grandchild); assert(grandchild.modified); assert.strictEqual(grandchild.path, `${parent.path}/${grandchild.aposDocId}`); assert.strictEqual(grandchild.slug, '/parent/grandchild'); }); it('published grandchild should not exist yet', async function() { assert(!await apos.page.find(apos.task.getReq({ mode: 'published' }), { aposDocId: grandchild.aposDocId }).toObject()); }); it('should be able to publish the grandchild page', async function() { await apos.page.publish(apos.task.getReq({ mode: 'draft' }), grandchild); const published = await apos.page.find(apos.task.getReq({ mode: 'published' }), { aposDocId: grandchild.aposDocId }).toObject(); assert(published); assert.strictEqual(published.aposMode, 'published'); assert.strictEqual(published.path, `${parent.path}/${grandchild.aposDocId}`); assert.strictEqual(published.slug, '/parent/grandchild'); }); it('should be able to move the grandchild page beneath the sibling page', async function() { grandchild = await apos.page.find(apos.task.getReq({ mode: 'draft' }), { _id: grandchild._id }).toObject(); // Should not be modified to start assert(!grandchild.modified); await apos.page.move(apos.task.getReq({ mode: 'draft' }), grandchild._id, sibling._id, 'lastChild'); sibling = await apos.page.find(apos.task.getReq({ mode: 'draft' }), { _id: sibling._id }).children(true).toObject(); assert(sibling?._children?.[0]?._id === grandchild._id); grandchild = await apos.page.find(apos.task.getReq({ mode: 'draft' }), { _id: grandchild._id }).toObject(); // Should be considered modified because we moved it assert(!grandchild.modified); }); it('published grandchild page should now also be beneath the sibling page', async function() { sibling = await apos.page.find(apos.task.getReq({ mode: 'published' }), { aposDocId: sibling.aposDocId }).children(true).toObject(); assert(sibling?._children?.[0]?.aposDocId === grandchild.aposDocId); }); it('should be able to publish the grandchild page again to re-execute the move in the published locale', async function() { await apos.page.publish(apos.task.getReq({ mode: 'draft' }), grandchild); }); it('published grandchild page should now be beneath sibling page', async function() { const siblingPublished = await apos.page.find(apos.task.getReq({ mode: 'published' }), { aposDocId: sibling.aposDocId }).children(true).toObject(); assert(siblingPublished && siblingPublished._children && siblingPublished._children[0] && siblingPublished._children[0]._id === grandchild._id.replace(':draft', ':published')); }); describe('unpublish', function() { describe('page', function() { const baseItem = { aposDocId: 'some-page', type: 'test-page', slug: '/some-page', visibility: 'public', path: '/some-page', level: 1, rank: 0 }; const draftItem = { ...baseItem, _id: 'some-page:en:draft', aposLocale: 'en:draft', aposMode: 'draft' }; const publishedItem = { ...baseItem, _id: 'some-page:en:published', aposLocale: 'en:published', aposMode: 'published' }; const previousItem = { ...baseItem, _id: 'some-page:en:previous', aposLocale: 'en:previous', aposMode: 'previous' }; let draft; let published; let previous; this.beforeEach(async function() { await apos.doc.db.insertMany([ draftItem, publishedItem, previousItem ]); const res = await apos.doc.db.findOne({ _id: 'some-page:en:published' }); const req = apos.task.getReq({ mode: 'published' }); draft = await apos.page.unpublish(req, res); published = await apos.doc.db.findOne({ _id: 'some-page:en:published' }); previous = await apos.doc.db.findOne({ _id: 'some-page:en:previous' }); }); this.afterEach(async function() { await apos.doc.db.deleteMany({ aposDocId: 'some-page' }); }); it('should remove the published and previous versions of a page', function() { assert(published === null); assert(previous === null); }); it('should update the draft version of a page', function() { assert(draft._id === draftItem._id); assert(draft.modified === true); assert(draft.lastPublishedAt === null); }); }); describe('parked page', function() { it('should not unpublish parked pages', async function() { const baseItem = { aposDocId: 'some-parked-page', type: 'test-page', slug: '/some-parked-page', visibility: 'public', path: '/some-parked-page', level: 1, rank: 0, parked: 1 }; const draftItem = { ...baseItem, _id: 'some-parked-page:en:draft', aposLocale: 'en:draft' }; const publishedItem = { ...baseItem, _id: 'some-parked-page:en:published', aposLocale: 'en:published' }; await apos.doc.db.insertMany([ draftItem, publishedItem ]); const res = await apos.doc.db.findOne({ _id: 'some-parked-page:en:published' }); const req = apos.task.getReq({ mode: 'published' }); try { await apos.page.unpublish(req, res); } catch (error) { assert(error.message === 'apostrophe:pageIsParkedAndCannotBeUnpublished'); return; } throw new Error('unpublish should have thrown'); }); }); describe('piece', function() { const baseItem = { aposDocId: 'some-product', type: 'product', slug: '/some-product', visibility: 'public' }; const draftItem = { ...baseItem, _id: 'some-product:en:draft', aposLocale: 'en:draft' }; const publishedItem = { ...baseItem, _id: 'some-product:en:published', aposLocale: 'en:published' }; const previousItem = { ...baseItem, _id: 'some-product:en:previous', aposLocale: 'en:previous' }; let draft; let published; let previous; this.beforeEach(async function() { await apos.doc.db.insertMany([ draftItem, publishedItem, previousItem ]); const res = await apos.doc.db.findOne({ _id: 'some-product:en:published' }); const req = apos.task.getReq({ mode: 'published' }); draft = await apos.product.unpublish(req, res); published = await apos.doc.db.findOne({ _id: 'some-product:en:published' }); previous = await apos.doc.db.findOne({ _id: 'some-product:en:previous' }); }); this.afterEach(async function() { await apos.doc.db.deleteMany({ aposDocId: 'some-product' }); }); it('should remove the published and previous versions of a piece', function() { assert(published === null); assert(previous === null); }); it('should update the draft version of a piece', function() { assert(draft._id === draftItem._id); assert(draft.modified === true); assert(draft.lastPublishedAt === null); }); }); }); });