Ghost/core/test/regression/site/site_spec.js

5621 lines
217 KiB
JavaScript
Raw Normal View History

const should = require('should'),
sinon = require('sinon'),
_ = require('lodash'),
✨Dynamic Routing Beta (#9596) refs #9601 ### Dynamic Routing This is the beta version of dynamic routing. - we had a initial implementation of "channels" available in the codebase - we have removed and moved this implementation - there is now a centralised place for dynamic routing - server/services/routing - each routing component is represented by a router type e.g. collections, routes, static pages, taxonomies, rss, preview of posts - keep as much as possible logic of routing helpers, middlewares and controllers - ensure test coverage - connect all the things together - yaml file + validation - routing + routers - url service - sitemaps - url access - deeper implementation of yaml validations - e.g. hard require slashes - ensure routing hierarchy/order - e.g. you enable the subscriber app - you have a custom static page, which lives under the same slug /subscribe - static pages are stronger than apps - e.g. the first collection owns the post it has filtered - a post cannot live in two collections - ensure apps are still working and hook into the routers layer (or better said: and register in the routing service) - put as much as possible comments to the code base for better understanding - ensure a clean debug log - ensure we can unmount routes - e.g. you have a collection permalink of /:slug/ represented by {globals.permalink} - and you change the permalink in the admin to dated permalink - the express route get's refreshed from /:slug/ to /:year/:month/:day/:slug/ - unmount without server restart, yey - ensure we are backwards compatible - e.g. render home.hbs for collection index if collection route is / - ensure you can access your configured permalink from the settings table with {globals.permalink} ### Render 503 if url service did not finish - return 503 if the url service has not finished generating the resource urls ### Rewrite sitemaps - we have rewritten the sitemaps "service", because the url generator does no longer happen on runtime - we generate all urls on bootstrap - the sitemaps service will consume created resource and router urls - these urls will be shown on the xml pages - we listen on url events - we listen on router events - we no longer have to fetch the resources, which is nice - the urlservice pre-fetches resources and emits their urls - the urlservice is the only component who knows which urls are valid - i made some ES6 adaptions - we keep the caching logic -> only regenerate xml if there is a change - updated tests - checked test coverage (100%) ### Re-work usage of Url utility - replace all usages of `urlService.utils.urlFor` by `urlService.getByResourceId` - only for resources e.g. post, author, tag - this is important, because with dynamic routing we no longer create static urls based on the settings permalink on runtime - adapt url utility - adapt tests
2018-06-05 20:02:20 +03:00
cheerio = require('cheerio'),
2018-04-26 18:34:47 +03:00
testUtils = require('../../utils'),
configUtils = require('../../utils/configUtils'),
urlUtils = require('../../utils/urlUtils'),
appsService = require('../../../server/services/apps'),
frontendSettingsService = require('../../../frontend/services/settings'),
themeService = require('../../../frontend/services/themes'),
siteApp = require('../../../server/web/parent-app');
describe('Integration - Web - Site', function () {
let app;
before(testUtils.integrationTesting.urlService.resetGenerators);
before(testUtils.teardown);
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, apps: 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.urlService.waitTillFinished();
})
.then(() => {
return appsService.init();
});
});
before(function () {
configUtils.set('url', 'http://example.com');
urlUtils.stubUrlUtilsFromConfig();
});
beforeEach(function () {
const postsAPI = require('../../../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 testUtils.mocks.express.invoke(app, req)
.then(function (response) {
response.statusCode.should.eql(200);
response.template.should.eql('post');
});
});
it('serve amp', function () {
const req = {
secure: true,
method: 'GET',
url: '/html-ipsum/amp/',
host: 'example.com'
};
return testUtils.mocks.express.invoke(app, req)
.then(function (response) {
response.statusCode.should.eql(200);
response.template.should.match(/amp\.hbs/);
response.body.should.match(/<h1>HTML Ipsum Presents<\/h1>/);
});
});
it('post not found', function () {
const req = {
secure: true,
method: 'GET',
url: '/not-found/',
host: 'example.com'
};
return testUtils.mocks.express.invoke(app, req)
.then(function (response) {
response.statusCode.should.eql(404);
response.template.should.eql('error-404');
});
});
it('serve static page', function () {
const req = {
secure: true,
method: 'GET',
url: '/static-page-test/',
host: 'example.com'
};
return testUtils.mocks.express.invoke(app, req)
.then(function (response) {
response.statusCode.should.eql(200);
response.template.should.eql('page');
});
});
it('serve author', function () {
const req = {
secure: true,
method: 'GET',
url: '/author/joe-bloggs/',
host: 'example.com'
};
return testUtils.mocks.express.invoke(app, req)
.then(function (response) {
const $ = cheerio.load(response.body);
response.statusCode.should.eql(200);
response.template.should.eql('author');
$('.author-bio').length.should.equal(1);
});
});
it('serve tag', function () {
const req = {
secure: true,
method: 'GET',
url: '/tag/bacon/',
host: 'example.com'
};
return testUtils.mocks.express.invoke(app, req)
.then(function (response) {
response.statusCode.should.eql(200);
response.template.should.eql('tag');
Revert post.page->post.type handling no issue - the column addition/removal can be too slow for large sites - will be added back in 3.0 --- Revert "Fixed canary api for page/type column" This reverts commit a5a7e7e919d83af3ea9cd7402a75dff60f2d7e9c. Revert "Updated frontend canary url config for page/type" This reverts commit 19100ec5e6edbe67464c4938521fe25d7ec15041. Revert "Updated canary api to handle type column correctly (#11006)" This reverts commit c3e8ba0523f5460662dcd3cddf0affd337b26eba. Revert "Ensured `page` filter works in routes.yaml" This reverts commit 9037c19e50c4da026f4b797413a682e1411b032f. Revert "Replaced usage of mongo util with nql-map-key-values" This reverts commit 8c5f1d0ef0ad9a03fb3e362c31063e47a0173411. Revert "Added shared nql-map-key-values module" This reverts commit ef4fd4b8ef3824290a00a371dad5a505431ab689. Revert "Ensured page prop is present on content api response" This reverts commit cfa0a0862bf7b247cfeb9b689cfdf6b8e3fb0c10. Revert "Fixed failing regression tests" This reverts commit 9c2bb3811fba8ea127b22f13d21112dcf1f5a46d. Revert "Updated xmlrpc and slack service to use type column" This reverts commit 44a02c7d3635967dd3fe8f96b793b50fd398bd40. Revert "Updated v0.1 posts api to work with type column" This reverts commit 2c81d7c914ac0a2c3b7f1d6d0385479e61d15f18. Revert "Removed updates to v0.1 specific code" This reverts commit 08d83c1f5332b7db6b96814651b496e707c2e124. Revert "Added missing context from ValidationError" This reverts commit cd45ab4f54abefeee8605df84cfc864fff1ad385. Revert "Renamed page->type in the page&posts serializers" This reverts commit df99e724e3d7dc1665916844983849494deea80d. Revert "Added mongo helper to input serializers" This reverts commit fb8eadb4a8109ba987d79decfe331c669a446609. Revert "Passed mongoTransformer through to NQL" This reverts commit 0ae3f0fdfc864dcf5c90c6b56cf975997974742c. Revert "Permitted mongoTransformer option for read methods" This reverts commit a89376bf2618520626d2cf1b8d86f3c8c453db23. Revert "Updated the count plugin to reference the type column" This reverts commit a52f15d3d3503bc9ce4e20961c1f4a0fd49316c7. Revert "Updated hashes for db integrity check" This reverts commit bb6b337be3d30e919e4edfdc2e59182cb81e9e5d. Revert "Remove page column and remaining references" This reverts commit 9d7190d69255ac011848c6bf654886be81abeedc. Revert "Added type column to data generator" This reverts commit e59806cb45c47e0bd547801de54ac5332913fbf5. Revert "Removed references to page column in rss tests" This reverts commit 04d0f855dede1a1bd910c1bc7ca4913ae27472ae. Revert "Removed page column references in validation tests" This reverts commit f0afbc5cc06449ccae034b930709e29133ca8374. Revert "Updated the post model to use the `type` column" This reverts commit 1189bc823ac6adde4f25d63d9fc83ca94e38d672. Revert "Updated url service to use type column" This reverts commit 61612ba8fd38a72d8ef6af5b2c199a9dcd80b80b. Revert "Updated the v2 api to deal with type column" This reverts commit 57afb2de2baf702575a2ea3e4d8e1b914f769e00. Revert "Added type property to post model defaults" This reverts commit dc3345b1c59d03261ecd678a9cbad0ec91ef5a38. Revert "Added type property to the default post fixtures" This reverts commit 82d8c380336b6455ad09622edf7ecd803d0cbe23. Revert "Added type column to posts table" This reverts commit 9b85fc6a69363c27d11963a5078136cedc696156.
2019-08-16 19:46:00 +03:00
postSpy.args[0][0].options.filter.should.eql('(tags:\'bacon\'+tags.visibility:public)+page:false');
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 testUtils.mocks.express.invoke(app, req)
.then(function (response) {
response.statusCode.should.eql(200);
});
});
it('serve collection', function () {
const req = {
secure: true,
method: 'GET',
url: '/',
host: 'example.com'
};
return testUtils.mocks.express.invoke(app, req)
.then(function (response) {
const $ = cheerio.load(response.body);
response.statusCode.should.eql(200);
response.template.should.eql('index');
$('.post-card').length.should.equal(2);
should.exist(response.res.locals.context);
should.exist(response.res.locals.version);
should.exist(response.res.locals.safeVersion);
should.exist(response.res.locals.safeVersion);
should.exist(response.res.locals.relativeUrl);
should.exist(response.res.locals.secure);
should.exist(response.res.routerOptions);
});
});
it('serve collection: page 2', function () {
const req = {
secure: true,
method: 'GET',
url: '/page/2/',
host: 'example.com'
};
return testUtils.mocks.express.invoke(app, req)
.then(function (response) {
const $ = cheerio.load(response.body);
response.statusCode.should.eql(200);
response.template.should.eql('index');
$('.post-card').length.should.equal(2);
});
});
it('serve theme asset', function () {
//configUtils.set('url', 'https://example.com');
const req = {
secure: true,
method: 'GET',
url: '/assets/css/screen.css',
host: 'example.com'
};
return testUtils.mocks.express.invoke(app, req)
.then(function (response) {
response.statusCode.should.eql(200);
});
});
});
describe('behaviour: prettify', function () {
it('url without slash', function () {
const req = {
secure: false,
method: 'GET',
url: '/prettify-me',
host: 'example.com'
};
return testUtils.mocks.express.invoke(app, req)
.then(function (response) {
response.statusCode.should.eql(301);
response.headers.location.should.eql('/prettify-me/');
});
});
});
describe('behaviour: url redirects', function () {
describe('pagination', function () {
it('redirect /page/1/ to /', function () {
const req = {
secure: false,
host: 'example.com',
method: 'GET',
url: '/page/1/'
};
return testUtils.mocks.express.invoke(app, req)
.then(function (response) {
response.statusCode.should.eql(301);
response.headers.location.should.eql('/');
});
});
});
describe('rss', function () {
it('redirect /feed/ to /rss/', function () {
const req = {
secure: false,
host: 'example.com',
method: 'GET',
url: '/feed/'
};
return testUtils.mocks.express.invoke(app, req)
.then(function (response) {
response.statusCode.should.eql(301);
response.headers.location.should.eql('/rss/');
});
});
});
});
});
describe('https', function () {
before(function () {
configUtils.set('url', 'https://example.com');
urlUtils.stubUrlUtilsFromConfig();
});
after(function () {
urlUtils.restore();
configUtils.restore();
});
describe('protocol', function () {
it('blog is https, request is http', function () {
const req = {
secure: false,
host: 'example.com',
method: 'GET',
url: '/html-ipsum'
};
return testUtils.mocks.express.invoke(app, req)
.then(function (response) {
response.statusCode.should.eql(301);
response.headers.location.should.eql('https://example.com/html-ipsum/');
});
});
it('blog is https, request is http, trailing slash exists already', function () {
const req = {
secure: false,
method: 'GET',
url: '/html-ipsum/',
host: 'example.com'
};
return testUtils.mocks.express.invoke(app, req)
.then(function (response) {
response.statusCode.should.eql(301);
response.headers.location.should.eql('https://example.com/html-ipsum/');
});
});
});
describe('assets', function () {
it('blog is https, request is http', function () {
const req = {
secure: false,
method: 'GET',
url: '/favicon.png',
host: 'example.com'
};
return testUtils.mocks.express.invoke(app, req)
.then(function (response) {
response.statusCode.should.eql(301);
response.headers.location.should.eql('https://example.com/favicon.png');
});
});
it('blog is https, request is http', function () {
const req = {
secure: false,
method: 'GET',
url: '/assets/css/main.css',
host: 'example.com'
};
return testUtils.mocks.express.invoke(app, req)
.then(function (response) {
response.statusCode.should.eql(301);
response.headers.location.should.eql('https://example.com/assets/css/main.css');
});
});
});
});
describe('extended routes.yaml: collections', function () {
describe('2 collections', function () {
before(function () {
sinon.stub(frontendSettingsService, 'get').returns({
routes: {
'/': {templates: ['home']}
},
collections: {
'/podcast/': {
permalink: '/podcast/:slug/',
filter: 'featured:true'
},
'/something/': {
permalink: '/something/:slug/',
filter: 'featured:false'
}
},
taxonomies: {
tag: '/categories/:slug/',
author: '/authors/:slug/'
}
});
testUtils.integrationTesting.urlService.resetGenerators();
testUtils.integrationTesting.defaultMocks(sinon, {theme: 'test-theme'});
return testUtils.integrationTesting.initGhost()
.then(function () {
sinon.stub(themeService.getActive(), 'engine').withArgs('ghost-api').returns('v2');
sinon.stub(themeService.getActive(), 'config').withArgs('posts_per_page').returns(2);
app = siteApp({start: true});
return testUtils.integrationTesting.urlService.waitTillFinished();
});
});
beforeEach(function () {
testUtils.integrationTesting.overrideGhostConfig(configUtils);
});
afterEach(function () {
configUtils.restore();
urlUtils.restore();
});
after(function () {
sinon.restore();
});
it('serve static route', function () {
const req = {
secure: true,
method: 'GET',
url: '/',
host: 'example.com'
};
return testUtils.mocks.express.invoke(app, req)
.then(function (response) {
response.statusCode.should.eql(200);
response.template.should.eql('home');
});
});
it('serve rss', function () {
const req = {
secure: true,
method: 'GET',
url: '/podcast/rss/',
host: 'example.com'
};
return testUtils.mocks.express.invoke(app, req)
.then(function (response) {
response.statusCode.should.eql(200);
});
});
it('serve post', function () {
const req = {
secure: true,
method: 'GET',
url: '/something/html-ipsum/',
host: 'example.com'
};
return testUtils.mocks.express.invoke(app, req)
.then(function (response) {
response.statusCode.should.eql(200);
response.template.should.eql('post');
});
});
it('serve collection: podcast with default template', function () {
const req = {
secure: true,
method: 'GET',
url: '/podcast/',
host: 'example.com'
};
return testUtils.mocks.express.invoke(app, req)
.then(function (response) {
const $ = cheerio.load(response.body);
response.statusCode.should.eql(200);
response.template.should.eql('index');
$('.post-card').length.should.equal(2);
});
});
it('serve collection: something with custom template', function () {
const req = {
secure: true,
method: 'GET',
url: '/something/',
host: 'example.com'
};
return testUtils.mocks.express.invoke(app, req)
.then(function (response) {
const $ = cheerio.load(response.body);
response.statusCode.should.eql(200);
response.template.should.eql('something');
});
});
});
describe('no collections', function () {
before(function () {
sinon.stub(frontendSettingsService, 'get').returns({
routes: {
'/something/': {templates: ['something']}
},
collections: {},
taxonomies: {}
});
testUtils.integrationTesting.urlService.resetGenerators();
testUtils.integrationTesting.defaultMocks(sinon, {theme: 'test-theme'});
return testUtils.integrationTesting.initGhost()
.then(function () {
sinon.stub(themeService.getActive(), 'engine').withArgs('ghost-api').returns('v2');
sinon.stub(themeService.getActive(), 'config').withArgs('posts_per_page').returns(2);
app = siteApp({start: true});
return testUtils.integrationTesting.urlService.waitTillFinished();
});
});
beforeEach(function () {
testUtils.integrationTesting.overrideGhostConfig(configUtils);
});
afterEach(function () {
configUtils.restore();
urlUtils.restore();
});
after(function () {
sinon.restore();
});
it('serve route', function () {
const req = {
secure: true,
method: 'GET',
url: '/something/',
host: 'example.com'
};
return testUtils.mocks.express.invoke(app, req)
.then(function (response) {
response.statusCode.should.eql(200);
response.template.should.eql('something');
});
});
});
describe('static permalink route', function () {
before(function () {
sinon.stub(frontendSettingsService, 'get').returns({
routes: {},
collections: {
'/podcast/': {
permalink: '/featured/',
filter: 'featured:true'
},
'/': {
permalink: '/:slug/'
}
},
taxonomies: {}
});
testUtils.integrationTesting.urlService.resetGenerators();
testUtils.integrationTesting.defaultMocks(sinon);
return testUtils.integrationTesting.initGhost()
.then(function () {
sinon.stub(themeService.getActive(), 'engine').withArgs('ghost-api').returns('v2');
sinon.stub(themeService.getActive(), 'config').withArgs('posts_per_page').returns(2);
app = siteApp({start: true});
return testUtils.integrationTesting.urlService.waitTillFinished();
});
});
beforeEach(function () {
testUtils.integrationTesting.overrideGhostConfig(configUtils);
});
afterEach(function () {
configUtils.restore();
urlUtils.restore();
});
after(function () {
sinon.restore();
});
it('serve post', function () {
const req = {
secure: true,
method: 'GET',
url: '/featured/',
host: 'example.com'
};
return testUtils.mocks.express.invoke(app, req)
.then(function (response) {
// We can't find a post with the slug "featured"
response.statusCode.should.eql(404);
response.template.should.eql('error-404');
});
});
it('serve post', function () {
const req = {
secure: true,
method: 'GET',
url: '/html-ipsum/',
host: 'example.com'
};
return testUtils.mocks.express.invoke(app, req)
.then(function (response) {
response.statusCode.should.eql(200);
response.template.should.eql('post');
});
});
it('serve author', function () {
const req = {
secure: true,
method: 'GET',
url: '/author/joe-bloggs/',
host: 'example.com'
};
return testUtils.mocks.express.invoke(app, req)
.then(function (response) {
response.statusCode.should.eql(404);
response.template.should.eql('error-404');
});
});
it('serve tag', function () {
const req = {
secure: true,
method: 'GET',
url: '/tag/bacon/',
host: 'example.com'
};
return testUtils.mocks.express.invoke(app, req)
.then(function (response) {
response.statusCode.should.eql(404);
response.template.should.eql('error-404');
});
});
});
describe('primary author permalink', function () {
before(function () {
sinon.stub(frontendSettingsService, 'get').returns({
routes: {},
collections: {
'/something/': {
permalink: '/:primary_author/:slug/'
}
},
taxonomies: {}
});
testUtils.integrationTesting.urlService.resetGenerators();
testUtils.integrationTesting.defaultMocks(sinon);
return testUtils.integrationTesting.initGhost()
.then(function () {
sinon.stub(themeService.getActive(), 'engine').withArgs('ghost-api').returns('v2');
sinon.stub(themeService.getActive(), 'config').withArgs('posts_per_page').returns(2);
app = siteApp({start: true});
return testUtils.integrationTesting.urlService.waitTillFinished();
});
});
beforeEach(function () {
testUtils.integrationTesting.overrideGhostConfig(configUtils);
});
afterEach(function () {
configUtils.restore();
urlUtils.restore();
});
after(function () {
sinon.restore();
});
it('serve post', function () {
const req = {
secure: true,
method: 'GET',
url: '/joe-bloggs/html-ipsum/',
host: 'example.com'
};
return testUtils.mocks.express.invoke(app, req)
.then(function (response) {
response.statusCode.should.eql(200);
response.template.should.eql('post');
});
});
it('post without author', function () {
const req = {
secure: true,
method: 'GET',
url: '/html-ipsum/',
host: 'example.com'
};
return testUtils.mocks.express.invoke(app, req)
.then(function (response) {
response.statusCode.should.eql(404);
response.template.should.eql('error-404');
});
});
it('page', function () {
const req = {
secure: true,
method: 'GET',
url: '/static-page-test/',
host: 'example.com'
};
return testUtils.mocks.express.invoke(app, req)
.then(function (response) {
response.statusCode.should.eql(200);
response.template.should.eql('page');
});
});
});
describe('primary tag permalink', function () {
before(function () {
sinon.stub(frontendSettingsService, 'get').returns({
routes: {},
collections: {
'/something/': {
permalink: '/something/:primary_tag/:slug/'
}
},
taxonomies: {}
});
testUtils.integrationTesting.urlService.resetGenerators();
testUtils.integrationTesting.defaultMocks(sinon);
return testUtils.integrationTesting.initGhost()
.then(function () {
sinon.stub(themeService.getActive(), 'engine').withArgs('ghost-api').returns('v2');
sinon.stub(themeService.getActive(), 'config').withArgs('posts_per_page').returns(2);
app = siteApp({start: true});
return testUtils.integrationTesting.urlService.waitTillFinished();
});
});
beforeEach(function () {
testUtils.integrationTesting.overrideGhostConfig(configUtils);
});
afterEach(function () {
configUtils.restore();
urlUtils.restore();
});
after(function () {
sinon.restore();
});
it('serve post', function () {
const req = {
secure: true,
method: 'GET',
url: '/something/kitchen-sink/html-ipsum/',
host: 'example.com'
};
return testUtils.mocks.express.invoke(app, req)
.then(function (response) {
response.statusCode.should.eql(200);
response.template.should.eql('post');
});
});
it('post without tag', function () {
const req = {
secure: true,
method: 'GET',
url: '/something/html-ipsum/',
host: 'example.com'
};
return testUtils.mocks.express.invoke(app, req)
.then(function (response) {
response.statusCode.should.eql(404);
response.template.should.eql('error-404');
});
});
it('post without tag', function () {
const req = {
secure: true,
method: 'GET',
url: '/html-ipsum/',
host: 'example.com'
};
return testUtils.mocks.express.invoke(app, req)
.then(function (response) {
response.statusCode.should.eql(404);
response.template.should.eql('error-404');
});
});
it('page', function () {
const req = {
secure: true,
method: 'GET',
url: '/static-page-test/',
host: 'example.com'
};
return testUtils.mocks.express.invoke(app, req)
.then(function (response) {
response.statusCode.should.eql(200);
response.template.should.eql('page');
});
});
});
describe('collection/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.urlService.waitTillFinished();
});
});
beforeEach(function () {
testUtils.integrationTesting.overrideGhostConfig(configUtils);
});
afterEach(function () {
configUtils.restore();
urlUtils.restore();
});
after(function () {
sinon.restore();
});
it('serve /food/', function () {
const req = {
secure: true,
method: 'GET',
url: '/food/',
host: 'example.com'
};
return testUtils.mocks.express.invoke(app, req)
.then(function (response) {
response.statusCode.should.eql(200);
response.template.should.eql('index');
});
});
it('serve bacon tag', function () {
const req = {
secure: true,
method: 'GET',
url: '/categories/bacon/',
host: 'example.com'
};
return testUtils.mocks.express.invoke(app, req)
.then(function (response) {
response.statusCode.should.eql(301);
});
});
it('serve /sport/', function () {
const req = {
secure: true,
method: 'GET',
url: '/sport/',
host: 'example.com'
};
return testUtils.mocks.express.invoke(app, req)
.then(function (response) {
response.statusCode.should.eql(200);
response.template.should.eql('index');
});
});
it('serve chorizo tag', function () {
const req = {
secure: true,
method: 'GET',
url: '/categories/chorizo/',
host: 'example.com'
};
return testUtils.mocks.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 testUtils.mocks.express.invoke(app, req)
.then(function (response) {
response.statusCode.should.eql(200);
});
});
});
});
describe('extended routes.yaml: templates', function () {
describe('default template, no template', function () {
before(function () {
sinon.stub(frontendSettingsService, 'get').returns({
routes: {},
collections: {
'/': {
permalink: '/:slug/',
templates: ['default']
},
'/magic/': {
permalink: '/magic/:slug/'
}
}
});
testUtils.integrationTesting.urlService.resetGenerators();
testUtils.integrationTesting.defaultMocks(sinon);
return testUtils.integrationTesting.initGhost()
.then(function () {
sinon.stub(themeService.getActive(), 'engine').withArgs('ghost-api').returns('v2');
sinon.stub(themeService.getActive(), 'config').withArgs('posts_per_page').returns(2);
app = siteApp({start: true});
return testUtils.integrationTesting.urlService.waitTillFinished();
});
});
beforeEach(function () {
testUtils.integrationTesting.overrideGhostConfig(configUtils);
});
afterEach(function () {
configUtils.restore();
urlUtils.restore();
});
after(function () {
sinon.restore();
});
it('serve collection', function () {
const req = {
secure: true,
method: 'GET',
url: '/',
host: 'example.com'
};
return testUtils.mocks.express.invoke(app, req)
.then(function (response) {
response.statusCode.should.eql(200);
response.template.should.eql('default');
});
});
it('serve second collectiom', function () {
const req = {
secure: true,
method: 'GET',
url: '/magic/',
host: 'example.com'
};
return testUtils.mocks.express.invoke(app, req)
.then(function (response) {
response.statusCode.should.eql(200);
response.template.should.eql('index');
});
});
});
describe('two templates', function () {
before(function () {
sinon.stub(frontendSettingsService, 'get').returns({
routes: {},
collections: {
'/': {
permalink: '/:slug/',
templates: ['something', 'default']
}
}
});
testUtils.integrationTesting.urlService.resetGenerators();
testUtils.integrationTesting.defaultMocks(sinon);
return testUtils.integrationTesting.initGhost()
.then(function () {
sinon.stub(themeService.getActive(), 'engine').withArgs('ghost-api').returns('v2');
sinon.stub(themeService.getActive(), 'config').withArgs('posts_per_page').returns(2);
app = siteApp({start: true});
return testUtils.integrationTesting.urlService.waitTillFinished();
});
});
beforeEach(function () {
testUtils.integrationTesting.overrideGhostConfig(configUtils);
});
afterEach(function () {
configUtils.restore();
urlUtils.restore();
});
after(function () {
sinon.restore();
});
it('serve collection', function () {
const req = {
secure: true,
method: 'GET',
url: '/',
host: 'example.com'
};
return testUtils.mocks.express.invoke(app, req)
.then(function (response) {
response.statusCode.should.eql(200);
response.template.should.eql('default');
});
});
});
describe('home.hbs priority', function () {
before(function () {
sinon.stub(frontendSettingsService, 'get').returns({
routes: {},
collections: {
'/': {
permalink: '/:slug/',
templates: ['something', 'default']
},
'/magic/': {
permalink: '/magic/:slug/',
templates: ['something', 'default']
}
}
});
testUtils.integrationTesting.urlService.resetGenerators();
testUtils.integrationTesting.defaultMocks(sinon, {theme: 'test-theme'});
return testUtils.integrationTesting.initGhost()
.then(function () {
sinon.stub(themeService.getActive(), 'engine').withArgs('ghost-api').returns('v2');
sinon.stub(themeService.getActive(), 'config').withArgs('posts_per_page').returns(2);
app = siteApp({start: true});
return testUtils.integrationTesting.urlService.waitTillFinished();
});
});
beforeEach(function () {
testUtils.integrationTesting.overrideGhostConfig(configUtils);
});
afterEach(function () {
configUtils.restore();
urlUtils.restore();
});
after(function () {
sinon.restore();
});
it('serve collection', function () {
const req = {
secure: true,
method: 'GET',
url: '/',
host: 'example.com'
};
return testUtils.mocks.express.invoke(app, req)
.then(function (response) {
response.statusCode.should.eql(200);
response.template.should.eql('home');
});
});
it('serve second page collection: should use index.hbs', function () {
const req = {
secure: true,
method: 'GET',
url: '/magic/',
host: 'example.com'
};
return testUtils.mocks.express.invoke(app, req)
.then(function (response) {
response.statusCode.should.eql(200);
response.template.should.eql('something');
});
});
});
});
describe('extended routes.yaml: routes', function () {
describe('channels', function () {
before(testUtils.integrationTesting.urlService.resetGenerators);
before(testUtils.teardown);
before(testUtils.setup('users:roles', 'posts'));
before(function () {
testUtils.integrationTesting.defaultMocks(sinon, {theme: 'test-theme-channels'});
sinon.stub(frontendSettingsService, 'get').returns({
routes: {
'/channel1/': {
controller: 'channel',
filter: 'tag:kitchen-sink',
data: {
query: {
tag: {
controller: '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.urlService.waitTillFinished();
});
});
beforeEach(function () {
testUtils.integrationTesting.overrideGhostConfig(configUtils);
});
afterEach(function () {
configUtils.restore();
urlUtils.restore();
});
after(function () {
sinon.restore();
});
it('serve channel 1', function () {
const req = {
secure: true,
method: 'GET',
url: '/channel1/',
host: 'example.com'
};
return testUtils.mocks.express.invoke(app, req)
.then(function (response) {
const $ = cheerio.load(response.body);
response.statusCode.should.eql(200);
response.template.should.eql('index');
$('.post-card').length.should.equal(2);
});
});
✨Dynamic Routing Beta (#9596) refs #9601 ### Dynamic Routing This is the beta version of dynamic routing. - we had a initial implementation of "channels" available in the codebase - we have removed and moved this implementation - there is now a centralised place for dynamic routing - server/services/routing - each routing component is represented by a router type e.g. collections, routes, static pages, taxonomies, rss, preview of posts - keep as much as possible logic of routing helpers, middlewares and controllers - ensure test coverage - connect all the things together - yaml file + validation - routing + routers - url service - sitemaps - url access - deeper implementation of yaml validations - e.g. hard require slashes - ensure routing hierarchy/order - e.g. you enable the subscriber app - you have a custom static page, which lives under the same slug /subscribe - static pages are stronger than apps - e.g. the first collection owns the post it has filtered - a post cannot live in two collections - ensure apps are still working and hook into the routers layer (or better said: and register in the routing service) - put as much as possible comments to the code base for better understanding - ensure a clean debug log - ensure we can unmount routes - e.g. you have a collection permalink of /:slug/ represented by {globals.permalink} - and you change the permalink in the admin to dated permalink - the express route get's refreshed from /:slug/ to /:year/:month/:day/:slug/ - unmount without server restart, yey - ensure we are backwards compatible - e.g. render home.hbs for collection index if collection route is / - ensure you can access your configured permalink from the settings table with {globals.permalink} ### Render 503 if url service did not finish - return 503 if the url service has not finished generating the resource urls ### Rewrite sitemaps - we have rewritten the sitemaps "service", because the url generator does no longer happen on runtime - we generate all urls on bootstrap - the sitemaps service will consume created resource and router urls - these urls will be shown on the xml pages - we listen on url events - we listen on router events - we no longer have to fetch the resources, which is nice - the urlservice pre-fetches resources and emits their urls - the urlservice is the only component who knows which urls are valid - i made some ES6 adaptions - we keep the caching logic -> only regenerate xml if there is a change - updated tests - checked test coverage (100%) ### Re-work usage of Url utility - replace all usages of `urlService.utils.urlFor` by `urlService.getByResourceId` - only for resources e.g. post, author, tag - this is important, because with dynamic routing we no longer create static urls based on the settings permalink on runtime - adapt url utility - adapt tests
2018-06-05 20:02:20 +03:00
it('serve channel 1: rss', function () {
const req = {
secure: true,
method: 'GET',
url: '/channel1/rss/',
host: 'example.com'
};
✨Dynamic Routing Beta (#9596) refs #9601 ### Dynamic Routing This is the beta version of dynamic routing. - we had a initial implementation of "channels" available in the codebase - we have removed and moved this implementation - there is now a centralised place for dynamic routing - server/services/routing - each routing component is represented by a router type e.g. collections, routes, static pages, taxonomies, rss, preview of posts - keep as much as possible logic of routing helpers, middlewares and controllers - ensure test coverage - connect all the things together - yaml file + validation - routing + routers - url service - sitemaps - url access - deeper implementation of yaml validations - e.g. hard require slashes - ensure routing hierarchy/order - e.g. you enable the subscriber app - you have a custom static page, which lives under the same slug /subscribe - static pages are stronger than apps - e.g. the first collection owns the post it has filtered - a post cannot live in two collections - ensure apps are still working and hook into the routers layer (or better said: and register in the routing service) - put as much as possible comments to the code base for better understanding - ensure a clean debug log - ensure we can unmount routes - e.g. you have a collection permalink of /:slug/ represented by {globals.permalink} - and you change the permalink in the admin to dated permalink - the express route get's refreshed from /:slug/ to /:year/:month/:day/:slug/ - unmount without server restart, yey - ensure we are backwards compatible - e.g. render home.hbs for collection index if collection route is / - ensure you can access your configured permalink from the settings table with {globals.permalink} ### Render 503 if url service did not finish - return 503 if the url service has not finished generating the resource urls ### Rewrite sitemaps - we have rewritten the sitemaps "service", because the url generator does no longer happen on runtime - we generate all urls on bootstrap - the sitemaps service will consume created resource and router urls - these urls will be shown on the xml pages - we listen on url events - we listen on router events - we no longer have to fetch the resources, which is nice - the urlservice pre-fetches resources and emits their urls - the urlservice is the only component who knows which urls are valid - i made some ES6 adaptions - we keep the caching logic -> only regenerate xml if there is a change - updated tests - checked test coverage (100%) ### Re-work usage of Url utility - replace all usages of `urlService.utils.urlFor` by `urlService.getByResourceId` - only for resources e.g. post, author, tag - this is important, because with dynamic routing we no longer create static urls based on the settings permalink on runtime - adapt url utility - adapt tests
2018-06-05 20:02:20 +03:00
return testUtils.mocks.express.invoke(app, req)
.then(function (response) {
response.statusCode.should.eql(200);
response.headers['content-type'].should.eql('text/xml; charset=UTF-8');
});
✨Dynamic Routing Beta (#9596) refs #9601 ### Dynamic Routing This is the beta version of dynamic routing. - we had a initial implementation of "channels" available in the codebase - we have removed and moved this implementation - there is now a centralised place for dynamic routing - server/services/routing - each routing component is represented by a router type e.g. collections, routes, static pages, taxonomies, rss, preview of posts - keep as much as possible logic of routing helpers, middlewares and controllers - ensure test coverage - connect all the things together - yaml file + validation - routing + routers - url service - sitemaps - url access - deeper implementation of yaml validations - e.g. hard require slashes - ensure routing hierarchy/order - e.g. you enable the subscriber app - you have a custom static page, which lives under the same slug /subscribe - static pages are stronger than apps - e.g. the first collection owns the post it has filtered - a post cannot live in two collections - ensure apps are still working and hook into the routers layer (or better said: and register in the routing service) - put as much as possible comments to the code base for better understanding - ensure a clean debug log - ensure we can unmount routes - e.g. you have a collection permalink of /:slug/ represented by {globals.permalink} - and you change the permalink in the admin to dated permalink - the express route get's refreshed from /:slug/ to /:year/:month/:day/:slug/ - unmount without server restart, yey - ensure we are backwards compatible - e.g. render home.hbs for collection index if collection route is / - ensure you can access your configured permalink from the settings table with {globals.permalink} ### Render 503 if url service did not finish - return 503 if the url service has not finished generating the resource urls ### Rewrite sitemaps - we have rewritten the sitemaps "service", because the url generator does no longer happen on runtime - we generate all urls on bootstrap - the sitemaps service will consume created resource and router urls - these urls will be shown on the xml pages - we listen on url events - we listen on router events - we no longer have to fetch the resources, which is nice - the urlservice pre-fetches resources and emits their urls - the urlservice is the only component who knows which urls are valid - i made some ES6 adaptions - we keep the caching logic -> only regenerate xml if there is a change - updated tests - checked test coverage (100%) ### Re-work usage of Url utility - replace all usages of `urlService.utils.urlFor` by `urlService.getByResourceId` - only for resources e.g. post, author, tag - this is important, because with dynamic routing we no longer create static urls based on the settings permalink on runtime - adapt url utility - adapt tests
2018-06-05 20:02:20 +03:00
});
it('serve channel 2', function () {
const req = {
secure: true,
method: 'GET',
url: '/channel2/',
host: 'example.com'
};
✨Dynamic Routing Beta (#9596) refs #9601 ### Dynamic Routing This is the beta version of dynamic routing. - we had a initial implementation of "channels" available in the codebase - we have removed and moved this implementation - there is now a centralised place for dynamic routing - server/services/routing - each routing component is represented by a router type e.g. collections, routes, static pages, taxonomies, rss, preview of posts - keep as much as possible logic of routing helpers, middlewares and controllers - ensure test coverage - connect all the things together - yaml file + validation - routing + routers - url service - sitemaps - url access - deeper implementation of yaml validations - e.g. hard require slashes - ensure routing hierarchy/order - e.g. you enable the subscriber app - you have a custom static page, which lives under the same slug /subscribe - static pages are stronger than apps - e.g. the first collection owns the post it has filtered - a post cannot live in two collections - ensure apps are still working and hook into the routers layer (or better said: and register in the routing service) - put as much as possible comments to the code base for better understanding - ensure a clean debug log - ensure we can unmount routes - e.g. you have a collection permalink of /:slug/ represented by {globals.permalink} - and you change the permalink in the admin to dated permalink - the express route get's refreshed from /:slug/ to /:year/:month/:day/:slug/ - unmount without server restart, yey - ensure we are backwards compatible - e.g. render home.hbs for collection index if collection route is / - ensure you can access your configured permalink from the settings table with {globals.permalink} ### Render 503 if url service did not finish - return 503 if the url service has not finished generating the resource urls ### Rewrite sitemaps - we have rewritten the sitemaps "service", because the url generator does no longer happen on runtime - we generate all urls on bootstrap - the sitemaps service will consume created resource and router urls - these urls will be shown on the xml pages - we listen on url events - we listen on router events - we no longer have to fetch the resources, which is nice - the urlservice pre-fetches resources and emits their urls - the urlservice is the only component who knows which urls are valid - i made some ES6 adaptions - we keep the caching logic -> only regenerate xml if there is a change - updated tests - checked test coverage (100%) ### Re-work usage of Url utility - replace all usages of `urlService.utils.urlFor` by `urlService.getByResourceId` - only for resources e.g. post, author, tag - this is important, because with dynamic routing we no longer create static urls based on the settings permalink on runtime - adapt url utility - adapt tests
2018-06-05 20:02:20 +03:00
return testUtils.mocks.express.invoke(app, req)
.then(function (response) {
const $ = cheerio.load(response.body);
✨Dynamic Routing Beta (#9596) refs #9601 ### Dynamic Routing This is the beta version of dynamic routing. - we had a initial implementation of "channels" available in the codebase - we have removed and moved this implementation - there is now a centralised place for dynamic routing - server/services/routing - each routing component is represented by a router type e.g. collections, routes, static pages, taxonomies, rss, preview of posts - keep as much as possible logic of routing helpers, middlewares and controllers - ensure test coverage - connect all the things together - yaml file + validation - routing + routers - url service - sitemaps - url access - deeper implementation of yaml validations - e.g. hard require slashes - ensure routing hierarchy/order - e.g. you enable the subscriber app - you have a custom static page, which lives under the same slug /subscribe - static pages are stronger than apps - e.g. the first collection owns the post it has filtered - a post cannot live in two collections - ensure apps are still working and hook into the routers layer (or better said: and register in the routing service) - put as much as possible comments to the code base for better understanding - ensure a clean debug log - ensure we can unmount routes - e.g. you have a collection permalink of /:slug/ represented by {globals.permalink} - and you change the permalink in the admin to dated permalink - the express route get's refreshed from /:slug/ to /:year/:month/:day/:slug/ - unmount without server restart, yey - ensure we are backwards compatible - e.g. render home.hbs for collection index if collection route is / - ensure you can access your configured permalink from the settings table with {globals.permalink} ### Render 503 if url service did not finish - return 503 if the url service has not finished generating the resource urls ### Rewrite sitemaps - we have rewritten the sitemaps "service", because the url generator does no longer happen on runtime - we generate all urls on bootstrap - the sitemaps service will consume created resource and router urls - these urls will be shown on the xml pages - we listen on url events - we listen on router events - we no longer have to fetch the resources, which is nice - the urlservice pre-fetches resources and emits their urls - the urlservice is the only component who knows which urls are valid - i made some ES6 adaptions - we keep the caching logic -> only regenerate xml if there is a change - updated tests - checked test coverage (100%) ### Re-work usage of Url utility - replace all usages of `urlService.utils.urlFor` by `urlService.getByResourceId` - only for resources e.g. post, author, tag - this is important, because with dynamic routing we no longer create static urls based on the settings permalink on runtime - adapt url utility - adapt tests
2018-06-05 20:02:20 +03:00
response.statusCode.should.eql(200);
response.template.should.eql('default');
✨Dynamic Routing Beta (#9596) refs #9601 ### Dynamic Routing This is the beta version of dynamic routing. - we had a initial implementation of "channels" available in the codebase - we have removed and moved this implementation - there is now a centralised place for dynamic routing - server/services/routing - each routing component is represented by a router type e.g. collections, routes, static pages, taxonomies, rss, preview of posts - keep as much as possible logic of routing helpers, middlewares and controllers - ensure test coverage - connect all the things together - yaml file + validation - routing + routers - url service - sitemaps - url access - deeper implementation of yaml validations - e.g. hard require slashes - ensure routing hierarchy/order - e.g. you enable the subscriber app - you have a custom static page, which lives under the same slug /subscribe - static pages are stronger than apps - e.g. the first collection owns the post it has filtered - a post cannot live in two collections - ensure apps are still working and hook into the routers layer (or better said: and register in the routing service) - put as much as possible comments to the code base for better understanding - ensure a clean debug log - ensure we can unmount routes - e.g. you have a collection permalink of /:slug/ represented by {globals.permalink} - and you change the permalink in the admin to dated permalink - the express route get's refreshed from /:slug/ to /:year/:month/:day/:slug/ - unmount without server restart, yey - ensure we are backwards compatible - e.g. render home.hbs for collection index if collection route is / - ensure you can access your configured permalink from the settings table with {globals.permalink} ### Render 503 if url service did not finish - return 503 if the url service has not finished generating the resource urls ### Rewrite sitemaps - we have rewritten the sitemaps "service", because the url generator does no longer happen on runtime - we generate all urls on bootstrap - the sitemaps service will consume created resource and router urls - these urls will be shown on the xml pages - we listen on url events - we listen on router events - we no longer have to fetch the resources, which is nice - the urlservice pre-fetches resources and emits their urls - the urlservice is the only component who knows which urls are valid - i made some ES6 adaptions - we keep the caching logic -> only regenerate xml if there is a change - updated tests - checked test coverage (100%) ### Re-work usage of Url utility - replace all usages of `urlService.utils.urlFor` by `urlService.getByResourceId` - only for resources e.g. post, author, tag - this is important, because with dynamic routing we no longer create static urls based on the settings permalink on runtime - adapt url utility - adapt tests
2018-06-05 20:02:20 +03:00
// default tempalte does not list posts
$('.post-card').length.should.equal(0);
});
});
✨Dynamic Routing Beta (#9596) refs #9601 ### Dynamic Routing This is the beta version of dynamic routing. - we had a initial implementation of "channels" available in the codebase - we have removed and moved this implementation - there is now a centralised place for dynamic routing - server/services/routing - each routing component is represented by a router type e.g. collections, routes, static pages, taxonomies, rss, preview of posts - keep as much as possible logic of routing helpers, middlewares and controllers - ensure test coverage - connect all the things together - yaml file + validation - routing + routers - url service - sitemaps - url access - deeper implementation of yaml validations - e.g. hard require slashes - ensure routing hierarchy/order - e.g. you enable the subscriber app - you have a custom static page, which lives under the same slug /subscribe - static pages are stronger than apps - e.g. the first collection owns the post it has filtered - a post cannot live in two collections - ensure apps are still working and hook into the routers layer (or better said: and register in the routing service) - put as much as possible comments to the code base for better understanding - ensure a clean debug log - ensure we can unmount routes - e.g. you have a collection permalink of /:slug/ represented by {globals.permalink} - and you change the permalink in the admin to dated permalink - the express route get's refreshed from /:slug/ to /:year/:month/:day/:slug/ - unmount without server restart, yey - ensure we are backwards compatible - e.g. render home.hbs for collection index if collection route is / - ensure you can access your configured permalink from the settings table with {globals.permalink} ### Render 503 if url service did not finish - return 503 if the url service has not finished generating the resource urls ### Rewrite sitemaps - we have rewritten the sitemaps "service", because the url generator does no longer happen on runtime - we generate all urls on bootstrap - the sitemaps service will consume created resource and router urls - these urls will be shown on the xml pages - we listen on url events - we listen on router events - we no longer have to fetch the resources, which is nice - the urlservice pre-fetches resources and emits their urls - the urlservice is the only component who knows which urls are valid - i made some ES6 adaptions - we keep the caching logic -> only regenerate xml if there is a change - updated tests - checked test coverage (100%) ### Re-work usage of Url utility - replace all usages of `urlService.utils.urlFor` by `urlService.getByResourceId` - only for resources e.g. post, author, tag - this is important, because with dynamic routing we no longer create static urls based on the settings permalink on runtime - adapt url utility - adapt tests
2018-06-05 20:02:20 +03:00
it('serve channel 3', function () {
const req = {
secure: true,
method: 'GET',
url: '/channel3/',
host: 'example.com'
};
✨Dynamic Routing Beta (#9596) refs #9601 ### Dynamic Routing This is the beta version of dynamic routing. - we had a initial implementation of "channels" available in the codebase - we have removed and moved this implementation - there is now a centralised place for dynamic routing - server/services/routing - each routing component is represented by a router type e.g. collections, routes, static pages, taxonomies, rss, preview of posts - keep as much as possible logic of routing helpers, middlewares and controllers - ensure test coverage - connect all the things together - yaml file + validation - routing + routers - url service - sitemaps - url access - deeper implementation of yaml validations - e.g. hard require slashes - ensure routing hierarchy/order - e.g. you enable the subscriber app - you have a custom static page, which lives under the same slug /subscribe - static pages are stronger than apps - e.g. the first collection owns the post it has filtered - a post cannot live in two collections - ensure apps are still working and hook into the routers layer (or better said: and register in the routing service) - put as much as possible comments to the code base for better understanding - ensure a clean debug log - ensure we can unmount routes - e.g. you have a collection permalink of /:slug/ represented by {globals.permalink} - and you change the permalink in the admin to dated permalink - the express route get's refreshed from /:slug/ to /:year/:month/:day/:slug/ - unmount without server restart, yey - ensure we are backwards compatible - e.g. render home.hbs for collection index if collection route is / - ensure you can access your configured permalink from the settings table with {globals.permalink} ### Render 503 if url service did not finish - return 503 if the url service has not finished generating the resource urls ### Rewrite sitemaps - we have rewritten the sitemaps "service", because the url generator does no longer happen on runtime - we generate all urls on bootstrap - the sitemaps service will consume created resource and router urls - these urls will be shown on the xml pages - we listen on url events - we listen on router events - we no longer have to fetch the resources, which is nice - the urlservice pre-fetches resources and emits their urls - the urlservice is the only component who knows which urls are valid - i made some ES6 adaptions - we keep the caching logic -> only regenerate xml if there is a change - updated tests - checked test coverage (100%) ### Re-work usage of Url utility - replace all usages of `urlService.utils.urlFor` by `urlService.getByResourceId` - only for resources e.g. post, author, tag - this is important, because with dynamic routing we no longer create static urls based on the settings permalink on runtime - adapt url utility - adapt tests
2018-06-05 20:02:20 +03:00
return testUtils.mocks.express.invoke(app, req)
.then(function (response) {
const $ = cheerio.load(response.body);
✨Dynamic Routing Beta (#9596) refs #9601 ### Dynamic Routing This is the beta version of dynamic routing. - we had a initial implementation of "channels" available in the codebase - we have removed and moved this implementation - there is now a centralised place for dynamic routing - server/services/routing - each routing component is represented by a router type e.g. collections, routes, static pages, taxonomies, rss, preview of posts - keep as much as possible logic of routing helpers, middlewares and controllers - ensure test coverage - connect all the things together - yaml file + validation - routing + routers - url service - sitemaps - url access - deeper implementation of yaml validations - e.g. hard require slashes - ensure routing hierarchy/order - e.g. you enable the subscriber app - you have a custom static page, which lives under the same slug /subscribe - static pages are stronger than apps - e.g. the first collection owns the post it has filtered - a post cannot live in two collections - ensure apps are still working and hook into the routers layer (or better said: and register in the routing service) - put as much as possible comments to the code base for better understanding - ensure a clean debug log - ensure we can unmount routes - e.g. you have a collection permalink of /:slug/ represented by {globals.permalink} - and you change the permalink in the admin to dated permalink - the express route get's refreshed from /:slug/ to /:year/:month/:day/:slug/ - unmount without server restart, yey - ensure we are backwards compatible - e.g. render home.hbs for collection index if collection route is / - ensure you can access your configured permalink from the settings table with {globals.permalink} ### Render 503 if url service did not finish - return 503 if the url service has not finished generating the resource urls ### Rewrite sitemaps - we have rewritten the sitemaps "service", because the url generator does no longer happen on runtime - we generate all urls on bootstrap - the sitemaps service will consume created resource and router urls - these urls will be shown on the xml pages - we listen on url events - we listen on router events - we no longer have to fetch the resources, which is nice - the urlservice pre-fetches resources and emits their urls - the urlservice is the only component who knows which urls are valid - i made some ES6 adaptions - we keep the caching logic -> only regenerate xml if there is a change - updated tests - checked test coverage (100%) ### Re-work usage of Url utility - replace all usages of `urlService.utils.urlFor` by `urlService.getByResourceId` - only for resources e.g. post, author, tag - this is important, because with dynamic routing we no longer create static urls based on the settings permalink on runtime - adapt url utility - adapt tests
2018-06-05 20:02:20 +03:00
response.statusCode.should.eql(200);
response.template.should.eql('channel3');
});
});
✨Dynamic Routing Beta (#9596) refs #9601 ### Dynamic Routing This is the beta version of dynamic routing. - we had a initial implementation of "channels" available in the codebase - we have removed and moved this implementation - there is now a centralised place for dynamic routing - server/services/routing - each routing component is represented by a router type e.g. collections, routes, static pages, taxonomies, rss, preview of posts - keep as much as possible logic of routing helpers, middlewares and controllers - ensure test coverage - connect all the things together - yaml file + validation - routing + routers - url service - sitemaps - url access - deeper implementation of yaml validations - e.g. hard require slashes - ensure routing hierarchy/order - e.g. you enable the subscriber app - you have a custom static page, which lives under the same slug /subscribe - static pages are stronger than apps - e.g. the first collection owns the post it has filtered - a post cannot live in two collections - ensure apps are still working and hook into the routers layer (or better said: and register in the routing service) - put as much as possible comments to the code base for better understanding - ensure a clean debug log - ensure we can unmount routes - e.g. you have a collection permalink of /:slug/ represented by {globals.permalink} - and you change the permalink in the admin to dated permalink - the express route get's refreshed from /:slug/ to /:year/:month/:day/:slug/ - unmount without server restart, yey - ensure we are backwards compatible - e.g. render home.hbs for collection index if collection route is / - ensure you can access your configured permalink from the settings table with {globals.permalink} ### Render 503 if url service did not finish - return 503 if the url service has not finished generating the resource urls ### Rewrite sitemaps - we have rewritten the sitemaps "service", because the url generator does no longer happen on runtime - we generate all urls on bootstrap - the sitemaps service will consume created resource and router urls - these urls will be shown on the xml pages - we listen on url events - we listen on router events - we no longer have to fetch the resources, which is nice - the urlservice pre-fetches resources and emits their urls - the urlservice is the only component who knows which urls are valid - i made some ES6 adaptions - we keep the caching logic -> only regenerate xml if there is a change - updated tests - checked test coverage (100%) ### Re-work usage of Url utility - replace all usages of `urlService.utils.urlFor` by `urlService.getByResourceId` - only for resources e.g. post, author, tag - this is important, because with dynamic routing we no longer create static urls based on the settings permalink on runtime - adapt url utility - adapt tests
2018-06-05 20:02:20 +03:00
it('serve channel 4', function () {
const req = {
secure: true,
method: 'GET',
url: '/channel4/',
host: 'example.com'
};
✨Dynamic Routing Beta (#9596) refs #9601 ### Dynamic Routing This is the beta version of dynamic routing. - we had a initial implementation of "channels" available in the codebase - we have removed and moved this implementation - there is now a centralised place for dynamic routing - server/services/routing - each routing component is represented by a router type e.g. collections, routes, static pages, taxonomies, rss, preview of posts - keep as much as possible logic of routing helpers, middlewares and controllers - ensure test coverage - connect all the things together - yaml file + validation - routing + routers - url service - sitemaps - url access - deeper implementation of yaml validations - e.g. hard require slashes - ensure routing hierarchy/order - e.g. you enable the subscriber app - you have a custom static page, which lives under the same slug /subscribe - static pages are stronger than apps - e.g. the first collection owns the post it has filtered - a post cannot live in two collections - ensure apps are still working and hook into the routers layer (or better said: and register in the routing service) - put as much as possible comments to the code base for better understanding - ensure a clean debug log - ensure we can unmount routes - e.g. you have a collection permalink of /:slug/ represented by {globals.permalink} - and you change the permalink in the admin to dated permalink - the express route get's refreshed from /:slug/ to /:year/:month/:day/:slug/ - unmount without server restart, yey - ensure we are backwards compatible - e.g. render home.hbs for collection index if collection route is / - ensure you can access your configured permalink from the settings table with {globals.permalink} ### Render 503 if url service did not finish - return 503 if the url service has not finished generating the resource urls ### Rewrite sitemaps - we have rewritten the sitemaps "service", because the url generator does no longer happen on runtime - we generate all urls on bootstrap - the sitemaps service will consume created resource and router urls - these urls will be shown on the xml pages - we listen on url events - we listen on router events - we no longer have to fetch the resources, which is nice - the urlservice pre-fetches resources and emits their urls - the urlservice is the only component who knows which urls are valid - i made some ES6 adaptions - we keep the caching logic -> only regenerate xml if there is a change - updated tests - checked test coverage (100%) ### Re-work usage of Url utility - replace all usages of `urlService.utils.urlFor` by `urlService.getByResourceId` - only for resources e.g. post, author, tag - this is important, because with dynamic routing we no longer create static urls based on the settings permalink on runtime - adapt url utility - adapt tests
2018-06-05 20:02:20 +03:00
return testUtils.mocks.express.invoke(app, req)
.then(function (response) {
const $ = cheerio.load(response.body);
✨Dynamic Routing Beta (#9596) refs #9601 ### Dynamic Routing This is the beta version of dynamic routing. - we had a initial implementation of "channels" available in the codebase - we have removed and moved this implementation - there is now a centralised place for dynamic routing - server/services/routing - each routing component is represented by a router type e.g. collections, routes, static pages, taxonomies, rss, preview of posts - keep as much as possible logic of routing helpers, middlewares and controllers - ensure test coverage - connect all the things together - yaml file + validation - routing + routers - url service - sitemaps - url access - deeper implementation of yaml validations - e.g. hard require slashes - ensure routing hierarchy/order - e.g. you enable the subscriber app - you have a custom static page, which lives under the same slug /subscribe - static pages are stronger than apps - e.g. the first collection owns the post it has filtered - a post cannot live in two collections - ensure apps are still working and hook into the routers layer (or better said: and register in the routing service) - put as much as possible comments to the code base for better understanding - ensure a clean debug log - ensure we can unmount routes - e.g. you have a collection permalink of /:slug/ represented by {globals.permalink} - and you change the permalink in the admin to dated permalink - the express route get's refreshed from /:slug/ to /:year/:month/:day/:slug/ - unmount without server restart, yey - ensure we are backwards compatible - e.g. render home.hbs for collection index if collection route is / - ensure you can access your configured permalink from the settings table with {globals.permalink} ### Render 503 if url service did not finish - return 503 if the url service has not finished generating the resource urls ### Rewrite sitemaps - we have rewritten the sitemaps "service", because the url generator does no longer happen on runtime - we generate all urls on bootstrap - the sitemaps service will consume created resource and router urls - these urls will be shown on the xml pages - we listen on url events - we listen on router events - we no longer have to fetch the resources, which is nice - the urlservice pre-fetches resources and emits their urls - the urlservice is the only component who knows which urls are valid - i made some ES6 adaptions - we keep the caching logic -> only regenerate xml if there is a change - updated tests - checked test coverage (100%) ### Re-work usage of Url utility - replace all usages of `urlService.utils.urlFor` by `urlService.getByResourceId` - only for resources e.g. post, author, tag - this is important, because with dynamic routing we no longer create static urls based on the settings permalink on runtime - adapt url utility - adapt tests
2018-06-05 20:02:20 +03:00
response.statusCode.should.eql(200);
response.template.should.eql('index');
✨Dynamic Routing Beta (#9596) refs #9601 ### Dynamic Routing This is the beta version of dynamic routing. - we had a initial implementation of "channels" available in the codebase - we have removed and moved this implementation - there is now a centralised place for dynamic routing - server/services/routing - each routing component is represented by a router type e.g. collections, routes, static pages, taxonomies, rss, preview of posts - keep as much as possible logic of routing helpers, middlewares and controllers - ensure test coverage - connect all the things together - yaml file + validation - routing + routers - url service - sitemaps - url access - deeper implementation of yaml validations - e.g. hard require slashes - ensure routing hierarchy/order - e.g. you enable the subscriber app - you have a custom static page, which lives under the same slug /subscribe - static pages are stronger than apps - e.g. the first collection owns the post it has filtered - a post cannot live in two collections - ensure apps are still working and hook into the routers layer (or better said: and register in the routing service) - put as much as possible comments to the code base for better understanding - ensure a clean debug log - ensure we can unmount routes - e.g. you have a collection permalink of /:slug/ represented by {globals.permalink} - and you change the permalink in the admin to dated permalink - the express route get's refreshed from /:slug/ to /:year/:month/:day/:slug/ - unmount without server restart, yey - ensure we are backwards compatible - e.g. render home.hbs for collection index if collection route is / - ensure you can access your configured permalink from the settings table with {globals.permalink} ### Render 503 if url service did not finish - return 503 if the url service has not finished generating the resource urls ### Rewrite sitemaps - we have rewritten the sitemaps "service", because the url generator does no longer happen on runtime - we generate all urls on bootstrap - the sitemaps service will consume created resource and router urls - these urls will be shown on the xml pages - we listen on url events - we listen on router events - we no longer have to fetch the resources, which is nice - the urlservice pre-fetches resources and emits their urls - the urlservice is the only component who knows which urls are valid - i made some ES6 adaptions - we keep the caching logic -> only regenerate xml if there is a change - updated tests - checked test coverage (100%) ### Re-work usage of Url utility - replace all usages of `urlService.utils.urlFor` by `urlService.getByResourceId` - only for resources e.g. post, author, tag - this is important, because with dynamic routing we no longer create static urls based on the settings permalink on runtime - adapt url utility - adapt tests
2018-06-05 20:02:20 +03:00
$('.post-card').length.should.equal(4);
});
✨Dynamic Routing Beta (#9596) refs #9601 ### Dynamic Routing This is the beta version of dynamic routing. - we had a initial implementation of "channels" available in the codebase - we have removed and moved this implementation - there is now a centralised place for dynamic routing - server/services/routing - each routing component is represented by a router type e.g. collections, routes, static pages, taxonomies, rss, preview of posts - keep as much as possible logic of routing helpers, middlewares and controllers - ensure test coverage - connect all the things together - yaml file + validation - routing + routers - url service - sitemaps - url access - deeper implementation of yaml validations - e.g. hard require slashes - ensure routing hierarchy/order - e.g. you enable the subscriber app - you have a custom static page, which lives under the same slug /subscribe - static pages are stronger than apps - e.g. the first collection owns the post it has filtered - a post cannot live in two collections - ensure apps are still working and hook into the routers layer (or better said: and register in the routing service) - put as much as possible comments to the code base for better understanding - ensure a clean debug log - ensure we can unmount routes - e.g. you have a collection permalink of /:slug/ represented by {globals.permalink} - and you change the permalink in the admin to dated permalink - the express route get's refreshed from /:slug/ to /:year/:month/:day/:slug/ - unmount without server restart, yey - ensure we are backwards compatible - e.g. render home.hbs for collection index if collection route is / - ensure you can access your configured permalink from the settings table with {globals.permalink} ### Render 503 if url service did not finish - return 503 if the url service has not finished generating the resource urls ### Rewrite sitemaps - we have rewritten the sitemaps "service", because the url generator does no longer happen on runtime - we generate all urls on bootstrap - the sitemaps service will consume created resource and router urls - these urls will be shown on the xml pages - we listen on url events - we listen on router events - we no longer have to fetch the resources, which is nice - the urlservice pre-fetches resources and emits their urls - the urlservice is the only component who knows which urls are valid - i made some ES6 adaptions - we keep the caching logic -> only regenerate xml if there is a change - updated tests - checked test coverage (100%) ### Re-work usage of Url utility - replace all usages of `urlService.utils.urlFor` by `urlService.getByResourceId` - only for resources e.g. post, author, tag - this is important, because with dynamic routing we no longer create static urls based on the settings permalink on runtime - adapt url utility - adapt tests
2018-06-05 20:02:20 +03:00
});
it('serve channel 5', function () {
const req = {
secure: true,
method: 'GET',
url: '/channel5/',
host: 'example.com'
};
✨Dynamic Routing Beta (#9596) refs #9601 ### Dynamic Routing This is the beta version of dynamic routing. - we had a initial implementation of "channels" available in the codebase - we have removed and moved this implementation - there is now a centralised place for dynamic routing - server/services/routing - each routing component is represented by a router type e.g. collections, routes, static pages, taxonomies, rss, preview of posts - keep as much as possible logic of routing helpers, middlewares and controllers - ensure test coverage - connect all the things together - yaml file + validation - routing + routers - url service - sitemaps - url access - deeper implementation of yaml validations - e.g. hard require slashes - ensure routing hierarchy/order - e.g. you enable the subscriber app - you have a custom static page, which lives under the same slug /subscribe - static pages are stronger than apps - e.g. the first collection owns the post it has filtered - a post cannot live in two collections - ensure apps are still working and hook into the routers layer (or better said: and register in the routing service) - put as much as possible comments to the code base for better understanding - ensure a clean debug log - ensure we can unmount routes - e.g. you have a collection permalink of /:slug/ represented by {globals.permalink} - and you change the permalink in the admin to dated permalink - the express route get's refreshed from /:slug/ to /:year/:month/:day/:slug/ - unmount without server restart, yey - ensure we are backwards compatible - e.g. render home.hbs for collection index if collection route is / - ensure you can access your configured permalink from the settings table with {globals.permalink} ### Render 503 if url service did not finish - return 503 if the url service has not finished generating the resource urls ### Rewrite sitemaps - we have rewritten the sitemaps "service", because the url generator does no longer happen on runtime - we generate all urls on bootstrap - the sitemaps service will consume created resource and router urls - these urls will be shown on the xml pages - we listen on url events - we listen on router events - we no longer have to fetch the resources, which is nice - the urlservice pre-fetches resources and emits their urls - the urlservice is the only component who knows which urls are valid - i made some ES6 adaptions - we keep the caching logic -> only regenerate xml if there is a change - updated tests - checked test coverage (100%) ### Re-work usage of Url utility - replace all usages of `urlService.utils.urlFor` by `urlService.getByResourceId` - only for resources e.g. post, author, tag - this is important, because with dynamic routing we no longer create static urls based on the settings permalink on runtime - adapt url utility - adapt tests
2018-06-05 20:02:20 +03:00
return testUtils.mocks.express.invoke(app, req)
.then(function (response) {
const $ = cheerio.load(response.body);
✨Dynamic Routing Beta (#9596) refs #9601 ### Dynamic Routing This is the beta version of dynamic routing. - we had a initial implementation of "channels" available in the codebase - we have removed and moved this implementation - there is now a centralised place for dynamic routing - server/services/routing - each routing component is represented by a router type e.g. collections, routes, static pages, taxonomies, rss, preview of posts - keep as much as possible logic of routing helpers, middlewares and controllers - ensure test coverage - connect all the things together - yaml file + validation - routing + routers - url service - sitemaps - url access - deeper implementation of yaml validations - e.g. hard require slashes - ensure routing hierarchy/order - e.g. you enable the subscriber app - you have a custom static page, which lives under the same slug /subscribe - static pages are stronger than apps - e.g. the first collection owns the post it has filtered - a post cannot live in two collections - ensure apps are still working and hook into the routers layer (or better said: and register in the routing service) - put as much as possible comments to the code base for better understanding - ensure a clean debug log - ensure we can unmount routes - e.g. you have a collection permalink of /:slug/ represented by {globals.permalink} - and you change the permalink in the admin to dated permalink - the express route get's refreshed from /:slug/ to /:year/:month/:day/:slug/ - unmount without server restart, yey - ensure we are backwards compatible - e.g. render home.hbs for collection index if collection route is / - ensure you can access your configured permalink from the settings table with {globals.permalink} ### Render 503 if url service did not finish - return 503 if the url service has not finished generating the resource urls ### Rewrite sitemaps - we have rewritten the sitemaps "service", because the url generator does no longer happen on runtime - we generate all urls on bootstrap - the sitemaps service will consume created resource and router urls - these urls will be shown on the xml pages - we listen on url events - we listen on router events - we no longer have to fetch the resources, which is nice - the urlservice pre-fetches resources and emits their urls - the urlservice is the only component who knows which urls are valid - i made some ES6 adaptions - we keep the caching logic -> only regenerate xml if there is a change - updated tests - checked test coverage (100%) ### Re-work usage of Url utility - replace all usages of `urlService.utils.urlFor` by `urlService.getByResourceId` - only for resources e.g. post, author, tag - this is important, because with dynamic routing we no longer create static urls based on the settings permalink on runtime - adapt url utility - adapt tests
2018-06-05 20:02:20 +03:00
response.statusCode.should.eql(200);
response.template.should.eql('index');
✨Dynamic Routing Beta (#9596) refs #9601 ### Dynamic Routing This is the beta version of dynamic routing. - we had a initial implementation of "channels" available in the codebase - we have removed and moved this implementation - there is now a centralised place for dynamic routing - server/services/routing - each routing component is represented by a router type e.g. collections, routes, static pages, taxonomies, rss, preview of posts - keep as much as possible logic of routing helpers, middlewares and controllers - ensure test coverage - connect all the things together - yaml file + validation - routing + routers - url service - sitemaps - url access - deeper implementation of yaml validations - e.g. hard require slashes - ensure routing hierarchy/order - e.g. you enable the subscriber app - you have a custom static page, which lives under the same slug /subscribe - static pages are stronger than apps - e.g. the first collection owns the post it has filtered - a post cannot live in two collections - ensure apps are still working and hook into the routers layer (or better said: and register in the routing service) - put as much as possible comments to the code base for better understanding - ensure a clean debug log - ensure we can unmount routes - e.g. you have a collection permalink of /:slug/ represented by {globals.permalink} - and you change the permalink in the admin to dated permalink - the express route get's refreshed from /:slug/ to /:year/:month/:day/:slug/ - unmount without server restart, yey - ensure we are backwards compatible - e.g. render home.hbs for collection index if collection route is / - ensure you can access your configured permalink from the settings table with {globals.permalink} ### Render 503 if url service did not finish - return 503 if the url service has not finished generating the resource urls ### Rewrite sitemaps - we have rewritten the sitemaps "service", because the url generator does no longer happen on runtime - we generate all urls on bootstrap - the sitemaps service will consume created resource and router urls - these urls will be shown on the xml pages - we listen on url events - we listen on router events - we no longer have to fetch the resources, which is nice - the urlservice pre-fetches resources and emits their urls - the urlservice is the only component who knows which urls are valid - i made some ES6 adaptions - we keep the caching logic -> only regenerate xml if there is a change - updated tests - checked test coverage (100%) ### Re-work usage of Url utility - replace all usages of `urlService.utils.urlFor` by `urlService.getByResourceId` - only for resources e.g. post, author, tag - this is important, because with dynamic routing we no longer create static urls based on the settings permalink on runtime - adapt url utility - adapt tests
2018-06-05 20:02:20 +03:00
$('.post-card').length.should.equal(4);
});
});
✨Dynamic Routing Beta (#9596) refs #9601 ### Dynamic Routing This is the beta version of dynamic routing. - we had a initial implementation of "channels" available in the codebase - we have removed and moved this implementation - there is now a centralised place for dynamic routing - server/services/routing - each routing component is represented by a router type e.g. collections, routes, static pages, taxonomies, rss, preview of posts - keep as much as possible logic of routing helpers, middlewares and controllers - ensure test coverage - connect all the things together - yaml file + validation - routing + routers - url service - sitemaps - url access - deeper implementation of yaml validations - e.g. hard require slashes - ensure routing hierarchy/order - e.g. you enable the subscriber app - you have a custom static page, which lives under the same slug /subscribe - static pages are stronger than apps - e.g. the first collection owns the post it has filtered - a post cannot live in two collections - ensure apps are still working and hook into the routers layer (or better said: and register in the routing service) - put as much as possible comments to the code base for better understanding - ensure a clean debug log - ensure we can unmount routes - e.g. you have a collection permalink of /:slug/ represented by {globals.permalink} - and you change the permalink in the admin to dated permalink - the express route get's refreshed from /:slug/ to /:year/:month/:day/:slug/ - unmount without server restart, yey - ensure we are backwards compatible - e.g. render home.hbs for collection index if collection route is / - ensure you can access your configured permalink from the settings table with {globals.permalink} ### Render 503 if url service did not finish - return 503 if the url service has not finished generating the resource urls ### Rewrite sitemaps - we have rewritten the sitemaps "service", because the url generator does no longer happen on runtime - we generate all urls on bootstrap - the sitemaps service will consume created resource and router urls - these urls will be shown on the xml pages - we listen on url events - we listen on router events - we no longer have to fetch the resources, which is nice - the urlservice pre-fetches resources and emits their urls - the urlservice is the only component who knows which urls are valid - i made some ES6 adaptions - we keep the caching logic -> only regenerate xml if there is a change - updated tests - checked test coverage (100%) ### Re-work usage of Url utility - replace all usages of `urlService.utils.urlFor` by `urlService.getByResourceId` - only for resources e.g. post, author, tag - this is important, because with dynamic routing we no longer create static urls based on the settings permalink on runtime - adapt url utility - adapt tests
2018-06-05 20:02:20 +03:00
it('serve channel 6', function () {
const req = {
secure: true,
method: 'GET',
url: '/channel6/',
host: 'example.com'
};
✨Dynamic Routing Beta (#9596) refs #9601 ### Dynamic Routing This is the beta version of dynamic routing. - we had a initial implementation of "channels" available in the codebase - we have removed and moved this implementation - there is now a centralised place for dynamic routing - server/services/routing - each routing component is represented by a router type e.g. collections, routes, static pages, taxonomies, rss, preview of posts - keep as much as possible logic of routing helpers, middlewares and controllers - ensure test coverage - connect all the things together - yaml file + validation - routing + routers - url service - sitemaps - url access - deeper implementation of yaml validations - e.g. hard require slashes - ensure routing hierarchy/order - e.g. you enable the subscriber app - you have a custom static page, which lives under the same slug /subscribe - static pages are stronger than apps - e.g. the first collection owns the post it has filtered - a post cannot live in two collections - ensure apps are still working and hook into the routers layer (or better said: and register in the routing service) - put as much as possible comments to the code base for better understanding - ensure a clean debug log - ensure we can unmount routes - e.g. you have a collection permalink of /:slug/ represented by {globals.permalink} - and you change the permalink in the admin to dated permalink - the express route get's refreshed from /:slug/ to /:year/:month/:day/:slug/ - unmount without server restart, yey - ensure we are backwards compatible - e.g. render home.hbs for collection index if collection route is / - ensure you can access your configured permalink from the settings table with {globals.permalink} ### Render 503 if url service did not finish - return 503 if the url service has not finished generating the resource urls ### Rewrite sitemaps - we have rewritten the sitemaps "service", because the url generator does no longer happen on runtime - we generate all urls on bootstrap - the sitemaps service will consume created resource and router urls - these urls will be shown on the xml pages - we listen on url events - we listen on router events - we no longer have to fetch the resources, which is nice - the urlservice pre-fetches resources and emits their urls - the urlservice is the only component who knows which urls are valid - i made some ES6 adaptions - we keep the caching logic -> only regenerate xml if there is a change - updated tests - checked test coverage (100%) ### Re-work usage of Url utility - replace all usages of `urlService.utils.urlFor` by `urlService.getByResourceId` - only for resources e.g. post, author, tag - this is important, because with dynamic routing we no longer create static urls based on the settings permalink on runtime - adapt url utility - adapt tests
2018-06-05 20:02:20 +03:00
return testUtils.mocks.express.invoke(app, req)
.then(function (response) {
const $ = cheerio.load(response.body);
✨Dynamic Routing Beta (#9596) refs #9601 ### Dynamic Routing This is the beta version of dynamic routing. - we had a initial implementation of "channels" available in the codebase - we have removed and moved this implementation - there is now a centralised place for dynamic routing - server/services/routing - each routing component is represented by a router type e.g. collections, routes, static pages, taxonomies, rss, preview of posts - keep as much as possible logic of routing helpers, middlewares and controllers - ensure test coverage - connect all the things together - yaml file + validation - routing + routers - url service - sitemaps - url access - deeper implementation of yaml validations - e.g. hard require slashes - ensure routing hierarchy/order - e.g. you enable the subscriber app - you have a custom static page, which lives under the same slug /subscribe - static pages are stronger than apps - e.g. the first collection owns the post it has filtered - a post cannot live in two collections - ensure apps are still working and hook into the routers layer (or better said: and register in the routing service) - put as much as possible comments to the code base for better understanding - ensure a clean debug log - ensure we can unmount routes - e.g. you have a collection permalink of /:slug/ represented by {globals.permalink} - and you change the permalink in the admin to dated permalink - the express route get's refreshed from /:slug/ to /:year/:month/:day/:slug/ - unmount without server restart, yey - ensure we are backwards compatible - e.g. render home.hbs for collection index if collection route is / - ensure you can access your configured permalink from the settings table with {globals.permalink} ### Render 503 if url service did not finish - return 503 if the url service has not finished generating the resource urls ### Rewrite sitemaps - we have rewritten the sitemaps "service", because the url generator does no longer happen on runtime - we generate all urls on bootstrap - the sitemaps service will consume created resource and router urls - these urls will be shown on the xml pages - we listen on url events - we listen on router events - we no longer have to fetch the resources, which is nice - the urlservice pre-fetches resources and emits their urls - the urlservice is the only component who knows which urls are valid - i made some ES6 adaptions - we keep the caching logic -> only regenerate xml if there is a change - updated tests - checked test coverage (100%) ### Re-work usage of Url utility - replace all usages of `urlService.utils.urlFor` by `urlService.getByResourceId` - only for resources e.g. post, author, tag - this is important, because with dynamic routing we no longer create static urls based on the settings permalink on runtime - adapt url utility - adapt tests
2018-06-05 20:02:20 +03:00
response.statusCode.should.eql(200);
response.template.should.eql('index');
✨Dynamic Routing Beta (#9596) refs #9601 ### Dynamic Routing This is the beta version of dynamic routing. - we had a initial implementation of "channels" available in the codebase - we have removed and moved this implementation - there is now a centralised place for dynamic routing - server/services/routing - each routing component is represented by a router type e.g. collections, routes, static pages, taxonomies, rss, preview of posts - keep as much as possible logic of routing helpers, middlewares and controllers - ensure test coverage - connect all the things together - yaml file + validation - routing + routers - url service - sitemaps - url access - deeper implementation of yaml validations - e.g. hard require slashes - ensure routing hierarchy/order - e.g. you enable the subscriber app - you have a custom static page, which lives under the same slug /subscribe - static pages are stronger than apps - e.g. the first collection owns the post it has filtered - a post cannot live in two collections - ensure apps are still working and hook into the routers layer (or better said: and register in the routing service) - put as much as possible comments to the code base for better understanding - ensure a clean debug log - ensure we can unmount routes - e.g. you have a collection permalink of /:slug/ represented by {globals.permalink} - and you change the permalink in the admin to dated permalink - the express route get's refreshed from /:slug/ to /:year/:month/:day/:slug/ - unmount without server restart, yey - ensure we are backwards compatible - e.g. render home.hbs for collection index if collection route is / - ensure you can access your configured permalink from the settings table with {globals.permalink} ### Render 503 if url service did not finish - return 503 if the url service has not finished generating the resource urls ### Rewrite sitemaps - we have rewritten the sitemaps "service", because the url generator does no longer happen on runtime - we generate all urls on bootstrap - the sitemaps service will consume created resource and router urls - these urls will be shown on the xml pages - we listen on url events - we listen on router events - we no longer have to fetch the resources, which is nice - the urlservice pre-fetches resources and emits their urls - the urlservice is the only component who knows which urls are valid - i made some ES6 adaptions - we keep the caching logic -> only regenerate xml if there is a change - updated tests - checked test coverage (100%) ### Re-work usage of Url utility - replace all usages of `urlService.utils.urlFor` by `urlService.getByResourceId` - only for resources e.g. post, author, tag - this is important, because with dynamic routing we no longer create static urls based on the settings permalink on runtime - adapt url utility - adapt tests
2018-06-05 20:02:20 +03:00
$('.post-card').length.should.equal(4);
});
✨Dynamic Routing Beta (#9596) refs #9601 ### Dynamic Routing This is the beta version of dynamic routing. - we had a initial implementation of "channels" available in the codebase - we have removed and moved this implementation - there is now a centralised place for dynamic routing - server/services/routing - each routing component is represented by a router type e.g. collections, routes, static pages, taxonomies, rss, preview of posts - keep as much as possible logic of routing helpers, middlewares and controllers - ensure test coverage - connect all the things together - yaml file + validation - routing + routers - url service - sitemaps - url access - deeper implementation of yaml validations - e.g. hard require slashes - ensure routing hierarchy/order - e.g. you enable the subscriber app - you have a custom static page, which lives under the same slug /subscribe - static pages are stronger than apps - e.g. the first collection owns the post it has filtered - a post cannot live in two collections - ensure apps are still working and hook into the routers layer (or better said: and register in the routing service) - put as much as possible comments to the code base for better understanding - ensure a clean debug log - ensure we can unmount routes - e.g. you have a collection permalink of /:slug/ represented by {globals.permalink} - and you change the permalink in the admin to dated permalink - the express route get's refreshed from /:slug/ to /:year/:month/:day/:slug/ - unmount without server restart, yey - ensure we are backwards compatible - e.g. render home.hbs for collection index if collection route is / - ensure you can access your configured permalink from the settings table with {globals.permalink} ### Render 503 if url service did not finish - return 503 if the url service has not finished generating the resource urls ### Rewrite sitemaps - we have rewritten the sitemaps "service", because the url generator does no longer happen on runtime - we generate all urls on bootstrap - the sitemaps service will consume created resource and router urls - these urls will be shown on the xml pages - we listen on url events - we listen on router events - we no longer have to fetch the resources, which is nice - the urlservice pre-fetches resources and emits their urls - the urlservice is the only component who knows which urls are valid - i made some ES6 adaptions - we keep the caching logic -> only regenerate xml if there is a change - updated tests - checked test coverage (100%) ### Re-work usage of Url utility - replace all usages of `urlService.utils.urlFor` by `urlService.getByResourceId` - only for resources e.g. post, author, tag - this is important, because with dynamic routing we no longer create static urls based on the settings permalink on runtime - adapt url utility - adapt tests
2018-06-05 20:02:20 +03:00
});
it('serve kitching-sink: redirect', function () {
const req = {
secure: true,
method: 'GET',
url: '/tag/kitchen-sink/',
host: 'example.com'
};
✨Dynamic Routing Beta (#9596) refs #9601 ### Dynamic Routing This is the beta version of dynamic routing. - we had a initial implementation of "channels" available in the codebase - we have removed and moved this implementation - there is now a centralised place for dynamic routing - server/services/routing - each routing component is represented by a router type e.g. collections, routes, static pages, taxonomies, rss, preview of posts - keep as much as possible logic of routing helpers, middlewares and controllers - ensure test coverage - connect all the things together - yaml file + validation - routing + routers - url service - sitemaps - url access - deeper implementation of yaml validations - e.g. hard require slashes - ensure routing hierarchy/order - e.g. you enable the subscriber app - you have a custom static page, which lives under the same slug /subscribe - static pages are stronger than apps - e.g. the first collection owns the post it has filtered - a post cannot live in two collections - ensure apps are still working and hook into the routers layer (or better said: and register in the routing service) - put as much as possible comments to the code base for better understanding - ensure a clean debug log - ensure we can unmount routes - e.g. you have a collection permalink of /:slug/ represented by {globals.permalink} - and you change the permalink in the admin to dated permalink - the express route get's refreshed from /:slug/ to /:year/:month/:day/:slug/ - unmount without server restart, yey - ensure we are backwards compatible - e.g. render home.hbs for collection index if collection route is / - ensure you can access your configured permalink from the settings table with {globals.permalink} ### Render 503 if url service did not finish - return 503 if the url service has not finished generating the resource urls ### Rewrite sitemaps - we have rewritten the sitemaps "service", because the url generator does no longer happen on runtime - we generate all urls on bootstrap - the sitemaps service will consume created resource and router urls - these urls will be shown on the xml pages - we listen on url events - we listen on router events - we no longer have to fetch the resources, which is nice - the urlservice pre-fetches resources and emits their urls - the urlservice is the only component who knows which urls are valid - i made some ES6 adaptions - we keep the caching logic -> only regenerate xml if there is a change - updated tests - checked test coverage (100%) ### Re-work usage of Url utility - replace all usages of `urlService.utils.urlFor` by `urlService.getByResourceId` - only for resources e.g. post, author, tag - this is important, because with dynamic routing we no longer create static urls based on the settings permalink on runtime - adapt url utility - adapt tests
2018-06-05 20:02:20 +03:00
return testUtils.mocks.express.invoke(app, req)
.then(function (response) {
response.statusCode.should.eql(301);
response.headers.location.should.eql('/channel1/');
});
});
✨Dynamic Routing Beta (#9596) refs #9601 ### Dynamic Routing This is the beta version of dynamic routing. - we had a initial implementation of "channels" available in the codebase - we have removed and moved this implementation - there is now a centralised place for dynamic routing - server/services/routing - each routing component is represented by a router type e.g. collections, routes, static pages, taxonomies, rss, preview of posts - keep as much as possible logic of routing helpers, middlewares and controllers - ensure test coverage - connect all the things together - yaml file + validation - routing + routers - url service - sitemaps - url access - deeper implementation of yaml validations - e.g. hard require slashes - ensure routing hierarchy/order - e.g. you enable the subscriber app - you have a custom static page, which lives under the same slug /subscribe - static pages are stronger than apps - e.g. the first collection owns the post it has filtered - a post cannot live in two collections - ensure apps are still working and hook into the routers layer (or better said: and register in the routing service) - put as much as possible comments to the code base for better understanding - ensure a clean debug log - ensure we can unmount routes - e.g. you have a collection permalink of /:slug/ represented by {globals.permalink} - and you change the permalink in the admin to dated permalink - the express route get's refreshed from /:slug/ to /:year/:month/:day/:slug/ - unmount without server restart, yey - ensure we are backwards compatible - e.g. render home.hbs for collection index if collection route is / - ensure you can access your configured permalink from the settings table with {globals.permalink} ### Render 503 if url service did not finish - return 503 if the url service has not finished generating the resource urls ### Rewrite sitemaps - we have rewritten the sitemaps "service", because the url generator does no longer happen on runtime - we generate all urls on bootstrap - the sitemaps service will consume created resource and router urls - these urls will be shown on the xml pages - we listen on url events - we listen on router events - we no longer have to fetch the resources, which is nice - the urlservice pre-fetches resources and emits their urls - the urlservice is the only component who knows which urls are valid - i made some ES6 adaptions - we keep the caching logic -> only regenerate xml if there is a change - updated tests - checked test coverage (100%) ### Re-work usage of Url utility - replace all usages of `urlService.utils.urlFor` by `urlService.getByResourceId` - only for resources e.g. post, author, tag - this is important, because with dynamic routing we no longer create static urls based on the settings permalink on runtime - adapt url utility - adapt tests
2018-06-05 20:02:20 +03:00
it('serve html-ipsum: redirect', function () {
const req = {
secure: true,
method: 'GET',
url: '/html-ipsum/',
host: 'example.com'
};
✨Dynamic Routing Beta (#9596) refs #9601 ### Dynamic Routing This is the beta version of dynamic routing. - we had a initial implementation of "channels" available in the codebase - we have removed and moved this implementation - there is now a centralised place for dynamic routing - server/services/routing - each routing component is represented by a router type e.g. collections, routes, static pages, taxonomies, rss, preview of posts - keep as much as possible logic of routing helpers, middlewares and controllers - ensure test coverage - connect all the things together - yaml file + validation - routing + routers - url service - sitemaps - url access - deeper implementation of yaml validations - e.g. hard require slashes - ensure routing hierarchy/order - e.g. you enable the subscriber app - you have a custom static page, which lives under the same slug /subscribe - static pages are stronger than apps - e.g. the first collection owns the post it has filtered - a post cannot live in two collections - ensure apps are still working and hook into the routers layer (or better said: and register in the routing service) - put as much as possible comments to the code base for better understanding - ensure a clean debug log - ensure we can unmount routes - e.g. you have a collection permalink of /:slug/ represented by {globals.permalink} - and you change the permalink in the admin to dated permalink - the express route get's refreshed from /:slug/ to /:year/:month/:day/:slug/ - unmount without server restart, yey - ensure we are backwards compatible - e.g. render home.hbs for collection index if collection route is / - ensure you can access your configured permalink from the settings table with {globals.permalink} ### Render 503 if url service did not finish - return 503 if the url service has not finished generating the resource urls ### Rewrite sitemaps - we have rewritten the sitemaps "service", because the url generator does no longer happen on runtime - we generate all urls on bootstrap - the sitemaps service will consume created resource and router urls - these urls will be shown on the xml pages - we listen on url events - we listen on router events - we no longer have to fetch the resources, which is nice - the urlservice pre-fetches resources and emits their urls - the urlservice is the only component who knows which urls are valid - i made some ES6 adaptions - we keep the caching logic -> only regenerate xml if there is a change - updated tests - checked test coverage (100%) ### Re-work usage of Url utility - replace all usages of `urlService.utils.urlFor` by `urlService.getByResourceId` - only for resources e.g. post, author, tag - this is important, because with dynamic routing we no longer create static urls based on the settings permalink on runtime - adapt url utility - adapt tests
2018-06-05 20:02:20 +03:00
return testUtils.mocks.express.invoke(app, req)
.then(function (response) {
response.statusCode.should.eql(301);
response.headers.location.should.eql('/channel6/');
});
});
✨Dynamic Routing Beta (#9596) refs #9601 ### Dynamic Routing This is the beta version of dynamic routing. - we had a initial implementation of "channels" available in the codebase - we have removed and moved this implementation - there is now a centralised place for dynamic routing - server/services/routing - each routing component is represented by a router type e.g. collections, routes, static pages, taxonomies, rss, preview of posts - keep as much as possible logic of routing helpers, middlewares and controllers - ensure test coverage - connect all the things together - yaml file + validation - routing + routers - url service - sitemaps - url access - deeper implementation of yaml validations - e.g. hard require slashes - ensure routing hierarchy/order - e.g. you enable the subscriber app - you have a custom static page, which lives under the same slug /subscribe - static pages are stronger than apps - e.g. the first collection owns the post it has filtered - a post cannot live in two collections - ensure apps are still working and hook into the routers layer (or better said: and register in the routing service) - put as much as possible comments to the code base for better understanding - ensure a clean debug log - ensure we can unmount routes - e.g. you have a collection permalink of /:slug/ represented by {globals.permalink} - and you change the permalink in the admin to dated permalink - the express route get's refreshed from /:slug/ to /:year/:month/:day/:slug/ - unmount without server restart, yey - ensure we are backwards compatible - e.g. render home.hbs for collection index if collection route is / - ensure you can access your configured permalink from the settings table with {globals.permalink} ### Render 503 if url service did not finish - return 503 if the url service has not finished generating the resource urls ### Rewrite sitemaps - we have rewritten the sitemaps "service", because the url generator does no longer happen on runtime - we generate all urls on bootstrap - the sitemaps service will consume created resource and router urls - these urls will be shown on the xml pages - we listen on url events - we listen on router events - we no longer have to fetch the resources, which is nice - the urlservice pre-fetches resources and emits their urls - the urlservice is the only component who knows which urls are valid - i made some ES6 adaptions - we keep the caching logic -> only regenerate xml if there is a change - updated tests - checked test coverage (100%) ### Re-work usage of Url utility - replace all usages of `urlService.utils.urlFor` by `urlService.getByResourceId` - only for resources e.g. post, author, tag - this is important, because with dynamic routing we no longer create static urls based on the settings permalink on runtime - adapt url utility - adapt tests
2018-06-05 20:02:20 +03:00
it('serve chorizo: no redirect', function () {
const req = {
secure: true,
method: 'GET',
url: '/tag/chorizo/',
host: 'example.com'
};
✨Dynamic Routing Beta (#9596) refs #9601 ### Dynamic Routing This is the beta version of dynamic routing. - we had a initial implementation of "channels" available in the codebase - we have removed and moved this implementation - there is now a centralised place for dynamic routing - server/services/routing - each routing component is represented by a router type e.g. collections, routes, static pages, taxonomies, rss, preview of posts - keep as much as possible logic of routing helpers, middlewares and controllers - ensure test coverage - connect all the things together - yaml file + validation - routing + routers - url service - sitemaps - url access - deeper implementation of yaml validations - e.g. hard require slashes - ensure routing hierarchy/order - e.g. you enable the subscriber app - you have a custom static page, which lives under the same slug /subscribe - static pages are stronger than apps - e.g. the first collection owns the post it has filtered - a post cannot live in two collections - ensure apps are still working and hook into the routers layer (or better said: and register in the routing service) - put as much as possible comments to the code base for better understanding - ensure a clean debug log - ensure we can unmount routes - e.g. you have a collection permalink of /:slug/ represented by {globals.permalink} - and you change the permalink in the admin to dated permalink - the express route get's refreshed from /:slug/ to /:year/:month/:day/:slug/ - unmount without server restart, yey - ensure we are backwards compatible - e.g. render home.hbs for collection index if collection route is / - ensure you can access your configured permalink from the settings table with {globals.permalink} ### Render 503 if url service did not finish - return 503 if the url service has not finished generating the resource urls ### Rewrite sitemaps - we have rewritten the sitemaps "service", because the url generator does no longer happen on runtime - we generate all urls on bootstrap - the sitemaps service will consume created resource and router urls - these urls will be shown on the xml pages - we listen on url events - we listen on router events - we no longer have to fetch the resources, which is nice - the urlservice pre-fetches resources and emits their urls - the urlservice is the only component who knows which urls are valid - i made some ES6 adaptions - we keep the caching logic -> only regenerate xml if there is a change - updated tests - checked test coverage (100%) ### Re-work usage of Url utility - replace all usages of `urlService.utils.urlFor` by `urlService.getByResourceId` - only for resources e.g. post, author, tag - this is important, because with dynamic routing we no longer create static urls based on the settings permalink on runtime - adapt url utility - adapt tests
2018-06-05 20:02:20 +03:00
return testUtils.mocks.express.invoke(app, req)
.then(function (response) {
response.statusCode.should.eql(200);
});
});
✨Dynamic Routing Beta (#9596) refs #9601 ### Dynamic Routing This is the beta version of dynamic routing. - we had a initial implementation of "channels" available in the codebase - we have removed and moved this implementation - there is now a centralised place for dynamic routing - server/services/routing - each routing component is represented by a router type e.g. collections, routes, static pages, taxonomies, rss, preview of posts - keep as much as possible logic of routing helpers, middlewares and controllers - ensure test coverage - connect all the things together - yaml file + validation - routing + routers - url service - sitemaps - url access - deeper implementation of yaml validations - e.g. hard require slashes - ensure routing hierarchy/order - e.g. you enable the subscriber app - you have a custom static page, which lives under the same slug /subscribe - static pages are stronger than apps - e.g. the first collection owns the post it has filtered - a post cannot live in two collections - ensure apps are still working and hook into the routers layer (or better said: and register in the routing service) - put as much as possible comments to the code base for better understanding - ensure a clean debug log - ensure we can unmount routes - e.g. you have a collection permalink of /:slug/ represented by {globals.permalink} - and you change the permalink in the admin to dated permalink - the express route get's refreshed from /:slug/ to /:year/:month/:day/:slug/ - unmount without server restart, yey - ensure we are backwards compatible - e.g. render home.hbs for collection index if collection route is / - ensure you can access your configured permalink from the settings table with {globals.permalink} ### Render 503 if url service did not finish - return 503 if the url service has not finished generating the resource urls ### Rewrite sitemaps - we have rewritten the sitemaps "service", because the url generator does no longer happen on runtime - we generate all urls on bootstrap - the sitemaps service will consume created resource and router urls - these urls will be shown on the xml pages - we listen on url events - we listen on router events - we no longer have to fetch the resources, which is nice - the urlservice pre-fetches resources and emits their urls - the urlservice is the only component who knows which urls are valid - i made some ES6 adaptions - we keep the caching logic -> only regenerate xml if there is a change - updated tests - checked test coverage (100%) ### Re-work usage of Url utility - replace all usages of `urlService.utils.urlFor` by `urlService.getByResourceId` - only for resources e.g. post, author, tag - this is important, because with dynamic routing we no longer create static urls based on the settings permalink on runtime - adapt url utility - adapt tests
2018-06-05 20:02:20 +03:00
it('serve joe-bloggs', function () {
const req = {
secure: true,
method: 'GET',
url: '/author/joe-bloggs/',
host: 'example.com'
};
✨Dynamic Routing Beta (#9596) refs #9601 ### Dynamic Routing This is the beta version of dynamic routing. - we had a initial implementation of "channels" available in the codebase - we have removed and moved this implementation - there is now a centralised place for dynamic routing - server/services/routing - each routing component is represented by a router type e.g. collections, routes, static pages, taxonomies, rss, preview of posts - keep as much as possible logic of routing helpers, middlewares and controllers - ensure test coverage - connect all the things together - yaml file + validation - routing + routers - url service - sitemaps - url access - deeper implementation of yaml validations - e.g. hard require slashes - ensure routing hierarchy/order - e.g. you enable the subscriber app - you have a custom static page, which lives under the same slug /subscribe - static pages are stronger than apps - e.g. the first collection owns the post it has filtered - a post cannot live in two collections - ensure apps are still working and hook into the routers layer (or better said: and register in the routing service) - put as much as possible comments to the code base for better understanding - ensure a clean debug log - ensure we can unmount routes - e.g. you have a collection permalink of /:slug/ represented by {globals.permalink} - and you change the permalink in the admin to dated permalink - the express route get's refreshed from /:slug/ to /:year/:month/:day/:slug/ - unmount without server restart, yey - ensure we are backwards compatible - e.g. render home.hbs for collection index if collection route is / - ensure you can access your configured permalink from the settings table with {globals.permalink} ### Render 503 if url service did not finish - return 503 if the url service has not finished generating the resource urls ### Rewrite sitemaps - we have rewritten the sitemaps "service", because the url generator does no longer happen on runtime - we generate all urls on bootstrap - the sitemaps service will consume created resource and router urls - these urls will be shown on the xml pages - we listen on url events - we listen on router events - we no longer have to fetch the resources, which is nice - the urlservice pre-fetches resources and emits their urls - the urlservice is the only component who knows which urls are valid - i made some ES6 adaptions - we keep the caching logic -> only regenerate xml if there is a change - updated tests - checked test coverage (100%) ### Re-work usage of Url utility - replace all usages of `urlService.utils.urlFor` by `urlService.getByResourceId` - only for resources e.g. post, author, tag - this is important, because with dynamic routing we no longer create static urls based on the settings permalink on runtime - adapt url utility - adapt tests
2018-06-05 20:02:20 +03:00
return testUtils.mocks.express.invoke(app, req)
.then(function (response) {
response.statusCode.should.eql(200);
});
});
✨Dynamic Routing Beta (#9596) refs #9601 ### Dynamic Routing This is the beta version of dynamic routing. - we had a initial implementation of "channels" available in the codebase - we have removed and moved this implementation - there is now a centralised place for dynamic routing - server/services/routing - each routing component is represented by a router type e.g. collections, routes, static pages, taxonomies, rss, preview of posts - keep as much as possible logic of routing helpers, middlewares and controllers - ensure test coverage - connect all the things together - yaml file + validation - routing + routers - url service - sitemaps - url access - deeper implementation of yaml validations - e.g. hard require slashes - ensure routing hierarchy/order - e.g. you enable the subscriber app - you have a custom static page, which lives under the same slug /subscribe - static pages are stronger than apps - e.g. the first collection owns the post it has filtered - a post cannot live in two collections - ensure apps are still working and hook into the routers layer (or better said: and register in the routing service) - put as much as possible comments to the code base for better understanding - ensure a clean debug log - ensure we can unmount routes - e.g. you have a collection permalink of /:slug/ represented by {globals.permalink} - and you change the permalink in the admin to dated permalink - the express route get's refreshed from /:slug/ to /:year/:month/:day/:slug/ - unmount without server restart, yey - ensure we are backwards compatible - e.g. render home.hbs for collection index if collection route is / - ensure you can access your configured permalink from the settings table with {globals.permalink} ### Render 503 if url service did not finish - return 503 if the url service has not finished generating the resource urls ### Rewrite sitemaps - we have rewritten the sitemaps "service", because the url generator does no longer happen on runtime - we generate all urls on bootstrap - the sitemaps service will consume created resource and router urls - these urls will be shown on the xml pages - we listen on url events - we listen on router events - we no longer have to fetch the resources, which is nice - the urlservice pre-fetches resources and emits their urls - the urlservice is the only component who knows which urls are valid - i made some ES6 adaptions - we keep the caching logic -> only regenerate xml if there is a change - updated tests - checked test coverage (100%) ### Re-work usage of Url utility - replace all usages of `urlService.utils.urlFor` by `urlService.getByResourceId` - only for resources e.g. post, author, tag - this is important, because with dynamic routing we no longer create static urls based on the settings permalink on runtime - adapt url utility - adapt tests
2018-06-05 20:02:20 +03:00
});
});
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: {}
✨Dynamic Routing Beta (#9596) refs #9601 ### Dynamic Routing This is the beta version of dynamic routing. - we had a initial implementation of "channels" available in the codebase - we have removed and moved this implementation - there is now a centralised place for dynamic routing - server/services/routing - each routing component is represented by a router type e.g. collections, routes, static pages, taxonomies, rss, preview of posts - keep as much as possible logic of routing helpers, middlewares and controllers - ensure test coverage - connect all the things together - yaml file + validation - routing + routers - url service - sitemaps - url access - deeper implementation of yaml validations - e.g. hard require slashes - ensure routing hierarchy/order - e.g. you enable the subscriber app - you have a custom static page, which lives under the same slug /subscribe - static pages are stronger than apps - e.g. the first collection owns the post it has filtered - a post cannot live in two collections - ensure apps are still working and hook into the routers layer (or better said: and register in the routing service) - put as much as possible comments to the code base for better understanding - ensure a clean debug log - ensure we can unmount routes - e.g. you have a collection permalink of /:slug/ represented by {globals.permalink} - and you change the permalink in the admin to dated permalink - the express route get's refreshed from /:slug/ to /:year/:month/:day/:slug/ - unmount without server restart, yey - ensure we are backwards compatible - e.g. render home.hbs for collection index if collection route is / - ensure you can access your configured permalink from the settings table with {globals.permalink} ### Render 503 if url service did not finish - return 503 if the url service has not finished generating the resource urls ### Rewrite sitemaps - we have rewritten the sitemaps "service", because the url generator does no longer happen on runtime - we generate all urls on bootstrap - the sitemaps service will consume created resource and router urls - these urls will be shown on the xml pages - we listen on url events - we listen on router events - we no longer have to fetch the resources, which is nice - the urlservice pre-fetches resources and emits their urls - the urlservice is the only component who knows which urls are valid - i made some ES6 adaptions - we keep the caching logic -> only regenerate xml if there is a change - updated tests - checked test coverage (100%) ### Re-work usage of Url utility - replace all usages of `urlService.utils.urlFor` by `urlService.getByResourceId` - only for resources e.g. post, author, tag - this is important, because with dynamic routing we no longer create static urls based on the settings permalink on runtime - adapt url utility - adapt tests
2018-06-05 20:02:20 +03:00
});
testUtils.integrationTesting.urlService.resetGenerators();
testUtils.integrationTesting.defaultMocks(sinon, {theme: 'test-theme'});
✨Dynamic Routing Beta (#9596) refs #9601 ### Dynamic Routing This is the beta version of dynamic routing. - we had a initial implementation of "channels" available in the codebase - we have removed and moved this implementation - there is now a centralised place for dynamic routing - server/services/routing - each routing component is represented by a router type e.g. collections, routes, static pages, taxonomies, rss, preview of posts - keep as much as possible logic of routing helpers, middlewares and controllers - ensure test coverage - connect all the things together - yaml file + validation - routing + routers - url service - sitemaps - url access - deeper implementation of yaml validations - e.g. hard require slashes - ensure routing hierarchy/order - e.g. you enable the subscriber app - you have a custom static page, which lives under the same slug /subscribe - static pages are stronger than apps - e.g. the first collection owns the post it has filtered - a post cannot live in two collections - ensure apps are still working and hook into the routers layer (or better said: and register in the routing service) - put as much as possible comments to the code base for better understanding - ensure a clean debug log - ensure we can unmount routes - e.g. you have a collection permalink of /:slug/ represented by {globals.permalink} - and you change the permalink in the admin to dated permalink - the express route get's refreshed from /:slug/ to /:year/:month/:day/:slug/ - unmount without server restart, yey - ensure we are backwards compatible - e.g. render home.hbs for collection index if collection route is / - ensure you can access your configured permalink from the settings table with {globals.permalink} ### Render 503 if url service did not finish - return 503 if the url service has not finished generating the resource urls ### Rewrite sitemaps - we have rewritten the sitemaps "service", because the url generator does no longer happen on runtime - we generate all urls on bootstrap - the sitemaps service will consume created resource and router urls - these urls will be shown on the xml pages - we listen on url events - we listen on router events - we no longer have to fetch the resources, which is nice - the urlservice pre-fetches resources and emits their urls - the urlservice is the only component who knows which urls are valid - i made some ES6 adaptions - we keep the caching logic -> only regenerate xml if there is a change - updated tests - checked test coverage (100%) ### Re-work usage of Url utility - replace all usages of `urlService.utils.urlFor` by `urlService.getByResourceId` - only for resources e.g. post, author, tag - this is important, because with dynamic routing we no longer create static urls based on the settings permalink on runtime - adapt url utility - adapt tests
2018-06-05 20:02:20 +03:00
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.urlService.waitTillFinished();
});
});
✨Dynamic Routing Beta (#9596) refs #9601 ### Dynamic Routing This is the beta version of dynamic routing. - we had a initial implementation of "channels" available in the codebase - we have removed and moved this implementation - there is now a centralised place for dynamic routing - server/services/routing - each routing component is represented by a router type e.g. collections, routes, static pages, taxonomies, rss, preview of posts - keep as much as possible logic of routing helpers, middlewares and controllers - ensure test coverage - connect all the things together - yaml file + validation - routing + routers - url service - sitemaps - url access - deeper implementation of yaml validations - e.g. hard require slashes - ensure routing hierarchy/order - e.g. you enable the subscriber app - you have a custom static page, which lives under the same slug /subscribe - static pages are stronger than apps - e.g. the first collection owns the post it has filtered - a post cannot live in two collections - ensure apps are still working and hook into the routers layer (or better said: and register in the routing service) - put as much as possible comments to the code base for better understanding - ensure a clean debug log - ensure we can unmount routes - e.g. you have a collection permalink of /:slug/ represented by {globals.permalink} - and you change the permalink in the admin to dated permalink - the express route get's refreshed from /:slug/ to /:year/:month/:day/:slug/ - unmount without server restart, yey - ensure we are backwards compatible - e.g. render home.hbs for collection index if collection route is / - ensure you can access your configured permalink from the settings table with {globals.permalink} ### Render 503 if url service did not finish - return 503 if the url service has not finished generating the resource urls ### Rewrite sitemaps - we have rewritten the sitemaps "service", because the url generator does no longer happen on runtime - we generate all urls on bootstrap - the sitemaps service will consume created resource and router urls - these urls will be shown on the xml pages - we listen on url events - we listen on router events - we no longer have to fetch the resources, which is nice - the urlservice pre-fetches resources and emits their urls - the urlservice is the only component who knows which urls are valid - i made some ES6 adaptions - we keep the caching logic -> only regenerate xml if there is a change - updated tests - checked test coverage (100%) ### Re-work usage of Url utility - replace all usages of `urlService.utils.urlFor` by `urlService.getByResourceId` - only for resources e.g. post, author, tag - this is important, because with dynamic routing we no longer create static urls based on the settings permalink on runtime - adapt url utility - adapt tests
2018-06-05 20:02:20 +03:00
beforeEach(function () {
testUtils.integrationTesting.overrideGhostConfig(configUtils);
});
afterEach(function () {
configUtils.restore();
urlUtils.restore();
});
after(function () {
sinon.restore();
});
it('serve /rss/', function () {
✨Dynamic Routing Beta (#9596) refs #9601 ### Dynamic Routing This is the beta version of dynamic routing. - we had a initial implementation of "channels" available in the codebase - we have removed and moved this implementation - there is now a centralised place for dynamic routing - server/services/routing - each routing component is represented by a router type e.g. collections, routes, static pages, taxonomies, rss, preview of posts - keep as much as possible logic of routing helpers, middlewares and controllers - ensure test coverage - connect all the things together - yaml file + validation - routing + routers - url service - sitemaps - url access - deeper implementation of yaml validations - e.g. hard require slashes - ensure routing hierarchy/order - e.g. you enable the subscriber app - you have a custom static page, which lives under the same slug /subscribe - static pages are stronger than apps - e.g. the first collection owns the post it has filtered - a post cannot live in two collections - ensure apps are still working and hook into the routers layer (or better said: and register in the routing service) - put as much as possible comments to the code base for better understanding - ensure a clean debug log - ensure we can unmount routes - e.g. you have a collection permalink of /:slug/ represented by {globals.permalink} - and you change the permalink in the admin to dated permalink - the express route get's refreshed from /:slug/ to /:year/:month/:day/:slug/ - unmount without server restart, yey - ensure we are backwards compatible - e.g. render home.hbs for collection index if collection route is / - ensure you can access your configured permalink from the settings table with {globals.permalink} ### Render 503 if url service did not finish - return 503 if the url service has not finished generating the resource urls ### Rewrite sitemaps - we have rewritten the sitemaps "service", because the url generator does no longer happen on runtime - we generate all urls on bootstrap - the sitemaps service will consume created resource and router urls - these urls will be shown on the xml pages - we listen on url events - we listen on router events - we no longer have to fetch the resources, which is nice - the urlservice pre-fetches resources and emits their urls - the urlservice is the only component who knows which urls are valid - i made some ES6 adaptions - we keep the caching logic -> only regenerate xml if there is a change - updated tests - checked test coverage (100%) ### Re-work usage of Url utility - replace all usages of `urlService.utils.urlFor` by `urlService.getByResourceId` - only for resources e.g. post, author, tag - this is important, because with dynamic routing we no longer create static urls based on the settings permalink on runtime - adapt url utility - adapt tests
2018-06-05 20:02:20 +03:00
const req = {
secure: true,
method: 'GET',
url: '/rss/',
host: 'example.com'
};
return testUtils.mocks.express.invoke(app, req)
.then(function (response) {
response.statusCode.should.eql(200);
});
});
it('serve /music/rss/', function () {
const req = {
secure: true,
method: 'GET',
url: '/music/rss/',
host: 'example.com'
};
return testUtils.mocks.express.invoke(app, req)
.then(function (response) {
response.statusCode.should.eql(404);
});
});
it('serve /cooking/rss/', function () {
const req = {
secure: true,
method: 'GET',
url: '/cooking/rss/',
host: 'example.com'
};
return testUtils.mocks.express.invoke(app, req)
.then(function (response) {
response.statusCode.should.eql(404);
});
});
it('serve /flat/rss/', function () {
const req = {
secure: true,
method: 'GET',
url: '/flat/rss/',
host: 'example.com'
};
return testUtils.mocks.express.invoke(app, req)
.then(function (response) {
response.statusCode.should.eql(200);
});
});
it('serve /podcast/rss/', function () {
const req = {
secure: true,
method: 'GET',
url: '/podcast/rss/',
host: 'example.com'
};
return testUtils.mocks.express.invoke(app, req)
.then(function (response) {
response.statusCode.should.eql(200);
response.template.should.eql('podcast/rss');
response.headers['content-type'].should.eql('text/xml; charset=utf-8');
response.body.match(/<link>/g).length.should.eql(2);
});
});
it('serve /podcast/', function () {
const req = {
secure: true,
method: 'GET',
url: '/podcast/',
host: 'example.com'
};
return testUtils.mocks.express.invoke(app, req)
.then(function (response) {
const $ = cheerio.load(response.body);
response.statusCode.should.eql(200);
$('head link')[2].attribs.href.should.eql('https://127.0.0.1:2369/rss/');
});
});
});
});
describe('canary', function () {
let postSpy;
describe('default routes.yaml', function () {
before(function () {
testUtils.integrationTesting.urlService.resetGenerators();
testUtils.integrationTesting.defaultMocks(sinon, {amp: true, apps: true});
testUtils.integrationTesting.overrideGhostConfig(configUtils);
return testUtils.integrationTesting.initGhost()
.then(function () {
sinon.stub(themeService.getActive(), 'engine').withArgs('ghost-api').returns('canary');
sinon.stub(themeService.getActive(), 'config').withArgs('posts_per_page').returns(2);
app = siteApp({start: true});
return testUtils.integrationTesting.urlService.waitTillFinished();
})
.then(() => {
return appsService.init();
});
});
before(function () {
configUtils.set('url', 'http://example.com');
urlUtils.stubUrlUtilsFromConfig();
});
beforeEach(function () {
const postsAPI = require('../../../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 testUtils.mocks.express.invoke(app, req)
.then(function (response) {
response.statusCode.should.eql(200);
response.template.should.eql('post');
});
});
it('serve amp', function () {
const req = {
secure: true,
method: 'GET',
url: '/html-ipsum/amp/',
host: 'example.com'
};
return testUtils.mocks.express.invoke(app, req)
.then(function (response) {
response.statusCode.should.eql(200);
response.template.should.match(/amp\.hbs/);
response.body.should.match(/<h1>HTML Ipsum Presents<\/h1>/);
});
});
it('post not found', function () {
const req = {
secure: true,
method: 'GET',
url: '/not-found/',
host: 'example.com'
};
return testUtils.mocks.express.invoke(app, req)
.then(function (response) {
response.statusCode.should.eql(404);
response.template.should.eql('error-404');
});
});
it('serve static page', function () {
const req = {
secure: true,
method: 'GET',
url: '/static-page-test/',
host: 'example.com'
};
return testUtils.mocks.express.invoke(app, req)
.then(function (response) {
response.statusCode.should.eql(200);
response.template.should.eql('page');
});
});
it('serve author', function () {
const req = {
secure: true,
method: 'GET',
url: '/author/joe-bloggs/',
host: 'example.com'
};
return testUtils.mocks.express.invoke(app, req)
.then(function (response) {
const $ = cheerio.load(response.body);
response.statusCode.should.eql(200);
response.template.should.eql('author');
$('.author-bio').length.should.equal(1);
});
});
it('serve tag', function () {
const req = {
secure: true,
method: 'GET',
url: '/tag/bacon/',
host: 'example.com'
};
return testUtils.mocks.express.invoke(app, req)
.then(function (response) {
response.statusCode.should.eql(200);
response.template.should.eql('tag');
Revert post.page->post.type handling no issue - the column addition/removal can be too slow for large sites - will be added back in 3.0 --- Revert "Fixed canary api for page/type column" This reverts commit a5a7e7e919d83af3ea9cd7402a75dff60f2d7e9c. Revert "Updated frontend canary url config for page/type" This reverts commit 19100ec5e6edbe67464c4938521fe25d7ec15041. Revert "Updated canary api to handle type column correctly (#11006)" This reverts commit c3e8ba0523f5460662dcd3cddf0affd337b26eba. Revert "Ensured `page` filter works in routes.yaml" This reverts commit 9037c19e50c4da026f4b797413a682e1411b032f. Revert "Replaced usage of mongo util with nql-map-key-values" This reverts commit 8c5f1d0ef0ad9a03fb3e362c31063e47a0173411. Revert "Added shared nql-map-key-values module" This reverts commit ef4fd4b8ef3824290a00a371dad5a505431ab689. Revert "Ensured page prop is present on content api response" This reverts commit cfa0a0862bf7b247cfeb9b689cfdf6b8e3fb0c10. Revert "Fixed failing regression tests" This reverts commit 9c2bb3811fba8ea127b22f13d21112dcf1f5a46d. Revert "Updated xmlrpc and slack service to use type column" This reverts commit 44a02c7d3635967dd3fe8f96b793b50fd398bd40. Revert "Updated v0.1 posts api to work with type column" This reverts commit 2c81d7c914ac0a2c3b7f1d6d0385479e61d15f18. Revert "Removed updates to v0.1 specific code" This reverts commit 08d83c1f5332b7db6b96814651b496e707c2e124. Revert "Added missing context from ValidationError" This reverts commit cd45ab4f54abefeee8605df84cfc864fff1ad385. Revert "Renamed page->type in the page&posts serializers" This reverts commit df99e724e3d7dc1665916844983849494deea80d. Revert "Added mongo helper to input serializers" This reverts commit fb8eadb4a8109ba987d79decfe331c669a446609. Revert "Passed mongoTransformer through to NQL" This reverts commit 0ae3f0fdfc864dcf5c90c6b56cf975997974742c. Revert "Permitted mongoTransformer option for read methods" This reverts commit a89376bf2618520626d2cf1b8d86f3c8c453db23. Revert "Updated the count plugin to reference the type column" This reverts commit a52f15d3d3503bc9ce4e20961c1f4a0fd49316c7. Revert "Updated hashes for db integrity check" This reverts commit bb6b337be3d30e919e4edfdc2e59182cb81e9e5d. Revert "Remove page column and remaining references" This reverts commit 9d7190d69255ac011848c6bf654886be81abeedc. Revert "Added type column to data generator" This reverts commit e59806cb45c47e0bd547801de54ac5332913fbf5. Revert "Removed references to page column in rss tests" This reverts commit 04d0f855dede1a1bd910c1bc7ca4913ae27472ae. Revert "Removed page column references in validation tests" This reverts commit f0afbc5cc06449ccae034b930709e29133ca8374. Revert "Updated the post model to use the `type` column" This reverts commit 1189bc823ac6adde4f25d63d9fc83ca94e38d672. Revert "Updated url service to use type column" This reverts commit 61612ba8fd38a72d8ef6af5b2c199a9dcd80b80b. Revert "Updated the v2 api to deal with type column" This reverts commit 57afb2de2baf702575a2ea3e4d8e1b914f769e00. Revert "Added type property to post model defaults" This reverts commit dc3345b1c59d03261ecd678a9cbad0ec91ef5a38. Revert "Added type property to the default post fixtures" This reverts commit 82d8c380336b6455ad09622edf7ecd803d0cbe23. Revert "Added type column to posts table" This reverts commit 9b85fc6a69363c27d11963a5078136cedc696156.
2019-08-16 19:46:00 +03:00
postSpy.args[0][0].options.filter.should.eql('(tags:\'bacon\'+tags.visibility:public)+page:false');
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 testUtils.mocks.express.invoke(app, req)
.then(function (response) {
response.statusCode.should.eql(200);
});
});
it('serve collection', function () {
const req = {
secure: true,
method: 'GET',
url: '/',
host: 'example.com'
};
return testUtils.mocks.express.invoke(app, req)
.then(function (response) {
const $ = cheerio.load(response.body);
response.statusCode.should.eql(200);
response.template.should.eql('index');
$('.post-card').length.should.equal(2);
should.exist(response.res.locals.context);
should.exist(response.res.locals.version);
should.exist(response.res.locals.safeVersion);
should.exist(response.res.locals.safeVersion);
should.exist(response.res.locals.relativeUrl);
should.exist(response.res.locals.secure);
should.exist(response.res.routerOptions);
});
});
it('serve collection: page 2', function () {
const req = {
secure: true,
method: 'GET',
url: '/page/2/',
host: 'example.com'
};
return testUtils.mocks.express.invoke(app, req)
.then(function (response) {
const $ = cheerio.load(response.body);
response.statusCode.should.eql(200);
response.template.should.eql('index');
$('.post-card').length.should.equal(2);
});
});
it('serve theme asset', function () {
//configUtils.set('url', 'https://example.com');
const req = {
secure: true,
method: 'GET',
url: '/assets/css/screen.css',
host: 'example.com'
};
return testUtils.mocks.express.invoke(app, req)
.then(function (response) {
response.statusCode.should.eql(200);
});
});
});
describe('behaviour: prettify', function () {
it('url without slash', function () {
const req = {
secure: false,
method: 'GET',
url: '/prettify-me',
host: 'example.com'
};
return testUtils.mocks.express.invoke(app, req)
.then(function (response) {
response.statusCode.should.eql(301);
response.headers.location.should.eql('/prettify-me/');
});
});
});
describe('behaviour: url redirects', function () {
describe('pagination', function () {
it('redirect /page/1/ to /', function () {
const req = {
secure: false,
host: 'example.com',
method: 'GET',
url: '/page/1/'
};
return testUtils.mocks.express.invoke(app, req)
.then(function (response) {
response.statusCode.should.eql(301);
response.headers.location.should.eql('/');
});
});
});
describe('rss', function () {
it('redirect /feed/ to /rss/', function () {
const req = {
secure: false,
host: 'example.com',
method: 'GET',
url: '/feed/'
};
return testUtils.mocks.express.invoke(app, req)
.then(function (response) {
response.statusCode.should.eql(301);
response.headers.location.should.eql('/rss/');
});
});
});
});
});
describe('https', function () {
before(function () {
configUtils.set('url', 'https://example.com');
urlUtils.stubUrlUtilsFromConfig();
});
after(function () {
urlUtils.restore();
configUtils.restore();
});
describe('protocol', function () {
it('blog is https, request is http', function () {
const req = {
secure: false,
host: 'example.com',
method: 'GET',
url: '/html-ipsum'
};
return testUtils.mocks.express.invoke(app, req)
.then(function (response) {
response.statusCode.should.eql(301);
response.headers.location.should.eql('https://example.com/html-ipsum/');
});
});
it('blog is https, request is http, trailing slash exists already', function () {
const req = {
secure: false,
method: 'GET',
url: '/html-ipsum/',
host: 'example.com'
};
return testUtils.mocks.express.invoke(app, req)
.then(function (response) {
response.statusCode.should.eql(301);
response.headers.location.should.eql('https://example.com/html-ipsum/');
});
});
});
describe('assets', function () {
it('blog is https, request is http', function () {
const req = {
secure: false,
method: 'GET',
url: '/favicon.png',
host: 'example.com'
};
return testUtils.mocks.express.invoke(app, req)
.then(function (response) {
response.statusCode.should.eql(301);
response.headers.location.should.eql('https://example.com/favicon.png');
});
});
it('blog is https, request is http', function () {
const req = {
secure: false,
method: 'GET',
url: '/assets/css/main.css',
host: 'example.com'
};
return testUtils.mocks.express.invoke(app, req)
.then(function (response) {
response.statusCode.should.eql(301);
response.headers.location.should.eql('https://example.com/assets/css/main.css');
});
});
});
});
describe('extended routes.yaml: collections', function () {
describe('2 collections', function () {
before(function () {
sinon.stub(frontendSettingsService, 'get').returns({
routes: {
'/': {templates: ['home']}
},
collections: {
'/podcast/': {
permalink: '/podcast/:slug/',
filter: 'featured:true'
},
'/something/': {
permalink: '/something/:slug/',
filter: 'featured:false'
}
},
taxonomies: {
tag: '/categories/:slug/',
author: '/authors/:slug/'
}
});
testUtils.integrationTesting.urlService.resetGenerators();
testUtils.integrationTesting.defaultMocks(sinon, {theme: 'test-theme'});
return testUtils.integrationTesting.initGhost()
.then(function () {
sinon.stub(themeService.getActive(), 'engine').withArgs('ghost-api').returns('canary');
sinon.stub(themeService.getActive(), 'config').withArgs('posts_per_page').returns(2);
app = siteApp({start: true});
return testUtils.integrationTesting.urlService.waitTillFinished();
});
});
beforeEach(function () {
testUtils.integrationTesting.overrideGhostConfig(configUtils);
});
afterEach(function () {
configUtils.restore();
urlUtils.restore();
});
after(function () {
sinon.restore();
});
it('serve static route', function () {
const req = {
secure: true,
method: 'GET',
url: '/',
host: 'example.com'
};
return testUtils.mocks.express.invoke(app, req)
.then(function (response) {
response.statusCode.should.eql(200);
response.template.should.eql('home');
});
});
it('serve rss', function () {
const req = {
secure: true,
method: 'GET',
url: '/podcast/rss/',
host: 'example.com'
};
return testUtils.mocks.express.invoke(app, req)
.then(function (response) {
response.statusCode.should.eql(200);
});
});
it('serve post', function () {
const req = {
secure: true,
method: 'GET',
url: '/something/html-ipsum/',
host: 'example.com'
};
return testUtils.mocks.express.invoke(app, req)
.then(function (response) {
response.statusCode.should.eql(200);
response.template.should.eql('post');
});
});
it('serve collection: podcast with default template', function () {
const req = {
secure: true,
method: 'GET',
url: '/podcast/',
host: 'example.com'
};
return testUtils.mocks.express.invoke(app, req)
.then(function (response) {
const $ = cheerio.load(response.body);
response.statusCode.should.eql(200);
response.template.should.eql('index');
$('.post-card').length.should.equal(2);
});
});
it('serve collection: something with custom template', function () {
const req = {
secure: true,
method: 'GET',
url: '/something/',
host: 'example.com'
};
return testUtils.mocks.express.invoke(app, req)
.then(function (response) {
const $ = cheerio.load(response.body);
response.statusCode.should.eql(200);
response.template.should.eql('something');
});
});
});
describe('no collections', function () {
before(function () {
sinon.stub(frontendSettingsService, 'get').returns({
routes: {
'/something/': {
templates: ['something']
}
},
collections: {},
taxonomies: {}
});
testUtils.integrationTesting.urlService.resetGenerators();
testUtils.integrationTesting.defaultMocks(sinon, {theme: 'test-theme'});
return testUtils.integrationTesting.initGhost()
.then(function () {
sinon.stub(themeService.getActive(), 'engine').withArgs('ghost-api').returns('canary');
sinon.stub(themeService.getActive(), 'config').withArgs('posts_per_page').returns(2);
app = siteApp({start: true});
return testUtils.integrationTesting.urlService.waitTillFinished();
});
});
beforeEach(function () {
testUtils.integrationTesting.overrideGhostConfig(configUtils);
});
afterEach(function () {
configUtils.restore();
urlUtils.restore();
});
after(function () {
sinon.restore();
});
it('serve route', function () {
const req = {
secure: true,
method: 'GET',
url: '/something/',
host: 'example.com'
};
return testUtils.mocks.express.invoke(app, req)
.then(function (response) {
response.statusCode.should.eql(200);
response.template.should.eql('something');
});
});
});
describe('static permalink route', function () {
before(function () {
sinon.stub(frontendSettingsService, 'get').returns({
routes: {},
collections: {
'/podcast/': {
permalink: '/featured/',
filter: 'featured:true'
},
'/': {
permalink: '/:slug/'
}
},
taxonomies: {}
});
testUtils.integrationTesting.urlService.resetGenerators();
testUtils.integrationTesting.defaultMocks(sinon);
return testUtils.integrationTesting.initGhost()
.then(function () {
sinon.stub(themeService.getActive(), 'engine').withArgs('ghost-api').returns('canary');
sinon.stub(themeService.getActive(), 'config').withArgs('posts_per_page').returns(2);
app = siteApp({start: true});
return testUtils.integrationTesting.urlService.waitTillFinished();
});
});
beforeEach(function () {
testUtils.integrationTesting.overrideGhostConfig(configUtils);
});
afterEach(function () {
configUtils.restore();
urlUtils.restore();
});
after(function () {
sinon.restore();
});
it('serve post', function () {
const req = {
secure: true,
method: 'GET',
url: '/featured/',
host: 'example.com'
};
return testUtils.mocks.express.invoke(app, req)
.then(function (response) {
// We can't find a post with the slug "featured"
response.statusCode.should.eql(404);
response.template.should.eql('error-404');
});
});
it('serve post', function () {
const req = {
secure: true,
method: 'GET',
url: '/html-ipsum/',
host: 'example.com'
};
return testUtils.mocks.express.invoke(app, req)
.then(function (response) {
response.statusCode.should.eql(200);
response.template.should.eql('post');
});
});
it('serve author', function () {
const req = {
secure: true,
method: 'GET',
url: '/author/joe-bloggs/',
host: 'example.com'
};
return testUtils.mocks.express.invoke(app, req)
.then(function (response) {
response.statusCode.should.eql(404);
response.template.should.eql('error-404');
});
});
it('serve tag', function () {
const req = {
secure: true,
method: 'GET',
url: '/tag/bacon/',
host: 'example.com'
};
return testUtils.mocks.express.invoke(app, req)
.then(function (response) {
response.statusCode.should.eql(404);
response.template.should.eql('error-404');
});
});
});
describe('primary author permalink', function () {
before(function () {
sinon.stub(frontendSettingsService, 'get').returns({
routes: {},
collections: {
'/something/': {
permalink: '/:primary_author/:slug/'
}
},
taxonomies: {}
});
testUtils.integrationTesting.urlService.resetGenerators();
testUtils.integrationTesting.defaultMocks(sinon);
return testUtils.integrationTesting.initGhost()
.then(function () {
sinon.stub(themeService.getActive(), 'engine').withArgs('ghost-api').returns('canary');
sinon.stub(themeService.getActive(), 'config').withArgs('posts_per_page').returns(2);
app = siteApp({start: true});
return testUtils.integrationTesting.urlService.waitTillFinished();
});
});
beforeEach(function () {
testUtils.integrationTesting.overrideGhostConfig(configUtils);
});
afterEach(function () {
configUtils.restore();
urlUtils.restore();
});
after(function () {
sinon.restore();
});
it('serve post', function () {
const req = {
secure: true,
method: 'GET',
url: '/joe-bloggs/html-ipsum/',
host: 'example.com'
};
return testUtils.mocks.express.invoke(app, req)
.then(function (response) {
response.statusCode.should.eql(200);
response.template.should.eql('post');
});
});
it('post without author', function () {
const req = {
secure: true,
method: 'GET',
url: '/html-ipsum/',
host: 'example.com'
};
return testUtils.mocks.express.invoke(app, req)
.then(function (response) {
response.statusCode.should.eql(404);
response.template.should.eql('error-404');
});
});
it('page', function () {
const req = {
secure: true,
method: 'GET',
url: '/static-page-test/',
host: 'example.com'
};
return testUtils.mocks.express.invoke(app, req)
.then(function (response) {
response.statusCode.should.eql(200);
response.template.should.eql('page');
});
});
});
describe('primary tag permalink', function () {
before(function () {
sinon.stub(frontendSettingsService, 'get').returns({
routes: {},
collections: {
'/something/': {
permalink: '/something/:primary_tag/:slug/'
}
},
taxonomies: {}
});
testUtils.integrationTesting.urlService.resetGenerators();
testUtils.integrationTesting.defaultMocks(sinon);
return testUtils.integrationTesting.initGhost()
.then(function () {
sinon.stub(themeService.getActive(), 'engine').withArgs('ghost-api').returns('canary');
sinon.stub(themeService.getActive(), 'config').withArgs('posts_per_page').returns(2);
app = siteApp({start: true});
return testUtils.integrationTesting.urlService.waitTillFinished();
});
});
beforeEach(function () {
testUtils.integrationTesting.overrideGhostConfig(configUtils);
});
afterEach(function () {
configUtils.restore();
urlUtils.restore();
});
after(function () {
sinon.restore();
});
it('serve post', function () {
const req = {
secure: true,
method: 'GET',
url: '/something/kitchen-sink/html-ipsum/',
host: 'example.com'
};
return testUtils.mocks.express.invoke(app, req)
.then(function (response) {
response.statusCode.should.eql(200);
response.template.should.eql('post');
});
});
it('post without tag', function () {
const req = {
secure: true,
method: 'GET',
url: '/something/html-ipsum/',
host: 'example.com'
};
return testUtils.mocks.express.invoke(app, req)
.then(function (response) {
response.statusCode.should.eql(404);
response.template.should.eql('error-404');
});
});
it('post without tag', function () {
const req = {
secure: true,
method: 'GET',
url: '/html-ipsum/',
host: 'example.com'
};
return testUtils.mocks.express.invoke(app, req)
.then(function (response) {
response.statusCode.should.eql(404);
response.template.should.eql('error-404');
});
});
it('page', function () {
const req = {
secure: true,
method: 'GET',
url: '/static-page-test/',
host: 'example.com'
};
return testUtils.mocks.express.invoke(app, req)
.then(function (response) {
response.statusCode.should.eql(200);
response.template.should.eql('page');
});
});
});
describe('collection/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.urlService.waitTillFinished();
});
});
beforeEach(function () {
testUtils.integrationTesting.overrideGhostConfig(configUtils);
});
afterEach(function () {
configUtils.restore();
urlUtils.restore();
});
after(function () {
sinon.restore();
});
it('serve /food/', function () {
const req = {
secure: true,
method: 'GET',
url: '/food/',
host: 'example.com'
};
return testUtils.mocks.express.invoke(app, req)
.then(function (response) {
response.statusCode.should.eql(200);
response.template.should.eql('index');
});
});
it('serve bacon tag', function () {
const req = {
secure: true,
method: 'GET',
url: '/categories/bacon/',
host: 'example.com'
};
return testUtils.mocks.express.invoke(app, req)
.then(function (response) {
response.statusCode.should.eql(301);
});
});
it('serve /sport/', function () {
const req = {
secure: true,
method: 'GET',
url: '/sport/',
host: 'example.com'
};
return testUtils.mocks.express.invoke(app, req)
.then(function (response) {
response.statusCode.should.eql(200);
response.template.should.eql('index');
});
});
it('serve chorizo tag', function () {
const req = {
secure: true,
method: 'GET',
url: '/categories/chorizo/',
host: 'example.com'
};
return testUtils.mocks.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 testUtils.mocks.express.invoke(app, req)
.then(function (response) {
response.statusCode.should.eql(200);
});
});
});
});
describe('extended routes.yaml: templates', function () {
describe('default template, no template', function () {
before(function () {
sinon.stub(frontendSettingsService, 'get').returns({
routes: {},
collections: {
'/': {
permalink: '/:slug/',
templates: ['default']
},
'/magic/': {
permalink: '/magic/:slug/'
}
}
});
testUtils.integrationTesting.urlService.resetGenerators();
testUtils.integrationTesting.defaultMocks(sinon);
return testUtils.integrationTesting.initGhost()
.then(function () {
sinon.stub(themeService.getActive(), 'engine').withArgs('ghost-api').returns('canary');
sinon.stub(themeService.getActive(), 'config').withArgs('posts_per_page').returns(2);
app = siteApp({start: true});
return testUtils.integrationTesting.urlService.waitTillFinished();
});
});
beforeEach(function () {
testUtils.integrationTesting.overrideGhostConfig(configUtils);
});
afterEach(function () {
configUtils.restore();
urlUtils.restore();
});
after(function () {
sinon.restore();
});
it('serve collection', function () {
const req = {
secure: true,
method: 'GET',
url: '/',
host: 'example.com'
};
return testUtils.mocks.express.invoke(app, req)
.then(function (response) {
response.statusCode.should.eql(200);
response.template.should.eql('default');
});
});
it('serve second collectiom', function () {
const req = {
secure: true,
method: 'GET',
url: '/magic/',
host: 'example.com'
};
return testUtils.mocks.express.invoke(app, req)
.then(function (response) {
response.statusCode.should.eql(200);
response.template.should.eql('index');
});
});
});
describe('two templates', function () {
before(function () {
sinon.stub(frontendSettingsService, 'get').returns({
routes: {},
collections: {
'/': {
permalink: '/:slug/',
templates: ['something', 'default']
}
}
});
testUtils.integrationTesting.urlService.resetGenerators();
testUtils.integrationTesting.defaultMocks(sinon);
return testUtils.integrationTesting.initGhost()
.then(function () {
sinon.stub(themeService.getActive(), 'engine').withArgs('ghost-api').returns('canary');
sinon.stub(themeService.getActive(), 'config').withArgs('posts_per_page').returns(2);
app = siteApp({start: true});
return testUtils.integrationTesting.urlService.waitTillFinished();
});
});
beforeEach(function () {
testUtils.integrationTesting.overrideGhostConfig(configUtils);
});
afterEach(function () {
configUtils.restore();
urlUtils.restore();
});
after(function () {
sinon.restore();
});
it('serve collection', function () {
const req = {
secure: true,
method: 'GET',
url: '/',
host: 'example.com'
};
return testUtils.mocks.express.invoke(app, req)
.then(function (response) {
response.statusCode.should.eql(200);
response.template.should.eql('default');
});
});
});
describe('home.hbs priority', function () {
before(function () {
sinon.stub(frontendSettingsService, 'get').returns({
routes: {},
collections: {
'/': {
permalink: '/:slug/',
templates: ['something', 'default']
},
'/magic/': {
permalink: '/magic/:slug/',
templates: ['something', 'default']
}
}
});
testUtils.integrationTesting.urlService.resetGenerators();
testUtils.integrationTesting.defaultMocks(sinon, {theme: 'test-theme'});
return testUtils.integrationTesting.initGhost()
.then(function () {
sinon.stub(themeService.getActive(), 'engine').withArgs('ghost-api').returns('canary');
sinon.stub(themeService.getActive(), 'config').withArgs('posts_per_page').returns(2);
app = siteApp({start: true});
return testUtils.integrationTesting.urlService.waitTillFinished();
});
});
beforeEach(function () {
testUtils.integrationTesting.overrideGhostConfig(configUtils);
});
afterEach(function () {
configUtils.restore();
urlUtils.restore();
});
after(function () {
sinon.restore();
});
it('serve collection', function () {
const req = {
secure: true,
method: 'GET',
url: '/',
host: 'example.com'
};
return testUtils.mocks.express.invoke(app, req)
.then(function (response) {
response.statusCode.should.eql(200);
response.template.should.eql('home');
});
});
it('serve second page collection: should use index.hbs', function () {
const req = {
secure: true,
method: 'GET',
url: '/magic/',
host: 'example.com'
};
return testUtils.mocks.express.invoke(app, req)
.then(function (response) {
response.statusCode.should.eql(200);
response.template.should.eql('something');
});
});
});
});
describe('extended routes.yaml: routes', function () {
describe('channels', function () {
before(testUtils.integrationTesting.urlService.resetGenerators);
before(testUtils.teardown);
before(testUtils.setup('users:roles', 'posts'));
before(function () {
testUtils.integrationTesting.defaultMocks(sinon, {theme: 'test-theme-channels'});
sinon.stub(frontendSettingsService, 'get').returns({
routes: {
'/channel1/': {
controller: 'channel',
filter: 'tag:kitchen-sink',
data: {
query: {
tag: {
controller: '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.urlService.waitTillFinished();
});
});
beforeEach(function () {
testUtils.integrationTesting.overrideGhostConfig(configUtils);
});
afterEach(function () {
configUtils.restore();
urlUtils.restore();
});
after(function () {
sinon.restore();
});
it('serve channel 1', function () {
const req = {
secure: true,
method: 'GET',
url: '/channel1/',
host: 'example.com'
};
return testUtils.mocks.express.invoke(app, req)
.then(function (response) {
const $ = cheerio.load(response.body);
response.statusCode.should.eql(200);
response.template.should.eql('index');
$('.post-card').length.should.equal(2);
});
});
it('serve channel 1: rss', function () {
const req = {
secure: true,
method: 'GET',
url: '/channel1/rss/',
host: 'example.com'
};
return testUtils.mocks.express.invoke(app, req)
.then(function (response) {
response.statusCode.should.eql(200);
response.headers['content-type'].should.eql('text/xml; charset=UTF-8');
});
});
it('serve channel 2', function () {
const req = {
secure: true,
method: 'GET',
url: '/channel2/',
host: 'example.com'
};
return testUtils.mocks.express.invoke(app, req)
.then(function (response) {
const $ = cheerio.load(response.body);
response.statusCode.should.eql(200);
response.template.should.eql('default');
// default tempalte does not list posts
$('.post-card').length.should.equal(0);
});
});
it('serve channel 3', function () {
const req = {
secure: true,
method: 'GET',
url: '/channel3/',
host: 'example.com'
};
return testUtils.mocks.express.invoke(app, req)
.then(function (response) {
const $ = cheerio.load(response.body);
response.statusCode.should.eql(200);
response.template.should.eql('channel3');
});
});
it('serve channel 4', function () {
const req = {
secure: true,
method: 'GET',
url: '/channel4/',
host: 'example.com'
};
return testUtils.mocks.express.invoke(app, req)
.then(function (response) {
const $ = cheerio.load(response.body);
response.statusCode.should.eql(200);
response.template.should.eql('index');
$('.post-card').length.should.equal(4);
});
});
it('serve channel 5', function () {
const req = {
secure: true,
method: 'GET',
url: '/channel5/',
host: 'example.com'
};
return testUtils.mocks.express.invoke(app, req)
.then(function (response) {
const $ = cheerio.load(response.body);
response.statusCode.should.eql(200);
response.template.should.eql('index');
$('.post-card').length.should.equal(4);
});
});
it('serve channel 6', function () {
const req = {
secure: true,
method: 'GET',
url: '/channel6/',
host: 'example.com'
};
return testUtils.mocks.express.invoke(app, req)
.then(function (response) {
const $ = cheerio.load(response.body);
response.statusCode.should.eql(200);
response.template.should.eql('index');
$('.post-card').length.should.equal(4);
});
});
it('serve kitching-sink: redirect', function () {
const req = {
secure: true,
method: 'GET',
url: '/tag/kitchen-sink/',
host: 'example.com'
};
return testUtils.mocks.express.invoke(app, req)
.then(function (response) {
response.statusCode.should.eql(301);
response.headers.location.should.eql('/channel1/');
});
});
it('serve html-ipsum: redirect', function () {
const req = {
secure: true,
method: 'GET',
url: '/html-ipsum/',
host: 'example.com'
};
return testUtils.mocks.express.invoke(app, req)
.then(function (response) {
response.statusCode.should.eql(301);
response.headers.location.should.eql('/channel6/');
});
});
it('serve chorizo: no redirect', function () {
const req = {
secure: true,
method: 'GET',
url: '/tag/chorizo/',
host: 'example.com'
};
return testUtils.mocks.express.invoke(app, req)
.then(function (response) {
response.statusCode.should.eql(200);
});
});
it('serve joe-bloggs', function () {
const req = {
secure: true,
method: 'GET',
url: '/author/joe-bloggs/',
host: 'example.com'
};
return testUtils.mocks.express.invoke(app, req)
.then(function (response) {
response.statusCode.should.eql(200);
});
});
});
});
describe('extended routes.yaml (5): rss override', function () {
before(function () {
sinon.stub(frontendSettingsService, 'get').returns({
routes: {
'/podcast/rss/': {
templates: ['podcast/rss'],
content_type: 'text/xml'
},
'/cooking/': {
controller: 'channel',
rss: false
},
'/flat/': {
controller: 'channel'
}
},
collections: {
'/podcast/': {
permalink: '/:slug/',
filter: 'featured:true',
templates: ['home'],
rss: false
},
'/music/': {
permalink: '/:slug/',
rss: false
},
'/': {
permalink: '/:slug/'
}
},
taxonomies: {}
});
testUtils.integrationTesting.urlService.resetGenerators();
testUtils.integrationTesting.defaultMocks(sinon, {theme: 'test-theme'});
return testUtils.integrationTesting.initGhost()
.then(function () {
sinon.stub(themeService.getActive(), 'engine').withArgs('ghost-api').returns('canary');
sinon.stub(themeService.getActive(), 'config').withArgs('posts_per_page').returns(2);
app = siteApp({start: true});
return testUtils.integrationTesting.urlService.waitTillFinished();
});
});
beforeEach(function () {
testUtils.integrationTesting.overrideGhostConfig(configUtils);
});
afterEach(function () {
configUtils.restore();
urlUtils.restore();
});
after(function () {
sinon.restore();
});
it('serve /rss/', function () {
const req = {
secure: true,
method: 'GET',
url: '/rss/',
host: 'example.com'
};
return testUtils.mocks.express.invoke(app, req)
.then(function (response) {
response.statusCode.should.eql(200);
});
});
it('serve /music/rss/', function () {
const req = {
secure: true,
method: 'GET',
url: '/music/rss/',
host: 'example.com'
};
return testUtils.mocks.express.invoke(app, req)
.then(function (response) {
response.statusCode.should.eql(404);
});
});
it('serve /cooking/rss/', function () {
const req = {
secure: true,
method: 'GET',
url: '/cooking/rss/',
host: 'example.com'
};
return testUtils.mocks.express.invoke(app, req)
.then(function (response) {
response.statusCode.should.eql(404);
});
});
it('serve /flat/rss/', function () {
const req = {
secure: true,
method: 'GET',
url: '/flat/rss/',
host: 'example.com'
};
return testUtils.mocks.express.invoke(app, req)
.then(function (response) {
response.statusCode.should.eql(200);
});
});
it('serve /podcast/rss/', function () {
const req = {
secure: true,
method: 'GET',
url: '/podcast/rss/',
host: 'example.com'
};
return testUtils.mocks.express.invoke(app, req)
.then(function (response) {
response.statusCode.should.eql(200);
response.template.should.eql('podcast/rss');
response.headers['content-type'].should.eql('text/xml; charset=utf-8');
response.body.match(/<link>/g).length.should.eql(2);
});
});
it('serve /podcast/', function () {
const req = {
secure: true,
method: 'GET',
url: '/podcast/',
host: 'example.com'
};
return testUtils.mocks.express.invoke(app, req)
.then(function (response) {
const $ = cheerio.load(response.body);
response.statusCode.should.eql(200);
$('head link')[2].attribs.href.should.eql('https://127.0.0.1:2369/rss/');
});
});
});
});
describe('v3', function () {
let postSpy;
describe('default routes.yaml', function () {
before(function () {
testUtils.integrationTesting.urlService.resetGenerators();
testUtils.integrationTesting.defaultMocks(sinon, {amp: true, apps: true});
testUtils.integrationTesting.overrideGhostConfig(configUtils);
return testUtils.integrationTesting.initGhost()
.then(function () {
sinon.stub(themeService.getActive(), 'engine').withArgs('ghost-api').returns('v3');
sinon.stub(themeService.getActive(), 'config').withArgs('posts_per_page').returns(2);
app = siteApp({start: true});
return testUtils.integrationTesting.urlService.waitTillFinished();
})
.then(() => {
return appsService.init();
});
});
before(function () {
configUtils.set('url', 'http://example.com');
urlUtils.stubUrlUtilsFromConfig();
});
beforeEach(function () {
const postsAPI = require('../../../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 testUtils.mocks.express.invoke(app, req)
.then(function (response) {
response.statusCode.should.eql(200);
response.template.should.eql('post');
});
});
it('serve amp', function () {
const req = {
secure: true,
method: 'GET',
url: '/html-ipsum/amp/',
host: 'example.com'
};
return testUtils.mocks.express.invoke(app, req)
.then(function (response) {
response.statusCode.should.eql(200);
response.template.should.match(/amp\.hbs/);
response.body.should.match(/<h1>HTML Ipsum Presents<\/h1>/);
});
});
it('post not found', function () {
const req = {
secure: true,
method: 'GET',
url: '/not-found/',
host: 'example.com'
};
return testUtils.mocks.express.invoke(app, req)
.then(function (response) {
response.statusCode.should.eql(404);
response.template.should.eql('error-404');
});
});
it('serve static page', function () {
const req = {
secure: true,
method: 'GET',
url: '/static-page-test/',
host: 'example.com'
};
return testUtils.mocks.express.invoke(app, req)
.then(function (response) {
response.statusCode.should.eql(200);
response.template.should.eql('page');
});
});
it('serve author', function () {
const req = {
secure: true,
method: 'GET',
url: '/author/joe-bloggs/',
host: 'example.com'
};
return testUtils.mocks.express.invoke(app, req)
.then(function (response) {
const $ = cheerio.load(response.body);
response.statusCode.should.eql(200);
response.template.should.eql('author');
$('.author-bio').length.should.equal(1);
});
});
it('serve tag', function () {
const req = {
secure: true,
method: 'GET',
url: '/tag/bacon/',
host: 'example.com'
};
return testUtils.mocks.express.invoke(app, req)
.then(function (response) {
response.statusCode.should.eql(200);
response.template.should.eql('tag');
postSpy.args[0][0].options.filter.should.eql('(tags:\'bacon\'+tags.visibility:public)+page:false');
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 testUtils.mocks.express.invoke(app, req)
.then(function (response) {
response.statusCode.should.eql(200);
});
});
it('serve collection', function () {
const req = {
secure: true,
method: 'GET',
url: '/',
host: 'example.com'
};
return testUtils.mocks.express.invoke(app, req)
.then(function (response) {
const $ = cheerio.load(response.body);
response.statusCode.should.eql(200);
response.template.should.eql('index');
$('.post-card').length.should.equal(2);
should.exist(response.res.locals.context);
should.exist(response.res.locals.version);
should.exist(response.res.locals.safeVersion);
should.exist(response.res.locals.safeVersion);
should.exist(response.res.locals.relativeUrl);
should.exist(response.res.locals.secure);
should.exist(response.res.routerOptions);
});
});
it('serve collection: page 2', function () {
const req = {
secure: true,
method: 'GET',
url: '/page/2/',
host: 'example.com'
};
return testUtils.mocks.express.invoke(app, req)
.then(function (response) {
const $ = cheerio.load(response.body);
response.statusCode.should.eql(200);
response.template.should.eql('index');
$('.post-card').length.should.equal(2);
});
});
it('serve theme asset', function () {
//configUtils.set('url', 'https://example.com');
const req = {
secure: true,
method: 'GET',
url: '/assets/css/screen.css',
host: 'example.com'
};
return testUtils.mocks.express.invoke(app, req)
.then(function (response) {
response.statusCode.should.eql(200);
});
});
});
describe('behaviour: prettify', function () {
it('url without slash', function () {
const req = {
secure: false,
method: 'GET',
url: '/prettify-me',
host: 'example.com'
};
return testUtils.mocks.express.invoke(app, req)
.then(function (response) {
response.statusCode.should.eql(301);
response.headers.location.should.eql('/prettify-me/');
});
});
});
describe('behaviour: url redirects', function () {
describe('pagination', function () {
it('redirect /page/1/ to /', function () {
const req = {
secure: false,
host: 'example.com',
method: 'GET',
url: '/page/1/'
};
return testUtils.mocks.express.invoke(app, req)
.then(function (response) {
response.statusCode.should.eql(301);
response.headers.location.should.eql('/');
});
});
});
describe('rss', function () {
it('redirect /feed/ to /rss/', function () {
const req = {
secure: false,
host: 'example.com',
method: 'GET',
url: '/feed/'
};
return testUtils.mocks.express.invoke(app, req)
.then(function (response) {
response.statusCode.should.eql(301);
response.headers.location.should.eql('/rss/');
});
});
});
});
});
describe('https', function () {
before(function () {
configUtils.set('url', 'https://example.com');
urlUtils.stubUrlUtilsFromConfig();
});
after(function () {
urlUtils.restore();
configUtils.restore();
});
describe('protocol', function () {
it('blog is https, request is http', function () {
const req = {
secure: false,
host: 'example.com',
method: 'GET',
url: '/html-ipsum'
};
return testUtils.mocks.express.invoke(app, req)
.then(function (response) {
response.statusCode.should.eql(301);
response.headers.location.should.eql('https://example.com/html-ipsum/');
});
});
it('blog is https, request is http, trailing slash exists already', function () {
const req = {
secure: false,
method: 'GET',
url: '/html-ipsum/',
host: 'example.com'
};
return testUtils.mocks.express.invoke(app, req)
.then(function (response) {
response.statusCode.should.eql(301);
response.headers.location.should.eql('https://example.com/html-ipsum/');
});
});
});
describe('assets', function () {
it('blog is https, request is http', function () {
const req = {
secure: false,
method: 'GET',
url: '/favicon.png',
host: 'example.com'
};
return testUtils.mocks.express.invoke(app, req)
.then(function (response) {
response.statusCode.should.eql(301);
response.headers.location.should.eql('https://example.com/favicon.png');
});
});
it('blog is https, request is http', function () {
const req = {
secure: false,
method: 'GET',
url: '/assets/css/main.css',
host: 'example.com'
};
return testUtils.mocks.express.invoke(app, req)
.then(function (response) {
response.statusCode.should.eql(301);
response.headers.location.should.eql('https://example.com/assets/css/main.css');
});
});
});
});
describe('extended routes.yaml: collections', function () {
describe('2 collections', function () {
before(function () {
sinon.stub(frontendSettingsService, 'get').returns({
routes: {
'/': {
templates: ['home']
}
},
collections: {
'/podcast/': {
permalink: '/podcast/:slug/',
filter: 'featured:true'
},
'/something/': {
permalink: '/something/:slug/',
filter: 'featured:false'
}
},
taxonomies: {
tag: '/categories/:slug/',
author: '/authors/:slug/'
}
});
testUtils.integrationTesting.urlService.resetGenerators();
testUtils.integrationTesting.defaultMocks(sinon, {theme: 'test-theme'});
return testUtils.integrationTesting.initGhost()
.then(function () {
sinon.stub(themeService.getActive(), 'engine').withArgs('ghost-api').returns('v3');
sinon.stub(themeService.getActive(), 'config').withArgs('posts_per_page').returns(2);
app = siteApp({start: true});
return testUtils.integrationTesting.urlService.waitTillFinished();
});
});
beforeEach(function () {
testUtils.integrationTesting.overrideGhostConfig(configUtils);
});
afterEach(function () {
configUtils.restore();
urlUtils.restore();
});
after(function () {
sinon.restore();
});
it('serve static route', function () {
const req = {
secure: true,
method: 'GET',
url: '/',
host: 'example.com'
};
return testUtils.mocks.express.invoke(app, req)
.then(function (response) {
response.statusCode.should.eql(200);
response.template.should.eql('home');
});
});
it('serve rss', function () {
const req = {
secure: true,
method: 'GET',
url: '/podcast/rss/',
host: 'example.com'
};
return testUtils.mocks.express.invoke(app, req)
.then(function (response) {
response.statusCode.should.eql(200);
});
});
it('serve post', function () {
const req = {
secure: true,
method: 'GET',
url: '/something/html-ipsum/',
host: 'example.com'
};
return testUtils.mocks.express.invoke(app, req)
.then(function (response) {
response.statusCode.should.eql(200);
response.template.should.eql('post');
});
});
it('serve collection: podcast', function () {
const req = {
secure: true,
method: 'GET',
url: '/podcast/',
host: 'example.com'
};
return testUtils.mocks.express.invoke(app, req)
.then(function (response) {
const $ = cheerio.load(response.body);
response.statusCode.should.eql(200);
response.template.should.eql('index');
$('.post-card').length.should.equal(2);
});
});
it('serve collection: something with custom template', function () {
const req = {
secure: true,
method: 'GET',
url: '/something/',
host: 'example.com'
};
return testUtils.mocks.express.invoke(app, req)
.then(function (response) {
const $ = cheerio.load(response.body);
response.statusCode.should.eql(200);
response.template.should.eql('something');
});
});
});
describe('no collections', function () {
before(function () {
sinon.stub(frontendSettingsService, 'get').returns({
routes: {
'/something/': {templates: ['something']}
},
collections: {},
taxonomies: {}
});
testUtils.integrationTesting.urlService.resetGenerators();
testUtils.integrationTesting.defaultMocks(sinon, {theme: 'test-theme'});
return testUtils.integrationTesting.initGhost()
.then(function () {
sinon.stub(themeService.getActive(), 'engine').withArgs('ghost-api').returns('v3');
sinon.stub(themeService.getActive(), 'config').withArgs('posts_per_page').returns(2);
app = siteApp({start: true});
return testUtils.integrationTesting.urlService.waitTillFinished();
});
});
beforeEach(function () {
testUtils.integrationTesting.overrideGhostConfig(configUtils);
});
afterEach(function () {
configUtils.restore();
urlUtils.restore();
});
after(function () {
sinon.restore();
});
it('serve route', function () {
const req = {
secure: true,
method: 'GET',
url: '/something/',
host: 'example.com'
};
return testUtils.mocks.express.invoke(app, req)
.then(function (response) {
response.statusCode.should.eql(200);
response.template.should.eql('something');
});
});
});
describe('static permalink route', function () {
before(function () {
sinon.stub(frontendSettingsService, 'get').returns({
routes: {},
collections: {
'/podcast/': {
permalink: '/featured/',
filter: 'featured:true'
},
'/': {
permalink: '/:slug/'
}
},
taxonomies: {}
});
testUtils.integrationTesting.urlService.resetGenerators();
testUtils.integrationTesting.defaultMocks(sinon);
return testUtils.integrationTesting.initGhost()
.then(function () {
sinon.stub(themeService.getActive(), 'engine').withArgs('ghost-api').returns('v3');
sinon.stub(themeService.getActive(), 'config').withArgs('posts_per_page').returns(2);
app = siteApp({start: true});
return testUtils.integrationTesting.urlService.waitTillFinished();
});
});
beforeEach(function () {
testUtils.integrationTesting.overrideGhostConfig(configUtils);
});
afterEach(function () {
configUtils.restore();
urlUtils.restore();
});
after(function () {
sinon.restore();
});
it('serve post', function () {
const req = {
secure: true,
method: 'GET',
url: '/featured/',
host: 'example.com'
};
return testUtils.mocks.express.invoke(app, req)
.then(function (response) {
// We can't find a post with the slug "featured"
response.statusCode.should.eql(404);
response.template.should.eql('error-404');
});
});
it('serve post', function () {
const req = {
secure: true,
method: 'GET',
url: '/html-ipsum/',
host: 'example.com'
};
return testUtils.mocks.express.invoke(app, req)
.then(function (response) {
response.statusCode.should.eql(200);
response.template.should.eql('post');
});
});
it('serve author', function () {
const req = {
secure: true,
method: 'GET',
url: '/author/joe-bloggs/',
host: 'example.com'
};
return testUtils.mocks.express.invoke(app, req)
.then(function (response) {
response.statusCode.should.eql(404);
response.template.should.eql('error-404');
});
});
it('serve tag', function () {
const req = {
secure: true,
method: 'GET',
url: '/tag/bacon/',
host: 'example.com'
};
return testUtils.mocks.express.invoke(app, req)
.then(function (response) {
response.statusCode.should.eql(404);
response.template.should.eql('error-404');
});
});
});
describe('primary author permalink', function () {
before(function () {
sinon.stub(frontendSettingsService, 'get').returns({
routes: {},
collections: {
'/something/': {
permalink: '/:primary_author/:slug/'
}
},
taxonomies: {}
});
testUtils.integrationTesting.urlService.resetGenerators();
testUtils.integrationTesting.defaultMocks(sinon);
return testUtils.integrationTesting.initGhost()
.then(function () {
sinon.stub(themeService.getActive(), 'engine').withArgs('ghost-api').returns('v3');
sinon.stub(themeService.getActive(), 'config').withArgs('posts_per_page').returns(2);
app = siteApp({start: true});
return testUtils.integrationTesting.urlService.waitTillFinished();
});
});
beforeEach(function () {
testUtils.integrationTesting.overrideGhostConfig(configUtils);
});
afterEach(function () {
configUtils.restore();
urlUtils.restore();
});
after(function () {
sinon.restore();
});
it('serve post', function () {
const req = {
secure: true,
method: 'GET',
url: '/joe-bloggs/html-ipsum/',
host: 'example.com'
};
return testUtils.mocks.express.invoke(app, req)
.then(function (response) {
response.statusCode.should.eql(200);
response.template.should.eql('post');
});
});
it('post without author', function () {
const req = {
secure: true,
method: 'GET',
url: '/html-ipsum/',
host: 'example.com'
};
return testUtils.mocks.express.invoke(app, req)
.then(function (response) {
response.statusCode.should.eql(404);
response.template.should.eql('error-404');
});
});
it('page', function () {
const req = {
secure: true,
method: 'GET',
url: '/static-page-test/',
host: 'example.com'
};
return testUtils.mocks.express.invoke(app, req)
.then(function (response) {
response.statusCode.should.eql(200);
response.template.should.eql('page');
});
});
});
describe('primary tag permalink', function () {
before(function () {
sinon.stub(frontendSettingsService, 'get').returns({
routes: {},
collections: {
'/something/': {
permalink: '/something/:primary_tag/:slug/'
}
},
taxonomies: {}
});
testUtils.integrationTesting.urlService.resetGenerators();
testUtils.integrationTesting.defaultMocks(sinon);
return testUtils.integrationTesting.initGhost()
.then(function () {
sinon.stub(themeService.getActive(), 'engine').withArgs('ghost-api').returns('v3');
sinon.stub(themeService.getActive(), 'config').withArgs('posts_per_page').returns(2);
app = siteApp({start: true});
return testUtils.integrationTesting.urlService.waitTillFinished();
});
});
beforeEach(function () {
testUtils.integrationTesting.overrideGhostConfig(configUtils);
});
afterEach(function () {
configUtils.restore();
urlUtils.restore();
});
after(function () {
sinon.restore();
});
it('serve post', function () {
const req = {
secure: true,
method: 'GET',
url: '/something/kitchen-sink/html-ipsum/',
host: 'example.com'
};
return testUtils.mocks.express.invoke(app, req)
.then(function (response) {
response.statusCode.should.eql(200);
response.template.should.eql('post');
});
});
it('post without tag', function () {
const req = {
secure: true,
method: 'GET',
url: '/something/html-ipsum/',
host: 'example.com'
};
return testUtils.mocks.express.invoke(app, req)
.then(function (response) {
response.statusCode.should.eql(404);
response.template.should.eql('error-404');
});
});
it('post without tag', function () {
const req = {
secure: true,
method: 'GET',
url: '/html-ipsum/',
host: 'example.com'
};
return testUtils.mocks.express.invoke(app, req)
.then(function (response) {
response.statusCode.should.eql(404);
response.template.should.eql('error-404');
});
});
it('page', function () {
const req = {
secure: true,
method: 'GET',
url: '/static-page-test/',
host: 'example.com'
};
return testUtils.mocks.express.invoke(app, req)
.then(function (response) {
response.statusCode.should.eql(200);
response.template.should.eql('page');
});
});
});
describe('collection/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.urlService.waitTillFinished();
});
});
beforeEach(function () {
testUtils.integrationTesting.overrideGhostConfig(configUtils);
});
afterEach(function () {
configUtils.restore();
urlUtils.restore();
});
after(function () {
sinon.restore();
});
it('serve /food/', function () {
const req = {
secure: true,
method: 'GET',
url: '/food/',
host: 'example.com'
};
return testUtils.mocks.express.invoke(app, req)
.then(function (response) {
response.statusCode.should.eql(200);
response.template.should.eql('index');
});
});
it('serve bacon tag', function () {
const req = {
secure: true,
method: 'GET',
url: '/categories/bacon/',
host: 'example.com'
};
return testUtils.mocks.express.invoke(app, req)
.then(function (response) {
response.statusCode.should.eql(301);
});
});
it('serve /sport/', function () {
const req = {
secure: true,
method: 'GET',
url: '/sport/',
host: 'example.com'
};
return testUtils.mocks.express.invoke(app, req)
.then(function (response) {
response.statusCode.should.eql(200);
response.template.should.eql('index');
});
});
it('serve chorizo tag', function () {
const req = {
secure: true,
method: 'GET',
url: '/categories/chorizo/',
host: 'example.com'
};
return testUtils.mocks.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 testUtils.mocks.express.invoke(app, req)
.then(function (response) {
response.statusCode.should.eql(200);
});
});
});
});
describe('extended routes.yaml: templates', function () {
describe('default template, no template', function () {
before(function () {
sinon.stub(frontendSettingsService, 'get').returns({
routes: {},
collections: {
'/': {
permalink: '/:slug/',
templates: ['default']
},
'/magic/': {
permalink: '/magic/:slug/'
}
}
});
testUtils.integrationTesting.urlService.resetGenerators();
testUtils.integrationTesting.defaultMocks(sinon);
return testUtils.integrationTesting.initGhost()
.then(function () {
sinon.stub(themeService.getActive(), 'engine').withArgs('ghost-api').returns('v3');
sinon.stub(themeService.getActive(), 'config').withArgs('posts_per_page').returns(2);
app = siteApp({start: true});
return testUtils.integrationTesting.urlService.waitTillFinished();
});
});
beforeEach(function () {
testUtils.integrationTesting.overrideGhostConfig(configUtils);
});
afterEach(function () {
configUtils.restore();
urlUtils.restore();
});
after(function () {
sinon.restore();
});
it('serve collection', function () {
const req = {
secure: true,
method: 'GET',
url: '/',
host: 'example.com'
};
return testUtils.mocks.express.invoke(app, req)
.then(function (response) {
response.statusCode.should.eql(200);
response.template.should.eql('default');
});
});
it('serve second collectiom', function () {
const req = {
secure: true,
method: 'GET',
url: '/magic/',
host: 'example.com'
};
return testUtils.mocks.express.invoke(app, req)
.then(function (response) {
response.statusCode.should.eql(200);
response.template.should.eql('index');
});
});
});
describe('two templates', function () {
before(function () {
sinon.stub(frontendSettingsService, 'get').returns({
routes: {},
collections: {
'/': {
permalink: '/:slug/',
templates: ['something', 'default']
}
}
});
testUtils.integrationTesting.urlService.resetGenerators();
testUtils.integrationTesting.defaultMocks(sinon);
return testUtils.integrationTesting.initGhost()
.then(function () {
sinon.stub(themeService.getActive(), 'engine').withArgs('ghost-api').returns('v3');
sinon.stub(themeService.getActive(), 'config').withArgs('posts_per_page').returns(2);
app = siteApp({start: true});
return testUtils.integrationTesting.urlService.waitTillFinished();
});
});
beforeEach(function () {
testUtils.integrationTesting.overrideGhostConfig(configUtils);
});
afterEach(function () {
configUtils.restore();
urlUtils.restore();
});
after(function () {
sinon.restore();
});
it('serve collection', function () {
const req = {
secure: true,
method: 'GET',
url: '/',
host: 'example.com'
};
return testUtils.mocks.express.invoke(app, req)
.then(function (response) {
response.statusCode.should.eql(200);
response.template.should.eql('default');
});
});
});
describe('home.hbs priority', function () {
before(function () {
sinon.stub(frontendSettingsService, 'get').returns({
routes: {},
collections: {
'/': {
permalink: '/:slug/',
templates: ['something', 'default']
},
'/magic/': {
permalink: '/magic/:slug/',
templates: ['something', 'default']
}
}
});
testUtils.integrationTesting.urlService.resetGenerators();
testUtils.integrationTesting.defaultMocks(sinon, {theme: 'test-theme'});
return testUtils.integrationTesting.initGhost()
.then(function () {
sinon.stub(themeService.getActive(), 'engine').withArgs('ghost-api').returns('v3');
sinon.stub(themeService.getActive(), 'config').withArgs('posts_per_page').returns(2);
app = siteApp({start: true});
return testUtils.integrationTesting.urlService.waitTillFinished();
});
});
beforeEach(function () {
testUtils.integrationTesting.overrideGhostConfig(configUtils);
});
afterEach(function () {
configUtils.restore();
urlUtils.restore();
});
after(function () {
sinon.restore();
});
it('serve collection', function () {
const req = {
secure: true,
method: 'GET',
url: '/',
host: 'example.com'
};
return testUtils.mocks.express.invoke(app, req)
.then(function (response) {
response.statusCode.should.eql(200);
response.template.should.eql('home');
});
});
it('serve second page collection: should use index.hbs', function () {
const req = {
secure: true,
method: 'GET',
url: '/magic/',
host: 'example.com'
};
return testUtils.mocks.express.invoke(app, req)
.then(function (response) {
response.statusCode.should.eql(200);
response.template.should.eql('something');
});
});
});
});
describe('extended routes.yaml: routes', function () {
describe('channels', function () {
before(testUtils.integrationTesting.urlService.resetGenerators);
before(testUtils.teardown);
before(testUtils.setup('users:roles', 'posts'));
before(function () {
testUtils.integrationTesting.defaultMocks(sinon, {theme: 'test-theme-channels'});
sinon.stub(frontendSettingsService, 'get').returns({
routes: {
'/channel1/': {
controller: 'channel',
filter: 'tag:kitchen-sink',
data: {
query: {
tag: {
controller: '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.urlService.waitTillFinished();
});
});
beforeEach(function () {
testUtils.integrationTesting.overrideGhostConfig(configUtils);
});
afterEach(function () {
configUtils.restore();
urlUtils.restore();
});
after(function () {
sinon.restore();
});
it('serve channel 1', function () {
const req = {
secure: true,
method: 'GET',
url: '/channel1/',
host: 'example.com'
};
return testUtils.mocks.express.invoke(app, req)
.then(function (response) {
const $ = cheerio.load(response.body);
response.statusCode.should.eql(200);
response.template.should.eql('index');
$('.post-card').length.should.equal(2);
});
});
it('serve channel 1: rss', function () {
const req = {
secure: true,
method: 'GET',
url: '/channel1/rss/',
host: 'example.com'
};
return testUtils.mocks.express.invoke(app, req)
.then(function (response) {
response.statusCode.should.eql(200);
response.headers['content-type'].should.eql('text/xml; charset=UTF-8');
});
});
it('serve channel 2', function () {
const req = {
secure: true,
method: 'GET',
url: '/channel2/',
host: 'example.com'
};
return testUtils.mocks.express.invoke(app, req)
.then(function (response) {
const $ = cheerio.load(response.body);
response.statusCode.should.eql(200);
response.template.should.eql('default');
// default tempalte does not list posts
$('.post-card').length.should.equal(0);
});
});
it('serve channel 3', function () {
const req = {
secure: true,
method: 'GET',
url: '/channel3/',
host: 'example.com'
};
return testUtils.mocks.express.invoke(app, req)
.then(function (response) {
const $ = cheerio.load(response.body);
response.statusCode.should.eql(200);
response.template.should.eql('channel3');
});
});
it('serve channel 4', function () {
const req = {
secure: true,
method: 'GET',
url: '/channel4/',
host: 'example.com'
};
return testUtils.mocks.express.invoke(app, req)
.then(function (response) {
const $ = cheerio.load(response.body);
response.statusCode.should.eql(200);
response.template.should.eql('index');
$('.post-card').length.should.equal(4);
});
});
it('serve channel 5', function () {
const req = {
secure: true,
method: 'GET',
url: '/channel5/',
host: 'example.com'
};
return testUtils.mocks.express.invoke(app, req)
.then(function (response) {
const $ = cheerio.load(response.body);
response.statusCode.should.eql(200);
response.template.should.eql('index');
$('.post-card').length.should.equal(4);
});
});
it('serve channel 6', function () {
const req = {
secure: true,
method: 'GET',
url: '/channel6/',
host: 'example.com'
};
return testUtils.mocks.express.invoke(app, req)
.then(function (response) {
const $ = cheerio.load(response.body);
response.statusCode.should.eql(200);
response.template.should.eql('index');
$('.post-card').length.should.equal(4);
});
});
it('serve kitching-sink: redirect', function () {
const req = {
secure: true,
method: 'GET',
url: '/tag/kitchen-sink/',
host: 'example.com'
};
return testUtils.mocks.express.invoke(app, req)
.then(function (response) {
response.statusCode.should.eql(301);
response.headers.location.should.eql('/channel1/');
});
});
it('serve html-ipsum: redirect', function () {
const req = {
secure: true,
method: 'GET',
url: '/html-ipsum/',
host: 'example.com'
};
return testUtils.mocks.express.invoke(app, req)
.then(function (response) {
response.statusCode.should.eql(301);
response.headers.location.should.eql('/channel6/');
});
});
it('serve chorizo: no redirect', function () {
const req = {
secure: true,
method: 'GET',
url: '/tag/chorizo/',
host: 'example.com'
};
return testUtils.mocks.express.invoke(app, req)
.then(function (response) {
response.statusCode.should.eql(200);
});
});
it('serve joe-bloggs', function () {
const req = {
secure: true,
method: 'GET',
url: '/author/joe-bloggs/',
host: 'example.com'
};
return testUtils.mocks.express.invoke(app, req)
.then(function (response) {
response.statusCode.should.eql(200);
});
});
});
});
describe('extended routes.yaml (5): rss override', function () {
before(function () {
sinon.stub(frontendSettingsService, 'get').returns({
routes: {
'/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.urlService.waitTillFinished();
});
});
beforeEach(function () {
testUtils.integrationTesting.overrideGhostConfig(configUtils);
});
afterEach(function () {
configUtils.restore();
urlUtils.restore();
});
after(function () {
sinon.restore();
});
it('serve /rss/', function () {
const req = {
secure: true,
method: 'GET',
url: '/rss/',
✨Dynamic Routing Beta (#9596) refs #9601 ### Dynamic Routing This is the beta version of dynamic routing. - we had a initial implementation of "channels" available in the codebase - we have removed and moved this implementation - there is now a centralised place for dynamic routing - server/services/routing - each routing component is represented by a router type e.g. collections, routes, static pages, taxonomies, rss, preview of posts - keep as much as possible logic of routing helpers, middlewares and controllers - ensure test coverage - connect all the things together - yaml file + validation - routing + routers - url service - sitemaps - url access - deeper implementation of yaml validations - e.g. hard require slashes - ensure routing hierarchy/order - e.g. you enable the subscriber app - you have a custom static page, which lives under the same slug /subscribe - static pages are stronger than apps - e.g. the first collection owns the post it has filtered - a post cannot live in two collections - ensure apps are still working and hook into the routers layer (or better said: and register in the routing service) - put as much as possible comments to the code base for better understanding - ensure a clean debug log - ensure we can unmount routes - e.g. you have a collection permalink of /:slug/ represented by {globals.permalink} - and you change the permalink in the admin to dated permalink - the express route get's refreshed from /:slug/ to /:year/:month/:day/:slug/ - unmount without server restart, yey - ensure we are backwards compatible - e.g. render home.hbs for collection index if collection route is / - ensure you can access your configured permalink from the settings table with {globals.permalink} ### Render 503 if url service did not finish - return 503 if the url service has not finished generating the resource urls ### Rewrite sitemaps - we have rewritten the sitemaps "service", because the url generator does no longer happen on runtime - we generate all urls on bootstrap - the sitemaps service will consume created resource and router urls - these urls will be shown on the xml pages - we listen on url events - we listen on router events - we no longer have to fetch the resources, which is nice - the urlservice pre-fetches resources and emits their urls - the urlservice is the only component who knows which urls are valid - i made some ES6 adaptions - we keep the caching logic -> only regenerate xml if there is a change - updated tests - checked test coverage (100%) ### Re-work usage of Url utility - replace all usages of `urlService.utils.urlFor` by `urlService.getByResourceId` - only for resources e.g. post, author, tag - this is important, because with dynamic routing we no longer create static urls based on the settings permalink on runtime - adapt url utility - adapt tests
2018-06-05 20:02:20 +03:00
host: 'example.com'
};
return testUtils.mocks.express.invoke(app, req)
.then(function (response) {
response.statusCode.should.eql(200);
});
});
it('serve /music/rss/', function () {
const req = {
secure: true,
method: 'GET',
url: '/music/rss/',
host: 'example.com'
};
✨Dynamic Routing Beta (#9596) refs #9601 ### Dynamic Routing This is the beta version of dynamic routing. - we had a initial implementation of "channels" available in the codebase - we have removed and moved this implementation - there is now a centralised place for dynamic routing - server/services/routing - each routing component is represented by a router type e.g. collections, routes, static pages, taxonomies, rss, preview of posts - keep as much as possible logic of routing helpers, middlewares and controllers - ensure test coverage - connect all the things together - yaml file + validation - routing + routers - url service - sitemaps - url access - deeper implementation of yaml validations - e.g. hard require slashes - ensure routing hierarchy/order - e.g. you enable the subscriber app - you have a custom static page, which lives under the same slug /subscribe - static pages are stronger than apps - e.g. the first collection owns the post it has filtered - a post cannot live in two collections - ensure apps are still working and hook into the routers layer (or better said: and register in the routing service) - put as much as possible comments to the code base for better understanding - ensure a clean debug log - ensure we can unmount routes - e.g. you have a collection permalink of /:slug/ represented by {globals.permalink} - and you change the permalink in the admin to dated permalink - the express route get's refreshed from /:slug/ to /:year/:month/:day/:slug/ - unmount without server restart, yey - ensure we are backwards compatible - e.g. render home.hbs for collection index if collection route is / - ensure you can access your configured permalink from the settings table with {globals.permalink} ### Render 503 if url service did not finish - return 503 if the url service has not finished generating the resource urls ### Rewrite sitemaps - we have rewritten the sitemaps "service", because the url generator does no longer happen on runtime - we generate all urls on bootstrap - the sitemaps service will consume created resource and router urls - these urls will be shown on the xml pages - we listen on url events - we listen on router events - we no longer have to fetch the resources, which is nice - the urlservice pre-fetches resources and emits their urls - the urlservice is the only component who knows which urls are valid - i made some ES6 adaptions - we keep the caching logic -> only regenerate xml if there is a change - updated tests - checked test coverage (100%) ### Re-work usage of Url utility - replace all usages of `urlService.utils.urlFor` by `urlService.getByResourceId` - only for resources e.g. post, author, tag - this is important, because with dynamic routing we no longer create static urls based on the settings permalink on runtime - adapt url utility - adapt tests
2018-06-05 20:02:20 +03:00
return testUtils.mocks.express.invoke(app, req)
.then(function (response) {
response.statusCode.should.eql(404);
});
✨Dynamic Routing Beta (#9596) refs #9601 ### Dynamic Routing This is the beta version of dynamic routing. - we had a initial implementation of "channels" available in the codebase - we have removed and moved this implementation - there is now a centralised place for dynamic routing - server/services/routing - each routing component is represented by a router type e.g. collections, routes, static pages, taxonomies, rss, preview of posts - keep as much as possible logic of routing helpers, middlewares and controllers - ensure test coverage - connect all the things together - yaml file + validation - routing + routers - url service - sitemaps - url access - deeper implementation of yaml validations - e.g. hard require slashes - ensure routing hierarchy/order - e.g. you enable the subscriber app - you have a custom static page, which lives under the same slug /subscribe - static pages are stronger than apps - e.g. the first collection owns the post it has filtered - a post cannot live in two collections - ensure apps are still working and hook into the routers layer (or better said: and register in the routing service) - put as much as possible comments to the code base for better understanding - ensure a clean debug log - ensure we can unmount routes - e.g. you have a collection permalink of /:slug/ represented by {globals.permalink} - and you change the permalink in the admin to dated permalink - the express route get's refreshed from /:slug/ to /:year/:month/:day/:slug/ - unmount without server restart, yey - ensure we are backwards compatible - e.g. render home.hbs for collection index if collection route is / - ensure you can access your configured permalink from the settings table with {globals.permalink} ### Render 503 if url service did not finish - return 503 if the url service has not finished generating the resource urls ### Rewrite sitemaps - we have rewritten the sitemaps "service", because the url generator does no longer happen on runtime - we generate all urls on bootstrap - the sitemaps service will consume created resource and router urls - these urls will be shown on the xml pages - we listen on url events - we listen on router events - we no longer have to fetch the resources, which is nice - the urlservice pre-fetches resources and emits their urls - the urlservice is the only component who knows which urls are valid - i made some ES6 adaptions - we keep the caching logic -> only regenerate xml if there is a change - updated tests - checked test coverage (100%) ### Re-work usage of Url utility - replace all usages of `urlService.utils.urlFor` by `urlService.getByResourceId` - only for resources e.g. post, author, tag - this is important, because with dynamic routing we no longer create static urls based on the settings permalink on runtime - adapt url utility - adapt tests
2018-06-05 20:02:20 +03:00
});
it('serve /cooking/rss/', function () {
const req = {
secure: true,
method: 'GET',
url: '/cooking/rss/',
host: 'example.com'
};
✨Dynamic Routing Beta (#9596) refs #9601 ### Dynamic Routing This is the beta version of dynamic routing. - we had a initial implementation of "channels" available in the codebase - we have removed and moved this implementation - there is now a centralised place for dynamic routing - server/services/routing - each routing component is represented by a router type e.g. collections, routes, static pages, taxonomies, rss, preview of posts - keep as much as possible logic of routing helpers, middlewares and controllers - ensure test coverage - connect all the things together - yaml file + validation - routing + routers - url service - sitemaps - url access - deeper implementation of yaml validations - e.g. hard require slashes - ensure routing hierarchy/order - e.g. you enable the subscriber app - you have a custom static page, which lives under the same slug /subscribe - static pages are stronger than apps - e.g. the first collection owns the post it has filtered - a post cannot live in two collections - ensure apps are still working and hook into the routers layer (or better said: and register in the routing service) - put as much as possible comments to the code base for better understanding - ensure a clean debug log - ensure we can unmount routes - e.g. you have a collection permalink of /:slug/ represented by {globals.permalink} - and you change the permalink in the admin to dated permalink - the express route get's refreshed from /:slug/ to /:year/:month/:day/:slug/ - unmount without server restart, yey - ensure we are backwards compatible - e.g. render home.hbs for collection index if collection route is / - ensure you can access your configured permalink from the settings table with {globals.permalink} ### Render 503 if url service did not finish - return 503 if the url service has not finished generating the resource urls ### Rewrite sitemaps - we have rewritten the sitemaps "service", because the url generator does no longer happen on runtime - we generate all urls on bootstrap - the sitemaps service will consume created resource and router urls - these urls will be shown on the xml pages - we listen on url events - we listen on router events - we no longer have to fetch the resources, which is nice - the urlservice pre-fetches resources and emits their urls - the urlservice is the only component who knows which urls are valid - i made some ES6 adaptions - we keep the caching logic -> only regenerate xml if there is a change - updated tests - checked test coverage (100%) ### Re-work usage of Url utility - replace all usages of `urlService.utils.urlFor` by `urlService.getByResourceId` - only for resources e.g. post, author, tag - this is important, because with dynamic routing we no longer create static urls based on the settings permalink on runtime - adapt url utility - adapt tests
2018-06-05 20:02:20 +03:00
return testUtils.mocks.express.invoke(app, req)
.then(function (response) {
response.statusCode.should.eql(404);
});
});
✨Dynamic Routing Beta (#9596) refs #9601 ### Dynamic Routing This is the beta version of dynamic routing. - we had a initial implementation of "channels" available in the codebase - we have removed and moved this implementation - there is now a centralised place for dynamic routing - server/services/routing - each routing component is represented by a router type e.g. collections, routes, static pages, taxonomies, rss, preview of posts - keep as much as possible logic of routing helpers, middlewares and controllers - ensure test coverage - connect all the things together - yaml file + validation - routing + routers - url service - sitemaps - url access - deeper implementation of yaml validations - e.g. hard require slashes - ensure routing hierarchy/order - e.g. you enable the subscriber app - you have a custom static page, which lives under the same slug /subscribe - static pages are stronger than apps - e.g. the first collection owns the post it has filtered - a post cannot live in two collections - ensure apps are still working and hook into the routers layer (or better said: and register in the routing service) - put as much as possible comments to the code base for better understanding - ensure a clean debug log - ensure we can unmount routes - e.g. you have a collection permalink of /:slug/ represented by {globals.permalink} - and you change the permalink in the admin to dated permalink - the express route get's refreshed from /:slug/ to /:year/:month/:day/:slug/ - unmount without server restart, yey - ensure we are backwards compatible - e.g. render home.hbs for collection index if collection route is / - ensure you can access your configured permalink from the settings table with {globals.permalink} ### Render 503 if url service did not finish - return 503 if the url service has not finished generating the resource urls ### Rewrite sitemaps - we have rewritten the sitemaps "service", because the url generator does no longer happen on runtime - we generate all urls on bootstrap - the sitemaps service will consume created resource and router urls - these urls will be shown on the xml pages - we listen on url events - we listen on router events - we no longer have to fetch the resources, which is nice - the urlservice pre-fetches resources and emits their urls - the urlservice is the only component who knows which urls are valid - i made some ES6 adaptions - we keep the caching logic -> only regenerate xml if there is a change - updated tests - checked test coverage (100%) ### Re-work usage of Url utility - replace all usages of `urlService.utils.urlFor` by `urlService.getByResourceId` - only for resources e.g. post, author, tag - this is important, because with dynamic routing we no longer create static urls based on the settings permalink on runtime - adapt url utility - adapt tests
2018-06-05 20:02:20 +03:00
it('serve /flat/rss/', function () {
✨Dynamic Routing Beta (#9596) refs #9601 ### Dynamic Routing This is the beta version of dynamic routing. - we had a initial implementation of "channels" available in the codebase - we have removed and moved this implementation - there is now a centralised place for dynamic routing - server/services/routing - each routing component is represented by a router type e.g. collections, routes, static pages, taxonomies, rss, preview of posts - keep as much as possible logic of routing helpers, middlewares and controllers - ensure test coverage - connect all the things together - yaml file + validation - routing + routers - url service - sitemaps - url access - deeper implementation of yaml validations - e.g. hard require slashes - ensure routing hierarchy/order - e.g. you enable the subscriber app - you have a custom static page, which lives under the same slug /subscribe - static pages are stronger than apps - e.g. the first collection owns the post it has filtered - a post cannot live in two collections - ensure apps are still working and hook into the routers layer (or better said: and register in the routing service) - put as much as possible comments to the code base for better understanding - ensure a clean debug log - ensure we can unmount routes - e.g. you have a collection permalink of /:slug/ represented by {globals.permalink} - and you change the permalink in the admin to dated permalink - the express route get's refreshed from /:slug/ to /:year/:month/:day/:slug/ - unmount without server restart, yey - ensure we are backwards compatible - e.g. render home.hbs for collection index if collection route is / - ensure you can access your configured permalink from the settings table with {globals.permalink} ### Render 503 if url service did not finish - return 503 if the url service has not finished generating the resource urls ### Rewrite sitemaps - we have rewritten the sitemaps "service", because the url generator does no longer happen on runtime - we generate all urls on bootstrap - the sitemaps service will consume created resource and router urls - these urls will be shown on the xml pages - we listen on url events - we listen on router events - we no longer have to fetch the resources, which is nice - the urlservice pre-fetches resources and emits their urls - the urlservice is the only component who knows which urls are valid - i made some ES6 adaptions - we keep the caching logic -> only regenerate xml if there is a change - updated tests - checked test coverage (100%) ### Re-work usage of Url utility - replace all usages of `urlService.utils.urlFor` by `urlService.getByResourceId` - only for resources e.g. post, author, tag - this is important, because with dynamic routing we no longer create static urls based on the settings permalink on runtime - adapt url utility - adapt tests
2018-06-05 20:02:20 +03:00
const req = {
secure: true,
method: 'GET',
url: '/flat/rss/',
✨Dynamic Routing Beta (#9596) refs #9601 ### Dynamic Routing This is the beta version of dynamic routing. - we had a initial implementation of "channels" available in the codebase - we have removed and moved this implementation - there is now a centralised place for dynamic routing - server/services/routing - each routing component is represented by a router type e.g. collections, routes, static pages, taxonomies, rss, preview of posts - keep as much as possible logic of routing helpers, middlewares and controllers - ensure test coverage - connect all the things together - yaml file + validation - routing + routers - url service - sitemaps - url access - deeper implementation of yaml validations - e.g. hard require slashes - ensure routing hierarchy/order - e.g. you enable the subscriber app - you have a custom static page, which lives under the same slug /subscribe - static pages are stronger than apps - e.g. the first collection owns the post it has filtered - a post cannot live in two collections - ensure apps are still working and hook into the routers layer (or better said: and register in the routing service) - put as much as possible comments to the code base for better understanding - ensure a clean debug log - ensure we can unmount routes - e.g. you have a collection permalink of /:slug/ represented by {globals.permalink} - and you change the permalink in the admin to dated permalink - the express route get's refreshed from /:slug/ to /:year/:month/:day/:slug/ - unmount without server restart, yey - ensure we are backwards compatible - e.g. render home.hbs for collection index if collection route is / - ensure you can access your configured permalink from the settings table with {globals.permalink} ### Render 503 if url service did not finish - return 503 if the url service has not finished generating the resource urls ### Rewrite sitemaps - we have rewritten the sitemaps "service", because the url generator does no longer happen on runtime - we generate all urls on bootstrap - the sitemaps service will consume created resource and router urls - these urls will be shown on the xml pages - we listen on url events - we listen on router events - we no longer have to fetch the resources, which is nice - the urlservice pre-fetches resources and emits their urls - the urlservice is the only component who knows which urls are valid - i made some ES6 adaptions - we keep the caching logic -> only regenerate xml if there is a change - updated tests - checked test coverage (100%) ### Re-work usage of Url utility - replace all usages of `urlService.utils.urlFor` by `urlService.getByResourceId` - only for resources e.g. post, author, tag - this is important, because with dynamic routing we no longer create static urls based on the settings permalink on runtime - adapt url utility - adapt tests
2018-06-05 20:02:20 +03:00
host: 'example.com'
};
return testUtils.mocks.express.invoke(app, req)
.then(function (response) {
response.statusCode.should.eql(200);
});
});
it('serve /podcast/rss/', function () {
const req = {
secure: true,
method: 'GET',
url: '/podcast/rss/',
host: 'example.com'
};
return testUtils.mocks.express.invoke(app, req)
.then(function (response) {
response.statusCode.should.eql(200);
response.template.should.eql('podcast/rss');
response.headers['content-type'].should.eql('text/xml; charset=utf-8');
response.body.match(/<link>/g).length.should.eql(2);
});
});
it('serve /podcast/', function () {
const req = {
secure: true,
method: 'GET',
url: '/podcast/',
host: 'example.com'
};
return testUtils.mocks.express.invoke(app, req)
.then(function (response) {
const $ = cheerio.load(response.body);
response.statusCode.should.eql(200);
$('head link')[2].attribs.href.should.eql('https://127.0.0.1:2369/rss/');
});
});
});
});
describe('parent app vhosts', function () {
describe('no separate admin', function () {
before(function () {
testUtils.integrationTesting.urlService.resetGenerators();
testUtils.integrationTesting.defaultMocks(sinon, {amp: true, apps: true});
testUtils.integrationTesting.overrideGhostConfig(configUtils);
configUtils.set('url', 'http://example.com');
configUtils.set('admin:url', null);
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.urlService.waitTillFinished();
})
.then(() => {
return appsService.init();
});
});
before(function () {
configUtils.set('url', 'http://example.com');
configUtils.set('admin:url', null);
urlUtils.stubUrlUtilsFromConfig();
});
after(function () {
configUtils.restore();
urlUtils.restore();
sinon.restore();
});
it('loads the front-end on configured url', function () {
const req = {
secure: false,
method: 'GET',
url: '/',
host: 'example.com'
};
return testUtils.mocks.express.invoke(app, req)
.then(function (response) {
response.statusCode.should.eql(200);
});
});
it('loads the front-end on localhost', function () {
const req = {
secure: false,
method: 'GET',
url: '/',
host: 'localhost'
};
return testUtils.mocks.express.invoke(app, req)
.then(function (response) {
response.statusCode.should.eql(200);
});
});
it('loads the admin', function () {
const req = {
secure: false,
method: 'GET',
url: '/ghost/',
host: 'example.com'
};
return testUtils.mocks.express.invoke(app, req)
.then(function (response) {
response.statusCode.should.eql(200);
});
});
it('loads the admin on localhost', function () {
const req = {
secure: false,
method: 'GET',
url: '/ghost/',
host: 'localhost'
};
return testUtils.mocks.express.invoke(app, req)
.then(function (response) {
response.statusCode.should.eql(200);
});
});
it('loads the api', function () {
const req = {
secure: false,
method: 'GET',
url: '/ghost/api/v2/admin/site/',
host: 'example.com'
};
return testUtils.mocks.express.invoke(app, req)
.then(function (response) {
response.statusCode.should.eql(200);
});
});
it('loads the api on localhost', function () {
const req = {
secure: false,
method: 'GET',
url: '/ghost/api/v2/admin/site/',
host: 'localhost'
};
return testUtils.mocks.express.invoke(app, req)
.then(function (response) {
response.statusCode.should.eql(200);
});
});
});
describe('separate admin host', function () {
before(function () {
testUtils.integrationTesting.urlService.resetGenerators();
testUtils.integrationTesting.defaultMocks(sinon, {amp: true, apps: true});
testUtils.integrationTesting.overrideGhostConfig(configUtils);
configUtils.set('url', 'http://example.com');
configUtils.set('admin:url', 'https://admin.example.com');
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.urlService.waitTillFinished();
})
.then(() => {
return appsService.init();
});
});
before(function () {
urlUtils.stubUrlUtilsFromConfig();
});
after(function () {
configUtils.restore();
urlUtils.restore();
sinon.restore();
});
it('loads the front-end on configured url', function () {
const req = {
secure: false,
method: 'GET',
url: '/',
host: 'example.com'
};
return testUtils.mocks.express.invoke(app, req)
.then(function (response) {
response.statusCode.should.eql(200);
});
});
it('loads the front-end on localhost', function () {
const req = {
secure: false,
method: 'GET',
url: '/',
host: 'localhost'
};
return testUtils.mocks.express.invoke(app, req)
.then(function (response) {
response.statusCode.should.eql(200);
});
});
it('redirects /ghost/ on configured url', function () {
const req = {
secure: false,
method: 'GET',
url: '/ghost/',
host: 'example.com'
};
return testUtils.mocks.express.invoke(app, req)
.then(function (response) {
response.statusCode.should.eql(301);
response.headers.location.should.eql('https://admin.example.com/ghost/');
});
});
it('404s the api on configured url', function () {
const req = {
secure: false,
method: 'GET',
url: '/ghost/api/v2/admin/site/',
host: 'example.com'
};
return testUtils.mocks.express.invoke(app, req)
.then(function (response) {
response.statusCode.should.eql(404);
});
});
it('404s the api on localhost', function () {
const req = {
secure: false,
method: 'GET',
url: '/ghost/api/v2/admin/site/',
host: 'localhost'
};
return testUtils.mocks.express.invoke(app, req)
.then(function (response) {
response.statusCode.should.eql(404);
});
});
it('loads the admin on configured admin url', function () {
const req = {
secure: true,
method: 'GET',
url: '/ghost/',
host: 'admin.example.com'
};
return testUtils.mocks.express.invoke(app, req)
.then(function (response) {
response.statusCode.should.eql(200);
});
});
it('loads the api on configured admin url', function () {
const req = {
secure: true,
method: 'GET',
url: '/ghost/api/v2/admin/site/',
host: 'admin.example.com'
};
return testUtils.mocks.express.invoke(app, req)
.then(function (response) {
response.statusCode.should.eql(200);
});
});
it('redirects to the correct protocol on configured admin url', function () {
const req = {
secure: false,
method: 'GET',
url: '/ghost/',
host: 'admin.example.com'
};
return testUtils.mocks.express.invoke(app, req)
.then(function (response) {
response.statusCode.should.eql(301);
response.headers.location.should.eql('https://admin.example.com/ghost/');
});
});
it('404s the front-end on configured admin url', function () {
const req = {
secure: false,
method: 'GET',
url: '/',
host: 'admin.example.com'
};
return testUtils.mocks.express.invoke(app, req)
.then(function (response) {
response.statusCode.should.eql(404);
});
});
});
describe('separate admin host w/ admin redirects disabled', function () {
before(function () {
testUtils.integrationTesting.urlService.resetGenerators();
testUtils.integrationTesting.defaultMocks(sinon, {amp: true, apps: true});
testUtils.integrationTesting.overrideGhostConfig(configUtils);
configUtils.set('url', 'http://example.com');
configUtils.set('admin:url', 'https://admin.example.com');
configUtils.set('admin:redirects', false);
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.urlService.waitTillFinished();
})
.then(() => {
return appsService.init();
});
});
before(function () {
urlUtils.stubUrlUtilsFromConfig();
});
after(function () {
configUtils.restore();
urlUtils.restore();
sinon.restore();
});
it('does not redirect /ghost/ on configured url', function () {
const req = {
secure: false,
method: 'GET',
url: '/ghost/',
host: 'example.com'
};
return testUtils.mocks.express.invoke(app, req)
.then(function (response) {
response.statusCode.should.eql(404);
});
});
});
describe('same host separate protocol', function () {
before(function () {
testUtils.integrationTesting.urlService.resetGenerators();
testUtils.integrationTesting.defaultMocks(sinon, {amp: true, apps: true});
testUtils.integrationTesting.overrideGhostConfig(configUtils);
configUtils.set('url', 'http://example.com');
configUtils.set('admin:url', 'https://example.com');
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.urlService.waitTillFinished();
})
.then(() => {
return appsService.init();
});
});
before(function () {
urlUtils.stubUrlUtilsFromConfig();
});
it('loads the front-end on configured url (http)', function () {
const req = {
secure: false,
method: 'GET',
url: '/',
host: 'example.com'
};
return testUtils.mocks.express.invoke(app, req)
.then(function (response) {
response.statusCode.should.eql(200);
});
});
it('404s the front-end on configured url (https)', function () {
const req = {
secure: true,
method: 'GET',
url: '/',
host: 'example.com'
};
return testUtils.mocks.express.invoke(app, req)
.then(function (response) {
response.statusCode.should.eql(200);
});
});
it('loads the front-end on localhost', function () {
const req = {
secure: false,
method: 'GET',
url: '/',
host: 'localhost'
};
return testUtils.mocks.express.invoke(app, req)
.then(function (response) {
response.statusCode.should.eql(200);
});
});
it('redirects /ghost/ on configured url', function () {
const req = {
secure: false,
method: 'GET',
url: '/ghost/',
host: 'example.com'
};
return testUtils.mocks.express.invoke(app, req)
.then(function (response) {
response.statusCode.should.eql(301);
response.headers.location.should.eql('https://example.com/ghost/');
});
});
it('redirects /ghost/ on localhost', function () {
const req = {
secure: false,
method: 'GET',
url: '/ghost/',
host: 'localhost'
};
return testUtils.mocks.express.invoke(app, req)
.then(function (response) {
response.statusCode.should.eql(301);
response.headers.location.should.eql('https://example.com/ghost/');
});
});
it('redirects api to correct protocol on configured admin url', function () {
const req = {
secure: false,
method: 'GET',
url: '/ghost/api/v2/admin/site/',
host: 'example.com'
};
return testUtils.mocks.express.invoke(app, req)
.then(function (response) {
response.statusCode.should.eql(301);
response.headers.location.should.eql('https://example.com/ghost/api/v2/admin/site/');
});
});
it('loads the admin on configured admin url', function () {
const req = {
secure: true,
method: 'GET',
url: '/ghost/',
host: 'example.com'
};
return testUtils.mocks.express.invoke(app, req)
.then(function (response) {
response.statusCode.should.eql(200);
});
});
it('redirects the admin on localhost', function () {
const req = {
secure: false,
method: 'GET',
url: '/ghost/',
host: 'localhost'
};
return testUtils.mocks.express.invoke(app, req)
.then(function (response) {
response.statusCode.should.eql(301);
response.headers.location.should.eql('https://example.com/ghost/');
});
});
it('loads the api on configured admin url', function () {
const req = {
secure: true,
method: 'GET',
url: '/ghost/api/v2/admin/site/',
host: 'example.com'
};
return testUtils.mocks.express.invoke(app, req)
.then(function (response) {
response.statusCode.should.eql(200);
});
});
it('redirects the api on localhost', function () {
const req = {
secure: false,
method: 'GET',
url: '/ghost/api/v2/admin/site/',
host: 'localhost'
};
return testUtils.mocks.express.invoke(app, req)
.then(function (response) {
response.statusCode.should.eql(301);
response.headers.location.should.eql('https://example.com/ghost/api/v2/admin/site/');
});
});
});
});
});