import assert from 'assert/strict'; import sinon from 'sinon'; import {PostRevisions} from '../src'; const config = { max_revisions: 10, revision_interval_ms: 1000 }; function makePostLike(data: any = {}) { return Object.assign({ id: 'fakeid', lexical: 'blah', html: 'blah', author_id: 'fakeauthorid', feature_image: data.feature_image || null, feature_image_alt: data.feature_image_alt || null, feature_image_caption: data.feature_image_caption || null, title: 'Title', reason: 'reason', post_status: 'published' }, data); } function makeRevision(data: any = {}) { return Object.assign({ post_id: 'fakeid', created_at_ts: data.created_at_ts || Date.now(), lexical: 'blah', html: 'blah', author_id: 'fakeauthorid', feature_image: data.feature_image || null, feature_image_alt: data.feature_image_alt || null, feature_image_caption: data.feature_image_caption || null, title: 'Title', reason: 'reason', post_status: 'published' }, data); } describe('PostRevisions', function () { describe('shouldGenerateRevision', function () { it('should return true if there are no revisions', function () { const postRevisions = new PostRevisions({config, model: {}}); const expected = {value: true, reason: 'initial_revision'}; const actual = postRevisions.shouldGenerateRevision(makePostLike(), []); assert.deepEqual(actual, expected); }); it('should return false if the current and previous html values are the same', function () { const postRevisions = new PostRevisions({config, model: {}}); const expected = {value: false}; const actual = postRevisions.shouldGenerateRevision(makePostLike({ lexical: 'current', html: 'blah' }), [makeRevision({ lexical: 'blah' })]); assert.deepEqual(actual, expected); }); it('should return true if forceRevision is true and the lexical has changed since the latest revision', function () { const postRevisions = new PostRevisions({config, model: {}}); const expected = {value: true, reason: 'explicit_save'}; const actual = postRevisions.shouldGenerateRevision(makePostLike({ lexical: 'blah' }), [{ lexical: 'blah2' }, { lexical: 'blah3' }].map(makeRevision), { forceRevision: true, isPublished: false }); assert.deepEqual(actual, expected); }); it('should return true if the current and previous title values are different and forceRevision is true', function () { const postRevisions = new PostRevisions({config, model: {}}); const expected = {value: true, reason: 'explicit_save'}; const actual = postRevisions.shouldGenerateRevision(makePostLike({ lexical: 'blah', html: 'blah', title: 'blah2' }), [{ lexical: 'blah', title: 'not blah' }].map(makeRevision), { forceRevision: true, isPublished: false }); assert.deepEqual(actual, expected); }); it('should return true if the current and previous feature_image values are different and forceRevision is true', function () { const postRevisions = new PostRevisions({config, model: {}}); const expected = {value: true, reason: 'explicit_save'}; const actual = postRevisions.shouldGenerateRevision(makePostLike({ lexical: 'blah', html: 'blah', title: 'blah', feature_image: 'new' }), [{ lexical: 'blah', html: 'blah', title: 'blah', feature_image: null }].map(makeRevision), { forceRevision: true, isPublished: false }); assert.deepEqual(actual, expected); }); it('should return true if post is unpublished and forceRevision is true', function () { const postRevisions = new PostRevisions({config, model: {}}); const expected = {value: true, reason: 'unpublished'}; const actual = postRevisions.shouldGenerateRevision(makePostLike({ lexical: 'blah', html: 'blah', title: 'blah2' }), [makeRevision({ lexical: 'blah' })], { isPublished: false, forceRevision: true, newStatus: 'draft', olderStatus: 'published' }); assert.deepEqual(actual, expected); }); it('should always return true if isPublished is true', function () { const postRevisions = new PostRevisions({config, model: {}}); const expected = {value: true, reason: 'published'}; const actual = postRevisions.shouldGenerateRevision(makePostLike({ lexical: 'blah', html: 'blah', title: 'blah2' }), [{ lexical: 'blah' }].map(makeRevision), { forceRevision: false, isPublished: true }); assert.deepEqual(actual, expected); }); it('should return true if the latest revision was more than the interval', function () { const postRevisions = new PostRevisions({config, model: {}}); const expected = {value: true, reason: 'background_save'}; const actual = postRevisions.shouldGenerateRevision(makePostLike({ lexical: 'blah', html: 'blah', title: 'blah' }), [{ lexical: 'blah2', created_at_ts: Date.now() - 2000 }].map(makeRevision), {}); assert.deepEqual(actual, expected); }); }); describe('getRevisions', function () { it('returns the original revisions if there is no previous', async function () { const postRevisions = new PostRevisions({config, model: {}}); const now = Date.now(); const expected = [{ lexical: 'blah', created_at_ts: now }].map(makeRevision); const actual = await postRevisions.getRevisions(makePostLike({}), [{ lexical: 'blah', created_at_ts: now }].map(makeRevision)); assert.deepEqual(actual, expected); }); it('returns the original revisions if the current and previous', async function () { const postRevisions = new PostRevisions({config, model: {}}); const now = Date.now(); const expected = [{ lexical: 'revision', created_at_ts: now }].map(makeRevision); const actual = await postRevisions.getRevisions(makePostLike({ lexical: 'blah', html: 'blah' }), [{ lexical: 'revision', created_at_ts: now }].map(makeRevision)); assert.deepEqual(actual, expected); }); it('returns one revision when there are no existing revisions', async function () { const postRevisions = new PostRevisions({config, model: {}}); const actual = await postRevisions.getRevisions(makePostLike({ id: '1', lexical: 'current', html: 'current', author_id: '123', title: 'foo bar baz' }), []); assert.equal(actual.length, 1); assert.equal(actual[0].lexical, 'current'); assert.equal(actual[0].author_id, '123'); assert.equal(actual[0].title, 'foo bar baz'); }); it('does not limit the number of revisions if under the max_revisions count', async function () { const postRevisions = new PostRevisions({ config: { max_revisions: 2, revision_interval_ms: config.revision_interval_ms }, model: {} }); const revisions = await postRevisions.getRevisions(makePostLike({ id: '1', lexical: 'current', html: 'current' }), []); const actual = await postRevisions.getRevisions(makePostLike({ id: '1', lexical: 'new', html: 'new' }), revisions, { forceRevision: true }); assert.equal(actual.length, 2); }); it('limits the number of revisions to the max_revisions count', async function () { const postRevisions = new PostRevisions({ config: { max_revisions: 1, revision_interval_ms: config.revision_interval_ms }, model: {} }); const revisions = await postRevisions.getRevisions(makePostLike({ id: '1', lexical: 'current', html: 'current' }), []); const actual = await postRevisions.getRevisions(makePostLike({ id: '1', lexical: 'new', html: 'new' }), revisions, { forceRevision: true }); assert.equal(actual.length, 1); }); }); describe('removeAuthor', function () { it('removes the provided author from post revisions', async function () { const authorId = 'abc123'; const options = { transacting: {} }; const revisions = [ { id: 'revision123', post_id: 'post123', author_id: 'author123' }, { id: 'revision456', post_id: 'post123', author_id: 'author123' }, { id: 'revision789', post_id: 'post123', author_id: 'author456' } ]; const modelStub = { findAll: sinon.stub().resolves({ toJSON: () => revisions }), bulkEdit: sinon.stub().resolves() }; const postRevisions = new PostRevisions({ config, model: modelStub }); await postRevisions.removeAuthorFromRevisions(authorId, options); assert.equal(modelStub.bulkEdit.calledOnce, true); const bulkEditArgs = modelStub.bulkEdit.getCall(0).args; assert.deepEqual(bulkEditArgs[0], ['revision123', 'revision456', 'revision789']); assert.equal(bulkEditArgs[1], 'post_revisions'); assert.deepEqual(bulkEditArgs[2], { data: { author_id: null }, column: 'id', transacting: options.transacting, throwErrors: true }); }); it('does nothing if there are no post revisions by the provided author', async function () { const modelStub = { findAll: sinon.stub().resolves({ toJSON: () => [] }), bulkEdit: sinon.stub().resolves() }; const postRevisions = new PostRevisions({ config, model: modelStub }); await postRevisions.removeAuthorFromRevisions('abc123', { transacting: {} }); assert.equal(modelStub.bulkEdit.calledOnce, false); }); }); });