UNPKG

apostrophe

Version:
1,173 lines (1,064 loc) 35.5 kB
const { createId } = require('@paralleldrive/cuid2'); const t = require('../test-lib/test.js'); const assert = require('assert'); const areaConfig = { '@apostrophecms/image': {}, '@apostrophecms/video': {}, '@apostrophecms/rich-text': { toolbar: [ 'styles', 'bold', 'italic', 'strike', 'link', 'bullet_list', 'ordered_list', 'blockquote' ], styles: [ { tag: 'p', label: 'Paragraph (P)' }, { tag: 'h3', label: 'Heading 3 (H3)' }, { tag: 'h4', label: 'Heading 4 (H4)' } ] } }; describe('Pages REST', function() { let apos; let homeId; let jar; this.timeout(t.timeout); after(function() { return t.destroy(apos); }); // EXISTENCE it('should be a property of the apos object', async function() { apos = await t.create({ root: module, modules: { '@apostrophecms/page': { options: { publicApiProjection: { title: 1, _url: 1, path: 1, level: 1, rank: 1 }, park: [], types: [ { name: '@apostrophecms/home-page', label: 'Home' }, { name: 'test-page', label: 'Test Page' } ] } }, 'two-column-widget': { extend: '@apostrophecms/widget-type', options: { label: 'Two Column' }, fields: { add: { one: { type: 'area', contextual: true, options: { widgets: areaConfig } }, two: { type: 'area', contextual: true, options: { widgets: areaConfig } } } } }, 'test-page': { extend: '@apostrophecms/page-type', fields: { add: { color: { type: 'string' }, body: { type: 'area', options: { widgets: { '@apostrophecms/rich-text': { toolbar: [ 'bold', 'italic' ] }, '@apostrophecms/image': {}, '@apostrophecms/video': {}, 'two-column': {} } } } } } } } }); assert(apos.page.__meta.name === '@apostrophecms/page'); }); it('should be able to insert test users', async function() { assert(apos.user.newInstance); const user = apos.user.newInstance(); assert(user); user.title = 'admin'; user.username = 'admin'; user.password = 'admin'; user.email = 'ad@min.com'; user.role = 'admin'; await apos.user.insert(apos.task.getReq(), user); const user2 = apos.user.newInstance(); assert(user2); user2.title = 'admin2'; user2.username = 'admin2'; user2.password = 'admin2'; user2.email = 'ad@min2.com'; user2.role = 'admin'; return apos.user.insert(apos.task.getReq(), user2); }); it('REST: should be able to log in as admin', async function() { jar = apos.http.jar(); // establish session let page = await apos.http.get('/', { jar }); assert(page.match(/logged out/)); // Log in await apos.http.post('/api/v1/@apostrophecms/login/login', { body: { username: 'admin', password: 'admin', session: true }, jar }); // Confirm login page = await apos.http.get('/', { jar }); assert(page.match(/logged in/)); }); it('can GET the home page without session', async function() { const home = await apos.http.get('/api/v1/@apostrophecms/page', {}); assert(home); assert(home.slug === '/'); // make sure new style paths used assert(home.path !== '/'); assert(`${home.path}:en:published` === home._id); assert(home.level === 0); homeId = home._id; }); it('should be able to use db to insert documents', async function() { const lastPublishedAt = new Date(); const testItems = [ { _id: 'parent:en:published', aposLocale: 'en:published', aposDocId: 'parent', type: 'test-page', slug: '/parent', visibility: 'public', path: `${homeId.replace(':en:published', '')}/parent`, level: 1, rank: 0, lastPublishedAt }, { _id: 'child:en:published', aposLocale: 'en:published', aposDocId: 'child', slug: '/child', type: 'test-page', visibility: 'public', path: `${homeId.replace(':en:published', '')}/parent/child`, level: 2, rank: 0, lastPublishedAt }, { _id: 'grandchild:en:published', aposLocale: 'en:published', aposDocId: 'grandchild', type: 'test-page', slug: '/grandchild', visibility: 'public', path: `${homeId.replace(':en:published', '')}/parent/child/grandchild`, level: 3, rank: 0, lastPublishedAt }, { _id: 'sibling:en:published', aposLocale: 'en:published', aposDocId: 'sibling', type: 'test-page', slug: '/sibling', visibility: 'public', path: `${homeId.replace(':en:published', '')}/parent/sibling`, level: 2, rank: 1, lastPublishedAt }, { _id: 'cousin:en:published', aposLocale: 'en:published', aposDocId: 'cousin', type: 'test-page', slug: '/cousin', visibility: 'public', path: `${homeId.replace(':en:published', '')}/parent/sibling/cousin`, level: 3, rank: 0, lastPublishedAt }, { _id: 'another-parent:en:published', aposLocale: 'en:published', aposDocId: 'another-parent', type: 'test-page', slug: '/another-parent', visibility: 'public', path: `${homeId.replace(':en:published', '')}/another-parent`, level: 1, rank: 1, lastPublishedAt }, { _id: 'neighbor:en:published', aposLocale: 'en:published', aposDocId: 'neighbor', type: 'test-page', slug: '/neighbor', visibility: 'public', path: `${homeId.replace(':en:published', '')}/neighbor`, level: 1, rank: 2, lastPublishedAt } ]; // Insert draft versions too to match the A3 data model const draftItems = await apos.doc.db.insertMany(testItems.map(item => ({ ...item, aposLocale: item.aposLocale.replace(':published', ':draft'), _id: item._id.replace(':published', ':draft') }))); assert(draftItems.result.ok === 1); assert(draftItems.insertedCount === 7); // Change the rank of the archive pages to preserve the constraint that it // comes last await apos.doc.db.updateMany({ type: '@apostrophecms/archive-page' }, { $set: { rank: 3 } }); const items = await apos.doc.db.insertMany(testItems); assert(items.result.ok === 1); assert(items.insertedCount === 7); }); it('is able to make a subpage of the homepage without _position or _targetId', async function() { const body = { slug: '/new-tab', visibility: 'public', type: 'test-page', title: 'New Tab' }; const page = await apos.http.post('/api/v1/@apostrophecms/page', { body, jar }); assert(page); assert(page.title === 'New Tab'); // Is the path generally correct? assert.strictEqual(page.path, `${homeId.replace(':en:published', '')}/${page._id.replace(':en:published', '')}`); assert.strictEqual(page.level, 1); }); it('is able to make a subpage of the homepage at index `1` with numerical _position', async function() { const body = { slug: '/second-new', visibility: 'public', type: 'test-page', title: 'Second New', _targetId: '_home', _position: '1' }; const page = await apos.http.post('/api/v1/@apostrophecms/page', { body, jar }); assert(page); assert(page.title === 'Second New'); // Is the path generally correct? assert.strictEqual(page.path, `${homeId.replace(':en:published', '')}/${page._id.replace(':en:published', '')}`); const home = await apos.http.get('/api/v1/@apostrophecms/page?children=1', { jar }); assert(home); assert(home._children); assert(home._children[1]); assert(home._children[1]._id === page._id); }); let newPageId; it('is able to make a subpage of /parent with _position and _targetId', async function() { const body = { slug: '/new-page', visibility: 'public', type: 'test-page', title: 'New Page', _targetId: 'parent:en:published', _position: 'lastChild' }; const page = await apos.http.post('/api/v1/@apostrophecms/page', { body, jar }); newPageId = page._id; assert(page); assert(page.title === 'New Page'); // Is the path generally correct? assert.strictEqual(page.path, `${homeId.replace(':en:published', '')}/parent/${page._id.replace(':en:published', '')}`); assert.strictEqual(page.level, 2); assert.strictEqual(page.rank, 2); }); it('cannot POST a page without a session', async function() { const body = { slug: '/new-tab', visibility: 'public', type: 'test-page', title: 'New Tab' }; try { await apos.http.post('/api/v1/@apostrophecms/page', { body }); assert(false); } catch (e) { assert(true); } }); it('should be able to find just a single page with ancestors', async function() { const page = await apos.http.get('/api/v1/@apostrophecms/page/child:en:published'); assert(page); assert(page.path === `${homeId.replace(':en:published', '')}/parent/child`); // There should be 2 ancestors. assert(page._ancestors.length === 2); // The first ancestor should be the homepage assert.strictEqual(page._ancestors[0].path, `${homeId.replace(':en:published', '')}`); // The second ancestor should be 'parent' assert.strictEqual(page._ancestors[1].path, `${homeId.replace(':en:published', '')}/parent`); // There should be only 1 result. assert(page); // There should be 2 ancestors. assert(page._ancestors.length === 2); }); it('should be able to find just a single page with children', async function() { const page = await apos.http.get('/api/v1/@apostrophecms/page/parent:en:published'); assert(page); assert(page.path === `${homeId.replace(':en:published', '')}/parent`); // There should be 1 ancestor assert(page._ancestors.length === 1); // The first ancestor should be the homepage assert.strictEqual(page._ancestors[0].path, `${homeId.replace(':en:published', '')}`); // There should be children assert(page._children); assert(page._children.length === 3); assert(page._children[0]._id === 'child:en:published'); assert(page._children[1]._id === 'sibling:en:published'); assert(page._children[2].slug === '/new-page'); }); it('is able to move root/parent/sibling/cousin after root/parent', async function() { const page = await apos.http.patch('/api/v1/@apostrophecms/page/cousin:en:published', { body: { _targetId: 'parent:en:published', _position: 'after' }, jar }); assert(page._id); // Is the new path correct? assert.strictEqual(page.path, `${homeId.replace(':en:published', '')}/cousin`); // Is the rank correct? const home = await apos.http.get('/api/v1/@apostrophecms/page', {}); assert(home._children); assert(home._children[1]._id === 'cousin:en:published'); }); it('is able to move root/cousin before root/parent/child', async function() { // 'Cousin' _id === 4312 // 'Child' _id === 2341 const page = await apos.http.patch('/api/v1/@apostrophecms/page/cousin:en:published', { body: { _targetId: 'child:en:published', _position: 'before' }, jar }); // Is the new path correct? assert.strictEqual(page.path, `${homeId.replace(':en:published', '')}/parent/cousin`); // Is the rank correct? assert.strictEqual(page.rank, 0); }); it('is able to move root/parent/child before root/parent/cousin using numerical position', async function() { const page = await apos.http.patch('/api/v1/@apostrophecms/page/child:en:published', { body: { _targetId: 'parent:en:published', _position: 0 }, jar }); // Is the new path correct? assert.strictEqual(page.path, `${homeId.replace(':en:published', '')}/parent/child`); // Is the rank correct? assert.strictEqual(page.rank, 0); }); it('is able to move root/parent/new-page between root/parent/cousin and root/parent/sibling using numerical position', async function() { const page = await apos.http.patch(`/api/v1/@apostrophecms/page/${newPageId}`, { body: { _targetId: 'parent:en:published', _position: 2 }, jar }); const cousin = await apos.http.get('/api/v1/@apostrophecms/page/cousin:en:published', { jar }); const sibling = await apos.http.get('/api/v1/@apostrophecms/page/sibling:en:published', { jar }); // Is the new path correct? assert.strictEqual(page.path, `${homeId.replace(':en:published', '')}/parent/${newPageId.replace(':en:published', '')}`); // Is the rank correct? assert(page.rank > cousin.rank); assert(page.rank < sibling.rank); }); it('is able to move root/neighbor as the last child of /root/parent using numerical position', async function() { const page = await apos.http.patch('/api/v1/@apostrophecms/page/neighbor:en:published', { body: { _targetId: 'parent:en:published', _position: 4 }, jar }); // `sibling` was previously the last child of `parent`. const sibling = await apos.http.get('/api/v1/@apostrophecms/page/sibling:en:published', { jar }); // Is the new path correct? assert.strictEqual(page.path, `${homeId.replace(':en:published', '')}/parent/neighbor`); // Is the rank correct? assert(page.rank > sibling.rank); }); it('allows positioning a page at its existing location (lastChild) using numerical position', async function() { const page = await apos.http.patch('/api/v1/@apostrophecms/page/neighbor:en:published', { body: { _targetId: 'parent:en:published', _position: 4 }, jar }); // `sibling` was previously the last child of `parent`. const sibling = await apos.http.get('/api/v1/@apostrophecms/page/sibling:en:published', { jar }); // Is the new path correct? assert.strictEqual(page.path, `${homeId.replace(':en:published', '')}/parent/neighbor`); // Is the rank correct? assert(page.rank > sibling.rank); }); it('is able to move root/parent/child/grandchild to the next-to-last position under `parent`', async function() { const page = await apos.http.patch('/api/v1/@apostrophecms/page/grandchild:en:published', { body: { _targetId: 'parent:en:published', _position: 4 }, jar }); // `sibling` was previously the next-to-last child of `parent`. const sibling = await apos.http.get('/api/v1/@apostrophecms/page/sibling:en:published', { jar }); const neighbor = await apos.http.get('/api/v1/@apostrophecms/page/neighbor:en:published', { jar }); // Is the new path correct? assert.strictEqual(page.path, `${homeId.replace(':en:published', '')}/parent/grandchild`); // Is the rank correct? assert(page.rank > sibling.rank); assert(page.rank < neighbor.rank); }); it('allows positioning a page at its existing location (4 of 5) using numerical position', async function() { const page = await apos.http.patch('/api/v1/@apostrophecms/page/grandchild:en:published', { body: { _targetId: 'parent:en:published', _position: 4 }, jar }); // `sibling` was previously the next-to-last child of `parent`. const sibling = await apos.http.get('/api/v1/@apostrophecms/page/sibling:en:published', { jar }); const neighbor = await apos.http.get('/api/v1/@apostrophecms/page/neighbor:en:published', { jar }); // Is the new path correct? assert.strictEqual(page.path, `${homeId.replace(':en:published', '')}/parent/grandchild`); // Is the rank correct? assert(page.rank > sibling.rank); assert(page.rank < neighbor.rank); }); it('is able to move root/parent/cousin inside root/parent/sibling', async function() { const page = await apos.http.patch('/api/v1/@apostrophecms/page/cousin:en:published', { body: { _targetId: 'sibling:en:published', _position: 'firstChild' }, jar }); // Is the new path correct? assert.strictEqual(page.path, `${homeId.replace(':en:published', '')}/parent/sibling/cousin`); // Is the rank correct? assert.strictEqual(page.rank, 0); }); it('moving /parent into /another-parent should also move /parent/sibling', async function() { await apos.http.patch('/api/v1/@apostrophecms/page/parent:en:published', { body: { _targetId: 'another-parent:en:published', _position: 'firstChild' }, jar }); const page = await apos.http.get('/api/v1/@apostrophecms/page/sibling:en:published', { jar }); // Is the grandchild's path correct? assert.strictEqual(page.path, `${homeId.replace(':en:published', '')}/another-parent/parent/sibling`); }); it('is able to make a subpage of the homepage at the last index with _position: lastChild', async function() { const body = { slug: '/third-new', visibility: 'public', type: 'test-page', title: 'Third New', _targetId: '_home', _position: 'lastChild' }; const page = await apos.http.post('/api/v1/@apostrophecms/page', { body, jar }); assert(page); assert(page.title === 'Third New'); // Is the path generally correct? assert.strictEqual(page.path, `${homeId.replace(':en:published', '')}/${page._id.replace(':en:published', '')}`); const home = await apos.http.get('/api/v1/@apostrophecms/page?children=1', { jar }); assert(home); assert(home._children); assert(home._children[3]._id === page._id); }); it('can use PUT to modify a page', async function() { const page = await apos.http.get('/api/v1/@apostrophecms/page/sibling:en:published', { jar }); assert(page); page.title = 'Changed Title'; page.color = 'blue'; await apos.http.put('/api/v1/@apostrophecms/page/sibling:en:published', { body: page, jar }); const page2 = await apos.http.get('/api/v1/@apostrophecms/page/sibling:en:published', { jar }); assert.strictEqual(page2.title, 'Changed Title'); assert.strictEqual(page2.color, 'blue'); }); it('can use PATCH to modify one property of a page', async function() { const page = await apos.http.get('/api/v1/@apostrophecms/page/sibling:en:published', { jar }); assert(page); page.title = 'Changed Title'; await apos.http.patch('/api/v1/@apostrophecms/page/sibling:en:published', { body: { title: 'New Title' }, jar }); const page2 = await apos.http.get('/api/v1/@apostrophecms/page/sibling:en:published', { jar }); assert.strictEqual(page2.title, 'New Title'); // Did not modify this assert.strictEqual(page2.color, 'blue'); }); it('can use PATCH to move a page beneath the home page with _targetId: _home', async function() { const page = await apos.http.patch('/api/v1/@apostrophecms/page/cousin:en:published', { body: { _targetId: '_home', _position: 'firstChild' }, jar }); assert(page._id); assert.strictEqual(page.path, `${homeId.replace(':en:published', '')}/${page.aposDocId}`); assert.strictEqual(page.level, 1); assert.strictEqual(page.rank, 0); }); it('can use PATCH to move a page into the archive using _archive as _targetId', async function() { let page = await apos.http.patch('/api/v1/@apostrophecms/page/cousin:en:published', { body: { _targetId: '_archive', _position: 'firstChild' }, jar }); assert(page._id); const archive = await apos.http.get('/api/v1/@apostrophecms/page/_archive?archived=1', { jar }); assert(archive); // Verify this is really working because of the _archived // shortcut assert(archive._id !== '_archive'); assert.strictEqual(page.path, `${homeId.replace(':en:published', '')}/${archive.aposDocId}/${page.aposDocId}`); assert.strictEqual(page.level, 2); assert.strictEqual(page.rank, 0); page = await apos.http.get('/api/v1/@apostrophecms/page/cousin:en:published?_edit=1&archived=1', { jar }); assert(page.archived); }); it('Can use PATCH to add a widget to an area by path', async function() { let page = await apos.http.get('/api/v1/@apostrophecms/page/sibling:en:published', { jar }); page = await apos.http.patch('/api/v1/@apostrophecms/page/sibling:en:published', { body: { $push: { 'body.items': { metaType: 'widget', type: '@apostrophecms/rich-text', content: 'This is <b>Bold</b>' } } }, jar }); page = await apos.http.get('/api/v1/@apostrophecms/page/sibling:en:published', { jar }); assert(page.body.items[0]); assert(page.body.items[0].content.match(/<b>Bold<\/b>/)); }); it('Can use PATCH to update a widget by path', async function() { let page = await apos.http.get('/api/v1/@apostrophecms/page/sibling:en:published', { jar }); page = await apos.http.patch('/api/v1/@apostrophecms/page/sibling:en:published', { body: { 'body.items.0': { metaType: 'widget', type: '@apostrophecms/rich-text', content: 'This is normal' } }, jar }); page = await apos.http.get('/api/v1/@apostrophecms/page/sibling:en:published', { jar }); assert(page.body.items[0]); assert(page.body.items[0].content.match(/normal/)); }); it('Can use PATCH to update a widget via @ syntax', async function() { let page = await apos.http.get('/api/v1/@apostrophecms/page/sibling:en:published', { jar }); const _id = `@${page.body.items[0]._id}`; page = await apos.http.patch('/api/v1/@apostrophecms/page/sibling:en:published', { body: { [_id]: { metaType: 'widget', type: '@apostrophecms/rich-text', content: 'I @ syntax' } }, jar }); page = await apos.http.get('/api/v1/@apostrophecms/page/sibling:en:published', { jar }); assert(page.body.items[0]); assert(page.body.items[0].content.match(/I @ syntax/)); }); it('Can use $position to insert a widget at the beginning of the area', async function() { let page = await apos.http.get('/api/v1/@apostrophecms/page/sibling:en:published', { jar }); page = await apos.http.patch('/api/v1/@apostrophecms/page/sibling:en:published', { body: { $push: { 'body.items': { $each: [ { metaType: 'widget', type: '@apostrophecms/rich-text', content: 'Oh in the beginning' } ], $position: 0 } } }, jar }); page = await apos.http.get('/api/v1/@apostrophecms/page/sibling:en:published', { jar }); assert(page.body.items[0]); assert(page.body.items[0].content.match(/Oh in the beginning/)); assert(page.body.items[1]); assert(page.body.items[1].content.match(/I @ syntax/)); assert(!page.body.items[2]); }); it('Can use $position to insert a widget in the middle of the area', async function() { let page = await apos.http.get('/api/v1/@apostrophecms/page/sibling:en:published', { jar }); page = await apos.http.patch('/api/v1/@apostrophecms/page/sibling:en:published', { body: { $push: { 'body.items': { $each: [ { metaType: 'widget', type: '@apostrophecms/rich-text', content: 'Why don\'t you meet me in the middle' } ], $position: 1 } } }, jar }); page = await apos.http.get('/api/v1/@apostrophecms/page/sibling:en:published', { jar }); assert(page.body.items[0]); assert(page.body.items[0].content.match(/Oh in the beginning/)); assert(page.body.items[1]); assert(page.body.items[1].content.match(/middle/)); assert(page.body.items[2]); assert(page.body.items[2].content.match(/I @ syntax/)); assert(!page.body.items[3]); }); it('Can use $before to insert a widget in the middle of the area', async function() { let page = await apos.http.get('/api/v1/@apostrophecms/page/sibling:en:published', { jar }); page = await apos.http.patch('/api/v1/@apostrophecms/page/sibling:en:published', { body: { $push: { 'body.items': { $each: [ { metaType: 'widget', type: '@apostrophecms/rich-text', content: 'before' } ], $before: page.body.items[1]._id } } }, jar }); page = await apos.http.get('/api/v1/@apostrophecms/page/sibling:en:published', { jar }); assert(page.body.items[0]); assert(page.body.items[0].content.match(/Oh in the beginning/)); assert(page.body.items[1]); assert(page.body.items[1].content.match(/before/)); assert(page.body.items[2]); assert(page.body.items[2].content.match(/middle/)); assert(page.body.items[3]); assert(page.body.items[3].content.match(/I @ syntax/)); assert(!page.body.items[4]); }); it('Can use $after to insert a widget in the middle of the area', async function() { let page = await apos.http.get('/api/v1/@apostrophecms/page/sibling:en:published', { jar }); page = await apos.http.patch('/api/v1/@apostrophecms/page/sibling:en:published', { body: { $push: { 'body.items': { $each: [ { metaType: 'widget', type: '@apostrophecms/rich-text', content: 'after' } ], $after: page.body.items[0]._id } } }, jar }); page = await apos.http.get('/api/v1/@apostrophecms/page/sibling:en:published', { jar }); assert(page.body.items[0]); assert(page.body.items[0].content.match(/Oh in the beginning/)); assert(page.body.items[1]); assert(page.body.items[1].content.match(/after/)); assert(page.body.items[2]); assert(page.body.items[2].content.match(/before/)); assert(page.body.items[3]); assert(page.body.items[3].content.match(/middle/)); assert(page.body.items[4]); assert(page.body.items[4].content.match(/I @ syntax/)); assert(!page.body.items[5]); }); let advisoryLockTestId; it('can insert a page for advisory lock testing', async function() { const body = { slug: '/advisory-test', visibility: 'public', type: 'test-page', title: 'Advisory Test' }; const page = await apos.http.post('/api/v1/@apostrophecms/page', { body, jar }); assert(page); assert(page.title === 'Advisory Test'); advisoryLockTestId = page._id; }); it('can get an advisory lock on a page while patching a property', async function() { const page = await apos.http.patch(`/api/v1/@apostrophecms/page/${advisoryLockTestId}`, { jar, body: { _advisoryLock: { tabId: 'xyz', lock: true }, title: 'Advisory Test Patched' } }); assert(page.title === 'Advisory Test Patched'); }); it('cannot get an advisory lock with a different context id', async function() { try { await apos.http.patch(`/api/v1/@apostrophecms/page/${advisoryLockTestId}`, { jar, body: { _advisoryLock: { tabId: 'pdq', lock: true } } }); assert(false); } catch (e) { assert(e.status === 409); assert(e.body.name === 'locked'); assert(e.body.data.me); } }); it('can get an advisory lock with a different context id if forcing', async function() { await apos.http.patch(`/api/v1/@apostrophecms/page/${advisoryLockTestId}`, { jar, body: { _advisoryLock: { tabId: 'pdq', lock: true, force: true } } }); }); it('can renew the advisory lock with the second context id after forcing', async function() { await apos.http.patch(`/api/v1/@apostrophecms/page/${advisoryLockTestId}`, { jar, body: { _advisoryLock: { tabId: 'pdq', lock: true } } }); }); it('can unlock the advisory lock while patching a property', async function() { const page = await apos.http.patch(`/api/v1/@apostrophecms/page/${advisoryLockTestId}`, { jar, body: { _advisoryLock: { tabId: 'pdq', lock: false }, title: 'Advisory Test Patched Again' } }); assert(page.title === 'Advisory Test Patched Again'); }); it('can relock with the first context id after unlocking', async function() { const doc = await apos.http.patch(`/api/v1/@apostrophecms/page/${advisoryLockTestId}`, { jar, body: { _advisoryLock: { tabId: 'xyz', lock: true } } }); assert(doc.title === 'Advisory Test Patched Again'); }); let diacriticsId; it('is able to make a page including diacritics', async function() { const body = { slug: '/ḑiaçritiçs-čharaćters', visibility: 'public', type: 'test-page', title: 'Ḑiaçritiçs Čharaćters', _targetId: '_home', _position: '1' }; const page = await apos.http.post('/api/v1/@apostrophecms/page', { body, jar }); assert(page); assert(page.title === 'Ḑiaçritiçs Čharaćters'); diacriticsId = page._id; // Accesses the published page. const published = await apos.http.get('/ḑiaçritiçs-čharaćters'); assert(published); }); it('can archive the diacritics page then access the draft preview', async function () { // Now only a draft preview will be available. await apos.page.archive(apos.task.getReq(), diacriticsId); try { const rendered = await apos.http.get('/ḑiaçritiçs-čharaćters', { jar }); assert(rendered.match(/Sing to me, Oh Muse\./)); } catch (error) { assert(false); } }); let jar2; it('should be able to log in as second user', async function() { jar2 = apos.http.jar(); // establish session let page = await apos.http.get('/', { jar: jar2 }); assert(page.match(/logged out/)); // Log in await apos.http.post('/api/v1/@apostrophecms/login/login', { body: { username: 'admin2', password: 'admin2', session: true }, jar: jar2 }); // Confirm login page = await apos.http.get('/', { jar: jar2 }); assert(page.match(/logged in/)); }); it('second user with a distinct tabId gets an appropriate error specifying who has the lock', async function() { try { await apos.http.patch(`/api/v1/@apostrophecms/page/${advisoryLockTestId}`, { jar: jar2, body: { _advisoryLock: { tabId: 'nbc', lock: true } } }); assert(false); } catch (e) { assert(e.status === 409); assert(e.body.name === 'locked'); assert(!e.body.data.me); assert(e.body.data.username === 'admin'); } }); it('can insert a test-page with _newInstance and additional properties', async function() { const newInstance = await apos.http.post('/api/v1/@apostrophecms/page', { body: { _newInstance: true, slug: '/page-01', type: 'test-page', title: 'Page 01' }, jar }); const inserted = await apos.http.post('/api/v1/@apostrophecms/page', { body: { ...newInstance, body: { metaType: 'area', items: [ { metaType: 'widget', type: '@apostrophecms/rich-text', id: createId(), content: '<p>This is the product key product with relationship</p>' } ] } }, jar }); const actual = { newInstance, inserted }; const expected = { newInstance: { _previewable: true, orphan: false, slug: '/page-01', title: 'Page 01', type: 'test-page', visibility: 'public' }, inserted: { _ancestors: inserted._ancestors, _create: true, _delete: true, _edit: true, _id: inserted._id, _publish: true, _url: '/page-01', aposDocId: inserted.aposDocId, aposLocale: 'en:published', aposMode: 'published', archived: false, body: { _docId: inserted.body._docId, _edit: true, _id: inserted.body._id, items: [ { _docId: inserted.body.items.at(0)._docId, _edit: true, _id: inserted.body.items.at(0)._id, aposPlaceholder: false, content: '<p>This is the product key product with relationship</p>', imageIds: [], metaType: 'widget', permalinkIds: [], type: '@apostrophecms/rich-text' } ], metaType: 'area' }, cacheInvalidatedAt: inserted.cacheInvalidatedAt, color: '', createdAt: inserted.createdAt, highSearchText: inserted.highSearchText, highSearchWords: inserted.highSearchWords, lastPublishedAt: inserted.lastPublishedAt, level: 1, lowSearchText: inserted.lowSearchText, metaType: 'doc', orphan: false, path: inserted.path, rank: inserted.rank, searchSummary: inserted.searchSummary, slug: '/page-01', title: 'Page 01', titleSortified: inserted.titleSortified, type: 'test-page', updatedAt: inserted.updatedAt, updatedBy: { _id: inserted.updatedBy._id, title: 'admin', username: 'admin' }, visibility: 'public' } }; assert.deepEqual(actual, expected); }); });