mirror of
https://github.com/TryGhost/Ghost.git
synced 2025-01-01 23:37:43 +03:00
✨ Reimplement custom theme templates (#8147)
closes #8082 - Update the `pickTemplate` logic to a) rely on getActive().hasTemplate() instead of being passed a list of paths b) support the concept of a fallback, which is returned if there is no theme, or if the theme doesn't have a more specific template - Update every instance of template picking, across the 3 internalApps, and render-channel, to use this new logic - update the tests
This commit is contained in:
parent
b06f03b370
commit
bb3cc8c0f8
@ -12,8 +12,8 @@ var path = require('path'),
|
||||
setResponseContext = require('../../../controllers/frontend/context');
|
||||
|
||||
function controller(req, res, next) {
|
||||
var defaultView = path.resolve(__dirname, 'views', 'amp.hbs'),
|
||||
paths = templates.getActiveThemePaths(req.app.get('activeTheme')),
|
||||
var templateName = 'amp',
|
||||
defaultTemplate = path.resolve(__dirname, 'views', templateName + '.hbs'),
|
||||
data = req.body || {};
|
||||
|
||||
if (res.error) {
|
||||
@ -24,14 +24,10 @@ function controller(req, res, next) {
|
||||
|
||||
// we have to check the context. Our context must be ['post', 'amp'], otherwise we won't render the template
|
||||
if (_.includes(res.locals.context, 'post') && _.includes(res.locals.context, 'amp')) {
|
||||
if (paths.hasOwnProperty('amp.hbs')) {
|
||||
return res.render('amp', data);
|
||||
} else {
|
||||
return res.render(defaultView, data);
|
||||
}
|
||||
} else {
|
||||
return next();
|
||||
return res.render(templates.pickTemplate(templateName, defaultTemplate), data);
|
||||
}
|
||||
|
||||
return next();
|
||||
}
|
||||
|
||||
function getPostData(req, res, next) {
|
||||
|
@ -7,7 +7,7 @@ var rewire = require('rewire'),
|
||||
errors = require('../../../errors'),
|
||||
should = require('should'),
|
||||
configUtils = require('../../../../test/utils/configUtils'),
|
||||
themeList = require('../../../themes').list,
|
||||
themes = require('../../../themes'),
|
||||
sandbox = sinon.sandbox.create();
|
||||
|
||||
// Helper function to prevent unit tests
|
||||
@ -23,9 +23,17 @@ describe('AMP Controller', function () {
|
||||
var res,
|
||||
req,
|
||||
defaultPath,
|
||||
setResponseContextStub;
|
||||
setResponseContextStub,
|
||||
hasTemplateStub;
|
||||
|
||||
beforeEach(function () {
|
||||
hasTemplateStub = sandbox.stub().returns(false);
|
||||
hasTemplateStub.withArgs('index').returns(true);
|
||||
|
||||
sandbox.stub(themes, 'getActive').returns({
|
||||
hasTemplate: hasTemplateStub
|
||||
});
|
||||
|
||||
res = {
|
||||
render: sandbox.spy(),
|
||||
locals: {
|
||||
@ -34,7 +42,6 @@ describe('AMP Controller', function () {
|
||||
};
|
||||
|
||||
req = {
|
||||
app: {get: function () { return 'casper'; }},
|
||||
route: {path: '/'},
|
||||
query: {r: ''},
|
||||
params: {},
|
||||
@ -54,12 +61,9 @@ describe('AMP Controller', function () {
|
||||
afterEach(function () {
|
||||
sandbox.restore();
|
||||
configUtils.restore();
|
||||
themeList.init();
|
||||
});
|
||||
|
||||
it('should render default amp page when theme has no amp template', function (done) {
|
||||
themeList.init({casper: {}});
|
||||
|
||||
setResponseContextStub = sandbox.stub();
|
||||
ampController.__set__('setResponseContext', setResponseContextStub);
|
||||
|
||||
@ -72,9 +76,7 @@ describe('AMP Controller', function () {
|
||||
});
|
||||
|
||||
it('should render theme amp page when theme has amp template', function (done) {
|
||||
themeList.init({casper: {
|
||||
'amp.hbs': '/content/themes/casper/amp.hbs'
|
||||
}});
|
||||
hasTemplateStub.withArgs('amp').returns(true);
|
||||
|
||||
setResponseContextStub = sandbox.stub();
|
||||
ampController.__set__('setResponseContext', setResponseContextStub);
|
||||
@ -88,7 +90,6 @@ describe('AMP Controller', function () {
|
||||
});
|
||||
|
||||
it('should render with error when error is passed in', function (done) {
|
||||
themeList.init({casper: {}});
|
||||
res.error = 'Test Error';
|
||||
|
||||
setResponseContextStub = sandbox.stub();
|
||||
@ -105,7 +106,6 @@ describe('AMP Controller', function () {
|
||||
|
||||
it('does not render amp page when amp context is missing', function (done) {
|
||||
var renderSpy;
|
||||
themeList.init({casper: {}});
|
||||
|
||||
setResponseContextStub = sandbox.stub();
|
||||
ampController.__set__('setResponseContext', setResponseContextStub);
|
||||
@ -123,7 +123,6 @@ describe('AMP Controller', function () {
|
||||
|
||||
it('does not render amp page when context is other than amp and post', function (done) {
|
||||
var renderSpy;
|
||||
themeList.init({casper: {}});
|
||||
|
||||
setResponseContextStub = sandbox.stub();
|
||||
ampController.__set__('setResponseContext', setResponseContextStub);
|
||||
|
@ -5,11 +5,12 @@ var path = require('path'),
|
||||
templates = require('../../../controllers/frontend/templates'),
|
||||
setResponseContext = require('../../../controllers/frontend/context'),
|
||||
brute = require('../../../middleware/brute'),
|
||||
|
||||
privateRouter = express.Router();
|
||||
|
||||
function controller(req, res) {
|
||||
var defaultView = path.resolve(__dirname, 'views', 'private.hbs'),
|
||||
paths = templates.getActiveThemePaths(req.app.get('activeTheme')),
|
||||
var templateName = 'private',
|
||||
defaultTemplate = path.resolve(__dirname, 'views', templateName + '.hbs'),
|
||||
data = {};
|
||||
|
||||
if (res.error) {
|
||||
@ -17,11 +18,8 @@ function controller(req, res) {
|
||||
}
|
||||
|
||||
setResponseContext(req, res);
|
||||
if (paths.hasOwnProperty('private.hbs')) {
|
||||
return res.render('private', data);
|
||||
} else {
|
||||
return res.render(defaultView, data);
|
||||
}
|
||||
|
||||
return res.render(templates.pickTemplate(templateName, defaultTemplate), data);
|
||||
}
|
||||
|
||||
// password-protected frontend route
|
||||
|
@ -1,13 +1,14 @@
|
||||
/*globals describe, beforeEach, afterEach, it*/
|
||||
var privateController = require('../lib/router').controller,
|
||||
should = require('should'),
|
||||
path = require('path'),
|
||||
sinon = require('sinon'),
|
||||
configUtils = require('../../../../test/utils/configUtils'),
|
||||
themeList = require('../../../themes').list,
|
||||
themes = require('../../../themes'),
|
||||
sandbox = sinon.sandbox.create();
|
||||
|
||||
describe('Private Controller', function () {
|
||||
var res, req, defaultPath;
|
||||
var res, req, defaultPath, hasTemplateStub;
|
||||
|
||||
// Helper function to prevent unit tests
|
||||
// from failing via timeout when they
|
||||
@ -19,13 +20,19 @@ describe('Private Controller', function () {
|
||||
}
|
||||
|
||||
beforeEach(function () {
|
||||
hasTemplateStub = sandbox.stub().returns(false);
|
||||
hasTemplateStub.withArgs('index').returns(true);
|
||||
|
||||
sandbox.stub(themes, 'getActive').returns({
|
||||
hasTemplate: hasTemplateStub
|
||||
});
|
||||
|
||||
res = {
|
||||
locals: {version: ''},
|
||||
render: sandbox.spy()
|
||||
};
|
||||
|
||||
req = {
|
||||
app: {get: function () { return 'casper'; }},
|
||||
route: {path: '/private/?r=/'},
|
||||
query: {r: ''},
|
||||
params: {}
|
||||
@ -43,14 +50,12 @@ describe('Private Controller', function () {
|
||||
afterEach(function () {
|
||||
sandbox.restore();
|
||||
configUtils.restore();
|
||||
themeList.init();
|
||||
});
|
||||
|
||||
it('Should render default password page when theme has no password template', function (done) {
|
||||
themeList.init({casper: {}});
|
||||
|
||||
res.render = function (view) {
|
||||
res.render = function (view, context) {
|
||||
view.should.eql(defaultPath);
|
||||
should.exist(context);
|
||||
done();
|
||||
};
|
||||
|
||||
@ -58,12 +63,11 @@ describe('Private Controller', function () {
|
||||
});
|
||||
|
||||
it('Should render theme password page when it exists', function (done) {
|
||||
themeList.init({casper: {
|
||||
'private.hbs': '/content/themes/casper/private.hbs'
|
||||
}});
|
||||
hasTemplateStub.withArgs('private').returns(true);
|
||||
|
||||
res.render = function (view) {
|
||||
res.render = function (view, context) {
|
||||
view.should.eql('private');
|
||||
should.exist(context);
|
||||
done();
|
||||
};
|
||||
|
||||
@ -71,7 +75,6 @@ describe('Private Controller', function () {
|
||||
});
|
||||
|
||||
it('Should render with error when error is passed in', function (done) {
|
||||
themeList.init({casper: {}});
|
||||
res.error = 'Test Error';
|
||||
|
||||
res.render = function (view, context) {
|
||||
|
@ -13,16 +13,13 @@ var path = require('path'),
|
||||
setResponseContext = require('../../../controllers/frontend/context');
|
||||
|
||||
function controller(req, res) {
|
||||
var defaultView = path.resolve(__dirname, 'views', 'subscribe.hbs'),
|
||||
paths = templates.getActiveThemePaths(req.app.get('activeTheme')),
|
||||
var templateName = 'subscribe',
|
||||
defaultTemplate = path.resolve(__dirname, 'views', templateName + '.hbs'),
|
||||
data = req.body;
|
||||
|
||||
setResponseContext(req, res);
|
||||
if (paths.hasOwnProperty('subscribe.hbs')) {
|
||||
return res.render('subscribe', data);
|
||||
} else {
|
||||
return res.render(defaultView, data);
|
||||
}
|
||||
|
||||
return res.render(templates.pickTemplate(templateName, defaultTemplate), data);
|
||||
}
|
||||
|
||||
function errorHandler(error, req, res, next) {
|
||||
|
@ -26,7 +26,7 @@ var debug = require('debug')('ghost:channels:single'),
|
||||
function renderPost(req, res) {
|
||||
debug('renderPost called');
|
||||
return function renderPost(post) {
|
||||
var view = templates.single(req.app.get('activeTheme'), post),
|
||||
var view = templates.single(post),
|
||||
response = formatResponse.single(post);
|
||||
|
||||
setResponseContext(req, res, response);
|
||||
|
@ -40,7 +40,7 @@ function renderChannel(req, res, next) {
|
||||
|
||||
// @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);
|
||||
var view = templates.channel(channelOpts);
|
||||
|
||||
// Do final data formatting and then render
|
||||
result.posts = posts;
|
||||
|
@ -3,11 +3,7 @@
|
||||
// Figure out which template should be used to render a request
|
||||
// based on the templates which are allowed, and what is available in the theme
|
||||
var _ = require('lodash'),
|
||||
themeList = require('../../themes').list;
|
||||
|
||||
function getActiveThemePaths(activeTheme) {
|
||||
return themeList.get(activeTheme);
|
||||
}
|
||||
themes = require('../../themes');
|
||||
|
||||
/**
|
||||
* ## Get Channel Template Hierarchy
|
||||
@ -70,31 +66,45 @@ function getSingleTemplateHierarchy(single) {
|
||||
* Taking the ordered list of allowed templates for this request
|
||||
* Cycle through and find the first one which has a match in the theme
|
||||
*
|
||||
* @param {Object} themePaths
|
||||
* @param {Array} templateList
|
||||
* @param {Array|String} templateList
|
||||
* @param {String} fallback - a fallback template
|
||||
*/
|
||||
function pickTemplate(themePaths, templateList) {
|
||||
var template = _.find(templateList, function (template) {
|
||||
return themePaths.hasOwnProperty(template + '.hbs');
|
||||
});
|
||||
function pickTemplate(templateList, fallback) {
|
||||
var template;
|
||||
|
||||
if (!_.isArray(templateList)) {
|
||||
templateList = [templateList];
|
||||
}
|
||||
|
||||
if (!themes.getActive()) {
|
||||
template = fallback;
|
||||
} else {
|
||||
template = _.find(templateList, function (template) {
|
||||
return themes.getActive().hasTemplate(template);
|
||||
});
|
||||
}
|
||||
|
||||
if (!template) {
|
||||
template = templateList[templateList.length - 1];
|
||||
template = fallback;
|
||||
}
|
||||
|
||||
return template;
|
||||
}
|
||||
|
||||
function getTemplateForSingle(activeTheme, single) {
|
||||
return pickTemplate(getActiveThemePaths(activeTheme), getSingleTemplateHierarchy(single));
|
||||
function getTemplateForSingle(single) {
|
||||
var templateList = getSingleTemplateHierarchy(single),
|
||||
fallback = templateList[templateList.length - 1];
|
||||
return pickTemplate(templateList, fallback);
|
||||
}
|
||||
|
||||
function getTemplateForChannel(activeTheme, channelOpts) {
|
||||
return pickTemplate(getActiveThemePaths(activeTheme), getChannelTemplateHierarchy(channelOpts));
|
||||
function getTemplateForChannel(channelOpts) {
|
||||
var templateList = getChannelTemplateHierarchy(channelOpts),
|
||||
fallback = templateList[templateList.length - 1];
|
||||
return pickTemplate(templateList, fallback);
|
||||
}
|
||||
|
||||
module.exports = {
|
||||
getActiveThemePaths: getActiveThemePaths,
|
||||
channel: getTemplateForChannel,
|
||||
single: getTemplateForSingle
|
||||
single: getTemplateForSingle,
|
||||
pickTemplate: pickTemplate
|
||||
};
|
||||
|
@ -6,19 +6,16 @@ var should = require('should'),
|
||||
// Stuff we are testing
|
||||
channels = require('../../../../server/controllers/frontend/channels'),
|
||||
api = require('../../../../server/api'),
|
||||
themeList = require('../../../../server/themes').list,
|
||||
themes = require('../../../../server/themes'),
|
||||
sandbox = sinon.sandbox.create();
|
||||
|
||||
describe('Channels', function () {
|
||||
var channelRouter, req, res;
|
||||
var channelRouter, req, res, hasTemplateStub;
|
||||
|
||||
// Initialise 'req' with the bare minimum properties
|
||||
function setupRequest() {
|
||||
req = {
|
||||
method: 'get',
|
||||
app: {
|
||||
get: sandbox.stub().returns('casper')
|
||||
}
|
||||
method: 'get'
|
||||
};
|
||||
}
|
||||
|
||||
@ -98,13 +95,22 @@ describe('Channels', function () {
|
||||
}, done);
|
||||
}
|
||||
|
||||
// Ensure hasTemplate returns values
|
||||
function setupActiveTheme() {
|
||||
hasTemplateStub = sandbox.stub().returns(false);
|
||||
hasTemplateStub.withArgs('index').returns(true);
|
||||
|
||||
sandbox.stub(themes, 'getActive').returns({
|
||||
hasTemplate: hasTemplateStub
|
||||
});
|
||||
}
|
||||
|
||||
before(function () {
|
||||
// We don't overwrite this, so only do it once
|
||||
channelRouter = channels.router();
|
||||
});
|
||||
|
||||
afterEach(function () {
|
||||
themeList.init();
|
||||
sandbox.restore();
|
||||
});
|
||||
|
||||
@ -118,13 +124,6 @@ describe('Channels', function () {
|
||||
});
|
||||
}
|
||||
|
||||
// Return basic paths for the activeTheme
|
||||
function setupActiveTheme() {
|
||||
themeList.init({casper: {
|
||||
'index.hbs': '/content/themes/casper/index.hbs'
|
||||
}});
|
||||
}
|
||||
|
||||
beforeEach(function () {
|
||||
// Setup Env for tests
|
||||
setupPostsAPIStub();
|
||||
@ -141,10 +140,7 @@ describe('Channels', function () {
|
||||
});
|
||||
|
||||
it('should render the first page of the index channel using home.hbs if available', function (done) {
|
||||
themeList.init({casper: {
|
||||
'index.hbs': '/content/themes/casper/index.hbs',
|
||||
'home.hbs': '/content/themes/casper/home.hbs'
|
||||
}});
|
||||
hasTemplateStub.withArgs('home').returns(true);
|
||||
|
||||
testChannelRender({url: '/'}, function (view) {
|
||||
should.exist(view);
|
||||
@ -163,11 +159,6 @@ describe('Channels', function () {
|
||||
});
|
||||
|
||||
it('should use index.hbs for second page even if home.hbs is available', function (done) {
|
||||
themeList.init({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');
|
||||
@ -253,13 +244,6 @@ describe('Channels', function () {
|
||||
});
|
||||
}
|
||||
|
||||
// Return basic paths for the activeTheme
|
||||
function setupActiveTheme() {
|
||||
themeList.init({casper: {
|
||||
'index.hbs': '/content/themes/casper/index.hbs'
|
||||
}});
|
||||
}
|
||||
|
||||
beforeEach(function () {
|
||||
// Setup Env for tests
|
||||
setupAPIStubs();
|
||||
@ -277,10 +261,7 @@ describe('Channels', function () {
|
||||
});
|
||||
|
||||
it('should render the first page of the tag channel using tag.hbs by default', function (done) {
|
||||
themeList.init({casper: {
|
||||
'index.hbs': '/content/themes/casper/index.hbs',
|
||||
'tag.hbs': '/content/themes/casper/tag.hbs'
|
||||
}});
|
||||
hasTemplateStub.withArgs('tag').returns(true);
|
||||
|
||||
testChannelRender({url: '/tag/my-tag/'}, function (view) {
|
||||
should.exist(view);
|
||||
@ -291,11 +272,8 @@ describe('Channels', function () {
|
||||
});
|
||||
|
||||
it('should render the first page of the tag channel using tag-:slug.hbs if available', function (done) {
|
||||
themeList.init({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'
|
||||
}});
|
||||
hasTemplateStub.withArgs('tag').returns(true);
|
||||
hasTemplateStub.withArgs('tag-my-tag').returns(true);
|
||||
|
||||
testChannelRender({url: '/tag/my-tag/'}, function (view) {
|
||||
should.exist(view);
|
||||
@ -315,10 +293,7 @@ describe('Channels', function () {
|
||||
});
|
||||
|
||||
it('should use tag.hbs to render the tag channel if available', function (done) {
|
||||
themeList.init({casper: {
|
||||
'index.hbs': '/content/themes/casper/index.hbs',
|
||||
'tag.hbs': '/content/themes/casper/tag.hbs'
|
||||
}});
|
||||
hasTemplateStub.withArgs('tag').returns(true);
|
||||
|
||||
testChannelRender({url: '/tag/my-tag/page/2/'}, function (view) {
|
||||
should.exist(view);
|
||||
@ -328,11 +303,8 @@ describe('Channels', function () {
|
||||
});
|
||||
|
||||
it('should use tag-:slug.hbs to render the tag channel if available', function (done) {
|
||||
themeList.init({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'
|
||||
}});
|
||||
hasTemplateStub.withArgs('tag').returns(true);
|
||||
hasTemplateStub.withArgs('tag-my-tag').returns(true);
|
||||
|
||||
testChannelRender({url: '/tag/my-tag/page/2/'}, function (view) {
|
||||
should.exist(view);
|
||||
|
@ -6,25 +6,48 @@ var moment = require('moment'),
|
||||
api = require('../../../../server/api'),
|
||||
frontend = require('../../../../server/controllers/frontend'),
|
||||
configUtils = require('../../../utils/configUtils'),
|
||||
themeList = require('../../../../server/themes').list,
|
||||
themes = require('../../../../server/themes'),
|
||||
settingsCache = require('../../../../server/settings/cache'),
|
||||
sandbox = sinon.sandbox.create();
|
||||
|
||||
describe('Frontend Controller', function () {
|
||||
var adminEditPagePath = '/ghost/editor/', localSettingsCache = {permalinks: '/:slug/'};
|
||||
var adminEditPagePath = '/ghost/editor/',
|
||||
localSettingsCache = {},
|
||||
hasTemplateStub;
|
||||
|
||||
function resetLocalSettingsCache() {
|
||||
localSettingsCache = {
|
||||
permalinks: '/:slug/',
|
||||
activeTheme: 'casper'
|
||||
};
|
||||
}
|
||||
|
||||
afterEach(function () {
|
||||
sandbox.restore();
|
||||
configUtils.restore();
|
||||
localSettingsCache = {
|
||||
permalinks: '/:slug/'
|
||||
};
|
||||
});
|
||||
|
||||
beforeEach(function () {
|
||||
// Ensure hasTemplate returns values
|
||||
function setupActiveTheme() {
|
||||
hasTemplateStub = sandbox.stub().returns(false);
|
||||
hasTemplateStub.withArgs('default').returns(true);
|
||||
hasTemplateStub.withArgs('index').returns(true);
|
||||
hasTemplateStub.withArgs('page').returns(true);
|
||||
hasTemplateStub.withArgs('page-about').returns(true);
|
||||
hasTemplateStub.withArgs('post').returns(true);
|
||||
|
||||
sandbox.stub(themes, 'getActive').returns({
|
||||
hasTemplate: hasTemplateStub
|
||||
});
|
||||
|
||||
sandbox.stub(settingsCache, 'get', function (key) {
|
||||
return localSettingsCache[key];
|
||||
});
|
||||
}
|
||||
|
||||
beforeEach(function () {
|
||||
resetLocalSettingsCache();
|
||||
setupActiveTheme();
|
||||
});
|
||||
|
||||
// Helper function to prevent unit tests
|
||||
@ -37,7 +60,7 @@ describe('Frontend Controller', function () {
|
||||
}
|
||||
|
||||
describe('single', function () {
|
||||
var req, res, casper, mockPosts = [{
|
||||
var req, res, mockPosts = [{
|
||||
posts: [{
|
||||
status: 'published',
|
||||
id: 1,
|
||||
@ -98,17 +121,7 @@ describe('Frontend Controller', function () {
|
||||
return Promise.resolve(post || {posts: []});
|
||||
});
|
||||
|
||||
casper = {
|
||||
assets: null,
|
||||
'default.hbs': '/content/themes/casper/default.hbs',
|
||||
'index.hbs': '/content/themes/casper/index.hbs',
|
||||
'page.hbs': '/content/themes/casper/page.hbs',
|
||||
'page-about.hbs': '/content/themes/casper/page-about.hbs',
|
||||
'post.hbs': '/content/themes/casper/post.hbs'
|
||||
};
|
||||
|
||||
req = {
|
||||
app: {get: function () { return 'casper'; }},
|
||||
path: '/', params: {}, route: {}
|
||||
};
|
||||
|
||||
@ -122,7 +135,6 @@ describe('Frontend Controller', function () {
|
||||
describe('static pages', function () {
|
||||
describe('custom page templates', function () {
|
||||
it('it will render a custom page-slug template if it exists', function (done) {
|
||||
themeList.init({casper: casper});
|
||||
req.path = '/' + mockPosts[2].posts[0].slug + '/';
|
||||
req.route = {path: '*'};
|
||||
res.render = function (view, context) {
|
||||
@ -136,8 +148,8 @@ describe('Frontend Controller', function () {
|
||||
});
|
||||
|
||||
it('it will use page.hbs if it exists and no page-slug template is present', function (done) {
|
||||
delete casper['page-about.hbs'];
|
||||
themeList.init({casper: casper});
|
||||
hasTemplateStub.withArgs('page-about').returns(false);
|
||||
|
||||
req.path = '/' + mockPosts[2].posts[0].slug + '/';
|
||||
req.route = {path: '*'};
|
||||
res.render = function (view, context) {
|
||||
@ -151,9 +163,9 @@ describe('Frontend Controller', function () {
|
||||
});
|
||||
|
||||
it('defaults to post.hbs without a page.hbs or page-slug template', function (done) {
|
||||
delete casper['page-about.hbs'];
|
||||
delete casper['page.hbs'];
|
||||
themeList.init({casper: casper});
|
||||
hasTemplateStub.withArgs('page-about').returns(false);
|
||||
hasTemplateStub.withArgs('page').returns(false);
|
||||
|
||||
req.path = '/' + mockPosts[2].posts[0].slug + '/';
|
||||
req.route = {path: '*'};
|
||||
res.render = function (view, context) {
|
||||
@ -169,8 +181,6 @@ describe('Frontend Controller', function () {
|
||||
|
||||
describe('permalink set to slug', function () {
|
||||
it('will render static page via /:slug/', function (done) {
|
||||
themeList.init({casper: casper});
|
||||
|
||||
req.path = '/' + mockPosts[0].posts[0].slug + '/';
|
||||
req.route = {path: '*'};
|
||||
res.render = function (view, context) {
|
||||
@ -238,8 +248,6 @@ describe('Frontend Controller', function () {
|
||||
});
|
||||
|
||||
it('will render static page via /:slug', function (done) {
|
||||
themeList.init({casper: casper});
|
||||
|
||||
req.path = '/' + mockPosts[0].posts[0].slug + '/';
|
||||
req.route = {path: '*'};
|
||||
res.render = function (view, context) {
|
||||
@ -294,8 +302,6 @@ describe('Frontend Controller', function () {
|
||||
});
|
||||
|
||||
it('will render post via /:slug/', function (done) {
|
||||
themeList.init({casper: casper});
|
||||
|
||||
req.path = '/' + mockPosts[1].posts[0].slug + '/';
|
||||
req.route = {path: '*'};
|
||||
res.render = function (view, context) {
|
||||
@ -383,7 +389,6 @@ describe('Frontend Controller', function () {
|
||||
});
|
||||
|
||||
it('will render post via /YYYY/MM/DD/:slug/', function (done) {
|
||||
themeList.init({casper: casper});
|
||||
var date = moment(mockPosts[1].posts[0].published_at).format('YYYY/MM/DD');
|
||||
req.path = '/' + [date, mockPosts[1].posts[0].slug].join('/') + '/';
|
||||
req.route = {path: '*'};
|
||||
@ -460,8 +465,6 @@ describe('Frontend Controller', function () {
|
||||
});
|
||||
|
||||
it('will render post via /:author/:slug/', function (done) {
|
||||
themeList.init({casper: casper});
|
||||
|
||||
req.path = '/' + ['test', mockPosts[1].posts[0].slug].join('/') + '/';
|
||||
req.route = {path: '*'};
|
||||
res.render = function (view, context) {
|
||||
@ -541,8 +544,6 @@ describe('Frontend Controller', function () {
|
||||
beforeEach(function () {
|
||||
localSettingsCache.permalinks = '/:year/:slug/';
|
||||
|
||||
themeList.init({casper: casper});
|
||||
|
||||
var date = moment(mockPosts[1].posts[0].published_at).format('YYYY');
|
||||
mockPosts[1].posts[0].url = '/' + date + '/' + mockPosts[1].posts[0].slug + '/';
|
||||
});
|
||||
@ -743,7 +744,6 @@ describe('Frontend Controller', function () {
|
||||
});
|
||||
|
||||
req = {
|
||||
app: {get: function () {return 'casper'; }},
|
||||
path: '/', params: {}, route: {}
|
||||
};
|
||||
|
||||
@ -752,8 +752,6 @@ describe('Frontend Controller', function () {
|
||||
render: sinon.spy(),
|
||||
redirect: sinon.spy()
|
||||
};
|
||||
|
||||
themeList.init({casper: {}});
|
||||
});
|
||||
|
||||
it('should render draft post', function (done) {
|
||||
@ -769,7 +767,6 @@ describe('Frontend Controller', function () {
|
||||
});
|
||||
|
||||
it('should render draft page', function (done) {
|
||||
themeList.init({casper: {'page.hbs': '/content/themes/casper/page.hbs'}});
|
||||
req.params = {uuid: 'abc-1234-01'};
|
||||
res.render = function (view, context) {
|
||||
view.should.equal('page');
|
||||
|
@ -1,17 +1,21 @@
|
||||
var should = require('should'),
|
||||
sinon = require('sinon'),
|
||||
rewire = require('rewire'),
|
||||
|
||||
// Stuff we are testing
|
||||
templates = rewire('../../../../server/controllers/frontend/templates'),
|
||||
themes = require('../../../../server/themes'),
|
||||
|
||||
themeList = require('../../../../server/themes').list;
|
||||
sandbox = sinon.sandbox.create();
|
||||
|
||||
describe('templates', function () {
|
||||
var getActiveThemeStub, hasTemplateStub;
|
||||
|
||||
afterEach(function () {
|
||||
themeList.init();
|
||||
sandbox.restore();
|
||||
});
|
||||
|
||||
describe('utils', function () {
|
||||
describe('[private] getChannelTemplateHierarchy', function () {
|
||||
var channelTemplateList = templates.__get__('getChannelTemplateHierarchy');
|
||||
|
||||
it('should return just index for empty channelOpts', function () {
|
||||
@ -45,23 +49,73 @@ describe('templates', function () {
|
||||
});
|
||||
});
|
||||
|
||||
describe('single', function () {
|
||||
describe('pickTemplate', function () {
|
||||
beforeEach(function () {
|
||||
hasTemplateStub = sandbox.stub().returns(false);
|
||||
|
||||
getActiveThemeStub = sandbox.stub(themes, 'getActive').returns({
|
||||
hasTemplate: hasTemplateStub
|
||||
});
|
||||
});
|
||||
|
||||
it('returns fallback if there is no activeTheme', function () {
|
||||
getActiveThemeStub.returns(undefined);
|
||||
|
||||
templates.pickTemplate(['tag-test', 'tag', 'index'], 'fallback').should.eql('fallback');
|
||||
templates.pickTemplate(['page-my-post', 'page', 'post'], 'fallback').should.eql('fallback');
|
||||
});
|
||||
|
||||
it('returns fallback if activeTheme has no templates', function () {
|
||||
templates.pickTemplate(['tag-test', 'tag', 'index'], 'fallback').should.eql('fallback');
|
||||
templates.pickTemplate(['page-about', 'page', 'post'], 'fallback').should.eql('fallback');
|
||||
});
|
||||
|
||||
describe('with many templates', function () {
|
||||
beforeEach(function () {
|
||||
themeList.init({casper: {
|
||||
assets: null,
|
||||
'default.hbs': '/content/themes/casper/default.hbs',
|
||||
'index.hbs': '/content/themes/casper/index.hbs',
|
||||
'page.hbs': '/content/themes/casper/page.hbs',
|
||||
'page-about.hbs': '/content/themes/casper/page-about.hbs',
|
||||
'post.hbs': '/content/themes/casper/post.hbs',
|
||||
'post-welcome-to-ghost.hbs': '/content/themes/casper/post-welcome-to-ghost.hbs'
|
||||
// Set available Templates
|
||||
hasTemplateStub.withArgs('default').returns(true);
|
||||
hasTemplateStub.withArgs('index').returns(true);
|
||||
hasTemplateStub.withArgs('page').returns(true);
|
||||
hasTemplateStub.withArgs('page-about').returns(true);
|
||||
hasTemplateStub.withArgs('post').returns(true);
|
||||
hasTemplateStub.withArgs('amp').returns(true);
|
||||
});
|
||||
|
||||
}});
|
||||
it('returns first matching template', function () {
|
||||
templates.pickTemplate(['page-about', 'page', 'post'], 'fallback').should.eql('page-about');
|
||||
templates.pickTemplate(['page-magic', 'page', 'post'], 'fallback').should.eql('page');
|
||||
templates.pickTemplate(['page', 'post'], 'fallback').should.eql('page');
|
||||
});
|
||||
|
||||
it('returns correctly if template list is a string', function () {
|
||||
templates.pickTemplate('amp', 'fallback').should.eql('amp');
|
||||
templates.pickTemplate('subscribe', 'fallback').should.eql('fallback');
|
||||
templates.pickTemplate('post', 'fallback').should.eql('post');
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('single', function () {
|
||||
beforeEach(function () {
|
||||
hasTemplateStub = sandbox.stub().returns(false);
|
||||
|
||||
getActiveThemeStub = sandbox.stub(themes, 'getActive').returns({
|
||||
hasTemplate: hasTemplateStub
|
||||
});
|
||||
});
|
||||
|
||||
describe('with many templates', function () {
|
||||
beforeEach(function () {
|
||||
// Set available Templates
|
||||
hasTemplateStub.withArgs('default').returns(true);
|
||||
hasTemplateStub.withArgs('index').returns(true);
|
||||
hasTemplateStub.withArgs('page').returns(true);
|
||||
hasTemplateStub.withArgs('page-about').returns(true);
|
||||
hasTemplateStub.withArgs('post').returns(true);
|
||||
});
|
||||
|
||||
it('will return correct template for a post WITHOUT custom template', function () {
|
||||
var view = templates.single('casper', {
|
||||
var view = templates.single({
|
||||
page: 0,
|
||||
slug: 'test-post'
|
||||
});
|
||||
@ -70,7 +124,8 @@ describe('templates', function () {
|
||||
});
|
||||
|
||||
it('will return correct template for a post WITH custom template', function () {
|
||||
var view = templates.single('casper', {
|
||||
hasTemplateStub.withArgs('post-welcome-to-ghost').returns(true);
|
||||
var view = templates.single({
|
||||
page: 0,
|
||||
slug: 'welcome-to-ghost'
|
||||
});
|
||||
@ -79,7 +134,7 @@ describe('templates', function () {
|
||||
});
|
||||
|
||||
it('will return correct template for a page WITHOUT custom template', function () {
|
||||
var view = templates.single('casper', {
|
||||
var view = templates.single({
|
||||
page: 1,
|
||||
slug: 'contact'
|
||||
});
|
||||
@ -88,7 +143,7 @@ describe('templates', function () {
|
||||
});
|
||||
|
||||
it('will return correct template for a page WITH custom template', function () {
|
||||
var view = templates.single('casper', {
|
||||
var view = templates.single({
|
||||
page: 1,
|
||||
slug: 'about'
|
||||
});
|
||||
@ -98,29 +153,31 @@ describe('templates', function () {
|
||||
});
|
||||
|
||||
it('will fall back to post even if no index.hbs', function () {
|
||||
themeList.init({casper: {
|
||||
assets: null,
|
||||
'default.hbs': '/content/themes/casper/default.hbs'
|
||||
}});
|
||||
hasTemplateStub.returns(false);
|
||||
|
||||
var view = templates.single('casper', {page: 1});
|
||||
var view = templates.single({page: 1});
|
||||
should.exist(view);
|
||||
view.should.eql('post');
|
||||
});
|
||||
});
|
||||
|
||||
describe('channel', function () {
|
||||
beforeEach(function () {
|
||||
hasTemplateStub = sandbox.stub().returns(false);
|
||||
|
||||
getActiveThemeStub = sandbox.stub(themes, 'getActive').returns({
|
||||
hasTemplate: hasTemplateStub
|
||||
});
|
||||
});
|
||||
|
||||
describe('without tag templates', function () {
|
||||
beforeEach(function () {
|
||||
themeList.init({casper: {
|
||||
assets: null,
|
||||
'default.hbs': '/content/themes/casper/default.hbs',
|
||||
'index.hbs': '/content/themes/casper/index.hbs'
|
||||
}});
|
||||
hasTemplateStub.withArgs('default').returns(true);
|
||||
hasTemplateStub.withArgs('index').returns(true);
|
||||
});
|
||||
|
||||
it('will return correct view for a tag', function () {
|
||||
var view = templates.channel('casper', {name: 'tag', slugParam: 'development', slugTemplate: true});
|
||||
var view = templates.channel({name: 'tag', slugParam: 'development', slugTemplate: true});
|
||||
should.exist(view);
|
||||
view.should.eql('index');
|
||||
});
|
||||
@ -128,35 +185,27 @@ describe('templates', function () {
|
||||
|
||||
describe('with tag templates', function () {
|
||||
beforeEach(function () {
|
||||
themeList.init({casper: {
|
||||
assets: null,
|
||||
'default.hbs': '/content/themes/casper/default.hbs',
|
||||
'index.hbs': '/content/themes/casper/index.hbs',
|
||||
'tag.hbs': '/content/themes/casper/tag.hbs',
|
||||
'tag-design.hbs': '/content/themes/casper/tag-about.hbs'
|
||||
}});
|
||||
hasTemplateStub.withArgs('default').returns(true);
|
||||
hasTemplateStub.withArgs('index').returns(true);
|
||||
hasTemplateStub.withArgs('tag').returns(true);
|
||||
hasTemplateStub.withArgs('tag-design').returns(true);
|
||||
});
|
||||
|
||||
it('will return correct view for a tag', function () {
|
||||
var view = templates.channel('casper', {name: 'tag', slugParam: 'design', slugTemplate: true});
|
||||
var view = templates.channel({name: 'tag', slugParam: 'design', slugTemplate: true});
|
||||
should.exist(view);
|
||||
view.should.eql('tag-design');
|
||||
});
|
||||
|
||||
it('will return correct view for a tag', function () {
|
||||
var view = templates.channel('casper', {name: 'tag', slugParam: 'development', slugTemplate: true});
|
||||
var view = templates.channel({name: 'tag', slugParam: 'development', slugTemplate: true});
|
||||
should.exist(view);
|
||||
view.should.eql('tag');
|
||||
});
|
||||
});
|
||||
|
||||
it('will fall back to index even if no index.hbs', function () {
|
||||
themeList.init({casper: {
|
||||
assets: null,
|
||||
'default.hbs': '/content/themes/casper/default.hbs'
|
||||
}});
|
||||
|
||||
var view = templates.channel('casper', {name: 'tag', slugParam: 'development', slugTemplate: true});
|
||||
var view = templates.channel({name: 'tag', slugParam: 'development', slugTemplate: true});
|
||||
should.exist(view);
|
||||
view.should.eql('index');
|
||||
});
|
||||
|
Loading…
Reference in New Issue
Block a user