mirror of
https://github.com/TryGhost/Ghost.git
synced 2025-02-10 23:36:14 -05:00
Merge pull request #6469 from ErisDS/dynamic-channel-routing
Dynamic channel routing
This commit is contained in:
commit
3c5c5ad9d0
12 changed files with 1180 additions and 821 deletions
|
@ -1,8 +1,8 @@
|
|||
var _ = require('lodash'),
|
||||
config = require('../../config'),
|
||||
getConfig;
|
||||
channelConfig;
|
||||
|
||||
getConfig = function getConfig(name) {
|
||||
channelConfig = function channelConfig() {
|
||||
var defaults = {
|
||||
index: {
|
||||
name: 'index',
|
||||
|
@ -22,7 +22,8 @@ getConfig = function getConfig(name) {
|
|||
options: {slug: '%s'}
|
||||
}
|
||||
},
|
||||
slugTemplate: true
|
||||
slugTemplate: true,
|
||||
editRedirect: '/ghost/settings/tags/:slug/'
|
||||
},
|
||||
author: {
|
||||
name: 'author',
|
||||
|
@ -37,11 +38,18 @@ getConfig = function getConfig(name) {
|
|||
options: {slug: '%s'}
|
||||
}
|
||||
},
|
||||
slugTemplate: true
|
||||
slugTemplate: true,
|
||||
editRedirect: '/ghost/team/:slug/'
|
||||
}
|
||||
};
|
||||
|
||||
return _.cloneDeep(defaults[name]);
|
||||
return defaults;
|
||||
};
|
||||
|
||||
module.exports = getConfig;
|
||||
module.exports.list = function list() {
|
||||
return channelConfig();
|
||||
};
|
||||
|
||||
module.exports.get = function get(name) {
|
||||
return _.cloneDeep(channelConfig()[name]);
|
||||
};
|
||||
|
|
93
core/server/controllers/frontend/channels.js
Normal file
93
core/server/controllers/frontend/channels.js
Normal file
|
@ -0,0 +1,93 @@
|
|||
var express = require('express'),
|
||||
_ = require('lodash'),
|
||||
config = require('../../config'),
|
||||
errors = require('../../errors'),
|
||||
rss = require('../../data/xml/rss'),
|
||||
utils = require('../../utils'),
|
||||
|
||||
channelConfig = require('./channel-config'),
|
||||
renderChannel = require('./render-channel'),
|
||||
|
||||
rssRouter,
|
||||
channelRouter;
|
||||
|
||||
function handlePageParam(req, res, next, page) {
|
||||
var pageRegex = new RegExp('/' + config.routeKeywords.page + '/(.*)?/'),
|
||||
rssRegex = new RegExp('/rss/(.*)?/');
|
||||
|
||||
page = parseInt(page, 10);
|
||||
|
||||
if (page === 1) {
|
||||
// Page 1 is an alias, do a permanent 301 redirect
|
||||
if (rssRegex.test(req.url)) {
|
||||
return utils.redirect301(res, req.originalUrl.replace(rssRegex, '/rss/'));
|
||||
} else {
|
||||
return utils.redirect301(res, req.originalUrl.replace(pageRegex, '/'));
|
||||
}
|
||||
} else if (page < 1 || isNaN(page)) {
|
||||
// Nothing less than 1 is a valid page number, go straight to a 404
|
||||
return next(new errors.NotFoundError());
|
||||
} else {
|
||||
// Set req.params.page to the already parsed number, and continue
|
||||
req.params.page = page;
|
||||
return next();
|
||||
}
|
||||
}
|
||||
|
||||
rssRouter = function rssRouter(channelConfig) {
|
||||
function rssConfigMiddleware(req, res, next) {
|
||||
req.channelConfig.isRSS = true;
|
||||
next();
|
||||
}
|
||||
|
||||
// @TODO move this to an RSS module
|
||||
var router = express.Router({mergeParams: true}),
|
||||
stack = [channelConfig, rssConfigMiddleware, rss],
|
||||
baseRoute = '/rss/';
|
||||
|
||||
router.get(baseRoute, stack);
|
||||
router.get(baseRoute + ':page/', stack);
|
||||
router.get('/feed/', function redirectToRSS(req, res) {
|
||||
return utils.redirect301(res, config.paths.subdir + req.baseUrl + baseRoute);
|
||||
});
|
||||
router.param('page', handlePageParam);
|
||||
|
||||
return router;
|
||||
};
|
||||
|
||||
channelRouter = function router() {
|
||||
function channelConfigMiddleware(channel) {
|
||||
return function doChannelConfig(req, res, next) {
|
||||
req.channelConfig = _.cloneDeep(channel);
|
||||
next();
|
||||
};
|
||||
}
|
||||
|
||||
var channelsRouter = express.Router({mergeParams: true}),
|
||||
baseRoute = '/',
|
||||
pageRoute = '/' + config.routeKeywords.page + '/:page/';
|
||||
|
||||
_.each(channelConfig.list(), function (channel) {
|
||||
var channelRouter = express.Router({mergeParams: true}),
|
||||
configChannel = channelConfigMiddleware(channel);
|
||||
|
||||
// @TODO figure out how to collapse this into a single rule
|
||||
channelRouter.get(baseRoute, configChannel, renderChannel);
|
||||
channelRouter.get(pageRoute, configChannel, renderChannel);
|
||||
channelRouter.param('page', handlePageParam);
|
||||
channelRouter.use(rssRouter(configChannel));
|
||||
|
||||
if (channel.editRedirect) {
|
||||
channelRouter.get('/edit/', function redirect(req, res) {
|
||||
res.redirect(config.paths.subdir + channel.editRedirect.replace(':slug', req.params.slug));
|
||||
});
|
||||
}
|
||||
|
||||
// Mount this channel router on the parent channels router
|
||||
channelsRouter.use(channel.route, channelRouter);
|
||||
});
|
||||
|
||||
return channelsRouter;
|
||||
};
|
||||
|
||||
module.exports.router = channelRouter;
|
|
@ -6,21 +6,17 @@
|
|||
|
||||
var _ = require('lodash'),
|
||||
api = require('../../api'),
|
||||
rss = require('../../data/xml/rss'),
|
||||
path = require('path'),
|
||||
config = require('../../config'),
|
||||
errors = require('../../errors'),
|
||||
filters = require('../../filters'),
|
||||
Promise = require('bluebird'),
|
||||
templates = require('./templates'),
|
||||
templates = require('./templates'),
|
||||
routeMatch = require('path-match')(),
|
||||
safeString = require('../../utils/index').safeString,
|
||||
handleError = require('./error'),
|
||||
fetchData = require('./fetch-data'),
|
||||
formatResponse = require('./format-response'),
|
||||
channelConfig = require('./channel-config'),
|
||||
setResponseContext = require('./context'),
|
||||
setRequestIsSecure = require('./secure'),
|
||||
setRequestIsSecure = require('./secure'),
|
||||
|
||||
frontendControllers,
|
||||
staticPostPermalink = routeMatch('/:slug/:edit?');
|
||||
|
@ -41,69 +37,7 @@ function renderPost(req, res) {
|
|||
};
|
||||
}
|
||||
|
||||
function renderChannel(name) {
|
||||
return function renderChannel(req, res, next) {
|
||||
// Parse the parameters we need from the URL
|
||||
var channelOpts = channelConfig(name),
|
||||
pageParam = req.params.page !== undefined ? req.params.page : 1,
|
||||
slugParam = req.params.slug ? safeString(req.params.slug) : undefined;
|
||||
|
||||
// Ensure we at least have an empty object for postOptions
|
||||
channelOpts.postOptions = channelOpts.postOptions || {};
|
||||
// Set page on postOptions for the query made later
|
||||
channelOpts.postOptions.page = pageParam;
|
||||
channelOpts.slugParam = slugParam;
|
||||
|
||||
// Call fetchData to get everything we need from the API
|
||||
return fetchData(channelOpts).then(function handleResult(result) {
|
||||
// If page is greater than number of pages we have, go straight to 404
|
||||
if (pageParam > result.meta.pagination.pages) {
|
||||
return next(new errors.NotFoundError());
|
||||
}
|
||||
|
||||
// @TODO: figure out if this can be removed, it's supposed to ensure that absolutely URLs get generated
|
||||
// correctly for the various objects, but I believe it doesn't work and a different approach is needed.
|
||||
setRequestIsSecure(req, result.posts);
|
||||
_.each(result.data, function (data) {
|
||||
setRequestIsSecure(req, data);
|
||||
});
|
||||
|
||||
// @TODO: properly design these filters
|
||||
filters.doFilter('prePostsRender', result.posts, res.locals).then(function then(posts) {
|
||||
var view = templates.channel(req.app.get('activeTheme'), channelOpts);
|
||||
|
||||
// Do final data formatting and then render
|
||||
result.posts = posts;
|
||||
result = formatResponse.channel(result);
|
||||
setResponseContext(req, res);
|
||||
res.render(view, result);
|
||||
});
|
||||
}).catch(handleError(next));
|
||||
};
|
||||
}
|
||||
|
||||
frontendControllers = {
|
||||
index: renderChannel('index'),
|
||||
tag: renderChannel('tag'),
|
||||
author: renderChannel('author'),
|
||||
rss: function (req, res, next) {
|
||||
// Temporary hack, channels will allow us to resolve this better eventually
|
||||
var tagPattern = new RegExp('^\\/' + config.routeKeywords.tag + '\\/.+'),
|
||||
authorPattern = new RegExp('^\\/' + config.routeKeywords.author + '\\/.+');
|
||||
|
||||
if (tagPattern.test(res.locals.relativeUrl)) {
|
||||
req.channelConfig = channelConfig('tag');
|
||||
} else if (authorPattern.test(res.locals.relativeUrl)) {
|
||||
req.channelConfig = channelConfig('author');
|
||||
} else {
|
||||
req.channelConfig = channelConfig('index');
|
||||
}
|
||||
|
||||
req.channelConfig.isRSS = true;
|
||||
|
||||
return rss(req, res, next);
|
||||
},
|
||||
|
||||
preview: function preview(req, res, next) {
|
||||
var params = {
|
||||
uuid: req.params.uuid,
|
||||
|
|
51
core/server/controllers/frontend/render-channel.js
Normal file
51
core/server/controllers/frontend/render-channel.js
Normal file
|
@ -0,0 +1,51 @@
|
|||
var _ = require('lodash'),
|
||||
errors = require('../../errors'),
|
||||
filters = require('../../filters'),
|
||||
safeString = require('../../utils/index').safeString,
|
||||
handleError = require('./error'),
|
||||
fetchData = require('./fetch-data'),
|
||||
formatResponse = require('./format-response'),
|
||||
setResponseContext = require('./context'),
|
||||
setRequestIsSecure = require('./secure'),
|
||||
templates = require('./templates');
|
||||
|
||||
function renderChannel(req, res, next) {
|
||||
// Parse the parameters we need from the URL
|
||||
var channelOpts = req.channelConfig,
|
||||
pageParam = req.params.page !== undefined ? req.params.page : 1,
|
||||
slugParam = req.params.slug ? safeString(req.params.slug) : undefined;
|
||||
|
||||
// Ensure we at least have an empty object for postOptions
|
||||
channelOpts.postOptions = channelOpts.postOptions || {};
|
||||
// Set page on postOptions for the query made later
|
||||
channelOpts.postOptions.page = pageParam;
|
||||
channelOpts.slugParam = slugParam;
|
||||
|
||||
// Call fetchData to get everything we need from the API
|
||||
return fetchData(channelOpts).then(function handleResult(result) {
|
||||
// If page is greater than number of pages we have, go straight to 404
|
||||
if (pageParam > result.meta.pagination.pages) {
|
||||
return next(new errors.NotFoundError());
|
||||
}
|
||||
|
||||
// @TODO: figure out if this can be removed, it's supposed to ensure that absolutely URLs get generated
|
||||
// correctly for the various objects, but I believe it doesn't work and a different approach is needed.
|
||||
setRequestIsSecure(req, result.posts);
|
||||
_.each(result.data, function (data) {
|
||||
setRequestIsSecure(req, data);
|
||||
});
|
||||
|
||||
// @TODO: properly design these filters
|
||||
filters.doFilter('prePostsRender', result.posts, res.locals).then(function then(posts) {
|
||||
var view = templates.channel(req.app.get('activeTheme'), channelOpts);
|
||||
|
||||
// Do final data formatting and then render
|
||||
result.posts = posts;
|
||||
result = formatResponse.channel(result);
|
||||
setResponseContext(req, res);
|
||||
res.render(view, result);
|
||||
});
|
||||
}).catch(handleError(next));
|
||||
}
|
||||
|
||||
module.exports = renderChannel;
|
|
@ -1,6 +1,6 @@
|
|||
var frontend = require('../controllers/frontend'),
|
||||
channels = require('../controllers/frontend/channels'),
|
||||
config = require('../config'),
|
||||
errors = require('../errors'),
|
||||
express = require('express'),
|
||||
utils = require('../utils'),
|
||||
|
||||
|
@ -10,52 +10,19 @@ frontendRoutes = function frontendRoutes(middleware) {
|
|||
var router = express.Router(),
|
||||
subdir = config.paths.subdir,
|
||||
routeKeywords = config.routeKeywords,
|
||||
indexRouter = express.Router(),
|
||||
tagRouter = express.Router({mergeParams: true}),
|
||||
authorRouter = express.Router({mergeParams: true}),
|
||||
rssRouter = express.Router({mergeParams: true}),
|
||||
privateRouter = express.Router();
|
||||
|
||||
function redirect301(res, path) {
|
||||
/*jslint unparam:true*/
|
||||
res.set({'Cache-Control': 'public, max-age=' + utils.ONE_YEAR_S});
|
||||
res.redirect(301, path);
|
||||
}
|
||||
|
||||
function handlePageParam(req, res, next, page) {
|
||||
var pageRegex = new RegExp('/' + routeKeywords.page + '/(.*)?/'),
|
||||
rssRegex = new RegExp('/rss/(.*)?/');
|
||||
|
||||
page = parseInt(page, 10);
|
||||
|
||||
if (page === 1) {
|
||||
// Page 1 is an alias, do a permanent 301 redirect
|
||||
if (rssRegex.test(req.url)) {
|
||||
return redirect301(res, req.originalUrl.replace(rssRegex, '/rss/'));
|
||||
} else {
|
||||
return redirect301(res, req.originalUrl.replace(pageRegex, '/'));
|
||||
}
|
||||
} else if (page < 1 || isNaN(page)) {
|
||||
// Nothing less than 1 is a valid page number, go straight to a 404
|
||||
return next(new errors.NotFoundError());
|
||||
} else {
|
||||
// Set req.params.page to the already parsed number, and continue
|
||||
req.params.page = page;
|
||||
return next();
|
||||
}
|
||||
}
|
||||
|
||||
// ### Admin routes
|
||||
router.get(/^\/(logout|signout)\/$/, function redirectToSignout(req, res) {
|
||||
redirect301(res, subdir + '/ghost/signout/');
|
||||
utils.redirect301(res, subdir + '/ghost/signout/');
|
||||
});
|
||||
router.get(/^\/signup\/$/, function redirectToSignup(req, res) {
|
||||
redirect301(res, subdir + '/ghost/signup/');
|
||||
utils.redirect301(res, subdir + '/ghost/signup/');
|
||||
});
|
||||
|
||||
// redirect to /ghost and let that do the authentication to prevent redirects to /ghost//admin etc.
|
||||
router.get(/^\/((ghost-admin|admin|wp-admin|dashboard|signin|login)\/?)$/, function redirectToAdmin(req, res) {
|
||||
redirect301(res, subdir + '/ghost/');
|
||||
utils.redirect301(res, subdir + '/ghost/');
|
||||
});
|
||||
|
||||
// password-protected frontend route
|
||||
|
@ -71,46 +38,15 @@ frontendRoutes = function frontendRoutes(middleware) {
|
|||
frontend.private
|
||||
);
|
||||
|
||||
rssRouter.route('/rss/').get(frontend.rss);
|
||||
rssRouter.route('/rss/:page/').get(frontend.rss);
|
||||
rssRouter.route('/feed/').get(function redirect(req, res) {
|
||||
redirect301(res, subdir + '/rss/');
|
||||
});
|
||||
rssRouter.param('page', handlePageParam);
|
||||
|
||||
// Index
|
||||
indexRouter.route('/').get(frontend.index);
|
||||
indexRouter.route('/' + routeKeywords.page + '/:page/').get(frontend.index);
|
||||
indexRouter.param('page', handlePageParam);
|
||||
indexRouter.use(rssRouter);
|
||||
|
||||
// Tags
|
||||
tagRouter.route('/').get(frontend.tag);
|
||||
tagRouter.route('/' + routeKeywords.page + '/:page/').get(frontend.tag);
|
||||
tagRouter.route('/edit?').get(function redirect(req, res) {
|
||||
res.redirect(subdir + '/ghost/settings/tags/' + req.params.slug + '/');
|
||||
});
|
||||
tagRouter.param('page', handlePageParam);
|
||||
tagRouter.use(rssRouter);
|
||||
|
||||
// Authors
|
||||
authorRouter.route('/').get(frontend.author);
|
||||
authorRouter.route('/edit?').get(function redirect(req, res) {
|
||||
res.redirect(subdir + '/ghost/team/' + req.params.slug + '/');
|
||||
});
|
||||
authorRouter.route('/' + routeKeywords.page + '/:page/').get(frontend.author);
|
||||
authorRouter.param('page', handlePageParam);
|
||||
authorRouter.use(rssRouter);
|
||||
|
||||
// Mount the Routers
|
||||
router.use('/' + routeKeywords.private + '/', privateRouter);
|
||||
router.use('/' + routeKeywords.author + '/:slug/', authorRouter);
|
||||
router.use('/' + routeKeywords.tag + '/:slug/', tagRouter);
|
||||
router.use('/', indexRouter);
|
||||
|
||||
// Post Live Preview
|
||||
router.get('/' + routeKeywords.preview + '/:uuid', frontend.preview);
|
||||
|
||||
// Private
|
||||
router.use('/' + routeKeywords.private + '/', privateRouter);
|
||||
|
||||
// Channels
|
||||
router.use(channels.router());
|
||||
|
||||
// Default
|
||||
router.get('*', frontend.single);
|
||||
|
||||
|
|
|
@ -94,6 +94,11 @@ utils = {
|
|||
base64String += '=';
|
||||
}
|
||||
return base64String;
|
||||
},
|
||||
redirect301: function redirect301(res, path) {
|
||||
/*jslint unparam:true*/
|
||||
res.set({'Cache-Control': 'public, max-age=' + utils.ONE_YEAR_S});
|
||||
res.redirect(301, path);
|
||||
}
|
||||
};
|
||||
|
||||
|
|
554
core/test/functional/routes/channel_spec.js
Normal file
554
core/test/functional/routes/channel_spec.js
Normal file
|
@ -0,0 +1,554 @@
|
|||
/*global describe, it, before, after */
|
||||
|
||||
// # Channel Route Tests
|
||||
// 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
|
||||
var request = require('supertest'),
|
||||
should = require('should'),
|
||||
cheerio = require('cheerio'),
|
||||
|
||||
testUtils = require('../../utils'),
|
||||
ghost = require('../../../../core');
|
||||
|
||||
describe('Channel Routes', function () {
|
||||
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();
|
||||
};
|
||||
}
|
||||
|
||||
before(function (done) {
|
||||
ghost().then(function (ghostServer) {
|
||||
// Setup the request object with the ghost express app
|
||||
request = request(ghostServer.rootApp);
|
||||
|
||||
done();
|
||||
}).catch(function (e) {
|
||||
console.log('Ghost Error: ', e);
|
||||
console.log(e.stack);
|
||||
});
|
||||
});
|
||||
|
||||
after(testUtils.teardown);
|
||||
|
||||
describe('Index', function () {
|
||||
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);
|
||||
}
|
||||
|
||||
var $ = cheerio.load(res.text);
|
||||
|
||||
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');
|
||||
$('.content .post').length.should.equal(1);
|
||||
$('.poweredby').text().should.equal('Proudly published with Ghost');
|
||||
$('body.home-template').length.should.equal(1);
|
||||
$('article.post').length.should.equal(1);
|
||||
$('article.tag-getting-started').length.should.equal(1);
|
||||
|
||||
done();
|
||||
});
|
||||
});
|
||||
|
||||
it('should not have as second page', function (done) {
|
||||
request.get('/page/2/')
|
||||
.expect('Cache-Control', testUtils.cacheRules.private)
|
||||
.expect(404)
|
||||
.expect(/Page not found/)
|
||||
.end(doEnd(done));
|
||||
});
|
||||
|
||||
describe('RSS', function () {
|
||||
before(function (done) {
|
||||
testUtils.initData().then(function () {
|
||||
return testUtils.fixtures.overrideOwnerUser();
|
||||
}).then(function () {
|
||||
done();
|
||||
});
|
||||
});
|
||||
|
||||
after(testUtils.teardown);
|
||||
|
||||
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));
|
||||
});
|
||||
});
|
||||
|
||||
describe('Paged', function () {
|
||||
// Add enough posts to trigger pages for both the index (5 pp) and rss (15 pp)
|
||||
// insertPosts adds 5 published posts, 1 draft post, 1 published static page and one draft page
|
||||
// we then insert with max 11 which ensures we have 16 published posts
|
||||
before(function (done) {
|
||||
testUtils.initData().then(function () {
|
||||
return testUtils.fixtures.insertPosts();
|
||||
}).then(function () {
|
||||
return testUtils.fixtures.insertMorePosts(11);
|
||||
}).then(function () {
|
||||
done();
|
||||
}).catch(done);
|
||||
});
|
||||
|
||||
after(testUtils.teardown);
|
||||
|
||||
it('should redirect without slash', function (done) {
|
||||
request.get('/page/2')
|
||||
.expect('Location', '/page/2/')
|
||||
.expect('Cache-Control', testUtils.cacheRules.year)
|
||||
.expect(301)
|
||||
.end(doEnd(done));
|
||||
});
|
||||
|
||||
it('should respond with html', function (done) {
|
||||
request.get('/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('/page/1/')
|
||||
.expect('Location', '/')
|
||||
.expect('Cache-Control', testUtils.cacheRules.year)
|
||||
.expect(301)
|
||||
.end(doEnd(done));
|
||||
});
|
||||
|
||||
it('should 404 if page too high', function (done) {
|
||||
request.get('/page/5/')
|
||||
.expect('Cache-Control', testUtils.cacheRules.private)
|
||||
.expect(404)
|
||||
.expect(/Page not found/)
|
||||
.end(doEnd(done));
|
||||
});
|
||||
|
||||
it('should 404 if page is zero', function (done) {
|
||||
request.get('/page/0/')
|
||||
.expect('Cache-Control', testUtils.cacheRules.private)
|
||||
.expect(404)
|
||||
.expect(/Page not found/)
|
||||
.end(doEnd(done));
|
||||
});
|
||||
|
||||
it('should 404 if page is less than zero', function (done) {
|
||||
request.get('/page/-5/')
|
||||
.expect('Cache-Control', testUtils.cacheRules.private)
|
||||
.expect(404)
|
||||
.expect(/Page not found/)
|
||||
.end(doEnd(done));
|
||||
});
|
||||
|
||||
it('should 404 if page is NaN', function (done) {
|
||||
request.get('/page/one/')
|
||||
.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('/rss/2')
|
||||
.expect('Location', '/rss/2/')
|
||||
.expect('Cache-Control', testUtils.cacheRules.year)
|
||||
.expect(301)
|
||||
.end(doEnd(done));
|
||||
});
|
||||
|
||||
it('should respond with xml', function (done) {
|
||||
request.get('/rss/2/')
|
||||
.expect('Content-Type', /xml/)
|
||||
.expect('Cache-Control', testUtils.cacheRules.public)
|
||||
.expect(200)
|
||||
.end(doEnd(done));
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
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);
|
||||
});
|
||||
|
||||
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));
|
||||
});
|
||||
});
|
||||
|
||||
describe('Paged', function () {
|
||||
// Add enough posts to trigger pages
|
||||
before(function (done) {
|
||||
testUtils.initData().then(function () {
|
||||
return testUtils.fixtures.insertPosts();
|
||||
}).then(function () {
|
||||
return testUtils.fixtures.insertMorePosts(22);
|
||||
}).then(function () {
|
||||
return testUtils.fixtures.insertMorePostsTags(22);
|
||||
}).then(function () {
|
||||
done();
|
||||
}).catch(done);
|
||||
});
|
||||
|
||||
after(testUtils.teardown);
|
||||
|
||||
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));
|
||||
});
|
||||
|
||||
it('should respond with html', function (done) {
|
||||
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) {
|
||||
request.get('/tag/injection/page/4/')
|
||||
.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 () {
|
||||
it('should redirect page 1', function (done) {
|
||||
request.get('/tag/getting-started/rss/1/')
|
||||
.expect('Location', '/tag/getting-started/rss/')
|
||||
.expect('Cache-Control', testUtils.cacheRules.year)
|
||||
.expect(301)
|
||||
.end(doEnd(done));
|
||||
});
|
||||
|
||||
it('should 404 if page too high', function (done) {
|
||||
request.get('/tag/getting-started/rss/2/')
|
||||
.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/getting-started/rss/0/')
|
||||
.expect('Cache-Control', testUtils.cacheRules.private)
|
||||
.expect(404)
|
||||
.expect(/Page not found/)
|
||||
.end(doEnd(done));
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
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/')
|
||||
.expect('Location', '/ghost/settings/tags/getting-started/')
|
||||
.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));
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('Author', 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);
|
||||
});
|
||||
|
||||
after(testUtils.teardown);
|
||||
|
||||
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));
|
||||
});
|
||||
|
||||
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));
|
||||
});
|
||||
});
|
||||
|
||||
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 () {
|
||||
return testUtils.fixtures.overrideOwnerUser('ghost-owner');
|
||||
}).then(function () {
|
||||
return testUtils.fixtures.insertPosts();
|
||||
}).then(function () {
|
||||
return testUtils.fixtures.insertMorePosts(9);
|
||||
}).then(function () {
|
||||
done();
|
||||
}).catch(done);
|
||||
});
|
||||
|
||||
after(testUtils.teardown);
|
||||
|
||||
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));
|
||||
});
|
||||
|
||||
it('should respond with html', function (done) {
|
||||
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) {
|
||||
request.get('/author/ghost-owner/page/4/')
|
||||
.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 () {
|
||||
it('should redirect page 1', function (done) {
|
||||
request.get('/author/ghost-owner/rss/1/')
|
||||
.expect('Location', '/author/ghost-owner/rss/')
|
||||
.expect('Cache-Control', testUtils.cacheRules.year)
|
||||
.expect(301)
|
||||
.end(doEnd(done));
|
||||
});
|
||||
|
||||
it('should 404 if page too high', function (done) {
|
||||
request.get('/author/ghost-owner/rss/2/')
|
||||
.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/rss/0/')
|
||||
.expect('Cache-Control', testUtils.cacheRules.private)
|
||||
.expect(404)
|
||||
.expect(/Page not found/)
|
||||
.end(doEnd(done));
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
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/')
|
||||
.expect('Location', '/ghost/team/ghost-owner/')
|
||||
.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));
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
|
@ -79,30 +79,6 @@ describe('Frontend Routing', function () {
|
|||
.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));
|
||||
});
|
||||
|
||||
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 encoded char not 301 from uncapitalise', function (done) {
|
||||
request.get('/|/')
|
||||
.expect('Cache-Control', testUtils.cacheRules.private)
|
||||
|
@ -110,52 +86,6 @@ describe('Frontend Routing', function () {
|
|||
.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));
|
||||
});
|
||||
});
|
||||
|
||||
describe('Home', function () {
|
||||
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);
|
||||
}
|
||||
|
||||
var $ = cheerio.load(res.text);
|
||||
|
||||
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');
|
||||
$('.content .post').length.should.equal(1);
|
||||
$('.poweredby').text().should.equal('Proudly published with Ghost');
|
||||
$('body.home-template').length.should.equal(1);
|
||||
$('article.post').length.should.equal(1);
|
||||
$('article.tag-getting-started').length.should.equal(1);
|
||||
|
||||
done();
|
||||
});
|
||||
});
|
||||
|
||||
it('should not have as second page', function (done) {
|
||||
request.get('/page/2/')
|
||||
.expect('Cache-Control', testUtils.cacheRules.private)
|
||||
.expect(404)
|
||||
.expect(/Page not found/)
|
||||
.end(doEnd(done));
|
||||
});
|
||||
});
|
||||
|
||||
describe('Single post', function () {
|
||||
|
@ -365,427 +295,6 @@ describe('Frontend Routing', function () {
|
|||
});
|
||||
});
|
||||
|
||||
describe('Archive pages', function () {
|
||||
// Add enough posts to trigger pages for both the archive (5 pp) and rss (15 pp)
|
||||
// insertPosts adds 5 published posts, 1 draft post, 1 published static page and one draft page
|
||||
// we then insert with max 11 which ensures we have 16 published posts
|
||||
before(function (done) {
|
||||
testUtils.initData().then(function () {
|
||||
return testUtils.fixtures.insertPosts();
|
||||
}).then(function () {
|
||||
return testUtils.fixtures.insertMorePosts(9);
|
||||
}).then(function () {
|
||||
done();
|
||||
}).catch(done);
|
||||
});
|
||||
|
||||
after(testUtils.teardown);
|
||||
|
||||
it('should redirect without slash', function (done) {
|
||||
request.get('/page/2')
|
||||
.expect('Location', '/page/2/')
|
||||
.expect('Cache-Control', testUtils.cacheRules.year)
|
||||
.expect(301)
|
||||
.end(doEnd(done));
|
||||
});
|
||||
|
||||
it('should respond with html', function (done) {
|
||||
request.get('/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('/page/1/')
|
||||
.expect('Location', '/')
|
||||
.expect('Cache-Control', testUtils.cacheRules.year)
|
||||
.expect(301)
|
||||
.end(doEnd(done));
|
||||
});
|
||||
|
||||
it('should 404 if page too high', function (done) {
|
||||
request.get('/page/4/')
|
||||
.expect('Cache-Control', testUtils.cacheRules.private)
|
||||
.expect(404)
|
||||
.expect(/Page not found/)
|
||||
.end(doEnd(done));
|
||||
});
|
||||
|
||||
it('should 404 if page is zero', function (done) {
|
||||
request.get('/page/0/')
|
||||
.expect('Cache-Control', testUtils.cacheRules.private)
|
||||
.expect(404)
|
||||
.expect(/Page not found/)
|
||||
.end(doEnd(done));
|
||||
});
|
||||
|
||||
it('should 404 if page is less than zero', function (done) {
|
||||
request.get('/page/-5/')
|
||||
.expect('Cache-Control', testUtils.cacheRules.private)
|
||||
.expect(404)
|
||||
.expect(/Page not found/)
|
||||
.end(doEnd(done));
|
||||
});
|
||||
|
||||
it('should 404 if page is NaN', function (done) {
|
||||
request.get('/page/one/')
|
||||
.expect('Cache-Control', testUtils.cacheRules.private)
|
||||
.expect(404)
|
||||
.expect(/Page not found/)
|
||||
.end(doEnd(done));
|
||||
});
|
||||
});
|
||||
|
||||
describe('RSS', function () {
|
||||
/**
|
||||
* These tests are here to cover the headers sent with requests
|
||||
* and high-level redirects that can't be tested with the unit
|
||||
* tests in unit/rss_spec.js
|
||||
*/
|
||||
before(function (done) {
|
||||
testUtils.initData().then(function () {
|
||||
return testUtils.fixtures.overrideOwnerUser();
|
||||
}).then(function () {
|
||||
done();
|
||||
});
|
||||
});
|
||||
|
||||
after(testUtils.teardown);
|
||||
|
||||
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));
|
||||
});
|
||||
|
||||
describe('RSS pages', function () {
|
||||
before(function (done) {
|
||||
testUtils.fixtures.insertPosts().then(function () {
|
||||
return testUtils.fixtures.insertMorePosts(11);
|
||||
}).then(function () {
|
||||
done();
|
||||
}).catch(done);
|
||||
});
|
||||
it('should redirect without slash', function (done) {
|
||||
request.get('/rss/2')
|
||||
.expect('Location', '/rss/2/')
|
||||
.expect('Cache-Control', testUtils.cacheRules.year)
|
||||
.expect(301)
|
||||
.end(doEnd(done));
|
||||
});
|
||||
|
||||
it('should respond with xml', function (done) {
|
||||
request.get('/rss/2/')
|
||||
.expect('Content-Type', /xml/)
|
||||
.expect('Cache-Control', testUtils.cacheRules.public)
|
||||
.expect(200)
|
||||
.end(doEnd(done));
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('Author pages', function () {
|
||||
// Add enough posts to trigger tag 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 () {
|
||||
return testUtils.fixtures.overrideOwnerUser('ghost-owner');
|
||||
}).then(function () {
|
||||
return testUtils.fixtures.insertPosts();
|
||||
}).then(function () {
|
||||
return testUtils.fixtures.insertMorePosts(9);
|
||||
}).then(function () {
|
||||
done();
|
||||
}).catch(done);
|
||||
});
|
||||
|
||||
after(testUtils.teardown);
|
||||
|
||||
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 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));
|
||||
});
|
||||
|
||||
it('should respond with html', function (done) {
|
||||
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) {
|
||||
request.get('/author/ghost-owner/page/4/')
|
||||
.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('Author based RSS pages', 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));
|
||||
});
|
||||
|
||||
it('should redirect page 1', function (done) {
|
||||
request.get('/author/ghost-owner/rss/1/')
|
||||
.expect('Location', '/author/ghost-owner/rss/')
|
||||
.expect('Cache-Control', testUtils.cacheRules.year)
|
||||
.expect(301)
|
||||
.end(doEnd(done));
|
||||
});
|
||||
|
||||
it('should 404 if page too high', function (done) {
|
||||
request.get('/author/ghost-owner/rss/2/')
|
||||
.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/rss/0/')
|
||||
.expect('Cache-Control', testUtils.cacheRules.private)
|
||||
.expect(404)
|
||||
.expect(/Page not found/)
|
||||
.end(doEnd(done));
|
||||
});
|
||||
});
|
||||
|
||||
describe('Author 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/')
|
||||
.expect('Location', '/ghost/team/ghost-owner/')
|
||||
.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));
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('Tag pages', function () {
|
||||
// Add enough posts to trigger tag pages
|
||||
before(function (done) {
|
||||
testUtils.initData().then(function () {
|
||||
return testUtils.fixtures.insertPosts();
|
||||
}).then(function () {
|
||||
return testUtils.fixtures.insertMorePosts(22);
|
||||
}).then(function () {
|
||||
return testUtils.fixtures.insertMorePostsTags(22);
|
||||
}).then(function () {
|
||||
done();
|
||||
}).catch(done);
|
||||
});
|
||||
|
||||
after(testUtils.teardown);
|
||||
|
||||
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 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));
|
||||
});
|
||||
|
||||
it('should respond with html', function (done) {
|
||||
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) {
|
||||
request.get('/tag/injection/page/4/')
|
||||
.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('Tag based RSS pages', 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));
|
||||
});
|
||||
|
||||
it('should redirect page 1', function (done) {
|
||||
request.get('/tag/getting-started/rss/1/')
|
||||
.expect('Location', '/tag/getting-started/rss/')
|
||||
.expect('Cache-Control', testUtils.cacheRules.year)
|
||||
.expect(301)
|
||||
.end(doEnd(done));
|
||||
});
|
||||
|
||||
it('should 404 if page too high', function (done) {
|
||||
request.get('/tag/getting-started/rss/2/')
|
||||
.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/getting-started/rss/0/')
|
||||
.expect('Cache-Control', testUtils.cacheRules.private)
|
||||
.expect(404)
|
||||
.expect(/Page not found/)
|
||||
.end(doEnd(done));
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('Tag 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/')
|
||||
.expect('Location', '/ghost/settings/tags/getting-started/')
|
||||
.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));
|
||||
});
|
||||
});
|
||||
|
||||
describe('Subdirectory (no slash)', function () {
|
||||
var forkedGhost, request;
|
||||
before(function (done) {
|
||||
|
|
421
core/test/unit/controllers/frontend/channels_spec.js
Normal file
421
core/test/unit/controllers/frontend/channels_spec.js
Normal file
|
@ -0,0 +1,421 @@
|
|||
/*globals describe, before, beforeEach, afterEach, it*/
|
||||
var should = require('should'),
|
||||
sinon = require('sinon'),
|
||||
Promise = require('bluebird'),
|
||||
_ = require('lodash'),
|
||||
|
||||
// Stuff we are testing
|
||||
channels = require('../../../../server/controllers/frontend/channels'),
|
||||
api = require('../../../../server/api'),
|
||||
|
||||
configUtils = require('../../../utils/configUtils'),
|
||||
|
||||
sandbox = sinon.sandbox.create();
|
||||
|
||||
describe('Channels', function () {
|
||||
var channelRouter, req, res;
|
||||
|
||||
// Initialise 'req' with the bare minimum properties
|
||||
function setupRequest() {
|
||||
req = {
|
||||
method: 'get',
|
||||
app: {
|
||||
get: sandbox.stub().returns('casper')
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
// Prevent unit tests from failing via timeout when they should just immediately fail
|
||||
function failTest(done) {
|
||||
return function (err) {
|
||||
done(err || 'Next was called with no Error');
|
||||
};
|
||||
}
|
||||
|
||||
// Run a test which should result in a render
|
||||
function testChannelRender(props, assertions, done) {
|
||||
res = {
|
||||
redirect: sandbox.spy()
|
||||
};
|
||||
|
||||
res.render = function (view) {
|
||||
assertions(view);
|
||||
res.redirect.called.should.be.false();
|
||||
done();
|
||||
};
|
||||
|
||||
_.extend(req, props);
|
||||
|
||||
channelRouter(req, res, failTest(done));
|
||||
}
|
||||
|
||||
// Run a test which should result in a redirect
|
||||
function testChannelRedirect(props, assertions, done) {
|
||||
res = {
|
||||
render: sandbox.spy(),
|
||||
set: sandbox.spy()
|
||||
};
|
||||
|
||||
res.redirect = function (status, path) {
|
||||
assertions(status, path);
|
||||
res.render.called.should.be.false();
|
||||
done();
|
||||
};
|
||||
|
||||
_.extend(req, props);
|
||||
|
||||
channelRouter(req, res, failTest(done));
|
||||
}
|
||||
|
||||
// Run a test which should result in next() being called
|
||||
function testChannelNext(props, assertions, done) {
|
||||
res = {
|
||||
redirect: sandbox.spy(),
|
||||
render: sandbox.spy(),
|
||||
set: sandbox.spy()
|
||||
};
|
||||
|
||||
_.extend(req, props);
|
||||
|
||||
channelRouter(req, res, function (empty) {
|
||||
assertions(empty);
|
||||
res.redirect.called.should.be.false();
|
||||
res.render.called.should.be.false();
|
||||
done();
|
||||
});
|
||||
}
|
||||
|
||||
// Run a test which results in next being called with an error
|
||||
function testChannelError(props, assertions, done) {
|
||||
testChannelNext(props, function (error) {
|
||||
should.exist(error);
|
||||
assertions(error);
|
||||
}, done);
|
||||
}
|
||||
|
||||
// Run a test which results in an explicit 404
|
||||
function testChannel404(props, done) {
|
||||
testChannelError(props, function (error) {
|
||||
error.errorType.should.eql('NotFoundError');
|
||||
error.code.should.eql(404);
|
||||
}, done);
|
||||
}
|
||||
|
||||
before(function () {
|
||||
// We don't overwrite this, so only do it once
|
||||
channelRouter = channels.router();
|
||||
});
|
||||
|
||||
afterEach(function () {
|
||||
configUtils.restore();
|
||||
sandbox.restore();
|
||||
});
|
||||
|
||||
describe('Index', function () {
|
||||
var postAPIStub;
|
||||
|
||||
// Stub the posts api
|
||||
function setupPostsAPIStub() {
|
||||
postAPIStub = sandbox.stub(api.posts, 'browse', function () {
|
||||
return Promise.resolve({posts: [{}], meta: {pagination: {pages: 3}}});
|
||||
});
|
||||
}
|
||||
|
||||
// Return basic paths for the activeTheme
|
||||
function setupActiveTheme() {
|
||||
configUtils.set({paths: {availableThemes: {casper: {
|
||||
'index.hbs': '/content/themes/casper/index.hbs'
|
||||
}}}});
|
||||
}
|
||||
|
||||
beforeEach(function () {
|
||||
// Setup Env for tests
|
||||
setupPostsAPIStub();
|
||||
setupActiveTheme();
|
||||
setupRequest();
|
||||
});
|
||||
|
||||
it('should render the first page of the index channel', function (done) {
|
||||
testChannelRender({url: '/'}, function (view) {
|
||||
should.exist(view);
|
||||
view.should.eql('index');
|
||||
postAPIStub.calledOnce.should.be.true();
|
||||
}, done);
|
||||
});
|
||||
|
||||
it('should render the first page of the index channel using home.hbs if available', function (done) {
|
||||
configUtils.set({paths: {availableThemes: {casper: {
|
||||
'index.hbs': '/content/themes/casper/index.hbs',
|
||||
'home.hbs': '/content/themes/casper/home.hbs'
|
||||
}}}});
|
||||
|
||||
testChannelRender({url: '/'}, function (view) {
|
||||
should.exist(view);
|
||||
view.should.eql('home');
|
||||
postAPIStub.calledOnce.should.be.true();
|
||||
}, done);
|
||||
});
|
||||
|
||||
describe('Paged', function () {
|
||||
it('should render the second page of the index channel', function (done) {
|
||||
testChannelRender({url: '/page/2/'}, function (view) {
|
||||
should.exist(view);
|
||||
view.should.eql('index');
|
||||
postAPIStub.calledOnce.should.be.true();
|
||||
}, done);
|
||||
});
|
||||
|
||||
it('should use index.hbs for second page even if home.hbs is available', function (done) {
|
||||
configUtils.set({paths: {availableThemes: {casper: {
|
||||
'index.hbs': '/content/themes/casper/index.hbs',
|
||||
'home.hbs': '/content/themes/casper/home.hbs'
|
||||
}}}});
|
||||
|
||||
testChannelRender({url: '/page/2/'}, function (view) {
|
||||
should.exist(view);
|
||||
view.should.eql('index');
|
||||
postAPIStub.calledOnce.should.be.true();
|
||||
}, done);
|
||||
});
|
||||
|
||||
it('should render the third page of the index channel', function (done) {
|
||||
testChannelRender({url: '/page/3/'}, function (view) {
|
||||
should.exist(view);
|
||||
view.should.eql('index');
|
||||
postAPIStub.calledOnce.should.be.true();
|
||||
}, done);
|
||||
});
|
||||
|
||||
it('should redirect /page/1/ to /', function (done) {
|
||||
testChannelRedirect({url: '/page/1/'}, function (status, path) {
|
||||
status.should.eql(301);
|
||||
path.should.eql('/');
|
||||
|
||||
res.set.called.should.be.true();
|
||||
postAPIStub.called.should.be.false();
|
||||
}, done);
|
||||
});
|
||||
|
||||
it('should 404 for /page/0/', function (done) {
|
||||
testChannel404({url: '/page/0/'}, done);
|
||||
});
|
||||
|
||||
it('should 404 for /page/4/', function (done) {
|
||||
testChannel404({url: '/page/4/'}, done);
|
||||
});
|
||||
});
|
||||
|
||||
describe('RSS', function () {
|
||||
it('should redirect /feed/ to /rss/', function (done) {
|
||||
testChannelRedirect({url: '/feed/'}, function (status, path) {
|
||||
status.should.eql(301);
|
||||
path.should.eql('/rss/');
|
||||
|
||||
res.set.called.should.be.true();
|
||||
postAPIStub.called.should.be.false();
|
||||
}, done);
|
||||
});
|
||||
|
||||
it('should redirect /rss/1/ to /rss/', function (done) {
|
||||
testChannelRedirect({url: '/rss/1/'}, function (status, path) {
|
||||
status.should.eql(301);
|
||||
path.should.eql('/rss/');
|
||||
|
||||
res.set.called.should.be.true();
|
||||
postAPIStub.called.should.be.false();
|
||||
}, done);
|
||||
});
|
||||
|
||||
it('should 404 for /rss/0/', function (done) {
|
||||
testChannel404({url: '/rss/0/'}, done);
|
||||
});
|
||||
|
||||
it('should 404 for /rss/4/', function (done) {
|
||||
testChannel404({url: '/rss/4/'}, done);
|
||||
});
|
||||
});
|
||||
|
||||
describe('Edit', function () {
|
||||
it('should NOT redirect /edit/, should pass through', function (done) {
|
||||
testChannelNext({url: '/edit/'}, done);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('Tag', function () {
|
||||
var postAPIStub, tagAPIStub;
|
||||
|
||||
// Stub the posts and tags api
|
||||
function setupAPIStubs() {
|
||||
postAPIStub = sandbox.stub(api.posts, 'browse', function () {
|
||||
return Promise.resolve({posts: [{}], meta: {pagination: {pages: 3}}});
|
||||
});
|
||||
|
||||
tagAPIStub = sandbox.stub(api.tags, 'read', function () {
|
||||
return Promise.resolve({tags: [{}]});
|
||||
});
|
||||
}
|
||||
|
||||
// Return basic paths for the activeTheme
|
||||
function setupActiveTheme() {
|
||||
configUtils.set({paths: {availableThemes: {casper: {
|
||||
'index.hbs': '/content/themes/casper/index.hbs'
|
||||
}}}});
|
||||
}
|
||||
|
||||
beforeEach(function () {
|
||||
// Setup Env for tests
|
||||
setupAPIStubs();
|
||||
setupActiveTheme();
|
||||
setupRequest();
|
||||
});
|
||||
|
||||
it('should render the first page of the tag channel using index.hbs by default', function (done) {
|
||||
testChannelRender({url: '/tag/my-tag/'}, function (view) {
|
||||
should.exist(view);
|
||||
view.should.eql('index');
|
||||
postAPIStub.calledOnce.should.be.true();
|
||||
tagAPIStub.calledOnce.should.be.true();
|
||||
}, done);
|
||||
});
|
||||
|
||||
it('should render the first page of the tag channel using tag.hbs by default', function (done) {
|
||||
configUtils.set({paths: {availableThemes: {casper: {
|
||||
'index.hbs': '/content/themes/casper/index.hbs',
|
||||
'tag.hbs': '/content/themes/casper/tag.hbs'
|
||||
}}}});
|
||||
|
||||
testChannelRender({url: '/tag/my-tag/'}, function (view) {
|
||||
should.exist(view);
|
||||
view.should.eql('tag');
|
||||
postAPIStub.calledOnce.should.be.true();
|
||||
tagAPIStub.calledOnce.should.be.true();
|
||||
}, done);
|
||||
});
|
||||
|
||||
it('should render the first page of the tag channel using tag-:slug.hbs if available', function (done) {
|
||||
configUtils.set({paths: {availableThemes: {casper: {
|
||||
'index.hbs': '/content/themes/casper/index.hbs',
|
||||
'tag.hbs': '/content/themes/casper/tag.hbs',
|
||||
'tag-my-tag.hbs': '/content/themes/casper/tag-my-tag.hbs'
|
||||
}}}});
|
||||
|
||||
testChannelRender({url: '/tag/my-tag/'}, function (view) {
|
||||
should.exist(view);
|
||||
view.should.eql('tag-my-tag');
|
||||
postAPIStub.calledOnce.should.be.true();
|
||||
tagAPIStub.calledOnce.should.be.true();
|
||||
}, done);
|
||||
});
|
||||
|
||||
describe('Paged', function () {
|
||||
it('should render the second page of the tag channel', function (done) {
|
||||
testChannelRender({url: '/tag/my-tag/page/2/'}, function (view) {
|
||||
should.exist(view);
|
||||
view.should.eql('index');
|
||||
postAPIStub.calledOnce.should.be.true();
|
||||
}, done);
|
||||
});
|
||||
|
||||
it('should use tag.hbs to render the tag channel if available', function (done) {
|
||||
configUtils.set({paths: {availableThemes: {casper: {
|
||||
'index.hbs': '/content/themes/casper/index.hbs',
|
||||
'tag.hbs': '/content/themes/casper/tag.hbs'
|
||||
}}}});
|
||||
|
||||
testChannelRender({url: '/tag/my-tag/page/2/'}, function (view) {
|
||||
should.exist(view);
|
||||
view.should.eql('tag');
|
||||
postAPIStub.calledOnce.should.be.true();
|
||||
}, done);
|
||||
});
|
||||
|
||||
it('should use tag-:slug.hbs to render the tag channel if available', function (done) {
|
||||
configUtils.set({paths: {availableThemes: {casper: {
|
||||
'index.hbs': '/content/themes/casper/index.hbs',
|
||||
'tag.hbs': '/content/themes/casper/tag.hbs',
|
||||
'tag-my-tag.hbs': '/content/themes/casper/tag-my-tag.hbs'
|
||||
}}}});
|
||||
|
||||
testChannelRender({url: '/tag/my-tag/page/2/'}, function (view) {
|
||||
should.exist(view);
|
||||
view.should.eql('tag-my-tag');
|
||||
postAPIStub.calledOnce.should.be.true();
|
||||
}, done);
|
||||
});
|
||||
|
||||
it('should render the second page of the tag channel', function (done) {
|
||||
testChannelRender({url: '/tag/my-tag/page/2/'}, function (view) {
|
||||
should.exist(view);
|
||||
view.should.eql('index');
|
||||
postAPIStub.calledOnce.should.be.true();
|
||||
}, done);
|
||||
});
|
||||
|
||||
it('should render the third page of the tag channel', function (done) {
|
||||
testChannelRender({url: '/tag/my-tag/page/3/'}, function (view) {
|
||||
should.exist(view);
|
||||
view.should.eql('index');
|
||||
postAPIStub.calledOnce.should.be.true();
|
||||
}, done);
|
||||
});
|
||||
|
||||
it('should redirect /tag/my-tag/page/1/ to /tag/my-tag/', function (done) {
|
||||
testChannelRedirect({url: '/tag/my-tag/page/1/'}, function (status, path) {
|
||||
status.should.eql(301);
|
||||
path.should.eql('/tag/my-tag/');
|
||||
|
||||
res.set.called.should.be.true();
|
||||
postAPIStub.called.should.be.false();
|
||||
}, done);
|
||||
});
|
||||
|
||||
it('should 404 for /tag/my-tag/page/0/', function (done) {
|
||||
testChannel404({url: '/tag/my-tag/page/0/'}, done);
|
||||
});
|
||||
|
||||
it('should 404 for /tag/my-tag/page/4/', function (done) {
|
||||
testChannel404({url: '/tag/my-tag/page/4/'}, done);
|
||||
});
|
||||
});
|
||||
|
||||
describe('RSS', function () {
|
||||
it('should redirect /tag/my-tag/feed/ to /tag/my-tag/rss/', function (done) {
|
||||
testChannelRedirect({url: '/tag/my-tag/feed/'}, function (status, path) {
|
||||
status.should.eql(301);
|
||||
path.should.eql('/tag/my-tag/rss/');
|
||||
|
||||
res.set.called.should.be.true();
|
||||
postAPIStub.called.should.be.false();
|
||||
}, done);
|
||||
});
|
||||
|
||||
it('should redirect /tag/my-tag/rss/1/ to /tag/my-tag/rss/', function (done) {
|
||||
testChannelRedirect({url: '/tag/my-tag/rss/1/'}, function (status, path) {
|
||||
status.should.eql(301);
|
||||
path.should.eql('/tag/my-tag/rss/');
|
||||
|
||||
res.set.called.should.be.true();
|
||||
postAPIStub.called.should.be.false();
|
||||
}, done);
|
||||
});
|
||||
|
||||
it('should 404 for /tag/my-tag/rss/0/', function (done) {
|
||||
testChannel404({url: '/tag/my-tag/rss/0/'}, done);
|
||||
});
|
||||
|
||||
it('should 404 for /tag/my-tag/rss/4/', function (done) {
|
||||
testChannel404({url: '/tag/my-tag/rss/4/'}, done);
|
||||
});
|
||||
});
|
||||
|
||||
describe('Edit', function () {
|
||||
it('should redirect /edit/ to ghost admin', function (done) {
|
||||
testChannelRedirect({url: '/tag/my-tag/edit/'}, function (path) {
|
||||
path.should.eql('/ghost/settings/tags/my-tag/');
|
||||
postAPIStub.called.should.be.false();
|
||||
}, done);
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
|
@ -34,177 +34,6 @@ describe('Frontend Controller', function () {
|
|||
};
|
||||
}
|
||||
|
||||
describe('index', function () {
|
||||
var req, res;
|
||||
|
||||
beforeEach(function () {
|
||||
sandbox.stub(api.posts, 'browse', function () {
|
||||
return Promise.resolve({
|
||||
posts: [],
|
||||
meta: {
|
||||
pagination: {
|
||||
page: 1,
|
||||
pages: 3
|
||||
}
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
configUtils.set({
|
||||
theme: {
|
||||
permalinks: '/:slug/',
|
||||
postsPerPage: 10
|
||||
}
|
||||
});
|
||||
|
||||
req = {
|
||||
app: {get: function () { return 'casper';}},
|
||||
path: '/', params: {}, route: {}
|
||||
};
|
||||
|
||||
res = {
|
||||
locals: {}
|
||||
};
|
||||
});
|
||||
|
||||
it('Renders home.hbs template when it exists in the active theme', function (done) {
|
||||
configUtils.set({paths: {availableThemes: {casper: {
|
||||
'index.hbs': '/content/themes/casper/index.hbs',
|
||||
'home.hbs': '/content/themes/casper/home.hbs'
|
||||
}}}});
|
||||
|
||||
res.render = function (view) {
|
||||
view.should.equal('home');
|
||||
done();
|
||||
};
|
||||
|
||||
frontend.index(req, res, failTest(done));
|
||||
});
|
||||
|
||||
it('Renders index.hbs template on 2nd page when home.hbs exists', function (done) {
|
||||
configUtils.set({paths: {availableThemes: {casper: {
|
||||
'index.hbs': '/content/themes/casper/index.hbs',
|
||||
'home.hbs': '/content/themes/casper/home.hbs'
|
||||
}}}});
|
||||
|
||||
req.path = '/page/2/';
|
||||
req.params = {page: 2};
|
||||
|
||||
res.render = function (view) {
|
||||
// assertion
|
||||
view.should.equal('index');
|
||||
done();
|
||||
};
|
||||
|
||||
frontend.index(req, res, failTest(done));
|
||||
});
|
||||
|
||||
it('Renders index.hbs template when home.hbs doesn\'t exist', function (done) {
|
||||
configUtils.set({paths: {availableThemes: {casper: {
|
||||
'index.hbs': '/content/themes/casper/index.hbs'
|
||||
}}}});
|
||||
|
||||
res.render = function (view) {
|
||||
view.should.equal('index');
|
||||
done();
|
||||
};
|
||||
|
||||
frontend.index(req, res, failTest(done));
|
||||
});
|
||||
});
|
||||
|
||||
describe('tag', function () {
|
||||
var req, res,
|
||||
mockTags = [{
|
||||
name: 'video',
|
||||
slug: 'video',
|
||||
id: 1
|
||||
}, {
|
||||
name: 'audio',
|
||||
slug: 'audio',
|
||||
id: 2
|
||||
}];
|
||||
|
||||
beforeEach(function () {
|
||||
sandbox.stub(api.posts, 'browse').returns(new Promise.resolve({
|
||||
posts: [{}],
|
||||
meta: {pagination: {page: 1, pages: 1}}
|
||||
}));
|
||||
|
||||
sandbox.stub(api.tags, 'read').returns(new Promise.resolve({tags: [mockTags[0]]}));
|
||||
|
||||
configUtils.set({
|
||||
theme: {
|
||||
permalinks: '/tag/:slug/',
|
||||
postsPerPage: '10'
|
||||
}
|
||||
});
|
||||
|
||||
req = {
|
||||
app: {get: function () { return 'casper';}},
|
||||
path: '/', params: {}, route: {}
|
||||
};
|
||||
|
||||
res = {
|
||||
locals: {}
|
||||
};
|
||||
});
|
||||
|
||||
it('it will render custom tag-slug template if it exists', function (done) {
|
||||
configUtils.set({paths: {availableThemes: {casper: {
|
||||
'tag-video.hbs': '/content/themes/casper/tag-video.hbs',
|
||||
'tag.hbs': '/content/themes/casper/tag.hbs',
|
||||
'index.hbs': '/content/themes/casper/index.hbs'
|
||||
}}}});
|
||||
|
||||
req.path = '/tag/' + mockTags[0].slug;
|
||||
req.params.slug = mockTags[0].slug;
|
||||
req.route = {path: '/tag/:slug'};
|
||||
res.render = function (view, context) {
|
||||
view.should.equal('tag-video');
|
||||
context.tag.should.eql(mockTags[0]);
|
||||
done();
|
||||
};
|
||||
|
||||
frontend.tag(req, res, failTest(done));
|
||||
});
|
||||
|
||||
it('it will render tag template if it exists and there is no tag-slug template', function (done) {
|
||||
configUtils.set({paths: {availableThemes: {casper: {
|
||||
'tag.hbs': '/content/themes/casper/tag.hbs',
|
||||
'index.hbs': '/content/themes/casper/index.hbs'
|
||||
}}}});
|
||||
|
||||
req.path = '/tag/' + mockTags[0].slug;
|
||||
req.params.slug = mockTags[0].slug;
|
||||
req.route = {path: '/tag/:slug'};
|
||||
res.render = function (view, context) {
|
||||
view.should.equal('tag');
|
||||
context.tag.should.eql(mockTags[0]);
|
||||
done();
|
||||
};
|
||||
|
||||
frontend.tag(req, res, failTest(done));
|
||||
});
|
||||
|
||||
it('it will fall back to index if there are no custom templates', function (done) {
|
||||
configUtils.set({paths: {availableThemes: {casper: {
|
||||
'index.hbs': '/content/themes/casper/index.hbs'
|
||||
}}}});
|
||||
|
||||
req.path = '/tag/' + mockTags[0].slug;
|
||||
req.params.slug = mockTags[0].slug;
|
||||
req.route = {path: '/tag/:slug'};
|
||||
res.render = function (view, context) {
|
||||
view.should.equal('index');
|
||||
context.tag.should.eql(mockTags[0]);
|
||||
done();
|
||||
};
|
||||
|
||||
frontend.tag(req, res, failTest(done));
|
||||
});
|
||||
});
|
||||
|
||||
describe('single', function () {
|
||||
var req, res, casper, mockPosts = [{
|
||||
posts: [{
|
||||
|
|
|
@ -104,7 +104,7 @@ describe('RSS', function () {
|
|||
done();
|
||||
};
|
||||
|
||||
req.channelConfig = channelConfig('index');
|
||||
req.channelConfig = channelConfig.get('index');
|
||||
req.channelConfig.isRSS = true;
|
||||
rss(req, res, failTest(done));
|
||||
});
|
||||
|
@ -146,7 +146,7 @@ describe('RSS', function () {
|
|||
done();
|
||||
};
|
||||
|
||||
req.channelConfig = channelConfig('index');
|
||||
req.channelConfig = channelConfig.get('index');
|
||||
req.channelConfig.isRSS = true;
|
||||
rss(req, res, failTest(done));
|
||||
});
|
||||
|
@ -175,7 +175,7 @@ describe('RSS', function () {
|
|||
done();
|
||||
};
|
||||
|
||||
req.channelConfig = channelConfig('index');
|
||||
req.channelConfig = channelConfig.get('index');
|
||||
req.channelConfig.isRSS = true;
|
||||
rss(req, res, failTest(done));
|
||||
});
|
||||
|
@ -209,7 +209,7 @@ describe('RSS', function () {
|
|||
done();
|
||||
};
|
||||
|
||||
req.channelConfig = channelConfig('index');
|
||||
req.channelConfig = channelConfig.get('index');
|
||||
req.channelConfig.isRSS = true;
|
||||
rss(req, res, failTest(done));
|
||||
});
|
||||
|
@ -241,7 +241,7 @@ describe('RSS', function () {
|
|||
done();
|
||||
};
|
||||
|
||||
req.channelConfig = channelConfig('index');
|
||||
req.channelConfig = channelConfig.get('index');
|
||||
req.channelConfig.isRSS = true;
|
||||
rss(req, res, failTest(done));
|
||||
});
|
||||
|
@ -289,7 +289,7 @@ describe('RSS', function () {
|
|||
done();
|
||||
};
|
||||
|
||||
req.channelConfig = channelConfig('index');
|
||||
req.channelConfig = channelConfig.get('index');
|
||||
req.channelConfig.isRSS = true;
|
||||
rss(req, res, failTest(done));
|
||||
});
|
||||
|
@ -298,7 +298,7 @@ describe('RSS', function () {
|
|||
// setup
|
||||
req.originalUrl = '/tag/magic/rss/';
|
||||
req.params.slug = 'magic';
|
||||
req.channelConfig = channelConfig('tag');
|
||||
req.channelConfig = channelConfig.get('tag');
|
||||
req.channelConfig.isRSS = true;
|
||||
|
||||
// test
|
||||
|
@ -316,7 +316,7 @@ describe('RSS', function () {
|
|||
it('should process the data correctly for an author feed', function (done) {
|
||||
req.originalUrl = '/author/joe/rss/';
|
||||
req.params.slug = 'joe';
|
||||
req.channelConfig = channelConfig('author');
|
||||
req.channelConfig = channelConfig.get('author');
|
||||
req.channelConfig.isRSS = true;
|
||||
|
||||
// test
|
||||
|
@ -359,7 +359,7 @@ describe('RSS', function () {
|
|||
results: {posts: [], meta: {pagination: {pages: 1}}}
|
||||
});
|
||||
});
|
||||
req.channelConfig = channelConfig('index');
|
||||
req.channelConfig = channelConfig.get('index');
|
||||
req.channelConfig.isRSS = true;
|
||||
|
||||
function secondCall() {
|
||||
|
@ -412,7 +412,7 @@ describe('RSS', function () {
|
|||
|
||||
req = {params: {page: 4}, route: {path: '/rss/:page/'}};
|
||||
req.originalUrl = req.route.path.replace(':page', req.params.page);
|
||||
req.channelConfig = channelConfig('index');
|
||||
req.channelConfig = channelConfig.get('index');
|
||||
req.channelConfig.isRSS = true;
|
||||
|
||||
rss(req, res, function (err) {
|
||||
|
@ -429,7 +429,7 @@ describe('RSS', function () {
|
|||
|
||||
req = {params: {page: 4}, route: {path: '/rss/:page/'}};
|
||||
req.originalUrl = req.route.path.replace(':page', req.params.page);
|
||||
req.channelConfig = channelConfig('index');
|
||||
req.channelConfig = channelConfig.get('index');
|
||||
req.channelConfig.isRSS = true;
|
||||
|
||||
rss(req, res, function (err) {
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
/*globals describe, it*/
|
||||
/*jshint expr:true*/
|
||||
var should = require('should'),
|
||||
sinon = require('sinon'),
|
||||
parsePackageJson = require('../../server/utils/parse-package-json'),
|
||||
validateThemes = require('../../server/utils/validate-themes'),
|
||||
readDirectory = require('../../server/utils/read-directory'),
|
||||
|
@ -455,4 +456,22 @@ describe('Server Utilities', function () {
|
|||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('redirect301', function () {
|
||||
it('performs a 301 correctly', function (done) {
|
||||
var res = {};
|
||||
|
||||
res.set = sinon.spy();
|
||||
|
||||
res.redirect = function (code, path) {
|
||||
code.should.equal(301);
|
||||
path.should.eql('my/awesome/path');
|
||||
res.set.calledWith({'Cache-Control': 'public, max-age=' + utils.ONE_YEAR_S}).should.be.true();
|
||||
|
||||
done();
|
||||
};
|
||||
|
||||
utils.redirect301(res, 'my/awesome/path');
|
||||
});
|
||||
});
|
||||
});
|
||||
|
|
Loading…
Add table
Reference in a new issue