mirror of
https://github.com/TryGhost/Ghost.git
synced 2025-04-08 02:52:39 -05:00
🐛 Fixed post access in the get helper (#14282)
refs https://github.com/TryGhost/Team/issues/1367 Because we are passing through a different member object as the context in the get helper, the content gating was not working correctly, as the member was missing a status property, this adds the property which fixes content gating. - Added extra tests for get helper {{access}} property - Added extra test for {{access}} property in next_post helper - In the future we might want to update the tests so they test the whole request -> HBS context flow. Currently the has context is still stubbed manually.
This commit is contained in:
parent
2cc2d114f4
commit
9ff8d7f910
3 changed files with 627 additions and 1 deletions
|
@ -33,7 +33,9 @@ function updateLocalTemplateOptions(req, res, next) {
|
|||
default_payment_card_last4: sub.default_payment_card_last4 || '****'
|
||||
});
|
||||
}),
|
||||
paid: req.member.status !== 'free'
|
||||
paid: req.member.status !== 'free',
|
||||
status: req.member.status,
|
||||
products: req.member.products
|
||||
} : null;
|
||||
|
||||
hbs.updateLocalTemplateOptions(res.locals, _.merge({}, localTemplateOptions, {
|
||||
|
|
292
test/e2e-frontend/helpers/get.test.js
Normal file
292
test/e2e-frontend/helpers/get.test.js
Normal file
|
@ -0,0 +1,292 @@
|
|||
const should = require('should');
|
||||
const sinon = require('sinon');
|
||||
const testUtils = require('../../utils');
|
||||
const models = require('../../../core/server/models/index');
|
||||
|
||||
const API_VERSION = 'canary';
|
||||
const DEFAULT_POST_FIXTURE_COUNT = 7;
|
||||
|
||||
const get = require('../../../core/frontend/helpers/get');
|
||||
|
||||
async function createPost(data) {
|
||||
const post = testUtils.DataGenerator.forKnex.createPost(data);
|
||||
await models.Post.add(post, {context: {internal: true}});
|
||||
return post;
|
||||
}
|
||||
|
||||
function buildMember(status, products = []) {
|
||||
return {
|
||||
uuid: '1234',
|
||||
email: 'test@example.com',
|
||||
name: 'John Doe',
|
||||
firstname: 'John',
|
||||
avatar_image: null,
|
||||
subscriptions: [],
|
||||
paid: status !== 'free',
|
||||
status: status,
|
||||
products
|
||||
};
|
||||
}
|
||||
|
||||
function testPosts(posts, map) {
|
||||
posts.should.be.an.Array();
|
||||
posts.length.should.eql(DEFAULT_POST_FIXTURE_COUNT + Object.keys(map).length);
|
||||
|
||||
// Free post
|
||||
for (const postID in map) {
|
||||
const expectData = map[postID];
|
||||
|
||||
const post = posts.find(p => p.id === postID);
|
||||
should.exist(post);
|
||||
|
||||
post.should.match(expectData);
|
||||
}
|
||||
}
|
||||
|
||||
describe('e2e {{#get}} helper', function () {
|
||||
let fn;
|
||||
let inverse;
|
||||
let locals = {};
|
||||
let publicPost, membersPost, paidPost, basicTierPost;
|
||||
|
||||
before(async function () {
|
||||
await testUtils.startGhost({
|
||||
backend: true,
|
||||
frontend: false
|
||||
});
|
||||
|
||||
publicPost = await createPost({
|
||||
slug: 'free-to-see',
|
||||
visibility: 'public',
|
||||
published_at: new Date() // here to ensure sorting is not modified
|
||||
});
|
||||
|
||||
membersPost = await createPost({
|
||||
slug: 'members-post',
|
||||
visibility: 'members',
|
||||
published_at: new Date() // here to ensure sorting is not modified
|
||||
});
|
||||
|
||||
paidPost = await createPost({
|
||||
slug: 'paid-to-see',
|
||||
visibility: 'paid',
|
||||
published_at: new Date() // here to ensure sorting is not modified
|
||||
});
|
||||
|
||||
basicTierPost = await createPost({
|
||||
slug: 'tiers-post',
|
||||
visibility: 'tiers',
|
||||
tiers: [{
|
||||
slug: 'default-product'
|
||||
}],
|
||||
published_at: new Date() // here to ensure sorting is not modified
|
||||
});
|
||||
});
|
||||
|
||||
// Assert fixtures are correct
|
||||
it('has valid fixtures', function () {
|
||||
publicPost.visibility.should.eql('public');
|
||||
membersPost.visibility.should.eql('members');
|
||||
paidPost.visibility.should.eql('paid');
|
||||
basicTierPost.visibility.should.eql('tiers');
|
||||
});
|
||||
|
||||
beforeEach(function () {
|
||||
fn = sinon.spy();
|
||||
inverse = sinon.spy();
|
||||
locals = {root: {_locals: {apiVersion: API_VERSION}}};
|
||||
});
|
||||
|
||||
describe('{{access}} property', function () {
|
||||
let member;
|
||||
|
||||
it('not authenticated member', async function () {
|
||||
member = null;
|
||||
locals = {root: {_locals: {apiVersion: API_VERSION}}, member};
|
||||
await get.call(
|
||||
{},
|
||||
'posts',
|
||||
{hash: {}, data: locals, fn: fn, inverse: inverse}
|
||||
);
|
||||
testPosts(fn.firstCall.args[0].posts, {
|
||||
[publicPost.id]: {
|
||||
access: true
|
||||
},
|
||||
[membersPost.id]: {
|
||||
access: false
|
||||
},
|
||||
[paidPost.id]: {
|
||||
access: false
|
||||
},
|
||||
[basicTierPost.id]: {
|
||||
access: false
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
it('free member', async function () {
|
||||
member = buildMember('free');
|
||||
locals = {root: {_locals: {apiVersion: API_VERSION}}, member};
|
||||
await get.call(
|
||||
{},
|
||||
'posts',
|
||||
{hash: {}, data: locals, fn: fn, inverse: inverse}
|
||||
);
|
||||
testPosts(fn.firstCall.args[0].posts, {
|
||||
[publicPost.id]: {
|
||||
access: true
|
||||
},
|
||||
[membersPost.id]: {
|
||||
access: true
|
||||
},
|
||||
[paidPost.id]: {
|
||||
access: false
|
||||
},
|
||||
[basicTierPost.id]: {
|
||||
access: false
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
it('paid member', async function () {
|
||||
member = buildMember('paid');
|
||||
locals = {root: {_locals: {apiVersion: API_VERSION}}, member};
|
||||
await get.call(
|
||||
{},
|
||||
'posts',
|
||||
{hash: {}, data: locals, fn: fn, inverse: inverse}
|
||||
);
|
||||
testPosts(fn.firstCall.args[0].posts, {
|
||||
[publicPost.id]: {
|
||||
access: true
|
||||
},
|
||||
[membersPost.id]: {
|
||||
access: true
|
||||
},
|
||||
[paidPost.id]: {
|
||||
access: true
|
||||
},
|
||||
[basicTierPost.id]: {
|
||||
access: false
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
it('comped member', async function () {
|
||||
member = buildMember('comped');
|
||||
locals = {root: {_locals: {apiVersion: API_VERSION}}, member};
|
||||
await get.call(
|
||||
{},
|
||||
'posts',
|
||||
{hash: {}, data: locals, fn: fn, inverse: inverse}
|
||||
);
|
||||
testPosts(fn.firstCall.args[0].posts, {
|
||||
[publicPost.id]: {
|
||||
access: true
|
||||
},
|
||||
[membersPost.id]: {
|
||||
access: true
|
||||
},
|
||||
[paidPost.id]: {
|
||||
access: true
|
||||
},
|
||||
[basicTierPost.id]: {
|
||||
access: false
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
/**
|
||||
* When using the get helper, you need to include tiers to properly determine {{access}} for posts with specific tiers
|
||||
*/
|
||||
it('tiers member not including tiers', async function () {
|
||||
member = buildMember('paid', [{
|
||||
name: 'Default Product',
|
||||
slug: 'default-product',
|
||||
type: 'paid',
|
||||
active: true
|
||||
}]);
|
||||
|
||||
locals = {root: {_locals: {apiVersion: API_VERSION}}, member};
|
||||
await get.call(
|
||||
{},
|
||||
'posts',
|
||||
{hash: {}, data: locals, fn: fn, inverse: inverse}
|
||||
);
|
||||
testPosts(fn.firstCall.args[0].posts, {
|
||||
[publicPost.id]: {
|
||||
access: true
|
||||
},
|
||||
[membersPost.id]: {
|
||||
access: true
|
||||
},
|
||||
[paidPost.id]: {
|
||||
access: true
|
||||
},
|
||||
[basicTierPost.id]: {
|
||||
access: false
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
it('tiers member including tiers', async function () {
|
||||
member = buildMember('paid', [{
|
||||
name: 'Default Product',
|
||||
slug: 'default-product',
|
||||
type: 'paid',
|
||||
active: true
|
||||
}]);
|
||||
|
||||
locals = {root: {_locals: {apiVersion: API_VERSION}}, member};
|
||||
await get.call(
|
||||
{},
|
||||
'posts',
|
||||
{hash: {include: 'tiers'}, data: locals, fn: fn, inverse: inverse}
|
||||
);
|
||||
testPosts(fn.firstCall.args[0].posts, {
|
||||
[publicPost.id]: {
|
||||
access: true
|
||||
},
|
||||
[membersPost.id]: {
|
||||
access: true
|
||||
},
|
||||
[paidPost.id]: {
|
||||
access: true
|
||||
},
|
||||
[basicTierPost.id]: {
|
||||
access: true
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
it('tiers member with different product', async function () {
|
||||
member = buildMember('paid', [{
|
||||
name: 'Default Product',
|
||||
slug: 'pro-product',
|
||||
type: 'paid',
|
||||
active: true
|
||||
}]);
|
||||
|
||||
locals = {root: {_locals: {apiVersion: API_VERSION}}, member};
|
||||
await get.call(
|
||||
{},
|
||||
'posts',
|
||||
{hash: {include: 'tiers'}, data: locals, fn: fn, inverse: inverse}
|
||||
);
|
||||
testPosts(fn.firstCall.args[0].posts, {
|
||||
[publicPost.id]: {
|
||||
access: true
|
||||
},
|
||||
[membersPost.id]: {
|
||||
access: true
|
||||
},
|
||||
[paidPost.id]: {
|
||||
access: true
|
||||
},
|
||||
[basicTierPost.id]: {
|
||||
access: false
|
||||
}
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
332
test/e2e-frontend/helpers/next_post.test.js
Normal file
332
test/e2e-frontend/helpers/next_post.test.js
Normal file
|
@ -0,0 +1,332 @@
|
|||
const should = require('should');
|
||||
const sinon = require('sinon');
|
||||
const testUtils = require('../../utils');
|
||||
const models = require('../../../core/server/models/index');
|
||||
|
||||
const API_VERSION = 'canary';
|
||||
|
||||
const next_post = require('../../../core/frontend/helpers/prev_post');
|
||||
|
||||
async function createPost(data) {
|
||||
const post = testUtils.DataGenerator.forKnex.createPost(data);
|
||||
await models.Post.add(post, {context: {internal: true}});
|
||||
return post;
|
||||
}
|
||||
|
||||
function buildMember(status, products = []) {
|
||||
return {
|
||||
uuid: '1234',
|
||||
email: 'test@example.com',
|
||||
name: 'John Doe',
|
||||
firstname: 'John',
|
||||
avatar_image: null,
|
||||
subscriptions: [],
|
||||
paid: status !== 'free',
|
||||
status: status,
|
||||
products
|
||||
};
|
||||
}
|
||||
|
||||
describe('e2e {{#next_post}} helper', function () {
|
||||
let fn;
|
||||
let inverse;
|
||||
let publicPost, membersPost, paidPost, basicTierPost, publicPost2;
|
||||
|
||||
before(async function () {
|
||||
await testUtils.startGhost({
|
||||
backend: true,
|
||||
frontend: false
|
||||
});
|
||||
|
||||
publicPost = await createPost({
|
||||
slug: 'free-to-see',
|
||||
visibility: 'public',
|
||||
published_at: new Date(2020, 0, 1) // here to ensure sorting is not modified
|
||||
});
|
||||
|
||||
membersPost = await createPost({
|
||||
slug: 'members-post',
|
||||
visibility: 'members',
|
||||
published_at: new Date(2020, 0, 2) // here to ensure sorting is not modified
|
||||
});
|
||||
|
||||
paidPost = await createPost({
|
||||
slug: 'paid-to-see',
|
||||
visibility: 'paid',
|
||||
published_at: new Date(2020, 0, 3) // here to ensure sorting is not modified
|
||||
});
|
||||
|
||||
basicTierPost = await createPost({
|
||||
slug: 'tiers-post',
|
||||
visibility: 'tiers',
|
||||
tiers: [{
|
||||
slug: 'default-product'
|
||||
}],
|
||||
published_at: new Date(2020, 0, 4) // here to ensure sorting is not modified
|
||||
});
|
||||
|
||||
publicPost2 = await createPost({
|
||||
slug: 'free-to-see',
|
||||
visibility: 'public',
|
||||
published_at: new Date(2020, 0, 5) // here to ensure sorting is not modified
|
||||
});
|
||||
});
|
||||
|
||||
// Assert fixtures are correct
|
||||
it('has valid fixtures', function () {
|
||||
publicPost.visibility.should.eql('public');
|
||||
membersPost.visibility.should.eql('members');
|
||||
paidPost.visibility.should.eql('paid');
|
||||
basicTierPost.visibility.should.eql('tiers');
|
||||
publicPost2.visibility.should.eql('public');
|
||||
});
|
||||
|
||||
beforeEach(function () {
|
||||
fn = sinon.spy();
|
||||
inverse = sinon.spy();
|
||||
});
|
||||
|
||||
describe('{{access}} property', function () {
|
||||
describe('not authenticated member', function () {
|
||||
const member = null;
|
||||
const locals = {
|
||||
root: {
|
||||
_locals: {
|
||||
apiVersion: API_VERSION
|
||||
},
|
||||
context: ['post']
|
||||
},
|
||||
member
|
||||
};
|
||||
let optionsData;
|
||||
|
||||
beforeEach(function () {
|
||||
optionsData = {name: 'next_post', data: locals, fn, inverse};
|
||||
});
|
||||
|
||||
it('next members post', async function () {
|
||||
await next_post
|
||||
.call(publicPost, optionsData);
|
||||
|
||||
fn.firstCall.args[0].should.match({id: membersPost.id, access: false});
|
||||
});
|
||||
|
||||
it('next paid post', async function () {
|
||||
await next_post
|
||||
.call(membersPost, optionsData);
|
||||
|
||||
fn.firstCall.args[0].should.match({id: paidPost.id, access: false});
|
||||
});
|
||||
|
||||
it('next tiers post', async function () {
|
||||
await next_post
|
||||
.call(paidPost, optionsData);
|
||||
|
||||
fn.firstCall.args[0].should.match({id: basicTierPost.id, access: false});
|
||||
});
|
||||
|
||||
it('next public post', async function () {
|
||||
await next_post
|
||||
.call(basicTierPost, optionsData);
|
||||
|
||||
fn.firstCall.args[0].should.match({id: publicPost2.id, access: true});
|
||||
});
|
||||
});
|
||||
|
||||
describe('free member', function () {
|
||||
const member = buildMember('free');
|
||||
const locals = {
|
||||
root: {
|
||||
_locals: {
|
||||
apiVersion: API_VERSION
|
||||
},
|
||||
context: ['post']
|
||||
},
|
||||
member
|
||||
};
|
||||
let optionsData;
|
||||
|
||||
beforeEach(function () {
|
||||
optionsData = {name: 'next_post', data: locals, fn, inverse};
|
||||
});
|
||||
|
||||
it('next members post', async function () {
|
||||
await next_post
|
||||
.call(publicPost, optionsData);
|
||||
|
||||
fn.firstCall.args[0].should.match({id: membersPost.id, access: true});
|
||||
});
|
||||
|
||||
it('next paid post', async function () {
|
||||
await next_post
|
||||
.call(membersPost, optionsData);
|
||||
|
||||
fn.firstCall.args[0].should.match({id: paidPost.id, access: false});
|
||||
});
|
||||
|
||||
it('next tiers post', async function () {
|
||||
await next_post
|
||||
.call(paidPost, optionsData);
|
||||
|
||||
fn.firstCall.args[0].should.match({id: basicTierPost.id, access: false});
|
||||
});
|
||||
|
||||
it('next public post', async function () {
|
||||
await next_post
|
||||
.call(basicTierPost, optionsData);
|
||||
|
||||
fn.firstCall.args[0].should.match({id: publicPost2.id, access: true});
|
||||
});
|
||||
});
|
||||
|
||||
describe('paid member', function () {
|
||||
const member = buildMember('paid');
|
||||
const locals = {
|
||||
root: {
|
||||
_locals: {
|
||||
apiVersion: API_VERSION
|
||||
},
|
||||
context: ['post']
|
||||
},
|
||||
member
|
||||
};
|
||||
let optionsData;
|
||||
|
||||
beforeEach(function () {
|
||||
optionsData = {name: 'next_post', data: locals, fn, inverse};
|
||||
});
|
||||
|
||||
it('next members post', async function () {
|
||||
await next_post
|
||||
.call(publicPost, optionsData);
|
||||
|
||||
fn.firstCall.args[0].should.match({id: membersPost.id, access: true});
|
||||
});
|
||||
|
||||
it('next paid post', async function () {
|
||||
await next_post
|
||||
.call(membersPost, optionsData);
|
||||
|
||||
fn.firstCall.args[0].should.match({id: paidPost.id, access: true});
|
||||
});
|
||||
|
||||
it('next tiers post', async function () {
|
||||
await next_post
|
||||
.call(paidPost, optionsData);
|
||||
|
||||
fn.firstCall.args[0].should.match({id: basicTierPost.id, access: false});
|
||||
});
|
||||
|
||||
it('next public post', async function () {
|
||||
await next_post
|
||||
.call(basicTierPost, optionsData);
|
||||
|
||||
fn.firstCall.args[0].should.match({id: publicPost2.id, access: true});
|
||||
});
|
||||
});
|
||||
|
||||
describe('tiers member', function () {
|
||||
const member = buildMember('tiers', [{
|
||||
name: 'Default Product',
|
||||
slug: 'default-product',
|
||||
type: 'paid',
|
||||
active: true
|
||||
}]);
|
||||
|
||||
const locals = {
|
||||
root: {
|
||||
_locals: {
|
||||
apiVersion: API_VERSION
|
||||
},
|
||||
context: ['post']
|
||||
},
|
||||
member
|
||||
};
|
||||
let optionsData;
|
||||
|
||||
beforeEach(function () {
|
||||
optionsData = {name: 'next_post', data: locals, fn, inverse};
|
||||
});
|
||||
|
||||
it('next members post', async function () {
|
||||
await next_post
|
||||
.call(publicPost, optionsData);
|
||||
|
||||
fn.firstCall.args[0].should.match({id: membersPost.id, access: true});
|
||||
});
|
||||
|
||||
it('next paid post', async function () {
|
||||
await next_post
|
||||
.call(membersPost, optionsData);
|
||||
|
||||
fn.firstCall.args[0].should.match({id: paidPost.id, access: true});
|
||||
});
|
||||
|
||||
it('next tiers post', async function () {
|
||||
await next_post
|
||||
.call(paidPost, optionsData);
|
||||
|
||||
fn.firstCall.args[0].should.match({id: basicTierPost.id, access: true});
|
||||
});
|
||||
|
||||
it('next public post', async function () {
|
||||
await next_post
|
||||
.call(basicTierPost, optionsData);
|
||||
|
||||
fn.firstCall.args[0].should.match({id: publicPost2.id, access: true});
|
||||
});
|
||||
});
|
||||
|
||||
describe('tiers member with different product', function () {
|
||||
const member = buildMember('tiers', [{
|
||||
name: 'Default Product',
|
||||
slug: 'pro-product',
|
||||
type: 'paid',
|
||||
active: true
|
||||
}]);
|
||||
|
||||
const locals = {
|
||||
root: {
|
||||
_locals: {
|
||||
apiVersion: API_VERSION
|
||||
},
|
||||
context: ['post']
|
||||
},
|
||||
member
|
||||
};
|
||||
let optionsData;
|
||||
|
||||
beforeEach(function () {
|
||||
optionsData = {name: 'next_post', data: locals, fn, inverse};
|
||||
});
|
||||
|
||||
it('next members post', async function () {
|
||||
await next_post
|
||||
.call(publicPost, optionsData);
|
||||
|
||||
fn.firstCall.args[0].should.match({id: membersPost.id, access: true});
|
||||
});
|
||||
|
||||
it('next paid post', async function () {
|
||||
await next_post
|
||||
.call(membersPost, optionsData);
|
||||
|
||||
fn.firstCall.args[0].should.match({id: paidPost.id, access: true});
|
||||
});
|
||||
|
||||
it('next tiers post', async function () {
|
||||
await next_post
|
||||
.call(paidPost, optionsData);
|
||||
|
||||
fn.firstCall.args[0].should.match({id: basicTierPost.id, access: false});
|
||||
});
|
||||
|
||||
it('next public post', async function () {
|
||||
await next_post
|
||||
.call(basicTierPost, optionsData);
|
||||
|
||||
fn.firstCall.args[0].should.match({id: publicPost2.id, access: true});
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
Loading…
Add table
Reference in a new issue