mirror of
https://github.com/TryGhost/Ghost.git
synced 2025-01-03 16:38:22 +03:00
Merge pull request #6145 from ErisDS/view-refactor
Unify code for picking a template to render with
This commit is contained in:
commit
bc83dbce09
@ -3,9 +3,9 @@ var config = require('../../config'),
|
||||
|
||||
defaults = {
|
||||
index: {
|
||||
name: 'home',
|
||||
name: 'index',
|
||||
route: '/',
|
||||
firstPageTemplate: 'home'
|
||||
frontPageTemplate: 'home'
|
||||
},
|
||||
tag: {
|
||||
name: 'tag',
|
||||
|
@ -82,10 +82,9 @@ function processQuery(query, slugParam) {
|
||||
* Does a first round of formatting on the response, and returns
|
||||
*
|
||||
* @param {Object} channelOptions
|
||||
* @param {String} slugParam
|
||||
* @returns {Promise} response
|
||||
*/
|
||||
function fetchData(channelOptions, slugParam) {
|
||||
function fetchData(channelOptions) {
|
||||
// Temporary workaround to make RSS work, moving towards dynamic channels will provide opportunities to
|
||||
// improve this, I hope :)
|
||||
function handlePostsPerPage(channelOptions) {
|
||||
@ -102,10 +101,10 @@ function fetchData(channelOptions, slugParam) {
|
||||
|
||||
// All channels must have a posts query, use the default if not provided
|
||||
postQuery = _.defaultsDeep({}, pageOptions, defaultPostQuery);
|
||||
props.posts = processQuery(postQuery, slugParam);
|
||||
props.posts = processQuery(postQuery, channelOptions.slugParam);
|
||||
|
||||
_.each(channelOptions.data, function (query, name) {
|
||||
props[name] = processQuery(query, slugParam);
|
||||
props[name] = processQuery(query, channelOptions.slugParam);
|
||||
});
|
||||
|
||||
return Promise.props(props).then(function formatResponse(results) {
|
||||
|
@ -12,7 +12,7 @@ var _ = require('lodash'),
|
||||
errors = require('../../errors'),
|
||||
filters = require('../../filters'),
|
||||
Promise = require('bluebird'),
|
||||
template = require('../../helpers/template'),
|
||||
templates = require('./templates'),
|
||||
routeMatch = require('path-match')(),
|
||||
safeString = require('../../utils/index').safeString,
|
||||
handleError = require('./error'),
|
||||
@ -21,7 +21,6 @@ var _ = require('lodash'),
|
||||
channelConfig = require('./channel-config'),
|
||||
setResponseContext = require('./context'),
|
||||
setRequestIsSecure = require('./secure'),
|
||||
getActiveThemePaths = require('./theme-paths'),
|
||||
|
||||
frontendControllers,
|
||||
staticPostPermalink = routeMatch('/:slug/:edit?');
|
||||
@ -34,8 +33,7 @@ var _ = require('lodash'),
|
||||
*/
|
||||
function renderPost(req, res) {
|
||||
return function renderPost(post) {
|
||||
var paths = getActiveThemePaths(req),
|
||||
view = template.getThemeViewForPost(paths, post),
|
||||
var view = templates.single(req.app.get('activeTheme'), post),
|
||||
response = formatResponse.single(post);
|
||||
|
||||
setResponseContext(req, res, response);
|
||||
@ -53,6 +51,7 @@ function renderChannel(channelOpts) {
|
||||
channelOpts.postOptions = channelOpts.postOptions || {};
|
||||
// Set page on postOptions for the query made later
|
||||
channelOpts.postOptions.page = pageParam;
|
||||
channelOpts.slugParam = slugParam;
|
||||
|
||||
// @TODO this should really use the url building code in config.url
|
||||
function createUrl(page) {
|
||||
@ -75,7 +74,7 @@ function renderChannel(channelOpts) {
|
||||
}
|
||||
|
||||
// Call fetchData to get everything we need from the API
|
||||
return fetchData(channelOpts, slugParam).then(function handleResult(result) {
|
||||
return fetchData(channelOpts).then(function handleResult(result) {
|
||||
// If page is greater than number of pages we have, redirect to last page
|
||||
if (pageParam > result.meta.pagination.pages) {
|
||||
return res.redirect(createUrl(result.meta.pagination.pages));
|
||||
@ -90,17 +89,7 @@ function renderChannel(channelOpts) {
|
||||
|
||||
// @TODO: properly design these filters
|
||||
filters.doFilter('prePostsRender', result.posts, res.locals).then(function then(posts) {
|
||||
var paths = getActiveThemePaths(req),
|
||||
view = 'index';
|
||||
|
||||
// Calculate which template to use to render the data
|
||||
if (channelOpts.firstPageTemplate && paths.hasOwnProperty(channelOpts.firstPageTemplate + '.hbs')) {
|
||||
view = (pageParam > 1) ? 'index' : channelOpts.firstPageTemplate;
|
||||
} else if (channelOpts.slugTemplate) {
|
||||
view = template.getThemeViewForChannel(paths, channelOpts.name, slugParam);
|
||||
} else if (paths.hasOwnProperty(channelOpts.name + '.hbs')) {
|
||||
view = channelOpts.name;
|
||||
}
|
||||
var view = templates.channel(req.app.get('activeTheme'), channelOpts);
|
||||
|
||||
// Do final data formatting and then render
|
||||
result.posts = posts;
|
||||
@ -248,7 +237,7 @@ frontendControllers = {
|
||||
},
|
||||
private: function private(req, res) {
|
||||
var defaultPage = path.resolve(config.paths.adminViews, 'private.hbs'),
|
||||
paths = getActiveThemePaths(req),
|
||||
paths = templates.getActiveThemePaths(req.app.get('activeTheme')),
|
||||
data = {};
|
||||
|
||||
if (res.error) {
|
||||
|
100
core/server/controllers/frontend/templates.js
Normal file
100
core/server/controllers/frontend/templates.js
Normal file
@ -0,0 +1,100 @@
|
||||
// # Templates
|
||||
//
|
||||
// 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'),
|
||||
config = require('../../config');
|
||||
|
||||
function getActiveThemePaths(activeTheme) {
|
||||
return config.paths.availableThemes[activeTheme];
|
||||
}
|
||||
|
||||
/**
|
||||
* ## Get Channel Template Hierarchy
|
||||
*
|
||||
* Fetch the ordered list of templates that can be used to render this request.
|
||||
* 'index' is the default / fallback
|
||||
* For channels with slugs: [:channelName-:slug, :channelName, index]
|
||||
* For channels without slugs: [:channelName, index]
|
||||
* Channels can also have a front page template which is used if this is the first page of the channel, e.g. 'home.hbs'
|
||||
*
|
||||
* @param {Object} channelOpts
|
||||
* @returns {String[]}
|
||||
*/
|
||||
function getChannelTemplateHierarchy(channelOpts) {
|
||||
var templateList = ['index'];
|
||||
|
||||
if (channelOpts.name && channelOpts.name !== 'index') {
|
||||
templateList.unshift(channelOpts.name);
|
||||
|
||||
if (channelOpts.slugTemplate && channelOpts.slugParam) {
|
||||
templateList.unshift(channelOpts.name + '-' + channelOpts.slugParam);
|
||||
}
|
||||
}
|
||||
|
||||
if (channelOpts.frontPageTemplate && channelOpts.postOptions.page === 1) {
|
||||
templateList.unshift(channelOpts.frontPageTemplate);
|
||||
}
|
||||
|
||||
return templateList;
|
||||
}
|
||||
|
||||
/**
|
||||
* ## Get Single Template Hierarchy
|
||||
*
|
||||
* Fetch the ordered list of templates that can be used to render this request.
|
||||
* 'post' is the default / fallback
|
||||
* For posts: [post-:slug, post]
|
||||
* For pages: [page-:slug, page, post]
|
||||
*
|
||||
* @param {Object} single
|
||||
* @returns {String[]}
|
||||
*/
|
||||
function getSingleTemplateHierarchy(single) {
|
||||
var templateList = ['post'],
|
||||
type = 'post';
|
||||
|
||||
if (single.page) {
|
||||
templateList.unshift('page');
|
||||
type = 'page';
|
||||
}
|
||||
|
||||
templateList.unshift(type + '-' + single.slug);
|
||||
|
||||
return templateList;
|
||||
}
|
||||
|
||||
/**
|
||||
* ## Pick Template
|
||||
*
|
||||
* 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
|
||||
*/
|
||||
function pickTemplate(themePaths, templateList) {
|
||||
var template = _.find(templateList, function (template) {
|
||||
return themePaths.hasOwnProperty(template + '.hbs');
|
||||
});
|
||||
|
||||
if (!template) {
|
||||
template = templateList[templateList.length - 1];
|
||||
}
|
||||
|
||||
return template;
|
||||
}
|
||||
|
||||
function getTemplateForSingle(activeTheme, single) {
|
||||
return pickTemplate(getActiveThemePaths(activeTheme), getSingleTemplateHierarchy(single));
|
||||
}
|
||||
|
||||
function getTemplateForChannel(activeTheme, channelOpts) {
|
||||
return pickTemplate(getActiveThemePaths(activeTheme), getChannelTemplateHierarchy(channelOpts));
|
||||
}
|
||||
|
||||
module.exports = {
|
||||
getActiveThemePaths: getActiveThemePaths,
|
||||
channel: getTemplateForChannel,
|
||||
single: getTemplateForSingle
|
||||
};
|
@ -1,14 +0,0 @@
|
||||
var config = require('../../config');
|
||||
|
||||
/**
|
||||
* Returns the paths object of the active theme via way of a promise.
|
||||
* @return {Promise} The promise resolves with the value of the paths.
|
||||
*/
|
||||
function getActiveThemePaths(req) {
|
||||
var activeTheme = req.app.get('activeTheme'),
|
||||
paths = config.paths.availableThemes[activeTheme];
|
||||
|
||||
return paths;
|
||||
}
|
||||
|
||||
module.exports = getActiveThemePaths;
|
@ -199,12 +199,14 @@ generate = function generate(req, res, next) {
|
||||
// Set page on postOptions for the query made later
|
||||
req.channelConfig.postOptions.page = pageParam;
|
||||
|
||||
req.channelConfig.slugParam = slugParam;
|
||||
|
||||
// No negative pages, or page 1
|
||||
if (isNaN(pageParam) || pageParam < 1 || (req.params.page !== undefined && pageParam === 1)) {
|
||||
return res.redirect(baseUrl);
|
||||
}
|
||||
|
||||
return getData(req.channelConfig, slugParam).then(function then(data) {
|
||||
return getData(req.channelConfig).then(function then(data) {
|
||||
var maxPage = data.results.meta.pagination.pages;
|
||||
|
||||
// If page is greater than number of pages we have, redirect to last page
|
||||
|
@ -8,10 +8,9 @@
|
||||
|
||||
var hbs = require('express-hbs'),
|
||||
_ = require('lodash'),
|
||||
api = require('../api'),
|
||||
config = require('../config'),
|
||||
filters = require('../filters'),
|
||||
template = require('./template'),
|
||||
// @TODO Fix this
|
||||
template = require('../controllers/frontend/templates'),
|
||||
body_class;
|
||||
|
||||
body_class = function (options) {
|
||||
@ -19,7 +18,9 @@ body_class = function (options) {
|
||||
context = options.data.root.context,
|
||||
post = this.post,
|
||||
tags = this.post && this.post.tags ? this.post.tags : this.tags || [],
|
||||
page = this.post && this.post.page ? this.post.page : this.page || false;
|
||||
page = this.post && this.post.page ? this.post.page : this.page || false,
|
||||
activeTheme = options.data.root.settings.activeTheme,
|
||||
view;
|
||||
|
||||
if (post) {
|
||||
// To be removed from pages by #2597 when we're ready to deprecate this
|
||||
@ -53,26 +54,20 @@ body_class = function (options) {
|
||||
classes.push('archive-template');
|
||||
}
|
||||
|
||||
return api.settings.read({context: {internal: true}, key: 'activeTheme'}).then(function (response) {
|
||||
var activeTheme = response.settings[0],
|
||||
paths = config.paths.availableThemes[activeTheme.value],
|
||||
view;
|
||||
if (post && page) {
|
||||
view = template.single(activeTheme, post).split('-');
|
||||
|
||||
if (post && page) {
|
||||
view = template.getThemeViewForPost(paths, post).split('-');
|
||||
|
||||
if (view[0] === 'page' && view.length > 1) {
|
||||
classes.push(view.join('-'));
|
||||
// To be removed by #2597 when we're ready to deprecate this
|
||||
view.splice(1, 0, 'template');
|
||||
classes.push(view.join('-'));
|
||||
}
|
||||
if (view[0] === 'page' && view.length > 1) {
|
||||
classes.push(view.join('-'));
|
||||
// To be removed by #2597 when we're ready to deprecate this
|
||||
view.splice(1, 0, 'template');
|
||||
classes.push(view.join('-'));
|
||||
}
|
||||
}
|
||||
|
||||
return filters.doFilter('body_class', classes).then(function (classes) {
|
||||
var classString = _.reduce(classes, function (memo, item) { return memo + ' ' + item; }, '');
|
||||
return new hbs.handlebars.SafeString(classString.trim());
|
||||
});
|
||||
return filters.doFilter('body_class', classes).then(function (classes) {
|
||||
var classString = _.reduce(classes, function (memo, item) { return memo + ' ' + item; }, '');
|
||||
return new hbs.handlebars.SafeString(classString.trim());
|
||||
});
|
||||
};
|
||||
|
||||
|
@ -22,47 +22,4 @@ templates.execute = function (name, context, options) {
|
||||
return new hbs.handlebars.SafeString(partial(context, options));
|
||||
};
|
||||
|
||||
// Given a theme object and a post object this will return
|
||||
// which theme template page should be used.
|
||||
// If given a post object that is a regular post
|
||||
// it will return 'post'.
|
||||
// If given a static post object it will return 'page'.
|
||||
// If given a static post object and a custom page template
|
||||
// exits it will return that page.
|
||||
templates.getThemeViewForPost = function (themePaths, post) {
|
||||
var customPageView = 'page-' + post.slug,
|
||||
view = 'post';
|
||||
|
||||
if (post.page) {
|
||||
if (themePaths.hasOwnProperty(customPageView + '.hbs')) {
|
||||
view = customPageView;
|
||||
} else if (themePaths.hasOwnProperty('page.hbs')) {
|
||||
view = 'page';
|
||||
}
|
||||
}
|
||||
|
||||
return view;
|
||||
};
|
||||
|
||||
// Given a theme object and a slug this will return
|
||||
// which theme template page should be used.
|
||||
// If no default or custom tag template exists then 'index'
|
||||
// will be returned
|
||||
// If no custom template exists but a default does then
|
||||
// the default will be returned
|
||||
// If given a slug and a custom template
|
||||
// exits it will return that view.
|
||||
templates.getThemeViewForChannel = function (themePaths, channelName, slug) {
|
||||
var customChannelView = channelName + '-' + slug,
|
||||
view = channelName;
|
||||
|
||||
if (themePaths.hasOwnProperty(customChannelView + '.hbs')) {
|
||||
view = customChannelView;
|
||||
} else if (!themePaths.hasOwnProperty(channelName + '.hbs')) {
|
||||
view = 'index';
|
||||
}
|
||||
|
||||
return view;
|
||||
};
|
||||
|
||||
module.exports = templates;
|
||||
|
@ -133,6 +133,7 @@ describe('fetchData', function () {
|
||||
postOptions: {
|
||||
filter: 'tags:%s'
|
||||
},
|
||||
slugParam: 'testing',
|
||||
data: {
|
||||
tag: {
|
||||
type: 'read',
|
||||
@ -140,10 +141,9 @@ describe('fetchData', function () {
|
||||
options: {slug: '%s'}
|
||||
}
|
||||
}
|
||||
},
|
||||
slugParam = 'testing';
|
||||
};
|
||||
|
||||
fetchData(channelOpts, slugParam).then(function (result) {
|
||||
fetchData(channelOpts).then(function (result) {
|
||||
should.exist(result);
|
||||
result.should.be.an.Object.with.properties('posts', 'meta', 'data');
|
||||
result.data.should.be.an.Object.with.properties('tag');
|
||||
|
@ -987,6 +987,8 @@ describe('Frontend Controller', function () {
|
||||
}]
|
||||
}));
|
||||
|
||||
config.set({paths: {availableThemes: {casper: casper}}});
|
||||
|
||||
var date = moment(mockPosts[1].posts[0].published_at).format('YYYY');
|
||||
mockPosts[1].posts[0].url = '/' + date + '/' + mockPosts[1].posts[0].slug + '/';
|
||||
});
|
||||
@ -1375,6 +1377,8 @@ describe('Frontend Controller', function () {
|
||||
render: sinon.spy(),
|
||||
redirect: sinon.spy()
|
||||
};
|
||||
|
||||
config.set({paths: {availableThemes: {casper: {}}}});
|
||||
});
|
||||
|
||||
it('should render draft post', function (done) {
|
||||
|
176
core/test/unit/controllers/frontend/templates_spec.js
Normal file
176
core/test/unit/controllers/frontend/templates_spec.js
Normal file
@ -0,0 +1,176 @@
|
||||
/*globals describe, it, afterEach, beforeEach*/
|
||||
/*jshint expr:true*/
|
||||
var should = require('should'),
|
||||
rewire = require('rewire'),
|
||||
_ = require('lodash'),
|
||||
|
||||
// Stuff we are testing
|
||||
templates = rewire('../../../../server/controllers/frontend/templates'),
|
||||
|
||||
config = require('../../../../server/config'),
|
||||
origConfig = _.cloneDeep(config);
|
||||
|
||||
// To stop jshint complaining
|
||||
should.equal(true, true);
|
||||
|
||||
describe('templates', function () {
|
||||
afterEach(function () {
|
||||
config.set(origConfig);
|
||||
});
|
||||
|
||||
describe('utils', function () {
|
||||
var channelTemplateList = templates.__get__('getChannelTemplateHierarchy');
|
||||
|
||||
it('should return just index for empty channelOpts', function () {
|
||||
channelTemplateList({}).should.eql(['index']);
|
||||
});
|
||||
|
||||
it('should return just index if channel name is index', function () {
|
||||
channelTemplateList({name: 'index'}).should.eql(['index']);
|
||||
});
|
||||
|
||||
it('should return just index if channel name is index even if slug is set', function () {
|
||||
channelTemplateList({name: 'index', slugTemplate: true, slugParam: 'test'}).should.eql(['index']);
|
||||
});
|
||||
|
||||
it('should return channel, index if channel has name', function () {
|
||||
channelTemplateList({name: 'tag'}).should.eql(['tag', 'index']);
|
||||
});
|
||||
|
||||
it('should return channel-slug, channel, index if channel has name & slug + slugTemplate set', function () {
|
||||
channelTemplateList({name: 'tag', slugTemplate: true, slugParam: 'test'}).should.eql(['tag-test', 'tag', 'index']);
|
||||
});
|
||||
|
||||
it('should return front, channel-slug, channel, index if name, slugParam+slugTemplate & frontPageTemplate+pageParam=1 is set', function () {
|
||||
channelTemplateList({
|
||||
name: 'tag', slugTemplate: true, slugParam: 'test', frontPageTemplate: 'front-tag', postOptions: {page: 1}
|
||||
}).should.eql(['front-tag', 'tag-test', 'tag', 'index']);
|
||||
});
|
||||
|
||||
it('should return home, index for index channel if front is set and pageParam = 1', function () {
|
||||
channelTemplateList({name: 'index', frontPageTemplate: 'home', postOptions: {page: 1}}).should.eql(['home', 'index']);
|
||||
});
|
||||
});
|
||||
|
||||
describe('single', function () {
|
||||
describe('with many templates', function () {
|
||||
beforeEach(function () {
|
||||
config.set({
|
||||
paths: {
|
||||
availableThemes: {
|
||||
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'
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
it('will return correct template for a post WITHOUT custom template', function () {
|
||||
var view = templates.single('casper', {
|
||||
page: 0,
|
||||
slug: 'test-post'
|
||||
});
|
||||
view.should.exist;
|
||||
view.should.eql('post');
|
||||
});
|
||||
|
||||
it('will return correct template for a post WITH custom template', function () {
|
||||
var view = templates.single('casper', {
|
||||
page: 0,
|
||||
slug: 'welcome-to-ghost'
|
||||
});
|
||||
view.should.exist;
|
||||
view.should.eql('post-welcome-to-ghost', 'post');
|
||||
});
|
||||
|
||||
it('will return correct template for a page WITHOUT custom template', function () {
|
||||
var view = templates.single('casper', {
|
||||
page: 1,
|
||||
slug: 'contact'
|
||||
});
|
||||
view.should.exist;
|
||||
view.should.eql('page');
|
||||
});
|
||||
|
||||
it('will return correct template for a page WITH custom template', function () {
|
||||
var view = templates.single('casper', {
|
||||
page: 1,
|
||||
slug: 'about'
|
||||
});
|
||||
view.should.exist;
|
||||
view.should.eql('page-about');
|
||||
});
|
||||
});
|
||||
|
||||
it('will fall back to post even if no index.hbs', function () {
|
||||
config.set({paths: {availableThemes: {casper: {
|
||||
assets: null,
|
||||
'default.hbs': '/content/themes/casper/default.hbs'
|
||||
}}}});
|
||||
|
||||
var view = templates.single('casper', {page: 1});
|
||||
view.should.exist;
|
||||
view.should.eql('post');
|
||||
});
|
||||
});
|
||||
|
||||
describe('channel', function () {
|
||||
describe('without tag templates', function () {
|
||||
beforeEach(function () {
|
||||
config.set({paths: {availableThemes: {casper: {
|
||||
assets: null,
|
||||
'default.hbs': '/content/themes/casper/default.hbs',
|
||||
'index.hbs': '/content/themes/casper/index.hbs'
|
||||
}}}});
|
||||
});
|
||||
|
||||
it('will return correct view for a tag', function () {
|
||||
var view = templates.channel('casper', {name: 'tag', slugParam: 'development', slugTemplate: true});
|
||||
view.should.exist;
|
||||
view.should.eql('index');
|
||||
});
|
||||
});
|
||||
|
||||
describe('with tag templates', function () {
|
||||
beforeEach(function () {
|
||||
config.set({paths: {availableThemes: {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'
|
||||
}}}});
|
||||
});
|
||||
|
||||
it('will return correct view for a tag', function () {
|
||||
var view = templates.channel('casper', {name: 'tag', slugParam: 'design', slugTemplate: true});
|
||||
view.should.exist;
|
||||
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});
|
||||
view.should.exist;
|
||||
view.should.eql('tag');
|
||||
});
|
||||
});
|
||||
|
||||
it('will fall back to index even if no index.hbs', function () {
|
||||
config.set({paths: {availableThemes: {casper: {
|
||||
assets: null,
|
||||
'default.hbs': '/content/themes/casper/default.hbs'
|
||||
}}}});
|
||||
|
||||
var view = templates.channel('casper', {name: 'tag', slugParam: 'development', slugTemplate: true});
|
||||
view.should.exist;
|
||||
view.should.eql('index');
|
||||
});
|
||||
});
|
||||
});
|
@ -1,26 +1,16 @@
|
||||
/*globals describe, before, after, it*/
|
||||
/*globals describe, before, beforeEach, after, it*/
|
||||
/*jshint expr:true*/
|
||||
var should = require('should'),
|
||||
sinon = require('sinon'),
|
||||
Promise = require('bluebird'),
|
||||
hbs = require('express-hbs'),
|
||||
utils = require('./utils'),
|
||||
|
||||
// Stuff we are testing
|
||||
handlebars = hbs.handlebars,
|
||||
helpers = require('../../../server/helpers'),
|
||||
api = require('../../../server/api');
|
||||
helpers = require('../../../server/helpers');
|
||||
|
||||
describe('{{body_class}} helper', function () {
|
||||
var sandbox;
|
||||
|
||||
var options = {};
|
||||
before(function () {
|
||||
sandbox = sinon.sandbox.create();
|
||||
sandbox.stub(api.settings, 'read', function () {
|
||||
return Promise.resolve({
|
||||
settings: [{value: 'casper'}]
|
||||
});
|
||||
});
|
||||
utils.loadHelpers();
|
||||
utils.overrideConfig({paths: {
|
||||
availableThemes: {
|
||||
@ -36,9 +26,19 @@ describe('{{body_class}} helper', function () {
|
||||
}});
|
||||
});
|
||||
|
||||
beforeEach(function () {
|
||||
options = {
|
||||
data: {
|
||||
root: {
|
||||
context: [],
|
||||
settings: {activeTheme: 'casper'}
|
||||
}
|
||||
}
|
||||
};
|
||||
});
|
||||
|
||||
after(function () {
|
||||
utils.restoreConfig();
|
||||
sandbox.restore();
|
||||
});
|
||||
|
||||
it('has loaded body_class helper', function () {
|
||||
@ -46,7 +46,9 @@ describe('{{body_class}} helper', function () {
|
||||
});
|
||||
|
||||
it('can render class string', function (done) {
|
||||
helpers.body_class.call({}, {data: {root: {context: ['home']}}}).then(function (rendered) {
|
||||
options.data.root.context = ['home'];
|
||||
|
||||
helpers.body_class.call({}, options).then(function (rendered) {
|
||||
should.exist(rendered);
|
||||
|
||||
rendered.string.should.equal('home-template');
|
||||
@ -55,111 +57,90 @@ describe('{{body_class}} helper', function () {
|
||||
}).catch(done);
|
||||
});
|
||||
|
||||
it('can render class string for context', function (done) {
|
||||
Promise.all([
|
||||
// Standard home page
|
||||
helpers.body_class.call(
|
||||
{relativeUrl: '/'},
|
||||
{data: {root: {context: ['home', 'index']}}}
|
||||
),
|
||||
// A post
|
||||
helpers.body_class.call(
|
||||
{relativeUrl: '/a-post-title', post: {}},
|
||||
{data: {root: {context: ['post']}}}
|
||||
),
|
||||
// Paginated index
|
||||
helpers.body_class.call(
|
||||
{relativeUrl: '/page/4'},
|
||||
{data: {root: {context: ['index', 'paged']}}}
|
||||
),
|
||||
// Tag page
|
||||
helpers.body_class.call(
|
||||
{relativeUrl: '/tag/foo', tag: {slug: 'foo'}},
|
||||
{data: {root: {context: ['tag']}}}
|
||||
),
|
||||
// Paginated tag page
|
||||
helpers.body_class.call(
|
||||
{relativeUrl: '/tag/foo/page/2', tag: {slug: 'foo'}},
|
||||
{data: {root: {context: ['tag', 'paged']}}}
|
||||
),
|
||||
// Author page
|
||||
helpers.body_class.call(
|
||||
{relativeUrl: '/author/bar', author: {slug: 'bar'}},
|
||||
{data: {root: {context: ['author']}}}
|
||||
),
|
||||
// Paginated author page
|
||||
helpers.body_class.call(
|
||||
{relativeUrl: '/author/bar/page/2', author: {slug: 'bar'}},
|
||||
{data: {root: {context: ['author', 'paged']}}}
|
||||
),
|
||||
// Private route for password protection
|
||||
helpers.body_class.call(
|
||||
{relativeUrl: '/private/'},
|
||||
{data: {root: {context: ['private']}}}
|
||||
),
|
||||
// Post with tags
|
||||
helpers.body_class.call(
|
||||
{relativeUrl: '/my-awesome-post/', post: {tags: [{slug: 'foo'}, {slug: 'bar'}]}},
|
||||
{data: {root: {context: ['post']}}}
|
||||
)
|
||||
]).then(function (rendered) {
|
||||
rendered.length.should.equal(9);
|
||||
describe('can render class string for context', function () {
|
||||
function callBodyClassWithContext(context, self) {
|
||||
options.data.root.context = context;
|
||||
return helpers.body_class.call(
|
||||
self,
|
||||
options
|
||||
);
|
||||
}
|
||||
|
||||
should.exist(rendered[0]);
|
||||
should.exist(rendered[1]);
|
||||
should.exist(rendered[2]);
|
||||
should.exist(rendered[3]);
|
||||
should.exist(rendered[4]);
|
||||
should.exist(rendered[5]);
|
||||
should.exist(rendered[6]);
|
||||
should.exist(rendered[7]);
|
||||
should.exist(rendered[8]);
|
||||
it('Standard home page', function (done) {
|
||||
callBodyClassWithContext(['home', 'index'], {relativeUrl: '/'}).then(function (rendered) {
|
||||
rendered.string.should.equal('home-template');
|
||||
done();
|
||||
}).catch(done);
|
||||
});
|
||||
|
||||
rendered[0].string.should.equal('home-template');
|
||||
rendered[1].string.should.equal('post-template');
|
||||
rendered[2].string.should.equal('paged archive-template');
|
||||
rendered[3].string.should.equal('tag-template tag-foo');
|
||||
rendered[4].string.should.equal('tag-template tag-foo paged archive-template');
|
||||
rendered[5].string.should.equal('author-template author-bar');
|
||||
rendered[6].string.should.equal('author-template author-bar paged archive-template');
|
||||
rendered[7].string.should.equal('private-template');
|
||||
rendered[8].string.should.equal('post-template tag-foo tag-bar');
|
||||
it('a post', function (done) {
|
||||
callBodyClassWithContext(['post'], {relativeUrl: '/a-post-title', post: {}}).then(function (rendered) {
|
||||
rendered.string.should.equal('post-template');
|
||||
done();
|
||||
}).catch(done);
|
||||
});
|
||||
|
||||
done();
|
||||
}).catch(done);
|
||||
});
|
||||
it('paginated index', function (done) {
|
||||
callBodyClassWithContext(['index', 'paged'], {relativeUrl: '/page/4'}).then(function (rendered) {
|
||||
rendered.string.should.equal('paged archive-template');
|
||||
done();
|
||||
}).catch(done);
|
||||
});
|
||||
|
||||
it('can render class for static page', function (done) {
|
||||
helpers.body_class.call(
|
||||
{
|
||||
relativeUrl: '/about',
|
||||
post: {
|
||||
page: true
|
||||
}
|
||||
},
|
||||
{data: {root: {context: ['page']}}}
|
||||
).then(function (rendered) {
|
||||
should.exist(rendered);
|
||||
rendered.string.should.equal('post-template page-template page');
|
||||
it('tag page', function (done) {
|
||||
callBodyClassWithContext(['tag'], {relativeUrl: '/tag/foo', tag: {slug: 'foo'}}).then(function (rendered) {
|
||||
rendered.string.should.equal('tag-template tag-foo');
|
||||
done();
|
||||
}).catch(done);
|
||||
});
|
||||
|
||||
done();
|
||||
}).catch(done);
|
||||
});
|
||||
it('paginated tag page', function (done) {
|
||||
callBodyClassWithContext(['tag', 'paged'], {relativeUrl: '/tag/foo/page/2', tag: {slug: 'foo'}}).then(function (rendered) {
|
||||
rendered.string.should.equal('tag-template tag-foo paged archive-template');
|
||||
done();
|
||||
}).catch(done);
|
||||
});
|
||||
|
||||
it('can render class for static page with custom template', function (done) {
|
||||
helpers.body_class.call(
|
||||
{
|
||||
relativeUrl: '/about',
|
||||
post: {
|
||||
page: true,
|
||||
slug: 'about'
|
||||
}
|
||||
},
|
||||
{data: {root: {context: ['page']}}}).then(function (rendered) {
|
||||
should.exist(rendered);
|
||||
rendered.string.should.equal('post-template page-template page page-about page-template-about');
|
||||
it('author page', function (done) {
|
||||
callBodyClassWithContext(['author'], {relativeUrl: '/author/bar', author: {slug: 'bar'}}).then(function (rendered) {
|
||||
rendered.string.should.equal('author-template author-bar');
|
||||
done();
|
||||
}).catch(done);
|
||||
});
|
||||
|
||||
done();
|
||||
}).catch(done);
|
||||
it('paginated author page', function (done) {
|
||||
callBodyClassWithContext(['author', 'paged'], {relativeUrl: '/author/bar/page/2', author: {slug: 'bar'}}).then(function (rendered) {
|
||||
rendered.string.should.equal('author-template author-bar paged archive-template');
|
||||
done();
|
||||
}).catch(done);
|
||||
});
|
||||
|
||||
it('private route for password protection', function (done) {
|
||||
callBodyClassWithContext(['private'], {relativeUrl: '/private/'}).then(function (rendered) {
|
||||
rendered.string.should.equal('private-template');
|
||||
done();
|
||||
}).catch(done);
|
||||
});
|
||||
|
||||
it('post with tags', function (done) {
|
||||
callBodyClassWithContext(['post'], {relativeUrl: '/my-awesome-post/', post: {tags: [{slug: 'foo'}, {slug: 'bar'}]}}).then(function (rendered) {
|
||||
rendered.string.should.equal('post-template tag-foo tag-bar');
|
||||
done();
|
||||
}).catch(done);
|
||||
});
|
||||
|
||||
it('a static page', function (done) {
|
||||
callBodyClassWithContext(['page'], {relativeUrl: '/about', post: {page: true}}).then(function (rendered) {
|
||||
rendered.string.should.equal('post-template page-template page');
|
||||
done();
|
||||
}).catch(done);
|
||||
});
|
||||
|
||||
it('a static page with custom template', function (done) {
|
||||
callBodyClassWithContext(['page'], {relativeUrl: '/about', post: {page: true, slug: 'about'}}).then(function (rendered) {
|
||||
rendered.string.should.equal('post-template page-template page page-about page-template-about');
|
||||
done();
|
||||
}).catch(done);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
@ -15,71 +15,4 @@ describe('Helpers Template', function () {
|
||||
should.exist(safeString);
|
||||
safeString.should.have.property('string').and.equal('<h1>Hello world</h1>');
|
||||
});
|
||||
|
||||
describe('getThemeViewForPost', function () {
|
||||
var themePaths = {
|
||||
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'
|
||||
},
|
||||
posts = [{
|
||||
page: 1,
|
||||
slug: 'about'
|
||||
}, {
|
||||
page: 1,
|
||||
slug: 'contact'
|
||||
}, {
|
||||
page: 0,
|
||||
slug: 'test-post'
|
||||
}];
|
||||
|
||||
it('will return correct view for a post', function () {
|
||||
var view = template.getThemeViewForPost(themePaths, posts[0]);
|
||||
view.should.exist;
|
||||
view.should.eql('page-about');
|
||||
|
||||
view = template.getThemeViewForPost(themePaths, posts[1]);
|
||||
view.should.exist;
|
||||
view.should.eql('page');
|
||||
|
||||
view = template.getThemeViewForPost(themePaths, posts[2]);
|
||||
view.should.exist;
|
||||
view.should.eql('post');
|
||||
});
|
||||
});
|
||||
|
||||
describe('getThemeViewForChannel', function () {
|
||||
var themePathsWithTagViews = {
|
||||
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'
|
||||
},
|
||||
themePaths = {
|
||||
assets: null,
|
||||
'default.hbs': '/content/themes/casper/default.hbs',
|
||||
'index.hbs': '/content/themes/casper/index.hbs'
|
||||
},
|
||||
CHANNEL = 'tag',
|
||||
CUSTOM_EXISTS = 'design',
|
||||
DEFAULT = 'development';
|
||||
|
||||
it('will return correct view for a tag', function () {
|
||||
var view = template.getThemeViewForChannel(themePathsWithTagViews, CHANNEL, CUSTOM_EXISTS);
|
||||
view.should.exist;
|
||||
view.should.eql('tag-design');
|
||||
|
||||
view = template.getThemeViewForChannel(themePathsWithTagViews, CHANNEL, DEFAULT);
|
||||
view.should.exist;
|
||||
view.should.eql('tag');
|
||||
|
||||
view = template.getThemeViewForChannel(themePaths, CHANNEL, DEFAULT);
|
||||
view.should.exist;
|
||||
view.should.eql('index');
|
||||
});
|
||||
});
|
||||
});
|
||||
|
Loading…
Reference in New Issue
Block a user