2019-08-19 14:41:09 +03:00
|
|
|
const should = require('should');
|
2020-03-30 18:26:47 +03:00
|
|
|
const hbs = require('../../../core/frontend/services/themes/engine');
|
2019-08-19 14:41:09 +03:00
|
|
|
const configUtils = require('../../utils/configUtils');
|
|
|
|
const path = require('path');
|
2020-03-30 18:26:47 +03:00
|
|
|
const helpers = require('../../../core/frontend/helpers');
|
2017-03-23 22:00:58 +03:00
|
|
|
|
2019-05-20 13:31:56 +03:00
|
|
|
const runHelper = data => helpers.navigation.call({}, data);
|
|
|
|
const runHelperThunk = data => () => runHelper(data);
|
2015-01-21 10:00:38 +03:00
|
|
|
|
|
|
|
describe('{{navigation}} helper', function () {
|
2019-05-20 13:31:56 +03:00
|
|
|
let optionsData;
|
2015-02-28 15:53:00 +03:00
|
|
|
|
2015-01-21 10:00:38 +03:00
|
|
|
before(function (done) {
|
2018-08-30 19:17:27 +03:00
|
|
|
hbs.express4({
|
2016-09-13 18:41:14 +03:00
|
|
|
partialsDir: [configUtils.config.get('paths').helperTemplates]
|
2015-03-10 18:52:00 +03:00
|
|
|
});
|
|
|
|
|
2015-01-21 10:00:38 +03:00
|
|
|
hbs.cachePartials(function () {
|
|
|
|
done();
|
|
|
|
});
|
2017-03-23 22:00:58 +03:00
|
|
|
|
|
|
|
// The navigation partial expects this helper
|
|
|
|
// @TODO: change to register with Ghost's own registration tools
|
2019-07-19 00:37:08 +03:00
|
|
|
hbs.registerHelper('link_class', helpers.link_class);
|
|
|
|
hbs.registerHelper('concat', helpers.concat);
|
2017-03-23 22:00:58 +03:00
|
|
|
hbs.registerHelper('url', helpers.url);
|
2019-05-20 13:31:56 +03:00
|
|
|
hbs.registerHelper('foreach', helpers.foreach);
|
2015-01-21 10:00:38 +03:00
|
|
|
});
|
|
|
|
|
2015-02-28 15:53:00 +03:00
|
|
|
beforeEach(function () {
|
|
|
|
optionsData = {
|
|
|
|
data: {
|
2019-09-10 12:37:04 +03:00
|
|
|
site: {
|
2019-12-04 07:12:02 +03:00
|
|
|
navigation: [],
|
|
|
|
secondary_navigation: []
|
2015-02-28 15:53:00 +03:00
|
|
|
},
|
|
|
|
root: {
|
|
|
|
relativeUrl: ''
|
|
|
|
}
|
|
|
|
}
|
|
|
|
};
|
|
|
|
});
|
|
|
|
|
2015-01-21 10:00:38 +03:00
|
|
|
it('should throw errors on invalid data', function () {
|
2015-02-28 15:53:00 +03:00
|
|
|
// Test 1: navigation = string
|
2019-09-10 12:37:04 +03:00
|
|
|
optionsData.data.site.navigation = 'not an object';
|
2019-05-20 13:31:56 +03:00
|
|
|
runHelperThunk(optionsData).should.throwError('navigation data is not an object or is a function');
|
2015-01-21 10:00:38 +03:00
|
|
|
|
2015-02-28 15:53:00 +03:00
|
|
|
// Test 2: navigation = function
|
2019-09-10 12:37:04 +03:00
|
|
|
optionsData.data.site.navigation = function () {
|
2017-03-21 11:24:11 +03:00
|
|
|
};
|
2019-05-20 13:31:56 +03:00
|
|
|
runHelperThunk(optionsData).should.throwError('navigation data is not an object or is a function');
|
2015-01-21 10:00:38 +03:00
|
|
|
|
2015-02-28 15:53:00 +03:00
|
|
|
// Test 3: invalid label
|
2019-09-10 12:37:04 +03:00
|
|
|
optionsData.data.site.navigation = [{label: 1, url: 'bar'}];
|
2019-05-20 13:31:56 +03:00
|
|
|
runHelperThunk(optionsData).should.throwError('Invalid value, Url and Label must be strings');
|
2015-02-28 15:53:00 +03:00
|
|
|
|
|
|
|
// Test 4: invalid url
|
2019-09-10 12:37:04 +03:00
|
|
|
optionsData.data.site.navigation = [{label: 'foo', url: 1}];
|
2019-05-20 13:31:56 +03:00
|
|
|
runHelperThunk(optionsData).should.throwError('Invalid value, Url and Label must be strings');
|
2015-01-21 10:00:38 +03:00
|
|
|
});
|
|
|
|
|
|
|
|
it('can render empty nav', function () {
|
2020-04-29 18:44:27 +03:00
|
|
|
const rendered = runHelper(optionsData);
|
2015-01-21 10:00:38 +03:00
|
|
|
|
|
|
|
should.exist(rendered);
|
|
|
|
rendered.string.should.be.equal('');
|
|
|
|
});
|
|
|
|
|
2016-06-07 22:10:20 +03:00
|
|
|
it('can handle relativeUrl not being set (e.g. for images/assets)', function () {
|
2020-04-29 18:44:27 +03:00
|
|
|
const singleItem = {label: 'Foo', url: '/foo'};
|
|
|
|
let rendered;
|
2016-06-07 22:10:20 +03:00
|
|
|
delete optionsData.data.root.relativeUrl;
|
|
|
|
|
2019-09-10 12:37:04 +03:00
|
|
|
optionsData.data.site.navigation = [singleItem];
|
2019-05-20 13:31:56 +03:00
|
|
|
rendered = runHelper(optionsData);
|
2016-06-07 22:10:20 +03:00
|
|
|
rendered.string.should.containEql('li');
|
|
|
|
rendered.string.should.containEql('nav-foo');
|
|
|
|
rendered.string.should.containEql('/foo');
|
|
|
|
});
|
|
|
|
|
2015-01-21 10:00:38 +03:00
|
|
|
it('can render one item', function () {
|
2020-04-29 18:44:27 +03:00
|
|
|
const singleItem = {label: 'Foo', url: '/foo'};
|
|
|
|
const testUrl = 'href="' + configUtils.config.get('url') + '/foo"';
|
|
|
|
let rendered;
|
2015-02-28 15:53:00 +03:00
|
|
|
|
2019-09-10 12:37:04 +03:00
|
|
|
optionsData.data.site.navigation = [singleItem];
|
2019-05-20 13:31:56 +03:00
|
|
|
rendered = runHelper(optionsData);
|
2015-01-21 10:00:38 +03:00
|
|
|
|
|
|
|
should.exist(rendered);
|
|
|
|
rendered.string.should.containEql('li');
|
|
|
|
rendered.string.should.containEql('nav-foo');
|
2015-02-16 00:44:07 +03:00
|
|
|
rendered.string.should.containEql(testUrl);
|
2015-01-21 10:00:38 +03:00
|
|
|
});
|
|
|
|
|
|
|
|
it('can render multiple items', function () {
|
2020-04-29 18:44:27 +03:00
|
|
|
const firstItem = {label: 'Foo', url: '/foo'};
|
|
|
|
const secondItem = {label: 'Bar Baz Qux', url: '/qux'};
|
|
|
|
const testUrl = 'href="' + configUtils.config.get('url') + '/foo"';
|
|
|
|
const testUrl2 = 'href="' + configUtils.config.get('url') + '/qux"';
|
|
|
|
let rendered;
|
2015-02-28 15:53:00 +03:00
|
|
|
|
2019-09-10 12:37:04 +03:00
|
|
|
optionsData.data.site.navigation = [firstItem, secondItem];
|
2019-05-20 13:31:56 +03:00
|
|
|
rendered = runHelper(optionsData);
|
2015-01-21 10:00:38 +03:00
|
|
|
|
|
|
|
should.exist(rendered);
|
|
|
|
rendered.string.should.containEql('nav-foo');
|
|
|
|
rendered.string.should.containEql('nav-bar-baz-qux');
|
2015-02-16 00:44:07 +03:00
|
|
|
rendered.string.should.containEql(testUrl);
|
|
|
|
rendered.string.should.containEql(testUrl2);
|
2015-01-21 10:00:38 +03:00
|
|
|
});
|
|
|
|
|
|
|
|
it('can annotate the current url', function () {
|
2020-04-29 18:44:27 +03:00
|
|
|
const firstItem = {label: 'Foo', url: '/foo'};
|
|
|
|
const secondItem = {label: 'Bar', url: '/qux'};
|
|
|
|
let rendered;
|
2015-02-28 15:53:00 +03:00
|
|
|
|
2019-09-10 12:37:04 +03:00
|
|
|
optionsData.data.site.navigation = [firstItem, secondItem];
|
2015-02-28 15:53:00 +03:00
|
|
|
optionsData.data.root.relativeUrl = '/foo';
|
2019-05-20 13:31:56 +03:00
|
|
|
rendered = runHelper(optionsData);
|
2015-01-21 10:00:38 +03:00
|
|
|
|
|
|
|
should.exist(rendered);
|
|
|
|
rendered.string.should.containEql('nav-foo');
|
|
|
|
rendered.string.should.containEql('nav-current');
|
|
|
|
rendered.string.should.containEql('nav-foo nav-current');
|
2015-02-16 00:44:07 +03:00
|
|
|
rendered.string.should.containEql('nav-bar"');
|
2015-01-21 10:00:38 +03:00
|
|
|
});
|
2016-02-02 13:29:05 +03:00
|
|
|
|
|
|
|
it('can annotate current url with trailing slash', function () {
|
2020-04-29 18:44:27 +03:00
|
|
|
const firstItem = {label: 'Foo', url: '/foo'};
|
|
|
|
const secondItem = {label: 'Bar', url: '/qux'};
|
|
|
|
let rendered;
|
2016-02-02 13:29:05 +03:00
|
|
|
|
2019-09-10 12:37:04 +03:00
|
|
|
optionsData.data.site.navigation = [firstItem, secondItem];
|
2016-02-02 13:29:05 +03:00
|
|
|
optionsData.data.root.relativeUrl = '/foo/';
|
2019-05-20 13:31:56 +03:00
|
|
|
rendered = runHelper(optionsData);
|
2016-02-02 13:29:05 +03:00
|
|
|
|
|
|
|
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"');
|
|
|
|
});
|
2017-01-11 13:45:56 +03:00
|
|
|
|
|
|
|
it('doesn\'t html-escape URLs', function () {
|
2020-04-29 18:44:27 +03:00
|
|
|
const firstItem = {label: 'Foo', url: '/?foo=bar&baz=qux'};
|
|
|
|
let rendered;
|
2017-01-11 13:45:56 +03:00
|
|
|
|
2019-09-10 12:37:04 +03:00
|
|
|
optionsData.data.site.navigation = [firstItem];
|
2019-05-20 13:31:56 +03:00
|
|
|
rendered = runHelper(optionsData);
|
2017-01-11 13:45:56 +03:00
|
|
|
|
|
|
|
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 () {
|
2020-04-29 18:44:27 +03:00
|
|
|
const firstItem = {label: 'Foo', url: '/?foo=space bar&<script>alert("gotcha")</script>'};
|
|
|
|
let rendered;
|
2017-01-11 13:45:56 +03:00
|
|
|
|
2019-09-10 12:37:04 +03:00
|
|
|
optionsData.data.site.navigation = [firstItem];
|
2019-05-20 13:31:56 +03:00
|
|
|
rendered = runHelper(optionsData);
|
2017-01-11 13:45:56 +03:00
|
|
|
|
|
|
|
should.exist(rendered);
|
|
|
|
rendered.string.should.containEql('foo=space%20bar');
|
|
|
|
rendered.string.should.not.containEql('<script>alert("gotcha")</script>');
|
|
|
|
rendered.string.should.containEql('%3Cscript%3Ealert(%22gotcha%22)%3C/script%3E');
|
|
|
|
});
|
|
|
|
|
|
|
|
it('doesn\'t double-encode URLs', function () {
|
2020-04-29 18:44:27 +03:00
|
|
|
const firstItem = {label: 'Foo', url: '/?foo=space%20bar'};
|
|
|
|
let rendered;
|
2017-01-11 13:45:56 +03:00
|
|
|
|
2019-09-10 12:37:04 +03:00
|
|
|
optionsData.data.site.navigation = [firstItem];
|
2019-05-20 13:31:56 +03:00
|
|
|
rendered = runHelper(optionsData);
|
2017-01-11 13:45:56 +03:00
|
|
|
|
|
|
|
should.exist(rendered);
|
|
|
|
rendered.string.should.not.containEql('foo=space%2520bar');
|
|
|
|
});
|
2019-12-04 07:12:02 +03:00
|
|
|
|
|
|
|
describe('type="secondary"', function () {
|
|
|
|
it('can render one item', function () {
|
2020-04-29 18:44:27 +03:00
|
|
|
const singleItem = {label: 'Foo', url: '/foo'};
|
|
|
|
const testUrl = 'href="' + configUtils.config.get('url') + '/foo"';
|
|
|
|
let rendered;
|
2019-12-04 07:12:02 +03:00
|
|
|
|
|
|
|
optionsData.data.site.secondary_navigation = [singleItem];
|
|
|
|
optionsData.hash = {type: 'secondary'};
|
|
|
|
rendered = runHelper(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 () {
|
2020-04-29 18:44:27 +03:00
|
|
|
const firstItem = {label: 'Foo', url: '/foo'};
|
|
|
|
const secondItem = {label: 'Bar Baz Qux', url: '/qux'};
|
|
|
|
const testUrl = 'href="' + configUtils.config.get('url') + '/foo"';
|
|
|
|
const testUrl2 = 'href="' + configUtils.config.get('url') + '/qux"';
|
|
|
|
let rendered;
|
2019-12-04 07:12:02 +03:00
|
|
|
|
|
|
|
optionsData.data.site.secondary_navigation = [firstItem, secondItem];
|
|
|
|
optionsData.hash = {type: 'secondary'};
|
|
|
|
rendered = runHelper(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);
|
|
|
|
});
|
|
|
|
});
|
2015-01-21 10:00:38 +03:00
|
|
|
});
|
2015-03-28 19:00:57 +03:00
|
|
|
|
|
|
|
describe('{{navigation}} helper with custom template', function () {
|
2020-04-29 18:44:27 +03:00
|
|
|
let optionsData;
|
2015-03-28 19:00:57 +03:00
|
|
|
|
|
|
|
before(function (done) {
|
2020-03-30 18:26:47 +03:00
|
|
|
hbs.express4({partialsDir: [path.resolve(__dirname, './test_tpl')]});
|
2015-03-28 19:00:57 +03:00
|
|
|
|
|
|
|
hbs.cachePartials(function () {
|
|
|
|
done();
|
|
|
|
});
|
|
|
|
});
|
|
|
|
|
|
|
|
beforeEach(function () {
|
|
|
|
optionsData = {
|
|
|
|
data: {
|
2019-09-10 12:37:04 +03:00
|
|
|
site: {
|
2019-12-04 07:12:02 +03:00
|
|
|
navigation: [{label: 'Foo', url: '/foo'}],
|
|
|
|
secondary_navigation: [{label: 'Fighters', url: '/foo'}]
|
2015-03-28 19:00:57 +03:00
|
|
|
},
|
|
|
|
root: {
|
|
|
|
relativeUrl: ''
|
|
|
|
}
|
|
|
|
}
|
|
|
|
};
|
|
|
|
});
|
|
|
|
|
2019-09-10 12:37:04 +03:00
|
|
|
it('can render one item and @site title', function () {
|
2020-04-29 18:44:27 +03:00
|
|
|
const testUrl = 'href="' + configUtils.config.get('url') + '/foo"';
|
|
|
|
let rendered;
|
2015-03-28 19:00:57 +03:00
|
|
|
|
2019-09-10 12:37:04 +03:00
|
|
|
// Set @site.title
|
|
|
|
optionsData.data.site.title = 'Chaos is a ladder.';
|
2019-03-09 22:35:19 +03:00
|
|
|
|
2019-05-20 13:31:56 +03:00
|
|
|
rendered = runHelper(optionsData);
|
2015-03-28 19:00:57 +03:00
|
|
|
|
|
|
|
should.exist(rendered);
|
|
|
|
rendered.string.should.containEql('Chaos is a ladder');
|
2019-03-09 22:35:19 +03:00
|
|
|
rendered.string.should.not.containEql('isHeader is set');
|
2019-12-04 07:12:02 +03:00
|
|
|
rendered.string.should.not.containEql('Jeremy Bearimy baby!');
|
2019-03-09 22:35:19 +03:00
|
|
|
rendered.string.should.containEql(testUrl);
|
|
|
|
rendered.string.should.containEql('Foo');
|
|
|
|
});
|
|
|
|
|
|
|
|
it('can pass attributes through', function () {
|
2020-04-29 18:44:27 +03:00
|
|
|
const testUrl = 'href="' + configUtils.config.get('url') + '/foo"';
|
|
|
|
let rendered;
|
2019-03-09 22:35:19 +03:00
|
|
|
|
|
|
|
// Simulate {{navigation isHeader=true}}
|
|
|
|
optionsData.hash = {isHeader: true};
|
|
|
|
|
2019-05-20 13:31:56 +03:00
|
|
|
rendered = runHelper(optionsData);
|
2019-03-09 22:35:19 +03:00
|
|
|
|
|
|
|
should.exist(rendered);
|
|
|
|
rendered.string.should.not.containEql('Chaos is a ladder');
|
|
|
|
rendered.string.should.containEql('isHeader is set');
|
2019-12-04 07:12:02 +03:00
|
|
|
rendered.string.should.not.containEql('Jeremy Bearimy baby!');
|
2015-03-28 19:00:57 +03:00
|
|
|
rendered.string.should.containEql(testUrl);
|
|
|
|
rendered.string.should.containEql('Foo');
|
|
|
|
});
|
2019-12-04 07:12:02 +03:00
|
|
|
|
|
|
|
it('sets isSecondary for type=secondary', function () {
|
2020-04-29 18:44:27 +03:00
|
|
|
const testUrl = 'href="' + configUtils.config.get('url') + '/foo"';
|
|
|
|
let rendered;
|
2019-12-04 07:12:02 +03:00
|
|
|
|
|
|
|
// Simulate {{navigation type="secondary"}}
|
|
|
|
optionsData.hash = {type: 'secondary'};
|
|
|
|
|
|
|
|
rendered = runHelper(optionsData);
|
|
|
|
|
|
|
|
should.exist(rendered);
|
|
|
|
rendered.string.should.not.containEql('Chaos is a ladder');
|
|
|
|
rendered.string.should.not.containEql('isHeader is set');
|
|
|
|
rendered.string.should.containEql('Jeremy Bearimy baby!');
|
|
|
|
rendered.string.should.containEql(testUrl);
|
|
|
|
rendered.string.should.containEql('Fighters');
|
|
|
|
});
|
2015-03-28 19:00:57 +03:00
|
|
|
});
|