0
Fork 0
mirror of https://github.com/TryGhost/Ghost.git synced 2025-02-17 23:44:39 -05:00
ghost/test/regression/api/v3/content/posts.test.js
Naz e715c5b82d Refactored posts regressions suites to use common initialization syntax
refs https://github.com/TryGhost/Toolbox/issues/138

- Using asycn/await syntax is way more readable and allows to identify further reusable patterns in test initialization. This refactor also served as an exploreation around how the code looks like at this point
2021-11-25 03:20:47 +13:00

439 lines
20 KiB
JavaScript

const should = require('should');
const moment = require('moment');
const supertest = require('supertest');
const _ = require('lodash');
const testUtils = require('../../../../utils');
const localUtils = require('./utils');
const configUtils = require('../../../../utils/configUtils');
const urlUtils = require('../../../../utils/urlUtils');
const config = require('../../../../../core/shared/config');
const ghost = testUtils.startGhost;
describe('api/v3/content/posts', function () {
let request;
before(async function () {
await ghost();
request = supertest.agent(config.get('url'));
await testUtils.initFixtures('users:no-owner', 'user:inactive', 'posts', 'tags:extra', 'api_keys');
});
afterEach(function () {
configUtils.restore();
urlUtils.restore();
});
const validKey = localUtils.getValidKey();
it('browse posts', function (done) {
request.get(localUtils.API.getApiQuery(`posts/?key=${validKey}`))
.set('Origin', testUtils.API.getURL())
.expect('Content-Type', /json/)
.expect('Cache-Control', testUtils.cacheRules.private)
.expect(200)
.end(function (err, res) {
if (err) {
return done(err);
}
res.headers.vary.should.eql('Accept-Encoding');
should.exist(res.headers['access-control-allow-origin']);
should.not.exist(res.headers['x-cache-invalidate']);
const jsonResponse = res.body;
should.exist(jsonResponse.posts);
localUtils.API.checkResponse(jsonResponse, 'posts');
jsonResponse.posts.should.have.length(11);
localUtils.API.checkResponse(jsonResponse.posts[0], 'post');
localUtils.API.checkResponse(jsonResponse.meta.pagination, 'pagination');
_.isBoolean(jsonResponse.posts[0].featured).should.eql(true);
// Default order 'published_at desc' check
jsonResponse.posts[0].slug.should.eql('welcome');
jsonResponse.posts[6].slug.should.eql('integrations');
// check meta response for this test
jsonResponse.meta.pagination.page.should.eql(1);
jsonResponse.meta.pagination.limit.should.eql(15);
jsonResponse.meta.pagination.pages.should.eql(1);
jsonResponse.meta.pagination.total.should.eql(11);
jsonResponse.meta.pagination.hasOwnProperty('next').should.be.true();
jsonResponse.meta.pagination.hasOwnProperty('prev').should.be.true();
should.not.exist(jsonResponse.meta.pagination.next);
should.not.exist(jsonResponse.meta.pagination.prev);
done();
});
});
it('browse posts with related authors/tags also returns primary_author/primary_tag', function (done) {
request.get(localUtils.API.getApiQuery(`posts/?key=${validKey}&include=authors,tags`))
.set('Origin', testUtils.API.getURL())
.expect('Content-Type', /json/)
.expect('Cache-Control', testUtils.cacheRules.private)
.expect(200)
.end(function (err, res) {
if (err) {
return done(err);
}
res.headers.vary.should.eql('Accept-Encoding');
should.exist(res.headers['access-control-allow-origin']);
should.not.exist(res.headers['x-cache-invalidate']);
const jsonResponse = res.body;
should.exist(jsonResponse.posts);
localUtils.API.checkResponse(jsonResponse, 'posts');
jsonResponse.posts.should.have.length(11);
localUtils.API.checkResponse(
jsonResponse.posts[0],
'post',
['authors', 'tags', 'primary_tag', 'primary_author'],
null
);
localUtils.API.checkResponse(jsonResponse.meta.pagination, 'pagination');
_.isBoolean(jsonResponse.posts[0].featured).should.eql(true);
// Default order 'published_at desc' check
jsonResponse.posts[0].slug.should.eql('welcome');
jsonResponse.posts[6].slug.should.eql('integrations');
// check meta response for this test
jsonResponse.meta.pagination.page.should.eql(1);
jsonResponse.meta.pagination.limit.should.eql(15);
jsonResponse.meta.pagination.pages.should.eql(1);
jsonResponse.meta.pagination.total.should.eql(11);
jsonResponse.meta.pagination.hasOwnProperty('next').should.be.true();
jsonResponse.meta.pagination.hasOwnProperty('prev').should.be.true();
should.not.exist(jsonResponse.meta.pagination.next);
should.not.exist(jsonResponse.meta.pagination.prev);
done();
});
});
it('browse posts with basic page filter should not return pages', function (done) {
request.get(localUtils.API.getApiQuery(`posts/?key=${validKey}&filter=page:true`))
.expect('Content-Type', /json/)
.expect('Cache-Control', testUtils.cacheRules.private)
.expect(200)
.end(function (err, res) {
if (err) {
return done(err);
}
const jsonResponse = res.body;
should.not.exist(res.headers['x-cache-invalidate']);
should.exist(jsonResponse.posts);
localUtils.API.checkResponse(jsonResponse, 'posts');
localUtils.API.checkResponse(jsonResponse.meta.pagination, 'pagination');
jsonResponse.posts.should.have.length(0);
done();
});
});
it('browse posts with basic page filter should not return pages', function (done) {
request.get(localUtils.API.getApiQuery(`posts/?key=${validKey}&filter=page:true,featured:true`))
.expect('Content-Type', /json/)
.expect('Cache-Control', testUtils.cacheRules.private)
.expect(200)
.end(function (err, res) {
if (err) {
return done(err);
}
const jsonResponse = res.body;
should.not.exist(res.headers['x-cache-invalidate']);
should.exist(jsonResponse.posts);
localUtils.API.checkResponse(jsonResponse, 'posts');
localUtils.API.checkResponse(jsonResponse.meta.pagination, 'pagination');
jsonResponse.posts.should.have.length(2);
jsonResponse.posts.filter(p => (p.page === true)).should.have.length(0);
done();
});
});
it('browse posts with published and draft status, should not return drafts', function (done) {
request.get(localUtils.API.getApiQuery(`posts/?key=${validKey}&filter=status:published,status:draft`))
.expect('Content-Type', /json/)
.expect('Cache-Control', testUtils.cacheRules.private)
.expect(200)
.end(function (err, res) {
if (err) {
return done(err);
}
const jsonResponse = res.body;
jsonResponse.posts.should.be.an.Array().with.lengthOf(11);
done();
});
});
it('browse posts with slug filter, should order in slug order', function () {
return request.get(localUtils.API.getApiQuery(`posts/?key=${validKey}&filter=slug:[write,ghostly-kitchen-sink,grow]`))
.expect('Content-Type', /json/)
.expect('Cache-Control', testUtils.cacheRules.private)
.expect(200)
.then((res) => {
const jsonResponse = res.body;
jsonResponse.posts.should.be.an.Array().with.lengthOf(3);
jsonResponse.posts[0].slug.should.equal('write');
jsonResponse.posts[1].slug.should.equal('ghostly-kitchen-sink');
jsonResponse.posts[2].slug.should.equal('grow');
});
});
it('browse posts with slug filter should order taking order parameter into account', function () {
return request.get(localUtils.API.getApiQuery(`posts/?key=${validKey}&order=slug%20DESC&filter=slug:[write,ghostly-kitchen-sink,grow]`))
.expect('Content-Type', /json/)
.expect('Cache-Control', testUtils.cacheRules.private)
.expect(200)
.then((res) => {
const jsonResponse = res.body;
jsonResponse.posts.should.be.an.Array().with.lengthOf(3);
jsonResponse.posts[0].slug.should.equal('write');
jsonResponse.posts[1].slug.should.equal('grow');
jsonResponse.posts[2].slug.should.equal('ghostly-kitchen-sink');
});
});
it('ensure origin header on redirect is not getting lost', function (done) {
// NOTE: force a redirect to the admin url
configUtils.set('admin:url', 'http://localhost:9999');
urlUtils.stubUrlUtilsFromConfig();
request.get(localUtils.API.getApiQuery(`posts?key=${validKey}`))
.set('Origin', 'https://example.com')
// 301 Redirects _should_ be cached
.expect('Cache-Control', testUtils.cacheRules.year)
.expect(301)
.end(function (err, res) {
if (err) {
return done(err);
}
res.headers.vary.should.eql('Accept, Accept-Encoding');
res.headers.location.should.eql(`http://localhost:9999/ghost/api/v3/content/posts/?key=${validKey}`);
should.exist(res.headers['access-control-allow-origin']);
should.not.exist(res.headers['x-cache-invalidate']);
done();
});
});
it('can\'t read page', function () {
return request
.get(localUtils.API.getApiQuery(`posts/${testUtils.DataGenerator.Content.posts[5].id}/?key=${validKey}`))
.set('Origin', testUtils.API.getURL())
.expect('Content-Type', /json/)
.expect('Cache-Control', testUtils.cacheRules.private)
.expect(404);
});
it('can read post with fields', function () {
const complexPostId = testUtils.DataGenerator.Content.posts.find(p => p.slug === 'not-so-short-bit-complex').id;
return request
.get(localUtils.API.getApiQuery(`posts/${complexPostId}/?key=${validKey}&fields=title,slug,excerpt&formats=plaintext`))
.set('Origin', testUtils.API.getURL())
.expect('Content-Type', /json/)
.expect('Cache-Control', testUtils.cacheRules.private)
.expect(200)
.then((res) => {
localUtils.API.checkResponse(res.body.posts[0], 'post', null, null, ['id', 'title', 'slug', 'excerpt', 'plaintext']);
// excerpt should transform links to absolute URLs
res.body.posts[0].excerpt.should.match(/\* Aliquam \[http:\/\/127.0.0.1:2369\/about#nowhere\]/);
});
});
describe('content gating', function () {
let publicPost;
let membersPost;
let paidPost;
let membersPostWithPaywallCard;
before (function () {
publicPost = testUtils.DataGenerator.forKnex.createPost({
slug: 'free-to-see',
visibility: 'public',
published_at: moment().add(15, 'seconds').toDate() // here to ensure sorting is not modified
});
membersPost = testUtils.DataGenerator.forKnex.createPost({
slug: 'thou-shalt-not-be-seen',
visibility: 'members',
published_at: moment().add(45, 'seconds').toDate() // here to ensure sorting is not modified
});
paidPost = testUtils.DataGenerator.forKnex.createPost({
slug: 'thou-shalt-be-paid-for',
visibility: 'paid',
published_at: moment().add(30, 'seconds').toDate() // here to ensure sorting is not modified
});
membersPostWithPaywallCard = testUtils.DataGenerator.forKnex.createPost({
slug: 'thou-shalt-have-a-taste',
visibility: 'members',
mobiledoc: '{"version":"0.3.1","markups":[],"atoms":[],"cards":[["paywall",{}]],"sections":[[1,"p",[[0,[],0,"Free content"]]],[10,0],[1,"p",[[0,[],0,"Members content"]]]]}',
html: '<p>Free content</p><!--members-only--><p>Members content</p>',
published_at: moment().add(5, 'seconds').toDate()
});
return testUtils.fixtures.insertPosts([
publicPost,
membersPost,
paidPost,
membersPostWithPaywallCard
]);
});
it('public post fields are always visible', function () {
return request
.get(localUtils.API.getApiQuery(`posts/${publicPost.id}/?key=${validKey}&fields=slug,html,plaintext&formats=html,plaintext`))
.set('Origin', testUtils.API.getURL())
.expect('Content-Type', /json/)
.expect('Cache-Control', testUtils.cacheRules.private)
.expect(200)
.then((res) => {
const jsonResponse = res.body;
should.exist(jsonResponse.posts);
const post = jsonResponse.posts[0];
localUtils.API.checkResponse(post, 'post', null, null, ['id', 'slug', 'html', 'plaintext']);
post.slug.should.eql('free-to-see');
post.html.should.not.eql('');
post.plaintext.should.not.eql('');
});
});
it('cannot read members only post content', function () {
return request
.get(localUtils.API.getApiQuery(`posts/${membersPost.id}/?key=${validKey}`))
.set('Origin', testUtils.API.getURL())
.expect('Content-Type', /json/)
.expect('Cache-Control', testUtils.cacheRules.private)
.expect(200)
.then((res) => {
const jsonResponse = res.body;
should.exist(jsonResponse.posts);
const post = jsonResponse.posts[0];
localUtils.API.checkResponse(post, 'post', null, null);
post.slug.should.eql('thou-shalt-not-be-seen');
post.html.should.eql('');
post.excerpt.should.eql('');
});
});
it('cannot read paid only post content', function () {
return request
.get(localUtils.API.getApiQuery(`posts/${paidPost.id}/?key=${validKey}`))
.set('Origin', testUtils.API.getURL())
.expect('Content-Type', /json/)
.expect('Cache-Control', testUtils.cacheRules.private)
.expect(200)
.then((res) => {
const jsonResponse = res.body;
should.exist(jsonResponse.posts);
const post = jsonResponse.posts[0];
localUtils.API.checkResponse(post, 'post', null, null);
post.slug.should.eql('thou-shalt-be-paid-for');
post.html.should.eql('');
post.excerpt.should.eql('');
});
});
it('cannot read members only post plaintext', function () {
return request
.get(localUtils.API.getApiQuery(`posts/${membersPost.id}/?key=${validKey}&formats=html,plaintext&fields=html,plaintext`))
.set('Origin', testUtils.API.getURL())
.expect('Content-Type', /json/)
.expect('Cache-Control', testUtils.cacheRules.private)
.expect(200)
.then((res) => {
const jsonResponse = res.body;
should.exist(jsonResponse.posts);
const post = jsonResponse.posts[0];
localUtils.API.checkResponse(post, 'post', null, null, ['id', 'html', 'plaintext']);
post.html.should.eql('');
post.plaintext.should.eql('');
});
});
it('can read "free" html and plaintext content of members post when using paywall card', function () {
return request
.get(localUtils.API.getApiQuery(`posts/${membersPostWithPaywallCard.id}/?key=${validKey}&formats=html,plaintext`))
.set('Origin', testUtils.API.getURL())
.expect('Content-Type', /json/)
.expect('Cache-Control', testUtils.cacheRules.private)
.expect(200)
.then((res) => {
const jsonResponse = res.body;
should.exist(jsonResponse.posts);
const post = jsonResponse.posts[0];
localUtils.API.checkResponse(post, 'post', ['plaintext']);
post.html.should.eql('<p>Free content</p>');
post.plaintext.should.eql('Free content');
});
});
it('cannot browse members only posts content', function () {
return request.get(localUtils.API.getApiQuery(`posts/?key=${validKey}`))
.set('Origin', testUtils.API.getURL())
.expect('Content-Type', /json/)
.expect('Cache-Control', testUtils.cacheRules.private)
.expect(200)
.then((res) => {
res.headers.vary.should.eql('Accept-Encoding');
should.exist(res.headers['access-control-allow-origin']);
should.not.exist(res.headers['x-cache-invalidate']);
const jsonResponse = res.body;
should.exist(jsonResponse.posts);
localUtils.API.checkResponse(jsonResponse, 'posts');
jsonResponse.posts.should.have.length(15);
localUtils.API.checkResponse(jsonResponse.posts[0], 'post', null, null);
localUtils.API.checkResponse(jsonResponse.meta.pagination, 'pagination');
_.isBoolean(jsonResponse.posts[0].featured).should.eql(true);
// Default order 'published_at desc' check
jsonResponse.posts[0].slug.should.eql('thou-shalt-not-be-seen');
jsonResponse.posts[1].slug.should.eql('thou-shalt-be-paid-for');
jsonResponse.posts[2].slug.should.eql('free-to-see');
jsonResponse.posts[3].slug.should.eql('thou-shalt-have-a-taste');
jsonResponse.posts[8].slug.should.eql('sell');
jsonResponse.posts[0].html.should.eql('');
jsonResponse.posts[1].html.should.eql('');
jsonResponse.posts[2].html.should.not.eql('');
jsonResponse.posts[8].html.should.not.eql('');
jsonResponse.posts[0].excerpt.should.eql('');
jsonResponse.posts[1].excerpt.should.eql('');
jsonResponse.posts[2].excerpt.should.not.eql('');
jsonResponse.posts[3].excerpt.should.not.eql('');
jsonResponse.posts[8].excerpt.should.not.eql('');
// check meta response for this test
jsonResponse.meta.pagination.page.should.eql(1);
jsonResponse.meta.pagination.limit.should.eql(15);
jsonResponse.meta.pagination.pages.should.eql(1);
jsonResponse.meta.pagination.total.should.eql(15);
jsonResponse.meta.pagination.hasOwnProperty('next').should.be.true();
jsonResponse.meta.pagination.hasOwnProperty('prev').should.be.true();
should.not.exist(jsonResponse.meta.pagination.next);
should.not.exist(jsonResponse.meta.pagination.prev);
});
});
});
});