var should = require('should'),
hbs = require('../../../server/services/themes/engine'),
configUtils = require('../../utils/configUtils'),
path = require('path'),
helpers = require('../../../server/helpers');
describe('{{navigation}} helper', function () {
var runHelper = function (data) {
return function () {
helpers.navigation(data);
};
},
optionsData;
before(function (done) {
hbs.express4({
partialsDir: [configUtils.config.get('paths').helperTemplates]
});
hbs.cachePartials(function () {
done();
});
// The navigation partial expects this helper
// @TODO: change to register with Ghost's own registration tools
hbs.registerHelper('url', helpers.url);
});
beforeEach(function () {
optionsData = {
data: {
blog: {
navigation: []
},
root: {
relativeUrl: ''
}
}
};
});
it('should throw errors on invalid data', function () {
// Test 1: navigation = string
optionsData.data.blog.navigation = 'not an object';
runHelper(optionsData).should.throwError('navigation data is not an object or is a function');
// Test 2: navigation = function
optionsData.data.blog.navigation = function () {
};
runHelper(optionsData).should.throwError('navigation data is not an object or is a function');
// Test 3: invalid label
optionsData.data.blog.navigation = [{label: 1, url: 'bar'}];
runHelper(optionsData).should.throwError('Invalid value, Url and Label must be strings');
// Test 4: invalid url
optionsData.data.blog.navigation = [{label: 'foo', url: 1}];
runHelper(optionsData).should.throwError('Invalid value, Url and Label must be strings');
});
it('can render empty nav', function () {
var rendered = helpers.navigation(optionsData);
should.exist(rendered);
rendered.string.should.be.equal('');
});
it('can handle relativeUrl not being set (e.g. for images/assets)', function () {
var singleItem = {label: 'Foo', url: '/foo'},
rendered;
delete optionsData.data.root.relativeUrl;
optionsData.data.blog.navigation = [singleItem];
rendered = helpers.navigation(optionsData);
rendered.string.should.containEql('li');
rendered.string.should.containEql('nav-foo');
rendered.string.should.containEql('/foo');
});
it('can render one item', function () {
var singleItem = {label: 'Foo', url: '/foo'},
testUrl = 'href="' + configUtils.config.get('url') + '/foo"',
rendered;
optionsData.data.blog.navigation = [singleItem];
rendered = helpers.navigation(optionsData);
should.exist(rendered);
rendered.string.should.containEql('li');
rendered.string.should.containEql('nav-foo');
rendered.string.should.containEql(testUrl);
});
it('can render multiple items', function () {
var firstItem = {label: 'Foo', url: '/foo'},
secondItem = {label: 'Bar Baz Qux', url: '/qux'},
testUrl = 'href="' + configUtils.config.get('url') + '/foo"',
testUrl2 = 'href="' + configUtils.config.get('url') + '/qux"',
rendered;
optionsData.data.blog.navigation = [firstItem, secondItem];
rendered = helpers.navigation(optionsData);
should.exist(rendered);
rendered.string.should.containEql('nav-foo');
rendered.string.should.containEql('nav-bar-baz-qux');
rendered.string.should.containEql(testUrl);
rendered.string.should.containEql(testUrl2);
});
it('can annotate the current url', function () {
var firstItem = {label: 'Foo', url: '/foo'},
secondItem = {label: 'Bar', url: '/qux'},
rendered;
optionsData.data.blog.navigation = [firstItem, secondItem];
optionsData.data.root.relativeUrl = '/foo';
rendered = helpers.navigation(optionsData);
should.exist(rendered);
rendered.string.should.containEql('nav-foo');
rendered.string.should.containEql('nav-current');
rendered.string.should.containEql('nav-foo nav-current');
rendered.string.should.containEql('nav-bar"');
});
it('can annotate current url with trailing slash', function () {
var firstItem = {label: 'Foo', url: '/foo'},
secondItem = {label: 'Bar', url: '/qux'},
rendered;
optionsData.data.blog.navigation = [firstItem, secondItem];
optionsData.data.root.relativeUrl = '/foo/';
rendered = helpers.navigation(optionsData);
should.exist(rendered);
rendered.string.should.containEql('nav-foo');
rendered.string.should.containEql('nav-current');
rendered.string.should.containEql('nav-foo nav-current');
rendered.string.should.containEql('nav-bar"');
});
it('doesn\'t html-escape URLs', function () {
var firstItem = {label: 'Foo', url: '/?foo=bar&baz=qux'},
rendered;
optionsData.data.blog.navigation = [firstItem];
rendered = helpers.navigation(optionsData);
should.exist(rendered);
rendered.string.should.not.containEql('=');
rendered.string.should.not.containEql('&');
rendered.string.should.containEql('/?foo=bar&baz=qux');
});
it('encodes URLs', function () {
var firstItem = {label: 'Foo', url: '/?foo=space bar&'},
rendered;
optionsData.data.blog.navigation = [firstItem];
rendered = helpers.navigation(optionsData);
should.exist(rendered);
rendered.string.should.containEql('foo=space%20bar');
rendered.string.should.not.containEql('');
rendered.string.should.containEql('%3Cscript%3Ealert(%22gotcha%22)%3C/script%3E');
});
it('doesn\'t double-encode URLs', function () {
var firstItem = {label: 'Foo', url: '/?foo=space%20bar'},
rendered;
optionsData.data.blog.navigation = [firstItem];
rendered = helpers.navigation(optionsData);
should.exist(rendered);
rendered.string.should.not.containEql('foo=space%2520bar');
});
});
describe('{{navigation}} helper with custom template', function () {
var optionsData;
before(function (done) {
hbs.express4({
partialsDir: [path.resolve(configUtils.config.get('paths').corePath, 'test/unit/helpers/test_tpl')]
});
hbs.cachePartials(function () {
done();
});
});
beforeEach(function () {
optionsData = {
data: {
blog: {
navigation: [],
title: 'Chaos is a ladder.'
},
root: {
relativeUrl: ''
}
}
};
});
it('can render one item and @blog title', function () {
var singleItem = {label: 'Foo', url: '/foo'},
testUrl = 'href="' + configUtils.config.get('url') + '/foo"',
rendered;
optionsData.data.blog.navigation = [singleItem];
rendered = helpers.navigation(optionsData);
should.exist(rendered);
rendered.string.should.containEql('Chaos is a ladder');
rendered.string.should.containEql(testUrl);
rendered.string.should.containEql('Foo');
});
});