From a01e58a15e9679321007eff7ece8360f0a6c5188 Mon Sep 17 00:00:00 2001 From: Matt Olson Date: Thu, 14 Dec 2017 08:56:36 -0800 Subject: [PATCH] STRF-4237: Split off rendering functionality * Split off actual rendering functionality into new module called paper-handlebars. This encapsulates rendering with Handlebars v3. This split allows us to introduce Handlebars v4 and potentially other template engines in the future, without changing the Paper interface. --- .eslintignore | 1 + .eslintrc | 22 +- .travis.yml | 4 +- CHANGELOG.md | 30 +- LICENSE | 2 +- README.md | 4 +- gulpfile.js | 23 -- helpers/all.js | 48 --- helpers/any.js | 57 ---- helpers/block.js | 19 -- helpers/cdn.js | 9 - helpers/compare.js | 45 --- helpers/concat.js | 14 - helpers/contains.js | 27 -- helpers/deprecated.js | 74 ----- helpers/dynamicComponent.js | 29 -- helpers/for.js | 48 --- helpers/getFontsCollection.js | 85 ----- helpers/getImage.js | 38 --- helpers/helperMissing.js | 9 - helpers/if.js | 103 ------ helpers/inject.js | 19 -- helpers/join.js | 27 -- helpers/json.js | 9 - helpers/lang.js | 15 - helpers/langJson.js | 13 - helpers/limit.js | 25 -- helpers/money.js | 40 --- helpers/nl2br.js | 11 - helpers/or.js | 48 --- helpers/pluck.js | 11 - helpers/pre.js | 13 - helpers/region.js | 17 - helpers/replace.js | 27 -- helpers/snippets.js | 9 - helpers/stylesheet.js | 29 -- helpers/thirdParty.js | 149 --------- helpers/toLowerCase.js | 14 - helpers/truncate.js | 38 --- index.js | 295 +++++++----------- package.json | 33 +- spec/index.js | 127 ++++++++ test/lib/logger.spec.js => spec/lib/logger.js | 0 .../lib/translator.js | 8 +- test/helpers/all.js | 103 ------ test/helpers/any.js | 98 ------ test/helpers/block.js | 51 --- test/helpers/cdn.js | 172 ---------- test/helpers/compare.js | 91 ------ test/helpers/concat.js | 23 -- test/helpers/dynamicComponent.js | 37 --- test/helpers/equals.js | 39 --- test/helpers/for.js | 93 ------ test/helpers/getFontsCollection.js | 48 --- test/helpers/getImage.js | 110 ------- test/helpers/getShortMonth.js | 32 -- test/helpers/helperMissing.js | 22 -- test/helpers/if.js | 290 ----------------- test/helpers/inject.js | 27 -- test/helpers/join.js | 50 --- test/helpers/json.js | 25 -- test/helpers/lang.js | 52 --- test/helpers/langJson.js | 46 --- test/helpers/limit.js | 31 -- test/helpers/nl2br.js | 25 -- test/helpers/or.js | 57 ---- test/helpers/pluck.js | 32 -- test/helpers/pre.js | 30 -- test/helpers/region.js | 59 ---- test/helpers/replace.js | 60 ---- test/helpers/snippets.js | 22 -- test/helpers/stylesheet.js | 53 ---- test/helpers/thirdParty.js | 220 ------------- test/helpers/toLowerCase.js | 49 --- test/helpers/truncate.js | 62 ---- test/index.js | 157 ---------- 76 files changed, 295 insertions(+), 3639 deletions(-) create mode 100644 .eslintignore delete mode 100644 gulpfile.js delete mode 100644 helpers/all.js delete mode 100644 helpers/any.js delete mode 100644 helpers/block.js delete mode 100644 helpers/cdn.js delete mode 100644 helpers/compare.js delete mode 100644 helpers/concat.js delete mode 100644 helpers/contains.js delete mode 100644 helpers/deprecated.js delete mode 100644 helpers/dynamicComponent.js delete mode 100644 helpers/for.js delete mode 100644 helpers/getFontsCollection.js delete mode 100644 helpers/getImage.js delete mode 100644 helpers/helperMissing.js delete mode 100644 helpers/if.js delete mode 100644 helpers/inject.js delete mode 100644 helpers/join.js delete mode 100644 helpers/json.js delete mode 100644 helpers/lang.js delete mode 100644 helpers/langJson.js delete mode 100644 helpers/limit.js delete mode 100644 helpers/money.js delete mode 100644 helpers/nl2br.js delete mode 100644 helpers/or.js delete mode 100644 helpers/pluck.js delete mode 100644 helpers/pre.js delete mode 100644 helpers/region.js delete mode 100644 helpers/replace.js delete mode 100644 helpers/snippets.js delete mode 100644 helpers/stylesheet.js delete mode 100644 helpers/thirdParty.js delete mode 100644 helpers/toLowerCase.js delete mode 100644 helpers/truncate.js create mode 100644 spec/index.js rename test/lib/logger.spec.js => spec/lib/logger.js (100%) rename test/lib/translator.spec.js => spec/lib/translator.js (97%) delete mode 100644 test/helpers/all.js delete mode 100644 test/helpers/any.js delete mode 100644 test/helpers/block.js delete mode 100644 test/helpers/cdn.js delete mode 100644 test/helpers/compare.js delete mode 100644 test/helpers/concat.js delete mode 100644 test/helpers/dynamicComponent.js delete mode 100644 test/helpers/equals.js delete mode 100644 test/helpers/for.js delete mode 100644 test/helpers/getFontsCollection.js delete mode 100644 test/helpers/getImage.js delete mode 100644 test/helpers/getShortMonth.js delete mode 100644 test/helpers/helperMissing.js delete mode 100644 test/helpers/if.js delete mode 100644 test/helpers/inject.js delete mode 100644 test/helpers/join.js delete mode 100644 test/helpers/json.js delete mode 100644 test/helpers/lang.js delete mode 100644 test/helpers/langJson.js delete mode 100644 test/helpers/limit.js delete mode 100644 test/helpers/nl2br.js delete mode 100644 test/helpers/or.js delete mode 100644 test/helpers/pluck.js delete mode 100644 test/helpers/pre.js delete mode 100644 test/helpers/region.js delete mode 100644 test/helpers/replace.js delete mode 100644 test/helpers/snippets.js delete mode 100644 test/helpers/stylesheet.js delete mode 100644 test/helpers/thirdParty.js delete mode 100644 test/helpers/toLowerCase.js delete mode 100644 test/helpers/truncate.js delete mode 100644 test/index.js diff --git a/.eslintignore b/.eslintignore new file mode 100644 index 00000000..8d87b1d2 --- /dev/null +++ b/.eslintignore @@ -0,0 +1 @@ +node_modules/* diff --git a/.eslintrc b/.eslintrc index 5381bcfe..26f7bf3b 100644 --- a/.eslintrc +++ b/.eslintrc @@ -1,27 +1,27 @@ { "parserOptions": { "ecmaVersion": 6, - "sourceType": "module" - }, - "ecmaFeatures": { - "modules": true + "sourceType": "module", + "ecmaFeatures": { + "impliedStrict": true + }, }, "env": { - "node": true - }, - "globals": { - + "node": true, + "es6": true, }, "rules": { "curly": 2, "no-eq-null": 2, - "wrap-iife": [ 2, "any" ], + "wrap-iife": [ + 2, + "any" + ], "new-cap": 2, "no-caller": 2, "dot-notation": 0, "no-debugger": 2, "no-undef": 2, - "no-unused-vars": 2, - "indent": [2, 4] + "no-unused-vars": [2, { "args": "none" }], } } diff --git a/.travis.yml b/.travis.yml index f01823bb..9016ff51 100644 --- a/.travis.yml +++ b/.travis.yml @@ -3,9 +3,7 @@ sudo: false node_js: - 4 - 6 - + - 8 script: - npm install - - npm install -g gulp-cli - - gulp - npm test diff --git a/CHANGELOG.md b/CHANGELOG.md index 69e06445..14ebcf5b 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,27 +1,47 @@ # Changelog +## 3.0.0-rc.1 (2018-01-10) +Refactor rendering functionality into paper-handlebars [#130](https://github.com/bigcommerce/paper/pull/130) to +allow for alternate template engines. + +v3.0 Contains several breaking changes: +- Removed the direct access of `contentServiceContext` for setting page content. From now on, use `addContent()` and `getContent()`. +- Removed `getTemplateProcessor()`. This is an internal concern of `paper-handlebars` and is used by `loadTemplates`. +- Removed `loadTemplatesSync()`. This was only used by helper tests and is no longer needed. +- Removed `handlebars` instance variable. Hopefully nobody is accessing that directly. Any helpers that were accessing it have been updated in `paper-handlebars` to use the global context they are given rather than accessing Paper directly at all. +- The `translator` attribute has been moved to `paper-handlebars` and is no longer accessible directly on Paper. +- The `decorators` attribute has been moved to `paper-handlebars` and is no longer accessible directly on Paper. +- The `settings` attribute has been renamed to `siteSettings`. This should only be accessed by `paper-handlebars`. +- The `cdnify()` function has been moved into a helper library in `paper-handlebars`. +- The `inject` attribute has been removed. This is storage used by two of the helpers, and the implementation has moved to `paper-handlebars`. + +## 2.0.7 (2017-10-17) +- Always render region wrapper even if no content is present [#128](https://github.com/bigcommerce/paper/pull/128) + ## 2.0.6 (2017-09-20) - Fix a bug in the `replace` helper [#127](https://github.com/bigcommerce/paper/pull/127) ## 2.0.5 (2017-09-15) -- Fix a bug in the `replace` helper [#125](https://github.com/bigcommerce/paper/pull/125) - Added support to use operators in the `unless` helper [#126](https://github.com/bigcommerce/paper/pull/126) +- Fix a bug in the `replace` helper [#125](https://github.com/bigcommerce/paper/pull/125) ## 2.0.4 (2017-08-21) -- Added a test to `first` helper [#120](https://github.com/bigcommerce/paper/pull/120) - Lodash deprecated method replaced [#121](https://github.com/bigcommerce/paper/pull/121) +- Added a test to `first` helper [#120](https://github.com/bigcommerce/paper/pull/120) ## 2.0.3 (2017-06-21) -- Adds `region` helper [#118](https://github.com/bigcommerce/paper/pull/118) +- Add `region` helper to render content blocks [#118](https://github.com/bigcommerce/paper/pull/118) + +## 2.0.2 (2017-04-20) - Bug fix in the `stylesheet` helper [#116](https://github.com/bigcommerce/paper/pull/116) ## 2.0.1 (2017-04-12) - Adds the `{{thumbnailImage}}` helper and removes the non-existent `{{thumbnail}}` helper [#114](https://github.com/bigcommerce/paper/pull/114) ## 2.0.0 (2017-04-11) -- Adds and exposes the helpers we've selected from the handlebars-helpers library [#110](https://github.com/bigcommerce/paper/pull/110) -- Update docs to describe the interface required by paper for assembler and callbacks [#111](https://github.com/bigcommerce/paper/pull/111) - Remove configId from cdn/assets urls for preventing busting the cached assets when configId changes [#112](https://github.com/bigcommerce/paper/pull/112) +- Update docs to describe the interface required by paper for assembler and callbacks [#111](https://github.com/bigcommerce/paper/pull/111) +- Adds and exposes the helpers we've selected from the handlebars-helpers library [#110](https://github.com/bigcommerce/paper/pull/110) ## 1.2.0 (2017-02-23) - Add a helper that can truncate text. [#106](https://github.com/bigcommerce/paper/pull/106), [#108](https://github.com/bigcommerce/paper/pull/108) diff --git a/LICENSE b/LICENSE index 968af6d4..e22f7536 100644 --- a/LICENSE +++ b/LICENSE @@ -1,4 +1,4 @@ -Copyright (c) 2015-2016, Bigcommerce Inc. +Copyright (c) 2015-2018, Bigcommerce Inc. All rights reserved. Redistribution and use in source and binary forms, with or without diff --git a/README.md b/README.md index 2515c183..534f2d2f 100644 --- a/README.md +++ b/README.md @@ -1,7 +1,7 @@ # stencil-paper [![Build Status](https://travis-ci.org/bigcommerce/paper.svg?branch=master)](https://travis-ci.org/bigcommerce/paper) [![npm (scoped)](https://img.shields.io/npm/v/@bigcommerce/stencil-paper.svg)](https://www.npmjs.com/package/@bigcommerce/stencil-paper) -*stencil-paper* is a plugin for `stencil-cli` and `stapler`. Its duty is to render the themes using [Handlebars](http://handlebarsjs.com/) template engine. +*stencil-paper* is a plugin for `stencil-cli` and `stapler`. Its duty is to load templates and translations, and call out to [paper-handlebars](https://github.com/bigcommerce/paper-handlebars) to render pages. ## Usage @@ -49,7 +49,7 @@ See the [stencil API reference](https://stencil.bigcommerce.com/docs/handlebars- #### License -Copyright (c) 2015-2016, Bigcommerce Inc. +Copyright (c) 2015-2018, Bigcommerce Inc. All rights reserved. Redistribution and use in source and binary forms, with or without diff --git a/gulpfile.js b/gulpfile.js deleted file mode 100644 index 76d66495..00000000 --- a/gulpfile.js +++ /dev/null @@ -1,23 +0,0 @@ -var gulp = require('gulp'), - eslint = require('gulp-eslint'); - -gulp.task('lint', function () { - // ESLint ignores files with "node_modules" paths. - // So, it's best to have gulp ignore the directory as well. - // Also, Be sure to return the stream from the task; - // Otherwise, the task may end before the stream has finished. - return gulp.src(['**/*.js','!node_modules/**']) - // eslint() attaches the lint output to the "eslint" property - // of the file object so it can be used by other modules. - .pipe(eslint()) - // eslint.format() outputs the lint results to the console. - // Alternatively use eslint.formatEach() (see Docs). - .pipe(eslint.format()) - // To have the process exit with an error code (1) on - // lint error, return the stream and pipe to failAfterError last. - .pipe(eslint.failAfterError()); -}); - -gulp.task('default', ['lint'], function () { - // This will only run if the lint task is successful... -}); diff --git a/helpers/all.js b/helpers/all.js deleted file mode 100644 index 7d294269..00000000 --- a/helpers/all.js +++ /dev/null @@ -1,48 +0,0 @@ -'use strict'; - -var _ = require('lodash'); - -/** - * Yield block only if all arguments are valid - * - * @example - * {{#all items theme_settings.optionA theme_settings.optionB}} ... {{/all}} - */ -function helper(paper) { - paper.handlebars.registerHelper('all', function () { - - var args = [], opts, result; - - // Translate arguments to array safely - for (var i = 0; i < arguments.length; i++) { - args.push(arguments[i]); - } - - // Take the last argument (content) out of testing array - opts = args.pop(); - - // Check if all the arguments are valid / truthy - result = _.all(args, function (arg) { - if (_.isArray(arg)) { - return !!arg.length; - } - // If an empty object is passed, arg is false - else if (_.isEmpty(arg) && _.isObject(arg)) { - return false; - } - // Everything else - else { - return !!arg; - } - }); - - // If everything was valid, then "all" condition satisfied - if (result) { - return opts.fn(this); - } else { - return opts.inverse(this); - } - }); -} - -module.exports = helper; diff --git a/helpers/any.js b/helpers/any.js deleted file mode 100644 index 165863cb..00000000 --- a/helpers/any.js +++ /dev/null @@ -1,57 +0,0 @@ -'use strict'; - -var _ = require('lodash'); - -/** - * Yield block if any object within a collection matches supplied predicate - * - * @example - * {{#any items selected=true}} ... {{/any}} - */ -function helper(paper) { - paper.handlebars.registerHelper('any', function () { - - var args = [], - opts, - predicate, - any; - - // Translate arguments to array safely - for (var i = 0; i < arguments.length; i++) { - args.push(arguments[i]); - } - - // Take the last argument (content) out of testing array - opts = args.pop(); - predicate = opts.hash; - - if (!_.isEmpty(predicate)) { - // With options hash, we check the contents of first argument - any = _.any(args[0], predicate); - } else { - // DEPRECATED: Moved to #or helper - // Without options hash, we check all the arguments - any = _.any(args, function (arg) { - if (_.isArray(arg)) { - return !!arg.length; - } - // If an empty object is passed, arg is false - else if (_.isEmpty(arg) && _.isObject(arg)) { - return false; - } - // Everything else - else { - return !!arg; - } - }); - } - - if (any) { - return opts.fn(this); - } - - return opts.inverse(this); - }); -} - -module.exports = helper; diff --git a/helpers/block.js b/helpers/block.js deleted file mode 100644 index f3ea6588..00000000 --- a/helpers/block.js +++ /dev/null @@ -1,19 +0,0 @@ -'use strict'; - -function helper(paper) { - paper.handlebars.registerHelper('block', function (name) { - const options = arguments[arguments.length - 1]; - - /* Look for partial by name. */ - var partial = paper.handlebars.partials[name] || options.fn; - return partial(this, {data: options.hash}); - }); - - paper.handlebars.registerHelper('partial', function (name) { - var options = arguments[arguments.length - 1]; - - paper.handlebars.registerPartial(name, options.fn); - }); -} - -module.exports = helper; diff --git a/helpers/cdn.js b/helpers/cdn.js deleted file mode 100644 index 3f53129e..00000000 --- a/helpers/cdn.js +++ /dev/null @@ -1,9 +0,0 @@ -'use strict'; - -function helper(paper) { - paper.handlebars.registerHelper('cdn', function (assetPath) { - return paper.cdnify(assetPath); - }); -} - -module.exports = helper; diff --git a/helpers/compare.js b/helpers/compare.js deleted file mode 100644 index 80c5e4ea..00000000 --- a/helpers/compare.js +++ /dev/null @@ -1,45 +0,0 @@ -'use strict'; - -function helper(paper) { - paper.handlebars.registerHelper('compare', function (lvalue, rvalue) { - const options = arguments[arguments.length - 1]; - var operator; - var operators; - var result; - - if (arguments.length < 3) { - throw new Error("Handlerbars Helper 'compare' needs 2 parameters"); - } - - operator = options.hash.operator || "=="; - - operators = { - '==': function (l, r) { return l == r; }, - '===': function (l, r) { return l === r; }, - '!=': function (l, r) { return l != r; }, - '!==': function (l, r) { return l !== r; }, - '<': function (l, r) { return l < r; }, - '>': function (l, r) { return l > r; }, - '<=': function (l, r) { return l <= r; }, - '>=': function (l, r) { return l >= r; }, - 'typeof': function (l, r) { return typeof l == r; } - }; - - // getOwnPropertyNames is used because checking the property - // directly (like operators[x]) is insecure - // (we could use switch instead) - if (Object.getOwnPropertyNames(operators).indexOf(operator) === -1) { - throw new Error("Handlerbars Helper 'compare' doesn't know the operator " + operator); - } - - result = operators[operator](lvalue, rvalue); - - if (result) { - return options.fn(this); - } else { - return options.inverse(this); - } - }); -} - -module.exports = helper; diff --git a/helpers/concat.js b/helpers/concat.js deleted file mode 100644 index 3b480974..00000000 --- a/helpers/concat.js +++ /dev/null @@ -1,14 +0,0 @@ -'use strict'; - -/** - * Concats two values, primarily used as a subhelper - * @example - * {{@lang (concat 'products.reviews.rating.' this) }} - */ -function helper(paper) { - paper.handlebars.registerHelper('concat', function (value, otherValue) { - return new paper.handlebars.SafeString(value + otherValue); - }); -} - -module.exports = helper; diff --git a/helpers/contains.js b/helpers/contains.js deleted file mode 100644 index abb48fc2..00000000 --- a/helpers/contains.js +++ /dev/null @@ -1,27 +0,0 @@ -'use strict'; - -var _ = require('lodash'); - -/** - * Is any value included in a collection or a string? - * - * @example - * {{#contains fonts "Roboto"}} ... {{/contains}} - * {{#contains font_path "Roboto"}} ... {{/contains}} - */ -function helper(paper) { - paper.handlebars.registerHelper('contains', function () { - var args = Array.prototype.slice.call(arguments, 0, -1), - options = _.last(arguments), - contained = _.contains.apply(_, args); - - // Yield block if true - if (contained) { - return options.fn(this); - } else { - return options.inverse(this); - } - }); -} - -module.exports = helper; diff --git a/helpers/deprecated.js b/helpers/deprecated.js deleted file mode 100644 index c4d7ff39..00000000 --- a/helpers/deprecated.js +++ /dev/null @@ -1,74 +0,0 @@ -'use strict'; - -var _ = require('lodash'); - -function helper(paper) { - paper.handlebars.registerHelper('pick', function () { - return _.pick.apply(null, arguments); - }); - - /** - * @deprecate Use lang + concat - */ - paper.handlebars.registerHelper('getShortMonth', function (index) { - - switch (index) { - case 1: - return 'Jan'; - case 2: - return 'Feb'; - case 3: - return 'Mar'; - case 4: - return 'Apr'; - case 5: - return 'May'; - case 6: - return 'Jun'; - case 7: - return 'Jul'; - case 8: - return 'Aug'; - case 9: - return 'Sep'; - case 10: - return 'Oct'; - case 11: - return 'Nov'; - case 12: - return 'Dec'; - } - - return ''; - }); - - /** - * @deprecate Use {{#if val1 '==' val2}}...{{/if}} - */ - paper.handlebars.registerHelper('equals', function (val1, val2) { - const options = arguments[arguments.length - 1]; - - if (val1 != val2) { - return ''; - } - - return options.fn(); - }); - - /** - * @deprecate Use {{#for start end (context)}}...{{/for}} - */ - paper.handlebars.registerHelper('enumerate', function (start, end) { - const options = arguments[arguments.length - 1]; - var out = ''; - var i = start; - - for (i; i <= end; i++) { - out = out + options.fn(i); - } - - return out + ''; - }); -} - -module.exports = helper; diff --git a/helpers/dynamicComponent.js b/helpers/dynamicComponent.js deleted file mode 100644 index 5730adb7..00000000 --- a/helpers/dynamicComponent.js +++ /dev/null @@ -1,29 +0,0 @@ -'use strict'; - -var Path = require('path'); - -function helper(paper) { - paper.handlebars.registerHelper('dynamicComponent', function (path) { - if (!this['partial']) { - return; - } - - // prevent access to __proto__ - // or any hidden object properties - path = path.replace('__', ''); - - // We don't want a slash as a prefix - if (path[0] === '/') { - path = path.substr(1); - } - - path = Path.join(path, this['partial']); - - if (paper.handlebars.partials[path]) { - - return paper.handlebars.partials[path](this); - } - }); -} - -module.exports = helper; diff --git a/helpers/for.js b/helpers/for.js deleted file mode 100644 index 36d2a3a9..00000000 --- a/helpers/for.js +++ /dev/null @@ -1,48 +0,0 @@ -'use strict'; - -var _ = require('lodash'); - -function helper(paper) { - paper.handlebars.registerHelper('for', function (from, to, context) { - const options = arguments[arguments.length - 1]; - const maxIterations = 100; - var output = ''; - - function isOptions(obj) { - return _.isObject(obj) && obj.fn; - } - - if (isOptions(to)) { - context = {}; - to = from; - from = 1; - - } else if (isOptions(context)) { - if (_.isObject(to)) { - context = to; - to = from; - from = 1; - } - } - - if (to <= from) { - return; - } - - from = parseInt(from, 10); - to = parseInt(to, 10); - - if ((to - from) >= maxIterations) { - to = from + maxIterations - 1; - } - - for (var i = from; i <= to; i++) { - context.$index = i; - output += options.fn(context); - } - - return output; - }); -} - -module.exports = helper; diff --git a/helpers/getFontsCollection.js b/helpers/getFontsCollection.js deleted file mode 100644 index 59580b75..00000000 --- a/helpers/getFontsCollection.js +++ /dev/null @@ -1,85 +0,0 @@ -'use strict'; - -var _ = require('lodash'); - -function helper(paper) { - var handlebars = paper.handlebars; - - handlebars.registerHelper('getFontsCollection', function () { - var fontKeyFormat = new RegExp(/\w+(-\w*)*-font$/), - googleFonts = [], - linkElements = []; - - _.each(paper.themeSettings, function (value, key) { - var split; - - if (fontKeyFormat.test(key)) { - split = value.split('_'); - - switch (split[0]) { - case 'Google': - googleFonts.push(value); - break; - - default: - break; - } - } - }); - - linkElements.push(googleParser(googleFonts)); - - return new handlebars.SafeString(linkElements.join('')); - }); -} - -/** - * Parser for Google fonts - * - * @param fonts - Array of fonts that might look like - * Google_Open+Sans - * Google_Open+Sans_400 - * Google_Open+Sans_400_sans - * Google_Open+Sans_400,700_sans - * Google_Open+Sans_400,700italic - * Google_Open+Sans_400,700italic_sans - * - * @returns {string} - */ - -function googleParser(fonts) { - var collection = [], - familyHash = {}; - - _.each(fonts, function fontsIterator(font) { - var split = font.split('_'), - familyKey = split[1], // Eg: Open+Sans - weights = split[2]; // Eg: 400,700italic - - if (_.isEmpty(familyKey)) { - return; - } - - if (_.isUndefined(weights)) { - weights = ''; - } - - if (!_.isArray(familyHash[familyKey])) { - familyHash[familyKey] = []; - } - - weights = weights.split(','); - - familyHash[familyKey].push(weights); - familyHash[familyKey] = _.uniq(_.flatten(familyHash[familyKey])); - }); - - - _.each(familyHash, function fontHashIterator(weights, family) { - collection.push(family + ':' + weights.join(',')); - }); - - return ''; -} - -module.exports = helper; diff --git a/helpers/getImage.js b/helpers/getImage.js deleted file mode 100644 index 51efc59f..00000000 --- a/helpers/getImage.js +++ /dev/null @@ -1,38 +0,0 @@ -'use strict'; - -var _ = require('lodash'); - -function helper(paper) { - paper.handlebars.registerHelper('getImage', function (image, presetName, defaultImageUrl) { - var sizeRegex = /^(\d+?)x(\d+?)$/g; - var settings = paper.themeSettings || {}; - var presets = settings._images; - var size; - var width; - var height; - - if (!_.isPlainObject(image) || !_.isString(image.data) || image.data.indexOf('{:size}') === -1) { - // return empty string if not a valid image object - defaultImageUrl = defaultImageUrl ? defaultImageUrl : ''; - return _.isString(image) ? image : defaultImageUrl; - } - - if (_.isPlainObject(presets) && _.isPlainObject(presets[presetName])) { - // If preset is one of the given presets in _images - width = parseInt(presets[presetName].width, 10) || 4096; - height = parseInt(presets[presetName].height, 10) || 4096; - size = width + 'x' + height; - - } else if (sizeRegex.test(settings[presetName])) { - // If preset name is a setting and match the NNNxNNN format - size = settings[presetName]; - } else { - // Use the original image size - size = 'original'; - } - - return image.data.replace('{:size}', size); - }); -}; - -module.exports = helper; diff --git a/helpers/helperMissing.js b/helpers/helperMissing.js deleted file mode 100644 index b9d3aaea..00000000 --- a/helpers/helperMissing.js +++ /dev/null @@ -1,9 +0,0 @@ -'use strict'; - -function helper(paper) { - paper.handlebars.registerHelper('helperMissing', function () { - return undefined; - }); -} - -module.exports = helper; diff --git a/helpers/if.js b/helpers/if.js deleted file mode 100644 index dc4b9bde..00000000 --- a/helpers/if.js +++ /dev/null @@ -1,103 +0,0 @@ -'use strict'; - -var _ = require('lodash'); - -function helper(paper) { - paper.handlebars.registerHelper('unless', function () { - const options = arguments[arguments.length - 1]; - arguments[arguments.length - 1] = Object.assign({}, options, { - fn: options.inverse || (() => false), - inverse: options.fn || (() => true), - hash: options.hash - }); - - return paper.handlebars.helpers['if'].apply(this, arguments); - }); - - paper.handlebars.registerHelper('if', function (lvalue, operator, rvalue) { - const options = arguments[arguments.length - 1]; - let result; - - function isOptions(obj) { - return _.isObject(obj) && obj.fn; - } - - // Only parameter - if (isOptions(operator)) { - // If an array is passed as the only parameter - if (_.isArray(lvalue)) { - result = !!lvalue.length; - } - // If an empty object is passed, treat as false - else if (_.isEmpty(lvalue) && _.isObject(lvalue)) { - result = false; - } - // Everything else - else { - result = !!lvalue; - } - } else { - - if (isOptions(rvalue)) { - // @TODO: this is block is for backwards compatibility with 'compare' helper - // Remove after operator='==' is removed from stencil theme - rvalue = operator; - operator = options.hash.operator || "=="; - } - - switch (operator) { - case '==': - result = (lvalue == rvalue); - break; - - case '===': - result = (lvalue === rvalue); - break; - - case '!=': - result = (lvalue != rvalue); - break; - - case '!==': - result = (lvalue !== rvalue); - break; - - case '<': - result = (lvalue < rvalue); - break; - - case '>': - result = (lvalue > rvalue); - break; - - case '<=': - result = (lvalue <= rvalue); - break; - - case '>=': - result = (lvalue >= rvalue); - break; - - case 'typeof': - result = (typeof lvalue === rvalue); - break; - - default: - throw new Error("Handlerbars Helper 'if' doesn't know the operator " + operator); - } - } - - if (!options.fn || !options.inverse) { - options.fn = () => true; - options.inverse = () => false; - } - - if (result) { - return options.fn(this); - } else { - return options.inverse(this); - } - }); -} - -module.exports = helper; diff --git a/helpers/inject.js b/helpers/inject.js deleted file mode 100644 index bf478140..00000000 --- a/helpers/inject.js +++ /dev/null @@ -1,19 +0,0 @@ -'use strict'; - -function helper(paper) { - paper.handlebars.registerHelper('inject', function (key, value) { - if (typeof value === 'function') { - return; - } - - paper.inject[key] = value; - }); - - paper.handlebars.registerHelper('jsContext', function () { - const jsContext = JSON.stringify(JSON.stringify(paper.inject)); - - return new paper.handlebars.SafeString(jsContext); - }); -} - -module.exports = helper; diff --git a/helpers/join.js b/helpers/join.js deleted file mode 100644 index 3606f2d4..00000000 --- a/helpers/join.js +++ /dev/null @@ -1,27 +0,0 @@ -'use strict'; - -function helper(paper) { - paper.handlebars.registerHelper('join', function (array, separator) { - const options = arguments[arguments.length - 1]; - var config = options.hash || {}; - - array = array.slice(); - - // Truncate array - if (config.limit && array.length > config.limit) { - array = array.slice(0, config.limit); - } - - // Use lastSeparator between last and second last item, if provided - if (config.lastSeparator) { - var truncatedArray = array.slice(0, -1), - lastItem = array.slice(-1); - - return truncatedArray.join(separator) + config.lastSeparator + lastItem; - } - - return array.join(separator); - }); -} - -module.exports = helper; diff --git a/helpers/json.js b/helpers/json.js deleted file mode 100644 index afc2c362..00000000 --- a/helpers/json.js +++ /dev/null @@ -1,9 +0,0 @@ -'use strict'; - -function helper(paper) { - paper.handlebars.registerHelper('json', function (data) { - return JSON.stringify(data); - }); -} - -module.exports = helper; diff --git a/helpers/lang.js b/helpers/lang.js deleted file mode 100644 index f8bc11f1..00000000 --- a/helpers/lang.js +++ /dev/null @@ -1,15 +0,0 @@ -'use strict'; - -function helper(paper) { - paper.handlebars.registerHelper('lang', function (translationKey) { - const options = arguments[arguments.length - 1]; - - if (paper.translator) { - return paper.translator.translate(translationKey, options.hash); - } - - return ''; - }); -} - -module.exports = helper; diff --git a/helpers/langJson.js b/helpers/langJson.js deleted file mode 100644 index 05409b13..00000000 --- a/helpers/langJson.js +++ /dev/null @@ -1,13 +0,0 @@ -'use strict'; - -function helper(paper) { - paper.handlebars.registerHelper('langJson', function (keyFilter) { - if (!paper.translator) { - return '{}'; - } - - return JSON.stringify(paper.translator.getLanguage(keyFilter)); - }); -} - -module.exports = helper; diff --git a/helpers/limit.js b/helpers/limit.js deleted file mode 100644 index 017b80c0..00000000 --- a/helpers/limit.js +++ /dev/null @@ -1,25 +0,0 @@ -'use strict'; - -var _ = require('lodash'); - -/** - * Limit an array to the second argument - * - * @example - * {{limit array 4}} - */ -function helper(paper) { - paper.handlebars.registerHelper('limit', function (data, limit) { - - if (_.isString(data)) { - return data.substring(0, limit); - } - if (!_.isArray(data)) { - return []; - } - - return data.slice(0, limit); - }); -} - -module.exports = helper; diff --git a/helpers/money.js b/helpers/money.js deleted file mode 100644 index ff3163d8..00000000 --- a/helpers/money.js +++ /dev/null @@ -1,40 +0,0 @@ -'use strict'; - -var _ = require('lodash'); - -/** - * Format numbers - * - * @param integer n: length of decimal - * @param mixed s: thousands delimiter - * @param mixed c: decimal delimiter - */ -function numberFormat(value, n, s, c) { - var re = '\\d(?=(\\d{3})+' + (n > 0 ? '\\D' : '$') + ')', - num = value.toFixed(Math.max(0, ~~n)); - - return (c ? num.replace('.', c) : num).replace(new RegExp(re, 'g'), '$&' + (s || ',')); -} - -function helper(paper) { - paper.handlebars.registerHelper('money', function (value) { - var money = paper.settings.money; - - if (!_.isNumber(value)) { - return ''; - } - - value = numberFormat( - value, - money.decimal_places, - money.thousands_token, - money.decimal_token - ); - - return money.currency_location === 'left' - ? money.currency_token + ' ' + value - : value + ' ' + money.currency_token; - }); -} - -module.exports = helper; diff --git a/helpers/nl2br.js b/helpers/nl2br.js deleted file mode 100644 index a7b82fec..00000000 --- a/helpers/nl2br.js +++ /dev/null @@ -1,11 +0,0 @@ -'use strict'; - -function helper(paper) { - // https://github.com/danharper/Handlebars-Helpers/blob/master/src/helpers.js#L89 - paper.handlebars.registerHelper('nl2br', function (text) { - var nl2br = (paper.handlebars.escapeExpression(text) + '').replace(/([^>\r\n]?)(\r\n|\n\r|\r|\n)/g, '$1' + '
' + '$2'); - return new paper.handlebars.SafeString(nl2br); - }); -} - -module.exports = helper; diff --git a/helpers/or.js b/helpers/or.js deleted file mode 100644 index 28425338..00000000 --- a/helpers/or.js +++ /dev/null @@ -1,48 +0,0 @@ -'use strict'; - -var _ = require('lodash'); - -/** - * Yield block if any object within a collection matches supplied predicate - * - * @example - * {{#or 1 0 0 0 0 0}} ... {{/or}} - */ -function helper(paper) { - paper.handlebars.registerHelper('or', function () { - var args = [], - opts, - any; - - // Translate arguments to array safely - for (var i = 0; i < arguments.length; i++) { - args.push(arguments[i]); - } - - // Take the last argument (content) out of testing array - opts = args.pop(); - - // Without options hash, we check all the arguments - any = _.any(args, function (arg) { - if (_.isArray(arg)) { - return !!arg.length; - } - // If an empty object is passed, arg is false - else if (_.isEmpty(arg) && _.isObject(arg)) { - return false; - } - // Everything else - else { - return !!arg; - } - }); - - if (any) { - return opts.fn(this); - } - - return opts.inverse(this); - }); -} - -module.exports = helper; diff --git a/helpers/pluck.js b/helpers/pluck.js deleted file mode 100644 index 64cdf5ae..00000000 --- a/helpers/pluck.js +++ /dev/null @@ -1,11 +0,0 @@ -'use strict'; - -var _ = require('lodash'); - -function helper(paper) { - paper.handlebars.registerHelper('pluck', function (collection, path) { - return _.pluck(collection, path); - }); -} - -module.exports = helper; diff --git a/helpers/pre.js b/helpers/pre.js deleted file mode 100644 index 8408b52e..00000000 --- a/helpers/pre.js +++ /dev/null @@ -1,13 +0,0 @@ -'use restrict'; - -function helper(paper) { - paper.handlebars.registerHelper('pre', function (value) { - var string = JSON.stringify(value, null, 2); - - string = paper.handlebars.Utils.escapeExpression(string); - - return '
' + string + '
'; - }); -} - -module.exports = helper; diff --git a/helpers/region.js b/helpers/region.js deleted file mode 100644 index e07b8d59..00000000 --- a/helpers/region.js +++ /dev/null @@ -1,17 +0,0 @@ -'use strict'; - -function helper(paper) { - paper.handlebars.registerHelper('region', function (params) { - let regionId = params.hash.name; - - if (!paper.contentServiceContext.regions) { - return ''; - } - - const content = `
${paper.contentServiceContext.regions[regionId] || ''}
`; - - return new paper.handlebars.SafeString(content); - }); -} - -module.exports = helper; diff --git a/helpers/replace.js b/helpers/replace.js deleted file mode 100644 index afce47ad..00000000 --- a/helpers/replace.js +++ /dev/null @@ -1,27 +0,0 @@ -'use strict'; - -function helper(paper) { - paper.handlebars.registerHelper('replace', function (needle, haystack) { - const options = arguments[arguments.length - 1]; - - if (typeof needle !== 'string') { - return options.inverse(this); - } - - const regex = new RegExp(escapeRegex(needle), 'g'); - - // Yield block if true - if (typeof haystack === 'string' && regex.test(haystack)) { - return haystack.replace(regex, options.fn(this)); - } else { - return options.inverse(this); - } - }); -} - - -function escapeRegex(string) { - return string.replace(/[-\\^$*+?.()|[\]{}]/g, "\\$&"); -} - -module.exports = helper; diff --git a/helpers/snippets.js b/helpers/snippets.js deleted file mode 100644 index ea46ef19..00000000 --- a/helpers/snippets.js +++ /dev/null @@ -1,9 +0,0 @@ -'use strict'; - -function helper(paper) { - paper.handlebars.registerHelper('snippet', function (location) { - return ''; - }); -} - -module.exports = helper; diff --git a/helpers/stylesheet.js b/helpers/stylesheet.js deleted file mode 100644 index 1d7d8ad0..00000000 --- a/helpers/stylesheet.js +++ /dev/null @@ -1,29 +0,0 @@ -'use strict'; - -const _ = require('lodash'); - -function helper(paper) { - paper.handlebars.registerHelper('stylesheet', function (assetPath) { - const options = arguments[arguments.length - 1]; - const configId = paper.settings['theme_config_id']; - // append the configId only if the asset path starts with assets/css/ - const path = configId && assetPath.match(/^\/?assets\/css\//) - ? assetPath.replace(/\.css$/, `-${configId}.css`) - : assetPath; - - const url = paper.cdnify(path); - - let attrs = { rel: 'stylesheet' }; - - // check if there is any extra attribute - if (_.isObject(options.hash)) { - attrs = _.merge(attrs, options.hash); - } - - attrs = _.map(attrs, (value, key) => `${key}="${value}"`).join( ' '); - - return ``; - }); -} - -module.exports = helper; diff --git a/helpers/thirdParty.js b/helpers/thirdParty.js deleted file mode 100644 index 76ee210c..00000000 --- a/helpers/thirdParty.js +++ /dev/null @@ -1,149 +0,0 @@ -'use strict'; - -const _ = require('lodash'); -const helpers = require('handlebars-helpers'); -const whitelist = [ - { - name: 'array', - include: [ - 'after', - 'arrayify', - 'before', - 'eachIndex', - 'filter', - 'first', - 'forEach', - 'inArray', - 'isArray', - 'last', - 'lengthEqual', - 'map', - 'some', - 'sort', - 'sortBy', - 'withAfter', - 'withBefore', - 'withFirst', - 'withLast', - 'withSort', - ], - }, - { - name: 'collection', - include: ['isEmpty', 'iterate', 'length'], - }, - { - name: 'comparison', - include: [ - 'and', - 'gt', - 'gte', - 'has', - 'eq', - 'ifEven', - 'ifNth', - 'ifOdd', - 'is', - 'isnt', - 'lt', - 'lte', - 'neither', - 'unlessEq', - 'unlessGt', - 'unlessLt', - 'unlessGteq', - 'unlessLteq', - ], - }, - { - name: 'date', - include: ['moment'], - }, - { - name: 'html', - include: ['ellipsis', 'sanitize', 'ul', 'ol', 'thumbnailImage'] - }, - { - name: 'inflection', - include: ['inflect', 'ordinalize'], - }, - { - name: 'markdown', - include: ['markdown'], - }, - { - name: 'math', - include: ['add', 'subtract', 'divide', 'multiply', 'floor', 'ceil', 'round', 'sum', 'avg'], - }, - { - name: 'misc', - include: ['default', 'option', 'noop', 'withHash'], - }, - { - name: 'number', - include: [ - 'addCommas', - 'phoneNumber', - 'random', - 'toAbbr', - 'toExponential', - 'toFixed', - 'toFloat', - 'toInt', - 'toPrecision', - ], - }, - { - name: 'object', - include: [ - 'extend', - 'forIn', - 'forOwn', - 'toPath', - 'get', - 'getObject', - 'hasOwn', - 'isObject', - 'merge', - 'JSONparse', - 'JSONstringify', - ], - }, - { - name: 'string', - include: [ - 'camelcase', - 'capitalize', - 'capitalizeAll', - 'center', - 'chop', - 'dashcase', - 'dotcase', - 'hyphenate', - 'isString', - 'lowercase', - 'occurrences', - 'pascalcase', - 'pathcase', - 'plusify', - 'reverse', - 'sentence', - 'snakecase', - 'split', - 'startsWith', - 'titleize', - 'trim', - 'uppercase' - ], - }, - { - name: 'url', - include: ['encodeURI', 'decodeURI', 'urlResolve', 'urlParse', 'stripQuerystring', 'stripProtocol'], - }, -]; - -function helper(paper) { - whitelist.forEach(helper => paper.handlebars.registerHelper(_.pick(helpers[helper.name](), helper.include))); -} - -module.exports = helper; diff --git a/helpers/toLowerCase.js b/helpers/toLowerCase.js deleted file mode 100644 index c5e662c5..00000000 --- a/helpers/toLowerCase.js +++ /dev/null @@ -1,14 +0,0 @@ -'use strict'; - -function helper(paper) { - paper.handlebars.registerHelper('toLowerCase', function (string) { - - if (typeof string !== 'string') { - return string; - } - - return string.toLowerCase(); - }); -} - -module.exports = helper; diff --git a/helpers/truncate.js b/helpers/truncate.js deleted file mode 100644 index b60668cc..00000000 --- a/helpers/truncate.js +++ /dev/null @@ -1,38 +0,0 @@ -'use strict'; - -const substring = require('stringz').substring; - -/* 2017-02-14 - * - * FUNCTION - * truncate(str, length) - * - * DESCRIPTION (WHAT) - * Returns the first X characters in a string (unless it reaches the end - * of the string first, in which case it will return fewer). Returns a - * new string that is truncated to the given length. - * - * USE CASE (WHY) - * As a merchant, I want to display a card, at the bottom of - * my home page that highlights the most recent blog post. In the card, - * I want to display the blog thumbnail, title and the first 40 characters - * of the post's body. In order to extract the first 40 characters, I need - * a Handlebars helper that works like the javascript substring() function. - * - * USAGE - * - * {{lang (truncate 'blog.post.body.' 40) }} - */ -function helper(paper) { - paper.handlebars.registerHelper('truncate', function (string, length) { - if (typeof string !== 'string') { - return string; - } - - const truncatedString = substring(string, 0, length); - - return new paper.handlebars.SafeString(truncatedString); - }); -} - -module.exports = helper; diff --git a/index.js b/index.js index fffa21ab..2f10e207 100644 --- a/index.js +++ b/index.js @@ -1,20 +1,9 @@ 'use strict'; -var _ = require('lodash'); -var Translator = require('./lib/translator'); -var Path = require('path'); -var Fs = require('fs'); -var Handlebars = require('handlebars'); -var Async = require('async'); -var helpers = []; -var handlebarsOptions = { - preventIndent: true -}; - -// Load helpers (this only run once) -Fs.readdirSync(Path.join(__dirname, 'helpers')).forEach(function (file) { - helpers.push(require('./helpers/' + file)); -}); +const _ = require('lodash'); +const Translator = require('./lib/translator'); +const Async = require('async'); +const HandlebarsRenderer = require('@bigcommerce/stencil-paper-handlebars'); /** * processor is an optional function to apply during template assembly. The @@ -59,237 +48,172 @@ Fs.readdirSync(Path.join(__dirname, 'helpers')).forEach(function (file) { * @param {getTranslationsCallback} callback - Callback when Assembler.getTranslations is done. */ -/** -* Paper constructor. In addition to store settings and theme settings (configuration), -* paper expects to be passed an assembler to gather all the templates required to render -* the top level template. -* -* @param {Object} settings - Site settings -* @param {Object} themeSettings - Theme settings (configuration) -* @param {Object} assembler - Assembler with getTemplates and getTranslations methods. -* @param {assemblerGetTemplates} assembler.getTemplates - Method to assemble templates -* @param {assemblerGetTranslations} assembler.getTranslations - Method to assemble translations -*/ class Paper { - constructor(settings, themeSettings, assembler) { - this.handlebars = Handlebars.create(); - - this.handlebars.templates = {}; - this.translator = null; - this.inject = {}; - this.decorators = []; - - this.settings = settings || {}; + /** + * Paper constructor. In addition to store settings and theme settings (configuration), + * paper expects to be passed an assembler to gather all the templates required to render + * the top level template. + * + * @param {Object} siteSettings - Site settings + * @param {Object} themeSettings - Theme settings (configuration) + * @param {Object} assembler - Assembler with getTemplates and getTranslations methods. + * @param {String} rendererType - One of ['handlebars-v3', 'handlebars-v4'] + * @param {assemblerGetTemplates} assembler.getTemplates - Method to assemble templates + * @param {assemblerGetTranslations} assembler.getTranslations - Method to assemble translations + */ + constructor(siteSettings, themeSettings, assembler, rendererType) { + this.siteSettings = siteSettings || {}; this.themeSettings = themeSettings || {}; this.assembler = assembler || {}; - this.contentServiceContext = {}; - helpers.forEach(helper => helper(this)); - } + // Build renderer based on type + switch(rendererType) { + case 'handlebars-v4': + this.renderer = new HandlebarsRenderer(this.siteSettings, this.themeSettings, 'v4'); + break; + case 'handlebars-v3': + default: + this.renderer = new HandlebarsRenderer(this.siteSettings, this.themeSettings, 'v3'); + break; + } - /** - * Renders a string with the given context - * @param {String} string - * @param {Object} context - */ - renderString(string, context) { - return this.handlebars.compile(string)(context); + this.preProcessor = this.renderer.getPreProcessor(); } - loadTheme(paths, acceptLanguage, done) { + /** + * Use the assembler to fetch partials/templates, and translations, then load them + * into the renderer. + * + * @param {String|Array} paths A string or array of strings - the template path(s) to load. + * @param {String} acceptLanguage The accept-language header - used to select a locale. + * @param {Function} callback + */ + loadTheme(paths, acceptLanguage, callback) { if (!_.isArray(paths)) { paths = paths ? [paths] : []; } Async.parallel([ - (next) => { + next => { this.loadTranslations(acceptLanguage, next); }, - (next) => { + next => { Async.map(paths, this.loadTemplates.bind(this), next); } - ], done); + ], callback); } /** - * Load Partials/Templates - * @param {Object} templates - * @param {Function} callback + * Use the assembler to fetch partials/templates, then load them + * into the renderer. + * + * @param {String} path The root template path to load. All dependencies will be loaded as well. + * @param {Function} callback */ loadTemplates(path, callback) { - let processor = this.getTemplateProcessor(); - - this.assembler.getTemplates(path, processor, (error, templates) => { - if (error) { - return callback(error); + this.assembler.getTemplates(path, this.preProcessor, (err, templates) => { + if (err) { + return callback(err); } - _.each(templates, (precompiled, path) => { - var template; - if (!this.handlebars.templates[path]) { - eval('template = ' + precompiled); - this.handlebars.templates[path] = this.handlebars.template(template); - } - }); - - this.handlebars.partials = this.handlebars.templates; + try { + this.renderer.addTemplates(templates); + } catch(ex) { + return callback(ex); + } callback(); }); } - getTemplateProcessor() { - return (templates) => { - let precompiledTemplates = {}; - - _.each(templates,(content, path) => { - precompiledTemplates[path] = this.handlebars.precompile(content, handlebarsOptions); - }); - - return precompiledTemplates; - } - } - /** - * Load Partials/Templates used for test cases and stencil-cli - * @param {Object} templates - * @return {Object} + * Is the given template loaded? + * + * @param {String} path The path to the template file + * @return {Boolean} is the given template loaded? */ - loadTemplatesSync(templates) { - _.each(templates,(content, fileName) => { - this.handlebars.templates[fileName] = this.handlebars.compile(content, handlebarsOptions); - }); - - this.handlebars.partials = this.handlebars.templates; - - return this; - }; + isTemplateLoaded(path) { + return this.renderer.isTemplateLoaded(path); + } /** - * @param {String} acceptLanguage - * @param {Object} translations + * Load translation files and give a translator to renderer. + * + * @param {String} acceptLanguage The accept-language header, used to select a locale + * @param {Function} callback */ loadTranslations(acceptLanguage, callback) { - this.assembler.getTranslations((error, translations) => { - if (error) { - return callback(error); + // Ask assembler to fetch translations file + this.assembler.getTranslations((err, translations) => { + if (err) { + return callback(err); } - // Make translations available to the helpers - this.translator = Translator.create(acceptLanguage, translations); - + // Make translations available to renderer + const translator = Translator.create(acceptLanguage, translations); + this.renderer.setTranslator(translator); callback(); }); }; /** - * Add CDN base url to the relative path - * @param {String} path Relative path - * @return {String} Url cdn + * Add a decorator to wrap output during render(). + * + * @param {Function} decorator */ - cdnify(path) { - const cdnUrl = this.settings['cdn_url'] || ''; - const versionId = this.settings['theme_version_id']; - const sessionId = this.settings['theme_session_id']; - const protocolMatch = /(.*!?:)/; - - if (path instanceof Handlebars.SafeString) { - path = path.string; - } - - if (!path) { - return ''; - } - - if (/^(?:https?:)?\/\//.test(path)) { - return path; - } - - if (protocolMatch.test(path)) { - var match = path.match(protocolMatch); - path = path.slice(match[0].length, path.length); - - if (path[0] === '/') { - path = path.slice(1, path.length); - } - - if (match[0] === 'webdav:') { - return [cdnUrl, 'content', path].join('/'); - } - - if (this.themeSettings.cdn) { - var endpointKey = match[0].substr(0, match[0].length - 1); - if (this.themeSettings.cdn.hasOwnProperty(endpointKey)) { - if (cdnUrl) { - return [this.themeSettings.cdn[endpointKey], path].join('/'); - } - - return ['/assets/cdn', endpointKey, path].join('/'); - } - } - - if (path[0] !== '/') { - path = '/' + path; - } - - return path; - } - - if (path[0] !== '/') { - path = '/' + path; - } - - if (!versionId) { - return path; - } - - if (path.match(/^\/assets\//)) { - path = path.substr(8, path.length); - } - - if (sessionId) { - return [cdnUrl, 'stencil', versionId, 'e', sessionId, path].join('/'); - } + addDecorator(decorator) { + this.renderer.addDecorator(decorator); + }; - return [cdnUrl, 'stencil', versionId, path].join('/'); + /** + * Add content to be rendered in the given regions. + * + * @param {Object} Regions with widgets + */ + addContent(regions) { + this.renderer.addContent(regions); }; /** - * @param {Function} decorator + * Get page content. + * + * @return {Object} Regions with widgets */ - addDecorator(decorator) { - this.decorators.push(decorator); + getContent() { + return this.renderer.getContent(); }; /** + * Render a string with the given context. + * + * @param {String} string + * @param {Object} context + * @return {String} + * @throws [CompileError|RenderError] + */ + renderString(string, context) { + return this.renderer.renderString(string, context); + } + + /** + * Renders a template with the given context + * * @param {String} path * @param {Object} context * @return {String} + * @throws [TemplateNotFoundError|RenderError|DecoratorError] */ render(path, context) { - let output; - - context = context || {}; - context.template = path; - - if (this.translator) { - context.locale_name = this.translator.getLocale(); - } - - output = this.handlebars.templates[path](context); - - _.each(this.decorators, function (decorator) { - output = decorator(output); - }); - - return output; - }; + return this.renderer.render(path, context); + } /** * Theme rendering logic + * * @param {String|Array} templatePath * @param {Object} data * @return {String|Object} + * @throws [TemplateNotFoundError|RenderError|DecoratorError] */ renderTheme(templatePath, data) { let html; @@ -336,7 +260,6 @@ class Paper { return output; } - } module.exports = Paper; diff --git a/package.json b/package.json index c5b1940d..696a25e4 100644 --- a/package.json +++ b/package.json @@ -1,12 +1,14 @@ { "name": "@bigcommerce/stencil-paper", - "version": "2.0.7", - "description": "A stencil plugin to register partials and helpers from handlebars and returns the compiled version for the stencil platform.", + "version": "3.0.0-rc.1", + "description": "A Stencil plugin to load template files and render pages using backend renderer plugins.", "main": "index.js", "author": "Bigcommerce", "license": "BSD-4-Clause", "scripts": { - "test": "lab -l -t 90" + "linter": "eslint .", + "test": "npm run linter && lab -v -t 94 --ignore i18n,WebAssembly spec", + "coverage": "lab -c -r console -o stdout -r html -o coverage.html spec" }, "repository": { "type": "git", @@ -21,23 +23,16 @@ }, "homepage": "https://github.com/bigcommerce/paper", "dependencies": { - "accept-language-parser": "^1.0.2", - "async": "^1.4.2", - "handlebars": "^3.0.1", - "handlebars-helpers": "^0.8.0", - "lodash": "^3.6.0", - "messageformat": "^0.2.2", - "stringz": "^0.1.1", - "tarjan-graph": "^0.3.0" + "@bigcommerce/stencil-paper-handlebars": "~2.0.0", + "accept-language-parser": "~1.4.1", + "async": "~1.5.2", + "lodash": "~3.10.1", + "messageformat": "~0.2.2" }, "devDependencies": { - "code": "^1.4.0", - "eslint": "^2.7.0", - "eslint-config-airbnb": "^6.2.0", - "eslint-plugin-react": "^4.3.0", - "gulp": "^3.9.1", - "gulp-eslint": "^2.0.0", - "lab": "^5.8.1", - "sinon": "^1.17.2" + "code": "~4.0.0", + "eslint": "~4.10.0", + "lab": "~13.0.1", + "sinon": "~2.1.0" } } diff --git a/spec/index.js b/spec/index.js new file mode 100644 index 00000000..3034b98c --- /dev/null +++ b/spec/index.js @@ -0,0 +1,127 @@ +var Code = require('code'), + Lab = require('lab'), + Paper = require('../index'), + lab = exports.lab = Lab.script(), + describe = lab.experiment, + expect = Code.expect, + it = lab.it; + +describe('loadTheme()', function() { + var assembler = { + getTemplates: (path, processor, callback) => { + process.nextTick(() => { + callback(null, processor({ + 'pages/product': '', + 'pages/partial': '

', + 'pages/localeName': '{{locale_name}}', + })); + }); + }, + getTranslations: callback => { + process.nextTick(() => { + callback(null, { + 'en': { + hello: 'Hello {name}', + level1: { + level2: 'we are in the second level' + } + }, + 'fr': { + hello: 'Bonjour {name}', + level1: { + level2: 'nous sommes dans le deuxième niveau' + } + }, + 'fr-CA': { + hello: 'Salut {name}' + } + }); + }); + } + }; + + it('should use the assembler interface to load templates and translations', done => { + const paper = new Paper(null, null, assembler); + + paper.loadTheme('pages/product', 'fr-CA;q=0.8, fr, en', () => { + expect(paper.renderer.isTemplateLoaded('pages/product')).to.be.true(); + expect(paper.renderer.isTemplateLoaded('pages/partial')).to.be.true(); + expect(paper.renderer.getTranslator().translate('hello', {name: 'Mario'})).to.be.equal('Bonjour Mario'); + expect(paper.renderer.getTranslator().translate('hello', {name: 'Already Compiled'})).to.be.equal('Bonjour Already Compiled'); + expect(paper.renderer.getTranslator().translate('does_not_exist')).to.be.equal('does_not_exist'); + + done(); + }); + }); + + it('should get the localeName from the acceptLanguage header', done => { + const paper = new Paper(null, null, assembler); + + paper.loadTheme('pages/localeName', 'fr-CA;q=0.8, fr, en', () => { + expect(paper.renderer.getTranslator().getLocale()).to.be.equal('fr'); + + done(); + }); + }); + + it('should default to english if the locale is not supported', done => { + const paper = new Paper(null, null, assembler); + + paper.loadTheme('pages/localeName', 'es-VE, en', () => { + expect(paper.renderer.getTranslator().getLocale()).to.be.equal('en'); + + done(); + }); + }); + + it('should include the langName in the template context', done => { + const paper = new Paper(null, null, assembler); + + paper.loadTheme('pages/localeName', 'fr-CA, en', () => { + expect(paper.render('pages/localeName', {})).to.be.equal('fr-CA'); + + done(); + }); + }); +}); + +describe('render()', function() { + const assembler = { + getTemplates: (path, processor, callback) => { + process.nextTick(() => { + callback(null, processor({ + 'pages/product': '{{> pages/partial}}', + 'pages/partial': '

{{variable}}

', + 'pages/greet': '

{{lang \'good\'}} {{lang \'morning\'}}

', + 'pages/pre': '{{{pre object}}}', + })); + }); + }, + getTranslations: callback => { + process.nextTick(() => { + callback(null, {}); + }); + } + }; + + const context = { + variable: 'hello world', + object: {} + }; + + it('should render pages/product', function(done) { + const paper = new Paper(null, null, assembler); + paper.loadTheme('pages/product', '', () => { + expect(paper.render('pages/product', context)).to.be.equal('

hello world

'); + done(); + }); + }); + + it('should render pages/partial', function(done) { + const paper = new Paper(null, null, assembler); + paper.loadTheme('pages/product', '', () => { + expect(paper.render('pages/partial', context)).to.be.equal('

hello world

'); + done(); + }); + }); +}); diff --git a/test/lib/logger.spec.js b/spec/lib/logger.js similarity index 100% rename from test/lib/logger.spec.js rename to spec/lib/logger.js diff --git a/test/lib/translator.spec.js b/spec/lib/translator.js similarity index 97% rename from test/lib/translator.spec.js rename to spec/lib/translator.js index ee8d7126..8f8ea670 100644 --- a/test/lib/translator.spec.js +++ b/spec/lib/translator.js @@ -188,7 +188,7 @@ describe('Translator', () => { it('should return a translation object', done => { const translator = Translator.create('en', translations); - expect(translator.getLanguage()).to.deep.equal({ + expect(translator.getLanguage()).to.equal({ locale: 'en', locales: { 'level1.level2': 'en', @@ -212,7 +212,7 @@ describe('Translator', () => { it('should return a cascaded translation object', done => { const translator = Translator.create('fr-CA', translations); - expect(translator.getLanguage()).to.deep.equal({ + expect(translator.getLanguage()).to.equal({ locale: 'fr-CA', locales: { 'level1.level2': 'fr', @@ -236,7 +236,7 @@ describe('Translator', () => { it('should return a translation object filtered by key', done => { const translator = Translator.create('en', translations); - expect(translator.getLanguage('hello')).to.deep.equal({ + expect(translator.getLanguage('hello')).to.equal({ locale: 'en', locales: { hello: 'en', @@ -246,7 +246,7 @@ describe('Translator', () => { }, }); - expect(translator.getLanguage('level1')).to.deep.equal({ + expect(translator.getLanguage('level1')).to.equal({ locale: 'en', locales: { 'level1.level2': 'en', diff --git a/test/helpers/all.js b/test/helpers/all.js deleted file mode 100644 index f4881d4b..00000000 --- a/test/helpers/all.js +++ /dev/null @@ -1,103 +0,0 @@ -var Code = require('code'), - Lab = require('lab'), - Paper = require('../../index'), - lab = exports.lab = Lab.script(), - describe = lab.experiment, - expect = Code.expect, - it = lab.it; - -function c(template, context) { - return new Paper().loadTemplatesSync({template: template}).render('template', context); -} - -describe('all helper', function() { - var context = { - num1: 1, - num2: 2, - product: {a: 1, b: 2}, - string: 'yolo', - alwaysTrue: true, - alwaysFalse: false, - emptyArray: [], - emptyObject: {}, - itemArray: [1,2], - emptyString: "", - big: 'big' - }; - - it('(with single argument) should behave like if helper', function(done) { - expect(c('{{#all 1}}{{big}}{{/all}}', context)) - .to.be.equal('big'); - - expect(c('{{#all 1}}big{{/all}}', context)) - .to.be.equal('big'); - - expect(c('{{#all "x"}}big{{/all}}', context)) - .to.be.equal('big'); - - expect(c('{{#all ""}}big{{/all}}', context)) - .to.be.equal(''); - - expect(c('{{#all 0}}big{{/all}}', context)) - .to.be.equal(''); - - expect(c('{{#all ""}}big{{else}}small{{/all}}', context)) - .to.be.equal('small'); - - expect(c('{{#all 0}}big{{else}}small{{/all}}', context)) - .to.be.equal('small'); - - expect(c('{{#all num2}}big{{else}}small{{/all}}', context)) - .to.be.equal('big'); - - expect(c('{{#all product}}big{{else}}small{{/all}}', context)) - .to.be.equal('big'); - - expect(c('{{#all itemArray}}big{{else}}small{{/all}}', context)) - .to.be.equal('big'); - - expect(c('{{#all string}}big{{else}}small{{/all}}', context)) - .to.be.equal('big'); - - expect(c('{{#all emptyObject}}big{{else}}small{{/all}}', context)) - .to.be.equal('small'); - - done(); - }); - - it('should render "big" if all conditions truthy', function(done) { - - expect(c('{{#all "1" true}}big{{/all}}', context)) - .to.be.equal('big'); - - expect(c('{{#all 1 "1"}}big{{/all}}', context)) - .to.be.equal('big'); - - expect(c('{{#all "text" alwaysTrue}}big{{/all}}', context)) - .to.be.equal('big'); - - expect(c('{{#all alwaysTrue product num1 num2}}big{{/all}}', context)) - .to.be.equal('big'); - - expect(c('{{#all alwaysTrue itemArray string}}big{{/all}}', context)) - .to.be.equal('big'); - - done(); - }); - - it('should render empty if any condition is falsy', function(done) { - - expect(c('{{#all emptyString num1}}big{{/all}}', context)) - .to.be.equal(''); - - expect(c('{{#all true 0 emptyArray alwaysTrue}}big{{/all}}', context)) - .to.be.equal(''); - - expect(c('{{#all true "" alwaysTrue}}big{{/all}}', context)) - .to.be.equal(''); - - done(); - }); - - -}); diff --git a/test/helpers/any.js b/test/helpers/any.js deleted file mode 100644 index db0347b5..00000000 --- a/test/helpers/any.js +++ /dev/null @@ -1,98 +0,0 @@ -var Code = require('code'), - Lab = require('lab'), - Paper = require('../../index'), - lab = exports.lab = Lab.script(), - describe = lab.experiment, - expect = Code.expect, - it = lab.it; - -function c(template, context) { - return new Paper().loadTemplatesSync({template: template}).render('template', context); -} - -describe('any helper (with option hash)', function() { - var context = { - big: 'big', - arrayWithObjs: [{a: 1},{b: 1},{a: 2}] - }; - - it('should return "big" with matching predicate ', function(done) { - - expect(c('{{#any arrayWithObjs a=1}}{{big}}{{/any}}', context)) - .to.be.equal('big'); - - expect(c('{{#any arrayWithObjs a=2}}{{big}}{{/any}}', context)) - .to.be.equal('big'); - - expect(c('{{#any arrayWithObjs b=1}}{{big}}{{/any}}', context)) - .to.be.equal('big'); - - done(); - }); - - it('should return nothing without matching predicate ', function(done) { - - expect(c('{{#any arrayWithObjs b=2}}{{big}}{{/any}}', context)) - .to.be.equal(''); - - expect(c('{{#any arrayWithObjs c=1}}{{big}}{{/any}}', context)) - .to.be.equal(''); - - expect(c('{{#any arrayWithObjs num=2}}{{big}}{{/any}}', context)) - .to.be.equal(''); - - done(); - - }); - -}); - - -describe('any helper (with multiple arguments)', function() { - // DEPRECATED: Moved to #or helper - var context = { - num1: 1, - num2: 2, - product: {a: 1, b: 2}, - string: 'yolo', - alwaysTrue: true, - alwaysFalse: false, - emptyArray: [], - emptyObject: {}, - itemArray: [1,2], - big: 'big', - arrayWithObjs: [{a: 1},{b: 1},{a: 2}] - }; - - it('should return "big" if at least one arg valid', function(done) { - - expect(c('{{#any arrayWithObjs string}}{{big}}{{/any}}', context)) - .to.be.equal('big'); - - expect(c('{{#any "something test" itemArray}}{{big}}{{/any}}', context)) - .to.be.equal('big'); - - expect(c('{{#any "this is before the other test"}}{{big}}{{/any}}', context)) - .to.be.equal('big'); - - expect(c('{{#any alwaysFalse emptyArray string}}{{big}}{{/any}}', context)) - .to.be.equal('big'); - - done(); - - }); - - it('should return "" when no arguments are valid', function(done) { - - expect(c('{{#any emptyArray emptyObject alwaysFalse}}{{big}}{{/any}}', context)) - .to.be.equal(''); - - expect(c('{{#any "" false}}{{big}}{{/any}}', context)) - .to.be.equal(''); - - done(); - - }); - -}); - diff --git a/test/helpers/block.js b/test/helpers/block.js deleted file mode 100644 index 8c0599b4..00000000 --- a/test/helpers/block.js +++ /dev/null @@ -1,51 +0,0 @@ -var Code = require('code'); -var Lab = require('lab'); -var Paper = require('../../index'); -var lab = exports.lab = Lab.script(); -var describe = lab.experiment; -var expect = Code.expect; -var it = lab.it; - -function c(templates, context) { - return new Paper().loadTemplatesSync(templates).render('template', context); -} - -describe('partial and block helpers', function () { - it('should insert partial into the corresponding block', function (done) { - var templates = { - template: '{{#partial "page"}}

{{title}}

{{content}}

{{/partial}}{{> layout}}', - layout: '{{#block "page"}}{{/block}}/', - }, - context = { - title: 'Hello', - content: 'World', - }; - - expect(c(templates, context)) - .to.contain('

Hello

World

/'); - - done(); - }); - - it('should not trigger an error if partial name is empty', function (done) { - var templates = { - template: '{{#partial}}World{{/partial}}{{> layout}}', - layout: 'Hello{{#block "page"}}{{/block}}', - }; - - expect(c(templates, {})).to.be.equal('Hello'); - - done(); - }); - - it('should not trigger an error if block name is empty', function (done) { - var templates = { - template: '{{#partial "page" "2134"}}World{{/partial}}{{> layout}}', - layout: 'Hello{{#block}}{{/block}}', - }; - - expect(c(templates, {})).to.be.equal('Hello'); - - done(); - }); -}); diff --git a/test/helpers/cdn.js b/test/helpers/cdn.js deleted file mode 100644 index 2adaac38..00000000 --- a/test/helpers/cdn.js +++ /dev/null @@ -1,172 +0,0 @@ -var Code = require('code'), - Lab = require('lab'), - Paper = require('../../index'), - lab = exports.lab = Lab.script(), - describe = lab.experiment, - expect = Code.expect, - it = lab.it; - -function c(template, context, settings, themeSettings) { - return new Paper(settings, themeSettings).loadTemplatesSync({template: template}).render('template', context); -} - -describe('cdn helper', function () { - var context = {}; - var settings = { - cdn_url: 'https://cdn.bcapp/3dsf74g', - theme_version_id: '123', - }; - var themeSettings = { - cdn: { - customcdn1: 'https://bigcommerce.customcdn.net', - customcdn2: 'http://cdn.mystore.com' - } - }; - - it('should render the css cdn url', function (done) { - expect(c('{{cdn "assets/css/style.css"}}', context, settings, themeSettings)) - .to.be.equal('https://cdn.bcapp/3dsf74g/stencil/123/css/style.css'); - - expect(c('{{cdn "/assets/css/style.css"}}', context, settings, themeSettings)) - .to.be.equal('https://cdn.bcapp/3dsf74g/stencil/123/css/style.css'); - - done(); - }); - - it('should render normal assets cdn url', function (done) { - expect(c('{{cdn "assets/js/app.js"}}', context, settings, themeSettings)) - .to.be.equal('https://cdn.bcapp/3dsf74g/stencil/123/js/app.js'); - - expect(c('{{cdn "assets/img/image.jpg"}}', context, settings, themeSettings)) - .to.be.equal('https://cdn.bcapp/3dsf74g/stencil/123/img/image.jpg'); - - done(); - }); - - it('should not use the cdn url', function (done) { - expect(c('{{cdn "assets/img/image.jpg"}}', context, {})) - .to.be.equal('/assets/img/image.jpg'); - - expect(c('{{cdn "assets/img/image.jpg"}}', context, {})) - .to.be.equal('/assets/img/image.jpg'); - - done(); - }); - - it('should return the same value if it is a full url', function (done) { - - expect(c('{{cdn "https://example.com/app.js"}}', context, settings, themeSettings)) - .to.be.equal('https://example.com/app.js'); - - expect(c('{{cdn "http://example.com/app.js"}}', context, settings, themeSettings)) - .to.be.equal('http://example.com/app.js'); - - expect(c('{{cdn "//example.com/app.js"}}', context, settings, themeSettings)) - .to.be.equal('//example.com/app.js'); - - done(); - }); - - it('should return an empty string if no path is provided', function (done) { - - expect(c('{{cdn ""}}', context, settings, themeSettings)) - .to.be.equal(''); - - done(); - }); - - it('should return a webDav asset if webdav protocol specified', function (done) { - - expect(c('{{cdn "webdav:img/image.jpg"}}', context, settings, themeSettings)) - .to.be.equal('https://cdn.bcapp/3dsf74g/content/img/image.jpg'); - - expect(c('{{cdn "webdav:/img/image.jpg"}}', context, settings, themeSettings)) - .to.be.equal('https://cdn.bcapp/3dsf74g/content/img/image.jpg'); - - done(); - }); - - it('should not return a webDav asset if webdav protocol is not correct', function (done) { - - expect(c('{{cdn "webbav:img/image.jpg"}}', context, settings, themeSettings)) - .to.be.equal('/img/image.jpg'); - - expect(c('{{cdn "webbav:/img/image.jpg"}}', context, settings, themeSettings)) - .to.be.equal('/img/image.jpg'); - - done(); - }); - - it('should return a custom CDN asset if protocol is configured', function (done) { - - expect(c('{{cdn "customcdn1:img/image.jpg"}}', context, settings, themeSettings)) - .to.be.equal('https://bigcommerce.customcdn.net/img/image.jpg'); - - expect(c('{{cdn "customcdn1:/img/image.jpg"}}', context, settings, themeSettings)) - .to.be.equal('https://bigcommerce.customcdn.net/img/image.jpg'); - - expect(c('{{cdn "customcdn2:img/image.jpg"}}', context, settings, themeSettings)) - .to.be.equal('http://cdn.mystore.com/img/image.jpg'); - - expect(c('{{cdn "customcdn2:/img/image.jpg"}}', context, settings, themeSettings)) - .to.be.equal('http://cdn.mystore.com/img/image.jpg'); - - done(); - }); - - it('should return a custom CDN asset when using nested helper', function (done) { - - expect(c('{{cdn (concat "customcdn1:" "img/image.jpg")}}', context, settings, themeSettings)) - .to.be.equal('https://bigcommerce.customcdn.net/img/image.jpg'); - - expect(c('{{cdn (concat "customcdn1:" "/img/image.jpg")}}', context, settings, themeSettings)) - .to.be.equal('https://bigcommerce.customcdn.net/img/image.jpg'); - - expect(c('{{cdn (concat "customcdn2:" "img/image.jpg")}}', context, settings, themeSettings)) - .to.be.equal('http://cdn.mystore.com/img/image.jpg'); - - expect(c('{{cdn (concat "customcdn2:" "/img/image.jpg")}}', context, settings, themeSettings)) - .to.be.equal('http://cdn.mystore.com/img/image.jpg'); - - done(); - }); - - it('should return a local CDN asset if no cdn url is configured', function (done) { - - expect(c('{{cdn "customcdn1:img/image.jpg"}}', context, {}, themeSettings)) - .to.be.equal('/assets/cdn/customcdn1/img/image.jpg'); - - expect(c('{{cdn "customcdn1:/img/image.jpg"}}', context, {}, themeSettings)) - .to.be.equal('/assets/cdn/customcdn1/img/image.jpg'); - - expect(c('{{cdn "customcdn2:img/image.jpg"}}', context, {}, themeSettings)) - .to.be.equal('/assets/cdn/customcdn2/img/image.jpg'); - - expect(c('{{cdn "customcdn2:/img/image.jpg"}}', context, {}, themeSettings)) - .to.be.equal('/assets/cdn/customcdn2/img/image.jpg'); - - done(); - }); - - it('should not return a custom CDN asset if protocol is not configured', function (done) { - - expect(c('{{cdn "customcdn1:img/image.jpg"}}', context, settings, {})) - .to.be.equal('/img/image.jpg'); - - expect(c('{{cdn "customcdn1:/img/image.jpg"}}', context, settings, {})) - .to.be.equal('/img/image.jpg'); - - done(); - }); - - it('should return basic asset URL if protocol is not correct', function (done) { - - expect(c('{{cdn "randomProtocol::img/image.jpg"}}', context, settings, themeSettings)) - .to.be.equal('/img/image.jpg'); - - expect(c('{{cdn "randomProtocol:/img/image.jpg"}}', context, settings, themeSettings)) - .to.be.equal('/img/image.jpg'); - - done(); - }); -}); diff --git a/test/helpers/compare.js b/test/helpers/compare.js deleted file mode 100644 index 804a1696..00000000 --- a/test/helpers/compare.js +++ /dev/null @@ -1,91 +0,0 @@ -var Code = require('code'), - Lab = require('lab'), - Paper = require('../../index'), - lab = exports.lab = Lab.script(), - describe = lab.experiment, - expect = Code.expect, - it = lab.it; - -function c(template, context) { - return new Paper().loadTemplatesSync({template: template}).render('template', context); -} - -describe('compare helper', function() { - var context = { - num1: 1, - num2: 2, - product: {a: 1, b: 2}, - string: 'yolo' - }; - - it('should render "big" if all compares match', function(done) { - - expect(c('{{#compare "1" num1 operator="=="}}big{{/compare}}', context)) - .to.be.equal('big'); - - expect(c('{{#compare 1 num1 operator="==="}}big{{/compare}}', context)) - .to.be.equal('big'); - - expect(c('{{#compare 2 num1 operator="!=="}}big{{/compare}}', context)) - .to.be.equal('big'); - - expect(c('{{#compare num2 num1 operator="!="}}big{{/compare}}', context)) - .to.be.equal('big'); - - expect(c('{{#compare num2 num1 operator=">"}}big{{/compare}}', context)) - .to.be.equal('big'); - - expect(c('{{#compare num1 num2 operator="<"}}big{{/compare}}', context)) - .to.be.equal('big'); - - expect(c('{{#compare num2 num1 operator=">="}}big{{/compare}}', context)) - .to.be.equal('big'); - - expect(c('{{#compare num1 num2 operator="<="}}big{{/compare}}', context)) - .to.be.equal('big'); - - expect(c('{{#compare product "object" operator="typeof"}}big{{/compare}}', context)) - .to.be.equal('big'); - - expect(c('{{#compare string "string" operator="typeof"}}big{{/compare}}', context)) - .to.be.equal('big'); - - done(); - }); - - it('should render empty for all cases', function(done) { - - expect(c('{{#compare "2" num1 operator="=="}}big{{/compare}}', context)) - .to.be.equal(''); - - expect(c('{{#compare 2 num1 operator="==="}}big{{/compare}}', context)) - .to.be.equal(''); - - expect(c('{{#compare 1 num1 operator="!=="}}big{{/compare}}', context)) - .to.be.equal(''); - - expect(c('{{#compare num2 2 operator="!="}}big{{/compare}}', context)) - .to.be.equal(''); - - expect(c('{{#compare num1 20 operator=">"}}big{{/compare}}', context)) - .to.be.equal(''); - - expect(c('{{#compare 4 num2 operator="<"}}big{{/compare}}', context)) - .to.be.equal(''); - - expect(c('{{#compare num1 40 operator=">="}}big{{/compare}}', context)) - .to.be.equal(''); - - expect(c('{{#compare num2 num1 operator="<="}}big{{/compare}}', context)) - .to.be.equal(''); - - expect(c('{{#compare product "string" operator="typeof"}}big{{/compare}}', context)) - .to.be.equal(''); - - expect(c('{{#compare string "object" operator="typeof"}}big{{/compare}}', context)) - .to.be.equal(''); - - done(); - }); - -}); diff --git a/test/helpers/concat.js b/test/helpers/concat.js deleted file mode 100644 index 43168cc8..00000000 --- a/test/helpers/concat.js +++ /dev/null @@ -1,23 +0,0 @@ -var Code = require('code'), - Lab = require('lab'), - Paper = require('../../index'), - lab = exports.lab = Lab.script(), - describe = lab.experiment, - expect = Code.expect, - it = lab.it; - -function c(template, context) { - return new Paper().loadTemplatesSync({template: template}).render('template', context); -} - -describe('concat helper', function() { - - it('should concatanate two strings', function(done) { - - expect(c('{{concat var1 var2}}', {var1: 'hello', var2: 'world'})) - .to.be.equal('helloworld'); - - done(); - }); -}); - diff --git a/test/helpers/dynamicComponent.js b/test/helpers/dynamicComponent.js deleted file mode 100644 index a5b31924..00000000 --- a/test/helpers/dynamicComponent.js +++ /dev/null @@ -1,37 +0,0 @@ -var Code = require('code'), - Lab = require('lab'), - Paper = require('../../index'), - lab = exports.lab = Lab.script(), - describe = lab.experiment, - expect = Code.expect, - it = lab.it; - -function c(template, templates, context) { - templates.template = template; - return new Paper().loadTemplatesSync(templates).render('template', context); -} - -describe('dynamicComponent helper', function() { - var templates = { - 'component/fields/one': '1{{animal}}', - 'component/fields/two': '2{{animal}}', - 'component/fields/three': '3{{animal}}', - }, - context = { - fields: [ - {partial: 'one', animal: 'dog'}, - {partial: 'two', animal: 'cat'}, - {partial: 'three', animal: 'mouse'}, - ], - field: {partial: 'two', animal: 'elephant'} - }; - - it('should render all dynamic templates accordingly', function(done) { - - expect(c('{{#each fields}}{{dynamicComponent "component/fields"}} {{/each}}', templates, context)) - .to.be.equal('1dog 2cat 3mouse '); - - done(); - }); -}); - diff --git a/test/helpers/equals.js b/test/helpers/equals.js deleted file mode 100644 index d808fc4a..00000000 --- a/test/helpers/equals.js +++ /dev/null @@ -1,39 +0,0 @@ -var Code = require('code'), - Lab = require('lab'), - Paper = require('../../index'), - lab = exports.lab = Lab.script(), - describe = lab.experiment, - expect = Code.expect, - it = lab.it; - -function c(template, context) { - return new Paper().loadTemplatesSync({template: template}).render('template', context); -} - -describe('equals helper', function() { - var context = { - value: 5 - }; - - it('should render yes if the value is equal to 5', function(done) { - - expect(c('{{#equals 5 value}}yes{{/equals}}', context)) - .to.be.equal('yes'); - - expect(c('{{#equals value 5}}yes{{/equals}}', context)) - .to.be.equal('yes'); - - done(); - }); - - it('should render empty string', function(done) { - - expect(c('{{#equals 6 value}}yes{{/equals}}', context)) - .to.be.equal(''); - - expect(c('{{#equals value 6}}yes{{/equals}}', context)) - .to.be.equal(''); - - done(); - }); -}); diff --git a/test/helpers/for.js b/test/helpers/for.js deleted file mode 100644 index f58c49ee..00000000 --- a/test/helpers/for.js +++ /dev/null @@ -1,93 +0,0 @@ -var Code = require('code'), - Lab = require('lab'), - Paper = require('../../index'), - lab = exports.lab = Lab.script(), - describe = lab.experiment, - expect = Code.expect, - it = lab.it; - -function c(template, context) { - return new Paper().loadTemplatesSync({template: template}).render('template', context); -} - -describe('for helper', function() { - - var context = {name: 'Joe'}; - - it('should itarate 10 times', function(done) { - - expect(c('{{#for 10 this}}{{$index}}:{{name}} {{/for}}', context)) - .to.contain('1:Joe 2:Joe 3:Joe 4:Joe 5:Joe 6:Joe 7:Joe 8:Joe 9:Joe 10:Joe'); - - expect(c('{{#for 1 10 this}}{{$index}}:{{name}} {{/for}}', context)) - .to.contain('1:Joe 2:Joe 3:Joe 4:Joe 5:Joe 6:Joe 7:Joe 8:Joe 9:Joe 10:Joe'); - - expect(c('{{#for 0 9 this}}{{$index}}:{{name}} {{/for}}', context)) - .to.contain('0:Joe 1:Joe 2:Joe 3:Joe 4:Joe 5:Joe 6:Joe 7:Joe 8:Joe 9:Joe'); - - expect(c('{{#for 1000 1010 this}}{{$index}}:{{name}} {{/for}}', context)) - .to.contain('1000:Joe 1001:Joe 1002:Joe 1003:Joe 1004:Joe 1005:Joe 1006:Joe 1007:Joe 1008:Joe 1009:Joe'); - - done(); - }); - - it('should not itarate more than 100 times', function(done) { - - expect(c('{{#for 0 99}}x{{/for}}', context).length) - .to.be.equal(100); - - expect(c('{{#for 1 100}}x{{/for}}', context).length) - .to.be.equal(100); - - expect(c('{{#for 0 3000 this}}.{{/for}}', context).length) - .to.be.equal(100); - - expect(c('{{#for 2015 3000}}.{{/for}}', context).length) - .to.be.equal(100); - - done(); - }); - - it('should render w/o context', function(done) { - - expect(c('{{#for 10}}{{$index}} {{/for}}', context)) - .to.be.equal('1 2 3 4 5 6 7 8 9 10 '); - - expect(c('{{#for 1 10}}{{$index}} {{/for}}', context)) - .to.be.equal('1 2 3 4 5 6 7 8 9 10 '); - - expect(c('{{#for 0 9}}{{$index}} {{/for}}', context)) - .to.be.equal('0 1 2 3 4 5 6 7 8 9 '); - - expect(c('{{#for 0 20}}.{{/for}}', context)) - .to.be.equal('.....................'); - - expect(c('{{#for 0 99}}{{/for}}', context)) - .to.be.equal(''); - - done(); - }); - - - it('should convert strings to integers and iterate 10 times', function(done) { - var context = { - start: '1', - end: '10' - } - - expect(c('{{#for start end}}{{$index}} {{/for}}', context)) - .to.be.equal('1 2 3 4 5 6 7 8 9 10 '); - done(); - }); - - it('should not iterate if "from" is less than "to"', function(done) { - var context = { - start: 10, - end: 1 - } - - expect(c('{{#for start end}}{{$index}} {{/for}}', context)) - .to.be.equal(''); - done(); - }); -}); diff --git a/test/helpers/getFontsCollection.js b/test/helpers/getFontsCollection.js deleted file mode 100644 index 90a88997..00000000 --- a/test/helpers/getFontsCollection.js +++ /dev/null @@ -1,48 +0,0 @@ -var Code = require('code'), - Lab = require('lab'), - Paper = require('../../index'), - lab = exports.lab = Lab.script(), - describe = lab.experiment, - expect = Code.expect, - it = lab.it; - -function c(template, themeSettings) { - return new Paper({}, themeSettings).loadTemplatesSync({template: template}).render('template', {}); -} - -describe('fonts helper', function () { - it('should return the expected font url', function (done) { - var themeSettings = { - 'test1-font': 'Google_Open+Sans', - 'test2-font': 'Google_Open+Sans_400italic', - 'test3-font': 'Google_Open+Sans_700', - 'test4-font': 'Google_Karla_700', - 'test5-font': 'Google_Lora_400_sans', - 'test6-font': 'Google_Volkron', - 'test7-font': 'Google_Droid_400,700', - 'test8-font': 'Google_Crimson+Text_400,700_sans', - 'random-property': 'not a font' - }; - - var template = "{{getFontsCollection}}"; - - expect(c(template, themeSettings)) - .to.be.equal(''); - done(); - }); - - it('should not crash if a malformed Google font is passed', function (done) { - var themeSettings = { - 'test1-font': 'Google_Open+Sans', - 'test2-font': 'Google_', - 'test3-font': 'Google' - }; - - var template = "{{getFontsCollection}}"; - - expect(c(template, themeSettings)) - .to.be.equal(''); - done(); - }); -}); diff --git a/test/helpers/getImage.js b/test/helpers/getImage.js deleted file mode 100644 index b59e8909..00000000 --- a/test/helpers/getImage.js +++ /dev/null @@ -1,110 +0,0 @@ -var Code = require('code'), - Lab = require('lab'), - Paper = require('../../index'), - lab = exports.lab = Lab.script(), - describe = lab.experiment, - expect = Code.expect, - it = lab.it; - -function c(template, context) { - var themeSettings = { - logo_image: '600x300', - gallery: '100x100', - _images: { - logo: { - width: 250, - height: 100 - }, - gallery: { - width: 300, - height: 300 - }, - missing_values: {}, - missing_width: {height: 100} - } - }; - return new Paper({}, themeSettings).loadTemplatesSync({template: template}).render('template', context); -} - -describe('getImage helper', function() { - var urlData = 'https://cdn.example.com/path/to/{:size}/image.png'; - var context = { - image_url: 'http://example.com/image.png', - not_an_image: null, - image: { - data: urlData - }, - logoPreset: 'logo', - }; - - it('should return a url if a url is passed', function(done) { - expect(c('{{getImage "http://example.com/image.jpg"}}', context)) - .to.be.equal('http://example.com/image.jpg'); - - expect(c('{{getImage "https://example.com/image.jpg"}}', context)) - .to.be.equal('https://example.com/image.jpg'); - - done(); - }); - - it('should return empty if image is invalid', function(done) { - - expect(c('{{getImage not_existing_image}}', context)) - .to.be.equal(''); - - expect(c('{{getImage not_an_image}}', context)) - .to.be.equal(''); - - done(); - }); - - it('should use the preset from _images', function(done) { - - expect(c('{{getImage image "logo"}}', context)) - .to.be.equal(urlData.replace('{:size}', '250x100')); - - expect(c('{{getImage image "gallery"}}', context)) - .to.be.equal(urlData.replace('{:size}', '300x300')); - - done(); - }); - - it('should use the size from the theme_settings', function(done) { - - expect(c('{{getImage image "logo_image"}}', context)) - .to.be.equal(urlData.replace('{:size}', '600x300')); - - done(); - }); - - it('should use the default image url if image is invalid', function(done) { - - expect(c('{{getImage not_an_image "logo" "http://image"}}', context)) - .to.be.equal('http://image'); - - expect(c('{{getImage not_an_image "logo" image_url}}', context)) - .to.be.equal(context.image_url); - - done(); - }); - - it('should use original size if not default is passed', function(done) { - - expect(c('{{getImage image "bad_preset"}}', context)) - .to.be.equal(urlData.replace('{:size}', 'original')); - - done(); - }); - - - it('should default to max value (width & height) if value is not provided', function(done) { - - expect(c('{{getImage image "missing_values"}}', context)) - .to.be.equal(urlData.replace('{:size}', '4096x4096')); - - expect(c('{{getImage image "missing_width"}}', context)) - .to.be.equal(urlData.replace('{:size}', '4096x100')); - - done(); - }); -}); diff --git a/test/helpers/getShortMonth.js b/test/helpers/getShortMonth.js deleted file mode 100644 index a2cfc005..00000000 --- a/test/helpers/getShortMonth.js +++ /dev/null @@ -1,32 +0,0 @@ -var Code = require('code'), - Lab = require('lab'), - Paper = require('../../index'), - lab = exports.lab = Lab.script(), - describe = lab.experiment, - expect = Code.expect, - it = lab.it; - -function c(template) { - return new Paper().loadTemplatesSync({template: template}).render('template'); -} - -describe('getShortMonth helper', function() { - - it('should render an object properly', function(done) { - - expect(c('{{getShortMonth 1}}')) .to.be.equal('Jan'); - expect(c('{{getShortMonth 2}}')) .to.be.equal('Feb'); - expect(c('{{getShortMonth 3}}')) .to.be.equal('Mar'); - expect(c('{{getShortMonth 4}}')) .to.be.equal('Apr'); - expect(c('{{getShortMonth 5}}')) .to.be.equal('May'); - expect(c('{{getShortMonth 6}}')) .to.be.equal('Jun'); - expect(c('{{getShortMonth 7}}')) .to.be.equal('Jul'); - expect(c('{{getShortMonth 8}}')) .to.be.equal('Aug'); - expect(c('{{getShortMonth 9}}')) .to.be.equal('Sep'); - expect(c('{{getShortMonth 10}}')) .to.be.equal('Oct'); - expect(c('{{getShortMonth 11}}')) .to.be.equal('Nov'); - expect(c('{{getShortMonth 12}}')) .to.be.equal('Dec'); - - done(); - }); -}); diff --git a/test/helpers/helperMissing.js b/test/helpers/helperMissing.js deleted file mode 100644 index 19f269ad..00000000 --- a/test/helpers/helperMissing.js +++ /dev/null @@ -1,22 +0,0 @@ -var Code = require('code'), - Lab = require('lab'), - Paper = require('../../index'), - lab = exports.lab = Lab.script(), - describe = lab.experiment, - expect = Code.expect, - it = lab.it; - -function c(template, context) { - return new Paper().loadTemplatesSync({template: template}).render('template', context); -} - -describe('helperMissing', function() { - - it('should return empty string if the helper is missing', function(done) { - - expect(c('{{thisHelperDoesNotExist}}', {})) - .to.be.equal(''); - - done(); - }); -}); diff --git a/test/helpers/if.js b/test/helpers/if.js deleted file mode 100644 index 98cc039c..00000000 --- a/test/helpers/if.js +++ /dev/null @@ -1,290 +0,0 @@ -var Code = require('code'), - Lab = require('lab'), - Paper = require('../../index'), - lab = exports.lab = Lab.script(), - describe = lab.experiment, - expect = Code.expect, - it = lab.it; - -function c(template, context) { - return new Paper().loadTemplatesSync({template: template}).render('template', context); -} - -describe('if helper', () => { - var context = { - num1: 1, - num2: 2, - product: {a: 1, b: 2}, - string: 'yolo', - alwaysTrue: true, - alwaysFalse: true, - big: 'big' - }; - - it('should have the same behavior as the original if helper', done => { - expect(c('{{#if 1}}{{big}}{{/if}}', context)) - .to.be.equal('big'); - - expect(c('{{#if 1}}big{{/if}}', context)) - .to.be.equal('big'); - - expect(c('{{#if "x"}}big{{/if}}', context)) - .to.be.equal('big'); - - expect(c('{{#if ""}}big{{/if}}', context)) - .to.be.equal(''); - - expect(c('{{#if 0}}big{{/if}}', context)) - .to.be.equal(''); - - expect(c('{{#if ""}}big{{else}}small{{/if}}', context)) - .to.be.equal('small'); - - expect(c('{{#if 0}}big{{else}}small{{/if}}', context)) - .to.be.equal('small'); - - expect(c('{{#if num2}}big{{else}}small{{/if}}', context)) - .to.be.equal('big'); - - expect(c('{{#if product}}big{{else}}small{{/if}}', context)) - .to.be.equal('big'); - - expect(c('{{#if string}}big{{else}}small{{/if}}', context)) - .to.be.equal('big'); - - done(); - }); - - it('should render "big" if all conditions match', done => { - - expect(c('{{#if "1" "==" num1}}big{{/if}}', context)) - .to.be.equal('big'); - - expect(c('{{#if 1 "===" num1}}big{{/if}}', context)) - .to.be.equal('big'); - - expect(c('{{#if 2 "!==" num1}}big{{/if}}', context)) - .to.be.equal('big'); - - expect(c('{{#if num2 "!=" num1}}big{{/if}}', context)) - .to.be.equal('big'); - - expect(c('{{#if num2 ">" num1}}big{{/if}}', context)) - .to.be.equal('big'); - - expect(c('{{#if num1 "<" num2}}big{{/if}}', context)) - .to.be.equal('big'); - - expect(c('{{#if num2 ">=" num1}}big{{/if}}', context)) - .to.be.equal('big'); - - expect(c('{{#if num1 "<=" num2}}big{{/if}}', context)) - .to.be.equal('big'); - - expect(c('{{#if product "typeof" "object"}}big{{/if}}', context)) - .to.be.equal('big'); - - expect(c('{{#if string "typeof" "string"}}big{{/if}}', context)) - .to.be.equal('big'); - - done(); - }); - - it('should render empty for all cases', done => { - - expect(c('{{#if "2" "==" num1}}big{{/if}}', context)) - .to.be.equal(''); - - expect(c('{{#if 2 "===" num1}}big{{/if}}', context)) - .to.be.equal(''); - - expect(c('{{#if 1 "!==" num1}}big{{/if}}', context)) - .to.be.equal(''); - - expect(c('{{#if num2 "!=" 2}}big{{/if}}', context)) - .to.be.equal(''); - - expect(c('{{#if num1 ">" 20}}big{{/if}}', context)) - .to.be.equal(''); - - expect(c('{{#if 4 "<" num2}}big{{/if}}', context)) - .to.be.equal(''); - - expect(c('{{#if num1 ">=" 40}}big{{/if}}', context)) - .to.be.equal(''); - - expect(c('{{#if num2 "<=" num1}}big{{/if}}', context)) - .to.be.equal(''); - - expect(c('{{#if product "typeof" "string"}}big{{/if}}', context)) - .to.be.equal(''); - - expect(c('{{#if string "typeof" "object"}}big{{/if}}', context)) - .to.be.equal(''); - - done(); - }); - - - it('should render "big" if all ifs match', done => { - - var context = { - num1: 1, - num2: 2, - product: {a: 1, b: 2}, - string: 'yolo' - }; - - expect(c('{{#if "1" num1 operator="=="}}big{{/if}}', context)) - .to.be.equal('big'); - - expect(c('{{#if 1 num1 operator="==="}}big{{/if}}', context)) - .to.be.equal('big'); - - expect(c('{{#if 2 num1 operator="!=="}}big{{/if}}', context)) - .to.be.equal('big'); - - expect(c('{{#if num2 num1 operator="!="}}big{{/if}}', context)) - .to.be.equal('big'); - - expect(c('{{#if num2 num1 operator=">"}}big{{/if}}', context)) - .to.be.equal('big'); - - expect(c('{{#if num1 num2 operator="<"}}big{{/if}}', context)) - .to.be.equal('big'); - - expect(c('{{#if num2 num1 operator=">="}}big{{/if}}', context)) - .to.be.equal('big'); - - expect(c('{{#if num1 num2 operator="<="}}big{{/if}}', context)) - .to.be.equal('big'); - - expect(c('{{#if product "object" operator="typeof"}}big{{/if}}', context)) - .to.be.equal('big'); - - expect(c('{{#if string "string" operator="typeof"}}big{{/if}}', context)) - .to.be.equal('big'); - - done(); - }); - - it('should render empty for all cases', done => { - const context = { - num1: 1, - num2: 2, - product: {a: 1, b: 2}, - string: 'yolo', - emptyArray: [], - emptyObject: {} - }; - - expect(c('{{#if emptyObject}}big{{/if}}', context)) - .to.be.equal(''); - - expect(c('{{#if emptyArray}}big{{/if}}', context)) - .to.be.equal(''); - - expect(c('{{#if emptyArray.length}}big{{/if}}', context)) - .to.be.equal(''); - - expect(c('{{#if "2" num1 operator="=="}}big{{/if}}', context)) - .to.be.equal(''); - - expect(c('{{#if 2 num1 operator="==="}}big{{/if}}', context)) - .to.be.equal(''); - - expect(c('{{#if 1 num1 operator="!=="}}big{{/if}}', context)) - .to.be.equal(''); - - expect(c('{{#if num2 2 operator="!="}}big{{/if}}', context)) - .to.be.equal(''); - - expect(c('{{#if num1 20 operator=">"}}big{{/if}}', context)) - .to.be.equal(''); - - expect(c('{{#if 4 num2 operator="<"}}big{{/if}}', context)) - .to.be.equal(''); - - expect(c('{{#if num1 40 operator=">="}}big{{/if}}', context)) - .to.be.equal(''); - - expect(c('{{#if num2 num1 operator="<="}}big{{/if}}', context)) - .to.be.equal(''); - - expect(c('{{#if product "string" operator="typeof"}}big{{/if}}', context)) - .to.be.equal(''); - - expect(c('{{#if string "object" operator="typeof"}}big{{/if}}', context)) - .to.be.equal(''); - - done(); - }); - - it('should work as a non-block helper when used as a subexpression', done => { - expect(c('{{#if (if num1 "!==" num2)}}{{big}}{{/if}}', context)) - .to.be.equal('big'); - - expect(c('{{#all (if num1 "!==" num2) "1" true}}big{{/all}}', context)) - .to.be.equal('big'); - - done(); - }); -}); - -describe('unless helper', () => { - const context = { - num1: 1, - num2: 2, - product: {a: 1, b: 2}, - alwaysTrue: true, - alwaysFalse: false, - notEmptyArray: [ 1, 2 , 3 ], - emptyArray: [], - }; - - it('should print hello', done => { - expect(c('{{#unless num1 "===" num2}}hello{{/unless}}', context)) - .to.be.equal('hello'); - - expect(c('{{#unless alwaysFalse}}hello{{/unless}}', context)) - .to.be.equal('hello'); - - expect(c('{{#unless does_not_exist}}hello{{/unless}}', context)) - .to.be.equal('hello'); - - done(); - }); - - it('should print empty', done => { - expect(c('{{#unless num1 "===" num1}}hello{{/unless}}', context)) - .to.be.equal(''); - - expect(c('{{#unless alwaysTrue}}hello{{/unless}}', context)) - .to.be.equal(''); - - expect(c('{{#unless product}}hello{{/unless}}', context)) - .to.be.equal(''); - - done(); - }); - - it('should work with arrays', done => { - expect(c('{{#unless emptyArray}}foo{{else}}bar{{/unless}}', context)) - .to.be.equal('foo'); - - expect(c('{{#unless notEmptyArray}}foo{{else}}bar{{/unless}}', context)) - .to.be.equal('bar'); - - done(); - }); - - it('should work as a non-block helper when used as a subexpression', done => { - expect(c('{{#if (unless num1 "===" num2)}}big{{/if}}', context)) - .to.be.equal('big'); - - expect(c('{{#all (unless num1 "===" num2) "1" true}}big{{/all}}', context)) - .to.be.equal('big'); - - done(); - }); -}) diff --git a/test/helpers/inject.js b/test/helpers/inject.js deleted file mode 100644 index 3917f531..00000000 --- a/test/helpers/inject.js +++ /dev/null @@ -1,27 +0,0 @@ -var Code = require('code'), - Lab = require('lab'), - Paper = require('../../index'), - lab = exports.lab = Lab.script(), - describe = lab.experiment, - expect = Code.expect, - it = lab.it; - -function c(template, context) { - return new Paper().loadTemplatesSync({template: template}).render('template', context); -} - -describe('inject helper', function() { - var context = { - value1: "Big", - value2: "Commerce", - }; - - it('should inject variables', function(done) { - var template = "{{inject 'data1' value1}}{{inject 'data2' value2}}{{jsContext}}"; - - expect(c(template, context)) - .to.be.equal('"{\\"data1\\":\\"Big\\",\\"data2\\":\\"Commerce\\"}"'); - - done(); - }); -}); diff --git a/test/helpers/join.js b/test/helpers/join.js deleted file mode 100644 index c7991d24..00000000 --- a/test/helpers/join.js +++ /dev/null @@ -1,50 +0,0 @@ -var Code = require('code'), - Lab = require('lab'), - Paper = require('../../index'), - lab = exports.lab = Lab.script(), - describe = lab.experiment, - expect = Code.expect, - it = lab.it; - -function c(template, context) { - return new Paper().loadTemplatesSync({template: template}).render('template', context); -} - -describe('join helper', function() { - var context = { - list: ['Mario', 'Chris', 'Mick', 'Hau', 'Cody'] - - }; - - it('should print a list of names', function(done) { - - expect(c('{{join list " "}}', context)) - .to.be.equal('Mario Chris Mick Hau Cody'); - - expect(c('{{join list ", "}}', context)) - .to.be.equal('Mario, Chris, Mick, Hau, Cody'); - - done(); - }); - - it('should print a list of names and limit to 3', function(done) { - expect(c('{{join list " " limit=3}}', context)) - .to.be.equal('Mario Chris Mick'); - - done(); - }); - - it('should print a list of names and use "and" for the last name', function(done) { - expect(c('{{join list ", " lastSeparator=" and "}}', context)) - .to.be.equal('Mario, Chris, Mick, Hau and Cody'); - - done(); - }); - - it('should print a list of names and limit to 3 and use "and" for the last name', function(done) { - expect(c('{{join list ", " limit=3 lastSeparator=" and "}}', context)) - .to.be.equal('Mario, Chris and Mick'); - - done(); - }); -}); diff --git a/test/helpers/json.js b/test/helpers/json.js deleted file mode 100644 index ea34da46..00000000 --- a/test/helpers/json.js +++ /dev/null @@ -1,25 +0,0 @@ -var Code = require('code'), - Lab = require('lab'), - Paper = require('../../index'), - lab = exports.lab = Lab.script(), - describe = lab.experiment, - expect = Code.expect, - it = lab.it; - -function c(template, context) { - return new Paper().loadTemplatesSync({template: template}).render('template', context); -} - -describe('json helper', function() { - - it('should render object to json format', function(done) { - var context = { - object: {a: 1, b: "hello"} - }; - - expect(c('{{{json object}}}', context)) - .to.contain('{"a":1,"b":"hello"}'); - - done(); - }); -}); diff --git a/test/helpers/lang.js b/test/helpers/lang.js deleted file mode 100644 index d7a4317b..00000000 --- a/test/helpers/lang.js +++ /dev/null @@ -1,52 +0,0 @@ -'use strict'; - -const Code = require('code'); -const Lab = require('lab'); -const Paper = require('../../index'); - -const lab = exports.lab = Lab.script(); -const beforeEach = lab.beforeEach; -const describe = lab.experiment; -const expect = Code.expect; -const it = lab.it; - -function compile(paper, template, context) { - context = context || {}; - - return paper.loadTemplatesSync({ template: template }).render('template', context); -} - -describe('lang helper', () => { - let context; - let paper; - - beforeEach(done => { - context = { - name: 'BigCommerce', - }; - - paper = new Paper(); - - paper.translator = { - getLocale: () => 'en', - translate: (key, params) => `Powered By ${params.name}`, - }; - - done(); - }); - - it('should translate the key with attributes', done => { - expect(compile(paper, '{{lang "powered_by" name=name}}', context)) - .to.be.equal('Powered By BigCommerce'); - - done(); - }); - - it('should return an empty string if translator is undefined', done => { - paper.translator = null; - - expect(compile(paper, '{{lang "powered_by" name=name}}', context)).to.be.equal(''); - - done(); - }); -}); diff --git a/test/helpers/langJson.js b/test/helpers/langJson.js deleted file mode 100644 index 7a372a7e..00000000 --- a/test/helpers/langJson.js +++ /dev/null @@ -1,46 +0,0 @@ -'use strict'; - -const Code = require('code'); -const Lab = require('lab'); -const Paper = require('../../index'); - -const lab = exports.lab = Lab.script(); -const beforeEach = lab.beforeEach; -const describe = lab.experiment; -const expect = Code.expect; -const it = lab.it; - -function compile(paper, template, context) { - context = context || {}; - - return paper.loadTemplatesSync({ template: template }).render('template', context); -} - -describe('langJson helper', () => { - let paper; - - beforeEach(done => { - paper = new Paper(); - - paper.translator = { - getLocale: () => 'en', - getLanguage: () => ({ locale: 'en' }), - }; - - done(); - }); - - it('should return translation as JSON string if translator is defined', done => { - expect(compile(paper, '{{{langJson}}}')).to.be.equal(JSON.stringify(paper.translator.getLanguage())); - - done(); - }); - - it('should return an empty object as JSON string if translator is not defined', done => { - paper.translator = null; - - expect(compile(paper, '{{{langJson}}}')).to.be.equal('{}'); - - done(); - }); -}); diff --git a/test/helpers/limit.js b/test/helpers/limit.js deleted file mode 100644 index 6cef45d6..00000000 --- a/test/helpers/limit.js +++ /dev/null @@ -1,31 +0,0 @@ -var Code = require('code'), - Lab = require('lab'), - Paper = require('../../index'), - lab = exports.lab = Lab.script(), - describe = lab.experiment, - expect = Code.expect, - it = lab.it; - -function c(template, context) { - return new Paper().loadTemplatesSync({template: template}).render('template', context); -} - -describe('limit helper', function() { - - it('should limit an array properly', function(done) { - - expect(c('{{#each (limit var 4)}}{{this}} {{/each}}', {var: [1,2,3,4,5,6,7,8]})) - .to.be.equal('1 2 3 4 '); - - done(); - }); - - it('should limit an string properly', function(done) { - var description = "This is longer than the chosen limit"; - - expect(c('{{limit var 10}}', {var: description})) - .to.be.equal('This is lo'); - - done(); - }); -}); diff --git a/test/helpers/nl2br.js b/test/helpers/nl2br.js deleted file mode 100644 index 8f19cc69..00000000 --- a/test/helpers/nl2br.js +++ /dev/null @@ -1,25 +0,0 @@ -var Code = require('code'), - Lab = require('lab'), - Paper = require('../../index'), - lab = exports.lab = Lab.script(), - describe = lab.experiment, - expect = Code.expect, - it = lab.it; - -function c(template, context) { - return new Paper().loadTemplatesSync({template: template}).render('template', context); -} - -describe('nl2br helper', function() { - var context = { - text: "Hello\nmy\nname\nis\nJack" - }; - - it('should convert new lines to
tags', function(done) { - - expect(c('{{nl2br text}}', context)) - .to.be.equal('Hello
\nmy
\nname
\nis
\nJack'); - - done(); - }); -}); diff --git a/test/helpers/or.js b/test/helpers/or.js deleted file mode 100644 index b722121f..00000000 --- a/test/helpers/or.js +++ /dev/null @@ -1,57 +0,0 @@ -var Code = require('code'), - Lab = require('lab'), - Paper = require('../../index'), - lab = exports.lab = Lab.script(), - describe = lab.experiment, - expect = Code.expect, - it = lab.it; - -function c(template, context) { - return new Paper().loadTemplatesSync({template: template}).render('template', context); -} - -describe('or helper', function() { - var context = { - num1: 1, - num2: 2, - product: {a: 1, b: 2}, - string: 'yolo', - alwaysTrue: true, - alwaysFalse: false, - emptyArray: [], - emptyObject: {}, - itemArray: [1,2], - big: 'big', - arrayWithObjs: [{a: 1},{b: 1},{a: 2}] - }; - - it('should return "big" if at least one arg valid', function(done) { - expect(c('{{#or arrayWithObjs string}}{{big}}{{/or}}', context)) - .to.be.equal('big'); - - expect(c('{{#or "something test" itemArray}}{{big}}{{/or}}', context)) - .to.be.equal('big'); - - expect(c('{{#or "this is before the other test"}}{{big}}{{/or}}', context)) - .to.be.equal('big'); - - expect(c('{{#or alwaysFalse emptyArray string}}{{big}}{{/or}}', context)) - .to.be.equal('big'); - - done(); - - }); - - it('should return "" when no arguments are valid', function(done) { - expect(c('{{#or emptyArray emptyObject alwaysFalse}}{{big}}{{/or}}', context)) - .to.be.equal(''); - - expect(c('{{#or "" false}}{{big}}{{/or}}', context)) - .to.be.equal(''); - - done(); - - }); - -}); - diff --git a/test/helpers/pluck.js b/test/helpers/pluck.js deleted file mode 100644 index 4c1d9db9..00000000 --- a/test/helpers/pluck.js +++ /dev/null @@ -1,32 +0,0 @@ -var Code = require('code'), - Lab = require('lab'), - Paper = require('../../index'), - lab = exports.lab = Lab.script(), - describe = lab.experiment, - expect = Code.expect, - it = lab.it; - -function c(template, context) { - return new Paper().loadTemplatesSync({template: template}).render('template', context); -} - -describe('pluck helper', function() { - - var context = { - users: [ - { 'user': 'barney', 'age': 36 }, - { 'user': 'fred', 'age': 40 } - ] - }; - - it('should get the values from all elements in collection', function(done) { - - expect(c('{{pluck users "age"}}', context)) - .to.contain('36,40'); - - expect(c('{{#each (pluck users "user")}}hello {{this}} {{/each}}', context)) - .to.contain('hello barney hello fred '); - - done(); - }); -}); diff --git a/test/helpers/pre.js b/test/helpers/pre.js deleted file mode 100644 index bb8459ad..00000000 --- a/test/helpers/pre.js +++ /dev/null @@ -1,30 +0,0 @@ -var Code = require('code'), - Lab = require('lab'), - Paper = require('../../index'), - lab = exports.lab = Lab.script(), - describe = lab.experiment, - expect = Code.expect, - it = lab.it; - -function c(template, context) { - return new Paper().loadTemplatesSync({template: template}).render('template', context); -} - -describe('pre helper', function() { - - it('should render an object properly', function(done) { - - expect(c('{{{pre var}}}', {var: {}})) - .to.be.equal('
{}
'); - - done(); - }); - - it('should scape html entities', function(done) { - - expect(c('{{{pre var}}}', {var: "
&\"500\"
"})) - .to.be.equal('
"<div>&\\"500\\"</div>"
'); - - done(); - }); -}); diff --git a/test/helpers/region.js b/test/helpers/region.js deleted file mode 100644 index c4339c4e..00000000 --- a/test/helpers/region.js +++ /dev/null @@ -1,59 +0,0 @@ -'use strict'; - -const Code = require('code'); -const Lab = require('lab'); -const Paper = require('../../index'); - -const lab = exports.lab = Lab.script(); -const before = lab.before; -const describe = lab.experiment; -const expect = Code.expect; -const it = lab.it; - -function compile(paper, template, context) { - context = context || {}; - return paper.loadTemplatesSync({ - template: template - }).render('template', context); -} - -describe('Region Helper', () => { - let context; - let paper; - - before(done => { - context = { - regions: { - 'banner-top': "hello world" - } - }; - - paper = new Paper(); - paper.contentServiceContext = context; - - done(); - }); - - it('should return an empty string if no content service data (missing contentServiceContext) on page context', done => { - let noPaperContext = new Paper(); - expect(compile(noPaperContext, '{{region name="banner-bottom"}}', context)) - .to.be.equal(''); - - done(); - }); - - it('should return an empty container if no matching region on context object', done => { - expect(compile(paper, '{{region name="banner-bottom"}}', context)) - .to.be.equal('
'); - - done(); - }); - - it('should return Hello World', done => { - expect(compile(paper, '{{region name="banner-top"}}', context)) - .to.be.equal('
hello world
'); - - done(); - }); - -}); diff --git a/test/helpers/replace.js b/test/helpers/replace.js deleted file mode 100644 index 58bcb1ee..00000000 --- a/test/helpers/replace.js +++ /dev/null @@ -1,60 +0,0 @@ -var Code = require('code'), - Lab = require('lab'), - Paper = require('../../index'), - lab = exports.lab = Lab.script(), - describe = lab.experiment, - expect = Code.expect, - it = lab.it; - -function c(template, context) { - return new Paper().loadTemplatesSync({ template, template2: "day" }).render('template', context); -} - -describe('replace helper', function() { - const context = { - content: "Either you run the %%var%% or the %%var%% runs you", - price: '$49.99', - }; - - it('should replace all ocurrance of %%var%% with "day"', function(done) { - - expect(c("{{#replace '%%var%%' content}}{{> template2}}{{/replace}}", context)) - .to.be.equal('Either you run the day or the day runs you'); - - done(); - }); - - it('should handle undefined values', function(done) { - const context = {}; - expect(c("{{#replace '%%var%%' content}}{{> template2}}{{/replace}}", context)) - .to.be.equal(''); - - done(); - }); - - it('should replace $', function(done) { - expect(c("{{#replace '$' price}}{{/replace}}", context)) - .to.be.equal('49.99'); - - expect(c("{{#replace '$' '$10.00'}}{{/replace}}", context)) - .to.be.equal('10.00'); - - expect(c("{{#replace '$' '$10.00'}}USD {{/replace}}", context)) - .to.be.equal('USD 10.00'); - - done(); - }); - - it('should gracefully handle not strings', function(done) { - expect(c("{{#replace something price}}{{/replace}}", context)) - .to.be.equal(''); - - expect(c("{{#replace $ '$10.00'}}{{/replace}}", context)) - .to.be.equal(''); - - expect(c("{{#replace foo bar}}{{/replace}}", context)) - .to.be.equal(''); - - done(); - }); -}); diff --git a/test/helpers/snippets.js b/test/helpers/snippets.js deleted file mode 100644 index 1c61576b..00000000 --- a/test/helpers/snippets.js +++ /dev/null @@ -1,22 +0,0 @@ -var Code = require('code'), - Lab = require('lab'), - Paper = require('../../index'), - lab = exports.lab = Lab.script(), - describe = lab.experiment, - expect = Code.expect, - it = lab.it; - -function c(template) { - return new Paper().loadTemplatesSync({template: template}).render('template', {}); -} - -describe('snippet helper', function() { - - it('should render a comment', function(done) { - - expect(c('{{{snippet "header"}}}')) - .to.be.equal(''); - - done(); - }); -}); diff --git a/test/helpers/stylesheet.js b/test/helpers/stylesheet.js deleted file mode 100644 index bb1035b3..00000000 --- a/test/helpers/stylesheet.js +++ /dev/null @@ -1,53 +0,0 @@ -var Code = require('code'), - Lab = require('lab'), - Paper = require('../../index'), - lab = exports.lab = Lab.script(), - describe = lab.experiment, - expect = Code.expect, - it = lab.it; - -function c(template, settings) { - return new Paper(settings).loadTemplatesSync({ template }).render('template', {}); -} - -describe('stylesheet helper', () => { - var settings = { - cdn_url: 'https://cdn.bcapp/hash', - theme_version_id: '123', - theme_config_id: 'xyz', - }; - it('should render a link tag with the cdn ulr and stencil-stylesheet data tag', done => { - expect(c('{{{stylesheet "assets/css/style.css"}}}', settings)) - .to.be.equal(''); - - done(); - }); - - it('should render a link tag and all extra attributes with no cdn url', done => { - expect(c('{{{stylesheet "assets/css/style.css" fuck rel="something" class="myStyle"}}}', {})) - .to.be.equal(''); - - done(); - }); - - it('should render a link with empty href', done => { - expect(c('{{{stylesheet "" }}}')) - .to.be.equal(''); - - done(); - }); - - it('should add configId to the filename', done => { - expect(c('{{{stylesheet "assets/css/style.css" }}}', { theme_config_id: 'foo' })) - .to.be.equal(''); - - done(); - }); - - it('should not append configId if the file is not in assets/css/ directory', done => { - expect(c('{{{stylesheet "assets/lib/style.css" }}}', { theme_config_id: 'foo' })) - .to.be.equal(''); - - done(); - }); -}); diff --git a/test/helpers/thirdParty.js b/test/helpers/thirdParty.js deleted file mode 100644 index f577f340..00000000 --- a/test/helpers/thirdParty.js +++ /dev/null @@ -1,220 +0,0 @@ -var Code = require('code'), - Lab = require('lab'), - Paper = require('../../index'), - lab = exports.lab = Lab.script(), - describe = lab.experiment, - expect = Code.expect, - it = lab.it; - -function c(template, context) { - return new Paper().loadTemplatesSync({template: template}).render('template', context); -} - -var context = { - array: [1, 2, 3, 4, 5], - options: { a: { b: { c: 'd' } } } -}; - -describe('third party handlebars-helpers', function() { - - describe('array helpers', function() { - - describe('after helper', function() { - it('returns all the items in an array after the index', function(done) { - expect(c('{{after array 1}}', context)) - .to.be.equal('2,3,4,5'); - - done(); - }); - }); - - describe('first helper', function() { - it('returns the first n items in an array', function(done) { - expect(c('{{first array 2}}', context)) - .to.be.equal('1,2'); - - done(); - }); - }); - - }); - - describe('collection helpers', function() { - - describe('length helper', function() { - it('returns the length of the array', function(done) { - expect(c('{{length array}}', context)) - .to.be.equal('5'); - - done(); - }); - }); - - }); - - describe('comparison helpers', function() { - - describe('contains helper', function() { - it('renders the contains block if it evaluates to true', function(done) { - expect(c(`{{#contains array 1}}This will be rendered.{{else}}This will not be rendered.{{/contains}}`, context)) - .to.be.equal('This will be rendered.'); - - done(); - }); - - it('renders the else block if it evaluates to false', function(done) { - expect(c(`{{#contains array '1'}}This will not be rendered.{{else}}This will be rendered.{{/contains}}`, context)) - .to.be.equal('This will be rendered.'); - - done(); - }); - }); - - }); - - describe('date helpers', function() { - - describe('contains moment', function() { - it('renders the date in the format specified', function(done) { - const now = new Date() - expect(c(`{{#moment "1 year ago" "YYYY"}}{{/moment}}`, context)) - .to.be.equal(`${now.getFullYear() - 1}`); - - done(); - }); - }); - - }); - - describe('html helpers', function() { - - describe('contains ellipsis', function() { - it('truncates a string to the specified length and appends an ellipsis', function(done) { - expect(c(`{{ellipsis "foo bar baz" 7}}`, context)) - .to.be.equal('foo bar…'); - - done(); - }); - }); - - describe('contains thumbnailImage', function() { - it('creates a
with a thumbnail linked to an image', function(done) { - const ctxt = { - data: { - id: 'id', - alt: 'alt', - thumbnail: 'thumbnail.png', - size: { - width: 32, - height: 32 - } - } - }; - expect(c(`{{{thumbnailImage data}}}`, ctxt)).to.be - .equal('
\n\"alt\"\n
'); - - done(); - }); - }); - - }); - - describe('inflection helpers', function() { - - describe('contains ordinalize', function() { - it('returns an ordinalized number as a string', function(done) { - expect(c(`{{ordinalize 42}}`, context)) - .to.be.equal('42nd'); - - done(); - }); - }); - - }); - - describe('markdown helpers', function() { - - describe('contains markdown', function() { - it('converts a string of markdown to HTML', function(done) { - expect(c(`{{#markdown}}# Foo{{/markdown}}`, context)) - .to.be.equal('

Foo

\n'); - - done(); - }); - }); - - }); - - describe('math helpers', function() { - - describe('contains avg', function() { - it('returns the average of the numbers in an array', function(done) { - expect(c(`{{avg array}}`, context)) - .to.be.equal('3'); - - done(); - }); - }); - - }); - - describe('misc helpers', function() { - - describe('contains option', function() { - it('returns the nested prop of this.options', function(done) { - expect(c(`{{option "a.b.c"}}`, context)) - .to.be.equal('d'); - - done(); - }); - }); - - }); - - describe('object helpers', function() { - - describe('contains isObject', function() { - it('returns true if the value is an object', function(done) { - expect(c(`{{isObject options}}`, context)) - .to.be.equal('true'); - - done(); - }); - - it('returns false if the value is not an object', function(done) { - expect(c(`{{isObject "foo"}}`, context)) - .to.be.equal('false'); - - done(); - }); - }); - - }); - - describe('string helpers', function() { - - describe('contains capitalize', function() { - it('capitalizes the first word in a sentence', function(done) { - expect(c(`{{capitalize "foo bar baz"}}`, context)) - .to.be.equal('Foo bar baz'); - - done(); - }); - }); - - }); - - describe('url helpers', function() { - - describe('contains stripQuerystring', function() { - it('strips the query string from a given url', function(done) { - expect(c(`{{stripQuerystring 'http://www.example.com?foo=1&bar=2&baz=3'}}`, context)) - .to.be.equal('http://www.example.com'); - - done(); - }); - }); - - }); - -}); diff --git a/test/helpers/toLowerCase.js b/test/helpers/toLowerCase.js deleted file mode 100644 index b211b561..00000000 --- a/test/helpers/toLowerCase.js +++ /dev/null @@ -1,49 +0,0 @@ -var Code = require('code'), - Lab = require('lab'), - Paper = require('../../index'), - lab = exports.lab = Lab.script(), - describe = lab.experiment, - expect = Code.expect, - it = lab.it; - -function c(template, context) { - return new Paper().loadTemplatesSync({template: template}).render('template', context); -} - -describe('toLowerCase helper', function() { - - var context = { - string: "I Love PIZZA", - number: 365, - object: {}, - array: [1, 2, 3] - }; - - it('should convert string to lower case', function(done) { - - expect(c('{{toLowerCase string}}', context)) - .to.contain('i love pizza'); - - expect(c('{{toLowerCase "HELLO"}}', context)) - .to.contain('hello'); - - done(); - }); - - it('should properly handle values other than strings', function(done) { - - expect(c('{{toLowerCase number}}', context)) - .to.contain('365'); - - expect(c('{{toLowerCase 5}}', context)) - .to.contain('5'); - - expect(c('{{toLowerCase object}}', context)) - .to.contain('[object Object]'); - - expect(c('{{toLowerCase array}}', context)) - .to.contain('1,2,3'); - - done(); - }); -}); diff --git a/test/helpers/truncate.js b/test/helpers/truncate.js deleted file mode 100644 index 975789f4..00000000 --- a/test/helpers/truncate.js +++ /dev/null @@ -1,62 +0,0 @@ -var Code = require('code'), - Lab = require('lab'), - Paper = require('../../index'), - lab = exports.lab = Lab.script(), - describe = lab.experiment, - expect = Code.expect, - it = lab.it; - -function c(template, context) { - return new Paper().loadTemplatesSync({template: template}).render('template', context); -} - -describe('truncate helper', function() { - - var context = { - chinese_string: '𠜎𠜱𠝹𠱓𠱸𠲖𠳏', - number: 2, - spanish_string: 'mañana', - string: 'hello world', - unicode_string: 'She ❤️️ this', - }; - - it('should return the entire string if length is longer than the input string', function(done) { - - expect(c('{{truncate string 15}}', context)) - .to.be.equal('hello world'); - done(); - }); - - it('should return the first length number of characters', function(done) { - - expect(c('{{truncate string 5}}', context)) - .to.be.equal('hello'); - done(); - }); - - it('should return the first argument, coerced to a string, if it is not a string', function(done) { - - expect(c('{{truncate number 5}}', context)) - .to.be.equal('2'); - done(); - }); - - it('should handle non-English strings', function(done) { - - expect(c('{{truncate spanish_string 3}}', context)) - .to.be.equal('mañ'); - - expect(c('{{truncate chinese_string 3}}', context)) - .to.be.equal('𠜎𠜱𠝹'); - - done(); - }); - - it('should handle unicode strings', function(done) { - - expect(c('{{truncate unicode_string 5}}', context)) - .to.be.equal('She ❤️'); - - done(); - }); -}); diff --git a/test/index.js b/test/index.js deleted file mode 100644 index 6343de6b..00000000 --- a/test/index.js +++ /dev/null @@ -1,157 +0,0 @@ -var Code = require('code'), - Lab = require('lab'), - Paper = require('../index'), - lab = exports.lab = Lab.script(), - describe = lab.experiment, - expect = Code.expect, - it = lab.it; - -describe('loadTheme()', function() { - var assembler = { - getTemplates: function(path, processor, callback) { - process.nextTick(function() { - var templates = { - 'pages/product': '', - 'pages/partial': '

', - 'pages/localeName': '{{locale_name}}', - }; - - callback(null, processor(templates)); - }); - }, - getTranslations: function (callback) { - process.nextTick(function() { - var translations = { - 'en': { - hello: 'Hello {name}', - level1: { - level2: 'we are in the second level' - } - }, - 'fr': { - hello: 'Bonjour {name}', - level1: { - level2: 'nous sommes dans le deuxième niveau' - } - }, - 'fr-CA': { - hello: 'Salut {name}' - } - }; - - callback(null, translations); - }); - } - }; - - it('should use the assembler interface to load templates and translations', done => { - const paper = new Paper(null, null, assembler); - - paper.loadTheme('pages/product', 'fr-CA;q=0.8, fr, en', () => { - expect(paper.handlebars.templates['pages/product']).to.be.a.function(); - expect(paper.handlebars.templates['pages/partial']).to.be.a.function(); - expect(paper.translator.translate('hello', {name: 'Mario'})).to.be.equal('Bonjour Mario'); - expect(paper.translator.translate('hello', {name: 'Already Compiled'})).to.be.equal('Bonjour Already Compiled'); - expect(paper.translator.translate('does_not_exist')).to.be.equal('does_not_exist'); - - done(); - }); - }); - - it('should get the localeName from the acceptLanguage header', done => { - const paper = new Paper(null, null, assembler); - - paper.loadTheme('pages/localeName', 'fr-CA;q=0.8, fr, en', () => { - expect(paper.translator.getLocale()).to.be.equal('fr'); - - done(); - }); - }); - - it('should default to english if the locale is not supported', done => { - const paper = new Paper(null, null, assembler); - - paper.loadTheme('pages/localeName', 'es-VE, en', () => { - expect(paper.translator.getLocale()).to.be.equal('en'); - - done(); - }); - }); - - it('should include the langName in the template context', done => { - const paper = new Paper(null, null, assembler); - - paper.loadTheme('pages/localeName', 'fr-CA, en', () => { - expect(paper.render('pages/localeName', {})).to.be.equal('fr-CA'); - - done(); - }); - }); -}); - -describe('cdnify()', function () { - it('should not include session id', function (done) { - var paper = new Paper({ - cdn_url: 'http://cdn.example.com/foo', - theme_version_id: '123', - }); - - expect(paper.cdnify('/assets/image.jpg')) - .to.be.equal('http://cdn.example.com/foo/stencil/123/image.jpg'); - - done(); - }); - - it('should use sessionId if available', function (done) { - var paper = new Paper({ - cdn_url: 'http://cdn.example.com/foo', - theme_version_id: '123', - theme_session_id: '345', - }); - - expect(paper.cdnify('/assets/image.jpg')) - .to.be.equal('http://cdn.example.com/foo/stencil/123/e/345/image.jpg'); - - done(); - }); -}); - -describe('render()', function() { - var templates = { - 'pages/product': '{{> pages/partial}}', - 'pages/partial': '

{{variable}}

', - 'pages/greet': '

{{lang \'good\'}} {{lang \'morning\'}}

', - 'pages/pre': '{{{pre object}}}', - }, - context = { - variable: 'hello world', - object: {} - }; - - it('should render pages/product', function(done) { - var compiled = new Paper().loadTemplatesSync(templates).render('pages/product', context); - expect(compiled).to.be.equal('

hello world

'); - done(); - }); - - it('should render pages/partial', function(done) { - var compiled = new Paper().loadTemplatesSync(templates).render('pages/partial', context); - expect(compiled).to.be.equal('

hello world

'); - done(); - }); - - it('should render with errors', function(done) { - var templates = { - 'errorPage': '{{' - }; - - try { - var compiled = new Paper().loadTemplatesSync(templates).render('errorPage', context); - expect(compiled).not.to.exist(); - } catch (ex) { - expect(ex).to.exist(); - } - - done(); - }); -});