diff --git a/test/regression/site/intergration-web-site/canary_spec.js b/test/regression/site/intergration-web-site/canary_spec.js new file mode 100644 index 0000000000..55f49f14f2 --- /dev/null +++ b/test/regression/site/intergration-web-site/canary_spec.js @@ -0,0 +1,1720 @@ +const should = require('should'); +const sinon = require('sinon'); +const _ = require('lodash'); +const cheerio = require('cheerio'); +const testUtils = require('../../../utils'); +const mockUtils = require('../../../utils/mocks'); +const configUtils = require('../../../utils/configUtils'); +const urlUtils = require('../../../utils/urlUtils'); +const appService = require('../../../../core/frontend/services/apps'); +const frontendSettingsService = require('../../../../core/frontend/services/settings'); +const themeService = require('../../../../core/frontend/services/themes'); +const siteApp = require('../../../../core/server/web/parent/app'); + +describe('Integration - Web - Site canary', function () { + let app; + + before(testUtils.integrationTesting.urlService.resetGenerators); + before(testUtils.teardownDb); + before(testUtils.setup('users:roles', 'posts')); + + describe('canary', function () { + let postSpy; + + describe('default routes.yaml', function () { + before(function () { + testUtils.integrationTesting.urlService.resetGenerators(); + testUtils.integrationTesting.defaultMocks(sinon, {amp: true}); + testUtils.integrationTesting.overrideGhostConfig(configUtils); + + return testUtils.integrationTesting.initGhost() + .then(function () { + sinon.stub(themeService.getActive(), 'engine').withArgs('ghost-api').returns('canary'); + sinon.stub(themeService.getActive(), 'config').withArgs('posts_per_page').returns(2); + + app = siteApp({start: true}); + return testUtils.integrationTesting.urlServiceInitAndWait(); + }) + .then(() => { + return appService.init(); + }); + }); + + before(function () { + configUtils.set('url', 'http://example.com'); + urlUtils.stubUrlUtilsFromConfig(); + }); + + beforeEach(function () { + const postsAPI = require('../../../../core/server/api/canary/posts-public'); + postSpy = sinon.spy(postsAPI.browse, 'query'); + }); + + afterEach(function () { + postSpy.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 mockUtils.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 mockUtils.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 mockUtils.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 mockUtils.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 mockUtils.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 mockUtils.express.invoke(app, req) + .then(function (response) { + response.statusCode.should.eql(200); + response.template.should.eql('tag'); + + postSpy.args[0][0].options.filter.should.eql('(tags:\'bacon\'+tags.visibility:public)+type:post'); + postSpy.args[0][0].options.page.should.eql(1); + postSpy.args[0][0].options.limit.should.eql(2); + }); + }); + + it('serve tag rss', function () { + const req = { + secure: true, + method: 'GET', + url: '/tag/bacon/rss/', + host: 'example.com' + }; + + return mockUtils.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 mockUtils.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 mockUtils.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 theme asset', function () { + //configUtils.set('url', 'https://example.com'); + + const req = { + secure: true, + method: 'GET', + url: '/assets/css/screen.css', + host: 'example.com' + }; + + return mockUtils.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 mockUtils.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 mockUtils.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 mockUtils.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 mockUtils.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 mockUtils.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: '/favicon.png', + host: 'example.com' + }; + + return mockUtils.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 mockUtils.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('canary'); + sinon.stub(themeService.getActive(), 'config').withArgs('posts_per_page').returns(2); + + app = siteApp({start: true}); + return testUtils.integrationTesting.urlServiceInitAndWait(); + }); + }); + + 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 mockUtils.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 mockUtils.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 mockUtils.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 mockUtils.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 mockUtils.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('canary'); + sinon.stub(themeService.getActive(), 'config').withArgs('posts_per_page').returns(2); + + app = siteApp({start: true}); + return testUtils.integrationTesting.urlServiceInitAndWait(); + }); + }); + + 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 mockUtils.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('canary'); + sinon.stub(themeService.getActive(), 'config').withArgs('posts_per_page').returns(2); + + app = siteApp({start: true}); + return testUtils.integrationTesting.urlServiceInitAndWait(); + }); + }); + + 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 mockUtils.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 mockUtils.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 mockUtils.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 mockUtils.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('canary'); + sinon.stub(themeService.getActive(), 'config').withArgs('posts_per_page').returns(2); + + app = siteApp({start: true}); + return testUtils.integrationTesting.urlServiceInitAndWait(); + }); + }); + + 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 mockUtils.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 mockUtils.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 mockUtils.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('canary'); + sinon.stub(themeService.getActive(), 'config').withArgs('posts_per_page').returns(2); + + app = siteApp({start: true}); + return testUtils.integrationTesting.urlServiceInitAndWait(); + }); + }); + + 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 mockUtils.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 mockUtils.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 mockUtils.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 mockUtils.express.invoke(app, req) + .then(function (response) { + response.statusCode.should.eql(200); + response.template.should.eql('page'); + }); + }); + }); + + describe('collection/routes with data key', function () { + before(function () { + sinon.stub(frontendSettingsService, 'get').returns({ + routes: { + '/my-page/': { + data: { + query: { + page: { + controller: 'pagesPublic', + resource: 'pages', + type: 'read', + options: { + slug: 'static-page-test' + } + } + }, + router: { + pages: [{redirect: true, slug: 'static-page-test'}] + } + }, + templates: ['page'] + } + }, + + collections: { + '/food/': { + permalink: '/food/:slug/', + filter: 'tag:bacon+tag:-chorizo', + data: { + query: { + tag: { + controller: 'tagsPublic', + resource: 'tags', + type: 'read', + options: { + slug: 'bacon' + } + } + }, + router: { + tags: [{redirect: true, slug: 'bacon'}] + } + } + }, + '/sport/': { + permalink: '/sport/:slug/', + filter: 'tag:chorizo+tag:-bacon', + data: { + query: { + apollo: { + controller: 'tagsPublic', + resource: 'tags', + type: 'read', + options: { + slug: 'chorizo' + } + } + }, + router: { + tags: [{redirect: false, slug: 'chorizo'}] + } + } + } + }, + + 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('canary'); + sinon.stub(themeService.getActive(), 'config').withArgs('posts_per_page').returns(2); + + app = siteApp({start: true}); + return testUtils.integrationTesting.urlServiceInitAndWait(); + }); + }); + + 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 mockUtils.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 mockUtils.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 mockUtils.express.invoke(app, req) + .then(function (response) { + response.statusCode.should.eql(200); + response.template.should.eql('index'); + }); + }); + + it('serve chorizo tag', function () { + const req = { + secure: true, + method: 'GET', + url: '/categories/chorizo/', + host: 'example.com' + }; + + return mockUtils.express.invoke(app, req) + .then(function (response) { + response.statusCode.should.eql(200); + }); + }); + + it('serve my-page', function () { + const req = { + secure: true, + method: 'GET', + url: '/my-page/', + host: 'example.com' + }; + + return mockUtils.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('canary'); + sinon.stub(themeService.getActive(), 'config').withArgs('posts_per_page').returns(2); + + app = siteApp({start: true}); + return testUtils.integrationTesting.urlServiceInitAndWait(); + }); + }); + + 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 mockUtils.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 mockUtils.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('canary'); + sinon.stub(themeService.getActive(), 'config').withArgs('posts_per_page').returns(2); + + app = siteApp({start: true}); + return testUtils.integrationTesting.urlServiceInitAndWait(); + }); + }); + + 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 mockUtils.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('canary'); + sinon.stub(themeService.getActive(), 'config').withArgs('posts_per_page').returns(2); + + app = siteApp({start: true}); + return testUtils.integrationTesting.urlServiceInitAndWait(); + }); + }); + + 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 mockUtils.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 mockUtils.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.teardownDb); + 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: 'tagsPublic', + 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: 'tagsPublic', + 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: 'authorsPublic', + resource: 'authors', + 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: 'authorsPublic', + resource: 'authors', + type: 'read', + options: { + slug: 'joe-bloggs', + redirect: false + } + } + }, + router: { + authors: [{redirect: false, slug: 'joe-bloggs'}] + } + } + }, + + '/channel6/': { + controller: 'channel', + data: { + query: { + post: { + controller: 'postsPublic', + resource: 'posts', + type: 'read', + options: { + slug: 'html-ipsum', + redirect: true + } + } + }, + router: { + posts: [{redirect: true, slug: 'html-ipsum'}] + } + } + } + }, + + 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('canary'); + sinon.stub(themeService.getActive(), 'config').withArgs('posts_per_page').returns(10); + + app = siteApp({start: true}); + return testUtils.integrationTesting.urlServiceInitAndWait(); + }); + }); + + 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 mockUtils.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 mockUtils.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 mockUtils.express.invoke(app, req) + .then(function (response) { + const $ = cheerio.load(response.body); + + response.statusCode.should.eql(200); + response.template.should.eql('default'); + + // default template 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 mockUtils.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 mockUtils.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 mockUtils.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 mockUtils.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 mockUtils.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 mockUtils.express.invoke(app, req) + .then(function (response) { + response.statusCode.should.eql(301); + response.headers.location.should.eql('/channel6/'); + }); + }); + + it('serve chorizo: no redirect', function () { + const req = { + secure: true, + method: 'GET', + url: '/tag/chorizo/', + host: 'example.com' + }; + + return mockUtils.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 mockUtils.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('canary'); + sinon.stub(themeService.getActive(), 'config').withArgs('posts_per_page').returns(2); + + app = siteApp({start: true}); + return testUtils.integrationTesting.urlServiceInitAndWait(); + }); + }); + + 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 mockUtils.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 mockUtils.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 mockUtils.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 mockUtils.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 mockUtils.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 mockUtils.express.invoke(app, req) + .then(function (response) { + const $ = cheerio.load(response.body); + response.statusCode.should.eql(200); + $('head link')[1].attribs.href.should.eql('https://127.0.0.1:2369/rss/'); + }); + }); + }); + }); +}); diff --git a/test/regression/site/intergration-web-site/site_spec.js b/test/regression/site/intergration-web-site/site_spec.js deleted file mode 100644 index 2ee4df347e..0000000000 --- a/test/regression/site/intergration-web-site/site_spec.js +++ /dev/null @@ -1,5119 +0,0 @@ -const should = require('should'); -const sinon = require('sinon'); -const _ = require('lodash'); -const cheerio = require('cheerio'); -const testUtils = require('../../../utils'); -const mockUtils = require('../../../utils/mocks'); -const configUtils = require('../../../utils/configUtils'); -const urlUtils = require('../../../utils/urlUtils'); -const appService = require('../../../../core/frontend/services/apps'); -const frontendSettingsService = require('../../../../core/frontend/services/settings'); -const themeService = require('../../../../core/frontend/services/themes'); -const siteApp = require('../../../../core/server/web/parent/app'); - -describe('Integration - Web - Site', function () { - let app; - - before(testUtils.integrationTesting.urlService.resetGenerators); - before(testUtils.teardownDb); - before(testUtils.setup('users:roles', 'posts')); - - describe('v2', function () { - let postSpy; - - describe('default routes.yaml', function () { - before(function () { - testUtils.integrationTesting.urlService.resetGenerators(); - testUtils.integrationTesting.defaultMocks(sinon, {amp: true}); - testUtils.integrationTesting.overrideGhostConfig(configUtils); - - return testUtils.integrationTesting.initGhost() - .then(function () { - sinon.stub(themeService.getActive(), 'engine').withArgs('ghost-api').returns('v2'); - sinon.stub(themeService.getActive(), 'config').withArgs('posts_per_page').returns(2); - - app = siteApp({start: true}); - return testUtils.integrationTesting.urlServiceInitAndWait(); - }) - .then(() => { - return appService.init(); - }); - }); - - before(function () { - configUtils.set('url', 'http://example.com'); - urlUtils.stubUrlUtilsFromConfig(); - }); - - beforeEach(function () { - const postsAPI = require('../../../../core/server/api/v2/posts-public'); - postSpy = sinon.spy(postsAPI.browse, 'query'); - }); - - afterEach(function () { - postSpy.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 mockUtils.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 mockUtils.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 mockUtils.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 mockUtils.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 mockUtils.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 mockUtils.express.invoke(app, req) - .then(function (response) { - response.statusCode.should.eql(200); - response.template.should.eql('tag'); - - postSpy.args[0][0].options.filter.should.eql('(tags:\'bacon\'+tags.visibility:public)+type:post'); - postSpy.args[0][0].options.page.should.eql(1); - postSpy.args[0][0].options.limit.should.eql(2); - }); - }); - - it('serve tag rss', function () { - const req = { - secure: true, - method: 'GET', - url: '/tag/bacon/rss/', - host: 'example.com' - }; - - return mockUtils.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 mockUtils.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 mockUtils.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 theme asset', function () { - //configUtils.set('url', 'https://example.com'); - - const req = { - secure: true, - method: 'GET', - url: '/assets/css/screen.css', - host: 'example.com' - }; - - return mockUtils.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 mockUtils.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 mockUtils.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 mockUtils.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 mockUtils.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 mockUtils.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: '/favicon.png', - host: 'example.com' - }; - - return mockUtils.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 mockUtils.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('v2'); - sinon.stub(themeService.getActive(), 'config').withArgs('posts_per_page').returns(2); - - app = siteApp({start: true}); - return testUtils.integrationTesting.urlServiceInitAndWait(); - }); - }); - - 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 mockUtils.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 mockUtils.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 mockUtils.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 mockUtils.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 mockUtils.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('v2'); - sinon.stub(themeService.getActive(), 'config').withArgs('posts_per_page').returns(2); - - app = siteApp({start: true}); - return testUtils.integrationTesting.urlServiceInitAndWait(); - }); - }); - - 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 mockUtils.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('v2'); - sinon.stub(themeService.getActive(), 'config').withArgs('posts_per_page').returns(2); - - app = siteApp({start: true}); - return testUtils.integrationTesting.urlServiceInitAndWait(); - }); - }); - - 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 mockUtils.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 mockUtils.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 mockUtils.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 mockUtils.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('v2'); - sinon.stub(themeService.getActive(), 'config').withArgs('posts_per_page').returns(2); - - app = siteApp({start: true}); - return testUtils.integrationTesting.urlServiceInitAndWait(); - }); - }); - - 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 mockUtils.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 mockUtils.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 mockUtils.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('v2'); - sinon.stub(themeService.getActive(), 'config').withArgs('posts_per_page').returns(2); - - app = siteApp({start: true}); - return testUtils.integrationTesting.urlServiceInitAndWait(); - }); - }); - - 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 mockUtils.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 mockUtils.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 mockUtils.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 mockUtils.express.invoke(app, req) - .then(function (response) { - response.statusCode.should.eql(200); - response.template.should.eql('page'); - }); - }); - }); - - describe('collection/routes with data key', function () { - before(function () { - sinon.stub(frontendSettingsService, 'get').returns({ - routes: { - '/my-page/': { - data: { - query: { - page: { - controller: 'pagesPublic', - resource: 'pages', - type: 'read', - options: { - slug: 'static-page-test' - } - } - }, - router: { - pages: [{redirect: true, slug: 'static-page-test'}] - } - }, - templates: ['page'] - } - }, - - collections: { - '/food/': { - permalink: '/food/:slug/', - filter: 'tag:bacon+tag:-chorizo', - data: { - query: { - tag: { - controller: 'tagsPublic', - resource: 'tags', - type: 'read', - options: { - slug: 'bacon' - } - } - }, - router: { - tags: [{redirect: true, slug: 'bacon'}] - } - } - }, - '/sport/': { - permalink: '/sport/:slug/', - filter: 'tag:chorizo+tag:-bacon', - data: { - query: { - apollo: { - controller: 'tagsPublic', - resource: 'tags', - type: 'read', - options: { - slug: 'chorizo' - } - } - }, - router: { - tags: [{redirect: false, slug: 'chorizo'}] - } - } - } - }, - - 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('v2'); - sinon.stub(themeService.getActive(), 'config').withArgs('posts_per_page').returns(2); - - app = siteApp({start: true}); - return testUtils.integrationTesting.urlServiceInitAndWait(); - }); - }); - - 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 mockUtils.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 mockUtils.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 mockUtils.express.invoke(app, req) - .then(function (response) { - response.statusCode.should.eql(200); - response.template.should.eql('index'); - }); - }); - - it('serve chorizo tag', function () { - const req = { - secure: true, - method: 'GET', - url: '/categories/chorizo/', - host: 'example.com' - }; - - return mockUtils.express.invoke(app, req) - .then(function (response) { - response.statusCode.should.eql(200); - }); - }); - - it('serve my-page', function () { - const req = { - secure: true, - method: 'GET', - url: '/my-page/', - host: 'example.com' - }; - - return mockUtils.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('v2'); - sinon.stub(themeService.getActive(), 'config').withArgs('posts_per_page').returns(2); - - app = siteApp({start: true}); - return testUtils.integrationTesting.urlServiceInitAndWait(); - }); - }); - - 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 mockUtils.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 mockUtils.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('v2'); - sinon.stub(themeService.getActive(), 'config').withArgs('posts_per_page').returns(2); - - app = siteApp({start: true}); - return testUtils.integrationTesting.urlServiceInitAndWait(); - }); - }); - - 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 mockUtils.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('v2'); - sinon.stub(themeService.getActive(), 'config').withArgs('posts_per_page').returns(2); - - app = siteApp({start: true}); - return testUtils.integrationTesting.urlServiceInitAndWait(); - }); - }); - - 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 mockUtils.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 mockUtils.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.teardownDb); - 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: 'tagsPublic', - 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: 'tagsPublic', - 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: 'authorsPublic', - resource: 'authors', - 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: 'authorsPublic', - resource: 'authors', - type: 'read', - options: { - slug: 'joe-bloggs', - redirect: false - } - } - }, - router: { - authors: [{redirect: false, slug: 'joe-bloggs'}] - } - } - }, - - '/channel6/': { - controller: 'channel', - data: { - query: { - post: { - controller: 'postsPublic', - resource: 'posts', - type: 'read', - options: { - slug: 'html-ipsum', - redirect: true - } - } - }, - router: { - posts: [{redirect: true, slug: 'html-ipsum'}] - } - } - } - }, - - 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('v2'); - sinon.stub(themeService.getActive(), 'config').withArgs('posts_per_page').returns(10); - - app = siteApp({start: true}); - return testUtils.integrationTesting.urlServiceInitAndWait(); - }); - }); - - 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 mockUtils.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 mockUtils.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 mockUtils.express.invoke(app, req) - .then(function (response) { - const $ = cheerio.load(response.body); - - response.statusCode.should.eql(200); - response.template.should.eql('default'); - - // default template 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 mockUtils.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 mockUtils.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 mockUtils.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 mockUtils.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 mockUtils.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 mockUtils.express.invoke(app, req) - .then(function (response) { - response.statusCode.should.eql(301); - response.headers.location.should.eql('/channel6/'); - }); - }); - - it('serve chorizo: no redirect', function () { - const req = { - secure: true, - method: 'GET', - url: '/tag/chorizo/', - host: 'example.com' - }; - - return mockUtils.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 mockUtils.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('v2'); - sinon.stub(themeService.getActive(), 'config').withArgs('posts_per_page').returns(2); - - app = siteApp({start: true}); - return testUtils.integrationTesting.urlServiceInitAndWait(); - }); - }); - - 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 mockUtils.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 mockUtils.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 mockUtils.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 mockUtils.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 mockUtils.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 mockUtils.express.invoke(app, req) - .then(function (response) { - const $ = cheerio.load(response.body); - response.statusCode.should.eql(200); - $('head link')[1].attribs.href.should.eql('https://127.0.0.1:2369/rss/'); - }); - }); - }); - }); - - describe('canary', function () { - let postSpy; - - describe('default routes.yaml', function () { - before(function () { - testUtils.integrationTesting.urlService.resetGenerators(); - testUtils.integrationTesting.defaultMocks(sinon, {amp: true}); - testUtils.integrationTesting.overrideGhostConfig(configUtils); - - return testUtils.integrationTesting.initGhost() - .then(function () { - sinon.stub(themeService.getActive(), 'engine').withArgs('ghost-api').returns('canary'); - sinon.stub(themeService.getActive(), 'config').withArgs('posts_per_page').returns(2); - - app = siteApp({start: true}); - return testUtils.integrationTesting.urlServiceInitAndWait(); - }) - .then(() => { - return appService.init(); - }); - }); - - before(function () { - configUtils.set('url', 'http://example.com'); - urlUtils.stubUrlUtilsFromConfig(); - }); - - beforeEach(function () { - const postsAPI = require('../../../../core/server/api/canary/posts-public'); - postSpy = sinon.spy(postsAPI.browse, 'query'); - }); - - afterEach(function () { - postSpy.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 mockUtils.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 mockUtils.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 mockUtils.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 mockUtils.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 mockUtils.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 mockUtils.express.invoke(app, req) - .then(function (response) { - response.statusCode.should.eql(200); - response.template.should.eql('tag'); - - postSpy.args[0][0].options.filter.should.eql('(tags:\'bacon\'+tags.visibility:public)+type:post'); - postSpy.args[0][0].options.page.should.eql(1); - postSpy.args[0][0].options.limit.should.eql(2); - }); - }); - - it('serve tag rss', function () { - const req = { - secure: true, - method: 'GET', - url: '/tag/bacon/rss/', - host: 'example.com' - }; - - return mockUtils.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 mockUtils.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 mockUtils.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 theme asset', function () { - //configUtils.set('url', 'https://example.com'); - - const req = { - secure: true, - method: 'GET', - url: '/assets/css/screen.css', - host: 'example.com' - }; - - return mockUtils.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 mockUtils.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 mockUtils.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 mockUtils.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 mockUtils.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 mockUtils.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: '/favicon.png', - host: 'example.com' - }; - - return mockUtils.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 mockUtils.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('canary'); - sinon.stub(themeService.getActive(), 'config').withArgs('posts_per_page').returns(2); - - app = siteApp({start: true}); - return testUtils.integrationTesting.urlServiceInitAndWait(); - }); - }); - - 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 mockUtils.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 mockUtils.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 mockUtils.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 mockUtils.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 mockUtils.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('canary'); - sinon.stub(themeService.getActive(), 'config').withArgs('posts_per_page').returns(2); - - app = siteApp({start: true}); - return testUtils.integrationTesting.urlServiceInitAndWait(); - }); - }); - - 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 mockUtils.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('canary'); - sinon.stub(themeService.getActive(), 'config').withArgs('posts_per_page').returns(2); - - app = siteApp({start: true}); - return testUtils.integrationTesting.urlServiceInitAndWait(); - }); - }); - - 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 mockUtils.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 mockUtils.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 mockUtils.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 mockUtils.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('canary'); - sinon.stub(themeService.getActive(), 'config').withArgs('posts_per_page').returns(2); - - app = siteApp({start: true}); - return testUtils.integrationTesting.urlServiceInitAndWait(); - }); - }); - - 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 mockUtils.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 mockUtils.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 mockUtils.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('canary'); - sinon.stub(themeService.getActive(), 'config').withArgs('posts_per_page').returns(2); - - app = siteApp({start: true}); - return testUtils.integrationTesting.urlServiceInitAndWait(); - }); - }); - - 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 mockUtils.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 mockUtils.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 mockUtils.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 mockUtils.express.invoke(app, req) - .then(function (response) { - response.statusCode.should.eql(200); - response.template.should.eql('page'); - }); - }); - }); - - describe('collection/routes with data key', function () { - before(function () { - sinon.stub(frontendSettingsService, 'get').returns({ - routes: { - '/my-page/': { - data: { - query: { - page: { - controller: 'pagesPublic', - resource: 'pages', - type: 'read', - options: { - slug: 'static-page-test' - } - } - }, - router: { - pages: [{redirect: true, slug: 'static-page-test'}] - } - }, - templates: ['page'] - } - }, - - collections: { - '/food/': { - permalink: '/food/:slug/', - filter: 'tag:bacon+tag:-chorizo', - data: { - query: { - tag: { - controller: 'tagsPublic', - resource: 'tags', - type: 'read', - options: { - slug: 'bacon' - } - } - }, - router: { - tags: [{redirect: true, slug: 'bacon'}] - } - } - }, - '/sport/': { - permalink: '/sport/:slug/', - filter: 'tag:chorizo+tag:-bacon', - data: { - query: { - apollo: { - controller: 'tagsPublic', - resource: 'tags', - type: 'read', - options: { - slug: 'chorizo' - } - } - }, - router: { - tags: [{redirect: false, slug: 'chorizo'}] - } - } - } - }, - - 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('canary'); - sinon.stub(themeService.getActive(), 'config').withArgs('posts_per_page').returns(2); - - app = siteApp({start: true}); - return testUtils.integrationTesting.urlServiceInitAndWait(); - }); - }); - - 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 mockUtils.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 mockUtils.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 mockUtils.express.invoke(app, req) - .then(function (response) { - response.statusCode.should.eql(200); - response.template.should.eql('index'); - }); - }); - - it('serve chorizo tag', function () { - const req = { - secure: true, - method: 'GET', - url: '/categories/chorizo/', - host: 'example.com' - }; - - return mockUtils.express.invoke(app, req) - .then(function (response) { - response.statusCode.should.eql(200); - }); - }); - - it('serve my-page', function () { - const req = { - secure: true, - method: 'GET', - url: '/my-page/', - host: 'example.com' - }; - - return mockUtils.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('canary'); - sinon.stub(themeService.getActive(), 'config').withArgs('posts_per_page').returns(2); - - app = siteApp({start: true}); - return testUtils.integrationTesting.urlServiceInitAndWait(); - }); - }); - - 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 mockUtils.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 mockUtils.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('canary'); - sinon.stub(themeService.getActive(), 'config').withArgs('posts_per_page').returns(2); - - app = siteApp({start: true}); - return testUtils.integrationTesting.urlServiceInitAndWait(); - }); - }); - - 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 mockUtils.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('canary'); - sinon.stub(themeService.getActive(), 'config').withArgs('posts_per_page').returns(2); - - app = siteApp({start: true}); - return testUtils.integrationTesting.urlServiceInitAndWait(); - }); - }); - - 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 mockUtils.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 mockUtils.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.teardownDb); - 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: 'tagsPublic', - 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: 'tagsPublic', - 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: 'authorsPublic', - resource: 'authors', - 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: 'authorsPublic', - resource: 'authors', - type: 'read', - options: { - slug: 'joe-bloggs', - redirect: false - } - } - }, - router: { - authors: [{redirect: false, slug: 'joe-bloggs'}] - } - } - }, - - '/channel6/': { - controller: 'channel', - data: { - query: { - post: { - controller: 'postsPublic', - resource: 'posts', - type: 'read', - options: { - slug: 'html-ipsum', - redirect: true - } - } - }, - router: { - posts: [{redirect: true, slug: 'html-ipsum'}] - } - } - } - }, - - 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('canary'); - sinon.stub(themeService.getActive(), 'config').withArgs('posts_per_page').returns(10); - - app = siteApp({start: true}); - return testUtils.integrationTesting.urlServiceInitAndWait(); - }); - }); - - 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 mockUtils.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 mockUtils.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 mockUtils.express.invoke(app, req) - .then(function (response) { - const $ = cheerio.load(response.body); - - response.statusCode.should.eql(200); - response.template.should.eql('default'); - - // default template 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 mockUtils.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 mockUtils.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 mockUtils.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 mockUtils.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 mockUtils.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 mockUtils.express.invoke(app, req) - .then(function (response) { - response.statusCode.should.eql(301); - response.headers.location.should.eql('/channel6/'); - }); - }); - - it('serve chorizo: no redirect', function () { - const req = { - secure: true, - method: 'GET', - url: '/tag/chorizo/', - host: 'example.com' - }; - - return mockUtils.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 mockUtils.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('canary'); - sinon.stub(themeService.getActive(), 'config').withArgs('posts_per_page').returns(2); - - app = siteApp({start: true}); - return testUtils.integrationTesting.urlServiceInitAndWait(); - }); - }); - - 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 mockUtils.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 mockUtils.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 mockUtils.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 mockUtils.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 mockUtils.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 mockUtils.express.invoke(app, req) - .then(function (response) { - const $ = cheerio.load(response.body); - response.statusCode.should.eql(200); - $('head link')[1].attribs.href.should.eql('https://127.0.0.1:2369/rss/'); - }); - }); - }); - }); - - describe('v3', function () { - let postSpy; - - describe('default routes.yaml', function () { - before(function () { - testUtils.integrationTesting.urlService.resetGenerators(); - testUtils.integrationTesting.defaultMocks(sinon, {amp: true}); - testUtils.integrationTesting.overrideGhostConfig(configUtils); - - return testUtils.integrationTesting.initGhost() - .then(function () { - sinon.stub(themeService.getActive(), 'engine').withArgs('ghost-api').returns('v3'); - sinon.stub(themeService.getActive(), 'config').withArgs('posts_per_page').returns(2); - - app = siteApp({start: true}); - return testUtils.integrationTesting.urlServiceInitAndWait(); - }) - .then(() => { - return appService.init(); - }); - }); - - before(function () { - configUtils.set('url', 'http://example.com'); - urlUtils.stubUrlUtilsFromConfig(); - }); - - beforeEach(function () { - const postsAPI = require('../../../../core/server/api/v3/posts-public'); - postSpy = sinon.spy(postsAPI.browse, 'query'); - }); - - afterEach(function () { - postSpy.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 mockUtils.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 mockUtils.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 mockUtils.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 mockUtils.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 mockUtils.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 mockUtils.express.invoke(app, req) - .then(function (response) { - response.statusCode.should.eql(200); - response.template.should.eql('tag'); - - postSpy.args[0][0].options.filter.should.eql('(tags:\'bacon\'+tags.visibility:public)+type:post'); - postSpy.args[0][0].options.page.should.eql(1); - postSpy.args[0][0].options.limit.should.eql(2); - }); - }); - - it('serve tag rss', function () { - const req = { - secure: true, - method: 'GET', - url: '/tag/bacon/rss/', - host: 'example.com' - }; - - return mockUtils.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 mockUtils.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 mockUtils.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 theme asset', function () { - //configUtils.set('url', 'https://example.com'); - - const req = { - secure: true, - method: 'GET', - url: '/assets/css/screen.css', - host: 'example.com' - }; - - return mockUtils.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 mockUtils.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 mockUtils.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 mockUtils.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 mockUtils.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 mockUtils.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: '/favicon.png', - host: 'example.com' - }; - - return mockUtils.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 mockUtils.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('v3'); - sinon.stub(themeService.getActive(), 'config').withArgs('posts_per_page').returns(2); - - app = siteApp({start: true}); - return testUtils.integrationTesting.urlServiceInitAndWait(); - }); - }); - - 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 mockUtils.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 mockUtils.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 mockUtils.express.invoke(app, req) - .then(function (response) { - response.statusCode.should.eql(200); - response.template.should.eql('post'); - }); - }); - - it('serve collection: podcast', function () { - const req = { - secure: true, - method: 'GET', - url: '/podcast/', - host: 'example.com' - }; - - return mockUtils.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 mockUtils.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('v3'); - sinon.stub(themeService.getActive(), 'config').withArgs('posts_per_page').returns(2); - - app = siteApp({start: true}); - return testUtils.integrationTesting.urlServiceInitAndWait(); - }); - }); - - 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 mockUtils.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('v3'); - sinon.stub(themeService.getActive(), 'config').withArgs('posts_per_page').returns(2); - - app = siteApp({start: true}); - return testUtils.integrationTesting.urlServiceInitAndWait(); - }); - }); - - 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 mockUtils.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 mockUtils.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 mockUtils.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 mockUtils.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('v3'); - sinon.stub(themeService.getActive(), 'config').withArgs('posts_per_page').returns(2); - - app = siteApp({start: true}); - return testUtils.integrationTesting.urlServiceInitAndWait(); - }); - }); - - 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 mockUtils.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 mockUtils.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 mockUtils.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('v3'); - sinon.stub(themeService.getActive(), 'config').withArgs('posts_per_page').returns(2); - - app = siteApp({start: true}); - return testUtils.integrationTesting.urlServiceInitAndWait(); - }); - }); - - 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 mockUtils.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 mockUtils.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 mockUtils.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 mockUtils.express.invoke(app, req) - .then(function (response) { - response.statusCode.should.eql(200); - response.template.should.eql('page'); - }); - }); - }); - - describe('collection/routes with data key', function () { - before(function () { - sinon.stub(frontendSettingsService, 'get').returns({ - routes: { - '/my-page/': { - data: { - query: { - page: { - controller: 'pagesPublic', - resource: 'pages', - type: 'read', - options: { - slug: 'static-page-test' - } - } - }, - router: { - pages: [{redirect: true, slug: 'static-page-test'}] - } - }, - templates: ['page'] - } - }, - - collections: { - '/food/': { - permalink: '/food/:slug/', - filter: 'tag:bacon+tag:-chorizo', - data: { - query: { - tag: { - controller: 'tagsPublic', - resource: 'tags', - type: 'read', - options: { - slug: 'bacon' - } - } - }, - router: { - tags: [{redirect: true, slug: 'bacon'}] - } - } - }, - '/sport/': { - permalink: '/sport/:slug/', - filter: 'tag:chorizo+tag:-bacon', - data: { - query: { - apollo: { - controller: 'tagsPublic', - resource: 'tags', - type: 'read', - options: { - slug: 'chorizo' - } - } - }, - router: { - tags: [{redirect: false, slug: 'chorizo'}] - } - } - } - }, - - 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('v3'); - sinon.stub(themeService.getActive(), 'config').withArgs('posts_per_page').returns(2); - - app = siteApp({start: true}); - return testUtils.integrationTesting.urlServiceInitAndWait(); - }); - }); - - 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 mockUtils.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 mockUtils.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 mockUtils.express.invoke(app, req) - .then(function (response) { - response.statusCode.should.eql(200); - response.template.should.eql('index'); - }); - }); - - it('serve chorizo tag', function () { - const req = { - secure: true, - method: 'GET', - url: '/categories/chorizo/', - host: 'example.com' - }; - - return mockUtils.express.invoke(app, req) - .then(function (response) { - response.statusCode.should.eql(200); - }); - }); - - it('serve my-page', function () { - const req = { - secure: true, - method: 'GET', - url: '/my-page/', - host: 'example.com' - }; - - return mockUtils.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('v3'); - sinon.stub(themeService.getActive(), 'config').withArgs('posts_per_page').returns(2); - - app = siteApp({start: true}); - return testUtils.integrationTesting.urlServiceInitAndWait(); - }); - }); - - 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 mockUtils.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 mockUtils.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('v3'); - sinon.stub(themeService.getActive(), 'config').withArgs('posts_per_page').returns(2); - - app = siteApp({start: true}); - return testUtils.integrationTesting.urlServiceInitAndWait(); - }); - }); - - 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 mockUtils.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('v3'); - sinon.stub(themeService.getActive(), 'config').withArgs('posts_per_page').returns(2); - - app = siteApp({start: true}); - return testUtils.integrationTesting.urlServiceInitAndWait(); - }); - }); - - 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 mockUtils.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 mockUtils.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.teardownDb); - 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: 'tagsPublic', - 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: 'tagsPublic', - 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: 'authorsPublic', - resource: 'authors', - 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: 'authorsPublic', - resource: 'authors', - type: 'read', - options: { - slug: 'joe-bloggs', - redirect: false - } - } - }, - router: { - authors: [{redirect: false, slug: 'joe-bloggs'}] - } - } - }, - - '/channel6/': { - controller: 'channel', - data: { - query: { - post: { - controller: 'postsPublic', - resource: 'posts', - type: 'read', - options: { - slug: 'html-ipsum', - redirect: true - } - } - }, - router: { - posts: [{redirect: true, slug: 'html-ipsum'}] - } - } - } - }, - - 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('v3'); - sinon.stub(themeService.getActive(), 'config').withArgs('posts_per_page').returns(10); - - app = siteApp({start: true}); - return testUtils.integrationTesting.urlServiceInitAndWait(); - }); - }); - - 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 mockUtils.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 mockUtils.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 mockUtils.express.invoke(app, req) - .then(function (response) { - const $ = cheerio.load(response.body); - - response.statusCode.should.eql(200); - response.template.should.eql('default'); - - // default template 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 mockUtils.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 mockUtils.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 mockUtils.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 mockUtils.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 mockUtils.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 mockUtils.express.invoke(app, req) - .then(function (response) { - response.statusCode.should.eql(301); - response.headers.location.should.eql('/channel6/'); - }); - }); - - it('serve chorizo: no redirect', function () { - const req = { - secure: true, - method: 'GET', - url: '/tag/chorizo/', - host: 'example.com' - }; - - return mockUtils.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 mockUtils.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: { - '/about/': 'about', - '/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('v3'); - sinon.stub(themeService.getActive(), 'config').withArgs('posts_per_page').returns(2); - - app = siteApp({start: true}); - return testUtils.integrationTesting.urlServiceInitAndWait(); - }); - }); - - 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 mockUtils.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 mockUtils.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 mockUtils.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 mockUtils.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 mockUtils.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 mockUtils.express.invoke(app, req) - .then(function (response) { - const $ = cheerio.load(response.body); - response.statusCode.should.eql(200); - $('head link')[1].attribs.href.should.eql('https://127.0.0.1:2369/rss/'); - }); - }); - }); - }); -}); diff --git a/test/regression/site/intergration-web-site/v2_spec.js b/test/regression/site/intergration-web-site/v2_spec.js new file mode 100644 index 0000000000..11eb3c9fcf --- /dev/null +++ b/test/regression/site/intergration-web-site/v2_spec.js @@ -0,0 +1,1716 @@ +const should = require('should'); +const sinon = require('sinon'); +const _ = require('lodash'); +const cheerio = require('cheerio'); +const testUtils = require('../../../utils'); +const mockUtils = require('../../../utils/mocks'); +const configUtils = require('../../../utils/configUtils'); +const urlUtils = require('../../../utils/urlUtils'); +const appService = require('../../../../core/frontend/services/apps'); +const frontendSettingsService = require('../../../../core/frontend/services/settings'); +const themeService = require('../../../../core/frontend/services/themes'); +const siteApp = require('../../../../core/server/web/parent/app'); + +describe('Integration - Web - Site v2', function () { + let app; + + before(testUtils.integrationTesting.urlService.resetGenerators); + before(testUtils.teardownDb); + before(testUtils.setup('users:roles', 'posts')); + + let postSpy; + + describe('default routes.yaml', function () { + before(function () { + testUtils.integrationTesting.urlService.resetGenerators(); + testUtils.integrationTesting.defaultMocks(sinon, {amp: true}); + testUtils.integrationTesting.overrideGhostConfig(configUtils); + + return testUtils.integrationTesting.initGhost() + .then(function () { + sinon.stub(themeService.getActive(), 'engine').withArgs('ghost-api').returns('v2'); + sinon.stub(themeService.getActive(), 'config').withArgs('posts_per_page').returns(2); + + app = siteApp({start: true}); + return testUtils.integrationTesting.urlServiceInitAndWait(); + }) + .then(() => { + return appService.init(); + }); + }); + + before(function () { + configUtils.set('url', 'http://example.com'); + urlUtils.stubUrlUtilsFromConfig(); + }); + + beforeEach(function () { + const postsAPI = require('../../../../core/server/api/v2/posts-public'); + postSpy = sinon.spy(postsAPI.browse, 'query'); + }); + + afterEach(function () { + postSpy.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 mockUtils.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 mockUtils.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 mockUtils.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 mockUtils.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 mockUtils.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 mockUtils.express.invoke(app, req) + .then(function (response) { + response.statusCode.should.eql(200); + response.template.should.eql('tag'); + + postSpy.args[0][0].options.filter.should.eql('(tags:\'bacon\'+tags.visibility:public)+type:post'); + postSpy.args[0][0].options.page.should.eql(1); + postSpy.args[0][0].options.limit.should.eql(2); + }); + }); + + it('serve tag rss', function () { + const req = { + secure: true, + method: 'GET', + url: '/tag/bacon/rss/', + host: 'example.com' + }; + + return mockUtils.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 mockUtils.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 mockUtils.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 theme asset', function () { + //configUtils.set('url', 'https://example.com'); + + const req = { + secure: true, + method: 'GET', + url: '/assets/css/screen.css', + host: 'example.com' + }; + + return mockUtils.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 mockUtils.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 mockUtils.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 mockUtils.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 mockUtils.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 mockUtils.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: '/favicon.png', + host: 'example.com' + }; + + return mockUtils.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 mockUtils.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('v2'); + sinon.stub(themeService.getActive(), 'config').withArgs('posts_per_page').returns(2); + + app = siteApp({start: true}); + return testUtils.integrationTesting.urlServiceInitAndWait(); + }); + }); + + 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 mockUtils.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 mockUtils.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 mockUtils.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 mockUtils.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 mockUtils.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('v2'); + sinon.stub(themeService.getActive(), 'config').withArgs('posts_per_page').returns(2); + + app = siteApp({start: true}); + return testUtils.integrationTesting.urlServiceInitAndWait(); + }); + }); + + 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 mockUtils.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('v2'); + sinon.stub(themeService.getActive(), 'config').withArgs('posts_per_page').returns(2); + + app = siteApp({start: true}); + return testUtils.integrationTesting.urlServiceInitAndWait(); + }); + }); + + 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 mockUtils.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 mockUtils.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 mockUtils.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 mockUtils.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('v2'); + sinon.stub(themeService.getActive(), 'config').withArgs('posts_per_page').returns(2); + + app = siteApp({start: true}); + return testUtils.integrationTesting.urlServiceInitAndWait(); + }); + }); + + 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 mockUtils.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 mockUtils.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 mockUtils.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('v2'); + sinon.stub(themeService.getActive(), 'config').withArgs('posts_per_page').returns(2); + + app = siteApp({start: true}); + return testUtils.integrationTesting.urlServiceInitAndWait(); + }); + }); + + 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 mockUtils.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 mockUtils.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 mockUtils.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 mockUtils.express.invoke(app, req) + .then(function (response) { + response.statusCode.should.eql(200); + response.template.should.eql('page'); + }); + }); + }); + + describe('collection/routes with data key', function () { + before(function () { + sinon.stub(frontendSettingsService, 'get').returns({ + routes: { + '/my-page/': { + data: { + query: { + page: { + controller: 'pagesPublic', + resource: 'pages', + type: 'read', + options: { + slug: 'static-page-test' + } + } + }, + router: { + pages: [{redirect: true, slug: 'static-page-test'}] + } + }, + templates: ['page'] + } + }, + + collections: { + '/food/': { + permalink: '/food/:slug/', + filter: 'tag:bacon+tag:-chorizo', + data: { + query: { + tag: { + controller: 'tagsPublic', + resource: 'tags', + type: 'read', + options: { + slug: 'bacon' + } + } + }, + router: { + tags: [{redirect: true, slug: 'bacon'}] + } + } + }, + '/sport/': { + permalink: '/sport/:slug/', + filter: 'tag:chorizo+tag:-bacon', + data: { + query: { + apollo: { + controller: 'tagsPublic', + resource: 'tags', + type: 'read', + options: { + slug: 'chorizo' + } + } + }, + router: { + tags: [{redirect: false, slug: 'chorizo'}] + } + } + } + }, + + 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('v2'); + sinon.stub(themeService.getActive(), 'config').withArgs('posts_per_page').returns(2); + + app = siteApp({start: true}); + return testUtils.integrationTesting.urlServiceInitAndWait(); + }); + }); + + 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 mockUtils.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 mockUtils.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 mockUtils.express.invoke(app, req) + .then(function (response) { + response.statusCode.should.eql(200); + response.template.should.eql('index'); + }); + }); + + it('serve chorizo tag', function () { + const req = { + secure: true, + method: 'GET', + url: '/categories/chorizo/', + host: 'example.com' + }; + + return mockUtils.express.invoke(app, req) + .then(function (response) { + response.statusCode.should.eql(200); + }); + }); + + it('serve my-page', function () { + const req = { + secure: true, + method: 'GET', + url: '/my-page/', + host: 'example.com' + }; + + return mockUtils.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('v2'); + sinon.stub(themeService.getActive(), 'config').withArgs('posts_per_page').returns(2); + + app = siteApp({start: true}); + return testUtils.integrationTesting.urlServiceInitAndWait(); + }); + }); + + 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 mockUtils.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 mockUtils.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('v2'); + sinon.stub(themeService.getActive(), 'config').withArgs('posts_per_page').returns(2); + + app = siteApp({start: true}); + return testUtils.integrationTesting.urlServiceInitAndWait(); + }); + }); + + 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 mockUtils.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('v2'); + sinon.stub(themeService.getActive(), 'config').withArgs('posts_per_page').returns(2); + + app = siteApp({start: true}); + return testUtils.integrationTesting.urlServiceInitAndWait(); + }); + }); + + 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 mockUtils.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 mockUtils.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.teardownDb); + 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: 'tagsPublic', + 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: 'tagsPublic', + 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: 'authorsPublic', + resource: 'authors', + 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: 'authorsPublic', + resource: 'authors', + type: 'read', + options: { + slug: 'joe-bloggs', + redirect: false + } + } + }, + router: { + authors: [{redirect: false, slug: 'joe-bloggs'}] + } + } + }, + + '/channel6/': { + controller: 'channel', + data: { + query: { + post: { + controller: 'postsPublic', + resource: 'posts', + type: 'read', + options: { + slug: 'html-ipsum', + redirect: true + } + } + }, + router: { + posts: [{redirect: true, slug: 'html-ipsum'}] + } + } + } + }, + + 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('v2'); + sinon.stub(themeService.getActive(), 'config').withArgs('posts_per_page').returns(10); + + app = siteApp({start: true}); + return testUtils.integrationTesting.urlServiceInitAndWait(); + }); + }); + + 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 mockUtils.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 mockUtils.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 mockUtils.express.invoke(app, req) + .then(function (response) { + const $ = cheerio.load(response.body); + + response.statusCode.should.eql(200); + response.template.should.eql('default'); + + // default template 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 mockUtils.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 mockUtils.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 mockUtils.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 mockUtils.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 mockUtils.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 mockUtils.express.invoke(app, req) + .then(function (response) { + response.statusCode.should.eql(301); + response.headers.location.should.eql('/channel6/'); + }); + }); + + it('serve chorizo: no redirect', function () { + const req = { + secure: true, + method: 'GET', + url: '/tag/chorizo/', + host: 'example.com' + }; + + return mockUtils.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 mockUtils.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('v2'); + sinon.stub(themeService.getActive(), 'config').withArgs('posts_per_page').returns(2); + + app = siteApp({start: true}); + return testUtils.integrationTesting.urlServiceInitAndWait(); + }); + }); + + 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 mockUtils.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 mockUtils.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 mockUtils.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 mockUtils.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 mockUtils.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 mockUtils.express.invoke(app, req) + .then(function (response) { + const $ = cheerio.load(response.body); + response.statusCode.should.eql(200); + $('head link')[1].attribs.href.should.eql('https://127.0.0.1:2369/rss/'); + }); + }); + }); +}); diff --git a/test/regression/site/intergration-web-site/v3_spec.js b/test/regression/site/intergration-web-site/v3_spec.js new file mode 100644 index 0000000000..8439019766 --- /dev/null +++ b/test/regression/site/intergration-web-site/v3_spec.js @@ -0,0 +1,1721 @@ +const should = require('should'); +const sinon = require('sinon'); +const _ = require('lodash'); +const cheerio = require('cheerio'); +const testUtils = require('../../../utils'); +const mockUtils = require('../../../utils/mocks'); +const configUtils = require('../../../utils/configUtils'); +const urlUtils = require('../../../utils/urlUtils'); +const appService = require('../../../../core/frontend/services/apps'); +const frontendSettingsService = require('../../../../core/frontend/services/settings'); +const themeService = require('../../../../core/frontend/services/themes'); +const siteApp = require('../../../../core/server/web/parent/app'); + +describe('Integration - Web - Site v3', function () { + let app; + + before(testUtils.integrationTesting.urlService.resetGenerators); + before(testUtils.teardownDb); + before(testUtils.setup('users:roles', 'posts')); + + describe('v3', function () { + let postSpy; + + describe('default routes.yaml', function () { + before(function () { + testUtils.integrationTesting.urlService.resetGenerators(); + testUtils.integrationTesting.defaultMocks(sinon, {amp: true}); + testUtils.integrationTesting.overrideGhostConfig(configUtils); + + return testUtils.integrationTesting.initGhost() + .then(function () { + sinon.stub(themeService.getActive(), 'engine').withArgs('ghost-api').returns('v3'); + sinon.stub(themeService.getActive(), 'config').withArgs('posts_per_page').returns(2); + + app = siteApp({start: true}); + return testUtils.integrationTesting.urlServiceInitAndWait(); + }) + .then(() => { + return appService.init(); + }); + }); + + before(function () { + configUtils.set('url', 'http://example.com'); + urlUtils.stubUrlUtilsFromConfig(); + }); + + beforeEach(function () { + const postsAPI = require('../../../../core/server/api/v3/posts-public'); + postSpy = sinon.spy(postsAPI.browse, 'query'); + }); + + afterEach(function () { + postSpy.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 mockUtils.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 mockUtils.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 mockUtils.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 mockUtils.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 mockUtils.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 mockUtils.express.invoke(app, req) + .then(function (response) { + response.statusCode.should.eql(200); + response.template.should.eql('tag'); + + postSpy.args[0][0].options.filter.should.eql('(tags:\'bacon\'+tags.visibility:public)+type:post'); + postSpy.args[0][0].options.page.should.eql(1); + postSpy.args[0][0].options.limit.should.eql(2); + }); + }); + + it('serve tag rss', function () { + const req = { + secure: true, + method: 'GET', + url: '/tag/bacon/rss/', + host: 'example.com' + }; + + return mockUtils.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 mockUtils.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 mockUtils.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 theme asset', function () { + //configUtils.set('url', 'https://example.com'); + + const req = { + secure: true, + method: 'GET', + url: '/assets/css/screen.css', + host: 'example.com' + }; + + return mockUtils.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 mockUtils.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 mockUtils.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 mockUtils.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 mockUtils.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 mockUtils.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: '/favicon.png', + host: 'example.com' + }; + + return mockUtils.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 mockUtils.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('v3'); + sinon.stub(themeService.getActive(), 'config').withArgs('posts_per_page').returns(2); + + app = siteApp({start: true}); + return testUtils.integrationTesting.urlServiceInitAndWait(); + }); + }); + + 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 mockUtils.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 mockUtils.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 mockUtils.express.invoke(app, req) + .then(function (response) { + response.statusCode.should.eql(200); + response.template.should.eql('post'); + }); + }); + + it('serve collection: podcast', function () { + const req = { + secure: true, + method: 'GET', + url: '/podcast/', + host: 'example.com' + }; + + return mockUtils.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 mockUtils.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('v3'); + sinon.stub(themeService.getActive(), 'config').withArgs('posts_per_page').returns(2); + + app = siteApp({start: true}); + return testUtils.integrationTesting.urlServiceInitAndWait(); + }); + }); + + 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 mockUtils.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('v3'); + sinon.stub(themeService.getActive(), 'config').withArgs('posts_per_page').returns(2); + + app = siteApp({start: true}); + return testUtils.integrationTesting.urlServiceInitAndWait(); + }); + }); + + 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 mockUtils.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 mockUtils.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 mockUtils.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 mockUtils.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('v3'); + sinon.stub(themeService.getActive(), 'config').withArgs('posts_per_page').returns(2); + + app = siteApp({start: true}); + return testUtils.integrationTesting.urlServiceInitAndWait(); + }); + }); + + 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 mockUtils.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 mockUtils.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 mockUtils.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('v3'); + sinon.stub(themeService.getActive(), 'config').withArgs('posts_per_page').returns(2); + + app = siteApp({start: true}); + return testUtils.integrationTesting.urlServiceInitAndWait(); + }); + }); + + 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 mockUtils.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 mockUtils.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 mockUtils.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 mockUtils.express.invoke(app, req) + .then(function (response) { + response.statusCode.should.eql(200); + response.template.should.eql('page'); + }); + }); + }); + + describe('collection/routes with data key', function () { + before(function () { + sinon.stub(frontendSettingsService, 'get').returns({ + routes: { + '/my-page/': { + data: { + query: { + page: { + controller: 'pagesPublic', + resource: 'pages', + type: 'read', + options: { + slug: 'static-page-test' + } + } + }, + router: { + pages: [{redirect: true, slug: 'static-page-test'}] + } + }, + templates: ['page'] + } + }, + + collections: { + '/food/': { + permalink: '/food/:slug/', + filter: 'tag:bacon+tag:-chorizo', + data: { + query: { + tag: { + controller: 'tagsPublic', + resource: 'tags', + type: 'read', + options: { + slug: 'bacon' + } + } + }, + router: { + tags: [{redirect: true, slug: 'bacon'}] + } + } + }, + '/sport/': { + permalink: '/sport/:slug/', + filter: 'tag:chorizo+tag:-bacon', + data: { + query: { + apollo: { + controller: 'tagsPublic', + resource: 'tags', + type: 'read', + options: { + slug: 'chorizo' + } + } + }, + router: { + tags: [{redirect: false, slug: 'chorizo'}] + } + } + } + }, + + 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('v3'); + sinon.stub(themeService.getActive(), 'config').withArgs('posts_per_page').returns(2); + + app = siteApp({start: true}); + return testUtils.integrationTesting.urlServiceInitAndWait(); + }); + }); + + 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 mockUtils.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 mockUtils.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 mockUtils.express.invoke(app, req) + .then(function (response) { + response.statusCode.should.eql(200); + response.template.should.eql('index'); + }); + }); + + it('serve chorizo tag', function () { + const req = { + secure: true, + method: 'GET', + url: '/categories/chorizo/', + host: 'example.com' + }; + + return mockUtils.express.invoke(app, req) + .then(function (response) { + response.statusCode.should.eql(200); + }); + }); + + it('serve my-page', function () { + const req = { + secure: true, + method: 'GET', + url: '/my-page/', + host: 'example.com' + }; + + return mockUtils.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('v3'); + sinon.stub(themeService.getActive(), 'config').withArgs('posts_per_page').returns(2); + + app = siteApp({start: true}); + return testUtils.integrationTesting.urlServiceInitAndWait(); + }); + }); + + 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 mockUtils.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 mockUtils.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('v3'); + sinon.stub(themeService.getActive(), 'config').withArgs('posts_per_page').returns(2); + + app = siteApp({start: true}); + return testUtils.integrationTesting.urlServiceInitAndWait(); + }); + }); + + 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 mockUtils.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('v3'); + sinon.stub(themeService.getActive(), 'config').withArgs('posts_per_page').returns(2); + + app = siteApp({start: true}); + return testUtils.integrationTesting.urlServiceInitAndWait(); + }); + }); + + 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 mockUtils.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 mockUtils.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.teardownDb); + 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: 'tagsPublic', + 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: 'tagsPublic', + 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: 'authorsPublic', + resource: 'authors', + 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: 'authorsPublic', + resource: 'authors', + type: 'read', + options: { + slug: 'joe-bloggs', + redirect: false + } + } + }, + router: { + authors: [{redirect: false, slug: 'joe-bloggs'}] + } + } + }, + + '/channel6/': { + controller: 'channel', + data: { + query: { + post: { + controller: 'postsPublic', + resource: 'posts', + type: 'read', + options: { + slug: 'html-ipsum', + redirect: true + } + } + }, + router: { + posts: [{redirect: true, slug: 'html-ipsum'}] + } + } + } + }, + + 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('v3'); + sinon.stub(themeService.getActive(), 'config').withArgs('posts_per_page').returns(10); + + app = siteApp({start: true}); + return testUtils.integrationTesting.urlServiceInitAndWait(); + }); + }); + + 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 mockUtils.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 mockUtils.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 mockUtils.express.invoke(app, req) + .then(function (response) { + const $ = cheerio.load(response.body); + + response.statusCode.should.eql(200); + response.template.should.eql('default'); + + // default template 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 mockUtils.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 mockUtils.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 mockUtils.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 mockUtils.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 mockUtils.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 mockUtils.express.invoke(app, req) + .then(function (response) { + response.statusCode.should.eql(301); + response.headers.location.should.eql('/channel6/'); + }); + }); + + it('serve chorizo: no redirect', function () { + const req = { + secure: true, + method: 'GET', + url: '/tag/chorizo/', + host: 'example.com' + }; + + return mockUtils.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 mockUtils.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: { + '/about/': 'about', + '/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('v3'); + sinon.stub(themeService.getActive(), 'config').withArgs('posts_per_page').returns(2); + + app = siteApp({start: true}); + return testUtils.integrationTesting.urlServiceInitAndWait(); + }); + }); + + 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 mockUtils.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 mockUtils.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 mockUtils.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 mockUtils.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 mockUtils.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 mockUtils.express.invoke(app, req) + .then(function (response) { + const $ = cheerio.load(response.body); + response.statusCode.should.eql(200); + $('head link')[1].attribs.href.should.eql('https://127.0.0.1:2369/rss/'); + }); + }); + }); + }); +});