From 7dc38e20787be6ddc8af01c7cc883c121c114a53 Mon Sep 17 00:00:00 2001 From: Naz Gargol Date: Tue, 10 Sep 2019 11:41:42 +0200 Subject: [PATCH] =?UTF-8?q?=F0=9F=94=A5=20Removed=20V1=20code/references?= =?UTF-8?q?=20in=20frontend=20resources/routing=20layer=20(#11087)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit no issue - Removed v1 'author' leftover in include statement for preview controller - Removed v1 'author' leftover in include statement for preview controller - Removed v1 'author' leftover in include statement in entry lookup routing helper - Migrated related test to use v2 API controller - Removed v0.1 routing confif - Removed v0.1 url config - Fixed tests that had to do with url's in resources after removing v0.1 resources from URL cache - Removed v1 'author' leftover in include statement in static routing helper - Modified the test to use v2 API - Removed v1 specific condition with 'page' in context helper - Fixed dynamic routing spec after theme switch to v2. All tested users have to have at least one published post to be shown as an author - Fixed URL Service spec to use theme engine v2 --- core/frontend/services/routing/config/v0.1.js | 70 - .../services/routing/controllers/entry.js | 7 - .../services/routing/controllers/preview.js | 3 +- .../services/routing/controllers/static.js | 3 +- .../services/routing/helpers/context.js | 9 +- .../services/routing/helpers/entry-lookup.js | 6 +- .../services/routing/helpers/fetch-data.js | 5 +- core/frontend/services/url/configs/v0.1.js | 121 -- core/test/acceptance/old/admin/tags_spec.js | 4 +- core/test/acceptance/old/admin/users_spec.js | 12 +- core/test/regression/site/UrlService_spec.js | 49 +- .../regression/site/dynamic_routing_spec.js | 20 +- core/test/regression/site/site_spec.js | 1771 +---------------- .../routing/controllers/preview_spec.js | 119 +- .../routing/controllers/static_spec.js | 30 +- .../services/routing/helpers/context_spec.js | 2 +- .../routing/helpers/fetch-data_spec.js | 89 +- .../unit/services/settings/validate_spec.js | 781 -------- .../utils/fixtures/themes/casper/package.json | 3 +- .../fixtures/themes/test-theme/package.json | 3 +- 20 files changed, 143 insertions(+), 2964 deletions(-) delete mode 100644 core/frontend/services/routing/config/v0.1.js delete mode 100644 core/frontend/services/url/configs/v0.1.js diff --git a/core/frontend/services/routing/config/v0.1.js b/core/frontend/services/routing/config/v0.1.js deleted file mode 100644 index 3884f8ab51..0000000000 --- a/core/frontend/services/routing/config/v0.1.js +++ /dev/null @@ -1,70 +0,0 @@ -/* eslint-disable */ -module.exports.QUERY = { - tag: { - controller: 'tags', - type: 'read', - resource: 'tags', - options: { - slug: '%s', - visibility: 'public' - } - }, - author: { - resourceAlias: 'authors', - controller: 'users', - type: 'read', - resource: 'users', - options: { - slug: '%s', - visibility: 'public' - } - }, - user: { - resourceAlias: 'authors', - controller: 'users', - type: 'read', - resource: 'users', - options: { - slug: '%s', - visibility: 'public' - } - }, - post: { - controller: 'posts', - type: 'read', - resource: 'posts', - options: { - slug: '%s', - status: 'published', - page: 0 - } - }, - page: { - controller: 'posts', - type: 'read', - resource: 'posts', - options: { - slug: '%s', - status: 'published', - page: 1 - } - }, - preview: { - controller: 'posts', - resource: 'posts' - } -}; - -module.exports.TAXONOMIES = { - tag: { - filter: 'tags:\'%s\'+tags.visibility:public', - editRedirect: '#/settings/tags/:slug/', - resource: 'tags' - }, - author: { - filter: 'authors:\'%s\'', - editRedirect: '#/team/:slug/', - resource: 'authors' - } -}; -/* eslint-enable */ diff --git a/core/frontend/services/routing/controllers/entry.js b/core/frontend/services/routing/controllers/entry.js index 561f6c8622..571fbcdeee 100644 --- a/core/frontend/services/routing/controllers/entry.js +++ b/core/frontend/services/routing/controllers/entry.js @@ -67,13 +67,6 @@ module.exports = function entryController(req, res, next) { * @NOTE: * * Ensure we redirect to the correct post url including subdirectory. - * - * @NOTE: - * Keep in mind, that the logic here is used for v0.1 and v2. - * v0.1 returns relative urls, v2 returns absolute urls. - * - * @TODO: - * Simplify if we drop v0.1. */ if (urlUtils.absoluteToRelative(entry.url, {withoutSubdirectory: true}) !== req.path) { debug('redirect'); diff --git a/core/frontend/services/routing/controllers/preview.js b/core/frontend/services/routing/controllers/preview.js index d379e406fe..4f8632dc97 100644 --- a/core/frontend/services/routing/controllers/preview.js +++ b/core/frontend/services/routing/controllers/preview.js @@ -18,8 +18,7 @@ module.exports = function previewController(req, res, next) { const params = { uuid: req.params.uuid, status: 'all', - // @TODO: Remove "author" if we drop v0.1 - include: 'author,authors,tags' + include: 'authors,tags' }; return api[res.routerOptions.query.controller] diff --git a/core/frontend/services/routing/controllers/static.js b/core/frontend/services/routing/controllers/static.js index cca4462853..6ab58c727a 100644 --- a/core/frontend/services/routing/controllers/static.js +++ b/core/frontend/services/routing/controllers/static.js @@ -14,8 +14,7 @@ function processQuery(query, locals) { // We override the `include` property for now, because the full data set is required anyway. if (_.get(query, 'resource') === 'posts') { _.extend(query.options, { - // @TODO: Remove "author" when we drop v0.1 - include: 'author,authors,tags' + include: 'authors,tags' }); } diff --git a/core/frontend/services/routing/helpers/context.js b/core/frontend/services/routing/helpers/context.js index cd71bac8c1..867e1ea7e1 100644 --- a/core/frontend/services/routing/helpers/context.js +++ b/core/frontend/services/routing/helpers/context.js @@ -43,7 +43,7 @@ function setResponseContext(req, res, data) { } // Add context 'amp' to either post or page, if we have an `*/amp` route - if (ampPattern.test(res.locals.relativeUrl) && data.post) { + if (ampPattern.test(res.locals.relativeUrl) && (data.post || data.page)) { res.locals.context.push('amp'); } @@ -64,12 +64,7 @@ function setResponseContext(req, res, data) { } } - // @TODO: remove first if condition when we drop v0.1 - if (data && data.post && data.post.page) { - if (!res.locals.context.includes('page')) { - res.locals.context.push('page'); - } - } else if (data && data.post) { + if (data && data.post) { if (!res.locals.context.includes('post')) { res.locals.context.push('post'); } diff --git a/core/frontend/services/routing/helpers/entry-lookup.js b/core/frontend/services/routing/helpers/entry-lookup.js index c7884b23d7..5d953012fc 100644 --- a/core/frontend/services/routing/helpers/entry-lookup.js +++ b/core/frontend/services/routing/helpers/entry-lookup.js @@ -40,11 +40,7 @@ function entryLookup(postUrl, routerOptions, locals) { } let options = { - /** - * @deprecated: `author`, will be removed in Ghost 3.0 - * @TODO: Remove "author" when we drop v0.1 - */ - include: 'author,authors,tags' + include: 'authors,tags' }; if (config.get('enableDeveloperExperiments')) { diff --git a/core/frontend/services/routing/helpers/fetch-data.js b/core/frontend/services/routing/helpers/fetch-data.js index eaf1b3c227..40c5bd2c89 100644 --- a/core/frontend/services/routing/helpers/fetch-data.js +++ b/core/frontend/services/routing/helpers/fetch-data.js @@ -7,7 +7,7 @@ const Promise = require('bluebird'); const config = require('../../../../server/config'); // The default settings for a default post query -// @TODO: get rid of this config and use v0.1 or v2 config +// @TODO: get rid of this config and use v2, v3 config const queryDefaults = { type: 'browse', resource: 'posts', @@ -22,9 +22,8 @@ const defaultQueryOptions = { options: { /** * @deprecated: `author`, will be removed in Ghost 3.0 - * @TODO: Remove "author" when we drop v0.1 */ - include: 'author,authors,tags' + include: 'authors,tags' } }; diff --git a/core/frontend/services/url/configs/v0.1.js b/core/frontend/services/url/configs/v0.1.js deleted file mode 100644 index 55fa266a76..0000000000 --- a/core/frontend/services/url/configs/v0.1.js +++ /dev/null @@ -1,121 +0,0 @@ -/* - * These are the default resources and filters. - * They contain minimum filters for public accessibility of resources. - */ - -module.exports = [ - { - type: 'posts', - modelOptions: { - modelName: 'Post', - filter: 'visibility:public+status:published+page:false', - exclude: [ - 'title', - 'mobiledoc', - 'html', - 'plaintext', - 'amp', - 'codeinjection_head', - 'codeinjection_foot', - 'meta_title', - 'meta_description', - 'custom_excerpt', - 'og_image', - 'og_title', - 'og_description', - 'twitter_image', - 'twitter_title', - 'twitter_description', - 'custom_template', - 'locale' - ], - withRelated: ['tags', 'authors'], - withRelatedPrimary: { - primary_tag: 'tags', - primary_author: 'authors' - }, - withRelatedFields: { - tags: ['tags.id', 'tags.slug'], - authors: ['users.id', 'users.slug'] - } - }, - events: { - add: 'post.published', - update: 'post.published.edited', - remove: 'post.unpublished' - } - }, - { - type: 'pages', - modelOptions: { - modelName: 'Post', - exclude: [ - 'title', - 'mobiledoc', - 'html', - 'plaintext', - 'amp', - 'codeinjection_head', - 'codeinjection_foot', - 'meta_title', - 'meta_description', - 'custom_excerpt', - 'og_image', - 'og_title', - 'og_description', - 'twitter_image', - 'twitter_title', - 'twitter_description', - 'custom_template', - 'locale', - 'tags', - 'authors', - 'primary_tag', - 'primary_author' - ], - filter: 'visibility:public+status:published+page:true' - }, - events: { - add: 'page.published', - update: 'page.published.edited', - remove: 'page.unpublished' - } - }, - { - type: 'tags', - keep: ['id', 'slug', 'updated_at', 'created_at'], - modelOptions: { - modelName: 'Tag', - exclude: ['description', 'meta_title', 'meta_description'], - filter: 'visibility:public' - }, - events: { - add: 'tag.added', - update: 'tag.edited', - remove: 'tag.deleted' - } - }, - { - type: 'authors', - modelOptions: { - modelName: 'User', - exclude: [ - 'bio', - 'website', - 'location', - 'facebook', - 'twitter', - 'accessibility', - 'meta_title', - 'meta_description', - 'tour' - ], - filter: 'visibility:public' - }, - events: { - add: 'user.activated', - update: 'user.activated.edited', - remove: 'user.deleted' - } - } -]; diff --git a/core/test/acceptance/old/admin/tags_spec.js b/core/test/acceptance/old/admin/tags_spec.js index 6553ba82da..63f8877ccc 100644 --- a/core/test/acceptance/old/admin/tags_spec.js +++ b/core/test/acceptance/old/admin/tags_spec.js @@ -43,7 +43,9 @@ describe('Tag API', function () { jsonResponse.meta.pagination.should.have.property('next', null); jsonResponse.meta.pagination.should.have.property('prev', null); - jsonResponse.tags[0].url.should.eql(`${config.get('url')}/tag/pollo/`); + // returns 404 because this tag has no published posts + jsonResponse.tags[0].url.should.eql(`${config.get('url')}/404/`); + jsonResponse.tags[1].url.should.eql(`${config.get('url')}/tag/kitchen-sink/`); should.exist(jsonResponse.tags[0].count.posts); }); diff --git a/core/test/acceptance/old/admin/users_spec.js b/core/test/acceptance/old/admin/users_spec.js index 1b860414cd..a022b5ad2d 100644 --- a/core/test/acceptance/old/admin/users_spec.js +++ b/core/test/acceptance/old/admin/users_spec.js @@ -1,9 +1,7 @@ const should = require('should'); const _ = require('lodash'); const supertest = require('supertest'); -const moment = require('moment'); const Promise = require('bluebird'); -const ObjectId = require('bson-objectid'); const testUtils = require('../../../utils'); const localUtils = require('./utils'); const config = require('../../../../server/config'); @@ -13,12 +11,11 @@ const ghost = testUtils.startGhost; let request; describe('User API', function () { - let ghostServer, inactiveUser, admin; + let inactiveUser, admin; before(function () { return ghost() - .then(function (_ghostServer) { - ghostServer = _ghostServer; + .then(function () { request = supertest.agent(config.get('url')); }) .then(function () { @@ -82,10 +79,11 @@ describe('User API', function () { testUtils.API.isISO8601(jsonResponse.users[3].created_at).should.be.true(); testUtils.API.isISO8601(jsonResponse.users[3].updated_at).should.be.true(); - jsonResponse.users[0].url.should.eql(`${config.get('url')}/author/admin-user/`); + // only "ghost" author has a published post + jsonResponse.users[0].url.should.eql(`${config.get('url')}/404/`); jsonResponse.users[1].url.should.eql(`${config.get('url')}/404/`); jsonResponse.users[2].url.should.eql(`${config.get('url')}/author/ghost/`); - jsonResponse.users[3].url.should.eql(`${config.get('url')}/author/joe-bloggs/`); + jsonResponse.users[3].url.should.eql(`${config.get('url')}/404/`); done(); }); diff --git a/core/test/regression/site/UrlService_spec.js b/core/test/regression/site/UrlService_spec.js index c876cfe318..b32f998593 100644 --- a/core/test/regression/site/UrlService_spec.js +++ b/core/test/regression/site/UrlService_spec.js @@ -16,7 +16,7 @@ describe('Integration: services/url/UrlService', function () { models.init(); sinon.stub(themes, 'getActive').returns({ - engine: () => 'v0.1' + engine: () => 'v2' }); }); @@ -148,11 +148,11 @@ describe('Integration: services/url/UrlService', function () { } if (generator.router.getResourceType() === 'tags') { - generator.getUrls().length.should.eql(5); + generator.getUrls().length.should.eql(3); } if (generator.router.getResourceType() === 'authors') { - generator.getUrls().length.should.eql(5); + generator.getUrls().length.should.eql(2); } }); @@ -175,25 +175,22 @@ describe('Integration: services/url/UrlService', function () { url.should.eql('/tag/chorizo/'); url = urlService.getUrlByResourceId(testUtils.DataGenerator.forKnex.tags[3].id); - url.should.eql('/tag/pollo/'); - - url = urlService.getUrlByResourceId(testUtils.DataGenerator.forKnex.tags[4].id); - url.should.eql('/tag/injection/'); + url.should.eql('/404/'); // tags with no posts should not be public url = urlService.getUrlByResourceId(testUtils.DataGenerator.forKnex.users[0].id); url.should.eql('/author/joe-bloggs/'); url = urlService.getUrlByResourceId(testUtils.DataGenerator.forKnex.users[1].id); - url.should.eql('/author/smith-wellingsworth/'); + url.should.eql('/404/'); // users with no posts should not be public url = urlService.getUrlByResourceId(testUtils.DataGenerator.forKnex.users[2].id); - url.should.eql('/author/jimothy-bogendath/'); + url.should.eql('/404/'); // users with no posts should not be public url = urlService.getUrlByResourceId(testUtils.DataGenerator.forKnex.users[3].id); url.should.eql('/author/slimer-mcectoplasm/'); url = urlService.getUrlByResourceId(testUtils.DataGenerator.forKnex.users[4].id); - url.should.eql('/author/contributor/'); + url.should.eql('/404/'); // users with no posts should not be public }); it('getResource', function () { @@ -356,11 +353,11 @@ describe('Integration: services/url/UrlService', function () { } if (generator.router.getResourceType() === 'tags') { - generator.getUrls().length.should.eql(5); + generator.getUrls().length.should.eql(3); } if (generator.router.getResourceType() === 'authors') { - generator.getUrls().length.should.eql(5); + generator.getUrls().length.should.eql(2); } }); @@ -384,25 +381,22 @@ describe('Integration: services/url/UrlService', function () { url.should.eql('/category/chorizo/'); url = urlService.getUrlByResourceId(testUtils.DataGenerator.forKnex.tags[3].id); - url.should.eql('/category/pollo/'); - - url = urlService.getUrlByResourceId(testUtils.DataGenerator.forKnex.tags[4].id); - url.should.eql('/category/injection/'); + url.should.eql('/404/'); // tags with no posts should not be public url = urlService.getUrlByResourceId(testUtils.DataGenerator.forKnex.users[0].id); url.should.eql('/persons/joe-bloggs/'); url = urlService.getUrlByResourceId(testUtils.DataGenerator.forKnex.users[1].id); - url.should.eql('/persons/smith-wellingsworth/'); + url.should.eql('/404/'); // users with no posts should not be public url = urlService.getUrlByResourceId(testUtils.DataGenerator.forKnex.users[2].id); - url.should.eql('/persons/jimothy-bogendath/'); + url.should.eql('/404/'); // users with no posts should not be public url = urlService.getUrlByResourceId(testUtils.DataGenerator.forKnex.users[3].id); url.should.eql('/persons/slimer-mcectoplasm/'); url = urlService.getUrlByResourceId(testUtils.DataGenerator.forKnex.users[4].id); - url.should.eql('/persons/contributor/'); + url.should.eql('/404/'); // users with no posts should not be public }); }); @@ -553,11 +547,11 @@ describe('Integration: services/url/UrlService', function () { } if (generator.router.getResourceType() === 'tags') { - generator.getUrls().length.should.eql(5); + generator.getUrls().length.should.eql(3); } - if (generator.router.getResourceType() === 'users') { - generator.getUrls().length.should.eql(5); + if (generator.router.getResourceType() === 'authors') { + generator.getUrls().length.should.eql(2); } }); @@ -581,25 +575,22 @@ describe('Integration: services/url/UrlService', function () { url.should.eql('/category/chorizo/'); url = urlService.getUrlByResourceId(testUtils.DataGenerator.forKnex.tags[3].id); - url.should.eql('/category/pollo/'); - - url = urlService.getUrlByResourceId(testUtils.DataGenerator.forKnex.tags[4].id); - url.should.eql('/category/injection/'); + url.should.eql('/404/'); // tags with no posts should not be public url = urlService.getUrlByResourceId(testUtils.DataGenerator.forKnex.users[0].id); url.should.eql('/persons/joe-bloggs/'); url = urlService.getUrlByResourceId(testUtils.DataGenerator.forKnex.users[1].id); - url.should.eql('/persons/smith-wellingsworth/'); + url.should.eql('/404/'); // users with no posts should not be public url = urlService.getUrlByResourceId(testUtils.DataGenerator.forKnex.users[2].id); - url.should.eql('/persons/jimothy-bogendath/'); + url.should.eql('/404/'); // users with no posts should not be public url = urlService.getUrlByResourceId(testUtils.DataGenerator.forKnex.users[3].id); url.should.eql('/persons/slimer-mcectoplasm/'); url = urlService.getUrlByResourceId(testUtils.DataGenerator.forKnex.users[4].id); - url.should.eql('/persons/contributor/'); + url.should.eql('/404/'); // users with no posts should not be public }); }); }); diff --git a/core/test/regression/site/dynamic_routing_spec.js b/core/test/regression/site/dynamic_routing_spec.js index 29a09a856c..4eb361594e 100644 --- a/core/test/regression/site/dynamic_routing_spec.js +++ b/core/test/regression/site/dynamic_routing_spec.js @@ -394,10 +394,28 @@ describe('Dynamic Routing', function () { return testUtils.initData(); }).then(function () { return testUtils.fixtures.overrideOwnerUser(ownerSlug); + }).then(function (insertedUser) { + return testUtils.fixtures.insertPosts([ + testUtils.DataGenerator.forKnex.createPost({ + author_id: insertedUser.id + }) + ]); }).then(function () { return testUtils.fixtures.insertOneUser(lockedUser); - }).then(function () { + }).then(function (insertedUser) { + return testUtils.fixtures.insertPosts([ + testUtils.DataGenerator.forKnex.createPost({ + author_id: insertedUser.id + }) + ]); + }).then(() => { return testUtils.fixtures.insertOneUser(suspendedUser); + }).then(function (insertedUser) { + return testUtils.fixtures.insertPosts([ + testUtils.DataGenerator.forKnex.createPost({ + author_id: insertedUser.id + }) + ]); }).then(function () { done(); }).catch(done); diff --git a/core/test/regression/site/site_spec.js b/core/test/regression/site/site_spec.js index b0a690b266..b689ad89b4 100644 --- a/core/test/regression/site/site_spec.js +++ b/core/test/regression/site/site_spec.js @@ -17,1752 +17,6 @@ describe('Integration - Web - Site', function () { before(testUtils.teardown); before(testUtils.setup('users:roles', 'posts')); - describe('v0.1', function () { - const api = require('../../../server/api')['v0.1']; - - describe('default routes.yaml', function () { - before(function () { - testUtils.integrationTesting.defaultMocks(sinon, {amp: true, apps: true}); - testUtils.integrationTesting.overrideGhostConfig(configUtils); - - return testUtils.integrationTesting.initGhost() - .then(function () { - sinon.stub(themeService.getActive(), 'engine').withArgs('ghost-api').returns('v0.1'); - sinon.stub(themeService.getActive(), 'config').withArgs('posts_per_page').returns(2); - - app = siteApp({start: true}); - return testUtils.integrationTesting.urlService.waitTillFinished(); - }) - .then(() => { - return appsService.init(); - }); - }); - - before(function () { - configUtils.set('url', 'http://example.com'); - urlUtils.stubUrlUtilsFromConfig(); - }); - - beforeEach(function () { - sinon.spy(api.posts, 'browse'); - }); - - afterEach(function () { - api.posts.browse.restore(); - }); - - after(function () { - configUtils.restore(); - urlUtils.restore(); - sinon.restore(); - }); - - describe('behaviour: default cases', function () { - it('serve post', function () { - const req = { - secure: true, - method: 'GET', - url: '/html-ipsum/', - host: 'example.com' - }; - - return testUtils.mocks.express.invoke(app, req) - .then(function (response) { - response.statusCode.should.eql(200); - response.template.should.eql('post'); - }); - }); - - it('serve amp', function () { - const req = { - secure: true, - method: 'GET', - url: '/html-ipsum/amp/', - host: 'example.com' - }; - - return testUtils.mocks.express.invoke(app, req) - .then(function (response) { - response.statusCode.should.eql(200); - response.template.should.match(/amp\.hbs/); - response.body.should.match(/

HTML Ipsum Presents<\/h1>/); - }); - }); - - it('post not found', function () { - const req = { - secure: true, - method: 'GET', - url: '/not-found/', - host: 'example.com' - }; - - return testUtils.mocks.express.invoke(app, req) - .then(function (response) { - response.statusCode.should.eql(404); - response.template.should.eql('error-404'); - }); - }); - - it('serve static page', function () { - const req = { - secure: true, - method: 'GET', - url: '/static-page-test/', - host: 'example.com' - }; - - return testUtils.mocks.express.invoke(app, req) - .then(function (response) { - response.statusCode.should.eql(200); - response.template.should.eql('page'); - }); - }); - - it('serve author', function () { - const req = { - secure: true, - method: 'GET', - url: '/author/joe-bloggs/', - host: 'example.com' - }; - - return testUtils.mocks.express.invoke(app, req) - .then(function (response) { - const $ = cheerio.load(response.body); - - response.statusCode.should.eql(200); - response.template.should.eql('author'); - - $('.author-bio').length.should.equal(1); - }); - }); - - it('serve tag', function () { - const req = { - secure: true, - method: 'GET', - url: '/tag/bacon/', - host: 'example.com' - }; - - return testUtils.mocks.express.invoke(app, req) - .then(function (response) { - response.statusCode.should.eql(200); - response.template.should.eql('tag'); - - api.posts.browse.args[0][0].filter.should.eql('tags:\'bacon\'+tags.visibility:public'); - api.posts.browse.args[0][0].page.should.eql(1); - api.posts.browse.args[0][0].limit.should.eql(2); - }); - }); - - it('serve tag rss', function () { - const req = { - secure: true, - method: 'GET', - url: '/tag/bacon/rss/', - host: 'example.com' - }; - - return testUtils.mocks.express.invoke(app, req) - .then(function (response) { - response.statusCode.should.eql(200); - }); - }); - - it('serve collection', function () { - const req = { - secure: true, - method: 'GET', - url: '/', - host: 'example.com' - }; - - return testUtils.mocks.express.invoke(app, req) - .then(function (response) { - const $ = cheerio.load(response.body); - - response.statusCode.should.eql(200); - response.template.should.eql('index'); - - $('.post-card').length.should.equal(2); - - should.exist(response.res.locals.context); - should.exist(response.res.locals.version); - should.exist(response.res.locals.safeVersion); - should.exist(response.res.locals.safeVersion); - should.exist(response.res.locals.relativeUrl); - should.exist(response.res.locals.secure); - should.exist(response.res.routerOptions); - }); - }); - - it('serve collection: page 2', function () { - const req = { - secure: true, - method: 'GET', - url: '/page/2/', - host: 'example.com' - }; - - return testUtils.mocks.express.invoke(app, req) - .then(function (response) { - const $ = cheerio.load(response.body); - - response.statusCode.should.eql(200); - response.template.should.eql('index'); - - $('.post-card').length.should.equal(2); - }); - }); - - it('serve public asset', function () { - const req = { - secure: false, - method: 'GET', - url: '/public/ghost-sdk.js', - host: 'example.com' - }; - - return testUtils.mocks.express.invoke(app, req) - .then(function (response) { - response.statusCode.should.eql(200); - }); - }); - - it('serve theme asset', function () { - const req = { - secure: true, - method: 'GET', - url: '/assets/css/screen.css', - host: 'example.com' - }; - - return testUtils.mocks.express.invoke(app, req) - .then(function (response) { - response.statusCode.should.eql(200); - }); - }); - }); - - describe('behaviour: prettify', function () { - it('url without slash', function () { - const req = { - secure: false, - method: 'GET', - url: '/prettify-me', - host: 'example.com' - }; - - return testUtils.mocks.express.invoke(app, req) - .then(function (response) { - response.statusCode.should.eql(301); - response.headers.location.should.eql('/prettify-me/'); - }); - }); - }); - - describe('behaviour: url redirects', function () { - describe('pagination', function () { - it('redirect /page/1/ to /', function () { - const req = { - secure: false, - host: 'example.com', - method: 'GET', - url: '/page/1/' - }; - - return testUtils.mocks.express.invoke(app, req) - .then(function (response) { - response.statusCode.should.eql(301); - response.headers.location.should.eql('/'); - }); - }); - }); - - describe('rss', function () { - it('redirect /feed/ to /rss/', function () { - const req = { - secure: false, - host: 'example.com', - method: 'GET', - url: '/feed/' - }; - - return testUtils.mocks.express.invoke(app, req) - .then(function (response) { - response.statusCode.should.eql(301); - response.headers.location.should.eql('/rss/'); - }); - }); - }); - }); - }); - - describe('https', function () { - before(function () { - configUtils.set('url', 'https://example.com'); - urlUtils.stubUrlUtilsFromConfig(); - }); - - after(function () { - urlUtils.restore(); - configUtils.restore(); - }); - - describe('protocol', function () { - it('blog is https, request is http', function () { - const req = { - secure: false, - host: 'example.com', - method: 'GET', - url: '/html-ipsum' - }; - - return testUtils.mocks.express.invoke(app, req) - .then(function (response) { - response.statusCode.should.eql(301); - response.headers.location.should.eql('https://example.com/html-ipsum/'); - }); - }); - - it('blog is https, request is http, trailing slash exists already', function () { - const req = { - secure: false, - method: 'GET', - url: '/html-ipsum/', - host: 'example.com' - }; - - return testUtils.mocks.express.invoke(app, req) - .then(function (response) { - response.statusCode.should.eql(301); - response.headers.location.should.eql('https://example.com/html-ipsum/'); - }); - }); - }); - - describe('assets', function () { - it('blog is https, request is http', function () { - const req = { - secure: false, - method: 'GET', - url: '/public/ghost-sdk.js', - host: 'example.com' - }; - - return testUtils.mocks.express.invoke(app, req) - .then(function (response) { - response.statusCode.should.eql(301); - response.headers.location.should.eql('https://example.com/public/ghost-sdk.js'); - }); - }); - - it('blog is https, request is http', function () { - const req = { - secure: false, - method: 'GET', - url: '/favicon.png', - host: 'example.com' - }; - - return testUtils.mocks.express.invoke(app, req) - .then(function (response) { - response.statusCode.should.eql(301); - response.headers.location.should.eql('https://example.com/favicon.png'); - }); - }); - - it('blog is https, request is http', function () { - const req = { - secure: false, - method: 'GET', - url: '/assets/css/main.css', - host: 'example.com' - }; - - return testUtils.mocks.express.invoke(app, req) - .then(function (response) { - response.statusCode.should.eql(301); - response.headers.location.should.eql('https://example.com/assets/css/main.css'); - }); - }); - }); - }); - - describe('extended routes.yaml: collections', function () { - describe('2 collections', function () { - before(function () { - sinon.stub(frontendSettingsService, 'get').returns({ - routes: { - '/': { - templates: ['home'] - } - }, - - collections: { - '/podcast/': { - permalink: '/podcast/:slug/', - filter: 'featured:true' - }, - - '/something/': { - permalink: '/something/:slug/', - filter: 'featured:false' - } - }, - - taxonomies: { - tag: '/categories/:slug/', - author: '/authors/:slug/' - } - }); - - testUtils.integrationTesting.urlService.resetGenerators(); - testUtils.integrationTesting.defaultMocks(sinon, {theme: 'test-theme'}); - - return testUtils.integrationTesting.initGhost() - .then(function () { - sinon.stub(themeService.getActive(), 'engine').withArgs('ghost-api').returns('v0.1'); - sinon.stub(themeService.getActive(), 'config').withArgs('posts_per_page').returns(2); - - app = siteApp({start: true}); - return testUtils.integrationTesting.urlService.waitTillFinished(); - }); - }); - - beforeEach(function () { - testUtils.integrationTesting.overrideGhostConfig(configUtils); - }); - - afterEach(function () { - configUtils.restore(); - urlUtils.restore(); - }); - - after(function () { - sinon.restore(); - }); - - it('serve static route', function () { - const req = { - secure: true, - method: 'GET', - url: '/', - host: 'example.com' - }; - - return testUtils.mocks.express.invoke(app, req) - .then(function (response) { - response.statusCode.should.eql(200); - response.template.should.eql('home'); - }); - }); - - it('serve rss', function () { - const req = { - secure: true, - method: 'GET', - url: '/podcast/rss/', - host: 'example.com' - }; - - return testUtils.mocks.express.invoke(app, req) - .then(function (response) { - response.statusCode.should.eql(200); - }); - }); - - it('serve post', function () { - const req = { - secure: true, - method: 'GET', - url: '/something/html-ipsum/', - host: 'example.com' - }; - - return testUtils.mocks.express.invoke(app, req) - .then(function (response) { - response.statusCode.should.eql(200); - response.template.should.eql('post'); - }); - }); - - it('serve collection: podcast with default template', function () { - const req = { - secure: true, - method: 'GET', - url: '/podcast/', - host: 'example.com' - }; - - return testUtils.mocks.express.invoke(app, req) - .then(function (response) { - const $ = cheerio.load(response.body); - - response.statusCode.should.eql(200); - response.template.should.eql('index'); - - $('.post-card').length.should.equal(2); - }); - }); - - it('serve collection: something with custom template', function () { - const req = { - secure: true, - method: 'GET', - url: '/something/', - host: 'example.com' - }; - - return testUtils.mocks.express.invoke(app, req) - .then(function (response) { - const $ = cheerio.load(response.body); - - response.statusCode.should.eql(200); - response.template.should.eql('something'); - }); - }); - }); - - describe('no collections', function () { - before(function () { - sinon.stub(frontendSettingsService, 'get').returns({ - routes: { - '/something/': {templates: ['something']} - }, - collections: {}, - taxonomies: {} - }); - - testUtils.integrationTesting.urlService.resetGenerators(); - testUtils.integrationTesting.defaultMocks(sinon, {theme: 'test-theme'}); - - return testUtils.integrationTesting.initGhost() - .then(function () { - sinon.stub(themeService.getActive(), 'engine').withArgs('ghost-api').returns('v0.1'); - sinon.stub(themeService.getActive(), 'config').withArgs('posts_per_page').returns(2); - - app = siteApp({start: true}); - return testUtils.integrationTesting.urlService.waitTillFinished(); - }); - }); - - beforeEach(function () { - testUtils.integrationTesting.overrideGhostConfig(configUtils); - }); - - afterEach(function () { - configUtils.restore(); - urlUtils.restore(); - }); - - after(function () { - sinon.restore(); - }); - - it('serve route', function () { - const req = { - secure: true, - method: 'GET', - url: '/something/', - host: 'example.com' - }; - - return testUtils.mocks.express.invoke(app, req) - .then(function (response) { - response.statusCode.should.eql(200); - response.template.should.eql('something'); - }); - }); - }); - - describe('static permalink route', function () { - before(function () { - sinon.stub(frontendSettingsService, 'get').returns({ - routes: {}, - - collections: { - '/podcast/': { - permalink: '/featured/', - filter: 'featured:true' - }, - - '/': { - permalink: '/:slug/' - } - }, - - taxonomies: {} - }); - - testUtils.integrationTesting.urlService.resetGenerators(); - testUtils.integrationTesting.defaultMocks(sinon); - - return testUtils.integrationTesting.initGhost() - .then(function () { - sinon.stub(themeService.getActive(), 'engine').withArgs('ghost-api').returns('v0.1'); - sinon.stub(themeService.getActive(), 'config').withArgs('posts_per_page').returns(2); - - app = siteApp({start: true}); - return testUtils.integrationTesting.urlService.waitTillFinished(); - }); - }); - - beforeEach(function () { - testUtils.integrationTesting.overrideGhostConfig(configUtils); - }); - - afterEach(function () { - configUtils.restore(); - urlUtils.restore(); - }); - - after(function () { - sinon.restore(); - }); - - it('serve post', function () { - const req = { - secure: true, - method: 'GET', - url: '/featured/', - host: 'example.com' - }; - - return testUtils.mocks.express.invoke(app, req) - .then(function (response) { - // We can't find a post with the slug "featured" - response.statusCode.should.eql(404); - response.template.should.eql('error-404'); - }); - }); - - it('serve post', function () { - const req = { - secure: true, - method: 'GET', - url: '/html-ipsum/', - host: 'example.com' - }; - - return testUtils.mocks.express.invoke(app, req) - .then(function (response) { - response.statusCode.should.eql(200); - response.template.should.eql('post'); - }); - }); - - it('serve author', function () { - const req = { - secure: true, - method: 'GET', - url: '/author/joe-bloggs/', - host: 'example.com' - }; - - return testUtils.mocks.express.invoke(app, req) - .then(function (response) { - response.statusCode.should.eql(404); - response.template.should.eql('error-404'); - }); - }); - - it('serve tag', function () { - const req = { - secure: true, - method: 'GET', - url: '/tag/bacon/', - host: 'example.com' - }; - - return testUtils.mocks.express.invoke(app, req) - .then(function (response) { - response.statusCode.should.eql(404); - response.template.should.eql('error-404'); - }); - }); - }); - - describe('primary author permalink', function () { - before(function () { - sinon.stub(frontendSettingsService, 'get').returns({ - routes: {}, - - collections: { - '/something/': { - permalink: '/:primary_author/:slug/' - } - }, - - taxonomies: {} - }); - - testUtils.integrationTesting.urlService.resetGenerators(); - testUtils.integrationTesting.defaultMocks(sinon); - - return testUtils.integrationTesting.initGhost() - .then(function () { - sinon.stub(themeService.getActive(), 'engine').withArgs('ghost-api').returns('v0.1'); - sinon.stub(themeService.getActive(), 'config').withArgs('posts_per_page').returns(2); - - app = siteApp({start: true}); - return testUtils.integrationTesting.urlService.waitTillFinished(); - }); - }); - - beforeEach(function () { - testUtils.integrationTesting.overrideGhostConfig(configUtils); - }); - - afterEach(function () { - configUtils.restore(); - urlUtils.restore(); - }); - - after(function () { - sinon.restore(); - }); - - it('serve post', function () { - const req = { - secure: true, - method: 'GET', - url: '/joe-bloggs/html-ipsum/', - host: 'example.com' - }; - - return testUtils.mocks.express.invoke(app, req) - .then(function (response) { - response.statusCode.should.eql(200); - response.template.should.eql('post'); - }); - }); - - it('post without author', function () { - const req = { - secure: true, - method: 'GET', - url: '/html-ipsum/', - host: 'example.com' - }; - - return testUtils.mocks.express.invoke(app, req) - .then(function (response) { - response.statusCode.should.eql(404); - response.template.should.eql('error-404'); - }); - }); - - it('page', function () { - const req = { - secure: true, - method: 'GET', - url: '/static-page-test/', - host: 'example.com' - }; - - return testUtils.mocks.express.invoke(app, req) - .then(function (response) { - response.statusCode.should.eql(200); - response.template.should.eql('page'); - }); - }); - }); - - describe('primary tag permalink', function () { - before(function () { - sinon.stub(frontendSettingsService, 'get').returns({ - routes: {}, - - collections: { - '/something/': { - permalink: '/something/:primary_tag/:slug/' - } - }, - - taxonomies: {} - }); - - testUtils.integrationTesting.urlService.resetGenerators(); - testUtils.integrationTesting.defaultMocks(sinon); - - return testUtils.integrationTesting.initGhost() - .then(function () { - sinon.stub(themeService.getActive(), 'engine').withArgs('ghost-api').returns('v0.1'); - sinon.stub(themeService.getActive(), 'config').withArgs('posts_per_page').returns(2); - - app = siteApp({start: true}); - return testUtils.integrationTesting.urlService.waitTillFinished(); - }); - }); - - beforeEach(function () { - testUtils.integrationTesting.overrideGhostConfig(configUtils); - }); - - afterEach(function () { - configUtils.restore(); - urlUtils.restore(); - }); - - after(function () { - sinon.restore(); - }); - - it('serve post', function () { - const req = { - secure: true, - method: 'GET', - url: '/something/kitchen-sink/html-ipsum/', - host: 'example.com' - }; - - return testUtils.mocks.express.invoke(app, req) - .then(function (response) { - response.statusCode.should.eql(200); - response.template.should.eql('post'); - }); - }); - - it('post without tag', function () { - const req = { - secure: true, - method: 'GET', - url: '/something/html-ipsum/', - host: 'example.com' - }; - - return testUtils.mocks.express.invoke(app, req) - .then(function (response) { - response.statusCode.should.eql(404); - response.template.should.eql('error-404'); - }); - }); - - it('post without tag', function () { - const req = { - secure: true, - method: 'GET', - url: '/html-ipsum/', - host: 'example.com' - }; - - return testUtils.mocks.express.invoke(app, req) - .then(function (response) { - response.statusCode.should.eql(404); - response.template.should.eql('error-404'); - }); - }); - - it('page', function () { - const req = { - secure: true, - method: 'GET', - url: '/static-page-test/', - host: 'example.com' - }; - - return testUtils.mocks.express.invoke(app, req) - .then(function (response) { - response.statusCode.should.eql(200); - response.template.should.eql('page'); - }); - }); - }); - - describe('collection with data key', function () { - before(function () { - sinon.stub(frontendSettingsService, 'get').returns({ - routes: {}, - - collections: { - '/food/': { - permalink: '/food/:slug/', - filter: 'tag:bacon', - data: { - query: { - tag: { - controller: 'tags', - resource: 'tags', - type: 'read', - options: { - slug: 'bacon' - } - } - }, - router: { - tags: [{redirect: true, slug: 'bacon'}] - } - } - }, - '/sport/': { - permalink: '/sport/:slug/', - filter: 'tag:pollo', - data: { - query: { - apollo: { - controller: 'tags', - resource: 'tags', - type: 'read', - options: { - slug: 'pollo' - } - } - }, - router: { - tags: [{redirect: false, slug: 'bacon'}] - } - } - } - }, - - taxonomies: { - tag: '/categories/:slug/', - author: '/authors/:slug/' - } - }); - - testUtils.integrationTesting.urlService.resetGenerators(); - testUtils.integrationTesting.defaultMocks(sinon); - - return testUtils.integrationTesting.initGhost() - .then(function () { - sinon.stub(themeService.getActive(), 'engine').withArgs('ghost-api').returns('v0.1'); - sinon.stub(themeService.getActive(), 'config').withArgs('posts_per_page').returns(2); - - app = siteApp({start: true}); - return testUtils.integrationTesting.urlService.waitTillFinished(); - }); - }); - - beforeEach(function () { - testUtils.integrationTesting.overrideGhostConfig(configUtils); - }); - - afterEach(function () { - configUtils.restore(); - urlUtils.restore(); - }); - - after(function () { - sinon.restore(); - }); - - it('serve /food/', function () { - const req = { - secure: true, - method: 'GET', - url: '/food/', - host: 'example.com' - }; - - return testUtils.mocks.express.invoke(app, req) - .then(function (response) { - response.statusCode.should.eql(200); - response.template.should.eql('index'); - }); - }); - - it('serve bacon tag', function () { - const req = { - secure: true, - method: 'GET', - url: '/categories/bacon/', - host: 'example.com' - }; - - return testUtils.mocks.express.invoke(app, req) - .then(function (response) { - response.statusCode.should.eql(301); - }); - }); - - it('serve /sport/', function () { - const req = { - secure: true, - method: 'GET', - url: '/sport/', - host: 'example.com' - }; - - return testUtils.mocks.express.invoke(app, req) - .then(function (response) { - response.statusCode.should.eql(200); - response.template.should.eql('index'); - }); - }); - - it('serve pollo tag', function () { - const req = { - secure: true, - method: 'GET', - url: '/categories/pollo/', - host: 'example.com' - }; - - return testUtils.mocks.express.invoke(app, req) - .then(function (response) { - response.statusCode.should.eql(200); - }); - }); - }); - }); - - describe('extended routes.yaml: templates', function () { - describe('default template, no template', function () { - before(function () { - sinon.stub(frontendSettingsService, 'get').returns({ - routes: {}, - - collections: { - '/': { - permalink: '/:slug/', - templates: ['default'] - }, - '/magic/': { - permalink: '/magic/:slug/' - } - } - }); - - testUtils.integrationTesting.urlService.resetGenerators(); - testUtils.integrationTesting.defaultMocks(sinon); - - return testUtils.integrationTesting.initGhost() - .then(function () { - sinon.stub(themeService.getActive(), 'engine').withArgs('ghost-api').returns('v0.1'); - sinon.stub(themeService.getActive(), 'config').withArgs('posts_per_page').returns(2); - - app = siteApp({start: true}); - return testUtils.integrationTesting.urlService.waitTillFinished(); - }); - }); - - beforeEach(function () { - testUtils.integrationTesting.overrideGhostConfig(configUtils); - }); - - afterEach(function () { - configUtils.restore(); - urlUtils.restore(); - }); - - after(function () { - sinon.restore(); - }); - - it('serve collection', function () { - const req = { - secure: true, - method: 'GET', - url: '/', - host: 'example.com' - }; - - return testUtils.mocks.express.invoke(app, req) - .then(function (response) { - response.statusCode.should.eql(200); - response.template.should.eql('default'); - }); - }); - - it('serve second collectiom', function () { - const req = { - secure: true, - method: 'GET', - url: '/magic/', - host: 'example.com' - }; - - return testUtils.mocks.express.invoke(app, req) - .then(function (response) { - response.statusCode.should.eql(200); - response.template.should.eql('index'); - }); - }); - }); - - describe('two templates', function () { - before(function () { - sinon.stub(frontendSettingsService, 'get').returns({ - routes: {}, - - collections: { - '/': { - permalink: '/:slug/', - templates: ['something', 'default'] - } - } - }); - - testUtils.integrationTesting.urlService.resetGenerators(); - testUtils.integrationTesting.defaultMocks(sinon); - - return testUtils.integrationTesting.initGhost() - .then(function () { - sinon.stub(themeService.getActive(), 'engine').withArgs('ghost-api').returns('v0.1'); - sinon.stub(themeService.getActive(), 'config').withArgs('posts_per_page').returns(2); - - app = siteApp({start: true}); - return testUtils.integrationTesting.urlService.waitTillFinished(); - }); - }); - - beforeEach(function () { - testUtils.integrationTesting.overrideGhostConfig(configUtils); - }); - - afterEach(function () { - configUtils.restore(); - urlUtils.restore(); - }); - - after(function () { - sinon.restore(); - }); - - it('serve collection', function () { - const req = { - secure: true, - method: 'GET', - url: '/', - host: 'example.com' - }; - - return testUtils.mocks.express.invoke(app, req) - .then(function (response) { - response.statusCode.should.eql(200); - response.template.should.eql('default'); - }); - }); - }); - - describe('home.hbs priority', function () { - before(function () { - sinon.stub(frontendSettingsService, 'get').returns({ - routes: {}, - - collections: { - '/': { - permalink: '/:slug/', - templates: ['something', 'default'] - }, - '/magic/': { - permalink: '/magic/:slug/', - templates: ['something', 'default'] - } - } - }); - - testUtils.integrationTesting.urlService.resetGenerators(); - testUtils.integrationTesting.defaultMocks(sinon, {theme: 'test-theme'}); - - return testUtils.integrationTesting.initGhost() - .then(function () { - sinon.stub(themeService.getActive(), 'engine').withArgs('ghost-api').returns('v0.1'); - sinon.stub(themeService.getActive(), 'config').withArgs('posts_per_page').returns(2); - - app = siteApp({start: true}); - return testUtils.integrationTesting.urlService.waitTillFinished(); - }); - }); - - beforeEach(function () { - testUtils.integrationTesting.overrideGhostConfig(configUtils); - }); - - afterEach(function () { - configUtils.restore(); - urlUtils.restore(); - }); - - after(function () { - sinon.restore(); - }); - - it('serve collection', function () { - const req = { - secure: true, - method: 'GET', - url: '/', - host: 'example.com' - }; - - return testUtils.mocks.express.invoke(app, req) - .then(function (response) { - response.statusCode.should.eql(200); - response.template.should.eql('home'); - }); - }); - - it('serve second page collection: should use index.hbs', function () { - const req = { - secure: true, - method: 'GET', - url: '/magic/', - host: 'example.com' - }; - - return testUtils.mocks.express.invoke(app, req) - .then(function (response) { - response.statusCode.should.eql(200); - response.template.should.eql('something'); - }); - }); - }); - }); - - describe('extended routes.yaml: routes', function () { - describe('channels', function () { - before(testUtils.integrationTesting.urlService.resetGenerators); - before(testUtils.teardown); - before(testUtils.setup('users:roles', 'posts')); - - before(function () { - testUtils.integrationTesting.defaultMocks(sinon, {theme: 'test-theme-channels'}); - - sinon.stub(frontendSettingsService, 'get').returns({ - routes: { - '/channel1/': { - controller: 'channel', - filter: 'tag:kitchen-sink', - data: { - query: { - tag: { - controller: 'tags', - resource: 'tags', - type: 'read', - options: { - slug: 'kitchen-sink' - } - } - }, - router: { - tags: [{redirect: true, slug: 'kitchen-sink'}] - } - } - }, - - '/channel2/': { - controller: 'channel', - filter: 'tag:bacon', - data: { - query: { - tag: { - controller: 'tags', - resource: 'tags', - type: 'read', - options: { - slug: 'bacon' - } - } - }, - router: { - tags: [{redirect: true, slug: 'bacon'}] - } - }, - templates: ['default'] - }, - - '/channel3/': { - controller: 'channel', - filter: 'author:joe-bloggs', - data: { - query: { - joe: { - controller: 'users', - resource: 'users', - type: 'read', - options: { - slug: 'joe-bloggs', - redirect: false - } - } - }, - router: { - authors: [{redirect: false, slug: 'joe-bloggs'}] - } - } - }, - - '/channel4/': { - controller: 'channel', - filter: 'author:joe-bloggs' - }, - - '/channel5/': { - controller: 'channel', - data: { - query: { - tag: { - controller: 'users', - resource: 'users', - type: 'read', - options: { - slug: 'joe-bloggs', - redirect: false - } - } - }, - router: { - authors: [{redirect: false, slug: 'joe-bloggs'}] - } - } - }, - - '/channel6/': { - controller: 'channel', - data: { - query: { - post: { - controller: 'posts', - resource: 'posts', - type: 'read', - options: { - slug: 'html-ipsum', - redirect: true - } - } - }, - router: { - posts: [{redirect: true, slug: 'html-ipsum'}] - } - } - }, - - '/channel7/': { - controller: 'channel', - data: { - query: { - post: { - controller: 'posts', - resource: 'posts', - type: 'read', - options: { - slug: 'static-page-test', - redirect: true - } - } - }, - router: { - posts: [{redirect: true, slug: 'static-page-test'}] - } - } - } - }, - - collections: { - '/': { - permalink: '/:slug/' - } - }, - - taxonomies: { - tag: '/tag/:slug/', - author: '/author/:slug/' - } - }); - - return testUtils.integrationTesting.initGhost() - .then(function () { - sinon.stub(themeService.getActive(), 'engine').withArgs('ghost-api').returns('v0.1'); - sinon.stub(themeService.getActive(), 'config').withArgs('posts_per_page').returns(10); - - app = siteApp({start: true}); - return testUtils.integrationTesting.urlService.waitTillFinished(); - }); - }); - - beforeEach(function () { - testUtils.integrationTesting.overrideGhostConfig(configUtils); - }); - - afterEach(function () { - configUtils.restore(); - urlUtils.restore(); - }); - - after(function () { - sinon.restore(); - }); - - it('serve channel 1', function () { - const req = { - secure: true, - method: 'GET', - url: '/channel1/', - host: 'example.com' - }; - - return testUtils.mocks.express.invoke(app, req) - .then(function (response) { - const $ = cheerio.load(response.body); - - response.statusCode.should.eql(200); - response.template.should.eql('index'); - - $('.post-card').length.should.equal(2); - }); - }); - - it('serve channel 1: rss', function () { - const req = { - secure: true, - method: 'GET', - url: '/channel1/rss/', - host: 'example.com' - }; - - return testUtils.mocks.express.invoke(app, req) - .then(function (response) { - response.statusCode.should.eql(200); - response.headers['content-type'].should.eql('text/xml; charset=UTF-8'); - }); - }); - - it('serve channel 2', function () { - const req = { - secure: true, - method: 'GET', - url: '/channel2/', - host: 'example.com' - }; - - return testUtils.mocks.express.invoke(app, req) - .then(function (response) { - const $ = cheerio.load(response.body); - - response.statusCode.should.eql(200); - response.template.should.eql('default'); - - // default tempalte does not list posts - $('.post-card').length.should.equal(0); - }); - }); - - it('serve channel 3', function () { - const req = { - secure: true, - method: 'GET', - url: '/channel3/', - host: 'example.com' - }; - - return testUtils.mocks.express.invoke(app, req) - .then(function (response) { - const $ = cheerio.load(response.body); - - response.statusCode.should.eql(200); - response.template.should.eql('channel3'); - }); - }); - - it('serve channel 4', function () { - const req = { - secure: true, - method: 'GET', - url: '/channel4/', - host: 'example.com' - }; - - return testUtils.mocks.express.invoke(app, req) - .then(function (response) { - const $ = cheerio.load(response.body); - - response.statusCode.should.eql(200); - response.template.should.eql('index'); - - $('.post-card').length.should.equal(4); - }); - }); - - it('serve channel 5', function () { - const req = { - secure: true, - method: 'GET', - url: '/channel5/', - host: 'example.com' - }; - - return testUtils.mocks.express.invoke(app, req) - .then(function (response) { - const $ = cheerio.load(response.body); - - response.statusCode.should.eql(200); - response.template.should.eql('index'); - - $('.post-card').length.should.equal(4); - }); - }); - - it('serve channel 6', function () { - const req = { - secure: true, - method: 'GET', - url: '/channel6/', - host: 'example.com' - }; - - return testUtils.mocks.express.invoke(app, req) - .then(function (response) { - const $ = cheerio.load(response.body); - - response.statusCode.should.eql(200); - response.template.should.eql('index'); - - $('.post-card').length.should.equal(4); - }); - }); - - it('serve channel 7', function () { - const req = { - secure: true, - method: 'GET', - url: '/channel7/', - host: 'example.com' - }; - - return testUtils.mocks.express.invoke(app, req) - .then(function (response) { - const $ = cheerio.load(response.body); - - response.statusCode.should.eql(200); - response.template.should.eql('index'); - - $('.post-card').length.should.equal(4); - }); - }); - - it('serve kitching-sink: redirect', function () { - const req = { - secure: true, - method: 'GET', - url: '/tag/kitchen-sink/', - host: 'example.com' - }; - - return testUtils.mocks.express.invoke(app, req) - .then(function (response) { - response.statusCode.should.eql(301); - response.headers.location.should.eql('/channel1/'); - }); - }); - - it('serve html-ipsum: redirect', function () { - const req = { - secure: true, - method: 'GET', - url: '/html-ipsum/', - host: 'example.com' - }; - - return testUtils.mocks.express.invoke(app, req) - .then(function (response) { - response.statusCode.should.eql(301); - response.headers.location.should.eql('/channel6/'); - }); - }); - - it('serve html-ipsum: redirect', function () { - const req = { - secure: true, - method: 'GET', - url: '/static-page-test/', - host: 'example.com' - }; - - return testUtils.mocks.express.invoke(app, req) - .then(function (response) { - response.statusCode.should.eql(301); - response.headers.location.should.eql('/channel7/'); - }); - }); - - it('serve chorizo: no redirect', function () { - const req = { - secure: true, - method: 'GET', - url: '/tag/chorizo/', - host: 'example.com' - }; - - return testUtils.mocks.express.invoke(app, req) - .then(function (response) { - response.statusCode.should.eql(200); - }); - }); - - it('serve joe-bloggs', function () { - const req = { - secure: true, - method: 'GET', - url: '/author/joe-bloggs/', - host: 'example.com' - }; - - return testUtils.mocks.express.invoke(app, req) - .then(function (response) { - response.statusCode.should.eql(200); - }); - }); - }); - }); - - describe('extended routes.yaml (5): rss override', function () { - before(function () { - sinon.stub(frontendSettingsService, 'get').returns({ - routes: { - '/podcast/rss/': { - templates: ['podcast/rss'], - content_type: 'text/xml' - }, - '/cooking/': { - controller: 'channel', - rss: false - }, - '/flat/': { - controller: 'channel' - } - }, - - collections: { - '/podcast/': { - permalink: '/:slug/', - filter: 'featured:true', - templates: ['home'], - rss: false - }, - '/music/': { - permalink: '/:slug/', - rss: false - }, - '/': { - permalink: '/:slug/' - } - }, - - taxonomies: {} - }); - - testUtils.integrationTesting.urlService.resetGenerators(); - testUtils.integrationTesting.defaultMocks(sinon, {theme: 'test-theme'}); - - return testUtils.integrationTesting.initGhost() - .then(function () { - sinon.stub(themeService.getActive(), 'engine').withArgs('ghost-api').returns('v0.1'); - sinon.stub(themeService.getActive(), 'config').withArgs('posts_per_page').returns(2); - - app = siteApp({start: true}); - return testUtils.integrationTesting.urlService.waitTillFinished(); - }); - }); - - beforeEach(function () { - testUtils.integrationTesting.overrideGhostConfig(configUtils); - }); - - afterEach(function () { - configUtils.restore(); - urlUtils.restore(); - }); - - after(function () { - sinon.restore(); - }); - - it('serve /rss/', function () { - const req = { - secure: true, - method: 'GET', - url: '/rss/', - host: 'example.com' - }; - - return testUtils.mocks.express.invoke(app, req) - .then(function (response) { - response.statusCode.should.eql(200); - }); - }); - - it('serve /music/rss/', function () { - const req = { - secure: true, - method: 'GET', - url: '/music/rss/', - host: 'example.com' - }; - - return testUtils.mocks.express.invoke(app, req) - .then(function (response) { - response.statusCode.should.eql(404); - }); - }); - - it('serve /cooking/rss/', function () { - const req = { - secure: true, - method: 'GET', - url: '/cooking/rss/', - host: 'example.com' - }; - - return testUtils.mocks.express.invoke(app, req) - .then(function (response) { - response.statusCode.should.eql(404); - }); - }); - - it('serve /flat/rss/', function () { - const req = { - secure: true, - method: 'GET', - url: '/flat/rss/', - host: 'example.com' - }; - - return testUtils.mocks.express.invoke(app, req) - .then(function (response) { - response.statusCode.should.eql(200); - }); - }); - - it('serve /podcast/rss/', function () { - const req = { - secure: true, - method: 'GET', - url: '/podcast/rss/', - host: 'example.com' - }; - - return testUtils.mocks.express.invoke(app, req) - .then(function (response) { - response.statusCode.should.eql(200); - response.template.should.eql('podcast/rss'); - response.headers['content-type'].should.eql('text/xml; charset=utf-8'); - response.body.match(//g).length.should.eql(2); - }); - }); - - it('serve /podcast/', function () { - const req = { - secure: true, - method: 'GET', - url: '/podcast/', - host: 'example.com' - }; - - return testUtils.mocks.express.invoke(app, req) - .then(function (response) { - const $ = cheerio.load(response.body); - response.statusCode.should.eql(200); - $('head link')[2].attribs.href.should.eql('https://127.0.0.1:2369/rss/'); - }); - }); - }); - }); - describe('v2', function () { let postSpy; @@ -5602,7 +3856,9 @@ describe('Integration - Web - Site', function () { before(function () { sinon.stub(frontendSettingsService, 'get').returns({ routes: { - '/': 'home' + '/': { + templates: ['home'] + } }, collections: { @@ -5624,7 +3880,7 @@ describe('Integration - Web - Site', function () { }); testUtils.integrationTesting.urlService.resetGenerators(); - testUtils.integrationTesting.defaultMocks(sinon); + testUtils.integrationTesting.defaultMocks(sinon, {theme: 'test-theme'}); return testUtils.integrationTesting.initGhost() .then(function () { @@ -5660,7 +3916,7 @@ describe('Integration - Web - Site', function () { return testUtils.mocks.express.invoke(app, req) .then(function (response) { response.statusCode.should.eql(200); - response.template.should.eql('default'); + response.template.should.eql('home'); }); }); @@ -5712,7 +3968,7 @@ describe('Integration - Web - Site', function () { }); }); - it('serve collection: something', function () { + it('serve collection: something with custom template', function () { const req = { secure: true, method: 'GET', @@ -5725,9 +3981,7 @@ describe('Integration - Web - Site', function () { const $ = cheerio.load(response.body); response.statusCode.should.eql(200); - response.template.should.eql('index'); - - $('.post-card').length.should.equal(2); + response.template.should.eql('something'); }); }); }); @@ -5736,14 +3990,14 @@ describe('Integration - Web - Site', function () { before(function () { sinon.stub(frontendSettingsService, 'get').returns({ routes: { - '/test/': 'test' + '/something/': {templates: ['something']} }, collections: {}, taxonomies: {} }); testUtils.integrationTesting.urlService.resetGenerators(); - testUtils.integrationTesting.defaultMocks(sinon); + testUtils.integrationTesting.defaultMocks(sinon, {theme: 'test-theme'}); return testUtils.integrationTesting.initGhost() .then(function () { @@ -5772,14 +4026,14 @@ describe('Integration - Web - Site', function () { const req = { secure: true, method: 'GET', - url: '/test/', + url: '/something/', host: 'example.com' }; return testUtils.mocks.express.invoke(app, req) .then(function (response) { response.statusCode.should.eql(200); - response.template.should.eql('default'); + response.template.should.eql('something'); }); }); }); @@ -6097,7 +4351,8 @@ describe('Integration - Web - Site', function () { router: { pages: [{redirect: true, slug: 'static-page-test'}] } - } + }, + templates: ['page'] } }, diff --git a/core/test/unit/services/routing/controllers/preview_spec.js b/core/test/unit/services/routing/controllers/preview_spec.js index f6b810f06c..5403cd8e6a 100644 --- a/core/test/unit/services/routing/controllers/preview_spec.js +++ b/core/test/unit/services/routing/controllers/preview_spec.js @@ -27,119 +27,6 @@ describe('Unit - services/routing/controllers/preview', function () { configUtils.restore(); }); - describe('v0.1', function () { - beforeEach(function () { - post = testUtils.DataGenerator.forKnex.createPost({status: 'draft'}); - - apiResponse = { - posts: [post] - }; - - req = { - path: '/', - params: { - uuid: 'something' - }, - route: {} - }; - - res = { - routerOptions: { - query: {controller: 'posts', resource: 'posts'} - }, - locals: { - apiVersion: 'v0.1' - }, - render: sinon.spy(), - redirect: sinon.spy(), - set: sinon.spy() - }; - - secureStub = sinon.stub(); - - sinon.stub(urlUtils, 'redirectToAdmin'); - sinon.stub(urlUtils, 'redirect301'); - sinon.stub(urlService, 'getUrlByResourceId'); - - sinon.stub(helpers, 'secure').get(function () { - return secureStub; - }); - - renderStub = sinon.stub(); - sinon.stub(helpers, 'renderEntry').get(function () { - return function () { - return renderStub; - }; - }); - - sinon.stub(api.posts, 'read').withArgs({ - uuid: req.params.uuid, - status: 'all', - include: 'author,authors,tags' - }).callsFake(function () { - return Promise.resolve(apiResponse); - }); - }); - - it('should render post', function (done) { - controllers.preview(req, res, failTest(done)).then(function () { - secureStub.called.should.be.true(); - renderStub.called.should.be.true(); - done(); - }).catch(done); - }); - - it('should call next if post is not found', function (done) { - apiResponse = {posts: []}; - - controllers.preview(req, res, function (err) { - should.not.exist(err); - renderStub.called.should.be.false(); - secureStub.called.should.be.false(); - done(); - }); - }); - - it('should call redirect if post is published', function (done) { - post.status = 'published'; - urlService.getUrlByResourceId.withArgs(post.id).returns('/something/'); - - urlUtils.redirect301.callsFake(function (res, postUrl) { - postUrl.should.eql('/something/'); - renderStub.called.should.be.false(); - secureStub.called.should.be.false(); - done(); - }); - - controllers.preview(req, res, failTest(done)); - }); - - it('should call redirect if /edit/ (options param) is detected', function (done) { - req.params.options = 'edit'; - - urlUtils.redirectToAdmin.callsFake(function (statusCode, res, editorUrl) { - statusCode.should.eql(302); - editorUrl.should.eql(EDITOR_URL + post.id); - renderStub.called.should.be.false(); - secureStub.called.should.be.false(); - done(); - }); - - controllers.preview(req, res, failTest(done)); - }); - - it('should call next for unknown options param detected', function (done) { - req.params.options = 'abcde'; - - controllers.preview(req, res, function (err) { - should.not.exist(err); - renderStub.called.should.be.false(); - secureStub.called.should.be.false(); - done(); - }); - }); - }); - describe('v2', function () { let previewStub; @@ -191,7 +78,7 @@ describe('Unit - services/routing/controllers/preview', function () { previewStub.withArgs({ uuid: req.params.uuid, status: 'all', - include: 'author,authors,tags' + include: 'authors,tags' }).resolves(apiResponse); sinon.stub(api.v2, 'preview').get(() => { @@ -261,7 +148,7 @@ describe('Unit - services/routing/controllers/preview', function () { previewStub.withArgs({ uuid: req.params.uuid, status: 'all', - include: 'author,authors,tags' + include: 'authors,tags' }).resolves(apiResponse); sinon.stub(api.canary, 'preview').get(() => { @@ -331,7 +218,7 @@ describe('Unit - services/routing/controllers/preview', function () { previewStub.withArgs({ uuid: req.params.uuid, status: 'all', - include: 'author,authors,tags' + include: 'authors,tags' }).resolves(apiResponse); sinon.stub(api.v3, 'preview').get(() => { diff --git a/core/test/unit/services/routing/controllers/static_spec.js b/core/test/unit/services/routing/controllers/static_spec.js index 6bf4b2f3da..096bfcc14f 100644 --- a/core/test/unit/services/routing/controllers/static_spec.js +++ b/core/test/unit/services/routing/controllers/static_spec.js @@ -14,22 +14,30 @@ function failTest(done) { } describe('Unit - services/routing/controllers/static', function () { - let req, res, secureStub, renderStub, handleErrorStub, formatResponseStub, posts, postsPerPage; + let req, + res, + secureStub, + renderStub, + handleErrorStub, + formatResponseStub, + postsPerPage, + tagsReadStub; beforeEach(function () { postsPerPage = 5; - posts = [ - testUtils.DataGenerator.forKnex.createPost() - ]; - secureStub = sinon.stub(); renderStub = sinon.stub(); handleErrorStub = sinon.stub(); formatResponseStub = sinon.stub(); formatResponseStub.entries = sinon.stub(); - sinon.stub(api.tags, 'read'); + tagsReadStub = sinon.stub().resolves(); + sinon.stub(api.v2, 'tagsPublic').get(() => { + return { + read: tagsReadStub + }; + }); sinon.stub(helpers, 'secure').get(function () { return secureStub; @@ -66,7 +74,7 @@ describe('Unit - services/routing/controllers/static', function () { render: sinon.spy(), redirect: sinon.spy(), locals: { - apiVersion: 'v0.1' + apiVersion: 'v2' } }; }); @@ -78,7 +86,7 @@ describe('Unit - services/routing/controllers/static', function () { it('no extra data to fetch', function (done) { helpers.renderer.callsFake(function () { helpers.formatResponse.entries.calledOnce.should.be.true(); - api.tags.read.called.should.be.false(); + tagsReadStub.called.should.be.false(); helpers.secure.called.should.be.false(); done(); }); @@ -89,7 +97,7 @@ describe('Unit - services/routing/controllers/static', function () { it('extra data to fetch', function (done) { res.routerOptions.data = { tag: { - controller: 'tags', + controller: 'tagsPublic', resource: 'tags', type: 'read', options: { @@ -98,10 +106,10 @@ describe('Unit - services/routing/controllers/static', function () { } }; - api.tags.read.resolves({tags: [{slug: 'bacon'}]}); + tagsReadStub = sinon.stub().resolves({tags: [{slug: 'bacon'}]}); helpers.renderer.callsFake(function () { - api.tags.read.called.should.be.true(); + tagsReadStub.called.should.be.true(); helpers.formatResponse.entries.calledOnce.should.be.true(); helpers.secure.calledOnce.should.be.true(); done(); diff --git a/core/test/unit/services/routing/helpers/context_spec.js b/core/test/unit/services/routing/helpers/context_spec.js index aee4b2c823..bf070f9694 100644 --- a/core/test/unit/services/routing/helpers/context_spec.js +++ b/core/test/unit/services/routing/helpers/context_spec.js @@ -286,7 +286,7 @@ describe('Contexts', function () { it('should correctly identify AMP page', function () { res.locals.relativeUrl = '/welcome-to-ghost/amp/'; - data.post = testUtils.DataGenerator.forKnex.createPost({page: true}); + data.page = testUtils.DataGenerator.forKnex.createPost({page: true}); delete res.routerOptions; helpers.context(req, res, data); diff --git a/core/test/unit/services/routing/helpers/fetch-data_spec.js b/core/test/unit/services/routing/helpers/fetch-data_spec.js index 4e15966cfe..6b1c4f8aac 100644 --- a/core/test/unit/services/routing/helpers/fetch-data_spec.js +++ b/core/test/unit/services/routing/helpers/fetch-data_spec.js @@ -1,11 +1,12 @@ -const should = require('should'), - sinon = require('sinon'), - api = require('../../../../../server/api')['v0.1'], - helpers = require('../../../../../frontend/services/routing/helpers'), - testUtils = require('../../../../utils'); +const should = require('should'); +const sinon = require('sinon'); +const api = require('../../../../../server/api').v2; +const helpers = require('../../../../../frontend/services/routing/helpers'); +const testUtils = require('../../../../utils'); describe('Unit - services/routing/helpers/fetch-data', function () { let posts, tags, locals; + let browsePostsStub, readTagsStub; beforeEach(function () { posts = [ @@ -22,19 +23,28 @@ describe('Unit - services/routing/helpers/fetch-data', function () { testUtils.DataGenerator.forKnex.createTag() ]; - sinon.stub(api.posts, 'browse') - .resolves({ - posts: posts, - meta: { - pagination: { - pages: 2 - } + browsePostsStub = sinon.stub().resolves({ + posts: posts, + meta: { + pagination: { + pages: 2 } - }); + } + }); + sinon.stub(api, 'postsPublic').get(() => { + return { + browse: browsePostsStub + }; + }); - sinon.stub(api.tags, 'read').resolves({tags: tags}); + readTagsStub = sinon.stub().resolves({tags: tags}); + sinon.stub(api, 'tagsPublic').get(() => { + return { + read: readTagsStub + }; + }); - locals = {apiVersion: 'v0.1'}; + locals = {apiVersion: 'v2'}; }); afterEach(function () { @@ -47,10 +57,10 @@ describe('Unit - services/routing/helpers/fetch-data', function () { result.should.be.an.Object().with.properties('posts', 'meta'); result.should.not.have.property('data'); - api.posts.browse.calledOnce.should.be.true(); - api.posts.browse.firstCall.args[0].should.be.an.Object(); - api.posts.browse.firstCall.args[0].should.have.property('include'); - api.posts.browse.firstCall.args[0].should.not.have.property('filter'); + browsePostsStub.calledOnce.should.be.true(); + browsePostsStub.firstCall.args[0].should.be.an.Object(); + browsePostsStub.firstCall.args[0].should.have.property('include'); + browsePostsStub.firstCall.args[0].should.not.have.property('filter'); done(); }).catch(done); @@ -64,11 +74,11 @@ describe('Unit - services/routing/helpers/fetch-data', function () { result.posts.length.should.eql(posts.length); - api.posts.browse.calledOnce.should.be.true(); - api.posts.browse.firstCall.args[0].should.be.an.Object(); - api.posts.browse.firstCall.args[0].should.have.property('include'); - api.posts.browse.firstCall.args[0].should.have.property('limit', 10); - api.posts.browse.firstCall.args[0].should.have.property('page', 2); + browsePostsStub.calledOnce.should.be.true(); + browsePostsStub.firstCall.args[0].should.be.an.Object(); + browsePostsStub.firstCall.args[0].should.have.property('include'); + browsePostsStub.firstCall.args[0].should.have.property('limit', 10); + browsePostsStub.firstCall.args[0].should.have.property('page', 2); done(); }).catch(done); @@ -102,11 +112,10 @@ describe('Unit - services/routing/helpers/fetch-data', function () { // @TODO v3 will deprecate this style (featured.posts) result.data.featured.posts.length.should.eql(posts.length); - api.posts.browse.calledTwice.should.be.true(); - api.posts.browse.firstCall.args[0].should.have.property('include', 'author,authors,tags'); - - api.posts.browse.secondCall.args[0].should.have.property('filter', 'featured:true'); - api.posts.browse.secondCall.args[0].should.have.property('limit', 3); + browsePostsStub.calledTwice.should.be.true(); + browsePostsStub.firstCall.args[0].should.have.property('include', 'authors,tags'); + browsePostsStub.secondCall.args[0].should.have.property('filter', 'featured:true'); + browsePostsStub.secondCall.args[0].should.have.property('limit', 3); done(); }).catch(done); }); @@ -139,11 +148,11 @@ describe('Unit - services/routing/helpers/fetch-data', function () { // @TODO v3 will deprecate this style (featured.posts) result.data.featured.posts.length.should.eql(posts.length); - api.posts.browse.calledTwice.should.be.true(); - api.posts.browse.firstCall.args[0].should.have.property('include', 'author,authors,tags'); - api.posts.browse.firstCall.args[0].should.have.property('page', 2); - api.posts.browse.secondCall.args[0].should.have.property('filter', 'featured:true'); - api.posts.browse.secondCall.args[0].should.have.property('limit', 3); + browsePostsStub.calledTwice.should.be.true(); + browsePostsStub.firstCall.args[0].should.have.property('include', 'authors,tags'); + browsePostsStub.firstCall.args[0].should.have.property('page', 2); + browsePostsStub.secondCall.args[0].should.have.property('filter', 'featured:true'); + browsePostsStub.secondCall.args[0].should.have.property('limit', 3); done(); }).catch(done); }); @@ -157,7 +166,7 @@ describe('Unit - services/routing/helpers/fetch-data', function () { filter: 'tags:%s', data: { tag: { - controller: 'tags', + controller: 'tagsPublic', type: 'read', resource: 'tags', options: {slug: '%s'} @@ -173,11 +182,11 @@ describe('Unit - services/routing/helpers/fetch-data', function () { result.posts.length.should.eql(posts.length); result.data.tag.length.should.eql(tags.length); - api.posts.browse.calledOnce.should.be.true(); - api.posts.browse.firstCall.args[0].should.have.property('include'); - api.posts.browse.firstCall.args[0].should.have.property('filter', 'tags:testing'); - api.posts.browse.firstCall.args[0].should.not.have.property('slug'); - api.tags.read.firstCall.args[0].should.have.property('slug', 'testing'); + browsePostsStub.calledOnce.should.be.true(); + browsePostsStub.firstCall.args[0].should.have.property('include'); + browsePostsStub.firstCall.args[0].should.have.property('filter', 'tags:testing'); + browsePostsStub.firstCall.args[0].should.not.have.property('slug'); + readTagsStub.firstCall.args[0].should.have.property('slug', 'testing'); done(); }).catch(done); }); diff --git a/core/test/unit/services/settings/validate_spec.js b/core/test/unit/services/settings/validate_spec.js index 6101915c01..b73402374b 100644 --- a/core/test/unit/services/settings/validate_spec.js +++ b/core/test/unit/services/settings/validate_spec.js @@ -21,787 +21,6 @@ describe('UNIT: services/settings/validate', function () { sinon.restore(); }); - describe('v0.1', function () { - before(function () { - apiVersion = 'v0.1'; - }); - - it('no type definitions / empty yaml file', function () { - const object = validate({}); - - object.should.eql({collections: {}, routes: {}, taxonomies: {}}); - }); - - it('throws error when using :\w+ notiation in collection', function () { - try { - validate({ - collections: { - '/magic/': { - permalink: '/magic/{slug}/' - }, - '/': { - permalink: '/:slug/' - } - } - }); - } catch (err) { - (err instanceof common.errors.ValidationError).should.be.true(); - return; - } - - throw new Error('should fail'); - }); - - it('throws error when using :\w+ notiation in taxonomies', function () { - try { - validate({ - taxonomies: { - tag: '/categories/:slug/' - } - }); - } catch (err) { - (err instanceof common.errors.ValidationError).should.be.true(); - return; - } - - throw new Error('should fail'); - }); - - it('throws error when using an undefined taxonomy', function () { - try { - validate({ - taxonomies: { - sweet_baked_good: '/patisserie/{slug}' - } - }); - } catch (err) { - (err instanceof common.errors.ValidationError).should.be.true(); - return; - } - - throw new Error('should fail'); - }); - - it('throws error when permalink is missing (collection)', function () { - try { - validate({ - collections: { - permalink: '/{slug}/' - } - }); - } catch (err) { - (err instanceof common.errors.ValidationError).should.be.true(); - return; - } - - throw new Error('should fail'); - }); - - it('throws error without leading or trailing slashes', function () { - try { - validate({ - routes: { - about: 'about' - } - }); - } catch (err) { - (err instanceof common.errors.ValidationError).should.be.true(); - return; - } - - throw new Error('should fail'); - }); - - it('throws error without trailing slash', function () { - try { - validate({ - routes: { - '/about': 'about' - } - }); - } catch (err) { - (err instanceof common.errors.ValidationError).should.be.true(); - return; - } - - throw new Error('should fail'); - }); - - it('throws error without leading slashe', function () { - try { - validate({ - routes: { - 'about/': 'about' - } - }); - } catch (err) { - (err instanceof common.errors.ValidationError).should.be.true(); - return; - } - - throw new Error('should fail'); - }); - - it('throws error without leading slash with permalink', function () { - try { - validate({ - collections: { - 'magic/': { - permalink: '/{slug}/' - } - } - }); - } catch (err) { - (err instanceof common.errors.ValidationError).should.be.true(); - return; - } - - throw new Error('should fail'); - }); - - it('throws error without leading or trailing slashes with permalink', function () { - try { - validate({ - collections: { - magic: { - permalink: '/{slug}/' - } - } - }); - } catch (err) { - (err instanceof common.errors.ValidationError).should.be.true(); - return; - } - - throw new Error('should fail'); - }); - - it('throws error without trailing slash with permalink', function () { - try { - validate({ - collections: { - '/magic': { - permalink: '/{slug}/' - } - } - }); - } catch (err) { - (err instanceof common.errors.ValidationError).should.be.true(); - return; - } - - throw new Error('should fail'); - }); - - it('throws error without trailing slash in permalink', function () { - try { - validate({ - collections: { - '/magic/': { - permalink: '/{slug}' - } - } - }); - } catch (err) { - (err instanceof common.errors.ValidationError).should.be.true(); - return; - } - - throw new Error('should fail'); - }); - - it('throws error without leading or trailing slashes in permalink', function () { - try { - validate({ - collections: { - '/magic/': { - permalink: '{slug}' - } - } - }); - } catch (err) { - (err instanceof common.errors.ValidationError).should.be.true(); - return; - } - - throw new Error('should fail'); - }); - - it('no validation error for routes', function () { - validate({ - routes: { - '/': 'home' - } - }); - }); - - it('no validation error for / collection', function () { - validate({ - collections: { - '/': { - permalink: '/{primary_tag}/{slug}/' - } - } - }); - }); - - it('transforms {.*} notation into :\w+', function () { - const object = validate({ - collections: { - '/magic/': { - permalink: '/magic/{year}/{slug}/' - }, - '/': { - permalink: '/{slug}/' - } - }, - taxonomies: { - tag: '/tags/{slug}/', - author: '/authors/{slug}/' - } - }); - - object.should.eql({ - routes: {}, - taxonomies: { - tag: '/tags/:slug/', - author: '/authors/:slug/' - }, - collections: { - '/magic/': { - permalink: '/magic/:year/:slug/', - templates: [] - }, - '/': { - permalink: '/:slug/', - templates: [] - } - } - }); - }); - - describe('template definitions', function () { - it('single value', function () { - const object = validate({ - routes: { - '/about/': 'about', - '/me/': { - template: 'me' - } - }, - collections: { - '/': { - permalink: '/{slug}/', - template: 'test' - } - } - }); - - object.should.eql({ - taxonomies: {}, - routes: { - '/about/': { - templates: ['about'] - }, - '/me/': { - templates: ['me'] - } - }, - collections: { - '/': { - permalink: '/:slug/', - templates: ['test'] - } - } - }); - }); - - it('array', function () { - const object = validate({ - routes: { - '/about/': 'about', - '/me/': { - template: ['me'] - } - }, - collections: { - '/': { - permalink: '/{slug}/', - template: ['test'] - } - } - }); - - object.should.eql({ - taxonomies: {}, - routes: { - '/about/': { - templates: ['about'] - }, - '/me/': { - templates: ['me'] - } - }, - collections: { - '/': { - permalink: '/:slug/', - templates: ['test'] - } - } - }); - }); - }); - - describe('data definitions', function () { - it('shortform', function () { - const object = validate({ - routes: { - '/food/': { - data: 'tag.food' - }, - '/music/': { - data: 'tag.music' - }, - '/ghost/': { - data: 'user.ghost' - }, - '/sleep/': { - data: { - bed: 'tag.bed', - dream: 'tag.dream' - } - }, - '/lala/': { - data: 'author.carsten' - } - }, - collections: { - '/more/': { - permalink: '/{slug}/', - data: { - home: 'page.home' - } - }, - '/podcast/': { - permalink: '/podcast/{slug}/', - data: { - something: 'tag.something' - } - }, - '/': { - permalink: '/{slug}/', - data: 'tag.sport' - } - } - }); - - object.should.eql({ - taxonomies: {}, - routes: { - '/food/': { - data: { - query: { - tag: { - controller: 'tags', - resource: 'tags', - type: 'read', - options: { - slug: 'food', - visibility: 'public' - } - } - }, - router: { - tags: [{redirect: true, slug: 'food'}] - } - }, - templates: [] - }, - '/ghost/': { - data: { - query: { - user: { - controller: 'users', - resource: 'users', - type: 'read', - options: { - slug: 'ghost', - visibility: 'public' - } - } - }, - router: { - authors: [{redirect: true, slug: 'ghost'}] - } - }, - templates: [] - }, - '/music/': { - data: { - query: { - tag: { - controller: 'tags', - resource: 'tags', - type: 'read', - options: { - slug: 'music', - visibility: 'public' - } - } - }, - router: { - tags: [{redirect: true, slug: 'music'}] - } - }, - templates: [] - }, - '/sleep/': { - data: { - query: { - bed: { - controller: 'tags', - resource: 'tags', - type: 'read', - options: { - slug: 'bed', - visibility: 'public' - } - }, - dream: { - controller: 'tags', - resource: 'tags', - type: 'read', - options: { - slug: 'dream', - visibility: 'public' - } - } - }, - router: { - tags: [{redirect: true, slug: 'bed'}, {redirect: true, slug: 'dream'}] - } - }, - templates: [] - }, - '/lala/': { - data: { - query: { - author: { - controller: 'users', - resource: 'users', - type: 'read', - options: { - slug: 'carsten', - visibility: 'public' - } - } - }, - router: { - authors: [{redirect: true, slug: 'carsten'}] - } - }, - templates: [] - } - }, - collections: { - '/more/': { - permalink: '/:slug/', - data: { - query: { - home: { - controller: 'posts', - resource: 'posts', - type: 'read', - options: { - page: 1, - slug: 'home', - status: 'published' - } - } - }, - router: { - posts: [{redirect: true, slug: 'home'}] - } - }, - templates: [] - }, - '/podcast/': { - permalink: '/podcast/:slug/', - data: { - query: { - something: { - controller: 'tags', - resource: 'tags', - type: 'read', - options: { - slug: 'something', - visibility: 'public' - } - } - }, - router: { - tags: [{redirect: true, slug: 'something'}] - } - }, - templates: [] - }, - '/': { - permalink: '/:slug/', - data: { - query: { - tag: { - controller: 'tags', - resource: 'tags', - type: 'read', - options: { - slug: 'sport', - visibility: 'public' - } - } - }, - router: { - tags: [{redirect: true, slug: 'sport'}] - } - }, - templates: [] - } - } - }); - }); - - it('longform', function () { - const object = validate({ - routes: { - '/food/': { - data: { - food: { - resource: 'posts', - type: 'browse' - } - } - }, - '/wellness/': { - data: { - posts: { - resource: 'posts', - type: 'read', - redirect: false - } - } - }, - '/partyparty/': { - data: { - people: { - resource: 'users', - type: 'read', - slug: 'djgutelaune', - redirect: true - } - } - } - }, - collections: { - '/yoga/': { - permalink: '/{slug}/', - data: { - gym: { - resource: 'posts', - type: 'read', - slug: 'ups', - status: 'draft' - } - } - } - } - }); - - object.should.eql({ - taxonomies: {}, - routes: { - '/food/': { - data: { - query: { - food: { - controller: 'posts', - resource: 'posts', - type: 'browse', - options: {} - } - }, - router: { - posts: [{redirect: true}] - } - }, - templates: [] - }, - '/wellness/': { - data: { - query: { - posts: { - controller: 'posts', - resource: 'posts', - type: 'read', - options: { - status: 'published', - slug: '%s', - page: 0 - } - } - }, - router: { - posts: [{redirect: false}] - } - }, - templates: [] - }, - '/partyparty/': { - data: { - query: { - people: { - controller: 'users', - resource: 'users', - type: 'read', - options: { - slug: 'djgutelaune', - visibility: 'public' - } - } - }, - router: { - authors: [{redirect: true, slug: 'djgutelaune'}] - } - }, - templates: [] - } - }, - collections: { - '/yoga/': { - permalink: '/:slug/', - data: { - query: { - gym: { - controller: 'posts', - resource: 'posts', - type: 'read', - options: { - page: 0, - slug: 'ups', - status: 'draft' - } - } - }, - router: { - posts: [{redirect: true, slug: 'ups'}] - } - }, - templates: [] - } - } - }); - }); - - it('errors: data shortform incorrect', function () { - try { - validate({ - collections: { - '/magic/': { - permalink: '/{slug}/', - data: 'tag:test' - } - } - }); - } catch (err) { - (err instanceof common.errors.ValidationError).should.be.true(); - return; - } - - throw new Error('should fail'); - }); - - it('errors: data longform resource is missing', function () { - try { - validate({ - collections: { - '/magic/': { - permalink: '/{slug}/', - data: { - type: 'edit' - } - } - } - }); - } catch (err) { - (err instanceof common.errors.ValidationError).should.be.true(); - return; - } - - throw new Error('should fail'); - }); - - it('errors: data longform type is missing', function () { - try { - validate({ - collections: { - '/magic/': { - permalink: '/{slug}/', - data: { - resource: 'subscribers' - } - } - } - }); - } catch (err) { - (err instanceof common.errors.ValidationError).should.be.true(); - return; - } - - throw new Error('should fail'); - }); - - it('errors: data longform name is author', function () { - try { - validate({ - collections: { - '/magic/': { - permalink: '/{slug}/', - data: { - author: { - resource: 'users' - } - } - } - } - }); - } catch (err) { - (err instanceof common.errors.ValidationError).should.be.true(); - return; - } - - throw new Error('should fail'); - }); - - it('errors: data longform does not use a custom name at all', function () { - try { - validate({ - collections: { - '/magic/': { - permalink: '/{slug}/', - data: { - resource: 'users' - } - } - } - }); - } catch (err) { - (err instanceof common.errors.ValidationError).should.be.true(); - return; - } - - throw new Error('should fail'); - }); - }); - }); - describe('v2', function () { before(function () { apiVersion = 'v2'; diff --git a/core/test/utils/fixtures/themes/casper/package.json b/core/test/utils/fixtures/themes/casper/package.json index c90078a121..c11b0aa023 100644 --- a/core/test/utils/fixtures/themes/casper/package.json +++ b/core/test/utils/fixtures/themes/casper/package.json @@ -4,7 +4,8 @@ "demo": "https://demo.ghost.io", "version": "2.4.2", "engines": { - "ghost": ">=2.0.0" + "ghost": ">=2.0.0", + "ghost-api": "v2" }, "license": "MIT", "screenshots": { diff --git a/core/test/utils/fixtures/themes/test-theme/package.json b/core/test/utils/fixtures/themes/test-theme/package.json index e3e3da6871..de7f2a528d 100644 --- a/core/test/utils/fixtures/themes/test-theme/package.json +++ b/core/test/utils/fixtures/themes/test-theme/package.json @@ -4,7 +4,8 @@ "demo": "https://demo.ghost.io", "version": "2.4.2", "engines": { - "ghost": ">=2.0.0" + "ghost": ">=2.0.0", + "ghost-api": "v2" }, "license": "MIT", "screenshots": {