diff --git a/core/frontend/helpers/get.js b/core/frontend/helpers/get.js index 548d72eeb6..5a4bdb7124 100644 --- a/core/frontend/helpers/get.js +++ b/core/frontend/helpers/get.js @@ -156,6 +156,7 @@ module.exports = function get(resource, options) { // Parse the options we're going to pass to the API apiOptions = parseOptions(ghostGlobals, this, apiOptions); + apiOptions.context = {member: data.member}; // @TODO: https://github.com/TryGhost/Ghost/issues/10548 return controller[action](apiOptions).then(function success(result) { diff --git a/core/frontend/helpers/prev_post.js b/core/frontend/helpers/prev_post.js index 4bfeac403d..70c8b7352d 100644 --- a/core/frontend/helpers/prev_post.js +++ b/core/frontend/helpers/prev_post.js @@ -32,7 +32,8 @@ const buildApiOptions = function buildApiOptions(options, post) { limit: 1, // This line deliberately uses double quotes because GQL cannot handle either double quotes // or escaped singles, see TryGhost/GQL#34 - filter: "slug:-" + slug + "+published_at:" + op + "'" + publishedAt + "'" // eslint-disable-line quotes + filter: "slug:-" + slug + "+published_at:" + op + "'" + publishedAt + "'", // eslint-disable-line quotes + context: {member: options.data.member} }; if (get(options, 'hash.in')) { @@ -48,6 +49,11 @@ const buildApiOptions = function buildApiOptions(options, post) { return apiOptions; }; +/** + * @param {*} options + * @param {*} data + * @returns {Promise} + */ const fetch = function fetch(options, data) { const self = this; const apiOptions = buildApiOptions(options, this); @@ -77,6 +83,10 @@ const fetch = function fetch(options, data) { // If prevNext method is called without valid post data then we must return a promise, if there is valid post data // then the promise is handled in the api call. +/** + * @param {*} options + * @returns {Promise} + */ module.exports = function prevNext(options) { options = options || {}; diff --git a/test/unit/frontend/helpers/get.test.js b/test/unit/frontend/helpers/get.test.js index ecc4227c98..26e87ee9bd 100644 --- a/test/unit/frontend/helpers/get.test.js +++ b/test/unit/frontend/helpers/get.test.js @@ -295,4 +295,33 @@ describe('{{#get}} helper', function () { browseStub.firstCall.args[0].limit.should.eql(2); }); }); + + describe('auth', function () { + /** + * @type sinon.SinonStub + */ + let browseStub; + let member; + + beforeEach(function () { + browseStub = sinon.stub().resolves(); + member = {uuid: 'test'}; + + sinon.stub(api, 'postsPublic').get(() => { + return { + browse: browseStub + }; + }); + }); + + it('should pass the member context', async function () { + locals = {root: {_locals: {apiVersion: API_VERSION}}, member}; + await get.call( + {}, + 'posts', + {hash: {}, data: locals, fn: fn, inverse: inverse} + ); + browseStub.firstCall.args[0].context.member.should.eql(member); + }); + }); }); diff --git a/test/unit/frontend/helpers/next_post.test.js b/test/unit/frontend/helpers/next_post.test.js index f4ccae05ad..a1d4f2dae8 100644 --- a/test/unit/frontend/helpers/next_post.test.js +++ b/test/unit/frontend/helpers/next_post.test.js @@ -4,6 +4,7 @@ const Promise = require('bluebird'); const markdownToMobiledoc = require('../../../utils/fixtures/data-generator').markdownToMobiledoc; const next_post = require('../../../../core/frontend/helpers/prev_post'); const api = require('../../../../core/server/api'); +const should = require('should'); describe('{{next_post}} helper', function () { const apiVersion = 'canary'; @@ -42,12 +43,12 @@ describe('{{next_post}} helper', function () { }); }); - it('shows \'if\' template with next post data', function (done) { + it('shows \'if\' template with next post data', async function () { const fn = sinon.spy(); const inverse = sinon.spy(); const optionsData = {name: 'next_post', data: locals, fn: fn, inverse: inverse}; - next_post + await next_post .call({ html: 'content', status: 'published', @@ -56,20 +57,15 @@ describe('{{next_post}} helper', function () { slug: 'current', published_at: new Date(0), url: '/current/' - }, optionsData) - .then(function () { - fn.calledOnce.should.be.true(); - inverse.calledOnce.should.be.false(); + }, optionsData); + fn.calledOnce.should.be.true(); + inverse.calledOnce.should.be.false(); - fn.firstCall.args.should.have.lengthOf(2); - fn.firstCall.args[0].should.have.properties('slug', 'title'); - fn.firstCall.args[1].should.be.an.Object().and.have.property('data'); - browsePostsStub.calledOnce.should.be.true(); - browsePostsStub.firstCall.args[0].include.should.eql('author,authors,tags,tiers'); - - done(); - }) - .catch(done); + fn.firstCall.args.should.have.lengthOf(2); + fn.firstCall.args[0].should.have.properties('slug', 'title'); + fn.firstCall.args[1].should.be.an.Object().and.have.property('data'); + browsePostsStub.calledOnce.should.be.true(); + browsePostsStub.firstCall.args[0].include.should.eql('author,authors,tags,tiers'); }); }); @@ -84,12 +80,12 @@ describe('{{next_post}} helper', function () { }); }); - it('shows \'else\' template', function (done) { + it('shows \'else\' template', async function () { const fn = sinon.spy(); const inverse = sinon.spy(); const optionsData = {name: 'next_post', data: locals, fn: fn, inverse: inverse}; - next_post + await next_post .call({ html: 'content', status: 'published', @@ -98,18 +94,13 @@ describe('{{next_post}} helper', function () { slug: 'current', published_at: new Date(0), url: '/current/' - }, optionsData) - .then(function () { - fn.called.should.be.false(); - inverse.called.should.be.true(); + }, optionsData); + fn.called.should.be.false(); + inverse.called.should.be.true(); - inverse.firstCall.args.should.have.lengthOf(2); - inverse.firstCall.args[0].should.have.properties('slug', 'title'); - inverse.firstCall.args[1].should.be.an.Object().and.have.property('data'); - - done(); - }) - .catch(done); + inverse.firstCall.args.should.have.lengthOf(2); + inverse.firstCall.args[0].should.have.properties('slug', 'title'); + inverse.firstCall.args[1].should.be.an.Object().and.have.property('data'); }); }); @@ -122,21 +113,16 @@ describe('{{next_post}} helper', function () { }); }); - it('shows \'else\' template', function (done) { + it('shows \'else\' template', async function () { const fn = sinon.spy(); const inverse = sinon.spy(); const optionsData = {name: 'next_post', data: locals, fn: fn, inverse: inverse}; - next_post - .call({}, optionsData) - .then(function () { - fn.called.should.be.false(); - inverse.called.should.be.true(); - browsePostsStub.called.should.be.false(); - - done(); - }) - .catch(done); + await next_post + .call({}, optionsData); + fn.called.should.be.false(); + inverse.called.should.be.true(); + browsePostsStub.called.should.be.false(); }); }); @@ -158,12 +144,12 @@ describe('{{next_post}} helper', function () { }); }); - it('shows \'else\' template', function (done) { + it('shows \'else\' template', async function () { const fn = sinon.spy(); const inverse = sinon.spy(); const optionsData = {name: 'next_post', data: locals, fn: fn, inverse: inverse}; - next_post + await next_post .call({ html: 'content', status: 'published', @@ -173,14 +159,9 @@ describe('{{next_post}} helper', function () { published_at: new Date(0), url: '/current/', page: true - }, optionsData) - .then(function () { - fn.called.should.be.false(); - inverse.called.should.be.true(); - - done(); - }) - .catch(done); + }, optionsData); + fn.called.should.be.false(); + inverse.called.should.be.true(); }); }); @@ -202,12 +183,12 @@ describe('{{next_post}} helper', function () { }); }); - it('shows \'else\' template', function (done) { + it('shows \'else\' template', async function () { const fn = sinon.spy(); const inverse = sinon.spy(); const optionsData = {name: 'next_post', data: locals, fn: fn, inverse: inverse}; - next_post + await next_post .call({ html: 'content', status: 'draft', @@ -216,14 +197,9 @@ describe('{{next_post}} helper', function () { slug: 'current', created_at: new Date(0), url: '/current/' - }, optionsData) - .then(function () { - fn.called.should.be.false(); - inverse.called.should.be.true(); - - done(); - }) - .catch(done); + }, optionsData); + fn.called.should.be.false(); + inverse.called.should.be.true(); }); }); @@ -238,12 +214,12 @@ describe('{{next_post}} helper', function () { }); }); - it('shows \'if\' template with prev post data with primary_tag set', function (done) { + it('shows \'if\' template with prev post data with primary_tag set', async function () { const fn = sinon.spy(); const inverse = sinon.spy(); const optionsData = {name: 'next_post', data: locals, fn: fn, inverse: inverse, hash: {in: 'primary_tag'}}; - next_post + await next_post .call({ html: 'content', status: 'published', @@ -253,29 +229,25 @@ describe('{{next_post}} helper', function () { published_at: new Date(0), primary_tag: {slug: 'test'}, url: '/current/' - }, optionsData) - .then(function () { - fn.calledOnce.should.be.true(); - inverse.calledOnce.should.be.false(); + }, optionsData); - fn.firstCall.args.should.have.lengthOf(2); - fn.firstCall.args[0].should.have.properties('slug', 'title'); - fn.firstCall.args[1].should.be.an.Object().and.have.property('data'); - browsePostsStub.calledOnce.should.be.true(); - browsePostsStub.firstCall.args[0].include.should.eql('author,authors,tags,tiers'); - browsePostsStub.firstCall.args[0].filter.should.match(/\+primary_tag:test/); + fn.calledOnce.should.be.true(); + inverse.calledOnce.should.be.false(); - done(); - }) - .catch(done); + fn.firstCall.args.should.have.lengthOf(2); + fn.firstCall.args[0].should.have.properties('slug', 'title'); + fn.firstCall.args[1].should.be.an.Object().and.have.property('data'); + browsePostsStub.calledOnce.should.be.true(); + browsePostsStub.firstCall.args[0].include.should.eql('author,authors,tags,tiers'); + browsePostsStub.firstCall.args[0].filter.should.match(/\+primary_tag:test/); }); - it('shows \'if\' template with prev post data with primary_author set', function (done) { + it('shows \'if\' template with prev post data with primary_author set', async function () { const fn = sinon.spy(); const inverse = sinon.spy(); const optionsData = {name: 'next_post', data: locals, fn: fn, inverse: inverse, hash: {in: 'primary_author'}}; - next_post + await next_post .call({ html: 'content', status: 'published', @@ -285,29 +257,25 @@ describe('{{next_post}} helper', function () { published_at: new Date(0), primary_author: {slug: 'hans'}, url: '/current/' - }, optionsData) - .then(function () { - fn.calledOnce.should.be.true(); - inverse.calledOnce.should.be.false(); + }, optionsData); - fn.firstCall.args.should.have.lengthOf(2); - fn.firstCall.args[0].should.have.properties('slug', 'title'); - fn.firstCall.args[1].should.be.an.Object().and.have.property('data'); - browsePostsStub.calledOnce.should.be.true(); - browsePostsStub.firstCall.args[0].include.should.eql('author,authors,tags,tiers'); - browsePostsStub.firstCall.args[0].filter.should.match(/\+primary_author:hans/); + fn.calledOnce.should.be.true(); + inverse.calledOnce.should.be.false(); - done(); - }) - .catch(done); + fn.firstCall.args.should.have.lengthOf(2); + fn.firstCall.args[0].should.have.properties('slug', 'title'); + fn.firstCall.args[1].should.be.an.Object().and.have.property('data'); + browsePostsStub.calledOnce.should.be.true(); + browsePostsStub.firstCall.args[0].include.should.eql('author,authors,tags,tiers'); + browsePostsStub.firstCall.args[0].filter.should.match(/\+primary_author:hans/); }); - it('shows \'if\' template with prev post data with author set', function (done) { + it('shows \'if\' template with prev post data with author set', async function () { const fn = sinon.spy(); const inverse = sinon.spy(); const optionsData = {name: 'next_post', data: locals, fn: fn, inverse: inverse, hash: {in: 'author'}}; - next_post + await next_post .call({ html: 'content', status: 'published', @@ -317,29 +285,25 @@ describe('{{next_post}} helper', function () { published_at: new Date(0), author: {slug: 'author-name'}, url: '/current/' - }, optionsData) - .then(function () { - fn.calledOnce.should.be.true(); - inverse.calledOnce.should.be.false(); + }, optionsData); - fn.firstCall.args.should.have.lengthOf(2); - fn.firstCall.args[0].should.have.properties('slug', 'title'); - fn.firstCall.args[1].should.be.an.Object().and.have.property('data'); - browsePostsStub.calledOnce.should.be.true(); - browsePostsStub.firstCall.args[0].include.should.eql('author,authors,tags,tiers'); - browsePostsStub.firstCall.args[0].filter.should.match(/\+author:author-name/); + fn.calledOnce.should.be.true(); + inverse.calledOnce.should.be.false(); - done(); - }) - .catch(done); + fn.firstCall.args.should.have.lengthOf(2); + fn.firstCall.args[0].should.have.properties('slug', 'title'); + fn.firstCall.args[1].should.be.an.Object().and.have.property('data'); + browsePostsStub.calledOnce.should.be.true(); + browsePostsStub.firstCall.args[0].include.should.eql('author,authors,tags,tiers'); + browsePostsStub.firstCall.args[0].filter.should.match(/\+author:author-name/); }); - it('shows \'if\' template with prev post data & ignores in author if author isnt present', function (done) { + it('shows \'if\' template with prev post data & ignores in author if author isnt present', async function () { const fn = sinon.spy(); const inverse = sinon.spy(); const optionsData = {name: 'next_post', data: locals, fn: fn, inverse: inverse, hash: {in: 'author'}}; - next_post + await next_post .call({ html: 'content', status: 'published', @@ -348,29 +312,25 @@ describe('{{next_post}} helper', function () { slug: 'current', published_at: new Date(0), url: '/current/' - }, optionsData) - .then(function () { - fn.calledOnce.should.be.true(); - inverse.calledOnce.should.be.false(); + }, optionsData); - fn.firstCall.args.should.have.lengthOf(2); - fn.firstCall.args[0].should.have.properties('slug', 'title'); - fn.firstCall.args[1].should.be.an.Object().and.have.property('data'); - browsePostsStub.calledOnce.should.be.true(); - browsePostsStub.firstCall.args[0].include.should.eql('author,authors,tags,tiers'); - browsePostsStub.firstCall.args[0].filter.should.not.match(/\+author:/); + fn.calledOnce.should.be.true(); + inverse.calledOnce.should.be.false(); - done(); - }) - .catch(done); + fn.firstCall.args.should.have.lengthOf(2); + fn.firstCall.args[0].should.have.properties('slug', 'title'); + fn.firstCall.args[1].should.be.an.Object().and.have.property('data'); + browsePostsStub.calledOnce.should.be.true(); + browsePostsStub.firstCall.args[0].include.should.eql('author,authors,tags,tiers'); + browsePostsStub.firstCall.args[0].filter.should.not.match(/\+author:/); }); - it('shows \'if\' template with prev post data & ignores unknown in value', function (done) { + it('shows \'if\' template with prev post data & ignores unknown in value', async function () { const fn = sinon.spy(); const inverse = sinon.spy(); const optionsData = {name: 'next_post', data: locals, fn: fn, inverse: inverse, hash: {in: 'magic'}}; - next_post + await next_post .call({ html: 'content', status: 'published', @@ -380,21 +340,17 @@ describe('{{next_post}} helper', function () { published_at: new Date(0), author: {slug: 'author-name'}, url: '/current/' - }, optionsData) - .then(function () { - fn.calledOnce.should.be.true(); - inverse.calledOnce.should.be.false(); + }, optionsData); - fn.firstCall.args.should.have.lengthOf(2); - fn.firstCall.args[0].should.have.properties('slug', 'title'); - fn.firstCall.args[1].should.be.an.Object().and.have.property('data'); - browsePostsStub.calledOnce.should.be.true(); - browsePostsStub.firstCall.args[0].include.should.eql('author,authors,tags,tiers'); - browsePostsStub.firstCall.args[0].filter.should.not.match(/\+magic/); + fn.calledOnce.should.be.true(); + inverse.calledOnce.should.be.false(); - done(); - }) - .catch(done); + fn.firstCall.args.should.have.lengthOf(2); + fn.firstCall.args[0].should.have.properties('slug', 'title'); + fn.firstCall.args[1].should.be.an.Object().and.have.property('data'); + browsePostsStub.calledOnce.should.be.true(); + browsePostsStub.firstCall.args[0].include.should.eql('author,authors,tags,tiers'); + browsePostsStub.firstCall.args[0].filter.should.not.match(/\+magic/); }); }); @@ -405,12 +361,12 @@ describe('{{next_post}} helper', function () { }); }); - it('should handle error from the API', function (done) { + it('should handle error from the API', async function () { const fn = sinon.spy(); const inverse = sinon.spy(); const optionsData = {name: 'next_post', data: locals, fn: fn, inverse: inverse}; - next_post + await next_post .call({ html: 'content', status: 'published', @@ -419,37 +375,80 @@ describe('{{next_post}} helper', function () { slug: 'current', published_at: new Date(0), url: '/current/' - }, optionsData) - .then(function () { - fn.called.should.be.false(); - inverse.calledOnce.should.be.true(); + }, optionsData); - inverse.firstCall.args[1].should.be.an.Object().and.have.property('data'); - inverse.firstCall.args[1].data.should.be.an.Object().and.have.property('error'); - inverse.firstCall.args[1].data.error.should.match(/^Something wasn't found/); + fn.called.should.be.false(); + inverse.calledOnce.should.be.true(); - done(); - }) - .catch(done); + inverse.firstCall.args[1].should.be.an.Object().and.have.property('data'); + inverse.firstCall.args[1].data.should.be.an.Object().and.have.property('error'); + inverse.firstCall.args[1].data.error.should.match(/^Something wasn't found/); }); - it('should show warning for call without any options', function (done) { + it('should show warning for call without any options', async function () { const fn = sinon.spy(); const inverse = sinon.spy(); const optionsData = {name: 'next_post', data: {root: {}}}; - next_post + await next_post .call( {}, optionsData - ) - .then(function () { - fn.called.should.be.false(); - inverse.called.should.be.false(); + ); - done(); - }) - .catch(done); + fn.called.should.be.false(); + inverse.called.should.be.false(); + }); + }); + + describe('auth', function () { + let member; + + beforeEach(function () { + member = {uuid: 'test'}; + browsePostsStub = sinon.stub().callsFake(function (options) { + return Promise.resolve({ + posts: [{slug: '/next/', title: 'post 3'}] + }); + }); + locals = { + root: { + _locals: { + apiVersion: apiVersion + }, + context: ['post'] + }, + member + }; + }); + + it('should pass the member context', async function () { + const fn = sinon.spy(); + const inverse = sinon.spy(); + const optionsData = {name: 'next_post', data: locals, fn: fn, inverse: inverse}; + + await next_post + .call({ + html: 'content', + status: 'published', + mobiledoc: markdownToMobiledoc('ff'), + title: 'post2', + slug: 'current', + published_at: new Date(0), + url: '/current/' + }, optionsData); + + fn.calledOnce.should.be.true(); + inverse.calledOnce.should.be.false(); + + fn.firstCall.args.should.have.lengthOf(2); + fn.firstCall.args[0].should.have.properties('slug', 'title'); + fn.firstCall.args[1].should.be.an.Object().and.have.property('data'); + browsePostsStub.calledOnce.should.be.true(); + browsePostsStub.firstCall.args[0].include.should.eql('author,authors,tags,tiers'); + + // Check context passed + browsePostsStub.firstCall.args[0].context.member.should.eql(member); }); }); }); diff --git a/test/unit/frontend/helpers/prev_post.test.js b/test/unit/frontend/helpers/prev_post.test.js index 82be1d7e6a..bc8f666a48 100644 --- a/test/unit/frontend/helpers/prev_post.test.js +++ b/test/unit/frontend/helpers/prev_post.test.js @@ -4,6 +4,7 @@ const Promise = require('bluebird'); const markdownToMobiledoc = require('../../../utils/fixtures/data-generator').markdownToMobiledoc; const prev_post = require('../../../../core/frontend/helpers/prev_post'); const api = require('../../../../core/server/api'); +const should = require('should'); describe('{{prev_post}} helper', function () { const apiVersion = 'canary'; @@ -42,12 +43,12 @@ describe('{{prev_post}} helper', function () { }); }); - it('shows \'if\' template with previous post data', function (done) { + it('shows \'if\' template with previous post data', async function () { const fn = sinon.spy(); const inverse = sinon.spy(); const optionsData = {name: 'prev_post', data: locals, fn: fn, inverse: inverse}; - prev_post + await prev_post .call({ html: 'content', status: 'published', @@ -56,20 +57,16 @@ describe('{{prev_post}} helper', function () { slug: 'current', published_at: new Date(0), url: '/current/' - }, optionsData) - .then(function () { - fn.calledOnce.should.be.true(); - inverse.calledOnce.should.be.false(); + }, optionsData); - fn.firstCall.args.should.have.lengthOf(2); - fn.firstCall.args[0].should.have.properties('slug', 'title'); - fn.firstCall.args[1].should.be.an.Object().and.have.property('data'); - browsePostsStub.calledOnce.should.be.true(); - browsePostsStub.firstCall.args[0].include.should.eql('author,authors,tags,tiers'); + fn.calledOnce.should.be.true(); + inverse.calledOnce.should.be.false(); - done(); - }) - .catch(done); + fn.firstCall.args.should.have.lengthOf(2); + fn.firstCall.args[0].should.have.properties('slug', 'title'); + fn.firstCall.args[1].should.be.an.Object().and.have.property('data'); + browsePostsStub.calledOnce.should.be.true(); + browsePostsStub.firstCall.args[0].include.should.eql('author,authors,tags,tiers'); }); }); @@ -82,12 +79,12 @@ describe('{{prev_post}} helper', function () { }); }); - it('shows \'else\' template', function (done) { + it('shows \'else\' template', async function () { const fn = sinon.spy(); const inverse = sinon.spy(); const optionsData = {name: 'prev_post', data: locals, fn: fn, inverse: inverse}; - prev_post + await prev_post .call({ html: 'content', status: 'published', @@ -96,18 +93,14 @@ describe('{{prev_post}} helper', function () { slug: 'current', published_at: new Date(0), url: '/current/' - }, optionsData) - .then(function () { - fn.called.should.be.false(); - inverse.called.should.be.true(); + }, optionsData); - inverse.firstCall.args.should.have.lengthOf(2); - inverse.firstCall.args[0].should.have.properties('slug', 'title'); - inverse.firstCall.args[1].should.be.an.Object().and.have.property('data'); + fn.called.should.be.false(); + inverse.called.should.be.true(); - done(); - }) - .catch(done); + inverse.firstCall.args.should.have.lengthOf(2); + inverse.firstCall.args[0].should.have.properties('slug', 'title'); + inverse.firstCall.args[1].should.be.an.Object().and.have.property('data'); }); }); @@ -120,21 +113,17 @@ describe('{{prev_post}} helper', function () { }); }); - it('shows \'else\' template', function (done) { + it('shows \'else\' template', async function () { const fn = sinon.spy(); const inverse = sinon.spy(); const optionsData = {name: 'prev_post', data: locals, fn: fn, inverse: inverse}; - prev_post - .call({}, optionsData) - .then(function () { - fn.called.should.be.false(); - inverse.called.should.be.true(); - browsePostsStub.called.should.be.false(); + await prev_post + .call({}, optionsData); - done(); - }) - .catch(done); + fn.called.should.be.false(); + inverse.called.should.be.true(); + browsePostsStub.called.should.be.false(); }); }); @@ -156,12 +145,12 @@ describe('{{prev_post}} helper', function () { }); }); - it('shows \'else\' template', function (done) { + it('shows \'else\' template', async function () { const fn = sinon.spy(); const inverse = sinon.spy(); const optionsData = {name: 'prev_post', data: locals, fn: fn, inverse: inverse}; - prev_post + await prev_post .call({ html: 'content', status: 'published', @@ -171,14 +160,10 @@ describe('{{prev_post}} helper', function () { published_at: new Date(0), url: '/current/', page: true - }, optionsData) - .then(function () { - fn.called.should.be.false(); - inverse.called.should.be.true(); + }, optionsData); - done(); - }) - .catch(done); + fn.called.should.be.false(); + inverse.called.should.be.true(); }); }); @@ -200,12 +185,12 @@ describe('{{prev_post}} helper', function () { }); }); - it('shows \'else\' template', function (done) { + it('shows \'else\' template', async function () { const fn = sinon.spy(); const inverse = sinon.spy(); const optionsData = {name: 'prev_post', data: locals, fn: fn, inverse: inverse}; - prev_post + await prev_post .call({ html: 'content', status: 'draft', @@ -214,14 +199,10 @@ describe('{{prev_post}} helper', function () { slug: 'current', created_at: new Date(0), url: '/current/' - }, optionsData) - .then(function () { - fn.called.should.be.false(); - inverse.called.should.be.true(); + }, optionsData); - done(); - }) - .catch(done); + fn.called.should.be.false(); + inverse.called.should.be.true(); }); }); @@ -236,12 +217,12 @@ describe('{{prev_post}} helper', function () { }); }); - it('shows \'if\' template with prev post data with primary_tag set', function (done) { + it('shows \'if\' template with prev post data with primary_tag set', async function () { const fn = sinon.spy(); const inverse = sinon.spy(); const optionsData = {name: 'prev_post', data: locals, fn: fn, inverse: inverse, hash: {in: 'primary_tag'}}; - prev_post + await prev_post .call({ html: 'content', status: 'published', @@ -251,29 +232,25 @@ describe('{{prev_post}} helper', function () { published_at: new Date(0), primary_tag: {slug: 'test'}, url: '/current/' - }, optionsData) - .then(function () { - fn.calledOnce.should.be.true(); - inverse.calledOnce.should.be.false(); + }, optionsData); - fn.firstCall.args.should.have.lengthOf(2); - fn.firstCall.args[0].should.have.properties('slug', 'title'); - fn.firstCall.args[1].should.be.an.Object().and.have.property('data'); - browsePostsStub.calledOnce.should.be.true(); - browsePostsStub.firstCall.args[0].include.should.eql('author,authors,tags,tiers'); - browsePostsStub.firstCall.args[0].filter.should.match(/\+primary_tag:test/); + fn.calledOnce.should.be.true(); + inverse.calledOnce.should.be.false(); - done(); - }) - .catch(done); + fn.firstCall.args.should.have.lengthOf(2); + fn.firstCall.args[0].should.have.properties('slug', 'title'); + fn.firstCall.args[1].should.be.an.Object().and.have.property('data'); + browsePostsStub.calledOnce.should.be.true(); + browsePostsStub.firstCall.args[0].include.should.eql('author,authors,tags,tiers'); + browsePostsStub.firstCall.args[0].filter.should.match(/\+primary_tag:test/); }); - it('shows \'if\' template with prev post data with primary_author set', function (done) { + it('shows \'if\' template with prev post data with primary_author set', async function () { const fn = sinon.spy(); const inverse = sinon.spy(); const optionsData = {name: 'prev_post', data: locals, fn: fn, inverse: inverse, hash: {in: 'primary_author'}}; - prev_post + await prev_post .call({ html: 'content', status: 'published', @@ -283,29 +260,25 @@ describe('{{prev_post}} helper', function () { published_at: new Date(0), primary_author: {slug: 'hans'}, url: '/current/' - }, optionsData) - .then(function () { - fn.calledOnce.should.be.true(); - inverse.calledOnce.should.be.false(); + }, optionsData); - fn.firstCall.args.should.have.lengthOf(2); - fn.firstCall.args[0].should.have.properties('slug', 'title'); - fn.firstCall.args[1].should.be.an.Object().and.have.property('data'); - browsePostsStub.calledOnce.should.be.true(); - browsePostsStub.firstCall.args[0].include.should.eql('author,authors,tags,tiers'); - browsePostsStub.firstCall.args[0].filter.should.match(/\+primary_author:hans/); + fn.calledOnce.should.be.true(); + inverse.calledOnce.should.be.false(); - done(); - }) - .catch(done); + fn.firstCall.args.should.have.lengthOf(2); + fn.firstCall.args[0].should.have.properties('slug', 'title'); + fn.firstCall.args[1].should.be.an.Object().and.have.property('data'); + browsePostsStub.calledOnce.should.be.true(); + browsePostsStub.firstCall.args[0].include.should.eql('author,authors,tags,tiers'); + browsePostsStub.firstCall.args[0].filter.should.match(/\+primary_author:hans/); }); - it('shows \'if\' template with prev post data with author set', function (done) { + it('shows \'if\' template with prev post data with author set', async function () { const fn = sinon.spy(); const inverse = sinon.spy(); const optionsData = {name: 'prev_post', data: locals, fn: fn, inverse: inverse, hash: {in: 'author'}}; - prev_post + await prev_post .call({ html: 'content', status: 'published', @@ -315,29 +288,25 @@ describe('{{prev_post}} helper', function () { published_at: new Date(0), author: {slug: 'author-name'}, url: '/current/' - }, optionsData) - .then(function () { - fn.calledOnce.should.be.true(); - inverse.calledOnce.should.be.false(); + }, optionsData); - fn.firstCall.args.should.have.lengthOf(2); - fn.firstCall.args[0].should.have.properties('slug', 'title'); - fn.firstCall.args[1].should.be.an.Object().and.have.property('data'); - browsePostsStub.calledOnce.should.be.true(); - browsePostsStub.firstCall.args[0].include.should.eql('author,authors,tags,tiers'); - browsePostsStub.firstCall.args[0].filter.should.match(/\+author:author-name/); + fn.calledOnce.should.be.true(); + inverse.calledOnce.should.be.false(); - done(); - }) - .catch(done); + fn.firstCall.args.should.have.lengthOf(2); + fn.firstCall.args[0].should.have.properties('slug', 'title'); + fn.firstCall.args[1].should.be.an.Object().and.have.property('data'); + browsePostsStub.calledOnce.should.be.true(); + browsePostsStub.firstCall.args[0].include.should.eql('author,authors,tags,tiers'); + browsePostsStub.firstCall.args[0].filter.should.match(/\+author:author-name/); }); - it('shows \'if\' template with prev post data & ignores in author if author isnt present', function (done) { + it('shows \'if\' template with prev post data & ignores in author if author isnt present', async function () { const fn = sinon.spy(); const inverse = sinon.spy(); const optionsData = {name: 'prev_post', data: locals, fn: fn, inverse: inverse, hash: {in: 'author'}}; - prev_post + await prev_post .call({ html: 'content', status: 'published', @@ -346,29 +315,25 @@ describe('{{prev_post}} helper', function () { slug: 'current', published_at: new Date(0), url: '/current/' - }, optionsData) - .then(function () { - fn.calledOnce.should.be.true(); - inverse.calledOnce.should.be.false(); + }, optionsData); - fn.firstCall.args.should.have.lengthOf(2); - fn.firstCall.args[0].should.have.properties('slug', 'title'); - fn.firstCall.args[1].should.be.an.Object().and.have.property('data'); - browsePostsStub.calledOnce.should.be.true(); - browsePostsStub.firstCall.args[0].include.should.eql('author,authors,tags,tiers'); - browsePostsStub.firstCall.args[0].filter.should.not.match(/\+author:/); + fn.calledOnce.should.be.true(); + inverse.calledOnce.should.be.false(); - done(); - }) - .catch(done); + fn.firstCall.args.should.have.lengthOf(2); + fn.firstCall.args[0].should.have.properties('slug', 'title'); + fn.firstCall.args[1].should.be.an.Object().and.have.property('data'); + browsePostsStub.calledOnce.should.be.true(); + browsePostsStub.firstCall.args[0].include.should.eql('author,authors,tags,tiers'); + browsePostsStub.firstCall.args[0].filter.should.not.match(/\+author:/); }); - it('shows \'if\' template with prev post data & ignores unknown in value', function (done) { + it('shows \'if\' template with prev post data & ignores unknown in value', async function () { const fn = sinon.spy(); const inverse = sinon.spy(); const optionsData = {name: 'prev_post', data: locals, fn: fn, inverse: inverse, hash: {in: 'magic'}}; - prev_post + await prev_post .call({ html: 'content', status: 'published', @@ -378,21 +343,17 @@ describe('{{prev_post}} helper', function () { published_at: new Date(0), author: {slug: 'author-name'}, url: '/current/' - }, optionsData) - .then(function () { - fn.calledOnce.should.be.true(); - inverse.calledOnce.should.be.false(); + }, optionsData); - fn.firstCall.args.should.have.lengthOf(2); - fn.firstCall.args[0].should.have.properties('slug', 'title'); - fn.firstCall.args[1].should.be.an.Object().and.have.property('data'); - browsePostsStub.calledOnce.should.be.true(); - browsePostsStub.firstCall.args[0].include.should.eql('author,authors,tags,tiers'); - browsePostsStub.firstCall.args[0].filter.should.not.match(/\+magic/); + fn.calledOnce.should.be.true(); + inverse.calledOnce.should.be.false(); - done(); - }) - .catch(done); + fn.firstCall.args.should.have.lengthOf(2); + fn.firstCall.args[0].should.have.properties('slug', 'title'); + fn.firstCall.args[1].should.be.an.Object().and.have.property('data'); + browsePostsStub.calledOnce.should.be.true(); + browsePostsStub.firstCall.args[0].include.should.eql('author,authors,tags,tiers'); + browsePostsStub.firstCall.args[0].filter.should.not.match(/\+magic/); }); }); @@ -403,12 +364,12 @@ describe('{{prev_post}} helper', function () { }); }); - it('should handle error from the API', function (done) { + it('should handle error from the API', async function () { const fn = sinon.spy(); const inverse = sinon.spy(); const optionsData = {name: 'prev_post', data: locals, fn: fn, inverse: inverse}; - prev_post + await prev_post .call({ html: 'content', status: 'published', @@ -417,37 +378,80 @@ describe('{{prev_post}} helper', function () { slug: 'current', published_at: new Date(0), url: '/current/' - }, optionsData) - .then(function () { - fn.called.should.be.false(); - inverse.calledOnce.should.be.true(); + }, optionsData); - inverse.firstCall.args[1].should.be.an.Object().and.have.property('data'); - inverse.firstCall.args[1].data.should.be.an.Object().and.have.property('error'); - inverse.firstCall.args[1].data.error.should.match(/^Something wasn't found/); + fn.called.should.be.false(); + inverse.calledOnce.should.be.true(); - done(); - }) - .catch(done); + inverse.firstCall.args[1].should.be.an.Object().and.have.property('data'); + inverse.firstCall.args[1].data.should.be.an.Object().and.have.property('error'); + inverse.firstCall.args[1].data.error.should.match(/^Something wasn't found/); }); - it('should show warning for call without any options', function (done) { + it('should show warning for call without any options', async function () { const fn = sinon.spy(); const inverse = sinon.spy(); const optionsData = {name: 'prev_post', data: {root: {}}}; - prev_post + await prev_post .call( {}, optionsData - ) - .then(function () { - fn.called.should.be.false(); - inverse.called.should.be.false(); + ); - done(); - }) - .catch(done); + fn.called.should.be.false(); + inverse.called.should.be.false(); + }); + }); + + describe('auth', function () { + let member; + + beforeEach(function () { + member = {uuid: 'test'}; + browsePostsStub = sinon.stub().callsFake(function (options) { + return Promise.resolve({ + posts: [{slug: '/next/', title: 'post 3'}] + }); + }); + locals = { + root: { + _locals: { + apiVersion: apiVersion + }, + context: ['post'] + }, + member + }; + }); + + it('should pass the member context', async function () { + const fn = sinon.spy(); + const inverse = sinon.spy(); + const optionsData = {name: 'prev_post', data: locals, fn: fn, inverse: inverse}; + + await prev_post + .call({ + html: 'content', + status: 'published', + mobiledoc: markdownToMobiledoc('ff'), + title: 'post2', + slug: 'current', + published_at: new Date(0), + url: '/current/' + }, optionsData); + + fn.calledOnce.should.be.true(); + inverse.calledOnce.should.be.false(); + + fn.firstCall.args.should.have.lengthOf(2); + fn.firstCall.args[0].should.have.properties('slug', 'title'); + fn.firstCall.args[1].should.be.an.Object().and.have.property('data'); + browsePostsStub.calledOnce.should.be.true(); + browsePostsStub.firstCall.args[0].include.should.eql('author,authors,tags,tiers'); + + // Check context passed + browsePostsStub.firstCall.args[0].context.member.should.eql(member); }); }); });