🐛 {{access}} property incorrect when using get/next-post/prev-post helpers (#14256)

refs https://github.com/TryGhost/Team/issues/1367

- The {{access}} property of a post always returned false for non-public content
- Added the member context to the get, next-post and prev-post helpers
- The get, next-post and prev-post helpers didn't add the member context to the internal API calls
- Added the members context to these calls
- Added tests that check if the member context is passed to the API calls
- Transformed next_post helper tests to async await syntax
- Transformed prev_post helper tests to async await syntax
This commit is contained in:
Simon Backx 2022-03-03 16:18:05 +01:00 committed by GitHub
parent e97abeceb5
commit f389bab6ab
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
5 changed files with 349 additions and 306 deletions

View File

@ -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) {

View File

@ -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<any>}
*/
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<any>}
*/
module.exports = function prevNext(options) {
options = options || {};

View File

@ -295,4 +295,33 @@ describe('{{#get}} helper', function () {
browseStub.firstCall.args[0].limit.should.eql(2);
});
});
describe('auth', function () {
/**
* @type sinon.SinonStub<any[], any>
*/
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);
});
});
});

View File

@ -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);
});
});
});

View File

@ -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);
});
});
});