diff --git a/core/frontend/helpers/navigation.js b/core/frontend/helpers/navigation.js index 41632a8243..1a8b16c7ea 100644 --- a/core/frontend/helpers/navigation.js +++ b/core/frontend/helpers/navigation.js @@ -75,8 +75,11 @@ module.exports = function navigation(options) { return out; }); - const context = _.merge({}, this, options.hash, {navigation: output}); + // CASE: The navigation helper should have access to the navigation items at the top level. + this.navigation = output; + // CASE: The navigation helper will forward attributes passed to it. + _.merge(this, options.hash); const data = createFrame(options.data); - return templates.execute('navigation', context, {data}); + return templates.execute('navigation', this, {data}); }; diff --git a/core/frontend/helpers/pagination.js b/core/frontend/helpers/pagination.js index dbf307174c..adbcc54dfc 100644 --- a/core/frontend/helpers/pagination.js +++ b/core/frontend/helpers/pagination.js @@ -41,10 +41,14 @@ pagination = function (options) { !_.isNumber(this.pagination.total) || !_.isNumber(this.pagination.limit)) { throw new errors.IncorrectUsageError({message: i18n.t('warnings.helpers.pagination.valuesMustBeNumeric')}); } - const context = _.merge({}, this, options.hash, this.pagination); + + // CASE: The pagination helper should have access to the pagination properties at the top level. + _.merge(this, this.pagination); + // CASE: The pagination helper will forward attributes passed to it. + _.merge(this, options.hash); const data = createFrame(options.data); - return templates.execute('pagination', context, {data}); + return templates.execute('pagination', this, {data}); }; module.exports = pagination; diff --git a/core/test/unit/helpers/navigation_spec.js b/core/test/unit/helpers/navigation_spec.js index ab6c2e6564..4e59b1038a 100644 --- a/core/test/unit/helpers/navigation_spec.js +++ b/core/test/unit/helpers/navigation_spec.js @@ -1,18 +1,14 @@ -var should = require('should'), - hbs = require('../../../frontend/services/themes/engine'), +const should = require('should'); +const hbs = require('../../../frontend/services/themes/engine'); +const configUtils = require('../../utils/configUtils'); +const path = require('path'); +const helpers = require('../../../frontend/helpers'); - configUtils = require('../../utils/configUtils'), - path = require('path'), - - helpers = require('../../../frontend/helpers'); +const runHelper = data => helpers.navigation.call({}, data); +const runHelperThunk = data => () => runHelper(data); describe('{{navigation}} helper', function () { - var runHelper = function (data) { - return function () { - helpers.navigation(data); - }; - }, - optionsData; + let optionsData; before(function (done) { hbs.express4({ @@ -26,6 +22,7 @@ describe('{{navigation}} helper', function () { // The navigation partial expects this helper // @TODO: change to register with Ghost's own registration tools hbs.registerHelper('url', helpers.url); + hbs.registerHelper('foreach', helpers.foreach); }); beforeEach(function () { @@ -44,24 +41,24 @@ describe('{{navigation}} helper', function () { 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'); + runHelperThunk(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'); + runHelperThunk(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'); + runHelperThunk(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'); + runHelperThunk(optionsData).should.throwError('Invalid value, Url and Label must be strings'); }); it('can render empty nav', function () { - var rendered = helpers.navigation(optionsData); + var rendered = runHelper(optionsData); should.exist(rendered); rendered.string.should.be.equal(''); @@ -73,7 +70,7 @@ describe('{{navigation}} helper', function () { delete optionsData.data.root.relativeUrl; optionsData.data.blog.navigation = [singleItem]; - rendered = helpers.navigation(optionsData); + rendered = runHelper(optionsData); rendered.string.should.containEql('li'); rendered.string.should.containEql('nav-foo'); rendered.string.should.containEql('/foo'); @@ -85,7 +82,7 @@ describe('{{navigation}} helper', function () { rendered; optionsData.data.blog.navigation = [singleItem]; - rendered = helpers.navigation(optionsData); + rendered = runHelper(optionsData); should.exist(rendered); rendered.string.should.containEql('li'); @@ -101,7 +98,7 @@ describe('{{navigation}} helper', function () { rendered; optionsData.data.blog.navigation = [firstItem, secondItem]; - rendered = helpers.navigation(optionsData); + rendered = runHelper(optionsData); should.exist(rendered); rendered.string.should.containEql('nav-foo'); @@ -117,7 +114,7 @@ describe('{{navigation}} helper', function () { optionsData.data.blog.navigation = [firstItem, secondItem]; optionsData.data.root.relativeUrl = '/foo'; - rendered = helpers.navigation(optionsData); + rendered = runHelper(optionsData); should.exist(rendered); rendered.string.should.containEql('nav-foo'); @@ -133,7 +130,7 @@ describe('{{navigation}} helper', function () { optionsData.data.blog.navigation = [firstItem, secondItem]; optionsData.data.root.relativeUrl = '/foo/'; - rendered = helpers.navigation(optionsData); + rendered = runHelper(optionsData); should.exist(rendered); rendered.string.should.containEql('nav-foo'); @@ -147,7 +144,7 @@ describe('{{navigation}} helper', function () { rendered; optionsData.data.blog.navigation = [firstItem]; - rendered = helpers.navigation(optionsData); + rendered = runHelper(optionsData); should.exist(rendered); rendered.string.should.not.containEql('='); @@ -160,7 +157,7 @@ describe('{{navigation}} helper', function () { rendered; optionsData.data.blog.navigation = [firstItem]; - rendered = helpers.navigation(optionsData); + rendered = runHelper(optionsData); should.exist(rendered); rendered.string.should.containEql('foo=space%20bar'); @@ -173,7 +170,7 @@ describe('{{navigation}} helper', function () { rendered; optionsData.data.blog.navigation = [firstItem]; - rendered = helpers.navigation(optionsData); + rendered = runHelper(optionsData); should.exist(rendered); rendered.string.should.not.containEql('foo=space%2520bar'); @@ -213,7 +210,7 @@ describe('{{navigation}} helper with custom template', function () { // Set @blog.title optionsData.data.blog.title = 'Chaos is a ladder.'; - rendered = helpers.navigation(optionsData); + rendered = runHelper(optionsData); should.exist(rendered); rendered.string.should.containEql('Chaos is a ladder'); @@ -229,7 +226,7 @@ describe('{{navigation}} helper with custom template', function () { // Simulate {{navigation isHeader=true}} optionsData.hash = {isHeader: true}; - rendered = helpers.navigation(optionsData); + rendered = runHelper(optionsData); should.exist(rendered); rendered.string.should.not.containEql('Chaos is a ladder');