Merge pull request #998 from wycats/babel

Add full support for es6
This commit is contained in:
Kevin Decker
2015-04-16 17:12:40 -05:00
51 changed files with 1785 additions and 1538 deletions
+186
View File
@@ -0,0 +1,186 @@
{
"globals": {
"self": false
},
"env": {
"node": true
},
"ecmaFeatures": {
"modules": true
},
"rules": {
// Possible Errors //
//-----------------//
"comma-dangle": [2, "never"],
"no-cond-assign": [2, "except-parens"],
// Allow for debugging
"no-console": 1,
"no-constant-condition": 2,
"no-control-regex": 2,
// Allow for debugging
"no-debugger": 1,
"no-dupe-args": 2,
"no-dupe-keys": 2,
"no-duplicate-case": 2,
"no-empty": 2,
"no-empty-class": 2,
"no-ex-assign": 2,
"no-extra-boolean-cast": 2,
"no-extra-parens": 0,
"no-extra-semi": 2,
"no-func-assign": 2,
// Stylistic... might consider disallowing in the future
"no-inner-declarations": 0,
"no-invalid-regexp": 2,
"no-irregular-whitespace": 2,
"no-negated-in-lhs": 2,
"no-obj-calls": 2,
"no-regex-spaces": 2,
"no-reserved-keys": 2, // Important for IE
"no-sparse-arrays": 0,
// Optimizer and coverage will handle/highlight this and can be useful for debugging
"no-unreachable": 1,
"use-isnan": 2,
"valid-jsdoc": 0,
"valid-typeof": 2,
// Best Practices //
//----------------//
"block-scoped-var": 0,
"complexity": 0,
"consistent-return": 0,
"curly": 2,
"default-case": 1,
"dot-notation": [2, {"allowKeywords": false}],
"eqeqeq": 0,
"guard-for-in": 1,
"no-alert": 2,
"no-caller": 2,
"no-div-regex": 1,
"no-else-return": 0,
"no-empty-label": 2,
"no-eq-null": 0,
"no-eval": 2,
"no-extend-native": 2,
"no-extra-bind": 2,
"no-fallthrough": 2,
"no-floating-decimal": 2,
"no-implied-eval": 2,
"no-iterator": 2,
"no-labels": 2,
"no-lone-blocks": 2,
"no-loop-func": 2,
"no-multi-spaces": 2,
"no-multi-str": 1,
"no-native-reassign": 2,
"no-new": 2,
"no-new-func": 2,
"no-new-wrappers": 2,
"no-octal": 2,
"no-octal-escape": 2,
"no-param-reassign": 0,
"no-process-env": 2,
"no-proto": 2,
"no-redeclare": 2,
"no-return-assign": 2,
"no-script-url": 2,
"no-self-compare": 2,
"no-sequences": 2,
"no-throw-literal": 2,
"no-unused-expressions": 2,
"no-void": 0,
"no-warning-comments": 1,
"no-with": 2,
"radix": 2,
"vars-on-top": 0,
"wrap-iife": 2,
"yoda": 0,
// Strict //
//--------//
"strict": 0,
// Variables //
//-----------//
"no-catch-shadow": 2,
"no-delete-var": 2,
"no-label-var": 2,
"no-shadow": 2,
"no-shadow-restricted-names": 2,
"no-undef": 2,
"no-undef-init": 2,
"no-undefined": 0,
"no-unused-vars": [2, {"vars": "all", "args": "after-used"}],
"no-use-before-define": [2, "nofunc"],
// Node.js //
//---------//
// Others left to environment defaults
"no-mixed-requires": 0,
// Stylistic //
//-----------//
"indent": 0,
"brace-style": [2, "1tbs", {"allowSingleLine": true}],
"camelcase": 2,
"comma-spacing": [2, {"before": false, "after": true}],
"comma-style": [2, "last"],
"consistent-this": [1, "self"],
"eol-last": 2,
"func-names": 0,
"func-style": [2, "declaration"],
"key-spacing": [2, {
"beforeColon": false,
"afterColon": true
}],
"max-nested-callbacks": 0,
"new-cap": 2,
"new-parens": 2,
"newline-after-var": 0,
"no-array-constructor": 2,
"no-continue": 0,
"no-inline-comments": 0,
"no-lonely-if": 2,
"no-mixed-spaces-and-tabs": 2,
"no-multiple-empty-lines": 0,
"no-nested-ternary": 1,
"no-new-object": 2,
"no-spaced-func": 2,
"no-ternary": 0,
"no-trailing-spaces": 2,
"no-underscore-dangle": 0,
"no-wrap-func": 2,
"one-var": 0,
"operator-assignment": 0,
"padded-blocks": 0,
"quote-props": 0,
"quotes": [2, "single", "avoid-escape"],
"semi": 2,
"semi-spacing": [2, {"before": false, "after": true}],
"sort-vars": 0,
"space-after-keywords": [2, "always"],
"space-before-blocks": [2, "always"],
"space-before-function-paren": [2, {"anonymous": "never", "named": "never"}],
"space-in-brackets": 0,
"space-in-parens": [2, "never"],
"space-infix-ops": 2,
"space-return-throw-case": 2,
"space-unary-ops": 2,
"spaced-line-comment": 2,
"wrap-regex": 1
}
}
+62 -37
View File
@@ -1,16 +1,16 @@
var childProcess = require('child_process');
/*eslint-disable no-process-env */
module.exports = function(grunt) {
grunt.initConfig({
pkg: grunt.file.readJSON('package.json'),
jshint: {
eslint: {
options: {
jshintrc: '.jshintrc'
},
files: [
'dist/cjs/**/!(*.min|parser).js'
'*.js',
'lib/**/!(*.min|parser).js',
'spec/**/!(*.amd|json2|require).js'
]
},
@@ -19,7 +19,7 @@ module.exports = function(grunt) {
copy: {
dist: {
options: {
processContent: function(content, path) {
processContent: function(content) {
return grunt.template.process('/*!\n\n <%= pkg.name %> v<%= pkg.version %>\n\n<%= grunt.file.read("LICENSE") %>\n@license\n*/\n')
+ content;
}
@@ -41,54 +41,78 @@ module.exports = function(grunt) {
}
},
packager: {
global: {
type: 'umd',
export: 'Handlebars',
files: [{
cwd: 'lib/',
expand: true,
src: ['handlebars*.js'],
dest: 'dist/'
}]
babel: {
options: {
loose: ['es6.modules']
},
amd: {
type: 'amd',
anonymous: true,
options: {
modules: 'amd'
},
files: [{
expand: true,
cwd: 'lib/',
src: '**/!(index).js',
src: '**/!(index|precompiler).js',
dest: 'dist/amd/'
}]
},
cjs: {
type: 'cjs',
options: {
modules: 'common'
},
files: [{
expand: true,
cwd: 'lib/',
expand: true,
src: '**/!(index).js',
dest: 'dist/cjs/'
}]
}
},
webpack: {
options: {
context: __dirname,
module: {
loaders: [
// the optional 'runtime' transformer tells babel to require the runtime instead of inlining it.
{ test: /\.jsx?$/, exclude: /node_modules/, loader: 'babel-loader?optional=runtime&loose=es6.modules' }
]
},
output: {
path: 'dist/',
library: 'Handlebars',
libraryTarget: 'umd'
}
},
handlebars: {
entry: './lib/handlebars.js',
output: {
filename: 'handlebars.js'
}
},
runtime: {
entry: './lib/handlebars.runtime.js',
output: {
filename: 'handlebars.runtime.js'
}
}
},
requirejs: {
options: {
optimize: "none",
baseUrl: "dist/amd/"
optimize: 'none',
baseUrl: 'dist/amd/'
},
dist: {
options: {
name: "handlebars",
out: "dist/handlebars.amd.js"
name: 'handlebars',
out: 'dist/handlebars.amd.js'
}
},
runtime: {
options: {
name: "handlebars.runtime",
out: "dist/handlebars.runtime.amd.js"
name: 'handlebars.runtime',
out: 'dist/handlebars.runtime.amd.js'
}
}
},
@@ -172,18 +196,18 @@ module.exports = function(grunt) {
});
// Build a new version of the library
this.registerTask('build', "Builds a distributable version of the current project", [
this.registerTask('build', 'Builds a distributable version of the current project', [
'eslint',
'parser',
'node',
'globals',
'jshint']);
'globals']);
this.registerTask('amd', ['packager:amd', 'requirejs']);
this.registerTask('node', ['packager:cjs']);
this.registerTask('globals', ['packager:global']);
this.registerTask('amd', ['babel:amd', 'requirejs']);
this.registerTask('node', ['babel:cjs']);
this.registerTask('globals', ['webpack']);
this.registerTask('tests', ['concat:tests']);
this.registerTask('release', 'Build final packages', ['amd', 'jshint', 'uglify', 'copy:dist', 'copy:components', 'copy:cdnjs']);
this.registerTask('release', 'Build final packages', ['eslint', 'amd', 'uglify', 'copy:dist', 'copy:components', 'copy:cdnjs']);
// Load tasks from npm
grunt.loadNpmTasks('grunt-contrib-clean');
@@ -191,11 +215,12 @@ module.exports = function(grunt) {
grunt.loadNpmTasks('grunt-contrib-connect');
grunt.loadNpmTasks('grunt-contrib-copy');
grunt.loadNpmTasks('grunt-contrib-requirejs');
grunt.loadNpmTasks('grunt-contrib-jshint');
grunt.loadNpmTasks('grunt-contrib-uglify');
grunt.loadNpmTasks('grunt-contrib-watch');
grunt.loadNpmTasks('grunt-babel');
grunt.loadNpmTasks('grunt-eslint');
grunt.loadNpmTasks('grunt-saucelabs');
grunt.loadNpmTasks('es6-module-packager');
grunt.loadNpmTasks('grunt-webpack');
grunt.task.loadTasks('tasks');
+18 -20
View File
@@ -1,21 +1,20 @@
/*globals Handlebars: true */
import Handlebars from "./handlebars.runtime";
import Handlebars from './handlebars.runtime';
// Compiler imports
import AST from "./handlebars/compiler/ast";
import { parser as Parser, parse } from "./handlebars/compiler/base";
import { Compiler, compile, precompile } from "./handlebars/compiler/compiler";
import JavaScriptCompiler from "./handlebars/compiler/javascript-compiler";
import Visitor from "./handlebars/compiler/visitor";
import AST from './handlebars/compiler/ast';
import { parser as Parser, parse } from './handlebars/compiler/base';
import { Compiler, compile, precompile } from './handlebars/compiler/compiler';
import JavaScriptCompiler from './handlebars/compiler/javascript-compiler';
import Visitor from './handlebars/compiler/visitor';
var _create = Handlebars.create;
var create = function() {
function create() {
var hb = _create();
hb.compile = function(input, options) {
return compile(input, options, hb);
};
hb.precompile = function (input, options) {
hb.precompile = function(input, options) {
return precompile(input, options, hb);
};
@@ -26,24 +25,23 @@ var create = function() {
hb.parse = parse;
return hb;
};
}
Handlebars = create();
Handlebars.create = create;
var inst = create();
inst.create = create;
Handlebars.Visitor = Visitor;
inst.Visitor = Visitor;
/*jshint -W040 */
/* istanbul ignore next */
var root = typeof global !== 'undefined' ? global : window,
$Handlebars = root.Handlebars;
var $Handlebars = global.Handlebars;
/* istanbul ignore next */
Handlebars.noConflict = function() {
if (root.Handlebars === Handlebars) {
root.Handlebars = $Handlebars;
inst.noConflict = function() {
if (global.Handlebars === inst) {
global.Handlebars = $Handlebars;
}
};
Handlebars['default'] = Handlebars;
inst['default'] = inst;
export default Handlebars;
export default inst;
+8 -8
View File
@@ -1,15 +1,15 @@
/*globals Handlebars: true */
module base from "./handlebars/base";
/*global window */
import * as base from './handlebars/base';
// Each of these augment the Handlebars object. No need to setup here.
// (This is done to easily share code between commonjs and browse envs)
import SafeString from "./handlebars/safe-string";
import Exception from "./handlebars/exception";
module Utils from "./handlebars/utils";
module runtime from "./handlebars/runtime";
import SafeString from './handlebars/safe-string';
import Exception from './handlebars/exception';
import * as Utils from './handlebars/utils';
import * as runtime from './handlebars/runtime';
// For compatibility and usage outside of module systems, make the Handlebars object a namespace
var create = function() {
function create() {
var hb = new base.HandlebarsEnvironment();
Utils.extend(hb, base);
@@ -24,7 +24,7 @@ var create = function() {
};
return hb;
};
}
var Handlebars = create();
Handlebars.create = create;
+31 -31
View File
@@ -1,7 +1,7 @@
module Utils from "./utils";
import Exception from "./exception";
import * as Utils from './utils';
import Exception from './exception';
export var VERSION = "3.0.1";
export var VERSION = '3.0.1';
export var COMPILER_REVISION = 6;
export var REVISION_CHANGES = {
@@ -45,7 +45,7 @@ HandlebarsEnvironment.prototype = {
registerPartial: function(name, partial) {
if (toString.call(name) === objectType) {
Utils.extend(this.partials, name);
Utils.extend(this.partials, name);
} else {
if (typeof partial === 'undefined') {
throw new Exception('Attempting to register a partial as undefined');
@@ -60,12 +60,12 @@ HandlebarsEnvironment.prototype = {
function registerDefaultHelpers(instance) {
instance.registerHelper('helperMissing', function(/* [args, ]options */) {
if(arguments.length === 1) {
if (arguments.length === 1) {
// A missing field in a {{foo}} constuct.
return undefined;
} else {
// Someone is actually trying to call something, blow up.
throw new Exception("Missing helper: '" + arguments[arguments.length-1].name + "'");
throw new Exception('Missing helper: "' + arguments[arguments.length - 1].name + '"');
}
});
@@ -73,12 +73,12 @@ function registerDefaultHelpers(instance) {
var inverse = options.inverse,
fn = options.fn;
if(context === true) {
if (context === true) {
return fn(this);
} else if(context === false || context == null) {
} else if (context === false || context == null) {
return inverse(this);
} else if (isArray(context)) {
if(context.length > 0) {
if (context.length > 0) {
if (options.ids) {
options.ids = [options.name];
}
@@ -104,7 +104,7 @@ function registerDefaultHelpers(instance) {
}
var fn = options.fn, inverse = options.inverse;
var i = 0, ret = "", data;
var i = 0, ret = '', data;
var contextPath;
if (options.data && options.ids) {
@@ -117,51 +117,51 @@ function registerDefaultHelpers(instance) {
data = createFrame(options.data);
}
function execIteration(key, i, last) {
function execIteration(field, index, last) {
if (data) {
data.key = key;
data.index = i;
data.first = i === 0;
data.last = !!last;
data.key = field;
data.index = index;
data.first = index === 0;
data.last = !!last;
if (contextPath) {
data.contextPath = contextPath + key;
data.contextPath = contextPath + field;
}
}
ret = ret + fn(context[key], {
ret = ret + fn(context[field], {
data: data,
blockParams: Utils.blockParams([context[key], key], [contextPath + key, null])
blockParams: Utils.blockParams([context[field], field], [contextPath + field, null])
});
}
if(context && typeof context === 'object') {
if (context && typeof context === 'object') {
if (isArray(context)) {
for(var j = context.length; i<j; i++) {
execIteration(i, i, i === context.length-1);
for (var j = context.length; i < j; i++) {
execIteration(i, i, i === context.length - 1);
}
} else {
var priorKey;
for(var key in context) {
if(context.hasOwnProperty(key)) {
for (var key in context) {
if (context.hasOwnProperty(key)) {
// We're running the iterations one step out of sync so we can detect
// the last iteration without have to scan the object twice and create
// an itermediate keys array.
// an itermediate keys array.
if (priorKey) {
execIteration(priorKey, i-1);
execIteration(priorKey, i - 1);
}
priorKey = key;
i++;
}
}
if (priorKey) {
execIteration(priorKey, i-1, true);
execIteration(priorKey, i - 1, true);
}
}
}
if(i === 0){
if (i === 0) {
ret = inverse(this);
}
@@ -194,7 +194,7 @@ function registerDefaultHelpers(instance) {
if (options.data && options.ids) {
var data = createFrame(options.data);
data.contextPath = Utils.appendContextPath(options.data.contextPath, options.ids[0]);
options = {data:data};
options = {data: data};
}
return fn(context, options);
@@ -227,15 +227,15 @@ export var logger = {
log: function(level, message) {
if (typeof console !== 'undefined' && logger.level <= level) {
var method = logger.methodMap[level];
(console[method] || console.log).call(console, message);
(console[method] || console.log).call(console, message); // eslint-disable-line no-console
}
}
};
export var log = logger.log;
export var createFrame = function(object) {
export function createFrame(object) {
var frame = Utils.extend({}, object);
frame._parent = object;
return frame;
};
}
+4 -5
View File
@@ -27,8 +27,8 @@ var AST = {
this.path = path;
this.params = params || [];
this.hash = hash;
this.program = program;
this.inverse = inverse;
this.program = program;
this.inverse = inverse;
this.openStrip = openStrip;
this.inverseStrip = inverseStrip;
@@ -76,8 +76,8 @@ var AST = {
this.data = data;
this.original = original;
this.parts = parts;
this.depth = depth;
this.parts = parts;
this.depth = depth;
},
StringLiteral: function(string, locInfo) {
@@ -130,7 +130,6 @@ var AST = {
// a mustache is definitely a helper if:
// * it is an eligible helper, and
// * it has at least one parameter or hash segment
// TODO: Make these public utility methods
helperExpression: function(node) {
return !!(node.type === 'SubExpression' || node.params.length || node.hash);
},
+5 -5
View File
@@ -1,8 +1,8 @@
import parser from "./parser";
import AST from "./ast";
import WhitespaceControl from "./whitespace-control";
module Helpers from "./helpers";
import { extend } from "../utils";
import parser from './parser';
import AST from './ast';
import WhitespaceControl from './whitespace-control';
import * as Helpers from './helpers';
import { extend } from '../utils';
export { parser };
+5 -4
View File
@@ -1,4 +1,5 @@
import {isArray} from "../utils";
/*global define */
import {isArray} from '../utils';
var SourceNode;
@@ -56,7 +57,7 @@ function castChunk(chunk, codeGen, loc) {
return ret;
} else if (typeof chunk === 'boolean' || typeof chunk === 'number') {
// Handle primitives that the SourceNode will throw up on
return chunk+'';
return chunk + '';
}
return chunk;
}
@@ -90,7 +91,7 @@ CodeGen.prototype = {
},
empty: function(loc) {
loc = loc || this.currentLocation || {start:{}};
loc = loc || this.currentLocation || {start: {}};
return new SourceNode(loc.start.line, loc.start.column, this.srcFile);
},
wrap: function(chunk, loc) {
@@ -98,7 +99,7 @@ CodeGen.prototype = {
return chunk;
}
loc = loc || this.currentLocation || {start:{}};
loc = loc || this.currentLocation || {start: {}};
chunk = castChunk(chunk, this, loc);
return new SourceNode(loc.start.line, loc.start.column, this.srcFile, chunk);
+30 -24
View File
@@ -1,6 +1,6 @@
import Exception from "../exception";
import {isArray, indexOf} from "../utils";
import AST from "./ast";
import Exception from '../exception';
import {isArray, indexOf} from '../utils';
import AST from './ast';
var slice = [].slice;
@@ -67,7 +67,9 @@ Compiler.prototype = {
};
if (knownHelpers) {
for (var name in knownHelpers) {
options.knownHelpers[name] = knownHelpers[name];
if (name in knownHelpers) {
options.knownHelpers[name] = knownHelpers[name];
}
}
}
@@ -75,7 +77,7 @@ Compiler.prototype = {
},
compileProgram: function(program) {
var result = new this.compiler().compile(program, this.options);
var result = new this.compiler().compile(program, this.options); // eslint-disable-line new-cap
var guid = this.guid++;
this.usePartial = this.usePartial || result.usePartial;
@@ -97,7 +99,7 @@ Compiler.prototype = {
this.options.blockParams.unshift(program.blockParams);
var body = program.body;
for(var i=0, l=body.length; i<l; i++) {
for (var i = 0, l = body.length; i < l; i++) {
this.accept(body[i]);
}
@@ -174,9 +176,9 @@ Compiler.prototype = {
},
MustacheStatement: function(mustache) {
this.SubExpression(mustache);
this.SubExpression(mustache); // eslint-disable-line new-cap
if(mustache.escaped && !this.options.noEscape) {
if (mustache.escaped && !this.options.noEscape) {
this.opcode('appendEscaped');
} else {
this.opcode('append');
@@ -231,7 +233,7 @@ Compiler.prototype = {
if (this.options.knownHelpers[name]) {
this.opcode('invokeKnownHelper', params.length, name);
} else if (this.options.knownHelpersOnly) {
throw new Exception("You specified knownHelpersOnly, but used the unknown helper " + name, sexpr);
throw new Exception('You specified knownHelpersOnly, but used the unknown helper ' + name, sexpr);
} else {
path.falsy = true;
@@ -250,7 +252,7 @@ Compiler.prototype = {
if (blockParamId) {
this.opcode('lookupBlockParam', blockParamId, path.parts);
} else if (!name) {
} else if (!name) {
// Context reference, i.e. `{{foo .}}` or `{{foo ..}}`
this.opcode('pushContext');
} else if (path.data) {
@@ -286,7 +288,7 @@ Compiler.prototype = {
this.opcode('pushHash');
for (i=0, l=pairs.length; i<l; i++) {
for (i = 0, l = pairs.length; i < l; i++) {
this.pushParam(pairs[i].value);
}
while (i--) {
@@ -336,13 +338,17 @@ Compiler.prototype = {
}
}
if (isHelper) { return 'helper'; }
else if (isEligible) { return 'ambiguous'; }
else { return 'simple'; }
if (isHelper) {
return 'helper';
} else if (isEligible) {
return 'ambiguous';
} else {
return 'simple';
}
},
pushParams: function(params) {
for(var i=0, l=params.length; i<l; i++) {
for (var i = 0, l = params.length; i < l; i++) {
this.pushParam(params[i]);
}
},
@@ -357,7 +363,7 @@ Compiler.prototype = {
.replace(/\//g, '.');
}
if(val.depth) {
if (val.depth) {
this.addDepth(val.depth);
}
this.opcode('getContext', val.depth || 0);
@@ -421,7 +427,7 @@ Compiler.prototype = {
export function precompile(input, options, env) {
if (input == null || (typeof input !== 'string' && input.type !== 'Program')) {
throw new Exception("You must pass a string or Handlebars AST to Handlebars.precompile. You passed " + input);
throw new Exception('You must pass a string or Handlebars AST to Handlebars.precompile. You passed ' + input);
}
options = options || {};
@@ -439,7 +445,7 @@ export function precompile(input, options, env) {
export function compile(input, options, env) {
if (input == null || (typeof input !== 'string' && input.type !== 'Program')) {
throw new Exception("You must pass a string or Handlebars AST to Handlebars.compile. You passed " + input);
throw new Exception('You must pass a string or Handlebars AST to Handlebars.compile. You passed ' + input);
}
options = options || {};
@@ -461,17 +467,17 @@ export function compile(input, options, env) {
}
// Template is only compiled on first use and cached after that point.
var ret = function(context, options) {
function ret(context, execOptions) {
if (!compiled) {
compiled = compileInput();
}
return compiled.call(this, context, options);
};
ret._setup = function(options) {
return compiled.call(this, context, execOptions);
}
ret._setup = function(setupOptions) {
if (!compiled) {
compiled = compileInput();
}
return compiled._setup(options);
return compiled._setup(setupOptions);
};
ret._child = function(i, data, blockParams, depths) {
if (!compiled) {
@@ -502,6 +508,6 @@ function transformLiteralToPath(sexpr) {
var literal = sexpr.path;
// Casting to string here to make false and 0 literal values play nicely with the rest
// of the system.
sexpr.path = new AST.PathExpression(false, 0, [literal.original+''], literal.original+'', literal.loc);
sexpr.path = new AST.PathExpression(false, 0, [literal.original + ''], literal.original + '', literal.loc);
}
}
+3 -3
View File
@@ -1,4 +1,4 @@
import Exception from "../exception";
import Exception from '../exception';
export function SourceLocation(source, locInfo) {
this.source = source;
@@ -23,7 +23,7 @@ export function id(token) {
export function stripFlags(open, close) {
return {
open: open.charAt(2) === '~',
close: close.charAt(close.length-3) === '~'
close: close.charAt(close.length - 3) === '~'
};
}
@@ -41,7 +41,7 @@ export function preparePath(data, parts, locInfo) {
depth = 0,
depthString = '';
for(var i=0,l=parts.length; i<l; i++) {
for (var i = 0, l = parts.length; i < l; i++) {
var part = parts[i].part,
// If we have [] syntax then we do not treat path references as operators,
// i.e. foo.[this] resolves to approximately context.foo['this']
+44 -40
View File
@@ -1,7 +1,7 @@
import { COMPILER_REVISION, REVISION_CHANGES } from "../base";
import Exception from "../exception";
import {isArray} from "../utils";
import CodeGen from "./code-gen";
import { COMPILER_REVISION, REVISION_CHANGES } from '../base';
import Exception from '../exception';
import {isArray} from '../utils';
import CodeGen from './code-gen';
function Literal(value) {
this.value = value;
@@ -14,7 +14,7 @@ JavaScriptCompiler.prototype = {
// alternative compiled forms for name lookup and buffering semantics
nameLookup: function(parent, name /* , type*/) {
if (JavaScriptCompiler.isValidJavaScriptVariableName(name)) {
return [parent, ".", name];
return [parent, '.', name];
} else {
return [parent, "['", name, "']"];
}
@@ -50,7 +50,7 @@ JavaScriptCompiler.prototype = {
},
initializeBuffer: function() {
return this.quotedString("");
return this.quotedString('');
},
// END PUBLIC API
@@ -169,8 +169,8 @@ JavaScriptCompiler.prototype = {
var varDeclarations = '';
var locals = this.stackVars.concat(this.registers.list);
if(locals.length > 0) {
varDeclarations += ", " + locals.join(", ");
if (locals.length > 0) {
varDeclarations += ', ' + locals.join(', ');
}
// Generate minimizer alias mappings
@@ -180,7 +180,7 @@ JavaScriptCompiler.prototype = {
// aliases will not be used, but this case is already being run on the client and
// we aren't concern about minimizing the template size.
var aliasCount = 0;
for (var alias in this.aliases) {
for (var alias in this.aliases) { // eslint-disable-line guard-for-in
var node = this.aliases[alias];
if (this.aliases.hasOwnProperty(alias) && node.children && node.referenceCount > 1) {
@@ -189,7 +189,7 @@ JavaScriptCompiler.prototype = {
}
}
var params = ["depth0", "helpers", "partials", "data"];
var params = ['depth0', 'helpers', 'partials', 'data'];
if (this.useBlockParams || this.useDepths) {
params.push('blockParams');
@@ -252,7 +252,7 @@ JavaScriptCompiler.prototype = {
this.source.push('return "";');
}
} else {
varDeclarations += ", buffer = " + (appendFirst ? '' : this.initializeBuffer());
varDeclarations += ', buffer = ' + (appendFirst ? '' : this.initializeBuffer());
if (bufferStart) {
bufferStart.prepend('return buffer + ');
@@ -446,6 +446,7 @@ JavaScriptCompiler.prototype = {
var len = parts.length;
for (; i < len; i++) {
/*eslint-disable no-loop-func */
this.replaceStack(function(current) {
var lookup = this.nameLookup(current, parts[i], type);
// We want to ensure that zero and false are handled properly if the context (falsy flag)
@@ -457,6 +458,7 @@ JavaScriptCompiler.prototype = {
return [' && ', lookup];
}
});
/*eslint-enable no-loop-func */
}
},
@@ -633,7 +635,7 @@ JavaScriptCompiler.prototype = {
'(', lookup,
(helper.paramsInit ? ['),(', helper.paramsInit] : []), '),',
'(typeof helper === ', this.aliasable('"function"'), ' ? ',
this.source.functionCall('helper','call', helper.callParams), ' : helper))'
this.source.functionCall('helper', 'call', helper.callParams), ' : helper))'
]);
},
@@ -728,9 +730,9 @@ JavaScriptCompiler.prototype = {
compileChildren: function(environment, options) {
var children = environment.children, child, compiler;
for(var i=0, l=children.length; i<l; i++) {
for (var i = 0, l = children.length; i < l; i++) {
child = children[i];
compiler = new this.compiler();
compiler = new this.compiler(); // eslint-disable-line new-cap
var index = this.matchExistingProgram(child);
@@ -777,7 +779,7 @@ JavaScriptCompiler.prototype = {
},
useRegister: function(name) {
if(!this.registers[name]) {
if (!this.registers[name]) {
this.registers[name] = true;
this.registers.list.push(name);
}
@@ -849,11 +851,11 @@ JavaScriptCompiler.prototype = {
incrStack: function() {
this.stackSlot++;
if(this.stackSlot > this.stackVars.length) { this.stackVars.push("stack" + this.stackSlot); }
if (this.stackSlot > this.stackVars.length) { this.stackVars.push('stack' + this.stackSlot); }
return this.topStackName();
},
topStackName: function() {
return "stack" + this.stackSlot;
return 'stack' + this.stackSlot;
},
flushInline: function() {
var inlineStack = this.inlineStack;
@@ -1019,33 +1021,35 @@ JavaScriptCompiler.prototype = {
};
var reservedWords = (
"break else new var" +
" case finally return void" +
" catch for switch while" +
" continue function this with" +
" default if throw" +
" delete in try" +
" do instanceof typeof" +
" abstract enum int short" +
" boolean export interface static" +
" byte extends long super" +
" char final native synchronized" +
" class float package throws" +
" const goto private transient" +
" debugger implements protected volatile" +
" double import public let yield await" +
" null true false"
).split(" ");
(function() {
var reservedWords = (
'break else new var' +
' case finally return void' +
' catch for switch while' +
' continue function this with' +
' default if throw' +
' delete in try' +
' do instanceof typeof' +
' abstract enum int short' +
' boolean export interface static' +
' byte extends long super' +
' char final native synchronized' +
' class float package throws' +
' const goto private transient' +
' debugger implements protected volatile' +
' double import public let yield await' +
' null true false'
).split(' ');
var compilerWords = JavaScriptCompiler.RESERVED_WORDS = {};
var compilerWords = JavaScriptCompiler.RESERVED_WORDS = {};
for(var i=0, l=reservedWords.length; i<l; i++) {
compilerWords[reservedWords[i]] = true;
}
for (var i = 0, l = reservedWords.length; i < l; i++) {
compilerWords[reservedWords[i]] = true;
}
}());
JavaScriptCompiler.isValidJavaScriptVariableName = function(name) {
return !JavaScriptCompiler.RESERVED_WORDS[name] && /^[a-zA-Z_$][0-9a-zA-Z_$]*$/.test(name);
return !JavaScriptCompiler.RESERVED_WORDS[name] && (/^[a-zA-Z_$][0-9a-zA-Z_$]*$/).test(name);
};
function strictLookup(requireTerminal, compiler, parts, type) {
+18 -16
View File
@@ -1,4 +1,5 @@
import Visitor from "./visitor";
/*eslint-disable new-cap */
import Visitor from './visitor';
export function print(ast) {
return new PrintVisitor().accept(ast);
@@ -11,13 +12,13 @@ export function PrintVisitor() {
PrintVisitor.prototype = new Visitor();
PrintVisitor.prototype.pad = function(string) {
var out = "";
var out = '';
for(var i=0,l=this.padding; i<l; i++) {
out = out + " ";
for (var i = 0, l = this.padding; i < l; i++) {
out = out + ' ';
}
out = out + string + "\n";
out = out + string + '\n';
return out;
};
@@ -28,14 +29,14 @@ PrintVisitor.prototype.Program = function(program) {
if (program.blockParams) {
var blockParams = 'BLOCK PARAMS: [';
for(i=0, l=program.blockParams.length; i<l; i++) {
for (i = 0, l = program.blockParams.length; i < l; i++) {
blockParams += ' ' + program.blockParams[i];
}
blockParams += ' ]';
out += this.pad(blockParams);
}
for(i=0, l=body.length; i<l; i++) {
for (i = 0, l = body.length; i < l; i++) {
out = out + this.accept(body[i]);
}
@@ -49,7 +50,7 @@ PrintVisitor.prototype.MustacheStatement = function(mustache) {
};
PrintVisitor.prototype.BlockStatement = function(block) {
var out = "";
var out = '';
out = out + this.pad('BLOCK:');
this.padding++;
@@ -75,7 +76,7 @@ PrintVisitor.prototype.BlockStatement = function(block) {
PrintVisitor.prototype.PartialStatement = function(partial) {
var content = 'PARTIAL:' + partial.name.original;
if(partial.params[0]) {
if (partial.params[0]) {
content += ' ' + this.accept(partial.params[0]);
}
if (partial.hash) {
@@ -95,15 +96,15 @@ PrintVisitor.prototype.CommentStatement = function(comment) {
PrintVisitor.prototype.SubExpression = function(sexpr) {
var params = sexpr.params, paramStrings = [], hash;
for(var i=0, l=params.length; i<l; i++) {
for (var i = 0, l = params.length; i < l; i++) {
paramStrings.push(this.accept(params[i]));
}
params = "[" + paramStrings.join(", ") + "]";
params = '[' + paramStrings.join(', ') + ']';
hash = sexpr.hash ? " " + this.accept(sexpr.hash) : "";
hash = sexpr.hash ? ' ' + this.accept(sexpr.hash) : '';
return this.accept(sexpr.path) + " " + params + hash;
return this.accept(sexpr.path) + ' ' + params + hash;
};
PrintVisitor.prototype.PathExpression = function(id) {
@@ -117,11 +118,11 @@ PrintVisitor.prototype.StringLiteral = function(string) {
};
PrintVisitor.prototype.NumberLiteral = function(number) {
return "NUMBER{" + number.value + "}";
return 'NUMBER{' + number.value + '}';
};
PrintVisitor.prototype.BooleanLiteral = function(bool) {
return "BOOLEAN{" + bool.value + "}";
return 'BOOLEAN{' + bool.value + '}';
};
PrintVisitor.prototype.UndefinedLiteral = function() {
@@ -136,7 +137,7 @@ PrintVisitor.prototype.Hash = function(hash) {
var pairs = hash.pairs;
var joinedPairs = [];
for (var i=0, l=pairs.length; i<l; i++) {
for (var i = 0, l = pairs.length; i < l; i++) {
joinedPairs.push(this.accept(pairs[i]));
}
@@ -145,3 +146,4 @@ PrintVisitor.prototype.Hash = function(hash) {
PrintVisitor.prototype.HashPair = function(pair) {
return pair.key + '=' + this.accept(pair.value);
};
/*eslint-enable new-cap */
+2 -2
View File
@@ -1,5 +1,5 @@
import Exception from "../exception";
import AST from "./ast";
import Exception from '../exception';
import AST from './ast';
function Visitor() {
this.parents = [];
+10 -13
View File
@@ -1,4 +1,4 @@
import Visitor from "./visitor";
import Visitor from './visitor';
function WhitespaceControl() {
}
@@ -38,7 +38,7 @@ WhitespaceControl.prototype.Program = function(program) {
// If we are on a standalone node, save the indent info for partials
if (current.type === 'PartialStatement') {
// Pull out the whitespace from the final line
current.indent = (/([ \t]+$)/).exec(body[i-1].original)[1];
current.indent = (/([ \t]+$)/).exec(body[i - 1].original)[1];
}
}
}
@@ -73,7 +73,7 @@ WhitespaceControl.prototype.BlockStatement = function(block) {
// Walk the inverse chain to find the last inverse that is actually in the chain.
while (lastInverse.chained) {
lastInverse = lastInverse.body[lastInverse.body.length-1].program;
lastInverse = lastInverse.body[lastInverse.body.length - 1].program;
}
}
@@ -108,14 +108,11 @@ WhitespaceControl.prototype.BlockStatement = function(block) {
// Find standalone else statments
if (isPrevWhitespace(program.body)
&& isNextWhitespace(firstInverse.body)) {
omitLeft(program.body);
omitRight(firstInverse.body);
}
} else {
if (block.closeStrip.open) {
omitLeft(program.body, null, true);
}
} else if (block.closeStrip.open) {
omitLeft(program.body, null, true);
}
return strip;
@@ -125,7 +122,7 @@ WhitespaceControl.prototype.MustacheStatement = function(mustache) {
return mustache.strip;
};
WhitespaceControl.prototype.PartialStatement =
WhitespaceControl.prototype.PartialStatement =
WhitespaceControl.prototype.CommentStatement = function(node) {
/* istanbul ignore next */
var strip = node.strip || {};
@@ -144,8 +141,8 @@ function isPrevWhitespace(body, i, isRoot) {
// Nodes that end with newlines are considered whitespace (but are special
// cased for strip operations)
var prev = body[i-1],
sibling = body[i-2];
var prev = body[i - 1],
sibling = body[i - 2];
if (!prev) {
return isRoot;
}
@@ -159,8 +156,8 @@ function isNextWhitespace(body, i, isRoot) {
i = -1;
}
var next = body[i+1],
sibling = body[i+2];
var next = body[i + 1],
sibling = body[i + 2];
if (!next) {
return isRoot;
}
+30 -30
View File
@@ -1,6 +1,6 @@
module Utils from "./utils";
import Exception from "./exception";
import { COMPILER_REVISION, REVISION_CHANGES, createFrame } from "./base";
import * as Utils from './utils';
import Exception from './exception';
import { COMPILER_REVISION, REVISION_CHANGES, createFrame } from './base';
export function checkRevision(compilerInfo) {
var compilerRevision = compilerInfo && compilerInfo[0] || 1,
@@ -10,12 +10,12 @@ export function checkRevision(compilerInfo) {
if (compilerRevision < currentRevision) {
var runtimeVersions = REVISION_CHANGES[currentRevision],
compilerVersions = REVISION_CHANGES[compilerRevision];
throw new Exception("Template was precompiled with an older version of Handlebars than the current runtime. "+
"Please update your precompiler to a newer version ("+runtimeVersions+") or downgrade your runtime to an older version ("+compilerVersions+").");
throw new Exception('Template was precompiled with an older version of Handlebars than the current runtime. ' +
'Please update your precompiler to a newer version (' + runtimeVersions + ') or downgrade your runtime to an older version (' + compilerVersions + ').');
} else {
// Use the embedded version info since the runtime doesn't know about this revision yet
throw new Exception("Template was precompiled with a newer version of Handlebars than the current runtime. "+
"Please update your runtime to a newer version ("+compilerInfo[1]+").");
throw new Exception('Template was precompiled with a newer version of Handlebars than the current runtime. ' +
'Please update your runtime to a newer version (' + compilerInfo[1] + ').');
}
}
}
@@ -25,7 +25,7 @@ export function checkRevision(compilerInfo) {
export function template(templateSpec, env) {
/* istanbul ignore next */
if (!env) {
throw new Exception("No environment passed to template");
throw new Exception('No environment passed to template');
}
if (!templateSpec || !templateSpec.main) {
throw new Exception('Unknown template object: ' + typeof templateSpec);
@@ -35,7 +35,7 @@ export function template(templateSpec, env) {
// for external users to override these as psuedo-supported APIs.
env.VM.checkRevision(templateSpec.compiler);
var invokePartialWrapper = function(partial, context, options) {
function invokePartialWrapper(partial, context, options) {
if (options.hash) {
context = Utils.extend({}, context, options.hash);
}
@@ -61,9 +61,9 @@ export function template(templateSpec, env) {
}
return result;
} else {
throw new Exception("The partial " + options.name + " could not be compiled when running in runtime-only mode");
throw new Exception('The partial ' + options.name + ' could not be compiled when running in runtime-only mode');
}
};
}
// Just add water
var container = {
@@ -97,34 +97,34 @@ export function template(templateSpec, env) {
var programWrapper = this.programs[i],
fn = this.fn(i);
if (data || depths || blockParams || declaredBlockParams) {
programWrapper = program(this, i, fn, data, declaredBlockParams, blockParams, depths);
programWrapper = wrapProgram(this, i, fn, data, declaredBlockParams, blockParams, depths);
} else if (!programWrapper) {
programWrapper = this.programs[i] = program(this, i, fn);
programWrapper = this.programs[i] = wrapProgram(this, i, fn);
}
return programWrapper;
},
data: function(data, depth) {
while (data && depth--) {
data = data._parent;
data: function(value, depth) {
while (value && depth--) {
value = value._parent;
}
return data;
return value;
},
merge: function(param, common) {
var ret = param || common;
var obj = param || common;
if (param && common && (param !== common)) {
ret = Utils.extend({}, common, param);
obj = Utils.extend({}, common, param);
}
return ret;
return obj;
},
noop: env.VM.noop,
compilerInfo: templateSpec.compiler
};
var ret = function(context, options) {
function ret(context, options) {
options = options || {};
var data = options.data;
@@ -139,7 +139,7 @@ export function template(templateSpec, env) {
}
return templateSpec.main.call(container, context, container.helpers, container.partials, data, blockParams, depths);
};
}
ret.isTop = true;
ret._setup = function(options) {
@@ -163,13 +163,13 @@ export function template(templateSpec, env) {
throw new Exception('must pass parent depths');
}
return program(container, i, templateSpec[i], data, 0, blockParams, depths);
return wrapProgram(container, i, templateSpec[i], data, 0, blockParams, depths);
};
return ret;
}
export function program(container, i, fn, data, declaredBlockParams, blockParams, depths) {
var prog = function(context, options) {
export function wrapProgram(container, i, fn, data, declaredBlockParams, blockParams, depths) {
function prog(context, options) {
options = options || {};
return fn.call(container,
@@ -178,7 +178,7 @@ export function program(container, i, fn, data, declaredBlockParams, blockParams
options.data || data,
blockParams && [options.blockParams].concat(blockParams),
depths && [context].concat(depths));
};
}
prog.program = i;
prog.depth = depths ? depths.length : 0;
prog.blockParams = declaredBlockParams || 0;
@@ -199,14 +199,14 @@ export function resolvePartial(partial, context, options) {
export function invokePartial(partial, context, options) {
options.partial = true;
if(partial === undefined) {
throw new Exception("The partial " + options.name + " could not be found");
} else if(partial instanceof Function) {
if (partial === undefined) {
throw new Exception('The partial ' + options.name + ' could not be found');
} else if (partial instanceof Function) {
return partial(context, options);
}
}
export function noop() { return ""; }
export function noop() { return ''; }
function initData(context, data) {
if (!data || !('root' in data)) {
+1 -1
View File
@@ -4,7 +4,7 @@ function SafeString(string) {
}
SafeString.prototype.toString = SafeString.prototype.toHTML = function() {
return "" + this.string;
return '' + this.string;
};
export default SafeString;
+8 -6
View File
@@ -1,11 +1,11 @@
/*jshint -W004 */
var escape = {
"&": "&amp;",
"<": "&lt;",
">": "&gt;",
'"': "&quot;",
"'": "&#x27;",
"`": "&#x60;"
'&': '&amp;',
'<': '&lt;',
'>': '&gt;',
'"': '&quot;',
"'": '&#x27;',
'`': '&#x60;'
};
var badChars = /[&<>"'`]/g;
@@ -31,6 +31,7 @@ export var toString = Object.prototype.toString;
// Sourced from lodash
// https://github.com/bestiejs/lodash/blob/master/LICENSE.txt
/*eslint-disable func-style */
var isFunction = function(value) {
return typeof value === 'function';
};
@@ -42,6 +43,7 @@ if (isFunction(/x/)) {
};
}
export var isFunction;
/*eslint-enable func-style */
/* istanbul ignore next */
export var isArray = Array.isArray || function(value) {
+8 -8
View File
@@ -3,7 +3,7 @@
// var local = handlebars.create();
var handlebars = require('../dist/cjs/handlebars')["default"];
var handlebars = require('../dist/cjs/handlebars')['default'];
var printer = require('../dist/cjs/handlebars/compiler/printer');
handlebars.PrintVisitor = printer.PrintVisitor;
@@ -12,13 +12,13 @@ handlebars.print = printer.print;
module.exports = handlebars;
// Publish a Node.js require() handler for .handlebars and .hbs files
function extension(module, filename) {
var fs = require('fs');
var templateString = fs.readFileSync(filename, 'utf8');
module.exports = handlebars.compile(templateString);
}
/* istanbul ignore else */
if (typeof require !== 'undefined' && require.extensions) {
var extension = function(module, filename) {
var fs = require("fs");
var templateString = fs.readFileSync(filename, "utf8");
module.exports = handlebars.compile(templateString);
};
require.extensions[".handlebars"] = extension;
require.extensions[".hbs"] = extension;
require.extensions['.handlebars'] = extension;
require.extensions['.hbs'] = extension;
}
+9 -9
View File
@@ -1,4 +1,4 @@
/*eslint-disable no-console */
var fs = require('fs'),
Handlebars = require('./index'),
basename = require('path').basename,
@@ -70,10 +70,10 @@ module.exports.cli = function(opts) {
stat = fs.statSync(path);
if (stat.isDirectory()) {
fs.readdirSync(template).map(function(file) {
var path = template + '/' + file;
var childPath = template + '/' + file;
if (extension.test(path) || fs.statSync(path).isDirectory()) {
processTemplate(path, root || template);
if (extension.test(childPath) || fs.statSync(childPath).isDirectory()) {
processTemplate(childPath, root || template);
}
});
} else {
@@ -99,7 +99,7 @@ module.exports.cli = function(opts) {
if (!root) {
template = basename(template);
} else if (template.indexOf(root) === 0) {
template = template.substring(root.length+1);
template = template.substring(root.length + 1);
}
template = template.replace(extension, '');
@@ -114,12 +114,12 @@ module.exports.cli = function(opts) {
if (opts.simple) {
output.add([precompiled, '\n']);
} else if (opts.partial) {
if(opts.amd && (opts.templates.length == 1 && !fs.statSync(opts.templates[0]).isDirectory())) {
if (opts.amd && (opts.templates.length == 1 && !fs.statSync(opts.templates[0]).isDirectory())) {
output.add('return ');
}
output.add(['Handlebars.partials[\'', template, '\'] = template(', precompiled, ');\n']);
} else {
if(opts.amd && (opts.templates.length == 1 && !fs.statSync(opts.templates[0]).isDirectory())) {
if (opts.amd && (opts.templates.length == 1 && !fs.statSync(opts.templates[0]).isDirectory())) {
output.add('return ');
}
output.add(['templates[\'', template, '\'] = template(', precompiled, ');\n']);
@@ -134,8 +134,8 @@ module.exports.cli = function(opts) {
// Output the content
if (!opts.simple) {
if (opts.amd) {
if(opts.templates.length > 1 || (opts.templates.length == 1 && fs.statSync(opts.templates[0]).isDirectory())) {
if(opts.partial){
if (opts.templates.length > 1 || (opts.templates.length == 1 && fs.statSync(opts.templates[0]).isDirectory())) {
if (opts.partial) {
output.add('return Handlebars.partials;\n');
} else {
output.add('return templates;\n');
+5 -1
View File
@@ -30,11 +30,13 @@
"devDependencies": {
"async": "^0.9.0",
"aws-sdk": "~1.5.0",
"babel-loader": "^5.0.0",
"babel-runtime": "^5.1.10",
"benchmark": "~1.0",
"dustjs-linkedin": "^2.0.2",
"eco": "~1.1.0-rc-3",
"es6-module-packager": "^2.0.0",
"grunt": "~0.4.1",
"grunt-babel": "^5.0.0",
"grunt-cli": "~0.1.10",
"grunt-contrib-clean": "0.x",
"grunt-contrib-concat": "0.x",
@@ -44,7 +46,9 @@
"grunt-contrib-requirejs": "0.x",
"grunt-contrib-uglify": "0.x",
"grunt-contrib-watch": "0.x",
"grunt-eslint": "^11.0.0",
"grunt-saucelabs": "8.x",
"grunt-webpack": "^1.0.8",
"istanbul": "^0.3.0",
"jison": "~0.3.0",
"keen.io": "0.0.3",
+34
View File
@@ -0,0 +1,34 @@
{
"globals": {
"CompilerContext": true,
"Handlebars": true,
"handlebarsEnv": true,
"shouldCompileTo": true,
"shouldCompileToWithPartials": true,
"shouldThrow": true,
"compileWithPartials": true,
"console": true,
"require": true,
"suite": true,
"equal": true,
"equals": true,
"test": true,
"testBoth": true,
"raises": true,
"deepEqual": true,
"start": true,
"stop": true,
"ok": true,
"strictEqual": true,
"define": true
},
"env": {
"mocha": true
},
"rules": {
// Disabling for tests, for now.
"no-path-concat": 0
}
}
+38 -41
View File
@@ -1,4 +1,3 @@
/*global Handlebars, handlebarsEnv, shouldThrow */
describe('ast', function() {
if (!Handlebars.AST) {
return;
@@ -15,7 +14,7 @@ describe('ast', function() {
}
};
function testLocationInfoStorage(node){
function testLocationInfoStorage(node) {
equals(node.loc.start.line, 1);
equals(node.loc.start.column, 1);
equals(node.loc.end.line, 1);
@@ -24,9 +23,7 @@ describe('ast', function() {
describe('MustacheStatement', function() {
it('should store args', function() {
var id = {isSimple: true},
hash = {},
mustache = new handlebarsEnv.AST.MustacheStatement({}, null, null, true, {}, LOCATION_INFO);
var mustache = new handlebarsEnv.AST.MustacheStatement({}, null, null, true, {}, LOCATION_INFO);
equals(mustache.type, 'MustacheStatement');
equals(mustache.escaped, true);
testLocationInfoStorage(mustache);
@@ -35,11 +32,11 @@ describe('ast', function() {
describe('BlockStatement', function() {
it('should throw on mustache mismatch', function() {
shouldThrow(function() {
handlebarsEnv.parse("\n {{#foo}}{{/bar}}");
handlebarsEnv.parse('\n {{#foo}}{{/bar}}');
}, Handlebars.Exception, "foo doesn't match bar - 2:5");
});
it('stores location info', function(){
it('stores location info', function() {
var mustacheNode = new handlebarsEnv.AST.MustacheStatement([{ original: 'foo'}], null, null, false, {});
var block = new handlebarsEnv.AST.BlockStatement(
mustacheNode,
@@ -54,114 +51,114 @@ describe('ast', function() {
});
});
describe('PathExpression', function() {
it('stores location info', function(){
it('stores location info', function() {
var idNode = new handlebarsEnv.AST.PathExpression(false, 0, [], 'foo', LOCATION_INFO);
testLocationInfoStorage(idNode);
});
});
describe('Hash', function(){
it('stores location info', function(){
describe('Hash', function() {
it('stores location info', function() {
var hash = new handlebarsEnv.AST.Hash([], LOCATION_INFO);
testLocationInfoStorage(hash);
});
});
describe('ContentStatement', function(){
it('stores location info', function(){
var content = new handlebarsEnv.AST.ContentStatement("HI", LOCATION_INFO);
describe('ContentStatement', function() {
it('stores location info', function() {
var content = new handlebarsEnv.AST.ContentStatement('HI', LOCATION_INFO);
testLocationInfoStorage(content);
});
});
describe('CommentStatement', function(){
it('stores location info', function(){
var comment = new handlebarsEnv.AST.CommentStatement("HI", {}, LOCATION_INFO);
describe('CommentStatement', function() {
it('stores location info', function() {
var comment = new handlebarsEnv.AST.CommentStatement('HI', {}, LOCATION_INFO);
testLocationInfoStorage(comment);
});
});
describe('NumberLiteral', function(){
it('stores location info', function(){
var integer = new handlebarsEnv.AST.NumberLiteral("6", LOCATION_INFO);
describe('NumberLiteral', function() {
it('stores location info', function() {
var integer = new handlebarsEnv.AST.NumberLiteral('6', LOCATION_INFO);
testLocationInfoStorage(integer);
});
});
describe('StringLiteral', function(){
it('stores location info', function(){
var string = new handlebarsEnv.AST.StringLiteral("6", LOCATION_INFO);
describe('StringLiteral', function() {
it('stores location info', function() {
var string = new handlebarsEnv.AST.StringLiteral('6', LOCATION_INFO);
testLocationInfoStorage(string);
});
});
describe('BooleanLiteral', function(){
it('stores location info', function(){
var bool = new handlebarsEnv.AST.BooleanLiteral("true", LOCATION_INFO);
describe('BooleanLiteral', function() {
it('stores location info', function() {
var bool = new handlebarsEnv.AST.BooleanLiteral('true', LOCATION_INFO);
testLocationInfoStorage(bool);
});
});
describe('PartialStatement', function(){
it('stores location info', function(){
describe('PartialStatement', function() {
it('stores location info', function() {
var pn = new handlebarsEnv.AST.PartialStatement('so_partial', [], {}, {}, LOCATION_INFO);
testLocationInfoStorage(pn);
});
});
describe('Program', function(){
it('storing location info', function(){
describe('Program', function() {
it('storing location info', function() {
var pn = new handlebarsEnv.AST.Program([], null, {}, LOCATION_INFO);
testLocationInfoStorage(pn);
});
});
describe("Line Numbers", function(){
describe('Line Numbers', function() {
var ast, body;
function testColumns(node, firstLine, lastLine, firstColumn, lastColumn){
function testColumns(node, firstLine, lastLine, firstColumn, lastColumn) {
equals(node.loc.start.line, firstLine);
equals(node.loc.start.column, firstColumn);
equals(node.loc.end.line, lastLine);
equals(node.loc.end.column, lastColumn);
}
ast = Handlebars.parse("line 1 {{line1Token}}\n line 2 {{line2token}}\n line 3 {{#blockHelperOnLine3}}\nline 4{{line4token}}\n" +
"line5{{else}}\n{{line6Token}}\n{{/blockHelperOnLine3}}");
ast = Handlebars.parse('line 1 {{line1Token}}\n line 2 {{line2token}}\n line 3 {{#blockHelperOnLine3}}\nline 4{{line4token}}\n' +
'line5{{else}}\n{{line6Token}}\n{{/blockHelperOnLine3}}');
body = ast.body;
it('gets ContentNode line numbers', function(){
it('gets ContentNode line numbers', function() {
var contentNode = body[0];
testColumns(contentNode, 1, 1, 0, 7);
});
it('gets MustacheStatement line numbers', function(){
it('gets MustacheStatement line numbers', function() {
var mustacheNode = body[1];
testColumns(mustacheNode, 1, 1, 7, 21);
});
it('gets line numbers correct when newlines appear', function(){
it('gets line numbers correct when newlines appear', function() {
testColumns(body[2], 1, 2, 21, 8);
});
it('gets MustacheStatement line numbers correct across newlines', function(){
it('gets MustacheStatement line numbers correct across newlines', function() {
var secondMustacheStatement = body[3];
testColumns(secondMustacheStatement, 2, 2, 8, 22);
});
it('gets the block helper information correct', function(){
it('gets the block helper information correct', function() {
var blockHelperNode = body[5];
testColumns(blockHelperNode, 3, 7, 8, 23);
});
it('correctly records the line numbers the program of a block helper', function(){
it('correctly records the line numbers the program of a block helper', function() {
var blockHelperNode = body[5],
program = blockHelperNode.program;
testColumns(program, 3, 5, 8, 5);
});
it('correctly records the line numbers of an inverse of a block helper', function(){
it('correctly records the line numbers of an inverse of a block helper', function() {
var blockHelperNode = body[5],
inverse = blockHelperNode.inverse;
@@ -169,7 +166,7 @@ describe('ast', function() {
});
});
describe('standalone flags', function(){
describe('standalone flags', function() {
describe('mustache', function() {
it('does not mark mustaches as standalone', function() {
var ast = Handlebars.parse(' {{comment}} ');
+119 -118
View File
@@ -1,38 +1,37 @@
/*global CompilerContext, Handlebars, beforeEach, shouldCompileTo, shouldThrow */
global.handlebarsEnv = null;
beforeEach(function() {
global.handlebarsEnv = Handlebars.create();
});
describe("basic context", function() {
it("most basic", function() {
shouldCompileTo("{{foo}}", { foo: "foo" }, "foo");
describe('basic context', function() {
it('most basic', function() {
shouldCompileTo('{{foo}}', { foo: 'foo' }, 'foo');
});
it("escaping", function() {
shouldCompileTo("\\{{foo}}", { foo: "food" }, "{{foo}}");
shouldCompileTo("content \\{{foo}}", { foo: "food" }, "content {{foo}}");
shouldCompileTo("\\\\{{foo}}", { foo: "food" }, "\\food");
shouldCompileTo("content \\\\{{foo}}", { foo: "food" }, "content \\food");
shouldCompileTo("\\\\ {{foo}}", { foo: "food" }, "\\\\ food");
it('escaping', function() {
shouldCompileTo('\\{{foo}}', { foo: 'food' }, '{{foo}}');
shouldCompileTo('content \\{{foo}}', { foo: 'food' }, 'content {{foo}}');
shouldCompileTo('\\\\{{foo}}', { foo: 'food' }, '\\food');
shouldCompileTo('content \\\\{{foo}}', { foo: 'food' }, 'content \\food');
shouldCompileTo('\\\\ {{foo}}', { foo: 'food' }, '\\\\ food');
});
it("compiling with a basic context", function() {
shouldCompileTo("Goodbye\n{{cruel}}\n{{world}}!", {cruel: "cruel", world: "world"}, "Goodbye\ncruel\nworld!",
"It works if all the required keys are provided");
it('compiling with a basic context', function() {
shouldCompileTo('Goodbye\n{{cruel}}\n{{world}}!', {cruel: 'cruel', world: 'world'}, 'Goodbye\ncruel\nworld!',
'It works if all the required keys are provided');
});
it("compiling with an undefined context", function() {
shouldCompileTo("Goodbye\n{{cruel}}\n{{world.bar}}!", undefined, "Goodbye\n\n!");
it('compiling with an undefined context', function() {
shouldCompileTo('Goodbye\n{{cruel}}\n{{world.bar}}!', undefined, 'Goodbye\n\n!');
shouldCompileTo("{{#unless foo}}Goodbye{{../test}}{{test2}}{{/unless}}", undefined, "Goodbye");
shouldCompileTo('{{#unless foo}}Goodbye{{../test}}{{test2}}{{/unless}}', undefined, 'Goodbye');
});
it("comments", function() {
shouldCompileTo("{{! Goodbye}}Goodbye\n{{cruel}}\n{{world}}!",
{cruel: "cruel", world: "world"}, "Goodbye\ncruel\nworld!",
"comments are ignored");
it('comments', function() {
shouldCompileTo('{{! Goodbye}}Goodbye\n{{cruel}}\n{{world}}!',
{cruel: 'cruel', world: 'world'}, 'Goodbye\ncruel\nworld!',
'comments are ignored');
shouldCompileTo(' {{~! comment ~}} blah', {}, 'blah');
shouldCompileTo(' {{~!-- long-comment --~}} blah', {}, 'blah');
@@ -42,28 +41,30 @@ describe("basic context", function() {
shouldCompileTo(' {{~!-- long-comment --}} blah', {}, ' blah');
});
it("boolean", function() {
var string = "{{#goodbye}}GOODBYE {{/goodbye}}cruel {{world}}!";
shouldCompileTo(string, {goodbye: true, world: "world"}, "GOODBYE cruel world!",
"booleans show the contents when true");
it('boolean', function() {
var string = '{{#goodbye}}GOODBYE {{/goodbye}}cruel {{world}}!';
shouldCompileTo(string, {goodbye: true, world: 'world'}, 'GOODBYE cruel world!',
'booleans show the contents when true');
shouldCompileTo(string, {goodbye: false, world: "world"}, "cruel world!",
"booleans do not show the contents when false");
shouldCompileTo(string, {goodbye: false, world: 'world'}, 'cruel world!',
'booleans do not show the contents when false');
});
it("zeros", function() {
shouldCompileTo("num1: {{num1}}, num2: {{num2}}", {num1: 42, num2: 0},
"num1: 42, num2: 0");
shouldCompileTo("num: {{.}}", 0, "num: 0");
shouldCompileTo("num: {{num1/num2}}", {num1: {num2: 0}}, "num: 0");
it('zeros', function() {
shouldCompileTo('num1: {{num1}}, num2: {{num2}}', {num1: 42, num2: 0},
'num1: 42, num2: 0');
shouldCompileTo('num: {{.}}', 0, 'num: 0');
shouldCompileTo('num: {{num1/num2}}', {num1: {num2: 0}}, 'num: 0');
});
it('false', function() {
/*eslint-disable no-new-wrappers */
shouldCompileTo('val1: {{val1}}, val2: {{val2}}', {val1: false, val2: new Boolean(false)}, 'val1: false, val2: false');
shouldCompileTo('val: {{.}}', false, 'val: false');
shouldCompileTo('val: {{val1/val2}}', {val1: {val2: false}}, 'val: false');
shouldCompileTo('val1: {{{val1}}}, val2: {{{val2}}}', {val1: false, val2: new Boolean(false)}, 'val1: false, val2: false');
shouldCompileTo('val: {{{val1/val2}}}', {val1: {val2: false}}, 'val: false');
/*eslint-enable */
});
it('should handle undefined and null', function() {
@@ -90,143 +91,143 @@ describe("basic context", function() {
'null!');
});
it("newlines", function() {
it('newlines', function() {
shouldCompileTo("Alan's\nTest", {}, "Alan's\nTest");
shouldCompileTo("Alan's\rTest", {}, "Alan's\rTest");
});
it("escaping text", function() {
it('escaping text', function() {
shouldCompileTo("Awesome's", {}, "Awesome's", "text is escaped so that it doesn't get caught on single quotes");
shouldCompileTo("Awesome\\", {}, "Awesome\\", "text is escaped so that the closing quote can't be ignored");
shouldCompileTo("Awesome\\\\ foo", {}, "Awesome\\\\ foo", "text is escaped so that it doesn't mess up backslashes");
shouldCompileTo("Awesome {{foo}}", {foo: '\\'}, "Awesome \\", "text is escaped so that it doesn't mess up backslashes");
shouldCompileTo(' " " ', {}, ' " " ', "double quotes never produce invalid javascript");
shouldCompileTo('Awesome\\', {}, 'Awesome\\', "text is escaped so that the closing quote can't be ignored");
shouldCompileTo('Awesome\\\\ foo', {}, 'Awesome\\\\ foo', "text is escaped so that it doesn't mess up backslashes");
shouldCompileTo('Awesome {{foo}}', {foo: '\\'}, 'Awesome \\', "text is escaped so that it doesn't mess up backslashes");
shouldCompileTo(" ' ' ", {}, " ' ' ", 'double quotes never produce invalid javascript');
});
it("escaping expressions", function() {
shouldCompileTo("{{{awesome}}}", {awesome: "&\"\\<>"}, '&\"\\<>',
it('escaping expressions', function() {
shouldCompileTo('{{{awesome}}}', {awesome: '&\'\\<>'}, '&\'\\<>',
"expressions with 3 handlebars aren't escaped");
shouldCompileTo("{{&awesome}}", {awesome: "&\"\\<>"}, '&\"\\<>',
shouldCompileTo('{{&awesome}}', {awesome: '&\'\\<>'}, '&\'\\<>',
"expressions with {{& handlebars aren't escaped");
shouldCompileTo("{{awesome}}", {awesome: "&\"'`\\<>"}, '&amp;&quot;&#x27;&#x60;\\&lt;&gt;',
"by default expressions should be escaped");
shouldCompileTo('{{awesome}}', {awesome: "&\"'`\\<>"}, '&amp;&quot;&#x27;&#x60;\\&lt;&gt;',
'by default expressions should be escaped');
shouldCompileTo("{{awesome}}", {awesome: "Escaped, <b> looks like: &lt;b&gt;"}, 'Escaped, &lt;b&gt; looks like: &amp;lt;b&amp;gt;',
"escaping should properly handle amperstands");
shouldCompileTo('{{awesome}}', {awesome: 'Escaped, <b> looks like: &lt;b&gt;'}, 'Escaped, &lt;b&gt; looks like: &amp;lt;b&amp;gt;',
'escaping should properly handle amperstands');
});
it("functions returning safestrings shouldn't be escaped", function() {
var hash = {awesome: function() { return new Handlebars.SafeString("&\"\\<>"); }};
shouldCompileTo("{{awesome}}", hash, '&\"\\<>',
var hash = {awesome: function() { return new Handlebars.SafeString('&\'\\<>'); }};
shouldCompileTo('{{awesome}}', hash, '&\'\\<>',
"functions returning safestrings aren't escaped");
});
it("functions", function() {
shouldCompileTo("{{awesome}}", {awesome: function() { return "Awesome"; }}, "Awesome",
"functions are called and render their output");
shouldCompileTo("{{awesome}}", {awesome: function() { return this.more; }, more: "More awesome"}, "More awesome",
"functions are bound to the context");
it('functions', function() {
shouldCompileTo('{{awesome}}', {awesome: function() { return 'Awesome'; }}, 'Awesome',
'functions are called and render their output');
shouldCompileTo('{{awesome}}', {awesome: function() { return this.more; }, more: 'More awesome'}, 'More awesome',
'functions are bound to the context');
});
it("functions with context argument", function() {
shouldCompileTo("{{awesome frank}}",
it('functions with context argument', function() {
shouldCompileTo('{{awesome frank}}',
{awesome: function(context) { return context; },
frank: "Frank"},
"Frank", "functions are called with context arguments");
frank: 'Frank'},
'Frank', 'functions are called with context arguments');
});
it("pathed functions with context argument", function() {
shouldCompileTo("{{bar.awesome frank}}",
it('pathed functions with context argument', function() {
shouldCompileTo('{{bar.awesome frank}}',
{bar: {awesome: function(context) { return context; }},
frank: "Frank"},
"Frank", "functions are called with context arguments");
frank: 'Frank'},
'Frank', 'functions are called with context arguments');
});
it("depthed functions with context argument", function() {
shouldCompileTo("{{#with frank}}{{../awesome .}}{{/with}}",
it('depthed functions with context argument', function() {
shouldCompileTo('{{#with frank}}{{../awesome .}}{{/with}}',
{awesome: function(context) { return context; },
frank: "Frank"},
"Frank", "functions are called with context arguments");
frank: 'Frank'},
'Frank', 'functions are called with context arguments');
});
it("block functions with context argument", function() {
shouldCompileTo("{{#awesome 1}}inner {{.}}{{/awesome}}",
it('block functions with context argument', function() {
shouldCompileTo('{{#awesome 1}}inner {{.}}{{/awesome}}',
{awesome: function(context, options) { return options.fn(context); }},
"inner 1", "block functions are called with context and options");
'inner 1', 'block functions are called with context and options');
});
it("depthed block functions with context argument", function() {
shouldCompileTo("{{#with value}}{{#../awesome 1}}inner {{.}}{{/../awesome}}{{/with}}",
it('depthed block functions with context argument', function() {
shouldCompileTo('{{#with value}}{{#../awesome 1}}inner {{.}}{{/../awesome}}{{/with}}',
{value: true, awesome: function(context, options) { return options.fn(context); }},
"inner 1", "block functions are called with context and options");
'inner 1', 'block functions are called with context and options');
});
it("block functions without context argument", function() {
shouldCompileTo("{{#awesome}}inner{{/awesome}}",
it('block functions without context argument', function() {
shouldCompileTo('{{#awesome}}inner{{/awesome}}',
{awesome: function(options) { return options.fn(this); }},
"inner", "block functions are called with options");
'inner', 'block functions are called with options');
});
it("pathed block functions without context argument", function() {
shouldCompileTo("{{#foo.awesome}}inner{{/foo.awesome}}",
it('pathed block functions without context argument', function() {
shouldCompileTo('{{#foo.awesome}}inner{{/foo.awesome}}',
{foo: {awesome: function() { return this; }}},
"inner", "block functions are called with options");
'inner', 'block functions are called with options');
});
it("depthed block functions without context argument", function() {
shouldCompileTo("{{#with value}}{{#../awesome}}inner{{/../awesome}}{{/with}}",
it('depthed block functions without context argument', function() {
shouldCompileTo('{{#with value}}{{#../awesome}}inner{{/../awesome}}{{/with}}',
{value: true, awesome: function() { return this; }},
"inner", "block functions are called with options");
'inner', 'block functions are called with options');
});
it("paths with hyphens", function() {
shouldCompileTo("{{foo-bar}}", {"foo-bar": "baz"}, "baz", "Paths can contain hyphens (-)");
shouldCompileTo("{{foo.foo-bar}}", {foo: {"foo-bar": "baz"}}, "baz", "Paths can contain hyphens (-)");
shouldCompileTo("{{foo/foo-bar}}", {foo: {"foo-bar": "baz"}}, "baz", "Paths can contain hyphens (-)");
it('paths with hyphens', function() {
shouldCompileTo('{{foo-bar}}', {'foo-bar': 'baz'}, 'baz', 'Paths can contain hyphens (-)');
shouldCompileTo('{{foo.foo-bar}}', {foo: {'foo-bar': 'baz'}}, 'baz', 'Paths can contain hyphens (-)');
shouldCompileTo('{{foo/foo-bar}}', {foo: {'foo-bar': 'baz'}}, 'baz', 'Paths can contain hyphens (-)');
});
it("nested paths", function() {
shouldCompileTo("Goodbye {{alan/expression}} world!", {alan: {expression: "beautiful"}},
"Goodbye beautiful world!", "Nested paths access nested objects");
it('nested paths', function() {
shouldCompileTo('Goodbye {{alan/expression}} world!', {alan: {expression: 'beautiful'}},
'Goodbye beautiful world!', 'Nested paths access nested objects');
});
it("nested paths with empty string value", function() {
shouldCompileTo("Goodbye {{alan/expression}} world!", {alan: {expression: ""}},
"Goodbye world!", "Nested paths access nested objects with empty string");
it('nested paths with empty string value', function() {
shouldCompileTo('Goodbye {{alan/expression}} world!', {alan: {expression: ''}},
'Goodbye world!', 'Nested paths access nested objects with empty string');
});
it("literal paths", function() {
shouldCompileTo("Goodbye {{[@alan]/expression}} world!", {"@alan": {expression: "beautiful"}},
"Goodbye beautiful world!", "Literal paths can be used");
shouldCompileTo("Goodbye {{[foo bar]/expression}} world!", {"foo bar": {expression: "beautiful"}},
"Goodbye beautiful world!", "Literal paths can be used");
it('literal paths', function() {
shouldCompileTo('Goodbye {{[@alan]/expression}} world!', {'@alan': {expression: 'beautiful'}},
'Goodbye beautiful world!', 'Literal paths can be used');
shouldCompileTo('Goodbye {{[foo bar]/expression}} world!', {'foo bar': {expression: 'beautiful'}},
'Goodbye beautiful world!', 'Literal paths can be used');
});
it('literal references', function() {
shouldCompileTo("Goodbye {{[foo bar]}} world!", {"foo bar": "beautiful"},
"Goodbye beautiful world!", "Literal paths can be used");
shouldCompileTo('Goodbye {{[foo bar]}} world!', {'foo bar': 'beautiful'},
'Goodbye beautiful world!', 'Literal paths can be used');
});
it("that current context path ({{.}}) doesn't hit helpers", function() {
shouldCompileTo("test: {{.}}", [null, {helper: "awesome"}], "test: ");
shouldCompileTo('test: {{.}}', [null, {helper: 'awesome'}], 'test: ');
});
it("complex but empty paths", function() {
shouldCompileTo("{{person/name}}", {person: {name: null}}, "");
shouldCompileTo("{{person/name}}", {person: {}}, "");
it('complex but empty paths', function() {
shouldCompileTo('{{person/name}}', {person: {name: null}}, '');
shouldCompileTo('{{person/name}}', {person: {}}, '');
});
it("this keyword in paths", function() {
var string = "{{#goodbyes}}{{this}}{{/goodbyes}}";
var hash = {goodbyes: ["goodbye", "Goodbye", "GOODBYE"]};
shouldCompileTo(string, hash, "goodbyeGoodbyeGOODBYE",
"This keyword in paths evaluates to current context");
it('this keyword in paths', function() {
var string = '{{#goodbyes}}{{this}}{{/goodbyes}}';
var hash = {goodbyes: ['goodbye', 'Goodbye', 'GOODBYE']};
shouldCompileTo(string, hash, 'goodbyeGoodbyeGOODBYE',
'This keyword in paths evaluates to current context');
string = "{{#hellos}}{{this/text}}{{/hellos}}";
hash = {hellos: [{text: "hello"}, {text: "Hello"}, {text: "HELLO"}]};
shouldCompileTo(string, hash, "helloHelloHELLO", "This keyword evaluates in more complex paths");
string = '{{#hellos}}{{this/text}}{{/hellos}}';
hash = {hellos: [{text: 'hello'}, {text: 'Hello'}, {text: 'HELLO'}]};
shouldCompileTo(string, hash, 'helloHelloHELLO', 'This keyword evaluates in more complex paths');
});
it("this keyword nested inside path", function() {
it('this keyword nested inside path', function() {
shouldThrow(function() {
CompilerContext.compile('{{#hellos}}{{text/this/foo}}{{/hellos}}');
}, Error, 'Invalid path: text/this - 1:13');
@@ -235,22 +236,22 @@ describe("basic context", function() {
shouldCompileTo('{{text/[this]}}', {text: {'this': 'bar'}}, 'bar');
});
it("this keyword in helpers", function() {
it('this keyword in helpers', function() {
var helpers = {foo: function(value) {
return 'bar ' + value;
}};
var string = "{{#goodbyes}}{{foo this}}{{/goodbyes}}";
var hash = {goodbyes: ["goodbye", "Goodbye", "GOODBYE"]};
shouldCompileTo(string, [hash, helpers], "bar goodbyebar Goodbyebar GOODBYE",
"This keyword in paths evaluates to current context");
var string = '{{#goodbyes}}{{foo this}}{{/goodbyes}}';
var hash = {goodbyes: ['goodbye', 'Goodbye', 'GOODBYE']};
shouldCompileTo(string, [hash, helpers], 'bar goodbyebar Goodbyebar GOODBYE',
'This keyword in paths evaluates to current context');
string = "{{#hellos}}{{foo this/text}}{{/hellos}}";
hash = {hellos: [{text: "hello"}, {text: "Hello"}, {text: "HELLO"}]};
shouldCompileTo(string, [hash, helpers], "bar hellobar Hellobar HELLO", "This keyword evaluates in more complex paths");
string = '{{#hellos}}{{foo this/text}}{{/hellos}}';
hash = {hellos: [{text: 'hello'}, {text: 'Hello'}, {text: 'HELLO'}]};
shouldCompileTo(string, [hash, helpers], 'bar hellobar Hellobar HELLO', 'This keyword evaluates in more complex paths');
});
it("this keyword nested inside helpers param", function() {
var string = "{{#hellos}}{{foo text/this/foo}}{{/hellos}}";
it('this keyword nested inside helpers param', function() {
var string = '{{#hellos}}{{foo text/this/foo}}{{/hellos}}';
shouldThrow(function() {
CompilerContext.compile(string);
}, Error, 'Invalid path: text/this - 1:17');
+73 -74
View File
@@ -1,112 +1,111 @@
/*global CompilerContext, shouldCompileTo, shouldThrow */
describe('blocks', function() {
it("array", function() {
var string = "{{#goodbyes}}{{text}}! {{/goodbyes}}cruel {{world}}!";
var hash = {goodbyes: [{text: "goodbye"}, {text: "Goodbye"}, {text: "GOODBYE"}], world: "world"};
shouldCompileTo(string, hash, "goodbye! Goodbye! GOODBYE! cruel world!",
"Arrays iterate over the contents when not empty");
it('array', function() {
var string = '{{#goodbyes}}{{text}}! {{/goodbyes}}cruel {{world}}!';
var hash = {goodbyes: [{text: 'goodbye'}, {text: 'Goodbye'}, {text: 'GOODBYE'}], world: 'world'};
shouldCompileTo(string, hash, 'goodbye! Goodbye! GOODBYE! cruel world!',
'Arrays iterate over the contents when not empty');
shouldCompileTo(string, {goodbyes: [], world: "world"}, "cruel world!",
"Arrays ignore the contents when empty");
shouldCompileTo(string, {goodbyes: [], world: 'world'}, 'cruel world!',
'Arrays ignore the contents when empty');
});
it('array without data', function() {
var string = '{{#goodbyes}}{{text}}{{/goodbyes}} {{#goodbyes}}{{text}}{{/goodbyes}}';
var hash = {goodbyes: [{text: 'goodbye'}, {text: 'Goodbye'}, {text: 'GOODBYE'}], world: 'world'};
shouldCompileTo(string, [hash,,,,false], 'goodbyeGoodbyeGOODBYE goodbyeGoodbyeGOODBYE');
var string = '{{#goodbyes}}{{text}}{{/goodbyes}} {{#goodbyes}}{{text}}{{/goodbyes}}';
var hash = {goodbyes: [{text: 'goodbye'}, {text: 'Goodbye'}, {text: 'GOODBYE'}], world: 'world'};
shouldCompileTo(string, [hash,,, false], 'goodbyeGoodbyeGOODBYE goodbyeGoodbyeGOODBYE');
});
it("array with @index", function() {
var string = "{{#goodbyes}}{{@index}}. {{text}}! {{/goodbyes}}cruel {{world}}!";
var hash = {goodbyes: [{text: "goodbye"}, {text: "Goodbye"}, {text: "GOODBYE"}], world: "world"};
it('array with @index', function() {
var string = '{{#goodbyes}}{{@index}}. {{text}}! {{/goodbyes}}cruel {{world}}!';
var hash = {goodbyes: [{text: 'goodbye'}, {text: 'Goodbye'}, {text: 'GOODBYE'}], world: 'world'};
var template = CompilerContext.compile(string);
var result = template(hash);
equal(result, "0. goodbye! 1. Goodbye! 2. GOODBYE! cruel world!", "The @index variable is used");
});
it("empty block", function() {
var string = "{{#goodbyes}}{{/goodbyes}}cruel {{world}}!";
var hash = {goodbyes: [{text: "goodbye"}, {text: "Goodbye"}, {text: "GOODBYE"}], world: "world"};
shouldCompileTo(string, hash, "cruel world!",
"Arrays iterate over the contents when not empty");
shouldCompileTo(string, {goodbyes: [], world: "world"}, "cruel world!",
"Arrays ignore the contents when empty");
equal(result, '0. goodbye! 1. Goodbye! 2. GOODBYE! cruel world!', 'The @index variable is used');
});
it("block with complex lookup", function() {
var string = "{{#goodbyes}}{{text}} cruel {{../name}}! {{/goodbyes}}";
var hash = {name: "Alan", goodbyes: [{text: "goodbye"}, {text: "Goodbye"}, {text: "GOODBYE"}]};
it('empty block', function() {
var string = '{{#goodbyes}}{{/goodbyes}}cruel {{world}}!';
var hash = {goodbyes: [{text: 'goodbye'}, {text: 'Goodbye'}, {text: 'GOODBYE'}], world: 'world'};
shouldCompileTo(string, hash, 'cruel world!',
'Arrays iterate over the contents when not empty');
shouldCompileTo(string, hash, "goodbye cruel Alan! Goodbye cruel Alan! GOODBYE cruel Alan! ",
"Templates can access variables in contexts up the stack with relative path syntax");
shouldCompileTo(string, {goodbyes: [], world: 'world'}, 'cruel world!',
'Arrays ignore the contents when empty');
});
it('block with complex lookup', function() {
var string = '{{#goodbyes}}{{text}} cruel {{../name}}! {{/goodbyes}}';
var hash = {name: 'Alan', goodbyes: [{text: 'goodbye'}, {text: 'Goodbye'}, {text: 'GOODBYE'}]};
shouldCompileTo(string, hash, 'goodbye cruel Alan! Goodbye cruel Alan! GOODBYE cruel Alan! ',
'Templates can access variables in contexts up the stack with relative path syntax');
});
it('multiple blocks with complex lookup', function() {
var string = '{{#goodbyes}}{{../name}}{{../name}}{{/goodbyes}}';
var hash = {name: 'Alan', goodbyes: [{text: 'goodbye'}, {text: 'Goodbye'}, {text: 'GOODBYE'}]};
var hash = {name: 'Alan', goodbyes: [{text: 'goodbye'}, {text: 'Goodbye'}, {text: 'GOODBYE'}]};
shouldCompileTo(string, hash, 'AlanAlanAlanAlanAlanAlan');
});
it("block with complex lookup using nested context", function() {
var string = "{{#goodbyes}}{{text}} cruel {{foo/../name}}! {{/goodbyes}}";
it('block with complex lookup using nested context', function() {
var string = '{{#goodbyes}}{{text}} cruel {{foo/../name}}! {{/goodbyes}}';
shouldThrow(function() {
CompilerContext.compile(string);
}, Error);
});
it("block with deep nested complex lookup", function() {
var string = "{{#outer}}Goodbye {{#inner}}cruel {{../sibling}} {{../../omg}}{{/inner}}{{/outer}}";
var hash = {omg: "OMG!", outer: [{ sibling: 'sad', inner: [{ text: "goodbye" }] }] };
it('block with deep nested complex lookup', function() {
var string = '{{#outer}}Goodbye {{#inner}}cruel {{../sibling}} {{../../omg}}{{/inner}}{{/outer}}';
var hash = {omg: 'OMG!', outer: [{ sibling: 'sad', inner: [{ text: 'goodbye' }] }] };
shouldCompileTo(string, hash, "Goodbye cruel sad OMG!");
shouldCompileTo(string, hash, 'Goodbye cruel sad OMG!');
});
describe('inverted sections', function() {
it("inverted sections with unset value", function() {
var string = "{{#goodbyes}}{{this}}{{/goodbyes}}{{^goodbyes}}Right On!{{/goodbyes}}";
it('inverted sections with unset value', function() {
var string = '{{#goodbyes}}{{this}}{{/goodbyes}}{{^goodbyes}}Right On!{{/goodbyes}}';
var hash = {};
shouldCompileTo(string, hash, "Right On!", "Inverted section rendered when value isn't set.");
shouldCompileTo(string, hash, 'Right On!', "Inverted section rendered when value isn't set.");
});
it("inverted section with false value", function() {
var string = "{{#goodbyes}}{{this}}{{/goodbyes}}{{^goodbyes}}Right On!{{/goodbyes}}";
it('inverted section with false value', function() {
var string = '{{#goodbyes}}{{this}}{{/goodbyes}}{{^goodbyes}}Right On!{{/goodbyes}}';
var hash = {goodbyes: false};
shouldCompileTo(string, hash, "Right On!", "Inverted section rendered when value is false.");
shouldCompileTo(string, hash, 'Right On!', 'Inverted section rendered when value is false.');
});
it("inverted section with empty set", function() {
var string = "{{#goodbyes}}{{this}}{{/goodbyes}}{{^goodbyes}}Right On!{{/goodbyes}}";
it('inverted section with empty set', function() {
var string = '{{#goodbyes}}{{this}}{{/goodbyes}}{{^goodbyes}}Right On!{{/goodbyes}}';
var hash = {goodbyes: []};
shouldCompileTo(string, hash, "Right On!", "Inverted section rendered when value is empty set.");
shouldCompileTo(string, hash, 'Right On!', 'Inverted section rendered when value is empty set.');
});
it("block inverted sections", function() {
shouldCompileTo("{{#people}}{{name}}{{^}}{{none}}{{/people}}", {none: "No people"},
"No people");
it('block inverted sections', function() {
shouldCompileTo('{{#people}}{{name}}{{^}}{{none}}{{/people}}', {none: 'No people'},
'No people');
});
it("chained inverted sections", function() {
shouldCompileTo("{{#people}}{{name}}{{else if none}}{{none}}{{/people}}", {none: "No people"},
"No people");
shouldCompileTo("{{#people}}{{name}}{{else if nothere}}fail{{else unless nothere}}{{none}}{{/people}}", {none: "No people"},
"No people");
shouldCompileTo("{{#people}}{{name}}{{else if none}}{{none}}{{else}}fail{{/people}}", {none: "No people"},
"No people");
it('chained inverted sections', function() {
shouldCompileTo('{{#people}}{{name}}{{else if none}}{{none}}{{/people}}', {none: 'No people'},
'No people');
shouldCompileTo('{{#people}}{{name}}{{else if nothere}}fail{{else unless nothere}}{{none}}{{/people}}', {none: 'No people'},
'No people');
shouldCompileTo('{{#people}}{{name}}{{else if none}}{{none}}{{else}}fail{{/people}}', {none: 'No people'},
'No people');
});
it("chained inverted sections with mismatch", function() {
it('chained inverted sections with mismatch', function() {
shouldThrow(function() {
shouldCompileTo("{{#people}}{{name}}{{else if none}}{{none}}{{/if}}", {none: "No people"},
"No people");
shouldCompileTo('{{#people}}{{name}}{{else if none}}{{none}}{{/if}}', {none: 'No people'},
'No people');
}, Error);
});
it("block inverted sections with empty arrays", function() {
shouldCompileTo("{{#people}}{{name}}{{^}}{{none}}{{/people}}", {none: "No people", people: []},
"No people");
it('block inverted sections with empty arrays', function() {
shouldCompileTo('{{#people}}{{name}}{{^}}{{none}}{{/people}}', {none: 'No people', people: []},
'No people');
});
});
@@ -131,23 +130,23 @@ describe('blocks', function() {
});
describe('compat mode', function() {
it("block with deep recursive lookup lookup", function() {
var string = "{{#outer}}Goodbye {{#inner}}cruel {{omg}}{{/inner}}{{/outer}}";
var hash = {omg: "OMG!", outer: [{ inner: [{ text: "goodbye" }] }] };
it('block with deep recursive lookup lookup', function() {
var string = '{{#outer}}Goodbye {{#inner}}cruel {{omg}}{{/inner}}{{/outer}}';
var hash = {omg: 'OMG!', outer: [{ inner: [{ text: 'goodbye' }] }] };
shouldCompileTo(string, [hash, undefined, undefined, true], "Goodbye cruel OMG!");
shouldCompileTo(string, [hash, undefined, undefined, true], 'Goodbye cruel OMG!');
});
it("block with deep recursive pathed lookup", function() {
var string = "{{#outer}}Goodbye {{#inner}}cruel {{omg.yes}}{{/inner}}{{/outer}}";
var hash = {omg: {yes: "OMG!"}, outer: [{ inner: [{ yes: 'no', text: "goodbye" }] }] };
it('block with deep recursive pathed lookup', function() {
var string = '{{#outer}}Goodbye {{#inner}}cruel {{omg.yes}}{{/inner}}{{/outer}}';
var hash = {omg: {yes: 'OMG!'}, outer: [{ inner: [{ yes: 'no', text: 'goodbye' }] }] };
shouldCompileTo(string, [hash, undefined, undefined, true], "Goodbye cruel OMG!");
shouldCompileTo(string, [hash, undefined, undefined, true], 'Goodbye cruel OMG!');
});
it("block with missed recursive lookup", function() {
var string = "{{#outer}}Goodbye {{#inner}}cruel {{omg.yes}}{{/inner}}{{/outer}}";
var hash = {omg: {no: "OMG!"}, outer: [{ inner: [{ yes: 'no', text: "goodbye" }] }] };
it('block with missed recursive lookup', function() {
var string = '{{#outer}}Goodbye {{#inner}}cruel {{omg.yes}}{{/inner}}{{/outer}}';
var hash = {omg: {no: 'OMG!'}, outer: [{ inner: [{ yes: 'no', text: 'goodbye' }] }] };
shouldCompileTo(string, [hash, undefined, undefined, true], "Goodbye cruel ");
shouldCompileTo(string, [hash, undefined, undefined, true], 'Goodbye cruel ');
});
});
});
+143 -142
View File
@@ -1,52 +1,51 @@
/*global CompilerContext, shouldCompileTo, shouldThrow, compileWithPartials, handlebarsEnv */
describe('builtin helpers', function() {
describe('#if', function() {
it("if", function() {
var string = "{{#if goodbye}}GOODBYE {{/if}}cruel {{world}}!";
shouldCompileTo(string, {goodbye: true, world: "world"}, "GOODBYE cruel world!",
"if with boolean argument shows the contents when true");
shouldCompileTo(string, {goodbye: "dummy", world: "world"}, "GOODBYE cruel world!",
"if with string argument shows the contents");
shouldCompileTo(string, {goodbye: false, world: "world"}, "cruel world!",
"if with boolean argument does not show the contents when false");
shouldCompileTo(string, {world: "world"}, "cruel world!",
"if with undefined does not show the contents");
shouldCompileTo(string, {goodbye: ['foo'], world: "world"}, "GOODBYE cruel world!",
"if with non-empty array shows the contents");
shouldCompileTo(string, {goodbye: [], world: "world"}, "cruel world!",
"if with empty array does not show the contents");
shouldCompileTo(string, {goodbye: 0, world: "world"}, "cruel world!",
"if with zero does not show the contents");
shouldCompileTo("{{#if goodbye includeZero=true}}GOODBYE {{/if}}cruel {{world}}!",
{goodbye: 0, world: "world"}, "GOODBYE cruel world!",
"if with zero does not show the contents");
it('if', function() {
var string = '{{#if goodbye}}GOODBYE {{/if}}cruel {{world}}!';
shouldCompileTo(string, {goodbye: true, world: 'world'}, 'GOODBYE cruel world!',
'if with boolean argument shows the contents when true');
shouldCompileTo(string, {goodbye: 'dummy', world: 'world'}, 'GOODBYE cruel world!',
'if with string argument shows the contents');
shouldCompileTo(string, {goodbye: false, world: 'world'}, 'cruel world!',
'if with boolean argument does not show the contents when false');
shouldCompileTo(string, {world: 'world'}, 'cruel world!',
'if with undefined does not show the contents');
shouldCompileTo(string, {goodbye: ['foo'], world: 'world'}, 'GOODBYE cruel world!',
'if with non-empty array shows the contents');
shouldCompileTo(string, {goodbye: [], world: 'world'}, 'cruel world!',
'if with empty array does not show the contents');
shouldCompileTo(string, {goodbye: 0, world: 'world'}, 'cruel world!',
'if with zero does not show the contents');
shouldCompileTo('{{#if goodbye includeZero=true}}GOODBYE {{/if}}cruel {{world}}!',
{goodbye: 0, world: 'world'}, 'GOODBYE cruel world!',
'if with zero does not show the contents');
});
it("if with function argument", function() {
var string = "{{#if goodbye}}GOODBYE {{/if}}cruel {{world}}!";
shouldCompileTo(string, {goodbye: function() {return true;}, world: "world"}, "GOODBYE cruel world!",
"if with function shows the contents when function returns true");
shouldCompileTo(string, {goodbye: function() {return this.world;}, world: "world"}, "GOODBYE cruel world!",
"if with function shows the contents when function returns string");
shouldCompileTo(string, {goodbye: function() {return false;}, world: "world"}, "cruel world!",
"if with function does not show the contents when returns false");
shouldCompileTo(string, {goodbye: function() {return this.foo;}, world: "world"}, "cruel world!",
"if with function does not show the contents when returns undefined");
it('if with function argument', function() {
var string = '{{#if goodbye}}GOODBYE {{/if}}cruel {{world}}!';
shouldCompileTo(string, {goodbye: function() {return true; }, world: 'world'}, 'GOODBYE cruel world!',
'if with function shows the contents when function returns true');
shouldCompileTo(string, {goodbye: function() {return this.world; }, world: 'world'}, 'GOODBYE cruel world!',
'if with function shows the contents when function returns string');
shouldCompileTo(string, {goodbye: function() {return false; }, world: 'world'}, 'cruel world!',
'if with function does not show the contents when returns false');
shouldCompileTo(string, {goodbye: function() {return this.foo; }, world: 'world'}, 'cruel world!',
'if with function does not show the contents when returns undefined');
});
});
describe('#with', function() {
it("with", function() {
var string = "{{#with person}}{{first}} {{last}}{{/with}}";
shouldCompileTo(string, {person: {first: "Alan", last: "Johnson"}}, "Alan Johnson");
it('with', function() {
var string = '{{#with person}}{{first}} {{last}}{{/with}}';
shouldCompileTo(string, {person: {first: 'Alan', last: 'Johnson'}}, 'Alan Johnson');
});
it("with with function argument", function() {
var string = "{{#with person}}{{first}} {{last}}{{/with}}";
shouldCompileTo(string, {person: function() { return {first: "Alan", last: "Johnson"};}}, "Alan Johnson");
it('with with function argument', function() {
var string = '{{#with person}}{{first}} {{last}}{{/with}}';
shouldCompileTo(string, {person: function() { return {first: 'Alan', last: 'Johnson'}; }}, 'Alan Johnson');
});
it("with with else", function() {
var string = "{{#with person}}Person is present{{else}}Person is not present{{/with}}";
shouldCompileTo(string, {}, "Person is not present");
it('with with else', function() {
var string = '{{#with person}}Person is present{{else}}Person is not present{{/with}}';
shouldCompileTo(string, {}, 'Person is not present');
});
});
@@ -57,73 +56,73 @@ describe('builtin helpers', function() {
});
});
it("each", function() {
var string = "{{#each goodbyes}}{{text}}! {{/each}}cruel {{world}}!";
var hash = {goodbyes: [{text: "goodbye"}, {text: "Goodbye"}, {text: "GOODBYE"}], world: "world"};
shouldCompileTo(string, hash, "goodbye! Goodbye! GOODBYE! cruel world!",
"each with array argument iterates over the contents when not empty");
shouldCompileTo(string, {goodbyes: [], world: "world"}, "cruel world!",
"each with array argument ignores the contents when empty");
it('each', function() {
var string = '{{#each goodbyes}}{{text}}! {{/each}}cruel {{world}}!';
var hash = {goodbyes: [{text: 'goodbye'}, {text: 'Goodbye'}, {text: 'GOODBYE'}], world: 'world'};
shouldCompileTo(string, hash, 'goodbye! Goodbye! GOODBYE! cruel world!',
'each with array argument iterates over the contents when not empty');
shouldCompileTo(string, {goodbyes: [], world: 'world'}, 'cruel world!',
'each with array argument ignores the contents when empty');
});
it('each without data', function() {
var string = '{{#each goodbyes}}{{text}}! {{/each}}cruel {{world}}!';
var hash = {goodbyes: [{text: 'goodbye'}, {text: 'Goodbye'}, {text: 'GOODBYE'}], world: 'world'};
shouldCompileTo(string, [hash,,,,false], 'goodbye! Goodbye! GOODBYE! cruel world!');
var string = '{{#each goodbyes}}{{text}}! {{/each}}cruel {{world}}!';
var hash = {goodbyes: [{text: 'goodbye'}, {text: 'Goodbye'}, {text: 'GOODBYE'}], world: 'world'};
shouldCompileTo(string, [hash,,,, false], 'goodbye! Goodbye! GOODBYE! cruel world!');
hash = {goodbyes: 'cruel', world: 'world'};
shouldCompileTo('{{#each .}}{{.}}{{/each}}', [hash,,,,false], 'cruelworld');
shouldCompileTo('{{#each .}}{{.}}{{/each}}', [hash,,,, false], 'cruelworld');
});
it('each without context', function() {
var string = '{{#each goodbyes}}{{text}}! {{/each}}cruel {{world}}!';
shouldCompileTo(string, [,,,,], 'cruel !');
var string = '{{#each goodbyes}}{{text}}! {{/each}}cruel {{world}}!';
shouldCompileTo(string, [,,,, ], 'cruel !');
});
it("each with an object and @key", function() {
var string = "{{#each goodbyes}}{{@key}}. {{text}}! {{/each}}cruel {{world}}!";
it('each with an object and @key', function() {
var string = '{{#each goodbyes}}{{@key}}. {{text}}! {{/each}}cruel {{world}}!';
function Clazz() {
this['<b>#1</b>'] = {text: 'goodbye'};
this[2] = {text: 'GOODBYE'};
}
Clazz.prototype.foo = 'fail';
var hash = {goodbyes: new Clazz(), world: 'world'};
var hash = {goodbyes: new Clazz(), world: 'world'};
// Object property iteration order is undefined according to ECMA spec,
// so we need to check both possible orders
// @see http://stackoverflow.com/questions/280713/elements-order-in-a-for-in-loop
var actual = compileWithPartials(string, hash);
var expected1 = "&lt;b&gt;#1&lt;/b&gt;. goodbye! 2. GOODBYE! cruel world!";
var expected2 = "2. GOODBYE! &lt;b&gt;#1&lt;/b&gt;. goodbye! cruel world!";
var expected1 = '&lt;b&gt;#1&lt;/b&gt;. goodbye! 2. GOODBYE! cruel world!';
var expected2 = '2. GOODBYE! &lt;b&gt;#1&lt;/b&gt;. goodbye! cruel world!';
equals(actual === expected1 || actual === expected2, true, "each with object argument iterates over the contents when not empty");
equals(actual === expected1 || actual === expected2, true, 'each with object argument iterates over the contents when not empty');
shouldCompileTo(string, {goodbyes: {}, world: 'world'}, 'cruel world!');
});
it("each with @index", function() {
var string = "{{#each goodbyes}}{{@index}}. {{text}}! {{/each}}cruel {{world}}!";
var hash = {goodbyes: [{text: "goodbye"}, {text: "Goodbye"}, {text: "GOODBYE"}], world: "world"};
it('each with @index', function() {
var string = '{{#each goodbyes}}{{@index}}. {{text}}! {{/each}}cruel {{world}}!';
var hash = {goodbyes: [{text: 'goodbye'}, {text: 'Goodbye'}, {text: 'GOODBYE'}], world: 'world'};
var template = CompilerContext.compile(string);
var result = template(hash);
equal(result, "0. goodbye! 1. Goodbye! 2. GOODBYE! cruel world!", "The @index variable is used");
equal(result, '0. goodbye! 1. Goodbye! 2. GOODBYE! cruel world!', 'The @index variable is used');
});
it("each with nested @index", function() {
var string = "{{#each goodbyes}}{{@index}}. {{text}}! {{#each ../goodbyes}}{{@index}} {{/each}}After {{@index}} {{/each}}{{@index}}cruel {{world}}!";
var hash = {goodbyes: [{text: "goodbye"}, {text: "Goodbye"}, {text: "GOODBYE"}], world: "world"};
it('each with nested @index', function() {
var string = '{{#each goodbyes}}{{@index}}. {{text}}! {{#each ../goodbyes}}{{@index}} {{/each}}After {{@index}} {{/each}}{{@index}}cruel {{world}}!';
var hash = {goodbyes: [{text: 'goodbye'}, {text: 'Goodbye'}, {text: 'GOODBYE'}], world: 'world'};
var template = CompilerContext.compile(string);
var result = template(hash);
equal(result, "0. goodbye! 0 1 2 After 0 1. Goodbye! 0 1 2 After 1 2. GOODBYE! 0 1 2 After 2 cruel world!", "The @index variable is used");
equal(result, '0. goodbye! 0 1 2 After 0 1. Goodbye! 0 1 2 After 1 2. GOODBYE! 0 1 2 After 2 cruel world!', 'The @index variable is used');
});
it('each with block params', function() {
var string = '{{#each goodbyes as |value index|}}{{index}}. {{value.text}}! {{#each ../goodbyes as |childValue childIndex|}} {{index}} {{childIndex}}{{/each}} After {{index}} {{/each}}{{index}}cruel {{world}}!';
var hash = {goodbyes: [{text: 'goodbye'}, {text: 'Goodbye'}], world: 'world'};
var hash = {goodbyes: [{text: 'goodbye'}, {text: 'Goodbye'}], world: 'world'};
var template = CompilerContext.compile(string);
var result = template(hash);
@@ -131,88 +130,88 @@ describe('builtin helpers', function() {
equal(result, '0. goodbye! 0 0 0 1 After 0 1. Goodbye! 1 0 1 1 After 1 cruel world!');
});
it("each object with @index", function() {
var string = "{{#each goodbyes}}{{@index}}. {{text}}! {{/each}}cruel {{world}}!";
var hash = {goodbyes: {'a': {text: "goodbye"}, b: {text: "Goodbye"}, c: {text: "GOODBYE"}}, world: "world"};
it('each object with @index', function() {
var string = '{{#each goodbyes}}{{@index}}. {{text}}! {{/each}}cruel {{world}}!';
var hash = {goodbyes: {'a': {text: 'goodbye'}, b: {text: 'Goodbye'}, c: {text: 'GOODBYE'}}, world: 'world'};
var template = CompilerContext.compile(string);
var result = template(hash);
equal(result, "0. goodbye! 1. Goodbye! 2. GOODBYE! cruel world!", "The @index variable is used");
equal(result, '0. goodbye! 1. Goodbye! 2. GOODBYE! cruel world!', 'The @index variable is used');
});
it("each with @first", function() {
var string = "{{#each goodbyes}}{{#if @first}}{{text}}! {{/if}}{{/each}}cruel {{world}}!";
var hash = {goodbyes: [{text: "goodbye"}, {text: "Goodbye"}, {text: "GOODBYE"}], world: "world"};
it('each with @first', function() {
var string = '{{#each goodbyes}}{{#if @first}}{{text}}! {{/if}}{{/each}}cruel {{world}}!';
var hash = {goodbyes: [{text: 'goodbye'}, {text: 'Goodbye'}, {text: 'GOODBYE'}], world: 'world'};
var template = CompilerContext.compile(string);
var result = template(hash);
equal(result, "goodbye! cruel world!", "The @first variable is used");
equal(result, 'goodbye! cruel world!', 'The @first variable is used');
});
it("each with nested @first", function() {
var string = "{{#each goodbyes}}({{#if @first}}{{text}}! {{/if}}{{#each ../goodbyes}}{{#if @first}}{{text}}!{{/if}}{{/each}}{{#if @first}} {{text}}!{{/if}}) {{/each}}cruel {{world}}!";
var hash = {goodbyes: [{text: "goodbye"}, {text: "Goodbye"}, {text: "GOODBYE"}], world: "world"};
it('each with nested @first', function() {
var string = '{{#each goodbyes}}({{#if @first}}{{text}}! {{/if}}{{#each ../goodbyes}}{{#if @first}}{{text}}!{{/if}}{{/each}}{{#if @first}} {{text}}!{{/if}}) {{/each}}cruel {{world}}!';
var hash = {goodbyes: [{text: 'goodbye'}, {text: 'Goodbye'}, {text: 'GOODBYE'}], world: 'world'};
var template = CompilerContext.compile(string);
var result = template(hash);
equal(result, "(goodbye! goodbye! goodbye!) (goodbye!) (goodbye!) cruel world!", "The @first variable is used");
equal(result, '(goodbye! goodbye! goodbye!) (goodbye!) (goodbye!) cruel world!', 'The @first variable is used');
});
it("each object with @first", function() {
var string = "{{#each goodbyes}}{{#if @first}}{{text}}! {{/if}}{{/each}}cruel {{world}}!";
var hash = {goodbyes: {'foo': {text: "goodbye"}, bar: {text: "Goodbye"}}, world: "world"};
it('each object with @first', function() {
var string = '{{#each goodbyes}}{{#if @first}}{{text}}! {{/if}}{{/each}}cruel {{world}}!';
var hash = {goodbyes: {'foo': {text: 'goodbye'}, bar: {text: 'Goodbye'}}, world: 'world'};
var template = CompilerContext.compile(string);
var result = template(hash);
equal(result, "goodbye! cruel world!", "The @first variable is used");
equal(result, 'goodbye! cruel world!', 'The @first variable is used');
});
it("each with @last", function() {
var string = "{{#each goodbyes}}{{#if @last}}{{text}}! {{/if}}{{/each}}cruel {{world}}!";
var hash = {goodbyes: [{text: "goodbye"}, {text: "Goodbye"}, {text: "GOODBYE"}], world: "world"};
it('each with @last', function() {
var string = '{{#each goodbyes}}{{#if @last}}{{text}}! {{/if}}{{/each}}cruel {{world}}!';
var hash = {goodbyes: [{text: 'goodbye'}, {text: 'Goodbye'}, {text: 'GOODBYE'}], world: 'world'};
var template = CompilerContext.compile(string);
var result = template(hash);
equal(result, "GOODBYE! cruel world!", "The @last variable is used");
equal(result, 'GOODBYE! cruel world!', 'The @last variable is used');
});
it("each object with @last", function() {
var string = "{{#each goodbyes}}{{#if @last}}{{text}}! {{/if}}{{/each}}cruel {{world}}!";
var hash = {goodbyes: {'foo': {text: "goodbye"}, bar: {text: "Goodbye"}}, world: "world"};
it('each object with @last', function() {
var string = '{{#each goodbyes}}{{#if @last}}{{text}}! {{/if}}{{/each}}cruel {{world}}!';
var hash = {goodbyes: {'foo': {text: 'goodbye'}, bar: {text: 'Goodbye'}}, world: 'world'};
var template = CompilerContext.compile(string);
var result = template(hash);
equal(result, "Goodbye! cruel world!", "The @last variable is used");
equal(result, 'Goodbye! cruel world!', 'The @last variable is used');
});
it("each with nested @last", function() {
var string = "{{#each goodbyes}}({{#if @last}}{{text}}! {{/if}}{{#each ../goodbyes}}{{#if @last}}{{text}}!{{/if}}{{/each}}{{#if @last}} {{text}}!{{/if}}) {{/each}}cruel {{world}}!";
var hash = {goodbyes: [{text: "goodbye"}, {text: "Goodbye"}, {text: "GOODBYE"}], world: "world"};
it('each with nested @last', function() {
var string = '{{#each goodbyes}}({{#if @last}}{{text}}! {{/if}}{{#each ../goodbyes}}{{#if @last}}{{text}}!{{/if}}{{/each}}{{#if @last}} {{text}}!{{/if}}) {{/each}}cruel {{world}}!';
var hash = {goodbyes: [{text: 'goodbye'}, {text: 'Goodbye'}, {text: 'GOODBYE'}], world: 'world'};
var template = CompilerContext.compile(string);
var result = template(hash);
equal(result, "(GOODBYE!) (GOODBYE!) (GOODBYE! GOODBYE! GOODBYE!) cruel world!", "The @last variable is used");
equal(result, '(GOODBYE!) (GOODBYE!) (GOODBYE! GOODBYE! GOODBYE!) cruel world!', 'The @last variable is used');
});
it("each with function argument", function() {
var string = "{{#each goodbyes}}{{text}}! {{/each}}cruel {{world}}!";
var hash = {goodbyes: function () { return [{text: "goodbye"}, {text: "Goodbye"}, {text: "GOODBYE"}];}, world: "world"};
shouldCompileTo(string, hash, "goodbye! Goodbye! GOODBYE! cruel world!",
"each with array function argument iterates over the contents when not empty");
shouldCompileTo(string, {goodbyes: [], world: "world"}, "cruel world!",
"each with array function argument ignores the contents when empty");
it('each with function argument', function() {
var string = '{{#each goodbyes}}{{text}}! {{/each}}cruel {{world}}!';
var hash = {goodbyes: function() { return [{text: 'goodbye'}, {text: 'Goodbye'}, {text: 'GOODBYE'}]; }, world: 'world'};
shouldCompileTo(string, hash, 'goodbye! Goodbye! GOODBYE! cruel world!',
'each with array function argument iterates over the contents when not empty');
shouldCompileTo(string, {goodbyes: [], world: 'world'}, 'cruel world!',
'each with array function argument ignores the contents when empty');
});
it("data passed to helpers", function() {
var string = "{{#each letters}}{{this}}{{detectDataInsideEach}}{{/each}}";
it('data passed to helpers', function() {
var string = '{{#each letters}}{{this}}{{detectDataInsideEach}}{{/each}}';
var hash = {letters: ['a', 'b', 'c']};
var template = CompilerContext.compile(string);
@@ -224,106 +223,108 @@ describe('builtin helpers', function() {
equal(result, 'a!b!c!', 'should output data');
});
it("each on implicit context", function() {
it('each on implicit context', function() {
shouldThrow(function() {
var template = CompilerContext.compile("{{#each}}{{text}}! {{/each}}cruel world!");
var template = CompilerContext.compile('{{#each}}{{text}}! {{/each}}cruel world!');
template({});
}, handlebarsEnv.Exception, 'Must pass iterator to #each');
});
});
describe("#log", function() {
describe('#log', function() {
/*eslint-disable no-console */
if (typeof console === 'undefined') {
return;
}
var log,
info,
error;
var $log,
$info,
$error;
beforeEach(function() {
log = console.log;
info = console.info;
error = console.error;
$log = console.log;
$info = console.info;
$error = console.error;
});
afterEach(function() {
console.log = log;
console.info = info;
console.error = error;
console.log = $log;
console.info = $info;
console.error = $error;
});
it('should call logger at default level', function() {
var string = "{{log blah}}";
var hash = { blah: "whee" };
var string = '{{log blah}}';
var hash = { blah: 'whee' };
var levelArg, logArg;
handlebarsEnv.log = function(level, arg){
handlebarsEnv.log = function(level, arg) {
levelArg = level;
logArg = arg;
};
shouldCompileTo(string, hash, "", "log should not display");
equals(1, levelArg, "should call log with 1");
equals("whee", logArg, "should call log with 'whee'");
shouldCompileTo(string, hash, '', 'log should not display');
equals(1, levelArg, 'should call log with 1');
equals('whee', logArg, "should call log with 'whee'");
});
it('should call logger at data level', function() {
var string = "{{log blah}}";
var hash = { blah: "whee" };
var string = '{{log blah}}';
var hash = { blah: 'whee' };
var levelArg, logArg;
handlebarsEnv.log = function(level, arg){
handlebarsEnv.log = function(level, arg) {
levelArg = level;
logArg = arg;
};
shouldCompileTo(string, [hash,,,,{level: '03'}], "");
shouldCompileTo(string, [hash,,,, {level: '03'}], '');
equals(3, levelArg);
equals("whee", logArg);
equals('whee', logArg);
});
it('should output to info', function() {
var string = "{{log blah}}";
var hash = { blah: "whee" };
var string = '{{log blah}}';
var hash = { blah: 'whee' };
var called;
console.info = function(log) {
equals("whee", log);
console.info = function(info) {
equals('whee', info);
called = true;
};
console.log = function(log) {
equals("whee", log);
equals('whee', log);
called = true;
};
shouldCompileTo(string, hash, "");
shouldCompileTo(string, hash, '');
equals(true, called);
});
it('should log at data level', function() {
var string = "{{log blah}}";
var hash = { blah: "whee" };
var string = '{{log blah}}';
var hash = { blah: 'whee' };
var called;
console.error = function(log) {
equals("whee", log);
equals('whee', log);
called = true;
};
shouldCompileTo(string, [hash,,,,{level: '03'}], "");
shouldCompileTo(string, [hash,,,, {level: '03'}], '');
equals(true, called);
});
it('should handle missing logger', function() {
var string = "{{log blah}}";
var hash = { blah: "whee" };
var string = '{{log blah}}';
var hash = { blah: 'whee' };
console.error = undefined;
shouldCompileTo(string, [hash,,,,{level: '03'}], "");
shouldCompileTo(string, [hash,,,, {level: '03'}], '');
});
/*eslint-enable no-console */
});
describe('#lookup', function() {
it('should lookup arbitrary content', function() {
var string = '{{#each goodbyes}}{{lookup ../data .}}{{/each}}',
hash = {goodbyes: [0, 1], data: ['foo', 'bar']};
hash = {goodbyes: [0, 1], data: ['foo', 'bar']};
var template = CompilerContext.compile(string);
var result = template(hash);
@@ -332,7 +333,7 @@ describe('builtin helpers', function() {
});
it('should not fail on undefined value', function() {
var string = '{{#each goodbyes}}{{lookup ../bar .}}{{/each}}',
hash = {goodbyes: [0, 1], data: ['foo', 'bar']};
hash = {goodbyes: [0, 1], data: ['foo', 'bar']};
var template = CompilerContext.compile(string);
var result = template(hash);
+4 -6
View File
@@ -1,5 +1,3 @@
/*global Handlebars, shouldThrow */
describe('compiler', function() {
if (!Handlebars.compile) {
return;
@@ -41,10 +39,10 @@ describe('compiler', function() {
});
it('can utilize AST instance', function() {
equal(Handlebars.compile(new Handlebars.AST.Program([ new Handlebars.AST.ContentStatement("Hello")], null, {}))(), 'Hello');
equal(Handlebars.compile(new Handlebars.AST.Program([ new Handlebars.AST.ContentStatement('Hello')], null, {}))(), 'Hello');
});
it("can pass through an empty string", function() {
it('can pass through an empty string', function() {
equal(Handlebars.compile('')(), '');
});
});
@@ -60,10 +58,10 @@ describe('compiler', function() {
});
it('can utilize AST instance', function() {
equal(/return "Hello"/.test(Handlebars.precompile(new Handlebars.AST.Program([ new Handlebars.AST.ContentStatement("Hello")]), null, {})), true);
equal(/return "Hello"/.test(Handlebars.precompile(new Handlebars.AST.Program([ new Handlebars.AST.ContentStatement('Hello')]), null, {})), true);
});
it("can pass through an empty string", function() {
it('can pass through an empty string', function() {
equal(/return ""/.test(Handlebars.precompile('')), true);
});
});
+83 -80
View File
@@ -1,25 +1,24 @@
/*global CompilerContext, Handlebars, handlebarsEnv, shouldThrow */
describe('data', function() {
it("passing in data to a compiled function that expects data - works with helpers", function() {
var template = CompilerContext.compile("{{hello}}", {data: true});
it('passing in data to a compiled function that expects data - works with helpers', function() {
var template = CompilerContext.compile('{{hello}}', {data: true});
var helpers = {
hello: function(options) {
return options.data.adjective + " " + this.noun;
return options.data.adjective + ' ' + this.noun;
}
};
var result = template({noun: "cat"}, {helpers: helpers, data: {adjective: "happy"}});
equals("happy cat", result, "Data output by helper");
var result = template({noun: 'cat'}, {helpers: helpers, data: {adjective: 'happy'}});
equals('happy cat', result, 'Data output by helper');
});
it("data can be looked up via @foo", function() {
var template = CompilerContext.compile("{{@hello}}");
var result = template({}, { data: { hello: "hello" } });
equals("hello", result, "@foo retrieves template data");
it('data can be looked up via @foo', function() {
var template = CompilerContext.compile('{{@hello}}');
var result = template({}, { data: { hello: 'hello' } });
equals('hello', result, '@foo retrieves template data');
});
it("deep @foo triggers automatic top-level data", function() {
it('deep @foo triggers automatic top-level data', function() {
var template = CompilerContext.compile('{{#let world="world"}}{{#if foo}}{{#if foo}}Hello {{@world}}{{/if}}{{/if}}{{/let}}');
var helpers = Handlebars.createFrame(handlebarsEnv.helpers);
@@ -28,65 +27,67 @@ describe('data', function() {
var frame = Handlebars.createFrame(options.data);
for (var prop in options.hash) {
frame[prop] = options.hash[prop];
if (prop in options.hash) {
frame[prop] = options.hash[prop];
}
}
return options.fn(this, { data: frame });
};
var result = template({ foo: true }, { helpers: helpers });
equals("Hello world", result, "Automatic data was triggered");
equals('Hello world', result, 'Automatic data was triggered');
});
it("parameter data can be looked up via @foo", function() {
var template = CompilerContext.compile("{{hello @world}}");
it('parameter data can be looked up via @foo', function() {
var template = CompilerContext.compile('{{hello @world}}');
var helpers = {
hello: function(noun) {
return "Hello " + noun;
return 'Hello ' + noun;
}
};
var result = template({}, { helpers: helpers, data: { world: "world" } });
equals("Hello world", result, "@foo as a parameter retrieves template data");
var result = template({}, { helpers: helpers, data: { world: 'world' } });
equals('Hello world', result, '@foo as a parameter retrieves template data');
});
it("hash values can be looked up via @foo", function() {
var template = CompilerContext.compile("{{hello noun=@world}}");
it('hash values can be looked up via @foo', function() {
var template = CompilerContext.compile('{{hello noun=@world}}');
var helpers = {
hello: function(options) {
return "Hello " + options.hash.noun;
return 'Hello ' + options.hash.noun;
}
};
var result = template({}, { helpers: helpers, data: { world: "world" } });
equals("Hello world", result, "@foo as a parameter retrieves template data");
var result = template({}, { helpers: helpers, data: { world: 'world' } });
equals('Hello world', result, '@foo as a parameter retrieves template data');
});
it("nested parameter data can be looked up via @foo.bar", function() {
var template = CompilerContext.compile("{{hello @world.bar}}");
it('nested parameter data can be looked up via @foo.bar', function() {
var template = CompilerContext.compile('{{hello @world.bar}}');
var helpers = {
hello: function(noun) {
return "Hello " + noun;
return 'Hello ' + noun;
}
};
var result = template({}, { helpers: helpers, data: { world: {bar: "world" } } });
equals("Hello world", result, "@foo as a parameter retrieves template data");
var result = template({}, { helpers: helpers, data: { world: {bar: 'world' } } });
equals('Hello world', result, '@foo as a parameter retrieves template data');
});
it("nested parameter data does not fail with @world.bar", function() {
var template = CompilerContext.compile("{{hello @world.bar}}");
it('nested parameter data does not fail with @world.bar', function() {
var template = CompilerContext.compile('{{hello @world.bar}}');
var helpers = {
hello: function(noun) {
return "Hello " + noun;
return 'Hello ' + noun;
}
};
var result = template({}, { helpers: helpers, data: { foo: {bar: "world" } } });
equals("Hello undefined", result, "@foo as a parameter retrieves template data");
var result = template({}, { helpers: helpers, data: { foo: {bar: 'world' } } });
equals('Hello undefined', result, '@foo as a parameter retrieves template data');
});
it("parameter data throws when using complex scope references", function() {
var string = "{{#goodbyes}}{{text}} cruel {{@foo/../name}}! {{/goodbyes}}";
it('parameter data throws when using complex scope references', function() {
var string = '{{#goodbyes}}{{text}} cruel {{@foo/../name}}! {{/goodbyes}}';
shouldThrow(function() {
CompilerContext.compile(string);
@@ -104,131 +105,133 @@ describe('data', function() {
equals('hello', result);
});
it("data is inherited downstream", function() {
var template = CompilerContext.compile("{{#let foo=1 bar=2}}{{#let foo=bar.baz}}{{@bar}}{{@foo}}{{/let}}{{@foo}}{{/let}}", { data: true });
it('data is inherited downstream', function() {
var template = CompilerContext.compile('{{#let foo=1 bar=2}}{{#let foo=bar.baz}}{{@bar}}{{@foo}}{{/let}}{{@foo}}{{/let}}', { data: true });
var helpers = {
let: function(options) {
var frame = Handlebars.createFrame(options.data);
for (var prop in options.hash) {
frame[prop] = options.hash[prop];
if (prop in options.hash) {
frame[prop] = options.hash[prop];
}
}
return options.fn(this, {data: frame});
}
};
var result = template({ bar: { baz: "hello world" } }, { helpers: helpers, data: {} });
equals("2hello world1", result, "data variables are inherited downstream");
var result = template({ bar: { baz: 'hello world' } }, { helpers: helpers, data: {} });
equals('2hello world1', result, 'data variables are inherited downstream');
});
it("passing in data to a compiled function that expects data - works with helpers in partials", function() {
var template = CompilerContext.compile("{{>my_partial}}", {data: true});
it('passing in data to a compiled function that expects data - works with helpers in partials', function() {
var template = CompilerContext.compile('{{>myPartial}}', {data: true});
var partials = {
my_partial: CompilerContext.compile("{{hello}}", {data: true})
myPartial: CompilerContext.compile('{{hello}}', {data: true})
};
var helpers = {
hello: function(options) {
return options.data.adjective + " " + this.noun;
return options.data.adjective + ' ' + this.noun;
}
};
var result = template({noun: "cat"}, {helpers: helpers, partials: partials, data: {adjective: "happy"}});
equals("happy cat", result, "Data output by helper inside partial");
var result = template({noun: 'cat'}, {helpers: helpers, partials: partials, data: {adjective: 'happy'}});
equals('happy cat', result, 'Data output by helper inside partial');
});
it("passing in data to a compiled function that expects data - works with helpers and parameters", function() {
var template = CompilerContext.compile("{{hello world}}", {data: true});
it('passing in data to a compiled function that expects data - works with helpers and parameters', function() {
var template = CompilerContext.compile('{{hello world}}', {data: true});
var helpers = {
hello: function(noun, options) {
return options.data.adjective + " " + noun + (this.exclaim ? "!" : "");
return options.data.adjective + ' ' + noun + (this.exclaim ? '!' : '');
}
};
var result = template({exclaim: true, world: "world"}, {helpers: helpers, data: {adjective: "happy"}});
equals("happy world!", result, "Data output by helper");
var result = template({exclaim: true, world: 'world'}, {helpers: helpers, data: {adjective: 'happy'}});
equals('happy world!', result, 'Data output by helper');
});
it("passing in data to a compiled function that expects data - works with block helpers", function() {
var template = CompilerContext.compile("{{#hello}}{{world}}{{/hello}}", {data: true});
it('passing in data to a compiled function that expects data - works with block helpers', function() {
var template = CompilerContext.compile('{{#hello}}{{world}}{{/hello}}', {data: true});
var helpers = {
hello: function(options) {
return options.fn(this);
},
world: function(options) {
return options.data.adjective + " world" + (this.exclaim ? "!" : "");
return options.data.adjective + ' world' + (this.exclaim ? '!' : '');
}
};
var result = template({exclaim: true}, {helpers: helpers, data: {adjective: "happy"}});
equals("happy world!", result, "Data output by helper");
var result = template({exclaim: true}, {helpers: helpers, data: {adjective: 'happy'}});
equals('happy world!', result, 'Data output by helper');
});
it("passing in data to a compiled function that expects data - works with block helpers that use ..", function() {
var template = CompilerContext.compile("{{#hello}}{{world ../zomg}}{{/hello}}", {data: true});
it('passing in data to a compiled function that expects data - works with block helpers that use ..', function() {
var template = CompilerContext.compile('{{#hello}}{{world ../zomg}}{{/hello}}', {data: true});
var helpers = {
hello: function(options) {
return options.fn({exclaim: "?"});
return options.fn({exclaim: '?'});
},
world: function(thing, options) {
return options.data.adjective + " " + thing + (this.exclaim || "");
return options.data.adjective + ' ' + thing + (this.exclaim || '');
}
};
var result = template({exclaim: true, zomg: "world"}, {helpers: helpers, data: {adjective: "happy"}});
equals("happy world?", result, "Data output by helper");
var result = template({exclaim: true, zomg: 'world'}, {helpers: helpers, data: {adjective: 'happy'}});
equals('happy world?', result, 'Data output by helper');
});
it("passing in data to a compiled function that expects data - data is passed to with block helpers where children use ..", function() {
var template = CompilerContext.compile("{{#hello}}{{world ../zomg}}{{/hello}}", {data: true});
it('passing in data to a compiled function that expects data - data is passed to with block helpers where children use ..', function() {
var template = CompilerContext.compile('{{#hello}}{{world ../zomg}}{{/hello}}', {data: true});
var helpers = {
hello: function(options) {
return options.data.accessData + " " + options.fn({exclaim: "?"});
return options.data.accessData + ' ' + options.fn({exclaim: '?'});
},
world: function(thing, options) {
return options.data.adjective + " " + thing + (this.exclaim || "");
return options.data.adjective + ' ' + thing + (this.exclaim || '');
}
};
var result = template({exclaim: true, zomg: "world"}, {helpers: helpers, data: {adjective: "happy", accessData: "#win"}});
equals("#win happy world?", result, "Data output by helper");
var result = template({exclaim: true, zomg: 'world'}, {helpers: helpers, data: {adjective: 'happy', accessData: '#win'}});
equals('#win happy world?', result, 'Data output by helper');
});
it("you can override inherited data when invoking a helper", function() {
var template = CompilerContext.compile("{{#hello}}{{world zomg}}{{/hello}}", {data: true});
it('you can override inherited data when invoking a helper', function() {
var template = CompilerContext.compile('{{#hello}}{{world zomg}}{{/hello}}', {data: true});
var helpers = {
hello: function(options) {
return options.fn({exclaim: "?", zomg: "world"}, { data: {adjective: "sad"} });
return options.fn({exclaim: '?', zomg: 'world'}, { data: {adjective: 'sad'} });
},
world: function(thing, options) {
return options.data.adjective + " " + thing + (this.exclaim || "");
return options.data.adjective + ' ' + thing + (this.exclaim || '');
}
};
var result = template({exclaim: true, zomg: "planet"}, {helpers: helpers, data: {adjective: "happy"}});
equals("sad world?", result, "Overriden data output by helper");
var result = template({exclaim: true, zomg: 'planet'}, {helpers: helpers, data: {adjective: 'happy'}});
equals('sad world?', result, 'Overriden data output by helper');
});
it("you can override inherited data when invoking a helper with depth", function() {
var template = CompilerContext.compile("{{#hello}}{{world ../zomg}}{{/hello}}", {data: true});
it('you can override inherited data when invoking a helper with depth', function() {
var template = CompilerContext.compile('{{#hello}}{{world ../zomg}}{{/hello}}', {data: true});
var helpers = {
hello: function(options) {
return options.fn({exclaim: "?"}, { data: {adjective: "sad"} });
return options.fn({exclaim: '?'}, { data: {adjective: 'sad'} });
},
world: function(thing, options) {
return options.data.adjective + " " + thing + (this.exclaim || "");
return options.data.adjective + ' ' + thing + (this.exclaim || '');
}
};
var result = template({exclaim: true, zomg: "world"}, {helpers: helpers, data: {adjective: "happy"}});
equals("sad world?", result, "Overriden data output by helper");
var result = template({exclaim: true, zomg: 'world'}, {helpers: helpers, data: {adjective: 'happy'}});
equals('sad world?', result, 'Overriden data output by helper');
});
describe('@root', function() {
+3 -3
View File
@@ -1,8 +1,6 @@
/*global handlebarsEnv */
require('./common');
var _ = require('underscore'),
fs = require('fs'),
var fs = require('fs'),
vm = require('vm');
global.Handlebars = 'no-conflict';
@@ -21,10 +19,12 @@ global.CompilerContext = {
};
function safeEval(templateSpec) {
/*eslint-disable no-eval, no-console */
try {
return eval('(' + templateSpec + ')');
} catch (err) {
console.error(templateSpec);
throw err;
}
/*eslint-enable no-eval, no-console */
}
+2 -3
View File
@@ -1,4 +1,3 @@
/*global CompilerContext, compileWithPartials, shouldCompileToWithPartials */
global.shouldCompileTo = function(string, hashOrArray, expected, message) {
shouldCompileToWithPartials(string, hashOrArray, false, expected, message);
};
@@ -14,7 +13,7 @@ global.compileWithPartials = function(string, hashOrArray, partials) {
var template,
ary,
options;
if(Object.prototype.toString.call(hashOrArray) === "[object Array]") {
if (Object.prototype.toString.call(hashOrArray) === '[object Array]') {
ary = [];
ary.push(hashOrArray[0]);
ary.push({ helpers: hashOrArray[1], partials: hashOrArray[2] });
@@ -34,7 +33,7 @@ global.compileWithPartials = function(string, hashOrArray, partials) {
global.equals = global.equal = function(a, b, msg) {
if (a !== b) {
throw new Error("'" + a + "' should === '" + b + "'" + (msg ? ": " + msg : ''));
throw new Error("'" + a + "' should === '" + b + "'" + (msg ? ': ' + msg : ''));
}
};
+2 -1
View File
@@ -1,4 +1,3 @@
/*global handlebarsEnv */
require('./common');
global.Handlebars = require('../../lib');
@@ -14,10 +13,12 @@ global.CompilerContext = {
};
function safeEval(templateSpec) {
/*eslint-disable no-eval, no-console */
try {
return eval('(' + templateSpec + ')');
} catch (err) {
console.error(templateSpec);
throw err;
}
/*eslint-enable no-eval, no-console */
}
+4 -1
View File
@@ -1,3 +1,4 @@
/*eslint-disable no-console */
var fs = require('fs'),
Mocha = require('mocha'),
path = require('path');
@@ -6,7 +7,7 @@ var errors = 0,
testDir = path.dirname(__dirname),
grep = process.argv[2];
var files = [ testDir + "/basic.js" ];
var files = [ testDir + '/basic.js' ];
var files = fs.readdirSync(testDir)
.filter(function(name) { return (/.*\.js$/).test(name); })
@@ -15,7 +16,9 @@ var files = fs.readdirSync(testDir)
run('./node', function() {
run('./browser', function() {
run('./runtime', function() {
/*eslint-disable no-process-exit */
process.exit(errors);
/*eslint-enable no-process-exit */
});
});
});
+8 -8
View File
@@ -1,8 +1,6 @@
/*global handlebarsEnv */
require('./common');
var _ = require('underscore'),
fs = require('fs'),
var fs = require('fs'),
vm = require('vm');
global.Handlebars = 'no-conflict';
@@ -10,15 +8,15 @@ vm.runInThisContext(fs.readFileSync(__dirname + '/../../dist/handlebars.runtime.
var parse = require('../../dist/cjs/handlebars/compiler/base').parse;
var compiler = require('../../dist/cjs/handlebars/compiler/compiler');
var JavaScriptCompiler = require('../../dist/cjs/handlebars/compiler/javascript-compiler')['default'];
var JavaScriptCompiler = require('../../dist/cjs/handlebars/compiler/javascript-compiler');
global.CompilerContext = {
browser: true,
compile: function(template, options) {
// Hack the compiler on to the environment for these specific tests
handlebarsEnv.precompile = function(template, options) {
return compiler.precompile(template, options, handlebarsEnv);
handlebarsEnv.precompile = function(precompileTemplate, precompileOptions) {
return compiler.precompile(precompileTemplate, precompileOptions, handlebarsEnv);
};
handlebarsEnv.parse = parse;
handlebarsEnv.Compiler = compiler.Compiler;
@@ -29,8 +27,8 @@ global.CompilerContext = {
},
compileWithPartial: function(template, options) {
// Hack the compiler on to the environment for these specific tests
handlebarsEnv.compile = function(template, options) {
return compiler.compile(template, options, handlebarsEnv);
handlebarsEnv.compile = function(compileTemplate, compileOptions) {
return compiler.compile(compileTemplate, compileOptions, handlebarsEnv);
};
handlebarsEnv.parse = parse;
handlebarsEnv.Compiler = compiler.Compiler;
@@ -41,10 +39,12 @@ global.CompilerContext = {
};
function safeEval(templateSpec) {
/*eslint-disable no-eval, no-console */
try {
return eval('(' + templateSpec + ')');
} catch (err) {
console.error(templateSpec);
throw err;
}
/*eslint-enable no-eval, no-console */
}
+264 -267
View File
@@ -1,165 +1,164 @@
/*global CompilerContext, Handlebars, shouldCompileTo, shouldCompileToWithPartials, shouldThrow, handlebarsEnv */
describe('helpers', function() {
it("helper with complex lookup$", function() {
var string = "{{#goodbyes}}{{{link ../prefix}}}{{/goodbyes}}";
var hash = {prefix: "/root", goodbyes: [{text: "Goodbye", url: "goodbye"}]};
it('helper with complex lookup$', function() {
var string = '{{#goodbyes}}{{{link ../prefix}}}{{/goodbyes}}';
var hash = {prefix: '/root', goodbyes: [{text: 'Goodbye', url: 'goodbye'}]};
var helpers = {link: function(prefix) {
return "<a href='" + prefix + "/" + this.url + "'>" + this.text + "</a>";
return '<a href="' + prefix + '/' + this.url + '">' + this.text + '</a>';
}};
shouldCompileTo(string, [hash, helpers], "<a href='/root/goodbye'>Goodbye</a>");
shouldCompileTo(string, [hash, helpers], '<a href="/root/goodbye">Goodbye</a>');
});
it("helper for raw block gets raw content", function() {
var string = "{{{{raw}}}} {{test}} {{{{/raw}}}}";
var hash = { test: "hello" };
it('helper for raw block gets raw content', function() {
var string = '{{{{raw}}}} {{test}} {{{{/raw}}}}';
var hash = { test: 'hello' };
var helpers = { raw: function(options) {
return options.fn();
} };
shouldCompileTo(string, [hash, helpers], " {{test}} ",
"raw block helper gets raw content");
shouldCompileTo(string, [hash, helpers], ' {{test}} ',
'raw block helper gets raw content');
});
it("helper for raw block gets parameters", function() {
var string = "{{{{raw 1 2 3}}}} {{test}} {{{{/raw}}}}";
var hash = { test: "hello" };
it('helper for raw block gets parameters', function() {
var string = '{{{{raw 1 2 3}}}} {{test}} {{{{/raw}}}}';
var hash = { test: 'hello' };
var helpers = { raw: function(a, b, c, options) {
return options.fn() + a + b + c;
} };
shouldCompileTo(string, [hash, helpers], " {{test}} 123",
"raw block helper gets raw content");
shouldCompileTo(string, [hash, helpers], ' {{test}} 123',
'raw block helper gets raw content');
});
it("helper block with complex lookup expression", function() {
var string = "{{#goodbyes}}{{../name}}{{/goodbyes}}";
var hash = {name: "Alan"};
it('helper block with complex lookup expression', function() {
var string = '{{#goodbyes}}{{../name}}{{/goodbyes}}';
var hash = {name: 'Alan'};
var helpers = {goodbyes: function(options) {
var out = "";
var byes = ["Goodbye", "goodbye", "GOODBYE"];
for (var i = 0,j = byes.length; i < j; i++) {
out += byes[i] + " " + options.fn(this) + "! ";
var out = '';
var byes = ['Goodbye', 'goodbye', 'GOODBYE'];
for (var i = 0, j = byes.length; i < j; i++) {
out += byes[i] + ' ' + options.fn(this) + '! ';
}
return out;
}};
shouldCompileTo(string, [hash, helpers], "Goodbye Alan! goodbye Alan! GOODBYE Alan! ");
shouldCompileTo(string, [hash, helpers], 'Goodbye Alan! goodbye Alan! GOODBYE Alan! ');
});
it("helper with complex lookup and nested template", function() {
var string = "{{#goodbyes}}{{#link ../prefix}}{{text}}{{/link}}{{/goodbyes}}";
var hash = {prefix: '/root', goodbyes: [{text: "Goodbye", url: "goodbye"}]};
var helpers = {link: function (prefix, options) {
return "<a href='" + prefix + "/" + this.url + "'>" + options.fn(this) + "</a>";
it('helper with complex lookup and nested template', function() {
var string = '{{#goodbyes}}{{#link ../prefix}}{{text}}{{/link}}{{/goodbyes}}';
var hash = {prefix: '/root', goodbyes: [{text: 'Goodbye', url: 'goodbye'}]};
var helpers = {link: function(prefix, options) {
return '<a href="' + prefix + '/' + this.url + '">' + options.fn(this) + '</a>';
}};
shouldCompileToWithPartials(string, [hash, helpers], false, "<a href='/root/goodbye'>Goodbye</a>");
shouldCompileToWithPartials(string, [hash, helpers], false, '<a href="/root/goodbye">Goodbye</a>');
});
it("helper with complex lookup and nested template in VM+Compiler", function() {
var string = "{{#goodbyes}}{{#link ../prefix}}{{text}}{{/link}}{{/goodbyes}}";
var hash = {prefix: '/root', goodbyes: [{text: "Goodbye", url: "goodbye"}]};
var helpers = {link: function (prefix, options) {
return "<a href='" + prefix + "/" + this.url + "'>" + options.fn(this) + "</a>";
it('helper with complex lookup and nested template in VM+Compiler', function() {
var string = '{{#goodbyes}}{{#link ../prefix}}{{text}}{{/link}}{{/goodbyes}}';
var hash = {prefix: '/root', goodbyes: [{text: 'Goodbye', url: 'goodbye'}]};
var helpers = {link: function(prefix, options) {
return '<a href="' + prefix + '/' + this.url + '">' + options.fn(this) + '</a>';
}};
shouldCompileToWithPartials(string, [hash, helpers], true, "<a href='/root/goodbye'>Goodbye</a>");
shouldCompileToWithPartials(string, [hash, helpers], true, '<a href="/root/goodbye">Goodbye</a>');
});
it('helper returning undefined value', function() {
shouldCompileTo(' {{nothere}}', [{}, {nothere: function() {}}], ' ');
shouldCompileTo(' {{#nothere}}{{/nothere}}', [{}, {nothere: function() {}}], ' ');
});
it("block helper", function() {
var string = "{{#goodbyes}}{{text}}! {{/goodbyes}}cruel {{world}}!";
it('block helper', function() {
var string = '{{#goodbyes}}{{text}}! {{/goodbyes}}cruel {{world}}!';
var template = CompilerContext.compile(string);
var result = template({world: "world"}, { helpers: {goodbyes: function(options) { return options.fn({text: "GOODBYE"}); }}});
equal(result, "GOODBYE! cruel world!", "Block helper executed");
var result = template({world: 'world'}, { helpers: {goodbyes: function(options) { return options.fn({text: 'GOODBYE'}); }}});
equal(result, 'GOODBYE! cruel world!', 'Block helper executed');
});
it("block helper staying in the same context", function() {
var string = "{{#form}}<p>{{name}}</p>{{/form}}";
it('block helper staying in the same context', function() {
var string = '{{#form}}<p>{{name}}</p>{{/form}}';
var template = CompilerContext.compile(string);
var result = template({name: "Yehuda"}, {helpers: {form: function(options) { return "<form>" + options.fn(this) + "</form>"; } }});
equal(result, "<form><p>Yehuda</p></form>", "Block helper executed with current context");
var result = template({name: 'Yehuda'}, {helpers: {form: function(options) { return '<form>' + options.fn(this) + '</form>'; } }});
equal(result, '<form><p>Yehuda</p></form>', 'Block helper executed with current context');
});
it("block helper should have context in this", function() {
var source = "<ul>{{#people}}<li>{{#link}}{{name}}{{/link}}</li>{{/people}}</ul>";
var link = function(options) {
it('block helper should have context in this', function() {
var source = '<ul>{{#people}}<li>{{#link}}{{name}}{{/link}}</li>{{/people}}</ul>';
function link(options) {
return '<a href="/people/' + this.id + '">' + options.fn(this) + '</a>';
};
var data = { "people": [
{ "name": "Alan", "id": 1 },
{ "name": "Yehuda", "id": 2 }
}
var data = { 'people': [
{ 'name': 'Alan', 'id': 1 },
{ 'name': 'Yehuda', 'id': 2 }
]};
shouldCompileTo(source, [data, {link: link}], "<ul><li><a href=\"/people/1\">Alan</a></li><li><a href=\"/people/2\">Yehuda</a></li></ul>");
shouldCompileTo(source, [data, {link: link}], '<ul><li><a href=\"/people/1\">Alan</a></li><li><a href=\"/people/2\">Yehuda</a></li></ul>');
});
it("block helper for undefined value", function() {
shouldCompileTo("{{#empty}}shouldn't render{{/empty}}", {}, "");
it('block helper for undefined value', function() {
shouldCompileTo("{{#empty}}shouldn't render{{/empty}}", {}, '');
});
it("block helper passing a new context", function() {
var string = "{{#form yehuda}}<p>{{name}}</p>{{/form}}";
it('block helper passing a new context', function() {
var string = '{{#form yehuda}}<p>{{name}}</p>{{/form}}';
var template = CompilerContext.compile(string);
var result = template({yehuda: {name: "Yehuda"}}, { helpers: {form: function(context, options) { return "<form>" + options.fn(context) + "</form>"; }}});
equal(result, "<form><p>Yehuda</p></form>", "Context variable resolved");
var result = template({yehuda: {name: 'Yehuda'}}, { helpers: {form: function(context, options) { return '<form>' + options.fn(context) + '</form>'; }}});
equal(result, '<form><p>Yehuda</p></form>', 'Context variable resolved');
});
it("block helper passing a complex path context", function() {
var string = "{{#form yehuda/cat}}<p>{{name}}</p>{{/form}}";
it('block helper passing a complex path context', function() {
var string = '{{#form yehuda/cat}}<p>{{name}}</p>{{/form}}';
var template = CompilerContext.compile(string);
var result = template({yehuda: {name: "Yehuda", cat: {name: "Harold"}}}, { helpers: {form: function(context, options) { return "<form>" + options.fn(context) + "</form>"; }}});
equal(result, "<form><p>Harold</p></form>", "Complex path variable resolved");
var result = template({yehuda: {name: 'Yehuda', cat: {name: 'Harold'}}}, { helpers: {form: function(context, options) { return '<form>' + options.fn(context) + '</form>'; }}});
equal(result, '<form><p>Harold</p></form>', 'Complex path variable resolved');
});
it("nested block helpers", function() {
var string = "{{#form yehuda}}<p>{{name}}</p>{{#link}}Hello{{/link}}{{/form}}";
it('nested block helpers', function() {
var string = '{{#form yehuda}}<p>{{name}}</p>{{#link}}Hello{{/link}}{{/form}}';
var template = CompilerContext.compile(string);
var result = template({
yehuda: {name: "Yehuda" }
yehuda: {name: 'Yehuda' }
}, {
helpers: {
link: function(options) { return "<a href='" + this.name + "'>" + options.fn(this) + "</a>"; },
form: function(context, options) { return "<form>" + options.fn(context) + "</form>"; }
link: function(options) { return '<a href="' + this.name + '">' + options.fn(this) + '</a>'; },
form: function(context, options) { return '<form>' + options.fn(context) + '</form>'; }
}
});
equal(result, "<form><p>Yehuda</p><a href='Yehuda'>Hello</a></form>", "Both blocks executed");
equal(result, '<form><p>Yehuda</p><a href="Yehuda">Hello</a></form>', 'Both blocks executed');
});
it("block helper inverted sections", function() {
var string = "{{#list people}}{{name}}{{^}}<em>Nobody's here</em>{{/list}}";
var list = function(context, options) {
it('block helper inverted sections', function() {
var string = '{{#list people}}{{name}}{{^}}<em>Nobody\'s here</em>{{/list}}';
function list(context, options) {
if (context.length > 0) {
var out = "<ul>";
for(var i = 0,j=context.length; i < j; i++) {
out += "<li>";
var out = '<ul>';
for (var i = 0, j = context.length; i < j; i++) {
out += '<li>';
out += options.fn(context[i]);
out += "</li>";
out += '</li>';
}
out += "</ul>";
out += '</ul>';
return out;
} else {
return "<p>" + options.inverse(this) + "</p>";
return '<p>' + options.inverse(this) + '</p>';
}
};
}
var hash = {people: [{name: "Alan"}, {name: "Yehuda"}]};
var hash = {people: [{name: 'Alan'}, {name: 'Yehuda'}]};
var empty = {people: []};
var rootMessage = {
people: [],
message: "Nobody's here"
message: 'Nobody\'s here'
};
var messageString = "{{#list people}}Hello{{^}}{{message}}{{/list}}";
var messageString = '{{#list people}}Hello{{^}}{{message}}{{/list}}';
// the meaning here may be kind of hard to catch, but list.not is always called,
// so we should see the output of both
shouldCompileTo(string, [hash, { list: list }], "<ul><li>Alan</li><li>Yehuda</li></ul>", "an inverse wrapper is passed in as a new context");
shouldCompileTo(string, [empty, { list: list }], "<p><em>Nobody's here</em></p>", "an inverse wrapper can be optionally called");
shouldCompileTo(messageString, [rootMessage, { list: list }], "<p>Nobody&#x27;s here</p>", "the context of an inverse is the parent of the block");
shouldCompileTo(string, [hash, { list: list }], '<ul><li>Alan</li><li>Yehuda</li></ul>', 'an inverse wrapper is passed in as a new context');
shouldCompileTo(string, [empty, { list: list }], '<p><em>Nobody\'s here</em></p>', 'an inverse wrapper can be optionally called');
shouldCompileTo(messageString, [rootMessage, { list: list }], '<p>Nobody&#x27;s here</p>', 'the context of an inverse is the parent of the block');
});
it('pathed lambas with parameters', function() {
@@ -178,43 +177,47 @@ describe('helpers', function() {
shouldCompileTo('{{hash/helper 1}}', [hash, helpers], 'winning');
});
describe("helpers hash", function() {
it("providing a helpers hash", function() {
shouldCompileTo("Goodbye {{cruel}} {{world}}!", [{cruel: "cruel"}, {world: function() { return "world"; }}], "Goodbye cruel world!",
"helpers hash is available");
describe('helpers hash', function() {
it('providing a helpers hash', function() {
shouldCompileTo('Goodbye {{cruel}} {{world}}!', [{cruel: 'cruel'}, {world: function() { return 'world'; }}], 'Goodbye cruel world!',
'helpers hash is available');
shouldCompileTo("Goodbye {{#iter}}{{cruel}} {{world}}{{/iter}}!", [{iter: [{cruel: "cruel"}]}, {world: function() { return "world"; }}],
"Goodbye cruel world!", "helpers hash is available inside other blocks");
shouldCompileTo('Goodbye {{#iter}}{{cruel}} {{world}}{{/iter}}!', [{iter: [{cruel: 'cruel'}]}, {world: function() { return 'world'; }}],
'Goodbye cruel world!', 'helpers hash is available inside other blocks');
});
it("in cases of conflict, helpers win", function() {
shouldCompileTo("{{{lookup}}}", [{lookup: 'Explicit'}, {lookup: function() { return 'helpers'; }}], "helpers",
"helpers hash has precedence escaped expansion");
shouldCompileTo("{{lookup}}", [{lookup: 'Explicit'}, {lookup: function() { return 'helpers'; }}], "helpers",
"helpers hash has precedence simple expansion");
it('in cases of conflict, helpers win', function() {
shouldCompileTo('{{{lookup}}}', [{lookup: 'Explicit'}, {lookup: function() { return 'helpers'; }}], 'helpers',
'helpers hash has precedence escaped expansion');
shouldCompileTo('{{lookup}}', [{lookup: 'Explicit'}, {lookup: function() { return 'helpers'; }}], 'helpers',
'helpers hash has precedence simple expansion');
});
it("the helpers hash is available is nested contexts", function() {
shouldCompileTo("{{#outer}}{{#inner}}{{helper}}{{/inner}}{{/outer}}",
[{'outer': {'inner': {'unused':[]}}}, {'helper': function() { return 'helper'; }}], "helper",
"helpers hash is available in nested contexts.");
it('the helpers hash is available is nested contexts', function() {
shouldCompileTo(
'{{#outer}}{{#inner}}{{helper}}{{/inner}}{{/outer}}',
[
{'outer': {'inner': {'unused': []}}},
{'helper': function() { return 'helper'; }}
],
'helper',
'helpers hash is available in nested contexts.');
});
it("the helper hash should augment the global hash", function() {
it('the helper hash should augment the global hash', function() {
handlebarsEnv.registerHelper('test_helper', function() { return 'found it!'; });
shouldCompileTo(
"{{test_helper}} {{#if cruel}}Goodbye {{cruel}} {{world}}!{{/if}}", [
{cruel: "cruel"},
{world: function() { return "world!"; }}
'{{test_helper}} {{#if cruel}}Goodbye {{cruel}} {{world}}!{{/if}}', [
{cruel: 'cruel'},
{world: function() { return 'world!'; }}
],
"found it! Goodbye cruel world!!");
'found it! Goodbye cruel world!!');
});
});
describe('registration', function() {
it('unregisters', function() {
var helpers = handlebarsEnv.helpers;
handlebarsEnv.helpers = {};
handlebarsEnv.registerHelper('foo', function() {
@@ -230,134 +233,128 @@ describe('helpers', function() {
handlebarsEnv.registerHelper({
'if': helpers['if'],
world: function() { return "world!"; },
test_helper: function() { return 'found it!'; }
world: function() { return 'world!'; },
testHelper: function() { return 'found it!'; }
});
shouldCompileTo(
"{{test_helper}} {{#if cruel}}Goodbye {{cruel}} {{world}}!{{/if}}",
[{cruel: "cruel"}],
"found it! Goodbye cruel world!!");
'{{testHelper}} {{#if cruel}}Goodbye {{cruel}} {{world}}!{{/if}}',
[{cruel: 'cruel'}],
'found it! Goodbye cruel world!!');
});
it('fails with multiple and args', function() {
shouldThrow(function() {
handlebarsEnv.registerHelper({
world: function() { return "world!"; },
test_helper: function() { return 'found it!'; }
world: function() { return 'world!'; },
testHelper: function() { return 'found it!'; }
}, {});
}, Error, 'Arg not supported with multiple helpers');
});
});
it("decimal number literals work", function() {
var string = 'Message: {{hello -1.2 1.2}}';
var hash = {};
var helpers = {hello: function(times, times2) {
if(typeof times !== 'number') { times = "NaN"; }
if(typeof times2 !== 'number') { times2 = "NaN"; }
return "Hello " + times + " " + times2 + " times";
it('decimal number literals work', function() {
var string = 'Message: {{hello -1.2 1.2}}';
var helpers = {hello: function(times, times2) {
if (typeof times !== 'number') { times = 'NaN'; }
if (typeof times2 !== 'number') { times2 = 'NaN'; }
return 'Hello ' + times + ' ' + times2 + ' times';
}};
shouldCompileTo(string, [hash, helpers], "Message: Hello -1.2 1.2 times", "template with a negative integer literal");
shouldCompileTo(string, [{}, helpers], 'Message: Hello -1.2 1.2 times', 'template with a negative integer literal');
});
it("negative number literals work", function() {
var string = 'Message: {{hello -12}}';
var hash = {};
var helpers = {hello: function(times) {
if(typeof times !== 'number') { times = "NaN"; }
return "Hello " + times + " times";
it('negative number literals work', function() {
var string = 'Message: {{hello -12}}';
var helpers = {hello: function(times) {
if (typeof times !== 'number') { times = 'NaN'; }
return 'Hello ' + times + ' times';
}};
shouldCompileTo(string, [hash, helpers], "Message: Hello -12 times", "template with a negative integer literal");
shouldCompileTo(string, [{}, helpers], 'Message: Hello -12 times', 'template with a negative integer literal');
});
describe("String literal parameters", function() {
it("simple literals work", function() {
var string = 'Message: {{hello "world" 12 true false}}';
var hash = {};
var helpers = {hello: function(param, times, bool1, bool2) {
if(typeof times !== 'number') { times = "NaN"; }
if(typeof bool1 !== 'boolean') { bool1 = "NaB"; }
if(typeof bool2 !== 'boolean') { bool2 = "NaB"; }
return "Hello " + param + " " + times + " times: " + bool1 + " " + bool2;
describe('String literal parameters', function() {
it('simple literals work', function() {
var string = 'Message: {{hello "world" 12 true false}}';
var helpers = {hello: function(param, times, bool1, bool2) {
if (typeof times !== 'number') { times = 'NaN'; }
if (typeof bool1 !== 'boolean') { bool1 = 'NaB'; }
if (typeof bool2 !== 'boolean') { bool2 = 'NaB'; }
return 'Hello ' + param + ' ' + times + ' times: ' + bool1 + ' ' + bool2;
}};
shouldCompileTo(string, [hash, helpers], "Message: Hello world 12 times: true false", "template with a simple String literal");
shouldCompileTo(string, [{}, helpers], 'Message: Hello world 12 times: true false', 'template with a simple String literal');
});
it("using a quote in the middle of a parameter raises an error", function() {
var string = 'Message: {{hello wo"rld"}}';
it('using a quote in the middle of a parameter raises an error', function() {
var string = 'Message: {{hello wo"rld"}}';
shouldThrow(function() {
CompilerContext.compile(string);
}, Error);
});
it("escaping a String is possible", function(){
var string = 'Message: {{{hello "\\"world\\""}}}';
var hash = {};
var helpers = {hello: function(param) { return "Hello " + param; }};
shouldCompileTo(string, [hash, helpers], "Message: Hello \"world\"", "template with an escaped String literal");
it('escaping a String is possible', function() {
var string = 'Message: {{{hello "\\"world\\""}}}';
var helpers = {hello: function(param) { return 'Hello ' + param; }};
shouldCompileTo(string, [{}, helpers], 'Message: Hello "world"', 'template with an escaped String literal');
});
it("it works with ' marks", function() {
var string = 'Message: {{{hello "Alan\'s world"}}}';
var hash = {};
var helpers = {hello: function(param) { return "Hello " + param; }};
shouldCompileTo(string, [hash, helpers], "Message: Hello Alan's world", "template with a ' mark");
var string = 'Message: {{{hello "Alan\'s world"}}}';
var helpers = {hello: function(param) { return 'Hello ' + param; }};
shouldCompileTo(string, [{}, helpers], "Message: Hello Alan's world", "template with a ' mark");
});
});
it("negative number literals work", function() {
var string = 'Message: {{hello -12}}';
var hash = {};
var helpers = {hello: function(times) {
if(typeof times !== 'number') { times = "NaN"; }
return "Hello " + times + " times";
it('negative number literals work', function() {
var string = 'Message: {{hello -12}}';
var helpers = {hello: function(times) {
if (typeof times !== 'number') { times = 'NaN'; }
return 'Hello ' + times + ' times';
}};
shouldCompileTo(string, [hash, helpers], "Message: Hello -12 times", "template with a negative integer literal");
shouldCompileTo(string, [{}, helpers], 'Message: Hello -12 times', 'template with a negative integer literal');
});
describe("multiple parameters", function() {
it("simple multi-params work", function() {
var string = 'Message: {{goodbye cruel world}}';
var hash = {cruel: "cruel", world: "world"};
var helpers = {goodbye: function(cruel, world) { return "Goodbye " + cruel + " " + world; }};
shouldCompileTo(string, [hash, helpers], "Message: Goodbye cruel world", "regular helpers with multiple params");
describe('multiple parameters', function() {
it('simple multi-params work', function() {
var string = 'Message: {{goodbye cruel world}}';
var hash = {cruel: 'cruel', world: 'world'};
var helpers = {goodbye: function(cruel, world) { return 'Goodbye ' + cruel + ' ' + world; }};
shouldCompileTo(string, [hash, helpers], 'Message: Goodbye cruel world', 'regular helpers with multiple params');
});
it("block multi-params work", function() {
var string = 'Message: {{#goodbye cruel world}}{{greeting}} {{adj}} {{noun}}{{/goodbye}}';
var hash = {cruel: "cruel", world: "world"};
it('block multi-params work', function() {
var string = 'Message: {{#goodbye cruel world}}{{greeting}} {{adj}} {{noun}}{{/goodbye}}';
var hash = {cruel: 'cruel', world: 'world'};
var helpers = {goodbye: function(cruel, world, options) {
return options.fn({greeting: "Goodbye", adj: cruel, noun: world});
return options.fn({greeting: 'Goodbye', adj: cruel, noun: world});
}};
shouldCompileTo(string, [hash, helpers], "Message: Goodbye cruel world", "block helpers with multiple params");
shouldCompileTo(string, [hash, helpers], 'Message: Goodbye cruel world', 'block helpers with multiple params');
});
});
describe('hash', function() {
it("helpers can take an optional hash", function() {
it('helpers can take an optional hash', function() {
var template = CompilerContext.compile('{{goodbye cruel="CRUEL" world="WORLD" times=12}}');
var helpers = {
goodbye: function(options) {
return "GOODBYE " + options.hash.cruel + " " + options.hash.world + " " + options.hash.times + " TIMES";
return 'GOODBYE ' + options.hash.cruel + ' ' + options.hash.world + ' ' + options.hash.times + ' TIMES';
}
};
var context = {};
var result = template(context, {helpers: helpers});
equals(result, "GOODBYE CRUEL WORLD 12 TIMES", "Helper output hash");
equals(result, 'GOODBYE CRUEL WORLD 12 TIMES', 'Helper output hash');
});
it("helpers can take an optional hash with booleans", function() {
it('helpers can take an optional hash with booleans', function() {
var helpers = {
goodbye: function(options) {
if (options.hash.print === true) {
return "GOODBYE " + options.hash.cruel + " " + options.hash.world;
return 'GOODBYE ' + options.hash.cruel + ' ' + options.hash.world;
} else if (options.hash.print === false) {
return "NOT PRINTING";
return 'NOT PRINTING';
} else {
return "THIS SHOULD NOT HAPPEN";
return 'THIS SHOULD NOT HAPPEN';
}
}
};
@@ -366,161 +363,161 @@ describe('helpers', function() {
var template = CompilerContext.compile('{{goodbye cruel="CRUEL" world="WORLD" print=true}}');
var result = template(context, {helpers: helpers});
equals(result, "GOODBYE CRUEL WORLD", "Helper output hash");
equals(result, 'GOODBYE CRUEL WORLD', 'Helper output hash');
template = CompilerContext.compile('{{goodbye cruel="CRUEL" world="WORLD" print=false}}');
result = template(context, {helpers: helpers});
equals(result, "NOT PRINTING", "Boolean helper parameter honored");
equals(result, 'NOT PRINTING', 'Boolean helper parameter honored');
});
it("block helpers can take an optional hash", function() {
it('block helpers can take an optional hash', function() {
var template = CompilerContext.compile('{{#goodbye cruel="CRUEL" times=12}}world{{/goodbye}}');
var helpers = {
goodbye: function(options) {
return "GOODBYE " + options.hash.cruel + " " + options.fn(this) + " " + options.hash.times + " TIMES";
return 'GOODBYE ' + options.hash.cruel + ' ' + options.fn(this) + ' ' + options.hash.times + ' TIMES';
}
};
var result = template({}, {helpers: helpers});
equals(result, "GOODBYE CRUEL world 12 TIMES", "Hash parameters output");
equals(result, 'GOODBYE CRUEL world 12 TIMES', 'Hash parameters output');
});
it("block helpers can take an optional hash with single quoted stings", function() {
var template = CompilerContext.compile("{{#goodbye cruel='CRUEL' times=12}}world{{/goodbye}}");
it('block helpers can take an optional hash with single quoted stings', function() {
var template = CompilerContext.compile('{{#goodbye cruel="CRUEL" times=12}}world{{/goodbye}}');
var helpers = {
goodbye: function(options) {
return "GOODBYE " + options.hash.cruel + " " + options.fn(this) + " " + options.hash.times + " TIMES";
return 'GOODBYE ' + options.hash.cruel + ' ' + options.fn(this) + ' ' + options.hash.times + ' TIMES';
}
};
var result = template({}, {helpers: helpers});
equals(result, "GOODBYE CRUEL world 12 TIMES", "Hash parameters output");
equals(result, 'GOODBYE CRUEL world 12 TIMES', 'Hash parameters output');
});
it("block helpers can take an optional hash with booleans", function() {
it('block helpers can take an optional hash with booleans', function() {
var helpers = {
goodbye: function(options) {
if (options.hash.print === true) {
return "GOODBYE " + options.hash.cruel + " " + options.fn(this);
return 'GOODBYE ' + options.hash.cruel + ' ' + options.fn(this);
} else if (options.hash.print === false) {
return "NOT PRINTING";
return 'NOT PRINTING';
} else {
return "THIS SHOULD NOT HAPPEN";
return 'THIS SHOULD NOT HAPPEN';
}
}
};
var template = CompilerContext.compile('{{#goodbye cruel="CRUEL" print=true}}world{{/goodbye}}');
var result = template({}, {helpers: helpers});
equals(result, "GOODBYE CRUEL world", "Boolean hash parameter honored");
equals(result, 'GOODBYE CRUEL world', 'Boolean hash parameter honored');
template = CompilerContext.compile('{{#goodbye cruel="CRUEL" print=false}}world{{/goodbye}}');
result = template({}, {helpers: helpers});
equals(result, "NOT PRINTING", "Boolean hash parameter honored");
equals(result, 'NOT PRINTING', 'Boolean hash parameter honored');
});
});
describe("helperMissing", function() {
it("if a context is not found, helperMissing is used", function() {
describe('helperMissing', function() {
it('if a context is not found, helperMissing is used', function() {
shouldThrow(function() {
var template = CompilerContext.compile("{{hello}} {{link_to world}}");
var template = CompilerContext.compile('{{hello}} {{link_to world}}');
template({});
}, undefined, /Missing helper: 'link_to'/);
}, undefined, /Missing helper: "link_to"/);
});
it("if a context is not found, custom helperMissing is used", function() {
var string = "{{hello}} {{link_to world}}";
var context = { hello: "Hello", world: "world" };
it('if a context is not found, custom helperMissing is used', function() {
var string = '{{hello}} {{link_to world}}';
var context = { hello: 'Hello', world: 'world' };
var helpers = {
helperMissing: function(mesg, options) {
if(options.name === "link_to") {
return new Handlebars.SafeString("<a>" + mesg + "</a>");
if (options.name === 'link_to') {
return new Handlebars.SafeString('<a>' + mesg + '</a>');
}
}
};
shouldCompileTo(string, [context, helpers], "Hello <a>world</a>");
shouldCompileTo(string, [context, helpers], 'Hello <a>world</a>');
});
it("if a value is not found, custom helperMissing is used", function() {
var string = "{{hello}} {{link_to}}";
var context = { hello: "Hello", world: "world" };
it('if a value is not found, custom helperMissing is used', function() {
var string = '{{hello}} {{link_to}}';
var context = { hello: 'Hello', world: 'world' };
var helpers = {
helperMissing: function(options) {
if(options.name === "link_to") {
return new Handlebars.SafeString("<a>winning</a>");
if (options.name === 'link_to') {
return new Handlebars.SafeString('<a>winning</a>');
}
}
};
shouldCompileTo(string, [context, helpers], "Hello <a>winning</a>");
shouldCompileTo(string, [context, helpers], 'Hello <a>winning</a>');
});
});
describe("knownHelpers", function() {
it("Known helper should render helper", function() {
var template = CompilerContext.compile("{{hello}}", {knownHelpers: {"hello" : true}});
describe('knownHelpers', function() {
it('Known helper should render helper', function() {
var template = CompilerContext.compile('{{hello}}', {knownHelpers: {hello: true}});
var result = template({}, {helpers: {hello: function() { return "foo"; }}});
equal(result, "foo", "'foo' should === '" + result);
var result = template({}, {helpers: {hello: function() { return 'foo'; }}});
equal(result, 'foo', "'foo' should === '" + result);
});
it("Unknown helper in knownHelpers only mode should be passed as undefined", function() {
var template = CompilerContext.compile("{{typeof hello}}", {knownHelpers: {'typeof': true}, knownHelpersOnly: true});
it('Unknown helper in knownHelpers only mode should be passed as undefined', function() {
var template = CompilerContext.compile('{{typeof hello}}', {knownHelpers: {'typeof': true}, knownHelpersOnly: true});
var result = template({}, {helpers: {'typeof': function(arg) { return typeof arg; }, hello: function() { return "foo"; }}});
equal(result, "undefined", "'undefined' should === '" + result);
var result = template({}, {helpers: {'typeof': function(arg) { return typeof arg; }, hello: function() { return 'foo'; }}});
equal(result, 'undefined', "'undefined' should === '" + result);
});
it("Builtin helpers available in knownHelpers only mode", function() {
var template = CompilerContext.compile("{{#unless foo}}bar{{/unless}}", {knownHelpersOnly: true});
it('Builtin helpers available in knownHelpers only mode', function() {
var template = CompilerContext.compile('{{#unless foo}}bar{{/unless}}', {knownHelpersOnly: true});
var result = template({});
equal(result, "bar", "'bar' should === '" + result);
equal(result, 'bar', "'bar' should === '" + result);
});
it("Field lookup works in knownHelpers only mode", function() {
var template = CompilerContext.compile("{{foo}}", {knownHelpersOnly: true});
it('Field lookup works in knownHelpers only mode', function() {
var template = CompilerContext.compile('{{foo}}', {knownHelpersOnly: true});
var result = template({foo: 'bar'});
equal(result, "bar", "'bar' should === '" + result);
equal(result, 'bar', "'bar' should === '" + result);
});
it("Conditional blocks work in knownHelpers only mode", function() {
var template = CompilerContext.compile("{{#foo}}bar{{/foo}}", {knownHelpersOnly: true});
it('Conditional blocks work in knownHelpers only mode', function() {
var template = CompilerContext.compile('{{#foo}}bar{{/foo}}', {knownHelpersOnly: true});
var result = template({foo: 'baz'});
equal(result, "bar", "'bar' should === '" + result);
equal(result, 'bar', "'bar' should === '" + result);
});
it("Invert blocks work in knownHelpers only mode", function() {
var template = CompilerContext.compile("{{^foo}}bar{{/foo}}", {knownHelpersOnly: true});
it('Invert blocks work in knownHelpers only mode', function() {
var template = CompilerContext.compile('{{^foo}}bar{{/foo}}', {knownHelpersOnly: true});
var result = template({foo: false});
equal(result, "bar", "'bar' should === '" + result);
equal(result, 'bar', "'bar' should === '" + result);
});
it("Functions are bound to the context in knownHelpers only mode", function() {
var template = CompilerContext.compile("{{foo}}", {knownHelpersOnly: true});
it('Functions are bound to the context in knownHelpers only mode', function() {
var template = CompilerContext.compile('{{foo}}', {knownHelpersOnly: true});
var result = template({foo: function() { return this.bar; }, bar: 'bar'});
equal(result, "bar", "'bar' should === '" + result);
equal(result, 'bar', "'bar' should === '" + result);
});
it("Unknown helper call in knownHelpers only mode should throw", function() {
it('Unknown helper call in knownHelpers only mode should throw', function() {
shouldThrow(function() {
CompilerContext.compile("{{typeof hello}}", {knownHelpersOnly: true});
CompilerContext.compile('{{typeof hello}}', {knownHelpersOnly: true});
}, Error);
});
});
describe("blockHelperMissing", function() {
it("lambdas are resolved by blockHelperMissing, not handlebars proper", function() {
var string = "{{#truthy}}yep{{/truthy}}";
describe('blockHelperMissing', function() {
it('lambdas are resolved by blockHelperMissing, not handlebars proper', function() {
var string = '{{#truthy}}yep{{/truthy}}';
var data = { truthy: function() { return true; } };
shouldCompileTo(string, data, "yep");
shouldCompileTo(string, data, 'yep');
});
it("lambdas resolved by blockHelperMissing are bound to the context", function() {
var string = "{{#truthy}}yep{{/truthy}}";
it('lambdas resolved by blockHelperMissing are bound to the context', function() {
var string = '{{#truthy}}yep{{/truthy}}';
var boundData = { truthy: function() { return this.truthiness(); }, truthiness: function() { return false; } };
shouldCompileTo(string, boundData, "");
shouldCompileTo(string, boundData, '');
});
});
@@ -528,13 +525,13 @@ describe('helpers', function() {
var context = {};
var helpers = {
blockHelperMissing: function() {
return 'missing: ' + arguments[arguments.length-1].name;
return 'missing: ' + arguments[arguments.length - 1].name;
},
helperMissing: function() {
return 'helper missing: ' + arguments[arguments.length-1].name;
return 'helper missing: ' + arguments[arguments.length - 1].name;
},
helper: function() {
return 'ran: ' + arguments[arguments.length-1].name;
return 'ran: ' + arguments[arguments.length - 1].name;
}
};
@@ -554,7 +551,7 @@ describe('helpers', function() {
shouldCompileTo('{{#helper 1}}{{/helper}}', [context, helpers], 'ran: helper');
});
it('should include in known helper calls', function() {
var template = CompilerContext.compile("{{helper}}", {knownHelpers: {'helper': true}, knownHelpersOnly: true});
var template = CompilerContext.compile('{{helper}}', {knownHelpers: {'helper': true}, knownHelpersOnly: true});
equal(template({}, {helpers: helpers}), 'ran: helper');
});
@@ -569,8 +566,8 @@ describe('helpers', function() {
});
describe('name conflicts', function() {
it("helpers take precedence over same-named context properties", function() {
var template = CompilerContext.compile("{{goodbye}} {{cruel world}}");
it('helpers take precedence over same-named context properties', function() {
var template = CompilerContext.compile('{{goodbye}} {{cruel world}}');
var helpers = {
goodbye: function() {
@@ -578,21 +575,21 @@ describe('helpers', function() {
},
cruel: function(world) {
return "cruel " + world.toUpperCase();
return 'cruel ' + world.toUpperCase();
}
};
var context = {
goodbye: "goodbye",
world: "world"
goodbye: 'goodbye',
world: 'world'
};
var result = template(context, {helpers: helpers});
equals(result, "GOODBYE cruel WORLD", "Helper executed");
equals(result, 'GOODBYE cruel WORLD', 'Helper executed');
});
it("helpers take precedence over same-named context properties$", function() {
var template = CompilerContext.compile("{{#goodbye}} {{cruel world}}{{/goodbye}}");
it('helpers take precedence over same-named context properties$', function() {
var template = CompilerContext.compile('{{#goodbye}} {{cruel world}}{{/goodbye}}');
var helpers = {
goodbye: function(options) {
@@ -600,21 +597,21 @@ describe('helpers', function() {
},
cruel: function(world) {
return "cruel " + world.toUpperCase();
return 'cruel ' + world.toUpperCase();
}
};
var context = {
goodbye: "goodbye",
world: "world"
goodbye: 'goodbye',
world: 'world'
};
var result = template(context, {helpers: helpers});
equals(result, "GOODBYE cruel WORLD", "Helper executed");
equals(result, 'GOODBYE cruel WORLD', 'Helper executed');
});
it("Scoped names take precedence over helpers", function() {
var template = CompilerContext.compile("{{this.goodbye}} {{cruel world}} {{cruel this.goodbye}}");
it('Scoped names take precedence over helpers', function() {
var template = CompilerContext.compile('{{this.goodbye}} {{cruel world}} {{cruel this.goodbye}}');
var helpers = {
goodbye: function() {
@@ -622,21 +619,21 @@ describe('helpers', function() {
},
cruel: function(world) {
return "cruel " + world.toUpperCase();
return 'cruel ' + world.toUpperCase();
}
};
var context = {
goodbye: "goodbye",
world: "world"
goodbye: 'goodbye',
world: 'world'
};
var result = template(context, {helpers: helpers});
equals(result, "goodbye cruel WORLD cruel GOODBYE", "Helper not executed");
equals(result, 'goodbye cruel WORLD cruel GOODBYE', 'Helper not executed');
});
it("Scoped names take precedence over block helpers", function() {
var template = CompilerContext.compile("{{#goodbye}} {{cruel world}}{{/goodbye}} {{this.goodbye}}");
it('Scoped names take precedence over block helpers', function() {
var template = CompilerContext.compile('{{#goodbye}} {{cruel world}}{{/goodbye}} {{this.goodbye}}');
var helpers = {
goodbye: function(options) {
@@ -644,17 +641,17 @@ describe('helpers', function() {
},
cruel: function(world) {
return "cruel " + world.toUpperCase();
return 'cruel ' + world.toUpperCase();
}
};
var context = {
goodbye: "goodbye",
world: "world"
goodbye: 'goodbye',
world: 'world'
};
var result = template(context, {helpers: helpers});
equals(result, "GOODBYE cruel WORLD goodbye", "Helper executed");
equals(result, 'GOODBYE cruel WORLD goodbye', 'Helper executed');
});
});
+7 -6
View File
@@ -1,4 +1,3 @@
/*global Handlebars, beforeEach, handlebarsEnv, shouldCompileTo */
describe('javascript-compiler api', function() {
if (!Handlebars.JavaScriptCompiler) {
return;
@@ -17,13 +16,15 @@ describe('javascript-compiler api', function() {
handlebarsEnv.JavaScriptCompiler.prototype.nameLookup = function(parent, name) {
return parent + '.bar_' + name;
};
shouldCompileTo("{{foo}}", { bar_foo: "food" }, "food");
/*eslint-disable camelcase */
shouldCompileTo('{{foo}}', { bar_foo: 'food' }, 'food');
/*eslint-enable camelcase */
});
// Tests nameLookup dot vs. bracket behavior. Bracket is required in certain cases
// to avoid errors in older browsers.
it('should handle reserved words', function() {
shouldCompileTo("{{foo}} {{~null~}}", { foo: "food" }, "food");
shouldCompileTo('{{foo}} {{~null~}}', { foo: 'food' }, 'food');
});
});
describe('#compilerInfo', function() {
@@ -45,7 +46,7 @@ describe('javascript-compiler api', function() {
throw new Error('It didn\'t work');
}
};
shouldCompileTo("{{foo}} ", { foo: "food" }, "food ");
shouldCompileTo('{{foo}} ', { foo: 'food' }, 'food ');
});
});
describe('buffer', function() {
@@ -65,13 +66,13 @@ describe('javascript-compiler api', function() {
handlebarsEnv.JavaScriptCompiler.prototype.initializeBuffer = function() {
return this.quotedString('foo_');
};
shouldCompileTo("{{foo}} ", { foo: "food" }, "foo_food ");
shouldCompileTo('{{foo}} ', { foo: 'food' }, 'foo_food ');
});
it('should allow append buffer override', function() {
handlebarsEnv.JavaScriptCompiler.prototype.appendToBuffer = function(string) {
return $superAppend.call(this, [string, ' + "_foo"']);
};
shouldCompileTo("{{foo}}", { foo: "food" }, "food_foo");
shouldCompileTo('{{foo}}', { foo: 'food' }, 'food_foo');
});
});
});
+74 -75
View File
@@ -1,235 +1,234 @@
/*global Handlebars, shouldThrow */
describe('parser', function() {
if (!Handlebars.print) {
return;
}
function ast_for(template) {
function astFor(template) {
var ast = Handlebars.parse(template);
return Handlebars.print(ast);
}
it('parses simple mustaches', function() {
equals(ast_for('{{123}}'), "{{ NUMBER{123} [] }}\n");
equals(ast_for('{{"foo"}}'), '{{ "foo" [] }}\n');
equals(ast_for('{{false}}'), '{{ BOOLEAN{false} [] }}\n');
equals(ast_for('{{true}}'), '{{ BOOLEAN{true} [] }}\n');
equals(ast_for('{{foo}}'), "{{ PATH:foo [] }}\n");
equals(ast_for('{{foo?}}'), "{{ PATH:foo? [] }}\n");
equals(ast_for('{{foo_}}'), "{{ PATH:foo_ [] }}\n");
equals(ast_for('{{foo-}}'), "{{ PATH:foo- [] }}\n");
equals(ast_for('{{foo:}}'), "{{ PATH:foo: [] }}\n");
equals(astFor('{{123}}'), '{{ NUMBER{123} [] }}\n');
equals(astFor('{{"foo"}}'), '{{ "foo" [] }}\n');
equals(astFor('{{false}}'), '{{ BOOLEAN{false} [] }}\n');
equals(astFor('{{true}}'), '{{ BOOLEAN{true} [] }}\n');
equals(astFor('{{foo}}'), '{{ PATH:foo [] }}\n');
equals(astFor('{{foo?}}'), '{{ PATH:foo? [] }}\n');
equals(astFor('{{foo_}}'), '{{ PATH:foo_ [] }}\n');
equals(astFor('{{foo-}}'), '{{ PATH:foo- [] }}\n');
equals(astFor('{{foo:}}'), '{{ PATH:foo: [] }}\n');
});
it('parses simple mustaches with data', function() {
equals(ast_for("{{@foo}}"), "{{ @PATH:foo [] }}\n");
equals(astFor('{{@foo}}'), '{{ @PATH:foo [] }}\n');
});
it('parses simple mustaches with data paths', function() {
equals(ast_for("{{@../foo}}"), "{{ @PATH:foo [] }}\n");
equals(astFor('{{@../foo}}'), '{{ @PATH:foo [] }}\n');
});
it('parses mustaches with paths', function() {
equals(ast_for("{{foo/bar}}"), "{{ PATH:foo/bar [] }}\n");
equals(astFor('{{foo/bar}}'), '{{ PATH:foo/bar [] }}\n');
});
it('parses mustaches with this/foo', function() {
equals(ast_for("{{this/foo}}"), "{{ PATH:foo [] }}\n");
equals(astFor('{{this/foo}}'), '{{ PATH:foo [] }}\n');
});
it('parses mustaches with - in a path', function() {
equals(ast_for("{{foo-bar}}"), "{{ PATH:foo-bar [] }}\n");
equals(astFor('{{foo-bar}}'), '{{ PATH:foo-bar [] }}\n');
});
it('parses mustaches with parameters', function() {
equals(ast_for("{{foo bar}}"), "{{ PATH:foo [PATH:bar] }}\n");
equals(astFor('{{foo bar}}'), '{{ PATH:foo [PATH:bar] }}\n');
});
it('parses mustaches with string parameters', function() {
equals(ast_for("{{foo bar \"baz\" }}"), '{{ PATH:foo [PATH:bar, "baz"] }}\n');
equals(astFor('{{foo bar \"baz\" }}'), '{{ PATH:foo [PATH:bar, "baz"] }}\n');
});
it('parses mustaches with NUMBER parameters', function() {
equals(ast_for("{{foo 1}}"), "{{ PATH:foo [NUMBER{1}] }}\n");
equals(astFor('{{foo 1}}'), '{{ PATH:foo [NUMBER{1}] }}\n');
});
it('parses mustaches with BOOLEAN parameters', function() {
equals(ast_for("{{foo true}}"), "{{ PATH:foo [BOOLEAN{true}] }}\n");
equals(ast_for("{{foo false}}"), "{{ PATH:foo [BOOLEAN{false}] }}\n");
equals(astFor('{{foo true}}'), '{{ PATH:foo [BOOLEAN{true}] }}\n');
equals(astFor('{{foo false}}'), '{{ PATH:foo [BOOLEAN{false}] }}\n');
});
it('parses mustaches with undefined and null paths', function() {
equals(ast_for("{{undefined}}"), "{{ UNDEFINED [] }}\n");
equals(ast_for("{{null}}"), "{{ NULL [] }}\n");
equals(astFor('{{undefined}}'), '{{ UNDEFINED [] }}\n');
equals(astFor('{{null}}'), '{{ NULL [] }}\n');
});
it('parses mustaches with undefined and null parameters', function() {
equals(ast_for("{{foo undefined null}}"), "{{ PATH:foo [UNDEFINED, NULL] }}\n");
equals(astFor('{{foo undefined null}}'), '{{ PATH:foo [UNDEFINED, NULL] }}\n');
});
it('parses mutaches with DATA parameters', function() {
equals(ast_for("{{foo @bar}}"), "{{ PATH:foo [@PATH:bar] }}\n");
equals(astFor('{{foo @bar}}'), '{{ PATH:foo [@PATH:bar] }}\n');
});
it('parses mustaches with hash arguments', function() {
equals(ast_for("{{foo bar=baz}}"), "{{ PATH:foo [] HASH{bar=PATH:baz} }}\n");
equals(ast_for("{{foo bar=1}}"), "{{ PATH:foo [] HASH{bar=NUMBER{1}} }}\n");
equals(ast_for("{{foo bar=true}}"), "{{ PATH:foo [] HASH{bar=BOOLEAN{true}} }}\n");
equals(ast_for("{{foo bar=false}}"), "{{ PATH:foo [] HASH{bar=BOOLEAN{false}} }}\n");
equals(ast_for("{{foo bar=@baz}}"), "{{ PATH:foo [] HASH{bar=@PATH:baz} }}\n");
equals(astFor('{{foo bar=baz}}'), '{{ PATH:foo [] HASH{bar=PATH:baz} }}\n');
equals(astFor('{{foo bar=1}}'), '{{ PATH:foo [] HASH{bar=NUMBER{1}} }}\n');
equals(astFor('{{foo bar=true}}'), '{{ PATH:foo [] HASH{bar=BOOLEAN{true}} }}\n');
equals(astFor('{{foo bar=false}}'), '{{ PATH:foo [] HASH{bar=BOOLEAN{false}} }}\n');
equals(astFor('{{foo bar=@baz}}'), '{{ PATH:foo [] HASH{bar=@PATH:baz} }}\n');
equals(ast_for("{{foo bar=baz bat=bam}}"), "{{ PATH:foo [] HASH{bar=PATH:baz, bat=PATH:bam} }}\n");
equals(ast_for("{{foo bar=baz bat=\"bam\"}}"), '{{ PATH:foo [] HASH{bar=PATH:baz, bat="bam"} }}\n');
equals(astFor('{{foo bar=baz bat=bam}}'), '{{ PATH:foo [] HASH{bar=PATH:baz, bat=PATH:bam} }}\n');
equals(astFor('{{foo bar=baz bat="bam"}}'), '{{ PATH:foo [] HASH{bar=PATH:baz, bat="bam"} }}\n');
equals(ast_for("{{foo bat='bam'}}"), '{{ PATH:foo [] HASH{bat="bam"} }}\n');
equals(astFor("{{foo bat='bam'}}"), '{{ PATH:foo [] HASH{bat="bam"} }}\n');
equals(ast_for("{{foo omg bar=baz bat=\"bam\"}}"), '{{ PATH:foo [PATH:omg] HASH{bar=PATH:baz, bat="bam"} }}\n');
equals(ast_for("{{foo omg bar=baz bat=\"bam\" baz=1}}"), '{{ PATH:foo [PATH:omg] HASH{bar=PATH:baz, bat="bam", baz=NUMBER{1}} }}\n');
equals(ast_for("{{foo omg bar=baz bat=\"bam\" baz=true}}"), '{{ PATH:foo [PATH:omg] HASH{bar=PATH:baz, bat="bam", baz=BOOLEAN{true}} }}\n');
equals(ast_for("{{foo omg bar=baz bat=\"bam\" baz=false}}"), '{{ PATH:foo [PATH:omg] HASH{bar=PATH:baz, bat="bam", baz=BOOLEAN{false}} }}\n');
equals(astFor('{{foo omg bar=baz bat=\"bam\"}}'), '{{ PATH:foo [PATH:omg] HASH{bar=PATH:baz, bat="bam"} }}\n');
equals(astFor('{{foo omg bar=baz bat=\"bam\" baz=1}}'), '{{ PATH:foo [PATH:omg] HASH{bar=PATH:baz, bat="bam", baz=NUMBER{1}} }}\n');
equals(astFor('{{foo omg bar=baz bat=\"bam\" baz=true}}'), '{{ PATH:foo [PATH:omg] HASH{bar=PATH:baz, bat="bam", baz=BOOLEAN{true}} }}\n');
equals(astFor('{{foo omg bar=baz bat=\"bam\" baz=false}}'), '{{ PATH:foo [PATH:omg] HASH{bar=PATH:baz, bat="bam", baz=BOOLEAN{false}} }}\n');
});
it('parses contents followed by a mustache', function() {
equals(ast_for("foo bar {{baz}}"), "CONTENT[ \'foo bar \' ]\n{{ PATH:baz [] }}\n");
equals(astFor('foo bar {{baz}}'), 'CONTENT[ \'foo bar \' ]\n{{ PATH:baz [] }}\n');
});
it('parses a partial', function() {
equals(ast_for("{{> foo }}"), "{{> PARTIAL:foo }}\n");
equals(ast_for("{{> 'foo' }}"), "{{> PARTIAL:foo }}\n");
equals(ast_for("{{> 1 }}"), "{{> PARTIAL:1 }}\n");
equals(astFor('{{> foo }}'), '{{> PARTIAL:foo }}\n');
equals(astFor('{{> "foo" }}'), '{{> PARTIAL:foo }}\n');
equals(astFor('{{> 1 }}'), '{{> PARTIAL:1 }}\n');
});
it('parses a partial with context', function() {
equals(ast_for("{{> foo bar}}"), "{{> PARTIAL:foo PATH:bar }}\n");
equals(astFor('{{> foo bar}}'), '{{> PARTIAL:foo PATH:bar }}\n');
});
it('parses a partial with hash', function() {
equals(ast_for("{{> foo bar=bat}}"), "{{> PARTIAL:foo HASH{bar=PATH:bat} }}\n");
equals(astFor('{{> foo bar=bat}}'), '{{> PARTIAL:foo HASH{bar=PATH:bat} }}\n');
});
it('parses a partial with context and hash', function() {
equals(ast_for("{{> foo bar bat=baz}}"), "{{> PARTIAL:foo PATH:bar HASH{bat=PATH:baz} }}\n");
equals(astFor('{{> foo bar bat=baz}}'), '{{> PARTIAL:foo PATH:bar HASH{bat=PATH:baz} }}\n');
});
it('parses a partial with a complex name', function() {
equals(ast_for("{{> shared/partial?.bar}}"), "{{> PARTIAL:shared/partial?.bar }}\n");
equals(astFor('{{> shared/partial?.bar}}'), '{{> PARTIAL:shared/partial?.bar }}\n');
});
it('parses a comment', function() {
equals(ast_for("{{! this is a comment }}"), "{{! ' this is a comment ' }}\n");
equals(astFor('{{! this is a comment }}'), "{{! ' this is a comment ' }}\n");
});
it('parses a multi-line comment', function() {
equals(ast_for("{{!\nthis is a multi-line comment\n}}"), "{{! \'\nthis is a multi-line comment\n\' }}\n");
equals(astFor('{{!\nthis is a multi-line comment\n}}'), "{{! \'\nthis is a multi-line comment\n\' }}\n");
});
it('parses an inverse section', function() {
equals(ast_for("{{#foo}} bar {{^}} baz {{/foo}}"), "BLOCK:\n PATH:foo []\n PROGRAM:\n CONTENT[ ' bar ' ]\n {{^}}\n CONTENT[ ' baz ' ]\n");
equals(astFor('{{#foo}} bar {{^}} baz {{/foo}}'), "BLOCK:\n PATH:foo []\n PROGRAM:\n CONTENT[ ' bar ' ]\n {{^}}\n CONTENT[ ' baz ' ]\n");
});
it('parses an inverse (else-style) section', function() {
equals(ast_for("{{#foo}} bar {{else}} baz {{/foo}}"), "BLOCK:\n PATH:foo []\n PROGRAM:\n CONTENT[ ' bar ' ]\n {{^}}\n CONTENT[ ' baz ' ]\n");
equals(astFor('{{#foo}} bar {{else}} baz {{/foo}}'), "BLOCK:\n PATH:foo []\n PROGRAM:\n CONTENT[ ' bar ' ]\n {{^}}\n CONTENT[ ' baz ' ]\n");
});
it('parses multiple inverse sections', function() {
equals(ast_for("{{#foo}} bar {{else if bar}}{{else}} baz {{/foo}}"), "BLOCK:\n PATH:foo []\n PROGRAM:\n CONTENT[ ' bar ' ]\n {{^}}\n BLOCK:\n PATH:if [PATH:bar]\n PROGRAM:\n {{^}}\n CONTENT[ ' baz ' ]\n");
equals(astFor('{{#foo}} bar {{else if bar}}{{else}} baz {{/foo}}'), "BLOCK:\n PATH:foo []\n PROGRAM:\n CONTENT[ ' bar ' ]\n {{^}}\n BLOCK:\n PATH:if [PATH:bar]\n PROGRAM:\n {{^}}\n CONTENT[ ' baz ' ]\n");
});
it('parses empty blocks', function() {
equals(ast_for("{{#foo}}{{/foo}}"), "BLOCK:\n PATH:foo []\n PROGRAM:\n");
equals(astFor('{{#foo}}{{/foo}}'), 'BLOCK:\n PATH:foo []\n PROGRAM:\n');
});
it('parses empty blocks with empty inverse section', function() {
equals(ast_for("{{#foo}}{{^}}{{/foo}}"), "BLOCK:\n PATH:foo []\n PROGRAM:\n {{^}}\n");
equals(astFor('{{#foo}}{{^}}{{/foo}}'), 'BLOCK:\n PATH:foo []\n PROGRAM:\n {{^}}\n');
});
it('parses empty blocks with empty inverse (else-style) section', function() {
equals(ast_for("{{#foo}}{{else}}{{/foo}}"), "BLOCK:\n PATH:foo []\n PROGRAM:\n {{^}}\n");
equals(astFor('{{#foo}}{{else}}{{/foo}}'), 'BLOCK:\n PATH:foo []\n PROGRAM:\n {{^}}\n');
});
it('parses non-empty blocks with empty inverse section', function() {
equals(ast_for("{{#foo}} bar {{^}}{{/foo}}"), "BLOCK:\n PATH:foo []\n PROGRAM:\n CONTENT[ ' bar ' ]\n {{^}}\n");
equals(astFor('{{#foo}} bar {{^}}{{/foo}}'), "BLOCK:\n PATH:foo []\n PROGRAM:\n CONTENT[ ' bar ' ]\n {{^}}\n");
});
it('parses non-empty blocks with empty inverse (else-style) section', function() {
equals(ast_for("{{#foo}} bar {{else}}{{/foo}}"), "BLOCK:\n PATH:foo []\n PROGRAM:\n CONTENT[ ' bar ' ]\n {{^}}\n");
equals(astFor('{{#foo}} bar {{else}}{{/foo}}'), "BLOCK:\n PATH:foo []\n PROGRAM:\n CONTENT[ ' bar ' ]\n {{^}}\n");
});
it('parses empty blocks with non-empty inverse section', function() {
equals(ast_for("{{#foo}}{{^}} bar {{/foo}}"), "BLOCK:\n PATH:foo []\n PROGRAM:\n {{^}}\n CONTENT[ ' bar ' ]\n");
equals(astFor('{{#foo}}{{^}} bar {{/foo}}'), "BLOCK:\n PATH:foo []\n PROGRAM:\n {{^}}\n CONTENT[ ' bar ' ]\n");
});
it('parses empty blocks with non-empty inverse (else-style) section', function() {
equals(ast_for("{{#foo}}{{else}} bar {{/foo}}"), "BLOCK:\n PATH:foo []\n PROGRAM:\n {{^}}\n CONTENT[ ' bar ' ]\n");
equals(astFor('{{#foo}}{{else}} bar {{/foo}}'), "BLOCK:\n PATH:foo []\n PROGRAM:\n {{^}}\n CONTENT[ ' bar ' ]\n");
});
it('parses a standalone inverse section', function() {
equals(ast_for("{{^foo}}bar{{/foo}}"), "BLOCK:\n PATH:foo []\n {{^}}\n CONTENT[ 'bar' ]\n");
equals(astFor('{{^foo}}bar{{/foo}}'), "BLOCK:\n PATH:foo []\n {{^}}\n CONTENT[ 'bar' ]\n");
});
it('throws on old inverse section', function() {
shouldThrow(function() {
ast_for("{{else foo}}bar{{/foo}}");
astFor('{{else foo}}bar{{/foo}}');
}, Error);
});
it('parses block with block params', function() {
equals(ast_for("{{#foo as |bar baz|}}content{{/foo}}"), "BLOCK:\n PATH:foo []\n PROGRAM:\n BLOCK PARAMS: [ bar baz ]\n CONTENT[ 'content' ]\n");
equals(astFor('{{#foo as |bar baz|}}content{{/foo}}'), "BLOCK:\n PATH:foo []\n PROGRAM:\n BLOCK PARAMS: [ bar baz ]\n CONTENT[ 'content' ]\n");
});
it('parses inverse block with block params', function() {
equals(ast_for("{{^foo as |bar baz|}}content{{/foo}}"), "BLOCK:\n PATH:foo []\n {{^}}\n BLOCK PARAMS: [ bar baz ]\n CONTENT[ 'content' ]\n");
equals(astFor('{{^foo as |bar baz|}}content{{/foo}}'), "BLOCK:\n PATH:foo []\n {{^}}\n BLOCK PARAMS: [ bar baz ]\n CONTENT[ 'content' ]\n");
});
it('parses chained inverse block with block params', function() {
equals(ast_for("{{#foo}}{{else foo as |bar baz|}}content{{/foo}}"), "BLOCK:\n PATH:foo []\n PROGRAM:\n {{^}}\n BLOCK:\n PATH:foo []\n PROGRAM:\n BLOCK PARAMS: [ bar baz ]\n CONTENT[ 'content' ]\n");
equals(astFor('{{#foo}}{{else foo as |bar baz|}}content{{/foo}}'), "BLOCK:\n PATH:foo []\n PROGRAM:\n {{^}}\n BLOCK:\n PATH:foo []\n PROGRAM:\n BLOCK PARAMS: [ bar baz ]\n CONTENT[ 'content' ]\n");
});
it("raises if there's a Parse error", function() {
it('raises if there\'s a Parse error', function() {
shouldThrow(function() {
ast_for("foo{{^}}bar");
astFor('foo{{^}}bar');
}, Error, /Parse error on line 1/);
shouldThrow(function() {
ast_for("{{foo}");
astFor('{{foo}');
}, Error, /Parse error on line 1/);
shouldThrow(function() {
ast_for("{{foo &}}");
astFor('{{foo &}}');
}, Error, /Parse error on line 1/);
shouldThrow(function() {
ast_for("{{#goodbyes}}{{/hellos}}");
astFor('{{#goodbyes}}{{/hellos}}');
}, Error, /goodbyes doesn't match hellos/);
shouldThrow(function() {
ast_for("{{{{goodbyes}}}} {{{{/hellos}}}}");
astFor('{{{{goodbyes}}}} {{{{/hellos}}}}');
}, Error, /goodbyes doesn't match hellos/);
});
it('should handle invalid paths', function() {
shouldThrow(function() {
ast_for("{{foo/../bar}}");
astFor('{{foo/../bar}}');
}, Error, /Invalid path: foo\/\.\. - 1:2/);
shouldThrow(function() {
ast_for("{{foo/./bar}}");
astFor('{{foo/./bar}}');
}, Error, /Invalid path: foo\/\. - 1:2/);
shouldThrow(function() {
ast_for("{{foo/this/bar}}");
astFor('{{foo/this/bar}}');
}, Error, /Invalid path: foo\/this - 1:2/);
});
it('knows how to report the correct line number in errors', function() {
shouldThrow(function() {
ast_for("hello\nmy\n{{foo}");
astFor('hello\nmy\n{{foo}');
}, Error, /Parse error on line 3/);
shouldThrow(function() {
ast_for("hello\n\nmy\n\n{{foo}");
astFor('hello\n\nmy\n\n{{foo}');
}, Error, /Parse error on line 5/);
});
it('knows how to report the correct line number in errors when the first character is a newline', function() {
shouldThrow(function() {
ast_for("\n\nhello\n\nmy\n\n{{foo}");
astFor('\n\nhello\n\nmy\n\n{{foo}');
}, Error, /Parse error on line 7/);
});
describe('externally compiled AST', function() {
it('can pass through an already-compiled AST', function() {
equals(ast_for(new Handlebars.AST.Program([new Handlebars.AST.ContentStatement("Hello")], null)), "CONTENT[ \'Hello\' ]\n");
equals(astFor(new Handlebars.AST.Program([new Handlebars.AST.ContentStatement('Hello')], null)), 'CONTENT[ \'Hello\' ]\n');
});
});
});
+106 -107
View File
@@ -1,11 +1,10 @@
/*global CompilerContext, Handlebars, handlebarsEnv, shouldCompileTo, shouldCompileToWithPartials, shouldThrow */
describe('partials', function() {
it('basic partials', function() {
var string = 'Dudes: {{#dudes}}{{> dude}}{{/dudes}}';
var partial = '{{name}} ({{url}}) ';
var hash = {dudes: [{name: 'Yehuda', url: 'http://yehuda'}, {name: 'Alan', url: 'http://alan'}]};
shouldCompileToWithPartials(string, [hash, {}, {dude: partial}], true, 'Dudes: Yehuda (http://yehuda) Alan (http://alan) ');
shouldCompileToWithPartials(string, [hash, {}, {dude: partial},,false], true, 'Dudes: Yehuda (http://yehuda) Alan (http://alan) ');
shouldCompileToWithPartials(string, [hash, {}, {dude: partial},, false], true, 'Dudes: Yehuda (http://yehuda) Alan (http://alan) ');
});
it('dynamic partials', function() {
@@ -18,7 +17,7 @@ describe('partials', function() {
}
};
shouldCompileToWithPartials(string, [hash, helpers, {dude: partial}], true, 'Dudes: Yehuda (http://yehuda) Alan (http://alan) ');
shouldCompileToWithPartials(string, [hash, helpers, {dude: partial},,false], true, 'Dudes: Yehuda (http://yehuda) Alan (http://alan) ');
shouldCompileToWithPartials(string, [hash, helpers, {dude: partial},, false], true, 'Dudes: Yehuda (http://yehuda) Alan (http://alan) ');
});
it('failing dynamic partials', function() {
var string = 'Dudes: {{#dudes}}{{> (partial)}}{{/dudes}}';
@@ -34,19 +33,19 @@ describe('partials', function() {
}, Handlebars.Exception, 'The partial missing could not be found');
});
it("partials with context", function() {
var string = "Dudes: {{>dude dudes}}";
var partial = "{{#this}}{{name}} ({{url}}) {{/this}}";
var hash = {dudes: [{name: "Yehuda", url: "http://yehuda"}, {name: "Alan", url: "http://alan"}]};
shouldCompileToWithPartials(string, [hash, {}, {dude: partial}], true, "Dudes: Yehuda (http://yehuda) Alan (http://alan) ",
"Partials can be passed a context");
it('partials with context', function() {
var string = 'Dudes: {{>dude dudes}}';
var partial = '{{#this}}{{name}} ({{url}}) {{/this}}';
var hash = {dudes: [{name: 'Yehuda', url: 'http://yehuda'}, {name: 'Alan', url: 'http://alan'}]};
shouldCompileToWithPartials(string, [hash, {}, {dude: partial}], true, 'Dudes: Yehuda (http://yehuda) Alan (http://alan) ',
'Partials can be passed a context');
});
it("partials with undefined context", function() {
var string = "Dudes: {{>dude dudes}}";
var partial = "{{foo}} Empty";
it('partials with undefined context', function() {
var string = 'Dudes: {{>dude dudes}}';
var partial = '{{foo}} Empty';
var hash = {};
shouldCompileToWithPartials(string, [hash, {}, {dude: partial}], true, "Dudes: Empty");
shouldCompileToWithPartials(string, [hash, {}, {dude: partial}], true, 'Dudes: Empty');
});
it('partials with duplicate parameters', function() {
@@ -55,133 +54,133 @@ describe('partials', function() {
}, Error, 'Unsupported number of partial arguments: 2 - 1:7');
});
it("partials with parameters", function() {
var string = "Dudes: {{#dudes}}{{> dude others=..}}{{/dudes}}";
var partial = "{{others.foo}}{{name}} ({{url}}) ";
var hash = {foo: 'bar', dudes: [{name: "Yehuda", url: "http://yehuda"}, {name: "Alan", url: "http://alan"}]};
shouldCompileToWithPartials(string, [hash, {}, {dude: partial}], true, "Dudes: barYehuda (http://yehuda) barAlan (http://alan) ",
"Basic partials output based on current context.");
it('partials with parameters', function() {
var string = 'Dudes: {{#dudes}}{{> dude others=..}}{{/dudes}}';
var partial = '{{others.foo}}{{name}} ({{url}}) ';
var hash = {foo: 'bar', dudes: [{name: 'Yehuda', url: 'http://yehuda'}, {name: 'Alan', url: 'http://alan'}]};
shouldCompileToWithPartials(string, [hash, {}, {dude: partial}], true, 'Dudes: barYehuda (http://yehuda) barAlan (http://alan) ',
'Basic partials output based on current context.');
});
it("partial in a partial", function() {
var string = "Dudes: {{#dudes}}{{>dude}}{{/dudes}}";
var dude = "{{name}} {{> url}} ";
var url = "<a href='{{url}}'>{{url}}</a>";
var hash = {dudes: [{name: "Yehuda", url: "http://yehuda"}, {name: "Alan", url: "http://alan"}]};
shouldCompileToWithPartials(string, [hash, {}, {dude: dude, url: url}], true, "Dudes: Yehuda <a href='http://yehuda'>http://yehuda</a> Alan <a href='http://alan'>http://alan</a> ", "Partials are rendered inside of other partials");
it('partial in a partial', function() {
var string = 'Dudes: {{#dudes}}{{>dude}}{{/dudes}}';
var dude = '{{name}} {{> url}} ';
var url = '<a href="{{url}}">{{url}}</a>';
var hash = {dudes: [{name: 'Yehuda', url: 'http://yehuda'}, {name: 'Alan', url: 'http://alan'}]};
shouldCompileToWithPartials(string, [hash, {}, {dude: dude, url: url}], true, 'Dudes: Yehuda <a href="http://yehuda">http://yehuda</a> Alan <a href="http://alan">http://alan</a> ', 'Partials are rendered inside of other partials');
});
it("rendering undefined partial throws an exception", function() {
it('rendering undefined partial throws an exception', function() {
shouldThrow(function() {
var template = CompilerContext.compile("{{> whatever}}");
var template = CompilerContext.compile('{{> whatever}}');
template();
}, Handlebars.Exception, 'The partial whatever could not be found');
});
it("registering undefined partial throws an exception", function() {
it('registering undefined partial throws an exception', function() {
shouldThrow(function() {
var undef;
handlebarsEnv.registerPartial('undefined_test', undef);
}, Handlebars.Exception, 'Attempting to register a partial as undefined');
});
it("rendering template partial in vm mode throws an exception", function() {
it('rendering template partial in vm mode throws an exception', function() {
shouldThrow(function() {
var template = CompilerContext.compile("{{> whatever}}");
var template = CompilerContext.compile('{{> whatever}}');
template();
}, Handlebars.Exception, 'The partial whatever could not be found');
});
it("rendering function partial in vm mode", function() {
var string = "Dudes: {{#dudes}}{{> dude}}{{/dudes}}";
var partial = function(context) {
it('rendering function partial in vm mode', function() {
var string = 'Dudes: {{#dudes}}{{> dude}}{{/dudes}}';
function partial(context) {
return context.name + ' (' + context.url + ') ';
};
var hash = {dudes: [{name: "Yehuda", url: "http://yehuda"}, {name: "Alan", url: "http://alan"}]};
shouldCompileTo(string, [hash, {}, {dude: partial}], "Dudes: Yehuda (http://yehuda) Alan (http://alan) ",
"Function partials output based in VM.");
}
var hash = {dudes: [{name: 'Yehuda', url: 'http://yehuda'}, {name: 'Alan', url: 'http://alan'}]};
shouldCompileTo(string, [hash, {}, {dude: partial}], 'Dudes: Yehuda (http://yehuda) Alan (http://alan) ',
'Function partials output based in VM.');
});
it("GH-14: a partial preceding a selector", function() {
var string = "Dudes: {{>dude}} {{another_dude}}";
var dude = "{{name}}";
var hash = {name:"Jeepers", another_dude:"Creepers"};
shouldCompileToWithPartials(string, [hash, {}, {dude:dude}], true, "Dudes: Jeepers Creepers", "Regular selectors can follow a partial");
it('GH-14: a partial preceding a selector', function() {
var string = 'Dudes: {{>dude}} {{anotherDude}}';
var dude = '{{name}}';
var hash = {name: 'Jeepers', anotherDude: 'Creepers'};
shouldCompileToWithPartials(string, [hash, {}, {dude: dude}], true, 'Dudes: Jeepers Creepers', 'Regular selectors can follow a partial');
});
it("Partials with slash paths", function() {
var string = "Dudes: {{> shared/dude}}";
var dude = "{{name}}";
var hash = {name:"Jeepers", another_dude:"Creepers"};
shouldCompileToWithPartials(string, [hash, {}, {'shared/dude':dude}], true, "Dudes: Jeepers", "Partials can use literal paths");
it('Partials with slash paths', function() {
var string = 'Dudes: {{> shared/dude}}';
var dude = '{{name}}';
var hash = {name: 'Jeepers', anotherDude: 'Creepers'};
shouldCompileToWithPartials(string, [hash, {}, {'shared/dude': dude}], true, 'Dudes: Jeepers', 'Partials can use literal paths');
});
it("Partials with slash and point paths", function() {
var string = "Dudes: {{> shared/dude.thing}}";
var dude = "{{name}}";
var hash = {name:"Jeepers", another_dude:"Creepers"};
shouldCompileToWithPartials(string, [hash, {}, {'shared/dude.thing':dude}], true, "Dudes: Jeepers", "Partials can use literal with points in paths");
it('Partials with slash and point paths', function() {
var string = 'Dudes: {{> shared/dude.thing}}';
var dude = '{{name}}';
var hash = {name: 'Jeepers', anotherDude: 'Creepers'};
shouldCompileToWithPartials(string, [hash, {}, {'shared/dude.thing': dude}], true, 'Dudes: Jeepers', 'Partials can use literal with points in paths');
});
it("Global Partials", function() {
handlebarsEnv.registerPartial('global_test', '{{another_dude}}');
it('Global Partials', function() {
handlebarsEnv.registerPartial('globalTest', '{{anotherDude}}');
var string = "Dudes: {{> shared/dude}} {{> global_test}}";
var dude = "{{name}}";
var hash = {name:"Jeepers", another_dude:"Creepers"};
shouldCompileToWithPartials(string, [hash, {}, {'shared/dude':dude}], true, "Dudes: Jeepers Creepers", "Partials can use globals or passed");
var string = 'Dudes: {{> shared/dude}} {{> globalTest}}';
var dude = '{{name}}';
var hash = {name: 'Jeepers', anotherDude: 'Creepers'};
shouldCompileToWithPartials(string, [hash, {}, {'shared/dude': dude}], true, 'Dudes: Jeepers Creepers', 'Partials can use globals or passed');
handlebarsEnv.unregisterPartial('global_test');
equals(handlebarsEnv.partials.global_test, undefined);
handlebarsEnv.unregisterPartial('globalTest');
equals(handlebarsEnv.partials.globalTest, undefined);
});
it("Multiple partial registration", function() {
it('Multiple partial registration', function() {
handlebarsEnv.registerPartial({
'shared/dude': '{{name}}',
global_test: '{{another_dude}}'
globalTest: '{{anotherDude}}'
});
var string = "Dudes: {{> shared/dude}} {{> global_test}}";
var hash = {name:"Jeepers", another_dude:"Creepers"};
shouldCompileToWithPartials(string, [hash], true, "Dudes: Jeepers Creepers", "Partials can use globals or passed");
var string = 'Dudes: {{> shared/dude}} {{> globalTest}}';
var hash = {name: 'Jeepers', anotherDude: 'Creepers'};
shouldCompileToWithPartials(string, [hash], true, 'Dudes: Jeepers Creepers', 'Partials can use globals or passed');
});
it("Partials with integer path", function() {
var string = "Dudes: {{> 404}}";
var dude = "{{name}}";
var hash = {name:"Jeepers", another_dude:"Creepers"};
shouldCompileToWithPartials(string, [hash, {}, {404:dude}], true, "Dudes: Jeepers", "Partials can use literal paths");
it('Partials with integer path', function() {
var string = 'Dudes: {{> 404}}';
var dude = '{{name}}';
var hash = {name: 'Jeepers', anotherDude: 'Creepers'};
shouldCompileToWithPartials(string, [hash, {}, {404: dude}], true, 'Dudes: Jeepers', 'Partials can use literal paths');
});
it("Partials with complex path", function() {
var string = "Dudes: {{> 404/asdf?.bar}}";
var dude = "{{name}}";
var hash = {name:"Jeepers", another_dude:"Creepers"};
shouldCompileToWithPartials(string, [hash, {}, {'404/asdf?.bar':dude}], true, "Dudes: Jeepers", "Partials can use literal paths");
it('Partials with complex path', function() {
var string = 'Dudes: {{> 404/asdf?.bar}}';
var dude = '{{name}}';
var hash = {name: 'Jeepers', anotherDude: 'Creepers'};
shouldCompileToWithPartials(string, [hash, {}, {'404/asdf?.bar': dude}], true, 'Dudes: Jeepers', 'Partials can use literal paths');
});
it("Partials with escaped", function() {
var string = "Dudes: {{> [+404/asdf?.bar]}}";
var dude = "{{name}}";
var hash = {name:"Jeepers", another_dude:"Creepers"};
shouldCompileToWithPartials(string, [hash, {}, {'+404/asdf?.bar':dude}], true, "Dudes: Jeepers", "Partials can use literal paths");
it('Partials with escaped', function() {
var string = 'Dudes: {{> [+404/asdf?.bar]}}';
var dude = '{{name}}';
var hash = {name: 'Jeepers', anotherDude: 'Creepers'};
shouldCompileToWithPartials(string, [hash, {}, {'+404/asdf?.bar': dude}], true, 'Dudes: Jeepers', 'Partials can use literal paths');
});
it("Partials with string", function() {
var string = "Dudes: {{> \"+404/asdf?.bar\"}}";
var dude = "{{name}}";
var hash = {name:"Jeepers", another_dude:"Creepers"};
shouldCompileToWithPartials(string, [hash, {}, {'+404/asdf?.bar':dude}], true, "Dudes: Jeepers", "Partials can use literal paths");
it('Partials with string', function() {
var string = 'Dudes: {{> \'+404/asdf?.bar\'}}';
var dude = '{{name}}';
var hash = {name: 'Jeepers', anotherDude: 'Creepers'};
shouldCompileToWithPartials(string, [hash, {}, {'+404/asdf?.bar': dude}], true, 'Dudes: Jeepers', 'Partials can use literal paths');
});
it('should handle empty partial', function() {
var string = "Dudes: {{#dudes}}{{> dude}}{{/dudes}}";
var partial = "";
var hash = {dudes: [{name: "Yehuda", url: "http://yehuda"}, {name: "Alan", url: "http://alan"}]};
shouldCompileToWithPartials(string, [hash, {}, {dude: partial}], true, "Dudes: ");
var string = 'Dudes: {{#dudes}}{{> dude}}{{/dudes}}';
var partial = '';
var hash = {dudes: [{name: 'Yehuda', url: 'http://yehuda'}, {name: 'Alan', url: 'http://alan'}]};
shouldCompileToWithPartials(string, [hash, {}, {dude: partial}], true, 'Dudes: ');
});
it("throw on missing partial", function() {
it('throw on missing partial', function() {
var compile = handlebarsEnv.compile;
handlebarsEnv.compile = undefined;
shouldThrow(function() {
@@ -200,28 +199,28 @@ describe('partials', function() {
});
describe('standalone partials', function() {
it("indented partials", function() {
var string = "Dudes:\n{{#dudes}}\n {{>dude}}\n{{/dudes}}";
var dude = "{{name}}\n";
var hash = {dudes: [{name: "Yehuda", url: "http://yehuda"}, {name: "Alan", url: "http://alan"}]};
it('indented partials', function() {
var string = 'Dudes:\n{{#dudes}}\n {{>dude}}\n{{/dudes}}';
var dude = '{{name}}\n';
var hash = {dudes: [{name: 'Yehuda', url: 'http://yehuda'}, {name: 'Alan', url: 'http://alan'}]};
shouldCompileToWithPartials(string, [hash, {}, {dude: dude}], true,
"Dudes:\n Yehuda\n Alan\n");
'Dudes:\n Yehuda\n Alan\n');
});
it("nested indented partials", function() {
var string = "Dudes:\n{{#dudes}}\n {{>dude}}\n{{/dudes}}";
var dude = "{{name}}\n {{> url}}";
var url = "{{url}}!\n";
var hash = {dudes: [{name: "Yehuda", url: "http://yehuda"}, {name: "Alan", url: "http://alan"}]};
it('nested indented partials', function() {
var string = 'Dudes:\n{{#dudes}}\n {{>dude}}\n{{/dudes}}';
var dude = '{{name}}\n {{> url}}';
var url = '{{url}}!\n';
var hash = {dudes: [{name: 'Yehuda', url: 'http://yehuda'}, {name: 'Alan', url: 'http://alan'}]};
shouldCompileToWithPartials(string, [hash, {}, {dude: dude, url: url}], true,
"Dudes:\n Yehuda\n http://yehuda!\n Alan\n http://alan!\n");
'Dudes:\n Yehuda\n http://yehuda!\n Alan\n http://alan!\n');
});
it("prevent nested indented partials", function() {
var string = "Dudes:\n{{#dudes}}\n {{>dude}}\n{{/dudes}}";
var dude = "{{name}}\n {{> url}}";
var url = "{{url}}!\n";
var hash = {dudes: [{name: "Yehuda", url: "http://yehuda"}, {name: "Alan", url: "http://alan"}]};
it('prevent nested indented partials', function() {
var string = 'Dudes:\n{{#dudes}}\n {{>dude}}\n{{/dudes}}';
var dude = '{{name}}\n {{> url}}';
var url = '{{url}}!\n';
var hash = {dudes: [{name: 'Yehuda', url: 'http://yehuda'}, {name: 'Alan', url: 'http://alan'}]};
shouldCompileToWithPartials(string, [hash, {}, {dude: dude, url: url}, {preventIndent: true}], true,
"Dudes:\n Yehuda\n http://yehuda!\n Alan\n http://alan!\n");
'Dudes:\n Yehuda\n http://yehuda!\n Alan\n http://alan!\n');
});
});
+1 -3
View File
@@ -1,6 +1,4 @@
/*global shouldThrow */
/*eslint-disable no-console */
describe('precompiler', function() {
// NOP Under non-node environments
if (typeof process === 'undefined') {
+43 -33
View File
@@ -1,26 +1,34 @@
/*global CompilerContext, shouldCompileTo, shouldThrow */
describe('Regressions', function() {
it("GH-94: Cannot read property of undefined", function() {
var data = {"books":[{"title":"The origin of species","author":{"name":"Charles Darwin"}},{"title":"Lazarillo de Tormes"}]};
var string = "{{#books}}{{title}}{{author.name}}{{/books}}";
shouldCompileTo(string, data, "The origin of speciesCharles DarwinLazarillo de Tormes",
"Renders without an undefined property error");
it('GH-94: Cannot read property of undefined', function() {
var data = {
'books': [{
'title': 'The origin of species',
'author': {
'name': 'Charles Darwin'
}
}, {
'title': 'Lazarillo de Tormes'
}]
};
var string = '{{#books}}{{title}}{{author.name}}{{/books}}';
shouldCompileTo(string, data, 'The origin of speciesCharles DarwinLazarillo de Tormes',
'Renders without an undefined property error');
});
it("GH-150: Inverted sections print when they shouldn't", function() {
var string = "{{^set}}not set{{/set}} :: {{#set}}set{{/set}}";
var string = '{{^set}}not set{{/set}} :: {{#set}}set{{/set}}';
shouldCompileTo(string, {}, "not set :: ", "inverted sections run when property isn't present in context");
shouldCompileTo(string, {set: undefined}, "not set :: ", "inverted sections run when property is undefined");
shouldCompileTo(string, {set: false}, "not set :: ", "inverted sections run when property is false");
shouldCompileTo(string, {set: true}, " :: set", "inverted sections don't run when property is true");
shouldCompileTo(string, {}, 'not set :: ', "inverted sections run when property isn't present in context");
shouldCompileTo(string, {set: undefined}, 'not set :: ', 'inverted sections run when property is undefined');
shouldCompileTo(string, {set: false}, 'not set :: ', 'inverted sections run when property is false');
shouldCompileTo(string, {set: true}, ' :: set', "inverted sections don't run when property is true");
});
it("GH-158: Using array index twice, breaks the template", function() {
var string = "{{arr.[0]}}, {{arr.[1]}}";
var data = { "arr": [1,2] };
it('GH-158: Using array index twice, breaks the template', function() {
var string = '{{arr.[0]}}, {{arr.[1]}}';
var data = { 'arr': [1, 2] };
shouldCompileTo(string, data, "1, 2", "it works as expected");
shouldCompileTo(string, data, '1, 2', 'it works as expected');
});
it("bug reported by @fat where lambdas weren't being properly resolved", function() {
@@ -39,12 +47,12 @@ describe('Regressions', function() {
+ '{{/hasThings}}';
var data = {
thing: function() {
return "blah";
return 'blah';
},
things: [
{className: "one", word: "@fat"},
{className: "two", word: "@dhg"},
{className: "three", word:"@sayrer"}
{className: 'one', word: '@fat'},
{className: 'two', word: '@dhg'},
{className: 'three', word: '@sayrer'}
],
hasThings: function() {
return true;
@@ -63,14 +71,14 @@ describe('Regressions', function() {
it('GH-408: Multiple loops fail', function() {
var context = [
{ name: "John Doe", location: { city: "Chicago" } },
{ name: "Jane Doe", location: { city: "New York"} }
{ name: 'John Doe', location: { city: 'Chicago' } },
{ name: 'Jane Doe', location: { city: 'New York'} }
];
var template = CompilerContext.compile('{{#.}}{{name}}{{/.}}{{#.}}{{name}}{{/.}}{{#.}}{{name}}{{/.}}');
var result = template(context);
equals(result, "John DoeJane DoeJohn DoeJane DoeJohn DoeJane Doe", 'It should output multiple times');
equals(result, 'John DoeJane DoeJohn DoeJane DoeJohn DoeJane Doe', 'It should output multiple times');
});
it('GS-428: Nested if else rendering', function() {
@@ -95,11 +103,13 @@ describe('Regressions', function() {
});
it('GH-534: Object prototype aliases', function() {
/*eslint-disable no-extend-native */
Object.prototype[0xD834] = true;
shouldCompileTo('{{foo}}', { foo: 'bar' }, 'bar');
delete Object.prototype[0xD834];
/*eslint-enable no-extend-native */
});
it('GH-437: Matching escaping', function() {
@@ -111,23 +121,23 @@ describe('Regressions', function() {
}, Error);
});
it("GH-676: Using array in escaping mustache fails", function() {
var string = "{{arr}}";
var data = { "arr": [1,2] };
it('GH-676: Using array in escaping mustache fails', function() {
var string = '{{arr}}';
var data = { 'arr': [1, 2] };
shouldCompileTo(string, data, data.arr.toString(), "it works as expected");
shouldCompileTo(string, data, data.arr.toString(), 'it works as expected');
});
it("Mustache man page", function() {
var string = "Hello {{name}}. You have just won ${{value}}!{{#in_ca}} Well, ${{taxed_value}}, after taxes.{{/in_ca}}";
it('Mustache man page', function() {
var string = 'Hello {{name}}. You have just won ${{value}}!{{#in_ca}} Well, ${{taxed_value}}, after taxes.{{/in_ca}}';
var data = {
"name": "Chris",
"value": 10000,
"taxed_value": 10000 - (10000 * 0.4),
"in_ca": true
'name': 'Chris',
'value': 10000,
'taxed_value': 10000 - (10000 * 0.4),
'in_ca': true
};
shouldCompileTo(string, data, "Hello Chris. You have just won $10000! Well, $6000, after taxes.", "the hello world mustache example works");
shouldCompileTo(string, data, 'Hello Chris. You have just won $10000! Well, $6000, after taxes.', 'the hello world mustache example works');
});
it('GH-731: zero context rendering', function() {
+7 -7
View File
@@ -1,21 +1,21 @@
if (typeof(require) !== 'undefined' && require.extensions[".handlebars"]) {
if (typeof require !== 'undefined' && require.extensions['.handlebars']) {
describe('Require', function() {
it('Load .handlebars files with require()', function() {
var template = require("./artifacts/example_1");
equal(template, require("./artifacts/example_1.handlebars"));
var template = require('./artifacts/example_1');
equal(template, require('./artifacts/example_1.handlebars'));
var expected = 'foo\n';
var result = template({foo: "foo"});
var result = template({foo: 'foo'});
equal(result, expected);
});
it('Load .hbs files with require()', function() {
var template = require("./artifacts/example_2");
equal(template, require("./artifacts/example_2.hbs"));
var template = require('./artifacts/example_2');
equal(template, require('./artifacts/example_2.hbs'));
var expected = 'Hello, World!\n';
var result = template({name: "World"});
var result = template({name: 'World'});
equal(result, expected);
});
-2
View File
@@ -1,5 +1,3 @@
/*globals Handlebars, shouldThrow */
describe('runtime', function() {
describe('#template', function() {
it('should throw on invalid templates', function() {
+5 -6
View File
@@ -1,8 +1,7 @@
/*global CompilerContext, Handlebars */
try {
if (typeof define !== 'function' || !define.amd) {
var SourceMap = require('source-map'),
SourceMapConsumer = SourceMap.SourceMapConsumer;
SourceMapConsumer = SourceMap.SourceMapConsumer;
}
} catch (err) {
/* NOP for in browser */
@@ -20,13 +19,13 @@ describe('source-map', function() {
equal(!!template.map, !CompilerContext.browser);
});
it('should map source properly', function() {
var source = ' b{{hello}} \n {{bar}}a {{#block arg hash=(subex 1 subval)}}{{/block}}',
template = Handlebars.precompile(source, {destName: 'dest.js', srcName: 'src.hbs'});
var templateSource = ' b{{hello}} \n {{bar}}a {{#block arg hash=(subex 1 subval)}}{{/block}}',
template = Handlebars.precompile(templateSource, {destName: 'dest.js', srcName: 'src.hbs'});
if (template.map) {
var consumer = new SourceMapConsumer(template.map),
lines = template.code.split('\n'),
srcLines = source.split('\n'),
srcLines = templateSource.split('\n'),
generated = grepLine('" b"', lines),
source = grepLine(' b', srcLines);
@@ -43,7 +42,7 @@ function grepLine(token, lines) {
var column = lines[i].indexOf(token);
if (column >= 0) {
return {
line: i+1,
line: i + 1,
column: column
};
}
+6 -5
View File
@@ -5,12 +5,11 @@ describe('spec', function() {
}
var _ = require('underscore'),
Handlebars = require('../lib'),
fs = require('fs');
var specDir =__dirname + '/mustache/specs/';
var specDir = __dirname + '/mustache/specs/';
var specs = _.filter(fs.readdirSync(specDir), function(name) {
return /.*\.json$/.test(name);
return (/.*\.json$/).test(name);
});
_.each(specs, function(name) {
@@ -26,8 +25,8 @@ describe('spec', function() {
// We nest the entire response from partials, not just the literals
|| (name === 'partials.json' && test.name === 'Standalone Indentation')
|| /\{\{\=/.test(test.template)
|| _.any(test.partials, function(partial) { return /\{\{\=/.test(partial); })) {
|| (/\{\{\=/).test(test.template)
|| _.any(test.partials, function(partial) { return (/\{\{\=/).test(partial); })) {
it.skip(name + ' - ' + test.name);
return;
}
@@ -35,7 +34,9 @@ describe('spec', function() {
var data = _.clone(test.data);
if (data.lambda) {
// Blergh
/*eslint-disable no-eval */
data.lambda = eval('(' + data.lambda.js + ')');
/*eslint-enable no-eval */
}
it(name + ' - ' + test.name, function() {
if (test.partials) {
-1
View File
@@ -1,4 +1,3 @@
/*global CompilerContext, Handlebars, shouldThrow */
var Exception = Handlebars.Exception;
describe('strict', function() {
+44 -45
View File
@@ -1,44 +1,43 @@
/*global CompilerContext */
describe('string params mode', function() {
it("arguments to helpers can be retrieved from options hash in string form", function() {
it('arguments to helpers can be retrieved from options hash in string form', function() {
var template = CompilerContext.compile('{{wycats is.a slave.driver}}', {stringParams: true});
var helpers = {
wycats: function(passiveVoice, noun) {
return "HELP ME MY BOSS " + passiveVoice + ' ' + noun;
return 'HELP ME MY BOSS ' + passiveVoice + ' ' + noun;
}
};
var result = template({}, {helpers: helpers});
equals(result, "HELP ME MY BOSS is.a slave.driver", "String parameters output");
equals(result, 'HELP ME MY BOSS is.a slave.driver', 'String parameters output');
});
it("when using block form, arguments to helpers can be retrieved from options hash in string form", function() {
it('when using block form, arguments to helpers can be retrieved from options hash in string form', function() {
var template = CompilerContext.compile('{{#wycats is.a slave.driver}}help :({{/wycats}}', {stringParams: true});
var helpers = {
wycats: function(passiveVoice, noun, options) {
return "HELP ME MY BOSS " + passiveVoice + ' ' +
return 'HELP ME MY BOSS ' + passiveVoice + ' ' +
noun + ': ' + options.fn(this);
}
};
var result = template({}, {helpers: helpers});
equals(result, "HELP ME MY BOSS is.a slave.driver: help :(", "String parameters output");
equals(result, 'HELP ME MY BOSS is.a slave.driver: help :(', 'String parameters output');
});
it("when inside a block in String mode, .. passes the appropriate context in the options hash", function() {
it('when inside a block in String mode, .. passes the appropriate context in the options hash', function() {
var template = CompilerContext.compile('{{#with dale}}{{tomdale ../need dad.joke}}{{/with}}', {stringParams: true});
var helpers = {
tomdale: function(desire, noun, options) {
return "STOP ME FROM READING HACKER NEWS I " +
options.contexts[0][desire] + " " + noun;
return 'STOP ME FROM READING HACKER NEWS I ' +
options.contexts[0][desire] + ' ' + noun;
},
"with": function(context, options) {
'with': function(context, options) {
return options.fn(options.contexts[0][context]);
}
};
@@ -49,31 +48,31 @@ describe('string params mode', function() {
need: 'need-a'
}, {helpers: helpers});
equals(result, "STOP ME FROM READING HACKER NEWS I need-a dad.joke", "Proper context variable output");
equals(result, 'STOP ME FROM READING HACKER NEWS I need-a dad.joke', 'Proper context variable output');
});
it("information about the types is passed along", function() {
var template = CompilerContext.compile('{{tomdale "need" dad.joke true false}}', { stringParams: true });
it('information about the types is passed along', function() {
var template = CompilerContext.compile("{{tomdale 'need' dad.joke true false}}", { stringParams: true });
var helpers = {
tomdale: function(desire, noun, trueBool, falseBool, options) {
equal(options.types[0], 'StringLiteral', "the string type is passed");
equal(options.types[1], 'PathExpression', "the expression type is passed");
equal(options.types[2], 'BooleanLiteral', "the expression type is passed");
equal(desire, "need", "the string form is passed for strings");
equal(noun, "dad.joke", "the string form is passed for expressions");
equal(trueBool, true, "raw booleans are passed through");
equal(falseBool, false, "raw booleans are passed through");
return "Helper called";
equal(options.types[0], 'StringLiteral', 'the string type is passed');
equal(options.types[1], 'PathExpression', 'the expression type is passed');
equal(options.types[2], 'BooleanLiteral', 'the expression type is passed');
equal(desire, 'need', 'the string form is passed for strings');
equal(noun, 'dad.joke', 'the string form is passed for expressions');
equal(trueBool, true, 'raw booleans are passed through');
equal(falseBool, false, 'raw booleans are passed through');
return 'Helper called';
}
};
var result = template({}, { helpers: helpers });
equal(result, "Helper called");
equal(result, 'Helper called');
});
it("hash parameters get type information", function() {
var template = CompilerContext.compile('{{tomdale he.says desire="need" noun=dad.joke bool=true}}', { stringParams: true });
it('hash parameters get type information', function() {
var template = CompilerContext.compile("{{tomdale he.says desire='need' noun=dad.joke bool=true}}", { stringParams: true });
var helpers = {
tomdale: function(exclamation, options) {
@@ -94,43 +93,43 @@ describe('string params mode', function() {
equal(result, 'Helper called');
});
it("hash parameters get context information", function() {
var template = CompilerContext.compile('{{#with dale}}{{tomdale he.says desire="need" noun=../dad/joke bool=true}}{{/with}}', { stringParams: true });
it('hash parameters get context information', function() {
var template = CompilerContext.compile("{{#with dale}}{{tomdale he.says desire='need' noun=../dad/joke bool=true}}{{/with}}", { stringParams: true });
var context = {dale: {}};
var helpers = {
tomdale: function(exclamation, options) {
equal(exclamation, "he.says");
equal(exclamation, 'he.says');
equal(options.types[0], 'PathExpression');
equal(options.contexts.length, 1);
equal(options.hashContexts.noun, context);
equal(options.hash.desire, "need");
equal(options.hash.noun, "dad.joke");
equal(options.hash.desire, 'need');
equal(options.hash.noun, 'dad.joke');
equal(options.hash.bool, true);
return "Helper called";
return 'Helper called';
},
"with": function(context, options) {
return options.fn(options.contexts[0][context]);
'with': function(withContext, options) {
return options.fn(options.contexts[0][withContext]);
}
};
var result = template(context, { helpers: helpers });
equal(result, "Helper called");
equal(result, 'Helper called');
});
it("when inside a block in String mode, .. passes the appropriate context in the options hash to a block helper", function() {
it('when inside a block in String mode, .. passes the appropriate context in the options hash to a block helper', function() {
var template = CompilerContext.compile('{{#with dale}}{{#tomdale ../need dad.joke}}wot{{/tomdale}}{{/with}}', {stringParams: true});
var helpers = {
tomdale: function(desire, noun, options) {
return "STOP ME FROM READING HACKER NEWS I " +
options.contexts[0][desire] + " " + noun + " " +
return 'STOP ME FROM READING HACKER NEWS I ' +
options.contexts[0][desire] + ' ' + noun + ' ' +
options.fn(this);
},
"with": function(context, options) {
'with': function(context, options) {
return options.fn(options.contexts[0][context]);
}
};
@@ -141,23 +140,23 @@ describe('string params mode', function() {
need: 'need-a'
}, {helpers: helpers});
equals(result, "STOP ME FROM READING HACKER NEWS I need-a dad.joke wot", "Proper context variable output");
equals(result, 'STOP ME FROM READING HACKER NEWS I need-a dad.joke wot', 'Proper context variable output');
});
it("with nested block ambiguous", function() {
it('with nested block ambiguous', function() {
var template = CompilerContext.compile('{{#with content}}{{#view}}{{firstName}} {{lastName}}{{/view}}{{/with}}', {stringParams: true});
var helpers = {
'with': function(options) {
return "WITH";
var helpers = {
'with': function() {
return 'WITH';
},
view: function() {
return "VIEW";
return 'VIEW';
}
};
var result = template({}, {helpers: helpers});
equals(result, "WITH");
equals(result, 'WITH');
});
it('should handle DATA', function() {
+79 -81
View File
@@ -1,134 +1,133 @@
/*global CompilerContext, Handlebars, shouldCompileTo, shouldThrow */
describe('subexpressions', function() {
it("arg-less helper", function() {
var string = "{{foo (bar)}}!";
var context = {};
var helpers = {
it('arg-less helper', function() {
var string = '{{foo (bar)}}!';
var context = {};
var helpers = {
foo: function(val) {
return val+val;
return val + val;
},
bar: function() {
return "LOL";
return 'LOL';
}
};
shouldCompileTo(string, [context, helpers], "LOLLOL!");
shouldCompileTo(string, [context, helpers], 'LOLLOL!');
});
it("helper w args", function() {
var string = '{{blog (equal a b)}}';
it('helper w args', function() {
var string = '{{blog (equal a b)}}';
var context = { bar: "LOL" };
var helpers = {
var context = { bar: 'LOL' };
var helpers = {
blog: function(val) {
return "val is " + val;
return 'val is ' + val;
},
equal: function(x, y) {
return x === y;
}
};
shouldCompileTo(string, [context, helpers], "val is true");
shouldCompileTo(string, [context, helpers], 'val is true');
});
it("mixed paths and helpers", function() {
var string = '{{blog baz.bat (equal a b) baz.bar}}';
it('mixed paths and helpers', function() {
var string = '{{blog baz.bat (equal a b) baz.bar}}';
var context = { bar: "LOL", baz: {bat: 'foo!', bar: 'bar!'} };
var helpers = {
var context = { bar: 'LOL', baz: {bat: 'foo!', bar: 'bar!'} };
var helpers = {
blog: function(val, that, theOther) {
return "val is " + val + ', ' + that + ' and ' + theOther;
return 'val is ' + val + ', ' + that + ' and ' + theOther;
},
equal: function(x, y) {
return x === y;
}
};
shouldCompileTo(string, [context, helpers], "val is foo!, true and bar!");
shouldCompileTo(string, [context, helpers], 'val is foo!, true and bar!');
});
it("supports much nesting", function() {
var string = '{{blog (equal (equal true true) true)}}';
it('supports much nesting', function() {
var string = '{{blog (equal (equal true true) true)}}';
var context = { bar: "LOL" };
var helpers = {
var context = { bar: 'LOL' };
var helpers = {
blog: function(val) {
return "val is " + val;
return 'val is ' + val;
},
equal: function(x, y) {
return x === y;
}
};
shouldCompileTo(string, [context, helpers], "val is true");
shouldCompileTo(string, [context, helpers], 'val is true');
});
it('GH-800 : Complex subexpressions', function() {
var context = {a: 'a', b:'b', c:{c:'c'}, d:'d', e: {e: 'e'}};
var helpers = {
var context = {a: 'a', b: 'b', c: {c: 'c'}, d: 'd', e: {e: 'e'}};
var helpers = {
dash: function(a, b) {
return a + "-" + b;
return a + '-' + b;
},
concat: function(a, b) {
return a + b;
}
};
shouldCompileTo('{{dash "abc" (concat a b)}}', [context, helpers], 'abc-ab');
shouldCompileTo("{{dash 'abc' (concat a b)}}", [context, helpers], 'abc-ab');
shouldCompileTo('{{dash d (concat a b)}}', [context, helpers], 'd-ab');
shouldCompileTo('{{dash c.c (concat a b)}}', [context, helpers], 'c-ab');
shouldCompileTo('{{dash (concat a b) c.c}}', [context, helpers], 'ab-c');
shouldCompileTo('{{dash (concat a e.e) c.c}}', [context, helpers], 'ae-c');
});
it("provides each nested helper invocation its own options hash", function() {
it('provides each nested helper invocation its own options hash', function() {
var string = '{{equal (equal true true) true}}';
var lastOptions = null;
var helpers = {
var helpers = {
equal: function(x, y, options) {
if (!options || options === lastOptions) {
throw new Error("options hash was reused");
throw new Error('options hash was reused');
}
lastOptions = options;
return x === y;
}
};
shouldCompileTo(string, [{}, helpers], "true");
shouldCompileTo(string, [{}, helpers], 'true');
});
it("with hashes", function() {
var string = '{{blog (equal (equal true true) true fun="yes")}}';
it('with hashes', function() {
var string = "{{blog (equal (equal true true) true fun='yes')}}";
var context = { bar: "LOL" };
var helpers = {
var context = { bar: 'LOL' };
var helpers = {
blog: function(val) {
return "val is " + val;
return 'val is ' + val;
},
equal: function(x, y) {
return x === y;
}
};
shouldCompileTo(string, [context, helpers], "val is true");
shouldCompileTo(string, [context, helpers], 'val is true');
});
it("as hashes", function() {
var string = '{{blog fun=(equal (blog fun=1) "val is 1")}}';
it('as hashes', function() {
var string = "{{blog fun=(equal (blog fun=1) 'val is 1')}}";
var helpers = {
var helpers = {
blog: function(options) {
return "val is " + options.hash.fun;
return 'val is ' + options.hash.fun;
},
equal: function(x, y) {
return x === y;
}
};
shouldCompileTo(string, [{}, helpers], "val is true");
shouldCompileTo(string, [{}, helpers], 'val is true');
});
it("multiple subexpressions in a hash", function() {
it('multiple subexpressions in a hash', function() {
var string = '{{input aria-label=(t "Name") placeholder=(t "Example User")}}';
var helpers = {
input: function(options) {
var hash = options.hash;
var ariaLabel = Handlebars.Utils.escapeExpression(hash['aria-label']);
var hash = options.hash;
var ariaLabel = Handlebars.Utils.escapeExpression(hash['aria-label']);
var placeholder = Handlebars.Utils.escapeExpression(hash.placeholder);
return new Handlebars.SafeString('<input aria-label="' + ariaLabel + '" placeholder="' + placeholder + '" />');
},
@@ -139,20 +138,20 @@ describe('subexpressions', function() {
shouldCompileTo(string, [{}, helpers], '<input aria-label="Name" placeholder="Example User" />');
});
it("multiple subexpressions in a hash with context", function() {
it('multiple subexpressions in a hash with context', function() {
var string = '{{input aria-label=(t item.field) placeholder=(t item.placeholder)}}';
var context = {
item: {
field: "Name",
placeholder: "Example User"
}
field: 'Name',
placeholder: 'Example User'
}
};
var helpers = {
input: function(options) {
var hash = options.hash;
var ariaLabel = Handlebars.Utils.escapeExpression(hash['aria-label']);
var hash = options.hash;
var ariaLabel = Handlebars.Utils.escapeExpression(hash['aria-label']);
var placeholder = Handlebars.Utils.escapeExpression(hash.placeholder);
return new Handlebars.SafeString('<input aria-label="' + ariaLabel + '" placeholder="' + placeholder + '" />');
},
@@ -161,23 +160,23 @@ describe('subexpressions', function() {
}
};
shouldCompileTo(string, [context, helpers], '<input aria-label="Name" placeholder="Example User" />');
});
});
it("in string params mode,", function() {
it('in string params mode,', function() {
var template = CompilerContext.compile('{{snog (blorg foo x=y) yeah a=b}}', {stringParams: true});
var helpers = {
snog: function(a, b, options) {
equals(a, 'foo');
equals(options.types.length, 2, "string params for outer helper processed correctly");
equals(options.types[0], 'SubExpression', "string params for outer helper processed correctly");
equals(options.types[1], 'PathExpression', "string params for outer helper processed correctly");
equals(options.types.length, 2, 'string params for outer helper processed correctly');
equals(options.types[0], 'SubExpression', 'string params for outer helper processed correctly');
equals(options.types[1], 'PathExpression', 'string params for outer helper processed correctly');
return a + b;
},
blorg: function(a, options) {
equals(options.types.length, 1, "string params for inner helper processed correctly");
equals(options.types[0], 'PathExpression', "string params for inner helper processed correctly");
equals(options.types.length, 1, 'string params for inner helper processed correctly');
equals(options.types[0], 'PathExpression', 'string params for inner helper processed correctly');
return a;
}
};
@@ -187,54 +186,53 @@ describe('subexpressions', function() {
yeah: {}
}, {helpers: helpers});
equals(result, "fooyeah");
equals(result, 'fooyeah');
});
it("as hashes in string params mode", function() {
it('as hashes in string params mode', function() {
var template = CompilerContext.compile('{{blog fun=(bork)}}', {stringParams: true});
var helpers = {
var helpers = {
blog: function(options) {
equals(options.hashTypes.fun, 'SubExpression');
return "val is " + options.hash.fun;
return 'val is ' + options.hash.fun;
},
bork: function() {
return "BORK";
return 'BORK';
}
};
var result = template({}, {helpers: helpers});
equals(result, "val is BORK");
equals(result, 'val is BORK');
});
it("subexpression functions on the context", function() {
var string = "{{foo (bar)}}!";
var context = {
it('subexpression functions on the context', function() {
var string = '{{foo (bar)}}!';
var context = {
bar: function() {
return "LOL";
return 'LOL';
}
};
var helpers = {
var helpers = {
foo: function(val) {
return val+val;
return val + val;
}
};
shouldCompileTo(string, [context, helpers], "LOLLOL!");
shouldCompileTo(string, [context, helpers], 'LOLLOL!');
});
it("subexpressions can't just be property lookups", function() {
var string = "{{foo (bar)}}!";
var context = {
bar: "LOL"
var string = '{{foo (bar)}}!';
var context = {
bar: 'LOL'
};
var helpers = {
var helpers = {
foo: function(val) {
return val+val;
return val + val;
}
};
shouldThrow(function() {
shouldCompileTo(string, [context, helpers], "LOLLOL!");
shouldCompileTo(string, [context, helpers], 'LOLLOL!');
});
});
});
+130 -131
View File
@@ -1,4 +1,3 @@
/*global Handlebars */
function shouldMatchTokens(result, tokens) {
for (var index = 0; index < result.length; index++) {
equals(result[index].name, tokens[index]);
@@ -22,7 +21,7 @@ describe('Tokenizer', function() {
var out = [],
token;
while (token = lexer.lex()) {
while ((token = lexer.lex())) {
var result = parser.terminals_[token] || token;
if (!result || result === 'EOF' || result === 'INVALID') {
break;
@@ -34,292 +33,292 @@ describe('Tokenizer', function() {
}
it('tokenizes a simple mustache as "OPEN ID CLOSE"', function() {
var result = tokenize("{{foo}}");
var result = tokenize('{{foo}}');
shouldMatchTokens(result, ['OPEN', 'ID', 'CLOSE']);
shouldBeToken(result[1], "ID", "foo");
shouldBeToken(result[1], 'ID', 'foo');
});
it('supports unescaping with &', function() {
var result = tokenize("{{&bar}}");
var result = tokenize('{{&bar}}');
shouldMatchTokens(result, ['OPEN', 'ID', 'CLOSE']);
shouldBeToken(result[0], "OPEN", "{{&");
shouldBeToken(result[1], "ID", "bar");
shouldBeToken(result[0], 'OPEN', '{{&');
shouldBeToken(result[1], 'ID', 'bar');
});
it('supports unescaping with {{{', function() {
var result = tokenize("{{{bar}}}");
var result = tokenize('{{{bar}}}');
shouldMatchTokens(result, ['OPEN_UNESCAPED', 'ID', 'CLOSE_UNESCAPED']);
shouldBeToken(result[1], "ID", "bar");
shouldBeToken(result[1], 'ID', 'bar');
});
it('supports escaping delimiters', function() {
var result = tokenize("{{foo}} \\{{bar}} {{baz}}");
var result = tokenize('{{foo}} \\{{bar}} {{baz}}');
shouldMatchTokens(result, ['OPEN', 'ID', 'CLOSE', 'CONTENT', 'CONTENT', 'OPEN', 'ID', 'CLOSE']);
shouldBeToken(result[3], "CONTENT", " ");
shouldBeToken(result[4], "CONTENT", "{{bar}} ");
shouldBeToken(result[3], 'CONTENT', ' ');
shouldBeToken(result[4], 'CONTENT', '{{bar}} ');
});
it('supports escaping multiple delimiters', function() {
var result = tokenize("{{foo}} \\{{bar}} \\{{baz}}");
var result = tokenize('{{foo}} \\{{bar}} \\{{baz}}');
shouldMatchTokens(result, ['OPEN', 'ID', 'CLOSE', 'CONTENT', 'CONTENT', 'CONTENT']);
shouldBeToken(result[3], "CONTENT", " ");
shouldBeToken(result[4], "CONTENT", "{{bar}} ");
shouldBeToken(result[5], "CONTENT", "{{baz}}");
shouldBeToken(result[3], 'CONTENT', ' ');
shouldBeToken(result[4], 'CONTENT', '{{bar}} ');
shouldBeToken(result[5], 'CONTENT', '{{baz}}');
});
it('supports escaping a triple stash', function() {
var result = tokenize("{{foo}} \\{{{bar}}} {{baz}}");
var result = tokenize('{{foo}} \\{{{bar}}} {{baz}}');
shouldMatchTokens(result, ['OPEN', 'ID', 'CLOSE', 'CONTENT', 'CONTENT', 'OPEN', 'ID', 'CLOSE']);
shouldBeToken(result[4], "CONTENT", "{{{bar}}} ");
shouldBeToken(result[4], 'CONTENT', '{{{bar}}} ');
});
it('supports escaping escape character', function() {
var result = tokenize("{{foo}} \\\\{{bar}} {{baz}}");
var result = tokenize('{{foo}} \\\\{{bar}} {{baz}}');
shouldMatchTokens(result, ['OPEN', 'ID', 'CLOSE', 'CONTENT', 'OPEN', 'ID', 'CLOSE', 'CONTENT', 'OPEN', 'ID', 'CLOSE']);
shouldBeToken(result[3], "CONTENT", " \\");
shouldBeToken(result[5], "ID", "bar");
shouldBeToken(result[3], 'CONTENT', ' \\');
shouldBeToken(result[5], 'ID', 'bar');
});
it('supports escaping multiple escape characters', function() {
var result = tokenize("{{foo}} \\\\{{bar}} \\\\{{baz}}");
var result = tokenize('{{foo}} \\\\{{bar}} \\\\{{baz}}');
shouldMatchTokens(result, ['OPEN', 'ID', 'CLOSE', 'CONTENT', 'OPEN', 'ID', 'CLOSE', 'CONTENT', 'OPEN', 'ID', 'CLOSE']);
shouldBeToken(result[3], "CONTENT", " \\");
shouldBeToken(result[5], "ID", "bar");
shouldBeToken(result[7], "CONTENT", " \\");
shouldBeToken(result[9], "ID", "baz");
shouldBeToken(result[3], 'CONTENT', ' \\');
shouldBeToken(result[5], 'ID', 'bar');
shouldBeToken(result[7], 'CONTENT', ' \\');
shouldBeToken(result[9], 'ID', 'baz');
});
it('supports escaped mustaches after escaped escape characters', function() {
var result = tokenize("{{foo}} \\\\{{bar}} \\{{baz}}");
var result = tokenize('{{foo}} \\\\{{bar}} \\{{baz}}');
shouldMatchTokens(result, ['OPEN', 'ID', 'CLOSE', 'CONTENT', 'OPEN', 'ID', 'CLOSE', 'CONTENT', 'CONTENT', 'CONTENT']);
shouldBeToken(result[3], "CONTENT", " \\");
shouldBeToken(result[4], "OPEN", "{{");
shouldBeToken(result[5], "ID", "bar");
shouldBeToken(result[7], "CONTENT", " ");
shouldBeToken(result[8], "CONTENT", "{{baz}}");
shouldBeToken(result[3], 'CONTENT', ' \\');
shouldBeToken(result[4], 'OPEN', '{{');
shouldBeToken(result[5], 'ID', 'bar');
shouldBeToken(result[7], 'CONTENT', ' ');
shouldBeToken(result[8], 'CONTENT', '{{baz}}');
});
it('supports escaped escape characters after escaped mustaches', function() {
var result = tokenize("{{foo}} \\{{bar}} \\\\{{baz}}");
var result = tokenize('{{foo}} \\{{bar}} \\\\{{baz}}');
shouldMatchTokens(result, ['OPEN', 'ID', 'CLOSE', 'CONTENT', 'CONTENT', 'CONTENT', 'OPEN', 'ID', 'CLOSE']);
shouldBeToken(result[4], "CONTENT", "{{bar}} ");
shouldBeToken(result[5], "CONTENT", "\\");
shouldBeToken(result[6], "OPEN", "{{");
shouldBeToken(result[7], "ID", "baz");
shouldBeToken(result[4], 'CONTENT', '{{bar}} ');
shouldBeToken(result[5], 'CONTENT', '\\');
shouldBeToken(result[6], 'OPEN', '{{');
shouldBeToken(result[7], 'ID', 'baz');
});
it('supports escaped escape character on a triple stash', function() {
var result = tokenize("{{foo}} \\\\{{{bar}}} {{baz}}");
var result = tokenize('{{foo}} \\\\{{{bar}}} {{baz}}');
shouldMatchTokens(result, ['OPEN', 'ID', 'CLOSE', 'CONTENT', 'OPEN_UNESCAPED', 'ID', 'CLOSE_UNESCAPED', 'CONTENT', 'OPEN', 'ID', 'CLOSE']);
shouldBeToken(result[3], "CONTENT", " \\");
shouldBeToken(result[5], "ID", "bar");
shouldBeToken(result[3], 'CONTENT', ' \\');
shouldBeToken(result[5], 'ID', 'bar');
});
it('tokenizes a simple path', function() {
var result = tokenize("{{foo/bar}}");
var result = tokenize('{{foo/bar}}');
shouldMatchTokens(result, ['OPEN', 'ID', 'SEP', 'ID', 'CLOSE']);
});
it('allows dot notation', function() {
var result = tokenize("{{foo.bar}}");
var result = tokenize('{{foo.bar}}');
shouldMatchTokens(result, ['OPEN', 'ID', 'SEP', 'ID', 'CLOSE']);
shouldMatchTokens(tokenize("{{foo.bar.baz}}"), ['OPEN', 'ID', 'SEP', 'ID', 'SEP', 'ID', 'CLOSE']);
shouldMatchTokens(tokenize('{{foo.bar.baz}}'), ['OPEN', 'ID', 'SEP', 'ID', 'SEP', 'ID', 'CLOSE']);
});
it('allows path literals with []', function() {
var result = tokenize("{{foo.[bar]}}");
var result = tokenize('{{foo.[bar]}}');
shouldMatchTokens(result, ['OPEN', 'ID', 'SEP', 'ID', 'CLOSE']);
});
it('allows multiple path literals on a line with []', function() {
var result = tokenize("{{foo.[bar]}}{{foo.[baz]}}");
var result = tokenize('{{foo.[bar]}}{{foo.[baz]}}');
shouldMatchTokens(result, ['OPEN', 'ID', 'SEP', 'ID', 'CLOSE', 'OPEN', 'ID', 'SEP', 'ID', 'CLOSE']);
});
it('tokenizes {{.}} as OPEN ID CLOSE', function() {
var result = tokenize("{{.}}");
var result = tokenize('{{.}}');
shouldMatchTokens(result, ['OPEN', 'ID', 'CLOSE']);
});
it('tokenizes a path as "OPEN (ID SEP)* ID CLOSE"', function() {
var result = tokenize("{{../foo/bar}}");
var result = tokenize('{{../foo/bar}}');
shouldMatchTokens(result, ['OPEN', 'ID', 'SEP', 'ID', 'SEP', 'ID', 'CLOSE']);
shouldBeToken(result[1], "ID", "..");
shouldBeToken(result[1], 'ID', '..');
});
it('tokenizes a path with .. as a parent path', function() {
var result = tokenize("{{../foo.bar}}");
var result = tokenize('{{../foo.bar}}');
shouldMatchTokens(result, ['OPEN', 'ID', 'SEP', 'ID', 'SEP', 'ID', 'CLOSE']);
shouldBeToken(result[1], "ID", "..");
shouldBeToken(result[1], 'ID', '..');
});
it('tokenizes a path with this/foo as OPEN ID SEP ID CLOSE', function() {
var result = tokenize("{{this/foo}}");
var result = tokenize('{{this/foo}}');
shouldMatchTokens(result, ['OPEN', 'ID', 'SEP', 'ID', 'CLOSE']);
shouldBeToken(result[1], "ID", "this");
shouldBeToken(result[3], "ID", "foo");
shouldBeToken(result[1], 'ID', 'this');
shouldBeToken(result[3], 'ID', 'foo');
});
it('tokenizes a simple mustache with spaces as "OPEN ID CLOSE"', function() {
var result = tokenize("{{ foo }}");
var result = tokenize('{{ foo }}');
shouldMatchTokens(result, ['OPEN', 'ID', 'CLOSE']);
shouldBeToken(result[1], "ID", "foo");
shouldBeToken(result[1], 'ID', 'foo');
});
it('tokenizes a simple mustache with line breaks as "OPEN ID ID CLOSE"', function() {
var result = tokenize("{{ foo \n bar }}");
var result = tokenize('{{ foo \n bar }}');
shouldMatchTokens(result, ['OPEN', 'ID', 'ID', 'CLOSE']);
shouldBeToken(result[1], "ID", "foo");
shouldBeToken(result[1], 'ID', 'foo');
});
it('tokenizes raw content as "CONTENT"', function() {
var result = tokenize("foo {{ bar }} baz");
var result = tokenize('foo {{ bar }} baz');
shouldMatchTokens(result, ['CONTENT', 'OPEN', 'ID', 'CLOSE', 'CONTENT']);
shouldBeToken(result[0], "CONTENT", "foo ");
shouldBeToken(result[4], "CONTENT", " baz");
shouldBeToken(result[0], 'CONTENT', 'foo ');
shouldBeToken(result[4], 'CONTENT', ' baz');
});
it('tokenizes a partial as "OPEN_PARTIAL ID CLOSE"', function() {
var result = tokenize("{{> foo}}");
var result = tokenize('{{> foo}}');
shouldMatchTokens(result, ['OPEN_PARTIAL', 'ID', 'CLOSE']);
});
it('tokenizes a partial with context as "OPEN_PARTIAL ID ID CLOSE"', function() {
var result = tokenize("{{> foo bar }}");
var result = tokenize('{{> foo bar }}');
shouldMatchTokens(result, ['OPEN_PARTIAL', 'ID', 'ID', 'CLOSE']);
});
it('tokenizes a partial without spaces as "OPEN_PARTIAL ID CLOSE"', function() {
var result = tokenize("{{>foo}}");
var result = tokenize('{{>foo}}');
shouldMatchTokens(result, ['OPEN_PARTIAL', 'ID', 'CLOSE']);
});
it('tokenizes a partial space at the }); as "OPEN_PARTIAL ID CLOSE"', function() {
var result = tokenize("{{>foo }}");
var result = tokenize('{{>foo }}');
shouldMatchTokens(result, ['OPEN_PARTIAL', 'ID', 'CLOSE']);
});
it('tokenizes a partial space at the }); as "OPEN_PARTIAL ID CLOSE"', function() {
var result = tokenize("{{>foo/bar.baz }}");
var result = tokenize('{{>foo/bar.baz }}');
shouldMatchTokens(result, ['OPEN_PARTIAL', 'ID', 'SEP', 'ID', 'SEP', 'ID', 'CLOSE']);
});
it('tokenizes a comment as "COMMENT"', function() {
var result = tokenize("foo {{! this is a comment }} bar {{ baz }}");
var result = tokenize('foo {{! this is a comment }} bar {{ baz }}');
shouldMatchTokens(result, ['CONTENT', 'COMMENT', 'CONTENT', 'OPEN', 'ID', 'CLOSE']);
shouldBeToken(result[1], "COMMENT", "{{! this is a comment }}");
shouldBeToken(result[1], 'COMMENT', '{{! this is a comment }}');
});
it('tokenizes a block comment as "COMMENT"', function() {
var result = tokenize("foo {{!-- this is a {{comment}} --}} bar {{ baz }}");
var result = tokenize('foo {{!-- this is a {{comment}} --}} bar {{ baz }}');
shouldMatchTokens(result, ['CONTENT', 'COMMENT', 'CONTENT', 'OPEN', 'ID', 'CLOSE']);
shouldBeToken(result[1], "COMMENT", "{{!-- this is a {{comment}} --}}");
shouldBeToken(result[1], 'COMMENT', '{{!-- this is a {{comment}} --}}');
});
it('tokenizes a block comment with whitespace as "COMMENT"', function() {
var result = tokenize("foo {{!-- this is a\n{{comment}}\n--}} bar {{ baz }}");
var result = tokenize('foo {{!-- this is a\n{{comment}}\n--}} bar {{ baz }}');
shouldMatchTokens(result, ['CONTENT', 'COMMENT', 'CONTENT', 'OPEN', 'ID', 'CLOSE']);
shouldBeToken(result[1], "COMMENT", "{{!-- this is a\n{{comment}}\n--}}");
shouldBeToken(result[1], 'COMMENT', '{{!-- this is a\n{{comment}}\n--}}');
});
it('tokenizes open and closing blocks as OPEN_BLOCK, ID, CLOSE ..., OPEN_ENDBLOCK ID CLOSE', function() {
var result = tokenize("{{#foo}}content{{/foo}}");
var result = tokenize('{{#foo}}content{{/foo}}');
shouldMatchTokens(result, ['OPEN_BLOCK', 'ID', 'CLOSE', 'CONTENT', 'OPEN_ENDBLOCK', 'ID', 'CLOSE']);
});
it('tokenizes inverse sections as "INVERSE"', function() {
shouldMatchTokens(tokenize("{{^}}"), ['INVERSE']);
shouldMatchTokens(tokenize("{{else}}"), ['INVERSE']);
shouldMatchTokens(tokenize("{{ else }}"), ['INVERSE']);
shouldMatchTokens(tokenize('{{^}}'), ['INVERSE']);
shouldMatchTokens(tokenize('{{else}}'), ['INVERSE']);
shouldMatchTokens(tokenize('{{ else }}'), ['INVERSE']);
});
it('tokenizes inverse sections with ID as "OPEN_INVERSE ID CLOSE"', function() {
var result = tokenize("{{^foo}}");
var result = tokenize('{{^foo}}');
shouldMatchTokens(result, ['OPEN_INVERSE', 'ID', 'CLOSE']);
shouldBeToken(result[1], "ID", "foo");
shouldBeToken(result[1], 'ID', 'foo');
});
it('tokenizes inverse sections with ID and spaces as "OPEN_INVERSE ID CLOSE"', function() {
var result = tokenize("{{^ foo }}");
var result = tokenize('{{^ foo }}');
shouldMatchTokens(result, ['OPEN_INVERSE', 'ID', 'CLOSE']);
shouldBeToken(result[1], "ID", "foo");
shouldBeToken(result[1], 'ID', 'foo');
});
it('tokenizes mustaches with params as "OPEN ID ID ID CLOSE"', function() {
var result = tokenize("{{ foo bar baz }}");
var result = tokenize('{{ foo bar baz }}');
shouldMatchTokens(result, ['OPEN', 'ID', 'ID', 'ID', 'CLOSE']);
shouldBeToken(result[1], "ID", "foo");
shouldBeToken(result[2], "ID", "bar");
shouldBeToken(result[3], "ID", "baz");
shouldBeToken(result[1], 'ID', 'foo');
shouldBeToken(result[2], 'ID', 'bar');
shouldBeToken(result[3], 'ID', 'baz');
});
it('tokenizes mustaches with String params as "OPEN ID ID STRING CLOSE"', function() {
var result = tokenize("{{ foo bar \"baz\" }}");
var result = tokenize('{{ foo bar \'baz\' }}');
shouldMatchTokens(result, ['OPEN', 'ID', 'ID', 'STRING', 'CLOSE']);
shouldBeToken(result[3], "STRING", "baz");
shouldBeToken(result[3], 'STRING', 'baz');
});
it('tokenizes mustaches with String params using single quotes as "OPEN ID ID STRING CLOSE"', function() {
var result = tokenize("{{ foo bar \'baz\' }}");
shouldMatchTokens(result, ['OPEN', 'ID', 'ID', 'STRING', 'CLOSE']);
shouldBeToken(result[3], "STRING", "baz");
shouldBeToken(result[3], 'STRING', 'baz');
});
it('tokenizes String params with spaces inside as "STRING"', function() {
var result = tokenize("{{ foo bar \"baz bat\" }}");
var result = tokenize('{{ foo bar "baz bat" }}');
shouldMatchTokens(result, ['OPEN', 'ID', 'ID', 'STRING', 'CLOSE']);
shouldBeToken(result[3], "STRING", "baz bat");
shouldBeToken(result[3], 'STRING', 'baz bat');
});
it('tokenizes String params with escapes quotes as STRING', function() {
var result = tokenize('{{ foo "bar\\"baz" }}');
shouldMatchTokens(result, ['OPEN', 'ID', 'STRING', 'CLOSE']);
shouldBeToken(result[2], "STRING", 'bar"baz');
shouldBeToken(result[2], 'STRING', 'bar"baz');
});
it('tokenizes String params using single quotes with escapes quotes as STRING', function() {
var result = tokenize("{{ foo 'bar\\'baz' }}");
shouldMatchTokens(result, ['OPEN', 'ID', 'STRING', 'CLOSE']);
shouldBeToken(result[2], "STRING", "bar'baz");
shouldBeToken(result[2], 'STRING', "bar'baz");
});
it('tokenizes numbers', function() {
var result = tokenize('{{ foo 1 }}');
shouldMatchTokens(result, ['OPEN', 'ID', 'NUMBER', 'CLOSE']);
shouldBeToken(result[2], "NUMBER", "1");
shouldBeToken(result[2], 'NUMBER', '1');
result = tokenize('{{ foo 1.1 }}');
shouldMatchTokens(result, ['OPEN', 'ID', 'NUMBER', 'CLOSE']);
shouldBeToken(result[2], "NUMBER", "1.1");
shouldBeToken(result[2], 'NUMBER', '1.1');
result = tokenize('{{ foo -1 }}');
shouldMatchTokens(result, ['OPEN', 'ID', 'NUMBER', 'CLOSE']);
shouldBeToken(result[2], "NUMBER", "-1");
shouldBeToken(result[2], 'NUMBER', '-1');
result = tokenize('{{ foo -1.1 }}');
shouldMatchTokens(result, ['OPEN', 'ID', 'NUMBER', 'CLOSE']);
shouldBeToken(result[2], "NUMBER", "-1.1");
shouldBeToken(result[2], 'NUMBER', '-1.1');
});
it('tokenizes booleans', function() {
var result = tokenize('{{ foo true }}');
shouldMatchTokens(result, ['OPEN', 'ID', 'BOOLEAN', 'CLOSE']);
shouldBeToken(result[2], "BOOLEAN", "true");
shouldBeToken(result[2], 'BOOLEAN', 'true');
result = tokenize('{{ foo false }}');
shouldMatchTokens(result, ['OPEN', 'ID', 'BOOLEAN', 'CLOSE']);
shouldBeToken(result[2], "BOOLEAN", "false");
shouldBeToken(result[2], 'BOOLEAN', 'false');
});
it('tokenizes undefined and null', function() {
@@ -330,77 +329,77 @@ describe('Tokenizer', function() {
});
it('tokenizes hash arguments', function() {
var result = tokenize("{{ foo bar=baz }}");
var result = tokenize('{{ foo bar=baz }}');
shouldMatchTokens(result, ['OPEN', 'ID', 'ID', 'EQUALS', 'ID', 'CLOSE']);
result = tokenize("{{ foo bar baz=bat }}");
result = tokenize('{{ foo bar baz=bat }}');
shouldMatchTokens(result, ['OPEN', 'ID', 'ID', 'ID', 'EQUALS', 'ID', 'CLOSE']);
result = tokenize("{{ foo bar baz=1 }}");
result = tokenize('{{ foo bar baz=1 }}');
shouldMatchTokens(result, ['OPEN', 'ID', 'ID', 'ID', 'EQUALS', 'NUMBER', 'CLOSE']);
result = tokenize("{{ foo bar baz=true }}");
result = tokenize('{{ foo bar baz=true }}');
shouldMatchTokens(result, ['OPEN', 'ID', 'ID', 'ID', 'EQUALS', 'BOOLEAN', 'CLOSE']);
result = tokenize("{{ foo bar baz=false }}");
result = tokenize('{{ foo bar baz=false }}');
shouldMatchTokens(result, ['OPEN', 'ID', 'ID', 'ID', 'EQUALS', 'BOOLEAN', 'CLOSE']);
result = tokenize("{{ foo bar\n baz=bat }}");
result = tokenize('{{ foo bar\n baz=bat }}');
shouldMatchTokens(result, ['OPEN', 'ID', 'ID', 'ID', 'EQUALS', 'ID', 'CLOSE']);
result = tokenize("{{ foo bar baz=\"bat\" }}");
result = tokenize('{{ foo bar baz=\"bat\" }}');
shouldMatchTokens(result, ['OPEN', 'ID', 'ID', 'ID', 'EQUALS', 'STRING', 'CLOSE']);
result = tokenize("{{ foo bar baz=\"bat\" bam=wot }}");
result = tokenize('{{ foo bar baz=\"bat\" bam=wot }}');
shouldMatchTokens(result, ['OPEN', 'ID', 'ID', 'ID', 'EQUALS', 'STRING', 'ID', 'EQUALS', 'ID', 'CLOSE']);
result = tokenize("{{foo omg bar=baz bat=\"bam\"}}");
result = tokenize('{{foo omg bar=baz bat=\"bam\"}}');
shouldMatchTokens(result, ['OPEN', 'ID', 'ID', 'ID', 'EQUALS', 'ID', 'ID', 'EQUALS', 'STRING', 'CLOSE']);
shouldBeToken(result[2], "ID", "omg");
shouldBeToken(result[2], 'ID', 'omg');
});
it('tokenizes special @ identifiers', function() {
var result = tokenize("{{ @foo }}");
var result = tokenize('{{ @foo }}');
shouldMatchTokens(result, ['OPEN', 'DATA', 'ID', 'CLOSE']);
shouldBeToken(result[2], "ID", "foo");
shouldBeToken(result[2], 'ID', 'foo');
result = tokenize("{{ foo @bar }}");
result = tokenize('{{ foo @bar }}');
shouldMatchTokens(result, ['OPEN', 'ID', 'DATA', 'ID', 'CLOSE']);
shouldBeToken(result[3], "ID", "bar");
shouldBeToken(result[3], 'ID', 'bar');
result = tokenize("{{ foo bar=@baz }}");
result = tokenize('{{ foo bar=@baz }}');
shouldMatchTokens(result, ['OPEN', 'ID', 'ID', 'EQUALS', 'DATA', 'ID', 'CLOSE']);
shouldBeToken(result[5], "ID", "baz");
shouldBeToken(result[5], 'ID', 'baz');
});
it('does not time out in a mustache with a single } followed by EOF', function() {
shouldMatchTokens(tokenize("{{foo}"), ['OPEN', 'ID']);
shouldMatchTokens(tokenize('{{foo}'), ['OPEN', 'ID']);
});
it('does not time out in a mustache when invalid ID characters are used', function() {
shouldMatchTokens(tokenize("{{foo & }}"), ['OPEN', 'ID']);
shouldMatchTokens(tokenize('{{foo & }}'), ['OPEN', 'ID']);
});
it('tokenizes subexpressions', function() {
var result = tokenize("{{foo (bar)}}");
var result = tokenize('{{foo (bar)}}');
shouldMatchTokens(result, ['OPEN', 'ID', 'OPEN_SEXPR', 'ID', 'CLOSE_SEXPR', 'CLOSE']);
shouldBeToken(result[1], "ID", "foo");
shouldBeToken(result[3], "ID", "bar");
shouldBeToken(result[1], 'ID', 'foo');
shouldBeToken(result[3], 'ID', 'bar');
result = tokenize("{{foo (a-x b-y)}}");
result = tokenize('{{foo (a-x b-y)}}');
shouldMatchTokens(result, ['OPEN', 'ID', 'OPEN_SEXPR', 'ID', 'ID', 'CLOSE_SEXPR', 'CLOSE']);
shouldBeToken(result[1], "ID", "foo");
shouldBeToken(result[3], "ID", "a-x");
shouldBeToken(result[4], "ID", "b-y");
shouldBeToken(result[1], 'ID', 'foo');
shouldBeToken(result[3], 'ID', 'a-x');
shouldBeToken(result[4], 'ID', 'b-y');
});
it('tokenizes nested subexpressions', function() {
var result = tokenize("{{foo (bar (lol rofl)) (baz)}}");
var result = tokenize('{{foo (bar (lol rofl)) (baz)}}');
shouldMatchTokens(result, ['OPEN', 'ID', 'OPEN_SEXPR', 'ID', 'OPEN_SEXPR', 'ID', 'ID', 'CLOSE_SEXPR', 'CLOSE_SEXPR', 'OPEN_SEXPR', 'ID', 'CLOSE_SEXPR', 'CLOSE']);
shouldBeToken(result[3], "ID", "bar");
shouldBeToken(result[5], "ID", "lol");
shouldBeToken(result[6], "ID", "rofl");
shouldBeToken(result[10], "ID", "baz");
shouldBeToken(result[3], 'ID', 'bar');
shouldBeToken(result[5], 'ID', 'lol');
shouldBeToken(result[6], 'ID', 'rofl');
shouldBeToken(result[10], 'ID', 'baz');
});
it('tokenizes nested subexpressions: literals', function() {
@@ -409,19 +408,19 @@ describe('Tokenizer', function() {
});
it('tokenizes block params', function() {
var result = tokenize("{{#foo as |bar|}}");
var result = tokenize('{{#foo as |bar|}}');
shouldMatchTokens(result, ['OPEN_BLOCK', 'ID', 'OPEN_BLOCK_PARAMS', 'ID', 'CLOSE_BLOCK_PARAMS', 'CLOSE']);
result = tokenize("{{#foo as |bar baz|}}");
result = tokenize('{{#foo as |bar baz|}}');
shouldMatchTokens(result, ['OPEN_BLOCK', 'ID', 'OPEN_BLOCK_PARAMS', 'ID', 'ID', 'CLOSE_BLOCK_PARAMS', 'CLOSE']);
result = tokenize("{{#foo as | bar baz |}}");
result = tokenize('{{#foo as | bar baz |}}');
shouldMatchTokens(result, ['OPEN_BLOCK', 'ID', 'OPEN_BLOCK_PARAMS', 'ID', 'ID', 'CLOSE_BLOCK_PARAMS', 'CLOSE']);
result = tokenize("{{#foo as as | bar baz |}}");
result = tokenize('{{#foo as as | bar baz |}}');
shouldMatchTokens(result, ['OPEN_BLOCK', 'ID', 'ID', 'OPEN_BLOCK_PARAMS', 'ID', 'ID', 'CLOSE_BLOCK_PARAMS', 'CLOSE']);
result = tokenize("{{else foo as |bar baz|}}");
result = tokenize('{{else foo as |bar baz|}}');
shouldMatchTokens(result, ['OPEN_INVERSE_CHAIN', 'ID', 'OPEN_BLOCK_PARAMS', 'ID', 'ID', 'CLOSE_BLOCK_PARAMS', 'CLOSE']);
});
});
+12 -16
View File
@@ -1,4 +1,3 @@
/*global CompilerContext */
describe('track ids', function() {
var context;
beforeEach(function() {
@@ -27,7 +26,7 @@ describe('track ids', function() {
equal(options.ids[0], 'is.a');
equal(options.ids[1], 'slave.driver');
return "HELP ME MY BOSS " + options.ids[0] + ':' + passiveVoice + ' ' + options.ids[1] + ':' + noun;
return 'HELP ME MY BOSS ' + options.ids[0] + ':' + passiveVoice + ' ' + options.ids[1] + ':' + noun;
}
};
@@ -41,7 +40,7 @@ describe('track ids', function() {
equal(options.hashIds.bat, 'is.a');
equal(options.hashIds.baz, 'slave.driver');
return "HELP ME MY BOSS " + options.hashIds.bat + ':' + options.hash.bat + ' ' + options.hashIds.baz + ':' + options.hash.baz;
return 'HELP ME MY BOSS ' + options.hashIds.bat + ':' + options.hash.bat + ' ' + options.hashIds.baz + ':' + options.hash.baz;
}
};
@@ -55,7 +54,7 @@ describe('track ids', function() {
equal(options.ids[0], 'is.a');
equal(options.ids[1], '../slave.driver');
return "HELP ME MY BOSS " + options.ids[0] + ':' + passiveVoice + ' ' + options.ids[1] + ':' + noun;
return 'HELP ME MY BOSS ' + options.ids[0] + ':' + passiveVoice + ' ' + options.ids[1] + ':' + noun;
}
};
@@ -69,11 +68,11 @@ describe('track ids', function() {
equal(options.ids[0], '@is.a');
equal(options.ids[1], '@slave.driver');
return "HELP ME MY BOSS " + options.ids[0] + ':' + passiveVoice + ' ' + options.ids[1] + ':' + noun;
return 'HELP ME MY BOSS ' + options.ids[0] + ':' + passiveVoice + ' ' + options.ids[1] + ':' + noun;
}
};
equals(template({}, {helpers: helpers, data:context}), 'HELP ME MY BOSS @is.a:foo @slave.driver:bar');
equals(template({}, {helpers: helpers, data: context}), 'HELP ME MY BOSS @is.a:foo @slave.driver:bar');
});
it('should return null for constants', function() {
@@ -85,7 +84,7 @@ describe('track ids', function() {
equal(options.ids[1], null);
equal(options.hashIds.key, null);
return "HELP ME MY BOSS " + passiveVoice + ' ' + noun + ' ' + options.hash.key;
return 'HELP ME MY BOSS ' + passiveVoice + ' ' + noun + ' ' + options.hash.key;
}
};
@@ -99,7 +98,7 @@ describe('track ids', function() {
wycats: function(passiveVoice, options) {
equal(options.ids[0], true);
return "HELP ME MY BOSS " + passiveVoice;
return 'HELP ME MY BOSS ' + passiveVoice;
}
};
@@ -120,7 +119,7 @@ describe('track ids', function() {
equal(options.ids[1], 'slave.driver');
equal(options.ids[2], 'zomg');
return "HELP ME MY BOSS " + options.ids[0] + ':' + passiveVoice + ' ' + options.ids[1] + ':' + noun;
return 'HELP ME MY BOSS ' + options.ids[0] + ':' + passiveVoice + ' ' + options.ids[1] + ':' + noun;
}
};
@@ -129,6 +128,9 @@ describe('track ids', function() {
describe('builtin helpers', function() {
var helpers = {
blockParams: function(name, options) {
return name + ':' + options.ids[0] + '\n';
},
wycats: function(name, options) {
return name + ':' + options.data.contextPath + '\n';
}
@@ -151,13 +153,7 @@ describe('track ids', function() {
equals(template({array: [{name: 'foo'}, {name: 'bar'}]}, {helpers: helpers}), 'foo:.array..0\nbar:.array..1\n');
});
it('should handle block params', function() {
var helpers = {
wycats: function(name, options) {
return name + ':' + options.ids[0] + '\n';
}
};
var template = CompilerContext.compile('{{#each array as |value|}}{{wycats value.name}}{{/each}}', {trackIds: true});
var template = CompilerContext.compile('{{#each array as |value|}}{{blockParams value.name}}{{/each}}', {trackIds: true});
equals(template({array: [{name: 'foo'}, {name: 'bar'}]}, {helpers: helpers}), 'foo:array.0.name\nbar:array.1.name\n');
});
+6 -8
View File
@@ -1,19 +1,17 @@
/*global Handlebars, shouldCompileTo */
describe('utils', function() {
describe('#SafeString', function() {
it("constructing a safestring from a string and checking its type", function() {
var safe = new Handlebars.SafeString("testing 1, 2, 3");
it('constructing a safestring from a string and checking its type', function() {
var safe = new Handlebars.SafeString('testing 1, 2, 3');
if (!(safe instanceof Handlebars.SafeString)) {
throw new Error('Must be instance of SafeString');
}
equals(safe == 'testing 1, 2, 3', true, 'SafeString is equivalent to its underlying string');
equals(safe.toString(), 'testing 1, 2, 3', 'SafeString is equivalent to its underlying string');
});
it("it should not escape SafeString properties", function() {
var name = new Handlebars.SafeString("<em>Sean O&#x27;Malley</em>");
it('it should not escape SafeString properties', function() {
var name = new Handlebars.SafeString('<em>Sean O&#x27;Malley</em>');
shouldCompileTo('{{name}}', [{ name: name }], "<em>Sean O&#x27;Malley</em>");
shouldCompileTo('{{name}}', [{name: name}], '<em>Sean O&#x27;Malley</em>');
});
});
-2
View File
@@ -1,5 +1,3 @@
/*global Handlebars, shouldThrow */
describe('Visitor', function() {
if (!Handlebars.Visitor || !Handlebars.print) {
return;
-2
View File
@@ -1,5 +1,3 @@
/*global shouldCompileTo, shouldCompileToWithPartials */
describe('whitespace control', function() {
it('should strip whitespace around mustache calls', function() {
var hash = {foo: 'bar<'};
+1 -1
View File
@@ -18,7 +18,7 @@ module.exports = function(grunt) {
grunt.log.writeln('Updating to version ' + version);
async.each([
['lib/handlebars/base.js', /var VERSION = "(.*)";/, 'var VERSION = "' + version + '";'],
['lib/handlebars/base.js', /var VERSION = ['"](.*)['"];/, 'var VERSION = "' + version + '";'],
['components/bower.json', /"version":.*/, '"version": "' + version + '",'],
['components/handlebars.js.nuspec', /<version>.*<\/version>/, '<version>' + version + '</version>']
],