✨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
|
|
|
// # Dynamic Routing Tests
|
2016-02-09 17:14:24 +03:00
|
|
|
// As it stands, these tests depend on the database, and as such are integration tests.
|
|
|
|
// These tests are here to cover the headers sent with requests and high-level redirects that can't be
|
|
|
|
// tested with the unit tests
|
2019-09-12 14:40:12 +03:00
|
|
|
const should = require('should');
|
|
|
|
const supertest = require('supertest');
|
|
|
|
const sinon = require('sinon');
|
|
|
|
const moment = require('moment');
|
|
|
|
const path = require('path');
|
|
|
|
const testUtils = require('../../utils');
|
|
|
|
const configUtils = require('../../utils/configUtils');
|
|
|
|
const cheerio = require('cheerio');
|
2020-05-27 20:47:53 +03:00
|
|
|
const config = require('../../../core/shared/config');
|
2020-03-30 18:26:47 +03:00
|
|
|
const api = require('../../../core/server/api');
|
|
|
|
const settingsCache = require('../../../core/server/services/settings/cache');
|
2019-09-12 14:40:12 +03:00
|
|
|
const ghost = testUtils.startGhost;
|
2016-02-09 17:14:24 +03:00
|
|
|
|
✨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
|
|
|
let request;
|
|
|
|
|
|
|
|
describe('Dynamic Routing', function () {
|
2018-08-07 12:49:51 +03:00
|
|
|
let ghostServer;
|
🔥✨ remove forceAdminSSL and urlSSL, add admin url (#7937)
* 🔥 kill apiUrl helper, use urlFor helper instead
More consistency of creating urls.
Creates an easier ability to add config changes.
Attention: urlFor function is getting a little nesty, BUT that is for now wanted to make easier and centralised changes to the configs.
The url util need's refactoring anyway.
* 🔥 urlSSL
Remove all urlSSL usages.
Add TODO's for the next commit to re-add logic for deleted logic.
e.g.
- cors helper generated an array of url's to allow requests from the defined config url's -> will be replaced by the admin url if available
- theme handler prefered the urlSSL in case it was defined -> will be replaced by using the urlFor helper to get the blog url (based on the request secure flag)
The changes in this commit doesn't have to be right, but it helped going step by step.
The next commit is the more interesting one.
* 🔥 ✨ remove forceAdminSSL, add new admin url and adapt logic
I wanted to remove the forceAdminSSL as separate commit, but was hard to realise.
That's why both changes are in one commit:
1. remove forceAdminSSL
2. add admin.url option
- fix TODO's from last commits
- rewrite the ssl middleware!
- create some private helper functions in the url helper to realise the changes
- rename some wordings and functions e.g. base === blog (we have so much different wordings)
- i would like to do more, but this would end in a non readable PR
- this commit contains the most important changes to offer admin.url option
* 🤖 adapt tests
IMPORTANT
- all changes in the routing tests were needed, because each routing test did not start the ghost server
- they just required the ghost application, which resulted in a random server port
- having a random server port results in a redirect, caused by the ssl/redirect middleware
* 😎 rename check-ssl middleware
* 🎨 fix theme-handler because of master rebase
2017-02-03 21:13:22 +03:00
|
|
|
|
2016-02-09 17:14:24 +03:00
|
|
|
function doEnd(done) {
|
|
|
|
return function (err, res) {
|
|
|
|
if (err) {
|
|
|
|
return done(err);
|
|
|
|
}
|
|
|
|
|
|
|
|
should.not.exist(res.headers['x-cache-invalidate']);
|
|
|
|
should.not.exist(res.headers['X-CSRF-Token']);
|
|
|
|
should.not.exist(res.headers['set-cookie']);
|
|
|
|
should.exist(res.headers.date);
|
|
|
|
|
|
|
|
done();
|
|
|
|
};
|
|
|
|
}
|
|
|
|
|
2017-11-29 14:48:05 +03:00
|
|
|
before(function () {
|
2017-09-21 17:05:35 +03:00
|
|
|
// Default is always casper. We use the old compatible 1.4 casper theme for these tests. Available in the test content folder.
|
2020-04-29 18:44:27 +03:00
|
|
|
const originalSettingsCacheGetFn = settingsCache.get;
|
2019-01-21 19:53:44 +03:00
|
|
|
sinon.stub(settingsCache, 'get').callsFake(function (key, options) {
|
2017-09-21 17:05:35 +03:00
|
|
|
if (key === 'active_theme') {
|
|
|
|
return 'casper-1.4';
|
|
|
|
}
|
|
|
|
|
|
|
|
return originalSettingsCacheGetFn(key, options);
|
|
|
|
});
|
|
|
|
|
2017-11-29 14:48:05 +03:00
|
|
|
return ghost()
|
|
|
|
.then(function (_ghostServer) {
|
|
|
|
ghostServer = _ghostServer;
|
|
|
|
request = supertest.agent(config.get('url'));
|
|
|
|
});
|
2016-02-09 17:14:24 +03:00
|
|
|
});
|
|
|
|
|
🔥✨ remove forceAdminSSL and urlSSL, add admin url (#7937)
* 🔥 kill apiUrl helper, use urlFor helper instead
More consistency of creating urls.
Creates an easier ability to add config changes.
Attention: urlFor function is getting a little nesty, BUT that is for now wanted to make easier and centralised changes to the configs.
The url util need's refactoring anyway.
* 🔥 urlSSL
Remove all urlSSL usages.
Add TODO's for the next commit to re-add logic for deleted logic.
e.g.
- cors helper generated an array of url's to allow requests from the defined config url's -> will be replaced by the admin url if available
- theme handler prefered the urlSSL in case it was defined -> will be replaced by using the urlFor helper to get the blog url (based on the request secure flag)
The changes in this commit doesn't have to be right, but it helped going step by step.
The next commit is the more interesting one.
* 🔥 ✨ remove forceAdminSSL, add new admin url and adapt logic
I wanted to remove the forceAdminSSL as separate commit, but was hard to realise.
That's why both changes are in one commit:
1. remove forceAdminSSL
2. add admin.url option
- fix TODO's from last commits
- rewrite the ssl middleware!
- create some private helper functions in the url helper to realise the changes
- rename some wordings and functions e.g. base === blog (we have so much different wordings)
- i would like to do more, but this would end in a non readable PR
- this commit contains the most important changes to offer admin.url option
* 🤖 adapt tests
IMPORTANT
- all changes in the routing tests were needed, because each routing test did not start the ghost server
- they just required the ghost application, which resulted in a random server port
- having a random server port results in a redirect, caused by the ssl/redirect middleware
* 😎 rename check-ssl middleware
* 🎨 fix theme-handler because of master rebase
2017-02-03 21:13:22 +03:00
|
|
|
after(function () {
|
2019-01-21 19:53:44 +03:00
|
|
|
sinon.restore();
|
🔥✨ remove forceAdminSSL and urlSSL, add admin url (#7937)
* 🔥 kill apiUrl helper, use urlFor helper instead
More consistency of creating urls.
Creates an easier ability to add config changes.
Attention: urlFor function is getting a little nesty, BUT that is for now wanted to make easier and centralised changes to the configs.
The url util need's refactoring anyway.
* 🔥 urlSSL
Remove all urlSSL usages.
Add TODO's for the next commit to re-add logic for deleted logic.
e.g.
- cors helper generated an array of url's to allow requests from the defined config url's -> will be replaced by the admin url if available
- theme handler prefered the urlSSL in case it was defined -> will be replaced by using the urlFor helper to get the blog url (based on the request secure flag)
The changes in this commit doesn't have to be right, but it helped going step by step.
The next commit is the more interesting one.
* 🔥 ✨ remove forceAdminSSL, add new admin url and adapt logic
I wanted to remove the forceAdminSSL as separate commit, but was hard to realise.
That's why both changes are in one commit:
1. remove forceAdminSSL
2. add admin.url option
- fix TODO's from last commits
- rewrite the ssl middleware!
- create some private helper functions in the url helper to realise the changes
- rename some wordings and functions e.g. base === blog (we have so much different wordings)
- i would like to do more, but this would end in a non readable PR
- this commit contains the most important changes to offer admin.url option
* 🤖 adapt tests
IMPORTANT
- all changes in the routing tests were needed, because each routing test did not start the ghost server
- they just required the ghost application, which resulted in a random server port
- having a random server port results in a redirect, caused by the ssl/redirect middleware
* 😎 rename check-ssl middleware
* 🎨 fix theme-handler because of master rebase
2017-02-03 21:13:22 +03:00
|
|
|
});
|
|
|
|
|
✨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('Collection Index', function () {
|
2016-02-09 17:14:24 +03:00
|
|
|
it('should respond with html', function (done) {
|
|
|
|
request.get('/')
|
|
|
|
.expect('Content-Type', /html/)
|
|
|
|
.expect('Cache-Control', testUtils.cacheRules.public)
|
|
|
|
.expect(200)
|
|
|
|
.end(function (err, res) {
|
|
|
|
if (err) {
|
|
|
|
return done(err);
|
|
|
|
}
|
|
|
|
|
2020-04-29 18:44:27 +03:00
|
|
|
const $ = cheerio.load(res.text);
|
2016-02-09 17:14:24 +03:00
|
|
|
|
|
|
|
should.not.exist(res.headers['x-cache-invalidate']);
|
|
|
|
should.not.exist(res.headers['X-CSRF-Token']);
|
|
|
|
should.not.exist(res.headers['set-cookie']);
|
|
|
|
should.exist(res.headers.date);
|
|
|
|
|
|
|
|
$('title').text().should.equal('Ghost');
|
2017-09-21 17:05:35 +03:00
|
|
|
$('.content .post').length.should.equal(5);
|
|
|
|
$('.poweredby').text().should.equal('Proudly published with Ghost');
|
|
|
|
$('body.home-template').length.should.equal(1);
|
|
|
|
$('article.post').length.should.equal(5);
|
|
|
|
$('article.tag-getting-started').length.should.equal(5);
|
2016-02-09 17:14:24 +03:00
|
|
|
|
|
|
|
done();
|
|
|
|
});
|
|
|
|
});
|
|
|
|
|
2017-06-08 18:36:14 +03:00
|
|
|
it('should not have a third page', function (done) {
|
|
|
|
request.get('/page/3/')
|
2016-02-09 17:14:24 +03:00
|
|
|
.expect('Cache-Control', testUtils.cacheRules.private)
|
|
|
|
.expect(404)
|
|
|
|
.expect(/Page not found/)
|
|
|
|
.end(doEnd(done));
|
|
|
|
});
|
|
|
|
|
|
|
|
describe('RSS', function () {
|
2020-02-24 23:51:09 +03:00
|
|
|
before(testUtils.teardownDb);
|
2016-07-15 19:22:41 +03:00
|
|
|
|
2016-02-09 17:14:24 +03:00
|
|
|
before(function (done) {
|
|
|
|
testUtils.initData().then(function () {
|
|
|
|
return testUtils.fixtures.overrideOwnerUser();
|
|
|
|
}).then(function () {
|
|
|
|
done();
|
|
|
|
});
|
|
|
|
});
|
|
|
|
|
2020-02-24 23:51:09 +03:00
|
|
|
after(testUtils.teardownDb);
|
2016-02-09 17:14:24 +03:00
|
|
|
|
|
|
|
it('should 301 redirect with CC=1year without slash', function (done) {
|
|
|
|
request.get('/rss')
|
|
|
|
.expect('Location', '/rss/')
|
|
|
|
.expect('Cache-Control', testUtils.cacheRules.year)
|
|
|
|
.expect(301)
|
|
|
|
.end(doEnd(done));
|
|
|
|
});
|
|
|
|
|
|
|
|
it('should respond with 200 & CC=public', function (done) {
|
|
|
|
request.get('/rss/')
|
|
|
|
.expect('Content-Type', 'text/xml; charset=utf-8')
|
|
|
|
.expect('Cache-Control', testUtils.cacheRules.public)
|
|
|
|
.expect(200)
|
|
|
|
.end(function (err, res) {
|
|
|
|
if (err) {
|
|
|
|
return done(err);
|
|
|
|
}
|
|
|
|
|
|
|
|
should.not.exist(res.headers['x-cache-invalidate']);
|
|
|
|
should.not.exist(res.headers['X-CSRF-Token']);
|
|
|
|
should.not.exist(res.headers['set-cookie']);
|
|
|
|
should.exist(res.headers.date);
|
|
|
|
// The remainder of the XML is tested in the unit/xml_spec.js
|
|
|
|
res.text.should.match(/^<\?xml version="1.0" encoding="UTF-8"\?><rss/);
|
|
|
|
|
|
|
|
done();
|
|
|
|
});
|
|
|
|
});
|
|
|
|
|
|
|
|
it('should get 301 redirect with CC=1year to /rss/ from /feed/', function (done) {
|
|
|
|
request.get('/feed/')
|
|
|
|
.expect('Location', '/rss/')
|
|
|
|
.expect('Cache-Control', testUtils.cacheRules.year)
|
|
|
|
.expect(301)
|
|
|
|
.end(doEnd(done));
|
|
|
|
});
|
|
|
|
});
|
|
|
|
});
|
|
|
|
|
✨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('Collection Entry', function () {
|
|
|
|
before(function () {
|
|
|
|
return testUtils.initData().then(function () {
|
|
|
|
return testUtils.fixtures.overrideOwnerUser();
|
|
|
|
}).then(function () {
|
|
|
|
return testUtils.fixtures.insertPostsAndTags();
|
|
|
|
});
|
|
|
|
});
|
|
|
|
|
|
|
|
it('should render page with slug permalink', function (done) {
|
|
|
|
request.get('/static-page-test/')
|
|
|
|
.expect('Content-Type', /html/)
|
|
|
|
.expect('Cache-Control', testUtils.cacheRules.public)
|
|
|
|
.expect(200)
|
|
|
|
.end(doEnd(done));
|
|
|
|
});
|
|
|
|
|
|
|
|
it('should not render page with dated permalink', function (done) {
|
|
|
|
const date = moment().format('YYYY/MM/DD');
|
|
|
|
|
|
|
|
request.get('/' + date + '/static-page-test/')
|
|
|
|
.expect('Content-Type', /html/)
|
|
|
|
.expect('Cache-Control', testUtils.cacheRules.private)
|
|
|
|
.expect(404)
|
|
|
|
.end(doEnd(done));
|
|
|
|
});
|
|
|
|
});
|
|
|
|
|
2016-02-09 17:14:24 +03:00
|
|
|
describe('Tag', function () {
|
|
|
|
before(function (done) {
|
|
|
|
testUtils.clearData().then(function () {
|
|
|
|
// we initialise data, but not a user. No user should be required for navigating the frontend
|
|
|
|
return testUtils.initData();
|
|
|
|
}).then(function () {
|
|
|
|
return testUtils.fixtures.overrideOwnerUser('ghost-owner');
|
|
|
|
}).then(function () {
|
|
|
|
done();
|
|
|
|
}).catch(done);
|
|
|
|
});
|
|
|
|
|
2020-02-24 23:51:09 +03:00
|
|
|
after(testUtils.teardownDb);
|
2016-04-13 19:10:40 +03:00
|
|
|
|
2017-08-16 13:06:30 +03:00
|
|
|
it('should return HTML for valid route', function (done) {
|
|
|
|
request.get('/tag/getting-started/')
|
|
|
|
.expect(200)
|
|
|
|
.expect('Content-Type', /html/)
|
|
|
|
.expect('Content-Type', /html/)
|
|
|
|
.expect('Cache-Control', testUtils.cacheRules.public)
|
|
|
|
.expect(200)
|
|
|
|
.end(function (err, res) {
|
|
|
|
if (err) {
|
|
|
|
return done(err);
|
|
|
|
}
|
|
|
|
|
2020-04-29 18:44:27 +03:00
|
|
|
const $ = cheerio.load(res.text);
|
2017-08-16 13:06:30 +03:00
|
|
|
|
|
|
|
should.not.exist(res.headers['x-cache-invalidate']);
|
|
|
|
should.not.exist(res.headers['X-CSRF-Token']);
|
|
|
|
should.not.exist(res.headers['set-cookie']);
|
|
|
|
should.exist(res.headers.date);
|
|
|
|
|
2017-09-21 17:05:35 +03:00
|
|
|
$('body').attr('class').should.eql('tag-template tag-getting-started nav-closed');
|
|
|
|
$('.content .post').length.should.equal(5);
|
|
|
|
$('.poweredby').text().should.equal('Proudly published with Ghost');
|
|
|
|
$('article.post').length.should.equal(5);
|
|
|
|
$('article.tag-getting-started').length.should.equal(5);
|
2017-08-16 13:06:30 +03:00
|
|
|
|
|
|
|
done();
|
|
|
|
});
|
|
|
|
});
|
|
|
|
|
2016-02-09 17:14:24 +03:00
|
|
|
it('should 404 for /tag/ route', function (done) {
|
|
|
|
request.get('/tag/')
|
|
|
|
.expect('Cache-Control', testUtils.cacheRules.private)
|
|
|
|
.expect(404)
|
|
|
|
.expect(/Page not found/)
|
|
|
|
.end(doEnd(done));
|
|
|
|
});
|
|
|
|
|
|
|
|
it('should 404 for unknown tag', function (done) {
|
|
|
|
request.get('/tag/spectacular/')
|
|
|
|
.expect('Cache-Control', testUtils.cacheRules.private)
|
|
|
|
.expect(404)
|
|
|
|
.expect(/Page not found/)
|
|
|
|
.end(doEnd(done));
|
|
|
|
});
|
|
|
|
|
|
|
|
it('should 404 for unknown tag with invalid characters', function (done) {
|
|
|
|
request.get('/tag/~$pectacular~/')
|
|
|
|
.expect('Cache-Control', testUtils.cacheRules.private)
|
|
|
|
.expect(404)
|
|
|
|
.expect(/Page not found/)
|
|
|
|
.end(doEnd(done));
|
|
|
|
});
|
|
|
|
|
|
|
|
describe('RSS', function () {
|
|
|
|
it('should redirect without slash', function (done) {
|
|
|
|
request.get('/tag/getting-started/rss')
|
|
|
|
.expect('Location', '/tag/getting-started/rss/')
|
|
|
|
.expect('Cache-Control', testUtils.cacheRules.year)
|
|
|
|
.expect(301)
|
|
|
|
.end(doEnd(done));
|
|
|
|
});
|
|
|
|
|
|
|
|
it('should respond with xml', function (done) {
|
|
|
|
request.get('/tag/getting-started/rss/')
|
|
|
|
.expect('Content-Type', /xml/)
|
|
|
|
.expect('Cache-Control', testUtils.cacheRules.public)
|
|
|
|
.expect(200)
|
|
|
|
.end(doEnd(done));
|
|
|
|
});
|
|
|
|
});
|
|
|
|
|
✨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('Edit', function () {
|
|
|
|
it('should redirect without slash', function (done) {
|
|
|
|
request.get('/tag/getting-started/edit')
|
|
|
|
.expect('Location', '/tag/getting-started/edit/')
|
|
|
|
.expect('Cache-Control', testUtils.cacheRules.year)
|
|
|
|
.expect(301)
|
|
|
|
.end(doEnd(done));
|
|
|
|
});
|
|
|
|
|
|
|
|
it('should redirect to tag settings', function (done) {
|
|
|
|
request.get('/tag/getting-started/edit/')
|
2020-04-16 13:10:15 +03:00
|
|
|
.expect('Location', 'http://127.0.0.1:2369/ghost/#/tags/getting-started/')
|
✨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
|
|
|
.expect('Cache-Control', testUtils.cacheRules.public)
|
|
|
|
.expect(302)
|
|
|
|
.end(doEnd(done));
|
|
|
|
});
|
|
|
|
|
|
|
|
it('should 404 for non-edit parameter', function (done) {
|
|
|
|
request.get('/tag/getting-started/notedit/')
|
|
|
|
.expect('Cache-Control', testUtils.cacheRules.private)
|
|
|
|
.expect(404)
|
|
|
|
.expect(/Page not found/)
|
|
|
|
.end(doEnd(done));
|
|
|
|
});
|
|
|
|
});
|
|
|
|
|
2019-09-12 14:40:12 +03:00
|
|
|
describe('Edit with admin redirects disabled', function () {
|
|
|
|
before(function () {
|
|
|
|
configUtils.set('admin:redirects', false);
|
|
|
|
|
|
|
|
return ghost({forceStart: true})
|
|
|
|
.then(function (_ghostServer) {
|
|
|
|
ghostServer = _ghostServer;
|
|
|
|
request = supertest.agent(config.get('url'));
|
|
|
|
});
|
|
|
|
});
|
|
|
|
|
|
|
|
after(function () {
|
|
|
|
configUtils.restore();
|
|
|
|
|
|
|
|
return ghost({forceStart: true})
|
|
|
|
.then(function (_ghostServer) {
|
|
|
|
ghostServer = _ghostServer;
|
|
|
|
request = supertest.agent(config.get('url'));
|
|
|
|
});
|
|
|
|
});
|
|
|
|
|
|
|
|
it('should redirect without slash', function (done) {
|
|
|
|
request.get('/tag/getting-started/edit')
|
|
|
|
.expect('Location', '/tag/getting-started/edit/')
|
|
|
|
.expect('Cache-Control', testUtils.cacheRules.year)
|
|
|
|
.expect(301)
|
|
|
|
.end(doEnd(done));
|
|
|
|
});
|
|
|
|
|
|
|
|
it('should not redirect to admin', function (done) {
|
|
|
|
request.get('/tag/getting-started/edit/')
|
|
|
|
.expect(404)
|
|
|
|
.expect('Cache-Control', testUtils.cacheRules.private)
|
|
|
|
.end(doEnd(done));
|
|
|
|
});
|
|
|
|
});
|
|
|
|
|
2019-01-08 12:48:53 +03:00
|
|
|
describe.skip('Paged', function () {
|
|
|
|
// Inserting more posts takes a bit longer
|
|
|
|
this.timeout(20000);
|
|
|
|
|
2020-02-24 23:51:09 +03:00
|
|
|
before(testUtils.teardownDb);
|
2016-07-15 19:22:41 +03:00
|
|
|
|
2016-02-09 17:14:24 +03:00
|
|
|
// Add enough posts to trigger pages
|
|
|
|
before(function (done) {
|
|
|
|
testUtils.initData().then(function () {
|
2016-06-10 08:12:46 +03:00
|
|
|
return testUtils.fixtures.insertPostsAndTags();
|
2016-02-09 17:14:24 +03:00
|
|
|
}).then(function () {
|
2019-01-08 12:48:53 +03:00
|
|
|
return testUtils.fixtures.insertExtraPosts(11);
|
2016-02-09 17:14:24 +03:00
|
|
|
}).then(function () {
|
2019-01-08 12:48:53 +03:00
|
|
|
return testUtils.fixtures.insertExtraPostsTags(11);
|
2016-02-09 17:14:24 +03:00
|
|
|
}).then(function () {
|
|
|
|
done();
|
|
|
|
}).catch(done);
|
|
|
|
});
|
|
|
|
|
2020-02-24 23:51:09 +03:00
|
|
|
after(testUtils.teardownDb);
|
2016-02-09 17:14:24 +03:00
|
|
|
|
|
|
|
it('should redirect without slash', function (done) {
|
|
|
|
request.get('/tag/injection/page/2')
|
|
|
|
.expect('Location', '/tag/injection/page/2/')
|
|
|
|
.expect('Cache-Control', testUtils.cacheRules.year)
|
|
|
|
.expect(301)
|
|
|
|
.end(doEnd(done));
|
|
|
|
});
|
|
|
|
|
2017-09-21 17:05:35 +03:00
|
|
|
it('should respond with html', function (done) {
|
2016-02-09 17:14:24 +03:00
|
|
|
request.get('/tag/injection/page/2/')
|
|
|
|
.expect('Content-Type', /html/)
|
|
|
|
.expect('Cache-Control', testUtils.cacheRules.public)
|
|
|
|
.expect(200)
|
|
|
|
.end(doEnd(done));
|
|
|
|
});
|
|
|
|
|
|
|
|
it('should redirect page 1', function (done) {
|
|
|
|
request.get('/tag/injection/page/1/')
|
|
|
|
.expect('Location', '/tag/injection/')
|
|
|
|
.expect('Cache-Control', testUtils.cacheRules.year)
|
|
|
|
.expect(301)
|
|
|
|
.end(doEnd(done));
|
|
|
|
});
|
|
|
|
|
|
|
|
it('should 404 if page too high', function (done) {
|
2019-01-08 12:48:53 +03:00
|
|
|
request.get('/tag/injection/page/3/')
|
2016-02-09 17:14:24 +03:00
|
|
|
.expect('Cache-Control', testUtils.cacheRules.private)
|
|
|
|
.expect(404)
|
|
|
|
.expect(/Page not found/)
|
|
|
|
.end(doEnd(done));
|
|
|
|
});
|
|
|
|
|
|
|
|
it('should 404 if page too low', function (done) {
|
|
|
|
request.get('/tag/injection/page/0/')
|
|
|
|
.expect('Cache-Control', testUtils.cacheRules.private)
|
|
|
|
.expect(404)
|
|
|
|
.expect(/Page not found/)
|
|
|
|
.end(doEnd(done));
|
|
|
|
});
|
|
|
|
|
|
|
|
describe('RSS', function () {
|
2019-09-02 13:34:13 +03:00
|
|
|
it('should 404 if index attempted with 0', function (done) {
|
|
|
|
request.get('/tag/getting-started/rss/0/')
|
|
|
|
.expect('Cache-Control', testUtils.cacheRules.private)
|
|
|
|
.expect(404)
|
|
|
|
.expect(/Page not found/)
|
2016-02-09 17:14:24 +03:00
|
|
|
.end(doEnd(done));
|
|
|
|
});
|
|
|
|
|
2019-09-02 13:34:13 +03:00
|
|
|
it('should 404 if index attempted with 1', function (done) {
|
|
|
|
request.get('/tag/getting-started/rss/1/')
|
2016-02-09 17:14:24 +03:00
|
|
|
.expect('Cache-Control', testUtils.cacheRules.private)
|
|
|
|
.expect(404)
|
|
|
|
.expect(/Page not found/)
|
|
|
|
.end(doEnd(done));
|
|
|
|
});
|
|
|
|
|
2019-09-02 13:34:13 +03:00
|
|
|
it('should 404 for other pages', function (done) {
|
|
|
|
request.get('/tag/getting-started/rss/2/')
|
2016-02-09 17:14:24 +03:00
|
|
|
.expect('Cache-Control', testUtils.cacheRules.private)
|
|
|
|
.expect(404)
|
|
|
|
.expect(/Page not found/)
|
|
|
|
.end(doEnd(done));
|
|
|
|
});
|
|
|
|
});
|
|
|
|
});
|
|
|
|
});
|
|
|
|
|
|
|
|
describe('Author', function () {
|
2020-04-29 18:44:27 +03:00
|
|
|
const lockedUser = {
|
|
|
|
name: 'Locked so what',
|
|
|
|
slug: 'locked-so-what',
|
|
|
|
email: 'locked@example.com',
|
|
|
|
status: 'locked'
|
|
|
|
};
|
|
|
|
|
|
|
|
const suspendedUser = {
|
|
|
|
name: 'Suspended meeh',
|
|
|
|
slug: 'suspended-meeh',
|
|
|
|
email: 'suspended@example.com',
|
|
|
|
status: 'inactive'
|
|
|
|
};
|
|
|
|
|
|
|
|
const ownerSlug = 'ghost-owner';
|
🐛 be able to serve locked users (#8711)
closes #8645, closes #8710
- locked users were once part of the category "active users", but were moved to the inactive category
-> we have added a protection of not being able to edit yourself when you are either suspended or locked
- but they are not really active users, they are restricted, because they have no access to the admin panel
- support three categories: active, inactive, restricted
* - revert restricted states
- instead, update permission layer: fallback to `all` by default, because you are able to serve any user status
- add more tests
- ATTENTION: there is a behaviour change, that a blog owner's author page can be served before setting up the blog, see conversation on slack
-> LTS serves 404
-> 1.0 would serve 200
2017-07-20 14:45:13 +03:00
|
|
|
|
2016-02-09 17:14:24 +03:00
|
|
|
before(function (done) {
|
|
|
|
testUtils.clearData().then(function () {
|
|
|
|
// we initialise data, but not a user. No user should be required for navigating the frontend
|
|
|
|
return testUtils.initData();
|
|
|
|
}).then(function () {
|
🐛 be able to serve locked users (#8711)
closes #8645, closes #8710
- locked users were once part of the category "active users", but were moved to the inactive category
-> we have added a protection of not being able to edit yourself when you are either suspended or locked
- but they are not really active users, they are restricted, because they have no access to the admin panel
- support three categories: active, inactive, restricted
* - revert restricted states
- instead, update permission layer: fallback to `all` by default, because you are able to serve any user status
- add more tests
- ATTENTION: there is a behaviour change, that a blog owner's author page can be served before setting up the blog, see conversation on slack
-> LTS serves 404
-> 1.0 would serve 200
2017-07-20 14:45:13 +03:00
|
|
|
return testUtils.fixtures.overrideOwnerUser(ownerSlug);
|
2019-09-10 12:41:42 +03:00
|
|
|
}).then(function (insertedUser) {
|
|
|
|
return testUtils.fixtures.insertPosts([
|
|
|
|
testUtils.DataGenerator.forKnex.createPost({
|
|
|
|
author_id: insertedUser.id
|
|
|
|
})
|
|
|
|
]);
|
🐛 be able to serve locked users (#8711)
closes #8645, closes #8710
- locked users were once part of the category "active users", but were moved to the inactive category
-> we have added a protection of not being able to edit yourself when you are either suspended or locked
- but they are not really active users, they are restricted, because they have no access to the admin panel
- support three categories: active, inactive, restricted
* - revert restricted states
- instead, update permission layer: fallback to `all` by default, because you are able to serve any user status
- add more tests
- ATTENTION: there is a behaviour change, that a blog owner's author page can be served before setting up the blog, see conversation on slack
-> LTS serves 404
-> 1.0 would serve 200
2017-07-20 14:45:13 +03:00
|
|
|
}).then(function () {
|
|
|
|
return testUtils.fixtures.insertOneUser(lockedUser);
|
2019-09-10 12:41:42 +03:00
|
|
|
}).then(function (insertedUser) {
|
|
|
|
return testUtils.fixtures.insertPosts([
|
|
|
|
testUtils.DataGenerator.forKnex.createPost({
|
|
|
|
author_id: insertedUser.id
|
|
|
|
})
|
|
|
|
]);
|
|
|
|
}).then(() => {
|
🐛 be able to serve locked users (#8711)
closes #8645, closes #8710
- locked users were once part of the category "active users", but were moved to the inactive category
-> we have added a protection of not being able to edit yourself when you are either suspended or locked
- but they are not really active users, they are restricted, because they have no access to the admin panel
- support three categories: active, inactive, restricted
* - revert restricted states
- instead, update permission layer: fallback to `all` by default, because you are able to serve any user status
- add more tests
- ATTENTION: there is a behaviour change, that a blog owner's author page can be served before setting up the blog, see conversation on slack
-> LTS serves 404
-> 1.0 would serve 200
2017-07-20 14:45:13 +03:00
|
|
|
return testUtils.fixtures.insertOneUser(suspendedUser);
|
2019-09-10 12:41:42 +03:00
|
|
|
}).then(function (insertedUser) {
|
|
|
|
return testUtils.fixtures.insertPosts([
|
|
|
|
testUtils.DataGenerator.forKnex.createPost({
|
|
|
|
author_id: insertedUser.id
|
|
|
|
})
|
|
|
|
]);
|
2016-02-09 17:14:24 +03:00
|
|
|
}).then(function () {
|
|
|
|
done();
|
|
|
|
}).catch(done);
|
|
|
|
});
|
|
|
|
|
2020-02-24 23:51:09 +03:00
|
|
|
after(testUtils.teardownDb);
|
2016-02-09 17:14:24 +03:00
|
|
|
|
|
|
|
it('should 404 for /author/ route', function (done) {
|
|
|
|
request.get('/author/')
|
|
|
|
.expect('Cache-Control', testUtils.cacheRules.private)
|
|
|
|
.expect(404)
|
|
|
|
.expect(/Page not found/)
|
|
|
|
.end(doEnd(done));
|
|
|
|
});
|
|
|
|
|
|
|
|
it('should 404 for unknown author', function (done) {
|
|
|
|
request.get('/author/spectacular/')
|
|
|
|
.expect('Cache-Control', testUtils.cacheRules.private)
|
|
|
|
.expect(404)
|
|
|
|
.expect(/Page not found/)
|
|
|
|
.end(doEnd(done));
|
|
|
|
});
|
|
|
|
|
|
|
|
it('should 404 for unknown author with invalid characters', function (done) {
|
|
|
|
request.get('/author/ghost!user^/')
|
|
|
|
.expect('Cache-Control', testUtils.cacheRules.private)
|
|
|
|
.expect(404)
|
|
|
|
.expect(/Page not found/)
|
|
|
|
.end(doEnd(done));
|
|
|
|
});
|
|
|
|
|
🐛 be able to serve locked users (#8711)
closes #8645, closes #8710
- locked users were once part of the category "active users", but were moved to the inactive category
-> we have added a protection of not being able to edit yourself when you are either suspended or locked
- but they are not really active users, they are restricted, because they have no access to the admin panel
- support three categories: active, inactive, restricted
* - revert restricted states
- instead, update permission layer: fallback to `all` by default, because you are able to serve any user status
- add more tests
- ATTENTION: there is a behaviour change, that a blog owner's author page can be served before setting up the blog, see conversation on slack
-> LTS serves 404
-> 1.0 would serve 200
2017-07-20 14:45:13 +03:00
|
|
|
it('[success] author is locked', function (done) {
|
|
|
|
request.get('/author/' + lockedUser.slug + '/')
|
|
|
|
.expect('Cache-Control', testUtils.cacheRules.public)
|
|
|
|
.expect(200)
|
|
|
|
.end(doEnd(done));
|
|
|
|
});
|
|
|
|
|
|
|
|
it('[success] author is suspended', function (done) {
|
|
|
|
request.get('/author/' + suspendedUser.slug + '/')
|
|
|
|
.expect('Cache-Control', testUtils.cacheRules.public)
|
|
|
|
.expect(200)
|
|
|
|
.end(doEnd(done));
|
|
|
|
});
|
|
|
|
|
|
|
|
it('[failure] ghost owner before blog setup', function (done) {
|
|
|
|
testUtils.fixtures.changeOwnerUserStatus({
|
|
|
|
slug: ownerSlug,
|
|
|
|
status: 'inactive'
|
|
|
|
}).then(function () {
|
|
|
|
request.get('/author/ghost-owner/')
|
|
|
|
.expect('Cache-Control', testUtils.cacheRules.public)
|
|
|
|
.expect(200)
|
|
|
|
.end(doEnd(done));
|
|
|
|
}).catch(done);
|
|
|
|
});
|
|
|
|
|
|
|
|
it('[success] ghost owner after blog setup', function (done) {
|
|
|
|
testUtils.fixtures.changeOwnerUserStatus({
|
|
|
|
slug: ownerSlug,
|
|
|
|
status: 'active'
|
|
|
|
}).then(function () {
|
|
|
|
request.get('/author/ghost-owner/')
|
|
|
|
.expect('Cache-Control', testUtils.cacheRules.public)
|
|
|
|
.expect(200)
|
|
|
|
.end(doEnd(done));
|
|
|
|
});
|
|
|
|
});
|
|
|
|
|
2016-02-09 17:14:24 +03:00
|
|
|
describe('RSS', function () {
|
|
|
|
it('should redirect without slash', function (done) {
|
|
|
|
request.get('/author/ghost-owner/rss')
|
|
|
|
.expect('Location', '/author/ghost-owner/rss/')
|
|
|
|
.expect('Cache-Control', testUtils.cacheRules.year)
|
|
|
|
.expect(301)
|
|
|
|
.end(doEnd(done));
|
|
|
|
});
|
|
|
|
|
|
|
|
it('should respond with xml', function (done) {
|
|
|
|
request.get('/author/ghost-owner/rss/')
|
|
|
|
.expect('Content-Type', /xml/)
|
|
|
|
.expect('Cache-Control', testUtils.cacheRules.public)
|
|
|
|
.expect(200)
|
|
|
|
.end(doEnd(done));
|
|
|
|
});
|
|
|
|
});
|
|
|
|
|
✨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('Edit', function () {
|
|
|
|
it('should redirect without slash', function (done) {
|
|
|
|
request.get('/author/ghost-owner/edit')
|
|
|
|
.expect('Location', '/author/ghost-owner/edit/')
|
|
|
|
.expect('Cache-Control', testUtils.cacheRules.year)
|
|
|
|
.expect(301)
|
|
|
|
.end(doEnd(done));
|
|
|
|
});
|
|
|
|
|
|
|
|
it('should redirect to editor', function (done) {
|
|
|
|
request.get('/author/ghost-owner/edit/')
|
2020-07-21 13:38:56 +03:00
|
|
|
.expect('Location', 'http://127.0.0.1:2369/ghost/#/staff/ghost-owner/')
|
✨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
|
|
|
.expect('Cache-Control', testUtils.cacheRules.public)
|
|
|
|
.expect(302)
|
|
|
|
.end(doEnd(done));
|
|
|
|
});
|
|
|
|
|
|
|
|
it('should 404 for something that isn\'t edit', function (done) {
|
|
|
|
request.get('/author/ghost-owner/notedit/')
|
|
|
|
.expect('Cache-Control', testUtils.cacheRules.private)
|
|
|
|
.expect(404)
|
|
|
|
.expect(/Page not found/)
|
|
|
|
.end(doEnd(done));
|
|
|
|
});
|
|
|
|
});
|
|
|
|
|
2019-09-12 14:40:12 +03:00
|
|
|
describe('Edit with admin redirects disabled', function () {
|
|
|
|
before(function () {
|
|
|
|
configUtils.set('admin:redirects', false);
|
|
|
|
|
|
|
|
return ghost({forceStart: true})
|
|
|
|
.then(function (_ghostServer) {
|
|
|
|
ghostServer = _ghostServer;
|
|
|
|
request = supertest.agent(config.get('url'));
|
|
|
|
});
|
|
|
|
});
|
|
|
|
|
|
|
|
after(function () {
|
|
|
|
configUtils.restore();
|
|
|
|
|
|
|
|
return ghost({forceStart: true})
|
|
|
|
.then(function (_ghostServer) {
|
|
|
|
ghostServer = _ghostServer;
|
|
|
|
request = supertest.agent(config.get('url'));
|
|
|
|
});
|
|
|
|
});
|
|
|
|
|
|
|
|
it('should redirect without slash', function (done) {
|
|
|
|
request.get('/author/ghost-owner/edit')
|
|
|
|
.expect('Location', '/author/ghost-owner/edit/')
|
|
|
|
.expect('Cache-Control', testUtils.cacheRules.year)
|
|
|
|
.expect(301)
|
|
|
|
.end(doEnd(done));
|
|
|
|
});
|
|
|
|
|
|
|
|
it('should not redirect to admin', function (done) {
|
|
|
|
request.get('/author/ghost-owner/edit/')
|
|
|
|
.expect('Cache-Control', testUtils.cacheRules.private)
|
|
|
|
.expect(404)
|
|
|
|
.end(doEnd(done));
|
|
|
|
});
|
|
|
|
});
|
|
|
|
|
2016-02-09 17:14:24 +03:00
|
|
|
describe('Paged', function () {
|
|
|
|
// Add enough posts to trigger pages
|
|
|
|
before(function (done) {
|
|
|
|
testUtils.clearData().then(function () {
|
|
|
|
// we initialise data, but not a user. No user should be required for navigating the frontend
|
|
|
|
return testUtils.initData();
|
|
|
|
}).then(function () {
|
2016-06-10 08:12:46 +03:00
|
|
|
return testUtils.fixtures.insertPostsAndTags();
|
2016-02-09 17:14:24 +03:00
|
|
|
}).then(function () {
|
2018-02-21 19:48:46 +03:00
|
|
|
return testUtils.fixtures.insertExtraPosts(9);
|
2016-02-09 17:14:24 +03:00
|
|
|
}).then(function () {
|
2018-04-25 18:13:35 +03:00
|
|
|
return testUtils.fixtures.overrideOwnerUser('ghost-owner');
|
|
|
|
}).then(function () {
|
2016-02-09 17:14:24 +03:00
|
|
|
done();
|
|
|
|
}).catch(done);
|
|
|
|
});
|
|
|
|
|
2020-02-24 23:51:09 +03:00
|
|
|
after(testUtils.teardownDb);
|
2016-02-09 17:14:24 +03:00
|
|
|
|
|
|
|
it('should redirect without slash', function (done) {
|
|
|
|
request.get('/author/ghost-owner/page/2')
|
|
|
|
.expect('Location', '/author/ghost-owner/page/2/')
|
|
|
|
.expect('Cache-Control', testUtils.cacheRules.year)
|
|
|
|
.expect(301)
|
|
|
|
.end(doEnd(done));
|
|
|
|
});
|
|
|
|
|
2017-09-21 17:05:35 +03:00
|
|
|
it('should respond with html', function (done) {
|
2016-02-09 17:14:24 +03:00
|
|
|
request.get('/author/ghost-owner/page/2/')
|
|
|
|
.expect('Content-Type', /html/)
|
|
|
|
.expect('Cache-Control', testUtils.cacheRules.public)
|
|
|
|
.expect(200)
|
|
|
|
.end(doEnd(done));
|
|
|
|
});
|
|
|
|
|
|
|
|
it('should redirect page 1', function (done) {
|
|
|
|
request.get('/author/ghost-owner/page/1/')
|
|
|
|
.expect('Location', '/author/ghost-owner/')
|
|
|
|
.expect('Cache-Control', testUtils.cacheRules.year)
|
|
|
|
.expect(301)
|
|
|
|
.end(doEnd(done));
|
|
|
|
});
|
|
|
|
|
|
|
|
it('should 404 if page too high', function (done) {
|
2017-06-08 18:36:14 +03:00
|
|
|
request.get('/author/ghost-owner/page/6/')
|
2016-02-09 17:14:24 +03:00
|
|
|
.expect('Cache-Control', testUtils.cacheRules.private)
|
|
|
|
.expect(404)
|
|
|
|
.expect(/Page not found/)
|
|
|
|
.end(doEnd(done));
|
|
|
|
});
|
|
|
|
|
|
|
|
it('should 404 if page too low', function (done) {
|
|
|
|
request.get('/author/ghost-owner/page/0/')
|
|
|
|
.expect('Cache-Control', testUtils.cacheRules.private)
|
|
|
|
.expect(404)
|
|
|
|
.expect(/Page not found/)
|
|
|
|
.end(doEnd(done));
|
|
|
|
});
|
|
|
|
|
|
|
|
describe('RSS', function () {
|
2019-09-02 13:34:13 +03:00
|
|
|
it('should 404 if index attempted with 0', function (done) {
|
|
|
|
request.get('/author/ghost-owner/rss/0/')
|
|
|
|
.expect('Cache-Control', testUtils.cacheRules.private)
|
|
|
|
.expect(404)
|
|
|
|
.expect(/Page not found/)
|
2016-02-09 17:14:24 +03:00
|
|
|
.end(doEnd(done));
|
|
|
|
});
|
|
|
|
|
2019-09-02 13:34:13 +03:00
|
|
|
it('should 404 if index attempted with 1', function (done) {
|
|
|
|
request.get('/author/ghost-owner/rss/1/')
|
2016-02-09 17:14:24 +03:00
|
|
|
.expect('Cache-Control', testUtils.cacheRules.private)
|
|
|
|
.expect(404)
|
|
|
|
.expect(/Page not found/)
|
|
|
|
.end(doEnd(done));
|
|
|
|
});
|
|
|
|
|
2019-09-02 13:34:13 +03:00
|
|
|
it('should 404 for other pages', function (done) {
|
|
|
|
request.get('/author/ghost-owner/rss/2/')
|
2016-02-09 17:14:24 +03:00
|
|
|
.expect('Cache-Control', testUtils.cacheRules.private)
|
|
|
|
.expect(404)
|
|
|
|
.expect(/Page not found/)
|
|
|
|
.end(doEnd(done));
|
|
|
|
});
|
|
|
|
});
|
|
|
|
});
|
|
|
|
});
|
2018-08-07 12:49:51 +03:00
|
|
|
|
|
|
|
describe('Reload routes.yaml', function () {
|
|
|
|
before(function (done) {
|
|
|
|
testUtils.clearData().then(function () {
|
|
|
|
// we initialise data, but not a user. No user should be required for navigating the frontend
|
|
|
|
return testUtils.initData();
|
|
|
|
}).then(function () {
|
|
|
|
return testUtils.fixtures.overrideOwnerUser('ghost-owner');
|
2020-09-09 15:28:12 +03:00
|
|
|
}).then(function () {
|
|
|
|
return testUtils.initFixtures('settings');
|
2018-08-07 12:49:51 +03:00
|
|
|
}).then(function () {
|
|
|
|
done();
|
|
|
|
}).catch(done);
|
|
|
|
});
|
|
|
|
|
2020-02-24 23:51:09 +03:00
|
|
|
after(testUtils.teardownDb);
|
2018-08-07 12:49:51 +03:00
|
|
|
after(function () {
|
|
|
|
return ghostServer.stop();
|
|
|
|
});
|
|
|
|
|
|
|
|
it('confirm current routing pattern', function (done) {
|
|
|
|
request.get('/welcome/')
|
|
|
|
.expect(200)
|
|
|
|
.end(function (err, res) {
|
|
|
|
if (err) {
|
|
|
|
return done(err);
|
|
|
|
}
|
|
|
|
|
|
|
|
done();
|
|
|
|
});
|
|
|
|
});
|
|
|
|
|
|
|
|
it('simulate upload of routes.yaml', function () {
|
2019-09-11 20:10:10 +03:00
|
|
|
return api.settings.upload.query({
|
2018-08-07 12:49:51 +03:00
|
|
|
context: testUtils.context.internal.context,
|
2019-09-11 20:10:10 +03:00
|
|
|
file: {
|
2020-03-30 18:26:47 +03:00
|
|
|
path: path.join(config.get('paths:appRoot'), 'test', 'utils', 'fixtures', 'settings', 'newroutes.yaml')
|
2019-09-11 20:10:10 +03:00
|
|
|
}
|
2018-08-07 12:49:51 +03:00
|
|
|
}).then(() => {
|
2021-02-17 20:36:27 +03:00
|
|
|
return testUtils.integrationTesting.urlService.isFinished({disableDbReadyEvent: true});
|
2018-08-07 12:49:51 +03:00
|
|
|
});
|
|
|
|
});
|
|
|
|
|
|
|
|
it('serve welcome post with old permalink structure', function (done) {
|
|
|
|
request.get('/welcome/')
|
|
|
|
.expect(404)
|
|
|
|
.end(function (err) {
|
|
|
|
if (err) {
|
|
|
|
return done(err);
|
|
|
|
}
|
|
|
|
|
|
|
|
done();
|
|
|
|
});
|
|
|
|
});
|
|
|
|
|
|
|
|
it('serve welcome post with new permalink structure', function (done) {
|
|
|
|
const year = moment().year();
|
|
|
|
request.get(`/blog/${year}/welcome/`)
|
|
|
|
.expect(200)
|
|
|
|
.end(function (err) {
|
|
|
|
if (err) {
|
|
|
|
return done(err);
|
|
|
|
}
|
|
|
|
|
|
|
|
done();
|
|
|
|
});
|
|
|
|
});
|
|
|
|
|
|
|
|
it('serve welcome post with new permalink structure and old date', function (done) {
|
|
|
|
request.get('/blog/2016/welcome/')
|
|
|
|
.expect(301)
|
|
|
|
.end(function (err) {
|
|
|
|
if (err) {
|
|
|
|
return done(err);
|
|
|
|
}
|
|
|
|
|
|
|
|
done();
|
|
|
|
});
|
|
|
|
});
|
|
|
|
|
|
|
|
it('serve serve rss', function (done) {
|
|
|
|
request.get('/blog/rss/')
|
|
|
|
.expect(200)
|
|
|
|
.end(function (err, res) {
|
|
|
|
if (err) {
|
|
|
|
return done(err);
|
|
|
|
}
|
|
|
|
|
|
|
|
const content = res.text;
|
|
|
|
const todayMoment = moment();
|
|
|
|
const year = todayMoment.format('YYYY');
|
|
|
|
const postLink = `/blog/${year}/welcome/`;
|
|
|
|
|
|
|
|
content.indexOf(postLink).should.be.above(0);
|
|
|
|
|
|
|
|
done();
|
|
|
|
});
|
|
|
|
});
|
|
|
|
|
|
|
|
it('serve collection index', function (done) {
|
|
|
|
request.get('/blog/')
|
|
|
|
.expect(200)
|
|
|
|
.end(function (err, res) {
|
|
|
|
if (err) {
|
|
|
|
return done(err);
|
|
|
|
}
|
|
|
|
|
|
|
|
done();
|
|
|
|
});
|
|
|
|
});
|
|
|
|
|
|
|
|
it('serve tag', function (done) {
|
|
|
|
request.get('/category/getting-started/')
|
|
|
|
.expect(200)
|
|
|
|
.end(function (err, res) {
|
|
|
|
if (err) {
|
|
|
|
return done(err);
|
|
|
|
}
|
|
|
|
|
|
|
|
done();
|
|
|
|
});
|
|
|
|
});
|
|
|
|
});
|
2016-02-09 17:14:24 +03:00
|
|
|
});
|