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:
Hannah Wolfe 2017-03-13 23:15:50 +00:00 committed by Katharina Irrgang
parent b06f03b370
commit bb3cc8c0f8
11 changed files with 217 additions and 196 deletions

View File

@ -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) {

View File

@ -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);

View File

@ -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

View File

@ -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) {

View File

@ -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) {

View File

@ -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);

View File

@ -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;

View File

@ -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
};

View File

@ -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);

View File

@ -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');

View File

@ -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');
});