2020-04-29 18:44:27 +03:00
|
|
|
const should = require('should');
|
|
|
|
const sinon = require('sinon');
|
2020-05-25 11:49:38 +03:00
|
|
|
const {events} = require('../../../../core/server/lib/common');
|
2020-04-29 18:44:27 +03:00
|
|
|
const controllers = require('../../../../core/frontend/services/routing/controllers');
|
|
|
|
const StaticRoutesRouter = require('../../../../core/frontend/services/routing/StaticRoutesRouter');
|
|
|
|
const configUtils = require('../../../utils/configUtils');
|
2018-06-21 16:46:42 +03:00
|
|
|
|
|
|
|
describe('UNIT - services/routing/StaticRoutesRouter', function () {
|
2020-04-29 18:44:27 +03:00
|
|
|
let req;
|
|
|
|
let res;
|
|
|
|
let next;
|
2018-06-21 16:46:42 +03:00
|
|
|
|
|
|
|
afterEach(function () {
|
|
|
|
configUtils.restore();
|
|
|
|
});
|
|
|
|
|
|
|
|
beforeEach(function () {
|
2020-05-25 11:49:38 +03:00
|
|
|
sinon.stub(events, 'emit');
|
|
|
|
sinon.stub(events, 'on');
|
2018-06-21 16:46:42 +03:00
|
|
|
|
2019-01-21 19:53:44 +03:00
|
|
|
sinon.spy(StaticRoutesRouter.prototype, 'mountRoute');
|
|
|
|
sinon.spy(StaticRoutesRouter.prototype, 'mountRouter');
|
2018-06-21 16:46:42 +03:00
|
|
|
|
2019-01-21 19:53:44 +03:00
|
|
|
req = sinon.stub();
|
|
|
|
res = sinon.stub();
|
|
|
|
next = sinon.stub();
|
2018-06-21 16:46:42 +03:00
|
|
|
|
|
|
|
res.locals = {};
|
|
|
|
});
|
|
|
|
|
|
|
|
afterEach(function () {
|
2019-01-21 19:53:44 +03:00
|
|
|
sinon.restore();
|
2018-06-21 16:46:42 +03:00
|
|
|
});
|
|
|
|
|
|
|
|
describe('static routes', function () {
|
|
|
|
it('instantiate: default', function () {
|
|
|
|
const staticRoutesRouter = new StaticRoutesRouter('/about/', {templates: ['test']});
|
|
|
|
should.exist(staticRoutesRouter.router);
|
|
|
|
|
|
|
|
should.not.exist(staticRoutesRouter.getFilter());
|
|
|
|
should.not.exist(staticRoutesRouter.getPermalinks());
|
|
|
|
|
|
|
|
staticRoutesRouter.templates.should.eql(['test']);
|
|
|
|
|
2020-05-25 11:49:38 +03:00
|
|
|
events.emit.calledOnce.should.be.true();
|
|
|
|
events.emit.calledWith('router.created', staticRoutesRouter).should.be.true();
|
2018-06-21 16:46:42 +03:00
|
|
|
|
|
|
|
staticRoutesRouter.mountRoute.callCount.should.eql(1);
|
|
|
|
|
|
|
|
// parent route
|
|
|
|
staticRoutesRouter.mountRoute.args[0][0].should.eql('/about/');
|
2018-06-24 01:32:08 +03:00
|
|
|
staticRoutesRouter.mountRoute.args[0][1].should.eql(controllers.static);
|
2018-06-21 16:46:42 +03:00
|
|
|
});
|
|
|
|
|
2018-06-24 01:32:08 +03:00
|
|
|
it('initialise with data+filter', function () {
|
|
|
|
const staticRoutesRouter = new StaticRoutesRouter('/about/', {
|
|
|
|
data: {query: {}, router: {}},
|
|
|
|
filter: 'tag:test'
|
|
|
|
});
|
|
|
|
|
|
|
|
should.exist(staticRoutesRouter.router);
|
|
|
|
|
|
|
|
should.not.exist(staticRoutesRouter.getPermalinks());
|
|
|
|
should.not.exist(staticRoutesRouter.getFilter());
|
|
|
|
staticRoutesRouter.templates.should.eql([]);
|
|
|
|
|
2020-05-25 11:49:38 +03:00
|
|
|
events.emit.calledOnce.should.be.true();
|
|
|
|
events.emit.calledWith('router.created', staticRoutesRouter).should.be.true();
|
2018-06-24 01:32:08 +03:00
|
|
|
|
|
|
|
staticRoutesRouter.mountRoute.callCount.should.eql(1);
|
|
|
|
|
|
|
|
// parent route
|
|
|
|
staticRoutesRouter.mountRoute.args[0][0].should.eql('/about/');
|
|
|
|
staticRoutesRouter.mountRoute.args[0][1].should.eql(controllers.static);
|
|
|
|
});
|
|
|
|
|
|
|
|
it('fn: _prepareStaticRouteContext', function () {
|
2018-06-21 16:46:42 +03:00
|
|
|
const staticRoutesRouter = new StaticRoutesRouter('/about/', {templates: []});
|
|
|
|
|
2018-06-24 01:32:08 +03:00
|
|
|
staticRoutesRouter._prepareStaticRouteContext(req, res, next);
|
2018-06-21 16:46:42 +03:00
|
|
|
next.called.should.be.true();
|
💡Changed static router - throw 400 for missing tpl
fixes #10990
- Changed the static router to throw a 400 error for a missing template file, rather than falling back to using the default.hbs file
- Falling back is weird and hard to understand, but throwing an error makes it clear that the user has to provide the matching template
- The new error reads 'Missing template [filename].hbs for route "[route]".'
Assume you have a route.yaml file something like:
```
routes:
/: home
```
- In Ghost v2, if you don't have a home.hbs template, Ghost falls back to using the default.hbs file if it's available
- Most themes have a default.hbs, however this file is a layout file, depended on by other templates, not a template file itself
- In production mode, using the default.hbs as a template causes weird, intermittent layout issues depending on which order pages are loaded
- This is due to this issue: https://github.com/barc/express-hbs/issues/161
- In Ghost v3, we will throw a 400 error for missing template files instead of having a fallback
- In the example above, navigating to '/' would throw the error 'Missing template home.hbs for route "/".'
2019-08-05 21:34:12 +03:00
|
|
|
|
|
|
|
res.routerOptions.should.have.properties('type', 'templates', 'defaultTemplate', 'context', 'data', 'contentType');
|
|
|
|
res.routerOptions.type.should.eql('custom');
|
|
|
|
res.routerOptions.templates.should.eql([]);
|
|
|
|
res.routerOptions.defaultTemplate.should.be.a.Function();
|
|
|
|
res.routerOptions.context.should.eql(['about']);
|
|
|
|
res.routerOptions.data.should.eql({});
|
|
|
|
|
|
|
|
should(res.routerOptions.contentType).be.undefined();
|
2018-06-26 20:04:39 +03:00
|
|
|
should.not.exist(res.locals.slug);
|
|
|
|
});
|
|
|
|
|
|
|
|
it('fn: _prepareStaticRouteContext', function () {
|
|
|
|
const staticRoutesRouter = new StaticRoutesRouter('/', {templates: []});
|
|
|
|
|
|
|
|
staticRoutesRouter._prepareStaticRouteContext(req, res, next);
|
|
|
|
next.called.should.be.true();
|
💡Changed static router - throw 400 for missing tpl
fixes #10990
- Changed the static router to throw a 400 error for a missing template file, rather than falling back to using the default.hbs file
- Falling back is weird and hard to understand, but throwing an error makes it clear that the user has to provide the matching template
- The new error reads 'Missing template [filename].hbs for route "[route]".'
Assume you have a route.yaml file something like:
```
routes:
/: home
```
- In Ghost v2, if you don't have a home.hbs template, Ghost falls back to using the default.hbs file if it's available
- Most themes have a default.hbs, however this file is a layout file, depended on by other templates, not a template file itself
- In production mode, using the default.hbs as a template causes weird, intermittent layout issues depending on which order pages are loaded
- This is due to this issue: https://github.com/barc/express-hbs/issues/161
- In Ghost v3, we will throw a 400 error for missing template files instead of having a fallback
- In the example above, navigating to '/' would throw the error 'Missing template home.hbs for route "/".'
2019-08-05 21:34:12 +03:00
|
|
|
|
|
|
|
res.routerOptions.should.have.properties('type', 'templates', 'defaultTemplate', 'context', 'data', 'contentType');
|
|
|
|
res.routerOptions.type.should.eql('custom');
|
|
|
|
res.routerOptions.templates.should.eql([]);
|
|
|
|
res.routerOptions.defaultTemplate.should.be.a.Function();
|
|
|
|
res.routerOptions.context.should.eql(['index']);
|
|
|
|
res.routerOptions.data.should.eql({});
|
|
|
|
|
2018-06-24 01:32:08 +03:00
|
|
|
should.not.exist(res.locals.slug);
|
|
|
|
});
|
|
|
|
});
|
|
|
|
|
|
|
|
describe('channels', function () {
|
|
|
|
describe('initialise', function () {
|
|
|
|
it('initialise with controller+data+filter', function () {
|
|
|
|
const staticRoutesRouter = new StaticRoutesRouter('/channel/', {
|
|
|
|
controller: 'channel',
|
|
|
|
data: {query: {}, router: {}},
|
|
|
|
filter: 'tag:test'
|
|
|
|
});
|
|
|
|
|
|
|
|
should.exist(staticRoutesRouter.router);
|
|
|
|
|
|
|
|
should.not.exist(staticRoutesRouter.getPermalinks());
|
|
|
|
staticRoutesRouter.getFilter().should.eql('tag:test');
|
|
|
|
staticRoutesRouter.templates.should.eql([]);
|
|
|
|
should.exist(staticRoutesRouter.data);
|
|
|
|
|
2020-05-25 11:49:38 +03:00
|
|
|
events.emit.calledOnce.should.be.true();
|
|
|
|
events.emit.calledWith('router.created', staticRoutesRouter).should.be.true();
|
2018-06-24 01:32:08 +03:00
|
|
|
|
|
|
|
staticRoutesRouter.mountRoute.callCount.should.eql(2);
|
|
|
|
|
|
|
|
// parent route
|
|
|
|
staticRoutesRouter.mountRoute.args[0][0].should.eql('/channel/');
|
|
|
|
staticRoutesRouter.mountRoute.args[0][1].should.eql(controllers.channel);
|
|
|
|
|
|
|
|
// pagination feature
|
|
|
|
staticRoutesRouter.mountRoute.args[1][0].should.eql('/channel/page/:page(\\d+)');
|
|
|
|
staticRoutesRouter.mountRoute.args[1][1].should.eql(controllers.channel);
|
|
|
|
});
|
|
|
|
|
|
|
|
it('initialise with controller+filter', function () {
|
|
|
|
const staticRoutesRouter = new StaticRoutesRouter('/channel/', {
|
|
|
|
controller: 'channel',
|
|
|
|
filter: 'tag:test'
|
|
|
|
});
|
|
|
|
|
|
|
|
should.exist(staticRoutesRouter.router);
|
|
|
|
|
|
|
|
should.not.exist(staticRoutesRouter.getPermalinks());
|
|
|
|
staticRoutesRouter.getFilter().should.eql('tag:test');
|
|
|
|
|
|
|
|
staticRoutesRouter.templates.should.eql([]);
|
|
|
|
|
2020-05-25 11:49:38 +03:00
|
|
|
events.emit.calledOnce.should.be.true();
|
|
|
|
events.emit.calledWith('router.created', staticRoutesRouter).should.be.true();
|
2018-06-24 01:32:08 +03:00
|
|
|
|
|
|
|
staticRoutesRouter.mountRoute.callCount.should.eql(2);
|
|
|
|
|
|
|
|
// parent route
|
|
|
|
staticRoutesRouter.mountRoute.args[0][0].should.eql('/channel/');
|
|
|
|
staticRoutesRouter.mountRoute.args[0][1].should.eql(controllers.channel);
|
|
|
|
|
|
|
|
// pagination feature
|
|
|
|
staticRoutesRouter.mountRoute.args[1][0].should.eql('/channel/page/:page(\\d+)');
|
|
|
|
staticRoutesRouter.mountRoute.args[1][1].should.eql(controllers.channel);
|
|
|
|
});
|
|
|
|
|
|
|
|
it('initialise with controller+data', function () {
|
|
|
|
const staticRoutesRouter = new StaticRoutesRouter('/channel/', {
|
|
|
|
controller: 'channel',
|
2019-07-05 14:40:43 +03:00
|
|
|
data: {query: {}, router: {}}
|
2018-06-24 01:32:08 +03:00
|
|
|
});
|
|
|
|
|
|
|
|
should.not.exist(staticRoutesRouter.getFilter());
|
|
|
|
});
|
|
|
|
|
|
|
|
it('initialise on subdirectory with controller+data+filter', function () {
|
|
|
|
configUtils.set('url', 'http://localhost:2366/blog/');
|
|
|
|
|
|
|
|
const staticRoutesRouter = new StaticRoutesRouter('/channel/', {
|
|
|
|
controller: 'channel',
|
|
|
|
data: {query: {}, router: {}},
|
|
|
|
filter: 'author:michi'
|
|
|
|
});
|
|
|
|
|
|
|
|
staticRoutesRouter.mountRoute.callCount.should.eql(2);
|
|
|
|
|
|
|
|
// parent route
|
|
|
|
staticRoutesRouter.mountRoute.args[0][0].should.eql('/channel/');
|
|
|
|
staticRoutesRouter.mountRoute.args[0][1].should.eql(controllers.channel);
|
|
|
|
|
|
|
|
// pagination feature
|
|
|
|
staticRoutesRouter.mountRoute.args[1][0].should.eql('/channel/page/:page(\\d+)');
|
|
|
|
staticRoutesRouter.mountRoute.args[1][1].should.eql(controllers.channel);
|
|
|
|
});
|
|
|
|
});
|
|
|
|
|
|
|
|
describe('fn: _prepareChannelContext', function () {
|
|
|
|
it('with data+filter', function () {
|
|
|
|
const staticRoutesRouter = new StaticRoutesRouter('/channel/', {
|
|
|
|
controller: 'channel',
|
|
|
|
data: {query: {}, router: {}},
|
|
|
|
filter: 'tag:test'
|
|
|
|
});
|
|
|
|
|
|
|
|
staticRoutesRouter._prepareChannelContext(req, res, next);
|
|
|
|
next.calledOnce.should.eql(true);
|
2018-06-26 02:12:50 +03:00
|
|
|
res.routerOptions.should.eql({
|
|
|
|
type: 'channel',
|
2018-06-24 01:32:08 +03:00
|
|
|
context: ['channel'],
|
|
|
|
filter: 'tag:test',
|
|
|
|
name: 'channel',
|
|
|
|
data: {},
|
|
|
|
limit: undefined,
|
|
|
|
order: undefined,
|
|
|
|
templates: []
|
|
|
|
});
|
|
|
|
});
|
|
|
|
|
|
|
|
it('with data', function () {
|
|
|
|
const staticRoutesRouter = new StaticRoutesRouter('/nothingcomparestoyou/', {
|
|
|
|
controller: 'channel',
|
|
|
|
data: {query: {type: 'read'}, router: {}}
|
|
|
|
});
|
|
|
|
|
|
|
|
staticRoutesRouter._prepareChannelContext(req, res, next);
|
|
|
|
next.calledOnce.should.eql(true);
|
2018-06-26 02:12:50 +03:00
|
|
|
res.routerOptions.should.eql({
|
|
|
|
type: 'channel',
|
2018-06-24 01:32:08 +03:00
|
|
|
context: ['nothingcomparestoyou'],
|
|
|
|
name: 'nothingcomparestoyou',
|
|
|
|
filter: undefined,
|
|
|
|
data: {type: 'read'},
|
|
|
|
limit: undefined,
|
|
|
|
order: undefined,
|
|
|
|
templates: []
|
|
|
|
});
|
|
|
|
});
|
|
|
|
|
|
|
|
it('with filter', function () {
|
|
|
|
const staticRoutesRouter = new StaticRoutesRouter('/channel/', {
|
|
|
|
controller: 'channel',
|
|
|
|
filter: 'tag:test'
|
|
|
|
});
|
|
|
|
|
|
|
|
staticRoutesRouter._prepareChannelContext(req, res, next);
|
|
|
|
next.calledOnce.should.eql(true);
|
2018-06-26 02:12:50 +03:00
|
|
|
res.routerOptions.should.eql({
|
|
|
|
type: 'channel',
|
2018-06-24 01:32:08 +03:00
|
|
|
context: ['channel'],
|
|
|
|
filter: 'tag:test',
|
|
|
|
name: 'channel',
|
|
|
|
limit: undefined,
|
|
|
|
order: undefined,
|
|
|
|
data: {},
|
|
|
|
templates: []
|
|
|
|
});
|
|
|
|
});
|
|
|
|
|
|
|
|
it('with order+limit', function () {
|
|
|
|
const staticRoutesRouter = new StaticRoutesRouter('/channel/', {
|
|
|
|
controller: 'channel',
|
|
|
|
filter: 'tag:test',
|
|
|
|
limit: 2,
|
|
|
|
order: 'published_at asc'
|
|
|
|
});
|
|
|
|
|
|
|
|
staticRoutesRouter._prepareChannelContext(req, res, next);
|
|
|
|
next.calledOnce.should.eql(true);
|
2018-06-26 02:12:50 +03:00
|
|
|
res.routerOptions.should.eql({
|
|
|
|
type: 'channel',
|
2018-06-24 01:32:08 +03:00
|
|
|
context: ['channel'],
|
|
|
|
filter: 'tag:test',
|
|
|
|
name: 'channel',
|
|
|
|
limit: 2,
|
|
|
|
order: 'published_at asc',
|
|
|
|
data: {},
|
|
|
|
templates: []
|
|
|
|
});
|
|
|
|
});
|
2018-06-21 16:46:42 +03:00
|
|
|
});
|
|
|
|
});
|
|
|
|
});
|