summaryrefslogtreecommitdiff
path: root/dev/lib/handlebars/src/spec
diff options
context:
space:
mode:
authortoasted-nutbread <toasted-nutbread@users.noreply.github.com>2024-02-05 06:11:08 -0500
committerGitHub <noreply@github.com>2024-02-05 11:11:08 +0000
commit71c3aff53173cc83a96d7d2715b7918bdbc2d8a5 (patch)
treed13cc4a90ef26bb6476daca1edc1999208f4fadc /dev/lib/handlebars/src/spec
parent0e7531bc5b443461d7e76e20877464ccf48a3ef5 (diff)
kbn-handlebars dependency update (#613)
* Update kbn-handlebars dependency * Move handlebars dependency to dev * Update package * Update readme * Update readme * Ignore legal file
Diffstat (limited to 'dev/lib/handlebars/src/spec')
-rw-r--r--dev/lib/handlebars/src/spec/.upstream_git_hash1
-rw-r--r--dev/lib/handlebars/src/spec/index.basic.test.ts481
-rw-r--r--dev/lib/handlebars/src/spec/index.blocks.test.ts366
-rw-r--r--dev/lib/handlebars/src/spec/index.builtins.test.ts676
-rw-r--r--dev/lib/handlebars/src/spec/index.compiler.test.ts86
-rw-r--r--dev/lib/handlebars/src/spec/index.data.test.ts269
-rw-r--r--dev/lib/handlebars/src/spec/index.helpers.test.ts958
-rw-r--r--dev/lib/handlebars/src/spec/index.partials.test.ts591
-rw-r--r--dev/lib/handlebars/src/spec/index.regressions.test.ts379
-rw-r--r--dev/lib/handlebars/src/spec/index.security.test.ts132
-rw-r--r--dev/lib/handlebars/src/spec/index.strict.test.ts164
-rw-r--r--dev/lib/handlebars/src/spec/index.subexpressions.test.ts214
-rw-r--r--dev/lib/handlebars/src/spec/index.utils.test.ts24
-rw-r--r--dev/lib/handlebars/src/spec/index.whitespace_control.test.ts88
14 files changed, 0 insertions, 4429 deletions
diff --git a/dev/lib/handlebars/src/spec/.upstream_git_hash b/dev/lib/handlebars/src/spec/.upstream_git_hash
deleted file mode 100644
index 5a6b1831..00000000
--- a/dev/lib/handlebars/src/spec/.upstream_git_hash
+++ /dev/null
@@ -1 +0,0 @@
-c65c6cce3f626e4896a9d59250f0908be695adae \ No newline at end of file
diff --git a/dev/lib/handlebars/src/spec/index.basic.test.ts b/dev/lib/handlebars/src/spec/index.basic.test.ts
deleted file mode 100644
index 6acf3ae9..00000000
--- a/dev/lib/handlebars/src/spec/index.basic.test.ts
+++ /dev/null
@@ -1,481 +0,0 @@
-/*
- * This file is forked from the handlebars project (https://github.com/handlebars-lang/handlebars.js),
- * and may include modifications made by Elasticsearch B.V.
- * Elasticsearch B.V. licenses this file to you under the MIT License.
- * See `packages/kbn-handlebars/LICENSE` for more information.
- */
-
-import Handlebars from '../..';
-import { expectTemplate } from '../__jest__/test_bench';
-
-describe('basic context', () => {
- it('most basic', () => {
- expectTemplate('{{foo}}').withInput({ foo: 'foo' }).toCompileTo('foo');
- });
-
- it('escaping', () => {
- expectTemplate('\\{{foo}}').withInput({ foo: 'food' }).toCompileTo('{{foo}}');
- expectTemplate('content \\{{foo}}').withInput({ foo: 'food' }).toCompileTo('content {{foo}}');
- expectTemplate('\\\\{{foo}}').withInput({ foo: 'food' }).toCompileTo('\\food');
- expectTemplate('content \\\\{{foo}}').withInput({ foo: 'food' }).toCompileTo('content \\food');
- expectTemplate('\\\\ {{foo}}').withInput({ foo: 'food' }).toCompileTo('\\\\ food');
- });
-
- it('compiling with a basic context', () => {
- expectTemplate('Goodbye\n{{cruel}}\n{{world}}!')
- .withInput({
- cruel: 'cruel',
- world: 'world',
- })
- .toCompileTo('Goodbye\ncruel\nworld!');
- });
-
- it('compiling with a string context', () => {
- expectTemplate('{{.}}{{length}}').withInput('bye').toCompileTo('bye3');
- });
-
- it('compiling with an undefined context', () => {
- expectTemplate('Goodbye\n{{cruel}}\n{{world.bar}}!')
- .withInput(undefined)
- .toCompileTo('Goodbye\n\n!');
-
- expectTemplate('{{#unless foo}}Goodbye{{../test}}{{test2}}{{/unless}}')
- .withInput(undefined)
- .toCompileTo('Goodbye');
- });
-
- it('comments', () => {
- expectTemplate('{{! Goodbye}}Goodbye\n{{cruel}}\n{{world}}!')
- .withInput({
- cruel: 'cruel',
- world: 'world',
- })
- .toCompileTo('Goodbye\ncruel\nworld!');
-
- expectTemplate(' {{~! comment ~}} blah').toCompileTo('blah');
- expectTemplate(' {{~!-- long-comment --~}} blah').toCompileTo('blah');
- expectTemplate(' {{! comment ~}} blah').toCompileTo(' blah');
- expectTemplate(' {{!-- long-comment --~}} blah').toCompileTo(' blah');
- expectTemplate(' {{~! comment}} blah').toCompileTo(' blah');
- expectTemplate(' {{~!-- long-comment --}} blah').toCompileTo(' blah');
- });
-
- it('boolean', () => {
- const string = '{{#goodbye}}GOODBYE {{/goodbye}}cruel {{world}}!';
- expectTemplate(string)
- .withInput({
- goodbye: true,
- world: 'world',
- })
- .toCompileTo('GOODBYE cruel world!');
-
- expectTemplate(string)
- .withInput({
- goodbye: false,
- world: 'world',
- })
- .toCompileTo('cruel world!');
- });
-
- it('zeros', () => {
- expectTemplate('num1: {{num1}}, num2: {{num2}}')
- .withInput({
- num1: 42,
- num2: 0,
- })
- .toCompileTo('num1: 42, num2: 0');
-
- expectTemplate('num: {{.}}').withInput(0).toCompileTo('num: 0');
-
- expectTemplate('num: {{num1/num2}}')
- .withInput({ num1: { num2: 0 } })
- .toCompileTo('num: 0');
- });
-
- it('false', () => {
- /* eslint-disable no-new-wrappers */
- expectTemplate('val1: {{val1}}, val2: {{val2}}')
- .withInput({
- val1: false,
- val2: new Boolean(false),
- })
- .toCompileTo('val1: false, val2: false');
-
- expectTemplate('val: {{.}}').withInput(false).toCompileTo('val: false');
-
- expectTemplate('val: {{val1/val2}}')
- .withInput({ val1: { val2: false } })
- .toCompileTo('val: false');
-
- expectTemplate('val1: {{{val1}}}, val2: {{{val2}}}')
- .withInput({
- val1: false,
- val2: new Boolean(false),
- })
- .toCompileTo('val1: false, val2: false');
-
- expectTemplate('val: {{{val1/val2}}}')
- .withInput({ val1: { val2: false } })
- .toCompileTo('val: false');
- /* eslint-enable */
- });
-
- it('should handle undefined and null', () => {
- expectTemplate('{{awesome undefined null}}')
- .withInput({
- awesome(_undefined: any, _null: any, options: any) {
- return (_undefined === undefined) + ' ' + (_null === null) + ' ' + typeof options;
- },
- })
- .toCompileTo('true true object');
-
- expectTemplate('{{undefined}}')
- .withInput({
- undefined() {
- return 'undefined!';
- },
- })
- .toCompileTo('undefined!');
-
- expectTemplate('{{null}}')
- .withInput({
- null() {
- return 'null!';
- },
- })
- .toCompileTo('null!');
- });
-
- it('newlines', () => {
- expectTemplate("Alan's\nTest").toCompileTo("Alan's\nTest");
- expectTemplate("Alan's\rTest").toCompileTo("Alan's\rTest");
- });
-
- it('escaping text', () => {
- expectTemplate("Awesome's").toCompileTo("Awesome's");
- expectTemplate('Awesome\\').toCompileTo('Awesome\\');
- expectTemplate('Awesome\\\\ foo').toCompileTo('Awesome\\\\ foo');
- expectTemplate('Awesome {{foo}}').withInput({ foo: '\\' }).toCompileTo('Awesome \\');
- expectTemplate(" ' ' ").toCompileTo(" ' ' ");
- });
-
- it('escaping expressions', () => {
- expectTemplate('{{{awesome}}}').withInput({ awesome: "&'\\<>" }).toCompileTo("&'\\<>");
-
- expectTemplate('{{&awesome}}').withInput({ awesome: "&'\\<>" }).toCompileTo("&'\\<>");
-
- expectTemplate('{{awesome}}')
- .withInput({ awesome: '&"\'`\\<>' })
- .toCompileTo('&amp;&quot;&#x27;&#x60;\\&lt;&gt;');
-
- expectTemplate('{{awesome}}')
- .withInput({ awesome: 'Escaped, <b> looks like: &lt;b&gt;' })
- .toCompileTo('Escaped, &lt;b&gt; looks like: &amp;lt;b&amp;gt;');
- });
-
- it("functions returning safestrings shouldn't be escaped", () => {
- expectTemplate('{{awesome}}')
- .withInput({
- awesome() {
- return new Handlebars.SafeString("&'\\<>");
- },
- })
- .toCompileTo("&'\\<>");
- });
-
- it('functions', () => {
- expectTemplate('{{awesome}}')
- .withInput({
- awesome() {
- return 'Awesome';
- },
- })
- .toCompileTo('Awesome');
-
- expectTemplate('{{awesome}}')
- .withInput({
- awesome() {
- return this.more;
- },
- more: 'More awesome',
- })
- .toCompileTo('More awesome');
- });
-
- it('functions with context argument', () => {
- expectTemplate('{{awesome frank}}')
- .withInput({
- awesome(context: any) {
- return context;
- },
- frank: 'Frank',
- })
- .toCompileTo('Frank');
- });
-
- it('pathed functions with context argument', () => {
- expectTemplate('{{bar.awesome frank}}')
- .withInput({
- bar: {
- awesome(context: any) {
- return context;
- },
- },
- frank: 'Frank',
- })
- .toCompileTo('Frank');
- });
-
- it('depthed functions with context argument', () => {
- expectTemplate('{{#with frank}}{{../awesome .}}{{/with}}')
- .withInput({
- awesome(context: any) {
- return context;
- },
- frank: 'Frank',
- })
- .toCompileTo('Frank');
- });
-
- it('block functions with context argument', () => {
- expectTemplate('{{#awesome 1}}inner {{.}}{{/awesome}}')
- .withInput({
- awesome(context: any, options: any) {
- return options.fn(context);
- },
- })
- .toCompileTo('inner 1');
- });
-
- it('depthed block functions with context argument', () => {
- expectTemplate('{{#with value}}{{#../awesome 1}}inner {{.}}{{/../awesome}}{{/with}}')
- .withInput({
- value: true,
- awesome(context: any, options: any) {
- return options.fn(context);
- },
- })
- .toCompileTo('inner 1');
- });
-
- it('block functions without context argument', () => {
- expectTemplate('{{#awesome}}inner{{/awesome}}')
- .withInput({
- awesome(options: any) {
- return options.fn(this);
- },
- })
- .toCompileTo('inner');
- });
-
- it('pathed block functions without context argument', () => {
- expectTemplate('{{#foo.awesome}}inner{{/foo.awesome}}')
- .withInput({
- foo: {
- awesome() {
- return this;
- },
- },
- })
- .toCompileTo('inner');
- });
-
- it('depthed block functions without context argument', () => {
- expectTemplate('{{#with value}}{{#../awesome}}inner{{/../awesome}}{{/with}}')
- .withInput({
- value: true,
- awesome() {
- return this;
- },
- })
- .toCompileTo('inner');
- });
-
- it('paths with hyphens', () => {
- expectTemplate('{{foo-bar}}').withInput({ 'foo-bar': 'baz' }).toCompileTo('baz');
-
- expectTemplate('{{foo.foo-bar}}')
- .withInput({ foo: { 'foo-bar': 'baz' } })
- .toCompileTo('baz');
-
- expectTemplate('{{foo/foo-bar}}')
- .withInput({ foo: { 'foo-bar': 'baz' } })
- .toCompileTo('baz');
- });
-
- it('nested paths', () => {
- expectTemplate('Goodbye {{alan/expression}} world!')
- .withInput({ alan: { expression: 'beautiful' } })
- .toCompileTo('Goodbye beautiful world!');
- });
-
- it('nested paths with empty string value', () => {
- expectTemplate('Goodbye {{alan/expression}} world!')
- .withInput({ alan: { expression: '' } })
- .toCompileTo('Goodbye world!');
- });
-
- it('literal paths', () => {
- expectTemplate('Goodbye {{[@alan]/expression}} world!')
- .withInput({ '@alan': { expression: 'beautiful' } })
- .toCompileTo('Goodbye beautiful world!');
-
- expectTemplate('Goodbye {{[foo bar]/expression}} world!')
- .withInput({ 'foo bar': { expression: 'beautiful' } })
- .toCompileTo('Goodbye beautiful world!');
- });
-
- it('literal references', () => {
- expectTemplate('Goodbye {{[foo bar]}} world!')
- .withInput({ 'foo bar': 'beautiful' })
- .toCompileTo('Goodbye beautiful world!');
-
- expectTemplate('Goodbye {{"foo bar"}} world!')
- .withInput({ 'foo bar': 'beautiful' })
- .toCompileTo('Goodbye beautiful world!');
-
- expectTemplate("Goodbye {{'foo bar'}} world!")
- .withInput({ 'foo bar': 'beautiful' })
- .toCompileTo('Goodbye beautiful world!');
-
- expectTemplate('Goodbye {{"foo[bar"}} world!')
- .withInput({ 'foo[bar': 'beautiful' })
- .toCompileTo('Goodbye beautiful world!');
-
- expectTemplate('Goodbye {{"foo\'bar"}} world!')
- .withInput({ "foo'bar": 'beautiful' })
- .toCompileTo('Goodbye beautiful world!');
-
- expectTemplate("Goodbye {{'foo\"bar'}} world!")
- .withInput({ 'foo"bar': 'beautiful' })
- .toCompileTo('Goodbye beautiful world!');
- });
-
- it("that current context path ({{.}}) doesn't hit helpers", () => {
- expectTemplate('test: {{.}}')
- .withInput(null)
- // @ts-expect-error Setting the helper to a string instead of a function doesn't make sense normally, but here it doesn't matter
- .withHelpers({ helper: 'awesome' })
- .toCompileTo('test: ');
- });
-
- it('complex but empty paths', () => {
- expectTemplate('{{person/name}}')
- .withInput({ person: { name: null } })
- .toCompileTo('');
-
- expectTemplate('{{person/name}}').withInput({ person: {} }).toCompileTo('');
- });
-
- it('this keyword in paths', () => {
- expectTemplate('{{#goodbyes}}{{this}}{{/goodbyes}}')
- .withInput({ goodbyes: ['goodbye', 'Goodbye', 'GOODBYE'] })
- .toCompileTo('goodbyeGoodbyeGOODBYE');
-
- expectTemplate('{{#hellos}}{{this/text}}{{/hellos}}')
- .withInput({
- hellos: [{ text: 'hello' }, { text: 'Hello' }, { text: 'HELLO' }],
- })
- .toCompileTo('helloHelloHELLO');
- });
-
- it('this keyword nested inside path', () => {
- expectTemplate('{{#hellos}}{{text/this/foo}}{{/hellos}}').toThrow(
- 'Invalid path: text/this - 1:13'
- );
-
- expectTemplate('{{[this]}}').withInput({ this: 'bar' }).toCompileTo('bar');
-
- expectTemplate('{{text/[this]}}')
- .withInput({ text: { this: 'bar' } })
- .toCompileTo('bar');
- });
-
- it('this keyword in helpers', () => {
- const helpers = {
- foo(value: any) {
- return 'bar ' + value;
- },
- };
-
- expectTemplate('{{#goodbyes}}{{foo this}}{{/goodbyes}}')
- .withInput({ goodbyes: ['goodbye', 'Goodbye', 'GOODBYE'] })
- .withHelpers(helpers)
- .toCompileTo('bar goodbyebar Goodbyebar GOODBYE');
-
- expectTemplate('{{#hellos}}{{foo this/text}}{{/hellos}}')
- .withInput({
- hellos: [{ text: 'hello' }, { text: 'Hello' }, { text: 'HELLO' }],
- })
- .withHelpers(helpers)
- .toCompileTo('bar hellobar Hellobar HELLO');
- });
-
- it('this keyword nested inside helpers param', () => {
- expectTemplate('{{#hellos}}{{foo text/this/foo}}{{/hellos}}').toThrow(
- 'Invalid path: text/this - 1:17'
- );
-
- expectTemplate('{{foo [this]}}')
- .withInput({
- foo(value: any) {
- return value;
- },
- this: 'bar',
- })
- .toCompileTo('bar');
-
- expectTemplate('{{foo text/[this]}}')
- .withInput({
- foo(value: any) {
- return value;
- },
- text: { this: 'bar' },
- })
- .toCompileTo('bar');
- });
-
- it('pass string literals', () => {
- expectTemplate('{{"foo"}}').toCompileTo('');
- expectTemplate('{{"foo"}}').withInput({ foo: 'bar' }).toCompileTo('bar');
-
- expectTemplate('{{#"foo"}}{{.}}{{/"foo"}}')
- .withInput({
- foo: ['bar', 'baz'],
- })
- .toCompileTo('barbaz');
- });
-
- it('pass number literals', () => {
- expectTemplate('{{12}}').toCompileTo('');
- expectTemplate('{{12}}').withInput({ '12': 'bar' }).toCompileTo('bar');
- expectTemplate('{{12.34}}').toCompileTo('');
- expectTemplate('{{12.34}}').withInput({ '12.34': 'bar' }).toCompileTo('bar');
- expectTemplate('{{12.34 1}}')
- .withInput({
- '12.34'(arg: any) {
- return 'bar' + arg;
- },
- })
- .toCompileTo('bar1');
- });
-
- it('pass boolean literals', () => {
- expectTemplate('{{true}}').toCompileTo('');
- expectTemplate('{{true}}').withInput({ '': 'foo' }).toCompileTo('');
- expectTemplate('{{false}}').withInput({ false: 'foo' }).toCompileTo('foo');
- });
-
- it('should handle literals in subexpression', () => {
- expectTemplate('{{foo (false)}}')
- .withInput({
- false() {
- return 'bar';
- },
- })
- .withHelper('foo', function (arg) {
- return arg;
- })
- .toCompileTo('bar');
- });
-});
diff --git a/dev/lib/handlebars/src/spec/index.blocks.test.ts b/dev/lib/handlebars/src/spec/index.blocks.test.ts
deleted file mode 100644
index 2d9a8707..00000000
--- a/dev/lib/handlebars/src/spec/index.blocks.test.ts
+++ /dev/null
@@ -1,366 +0,0 @@
-/*
- * This file is forked from the handlebars project (https://github.com/handlebars-lang/handlebars.js),
- * and may include modifications made by Elasticsearch B.V.
- * Elasticsearch B.V. licenses this file to you under the MIT License.
- * See `packages/kbn-handlebars/LICENSE` for more information.
- */
-
-import Handlebars, { type HelperOptions } from '../..';
-import { expectTemplate } from '../__jest__/test_bench';
-
-describe('blocks', () => {
- it('array', () => {
- const string = '{{#goodbyes}}{{text}}! {{/goodbyes}}cruel {{world}}!';
-
- expectTemplate(string)
- .withInput({
- goodbyes: [{ text: 'goodbye' }, { text: 'Goodbye' }, { text: 'GOODBYE' }],
- world: 'world',
- })
- .toCompileTo('goodbye! Goodbye! GOODBYE! cruel world!');
-
- expectTemplate(string)
- .withInput({
- goodbyes: [],
- world: 'world',
- })
- .toCompileTo('cruel world!');
- });
-
- it('array without data', () => {
- expectTemplate('{{#goodbyes}}{{text}}{{/goodbyes}} {{#goodbyes}}{{text}}{{/goodbyes}}')
- .withInput({
- goodbyes: [{ text: 'goodbye' }, { text: 'Goodbye' }, { text: 'GOODBYE' }],
- world: 'world',
- })
- .toCompileTo('goodbyeGoodbyeGOODBYE goodbyeGoodbyeGOODBYE');
- });
-
- it('array with @index', () => {
- expectTemplate('{{#goodbyes}}{{@index}}. {{text}}! {{/goodbyes}}cruel {{world}}!')
- .withInput({
- goodbyes: [{ text: 'goodbye' }, { text: 'Goodbye' }, { text: 'GOODBYE' }],
- world: 'world',
- })
- .toCompileTo('0. goodbye! 1. Goodbye! 2. GOODBYE! cruel world!');
- });
-
- it('empty block', () => {
- const string = '{{#goodbyes}}{{/goodbyes}}cruel {{world}}!';
-
- expectTemplate(string)
- .withInput({
- goodbyes: [{ text: 'goodbye' }, { text: 'Goodbye' }, { text: 'GOODBYE' }],
- world: 'world',
- })
- .toCompileTo('cruel world!');
-
- expectTemplate(string)
- .withInput({
- goodbyes: [],
- world: 'world',
- })
- .toCompileTo('cruel world!');
- });
-
- it('block with complex lookup', () => {
- expectTemplate('{{#goodbyes}}{{text}} cruel {{../name}}! {{/goodbyes}}')
- .withInput({
- name: 'Alan',
- goodbyes: [{ text: 'goodbye' }, { text: 'Goodbye' }, { text: 'GOODBYE' }],
- })
- .toCompileTo('goodbye cruel Alan! Goodbye cruel Alan! GOODBYE cruel Alan! ');
- });
-
- it('multiple blocks with complex lookup', () => {
- expectTemplate('{{#goodbyes}}{{../name}}{{../name}}{{/goodbyes}}')
- .withInput({
- name: 'Alan',
- goodbyes: [{ text: 'goodbye' }, { text: 'Goodbye' }, { text: 'GOODBYE' }],
- })
- .toCompileTo('AlanAlanAlanAlanAlanAlan');
- });
-
- it('block with complex lookup using nested context', () => {
- expectTemplate('{{#goodbyes}}{{text}} cruel {{foo/../name}}! {{/goodbyes}}').toThrow(Error);
- });
-
- it('block with deep nested complex lookup', () => {
- expectTemplate(
- '{{#outer}}Goodbye {{#inner}}cruel {{../sibling}} {{../../omg}}{{/inner}}{{/outer}}'
- )
- .withInput({
- omg: 'OMG!',
- outer: [{ sibling: 'sad', inner: [{ text: 'goodbye' }] }],
- })
- .toCompileTo('Goodbye cruel sad OMG!');
- });
-
- it('works with cached blocks', () => {
- expectTemplate('{{#each person}}{{#with .}}{{first}} {{last}}{{/with}}{{/each}}')
- .withCompileOptions({ data: false })
- .withInput({
- person: [
- { first: 'Alan', last: 'Johnson' },
- { first: 'Alan', last: 'Johnson' },
- ],
- })
- .toCompileTo('Alan JohnsonAlan Johnson');
- });
-
- describe('inverted sections', () => {
- it('inverted sections with unset value', () => {
- expectTemplate(
- '{{#goodbyes}}{{this}}{{/goodbyes}}{{^goodbyes}}Right On!{{/goodbyes}}'
- ).toCompileTo('Right On!');
- });
-
- it('inverted section with false value', () => {
- expectTemplate('{{#goodbyes}}{{this}}{{/goodbyes}}{{^goodbyes}}Right On!{{/goodbyes}}')
- .withInput({ goodbyes: false })
- .toCompileTo('Right On!');
- });
-
- it('inverted section with empty set', () => {
- expectTemplate('{{#goodbyes}}{{this}}{{/goodbyes}}{{^goodbyes}}Right On!{{/goodbyes}}')
- .withInput({ goodbyes: [] })
- .toCompileTo('Right On!');
- });
-
- it('block inverted sections', () => {
- expectTemplate('{{#people}}{{name}}{{^}}{{none}}{{/people}}')
- .withInput({ none: 'No people' })
- .toCompileTo('No people');
- });
-
- it('chained inverted sections', () => {
- expectTemplate('{{#people}}{{name}}{{else if none}}{{none}}{{/people}}')
- .withInput({ none: 'No people' })
- .toCompileTo('No people');
-
- expectTemplate(
- '{{#people}}{{name}}{{else if nothere}}fail{{else unless nothere}}{{none}}{{/people}}'
- )
- .withInput({ none: 'No people' })
- .toCompileTo('No people');
-
- expectTemplate('{{#people}}{{name}}{{else if none}}{{none}}{{else}}fail{{/people}}')
- .withInput({ none: 'No people' })
- .toCompileTo('No people');
- });
-
- it('chained inverted sections with mismatch', () => {
- expectTemplate('{{#people}}{{name}}{{else if none}}{{none}}{{/if}}').toThrow(Error);
- });
-
- it('block inverted sections with empty arrays', () => {
- expectTemplate('{{#people}}{{name}}{{^}}{{none}}{{/people}}')
- .withInput({
- none: 'No people',
- people: [],
- })
- .toCompileTo('No people');
- });
- });
-
- describe('standalone sections', () => {
- it('block standalone else sections', () => {
- expectTemplate('{{#people}}\n{{name}}\n{{^}}\n{{none}}\n{{/people}}\n')
- .withInput({ none: 'No people' })
- .toCompileTo('No people\n');
-
- expectTemplate('{{#none}}\n{{.}}\n{{^}}\n{{none}}\n{{/none}}\n')
- .withInput({ none: 'No people' })
- .toCompileTo('No people\n');
-
- expectTemplate('{{#people}}\n{{name}}\n{{^}}\n{{none}}\n{{/people}}\n')
- .withInput({ none: 'No people' })
- .toCompileTo('No people\n');
- });
-
- it('block standalone chained else sections', () => {
- expectTemplate('{{#people}}\n{{name}}\n{{else if none}}\n{{none}}\n{{/people}}\n')
- .withInput({ none: 'No people' })
- .toCompileTo('No people\n');
-
- expectTemplate('{{#people}}\n{{name}}\n{{else if none}}\n{{none}}\n{{^}}\n{{/people}}\n')
- .withInput({ none: 'No people' })
- .toCompileTo('No people\n');
- });
-
- it('should handle nesting', () => {
- expectTemplate('{{#data}}\n{{#if true}}\n{{.}}\n{{/if}}\n{{/data}}\nOK.')
- .withInput({
- data: [1, 3, 5],
- })
- .toCompileTo('1\n3\n5\nOK.');
- });
- });
-
- describe('decorators', () => {
- it('should apply mustache decorators', () => {
- expectTemplate('{{#helper}}{{*decorator}}{{/helper}}')
- .withHelper('helper', function (options: HelperOptions) {
- return (options.fn as any).run;
- })
- .withDecorator('decorator', function (fn) {
- (fn as any).run = 'success';
- return fn;
- })
- .toCompileTo('success');
- });
-
- it('should apply allow undefined return', () => {
- expectTemplate('{{#helper}}{{*decorator}}suc{{/helper}}')
- .withHelper('helper', function (options: HelperOptions) {
- return options.fn() + (options.fn as any).run;
- })
- .withDecorator('decorator', function (fn) {
- (fn as any).run = 'cess';
- })
- .toCompileTo('success');
- });
-
- it('should apply block decorators', () => {
- expectTemplate('{{#helper}}{{#*decorator}}success{{/decorator}}{{/helper}}')
- .withHelper('helper', function (options: HelperOptions) {
- return (options.fn as any).run;
- })
- .withDecorator('decorator', function (fn, props, container, options) {
- (fn as any).run = options.fn();
- return fn;
- })
- .toCompileTo('success');
- });
-
- it('should support nested decorators', () => {
- expectTemplate(
- '{{#helper}}{{#*decorator}}{{#*nested}}suc{{/nested}}cess{{/decorator}}{{/helper}}'
- )
- .withHelper('helper', function (options: HelperOptions) {
- return (options.fn as any).run;
- })
- .withDecorators({
- decorator(fn, props, container, options) {
- (fn as any).run = options.fn.nested + options.fn();
- return fn;
- },
- nested(fn, props, container, options) {
- props.nested = options.fn();
- },
- })
- .toCompileTo('success');
- });
-
- it('should apply multiple decorators', () => {
- expectTemplate(
- '{{#helper}}{{#*decorator}}suc{{/decorator}}{{#*decorator}}cess{{/decorator}}{{/helper}}'
- )
- .withHelper('helper', function (options: HelperOptions) {
- return (options.fn as any).run;
- })
- .withDecorator('decorator', function (fn, props, container, options) {
- (fn as any).run = ((fn as any).run || '') + options.fn();
- return fn;
- })
- .toCompileTo('success');
- });
-
- it('should access parent variables', () => {
- expectTemplate('{{#helper}}{{*decorator foo}}{{/helper}}')
- .withHelper('helper', function (options: HelperOptions) {
- return (options.fn as any).run;
- })
- .withDecorator('decorator', function (fn, props, container, options) {
- (fn as any).run = options.args;
- return fn;
- })
- .withInput({ foo: 'success' })
- .toCompileTo('success');
- });
-
- it('should work with root program', () => {
- let run;
- expectTemplate('{{*decorator "success"}}')
- .withDecorator('decorator', function (fn, props, container, options) {
- expect(options.args[0]).toEqual('success');
- run = true;
- return fn;
- })
- .withInput({ foo: 'success' })
- .toCompileTo('');
- expect(run).toEqual(true);
- });
-
- it('should fail when accessing variables from root', () => {
- let run;
- expectTemplate('{{*decorator foo}}')
- .withDecorator('decorator', function (fn, props, container, options) {
- expect(options.args[0]).toBeUndefined();
- run = true;
- return fn;
- })
- .withInput({ foo: 'fail' })
- .toCompileTo('');
- expect(run).toEqual(true);
- });
-
- describe('registration', () => {
- beforeEach(() => {
- global.kbnHandlebarsEnv = Handlebars.create();
- });
-
- afterEach(() => {
- global.kbnHandlebarsEnv = null;
- });
-
- it('unregisters', () => {
- // @ts-expect-error: Cannot assign to 'decorators' because it is a read-only property.
- kbnHandlebarsEnv!.decorators = {};
-
- kbnHandlebarsEnv!.registerDecorator('foo', function () {
- return 'fail';
- });
-
- expect(!!kbnHandlebarsEnv!.decorators.foo).toEqual(true);
- kbnHandlebarsEnv!.unregisterDecorator('foo');
- expect(kbnHandlebarsEnv!.decorators.foo).toBeUndefined();
- });
-
- it('allows multiple globals', () => {
- // @ts-expect-error: Cannot assign to 'decorators' because it is a read-only property.
- kbnHandlebarsEnv!.decorators = {};
-
- // @ts-expect-error: Expected 2 arguments, but got 1.
- kbnHandlebarsEnv!.registerDecorator({
- foo() {},
- bar() {},
- });
-
- expect(!!kbnHandlebarsEnv!.decorators.foo).toEqual(true);
- expect(!!kbnHandlebarsEnv!.decorators.bar).toEqual(true);
- kbnHandlebarsEnv!.unregisterDecorator('foo');
- kbnHandlebarsEnv!.unregisterDecorator('bar');
- expect(kbnHandlebarsEnv!.decorators.foo).toBeUndefined();
- expect(kbnHandlebarsEnv!.decorators.bar).toBeUndefined();
- });
-
- it('fails with multiple and args', () => {
- expect(() => {
- kbnHandlebarsEnv!.registerDecorator(
- // @ts-expect-error: Argument of type '{ world(): string; testHelper(): string; }' is not assignable to parameter of type 'string'.
- {
- world() {
- return 'world!';
- },
- testHelper() {
- return 'found it!';
- },
- },
- {}
- );
- }).toThrow('Arg not supported with multiple decorators');
- });
- });
- });
-});
diff --git a/dev/lib/handlebars/src/spec/index.builtins.test.ts b/dev/lib/handlebars/src/spec/index.builtins.test.ts
deleted file mode 100644
index c47ec29f..00000000
--- a/dev/lib/handlebars/src/spec/index.builtins.test.ts
+++ /dev/null
@@ -1,676 +0,0 @@
-/*
- * This file is forked from the handlebars project (https://github.com/handlebars-lang/handlebars.js),
- * and may include modifications made by Elasticsearch B.V.
- * Elasticsearch B.V. licenses this file to you under the MIT License.
- * See `packages/kbn-handlebars/LICENSE` for more information.
- */
-
-/* eslint-disable max-classes-per-file */
-
-import Handlebars from '../..';
-import { expectTemplate } from '../__jest__/test_bench';
-
-describe('builtin helpers', () => {
- describe('#if', () => {
- it('if', () => {
- const string = '{{#if goodbye}}GOODBYE {{/if}}cruel {{world}}!';
-
- expectTemplate(string)
- .withInput({
- goodbye: true,
- world: 'world',
- })
- .toCompileTo('GOODBYE cruel world!');
-
- expectTemplate(string)
- .withInput({
- goodbye: 'dummy',
- world: 'world',
- })
- .toCompileTo('GOODBYE cruel world!');
-
- expectTemplate(string)
- .withInput({
- goodbye: false,
- world: 'world',
- })
- .toCompileTo('cruel world!');
-
- expectTemplate(string).withInput({ world: 'world' }).toCompileTo('cruel world!');
-
- expectTemplate(string)
- .withInput({
- goodbye: ['foo'],
- world: 'world',
- })
- .toCompileTo('GOODBYE cruel world!');
-
- expectTemplate(string)
- .withInput({
- goodbye: [],
- world: 'world',
- })
- .toCompileTo('cruel world!');
-
- expectTemplate(string)
- .withInput({
- goodbye: 0,
- world: 'world',
- })
- .toCompileTo('cruel world!');
-
- expectTemplate('{{#if goodbye includeZero=true}}GOODBYE {{/if}}cruel {{world}}!')
- .withInput({
- goodbye: 0,
- world: 'world',
- })
- .toCompileTo('GOODBYE cruel world!');
- });
-
- it('if with function argument', () => {
- const string = '{{#if goodbye}}GOODBYE {{/if}}cruel {{world}}!';
-
- expectTemplate(string)
- .withInput({
- goodbye() {
- return true;
- },
- world: 'world',
- })
- .toCompileTo('GOODBYE cruel world!');
-
- expectTemplate(string)
- .withInput({
- goodbye() {
- return this.world;
- },
- world: 'world',
- })
- .toCompileTo('GOODBYE cruel world!');
-
- expectTemplate(string)
- .withInput({
- goodbye() {
- return false;
- },
- world: 'world',
- })
- .toCompileTo('cruel world!');
-
- expectTemplate(string)
- .withInput({
- goodbye() {
- return this.foo;
- },
- world: 'world',
- })
- .toCompileTo('cruel world!');
- });
-
- it('should not change the depth list', () => {
- expectTemplate('{{#with foo}}{{#if goodbye}}GOODBYE cruel {{../world}}!{{/if}}{{/with}}')
- .withInput({
- foo: { goodbye: true },
- world: 'world',
- })
- .toCompileTo('GOODBYE cruel world!');
- });
- });
-
- describe('#with', () => {
- it('with', () => {
- expectTemplate('{{#with person}}{{first}} {{last}}{{/with}}')
- .withInput({
- person: {
- first: 'Alan',
- last: 'Johnson',
- },
- })
- .toCompileTo('Alan Johnson');
- });
-
- it('with with function argument', () => {
- expectTemplate('{{#with person}}{{first}} {{last}}{{/with}}')
- .withInput({
- person() {
- return {
- first: 'Alan',
- last: 'Johnson',
- };
- },
- })
- .toCompileTo('Alan Johnson');
- });
-
- it('with with else', () => {
- expectTemplate(
- '{{#with person}}Person is present{{else}}Person is not present{{/with}}'
- ).toCompileTo('Person is not present');
- });
-
- it('with provides block parameter', () => {
- expectTemplate('{{#with person as |foo|}}{{foo.first}} {{last}}{{/with}}')
- .withInput({
- person: {
- first: 'Alan',
- last: 'Johnson',
- },
- })
- .toCompileTo('Alan Johnson');
- });
-
- it('works when data is disabled', () => {
- expectTemplate('{{#with person as |foo|}}{{foo.first}} {{last}}{{/with}}')
- .withInput({ person: { first: 'Alan', last: 'Johnson' } })
- .withCompileOptions({ data: false })
- .toCompileTo('Alan Johnson');
- });
- });
-
- describe('#each', () => {
- it('each', () => {
- const string = '{{#each goodbyes}}{{text}}! {{/each}}cruel {{world}}!';
-
- expectTemplate(string)
- .withInput({
- goodbyes: [{ text: 'goodbye' }, { text: 'Goodbye' }, { text: 'GOODBYE' }],
- world: 'world',
- })
- .toCompileTo('goodbye! Goodbye! GOODBYE! cruel world!');
-
- expectTemplate(string)
- .withInput({
- goodbyes: [],
- world: 'world',
- })
- .toCompileTo('cruel world!');
- });
-
- it('each without data', () => {
- expectTemplate('{{#each goodbyes}}{{text}}! {{/each}}cruel {{world}}!')
- .withInput({
- goodbyes: [{ text: 'goodbye' }, { text: 'Goodbye' }, { text: 'GOODBYE' }],
- world: 'world',
- })
- .withRuntimeOptions({ data: false })
- .withCompileOptions({ data: false })
- .toCompileTo('goodbye! Goodbye! GOODBYE! cruel world!');
-
- expectTemplate('{{#each .}}{{.}}{{/each}}')
- .withInput({ goodbyes: 'cruel', world: 'world' })
- .withRuntimeOptions({ data: false })
- .withCompileOptions({ data: false })
- .toCompileTo('cruelworld');
- });
-
- it('each without context', () => {
- expectTemplate('{{#each goodbyes}}{{text}}! {{/each}}cruel {{world}}!')
- .withInput(undefined)
- .toCompileTo('cruel !');
- });
-
- it('each with an object and @key', () => {
- const string = '{{#each goodbyes}}{{@key}}. {{text}}! {{/each}}cruel {{world}}!';
-
- function Clazz(this: any) {
- this['<b>#1</b>'] = { text: 'goodbye' };
- this[2] = { text: 'GOODBYE' };
- }
- Clazz.prototype.foo = 'fail';
- const hash = { goodbyes: new (Clazz as any)(), 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
- try {
- expectTemplate(string)
- .withInput(hash)
- .toCompileTo('&lt;b&gt;#1&lt;/b&gt;. goodbye! 2. GOODBYE! cruel world!');
- } catch (e) {
- expectTemplate(string)
- .withInput(hash)
- .toCompileTo('2. GOODBYE! &lt;b&gt;#1&lt;/b&gt;. goodbye! cruel world!');
- }
-
- expectTemplate(string)
- .withInput({
- goodbyes: {},
- world: 'world',
- })
- .toCompileTo('cruel world!');
- });
-
- it('each with @index', () => {
- expectTemplate('{{#each goodbyes}}{{@index}}. {{text}}! {{/each}}cruel {{world}}!')
- .withInput({
- goodbyes: [{ text: 'goodbye' }, { text: 'Goodbye' }, { text: 'GOODBYE' }],
- world: 'world',
- })
- .toCompileTo('0. goodbye! 1. Goodbye! 2. GOODBYE! cruel world!');
- });
-
- it('each with nested @index', () => {
- expectTemplate(
- '{{#each goodbyes}}{{@index}}. {{text}}! {{#each ../goodbyes}}{{@index}} {{/each}}After {{@index}} {{/each}}{{@index}}cruel {{world}}!'
- )
- .withInput({
- goodbyes: [{ text: 'goodbye' }, { text: 'Goodbye' }, { text: 'GOODBYE' }],
- world: 'world',
- })
- .toCompileTo(
- '0. goodbye! 0 1 2 After 0 1. Goodbye! 0 1 2 After 1 2. GOODBYE! 0 1 2 After 2 cruel world!'
- );
- });
-
- it('each with block params', () => {
- expectTemplate(
- '{{#each goodbyes as |value index|}}{{index}}. {{value.text}}! {{#each ../goodbyes as |childValue childIndex|}} {{index}} {{childIndex}}{{/each}} After {{index}} {{/each}}{{index}}cruel {{world}}!'
- )
- .withInput({
- goodbyes: [{ text: 'goodbye' }, { text: 'Goodbye' }],
- world: 'world',
- })
- .toCompileTo('0. goodbye! 0 0 0 1 After 0 1. Goodbye! 1 0 1 1 After 1 cruel world!');
- });
-
- // TODO: This test has been added to the `4.x` branch of the handlebars.js repo along with a code-fix,
- // but a new version of the handlebars package containing this fix has not yet been published to npm.
- //
- // Before enabling this code, a new version of handlebars needs to be released and the corresponding
- // updates needs to be applied to this implementation.
- //
- // See: https://github.com/handlebars-lang/handlebars.js/commit/30dbf0478109ded8f12bb29832135d480c17e367
- it.skip('each with block params and strict compilation', () => {
- expectTemplate('{{#each goodbyes as |value index|}}{{index}}. {{value.text}}!{{/each}}')
- .withCompileOptions({ strict: true })
- .withInput({ goodbyes: [{ text: 'goodbye' }, { text: 'Goodbye' }] })
- .toCompileTo('0. goodbye!1. Goodbye!');
- });
-
- it('each object with @index', () => {
- expectTemplate('{{#each goodbyes}}{{@index}}. {{text}}! {{/each}}cruel {{world}}!')
- .withInput({
- goodbyes: {
- a: { text: 'goodbye' },
- b: { text: 'Goodbye' },
- c: { text: 'GOODBYE' },
- },
- world: 'world',
- })
- .toCompileTo('0. goodbye! 1. Goodbye! 2. GOODBYE! cruel world!');
- });
-
- it('each with @first', () => {
- expectTemplate('{{#each goodbyes}}{{#if @first}}{{text}}! {{/if}}{{/each}}cruel {{world}}!')
- .withInput({
- goodbyes: [{ text: 'goodbye' }, { text: 'Goodbye' }, { text: 'GOODBYE' }],
- world: 'world',
- })
- .toCompileTo('goodbye! cruel world!');
- });
-
- it('each with nested @first', () => {
- expectTemplate(
- '{{#each goodbyes}}({{#if @first}}{{text}}! {{/if}}{{#each ../goodbyes}}{{#if @first}}{{text}}!{{/if}}{{/each}}{{#if @first}} {{text}}!{{/if}}) {{/each}}cruel {{world}}!'
- )
- .withInput({
- goodbyes: [{ text: 'goodbye' }, { text: 'Goodbye' }, { text: 'GOODBYE' }],
- world: 'world',
- })
- .toCompileTo('(goodbye! goodbye! goodbye!) (goodbye!) (goodbye!) cruel world!');
- });
-
- it('each object with @first', () => {
- expectTemplate('{{#each goodbyes}}{{#if @first}}{{text}}! {{/if}}{{/each}}cruel {{world}}!')
- .withInput({
- goodbyes: { foo: { text: 'goodbye' }, bar: { text: 'Goodbye' } },
- world: 'world',
- })
- .toCompileTo('goodbye! cruel world!');
- });
-
- it('each with @last', () => {
- expectTemplate('{{#each goodbyes}}{{#if @last}}{{text}}! {{/if}}{{/each}}cruel {{world}}!')
- .withInput({
- goodbyes: [{ text: 'goodbye' }, { text: 'Goodbye' }, { text: 'GOODBYE' }],
- world: 'world',
- })
- .toCompileTo('GOODBYE! cruel world!');
- });
-
- it('each object with @last', () => {
- expectTemplate('{{#each goodbyes}}{{#if @last}}{{text}}! {{/if}}{{/each}}cruel {{world}}!')
- .withInput({
- goodbyes: { foo: { text: 'goodbye' }, bar: { text: 'Goodbye' } },
- world: 'world',
- })
- .toCompileTo('Goodbye! cruel world!');
- });
-
- it('each with nested @last', () => {
- expectTemplate(
- '{{#each goodbyes}}({{#if @last}}{{text}}! {{/if}}{{#each ../goodbyes}}{{#if @last}}{{text}}!{{/if}}{{/each}}{{#if @last}} {{text}}!{{/if}}) {{/each}}cruel {{world}}!'
- )
- .withInput({
- goodbyes: [{ text: 'goodbye' }, { text: 'Goodbye' }, { text: 'GOODBYE' }],
- world: 'world',
- })
- .toCompileTo('(GOODBYE!) (GOODBYE!) (GOODBYE! GOODBYE! GOODBYE!) cruel world!');
- });
-
- it('each with function argument', () => {
- const string = '{{#each goodbyes}}{{text}}! {{/each}}cruel {{world}}!';
-
- expectTemplate(string)
- .withInput({
- goodbyes() {
- return [{ text: 'goodbye' }, { text: 'Goodbye' }, { text: 'GOODBYE' }];
- },
- world: 'world',
- })
- .toCompileTo('goodbye! Goodbye! GOODBYE! cruel world!');
-
- expectTemplate(string)
- .withInput({
- goodbyes: [],
- world: 'world',
- })
- .toCompileTo('cruel world!');
- });
-
- it('each object when last key is an empty string', () => {
- expectTemplate('{{#each goodbyes}}{{@index}}. {{text}}! {{/each}}cruel {{world}}!')
- .withInput({
- goodbyes: {
- a: { text: 'goodbye' },
- b: { text: 'Goodbye' },
- '': { text: 'GOODBYE' },
- },
- world: 'world',
- })
- .toCompileTo('0. goodbye! 1. Goodbye! 2. GOODBYE! cruel world!');
- });
-
- it('data passed to helpers', () => {
- expectTemplate('{{#each letters}}{{this}}{{detectDataInsideEach}}{{/each}}')
- .withInput({ letters: ['a', 'b', 'c'] })
- .withHelper('detectDataInsideEach', function (options) {
- return options.data && options.data.exclaim;
- })
- .withRuntimeOptions({
- data: {
- exclaim: '!',
- },
- })
- .toCompileTo('a!b!c!');
- });
-
- it('each on implicit context', () => {
- expectTemplate('{{#each}}{{text}}! {{/each}}cruel world!').toThrow(Handlebars.Exception);
- });
-
- it('each on iterable', () => {
- class Iterator {
- private arr: any[];
- private index: number = 0;
-
- constructor(arr: any[]) {
- this.arr = arr;
- }
-
- next() {
- const value = this.arr[this.index];
- const done = this.index === this.arr.length;
- if (!done) {
- this.index++;
- }
- return { value, done };
- }
- }
-
- class Iterable {
- private arr: any[];
-
- constructor(arr: any[]) {
- this.arr = arr;
- }
-
- [Symbol.iterator]() {
- return new Iterator(this.arr);
- }
- }
-
- const string = '{{#each goodbyes}}{{text}}! {{/each}}cruel {{world}}!';
-
- expectTemplate(string)
- .withInput({
- goodbyes: new Iterable([{ text: 'goodbye' }, { text: 'Goodbye' }, { text: 'GOODBYE' }]),
- world: 'world',
- })
- .toCompileTo('goodbye! Goodbye! GOODBYE! cruel world!');
-
- expectTemplate(string)
- .withInput({
- goodbyes: new Iterable([]),
- world: 'world',
- })
- .toCompileTo('cruel world!');
- });
- });
-
- describe('#log', function () {
- /* eslint-disable no-console */
- let $log: typeof console.log;
- let $info: typeof console.info;
- let $error: typeof console.error;
-
- beforeEach(function () {
- $log = console.log;
- $info = console.info;
- $error = console.error;
-
- global.kbnHandlebarsEnv = Handlebars.create();
- });
-
- afterEach(function () {
- console.log = $log;
- console.info = $info;
- console.error = $error;
-
- global.kbnHandlebarsEnv = null;
- });
-
- it('should call logger at default level', function () {
- let levelArg;
- let logArg;
- kbnHandlebarsEnv!.log = function (level, arg) {
- levelArg = level;
- logArg = arg;
- };
-
- expectTemplate('{{log blah}}').withInput({ blah: 'whee' }).toCompileTo('');
- expect(1).toEqual(levelArg);
- expect('whee').toEqual(logArg);
- });
-
- it('should call logger at data level', function () {
- let levelArg;
- let logArg;
- kbnHandlebarsEnv!.log = function (level, arg) {
- levelArg = level;
- logArg = arg;
- };
-
- expectTemplate('{{log blah}}')
- .withInput({ blah: 'whee' })
- .withRuntimeOptions({ data: { level: '03' } })
- .withCompileOptions({ data: true })
- .toCompileTo('');
- expect('03').toEqual(levelArg);
- expect('whee').toEqual(logArg);
- });
-
- it('should output to info', function () {
- let calls = 0;
- const callsExpected = process.env.AST || process.env.EVAL ? 1 : 2;
-
- console.info = function (info) {
- expect('whee').toEqual(info);
- calls++;
- if (calls === callsExpected) {
- console.info = $info;
- console.log = $log;
- }
- };
- console.log = function (log) {
- expect('whee').toEqual(log);
- calls++;
- if (calls === callsExpected) {
- console.info = $info;
- console.log = $log;
- }
- };
-
- expectTemplate('{{log blah}}').withInput({ blah: 'whee' }).toCompileTo('');
- expect(calls).toEqual(callsExpected);
- });
-
- it('should log at data level', function () {
- let calls = 0;
- const callsExpected = process.env.AST || process.env.EVAL ? 1 : 2;
-
- console.error = function (log) {
- expect('whee').toEqual(log);
- calls++;
- if (calls === callsExpected) console.error = $error;
- };
-
- expectTemplate('{{log blah}}')
- .withInput({ blah: 'whee' })
- .withRuntimeOptions({ data: { level: '03' } })
- .withCompileOptions({ data: true })
- .toCompileTo('');
- expect(calls).toEqual(callsExpected);
- });
-
- it('should handle missing logger', function () {
- let calls = 0;
- const callsExpected = process.env.AST || process.env.EVAL ? 1 : 2;
-
- // @ts-expect-error
- console.error = undefined;
- console.log = function (log) {
- expect('whee').toEqual(log);
- calls++;
- if (calls === callsExpected) console.log = $log;
- };
-
- expectTemplate('{{log blah}}')
- .withInput({ blah: 'whee' })
- .withRuntimeOptions({ data: { level: '03' } })
- .withCompileOptions({ data: true })
- .toCompileTo('');
- expect(calls).toEqual(callsExpected);
- });
-
- it('should handle string log levels', function () {
- let calls = 0;
- const callsExpected = process.env.AST || process.env.EVAL ? 1 : 2;
-
- console.error = function (log) {
- expect('whee').toEqual(log);
- calls++;
- };
-
- expectTemplate('{{log blah}}')
- .withInput({ blah: 'whee' })
- .withRuntimeOptions({ data: { level: 'error' } })
- .withCompileOptions({ data: true })
- .toCompileTo('');
- expect(calls).toEqual(callsExpected);
-
- calls = 0;
-
- expectTemplate('{{log blah}}')
- .withInput({ blah: 'whee' })
- .withRuntimeOptions({ data: { level: 'ERROR' } })
- .withCompileOptions({ data: true })
- .toCompileTo('');
- expect(calls).toEqual(callsExpected);
- });
-
- it('should handle hash log levels [1]', function () {
- let calls = 0;
- const callsExpected = process.env.AST || process.env.EVAL ? 1 : 2;
-
- console.error = function (log) {
- expect('whee').toEqual(log);
- calls++;
- };
-
- expectTemplate('{{log blah level="error"}}').withInput({ blah: 'whee' }).toCompileTo('');
- expect(calls).toEqual(callsExpected);
- });
-
- it('should handle hash log levels [2]', function () {
- let called = false;
-
- console.info =
- console.log =
- console.error =
- console.debug =
- function () {
- called = true;
- console.info = console.log = console.error = console.debug = $log;
- };
-
- expectTemplate('{{log blah level="debug"}}').withInput({ blah: 'whee' }).toCompileTo('');
- expect(false).toEqual(called);
- });
-
- it('should pass multiple log arguments', function () {
- let calls = 0;
- const callsExpected = process.env.AST || process.env.EVAL ? 1 : 2;
-
- console.info = console.log = function (log1, log2, log3) {
- expect('whee').toEqual(log1);
- expect('foo').toEqual(log2);
- expect(1).toEqual(log3);
- calls++;
- if (calls === callsExpected) console.log = $log;
- };
-
- expectTemplate('{{log blah "foo" 1}}').withInput({ blah: 'whee' }).toCompileTo('');
- expect(calls).toEqual(callsExpected);
- });
-
- it('should pass zero log arguments', function () {
- let calls = 0;
- const callsExpected = process.env.AST || process.env.EVAL ? 1 : 2;
-
- console.info = console.log = function () {
- expect(arguments.length).toEqual(0);
- calls++;
- if (calls === callsExpected) console.log = $log;
- };
-
- expectTemplate('{{log}}').withInput({ blah: 'whee' }).toCompileTo('');
- expect(calls).toEqual(callsExpected);
- });
- /* eslint-enable no-console */
- });
-
- describe('#lookup', () => {
- it('should lookup arbitrary content', () => {
- expectTemplate('{{#each goodbyes}}{{lookup ../data .}}{{/each}}')
- .withInput({ goodbyes: [0, 1], data: ['foo', 'bar'] })
- .toCompileTo('foobar');
- });
-
- it('should not fail on undefined value', () => {
- expectTemplate('{{#each goodbyes}}{{lookup ../bar .}}{{/each}}')
- .withInput({ goodbyes: [0, 1], data: ['foo', 'bar'] })
- .toCompileTo('');
- });
- });
-});
diff --git a/dev/lib/handlebars/src/spec/index.compiler.test.ts b/dev/lib/handlebars/src/spec/index.compiler.test.ts
deleted file mode 100644
index ef5c55f2..00000000
--- a/dev/lib/handlebars/src/spec/index.compiler.test.ts
+++ /dev/null
@@ -1,86 +0,0 @@
-/*
- * This file is forked from the handlebars project (https://github.com/handlebars-lang/handlebars.js),
- * and may include modifications made by Elasticsearch B.V.
- * Elasticsearch B.V. licenses this file to you under the MIT License.
- * See `packages/kbn-handlebars/LICENSE` for more information.
- */
-
-import Handlebars from '../..';
-import { forEachCompileFunctionName } from '../__jest__/test_bench';
-
-describe('compiler', () => {
- forEachCompileFunctionName((compileName) => {
- const compile = Handlebars[compileName].bind(Handlebars);
-
- describe(`#${compileName}`, () => {
- it('should fail with invalid input', () => {
- expect(function () {
- compile(null);
- }).toThrow(
- `You must pass a string or Handlebars AST to Handlebars.${compileName}. You passed null`
- );
-
- expect(function () {
- compile({});
- }).toThrow(
- `You must pass a string or Handlebars AST to Handlebars.${compileName}. You passed [object Object]`
- );
- });
-
- it('should include the location in the error (row and column)', () => {
- try {
- compile(' \n {{#if}}\n{{/def}}')();
- expect(true).toEqual(false);
- } catch (err) {
- expect(err.message).toEqual("if doesn't match def - 2:5");
- if (Object.getOwnPropertyDescriptor(err, 'column')!.writable) {
- // In Safari 8, the column-property is read-only. This means that even if it is set with defineProperty,
- // its value won't change (https://github.com/jquery/esprima/issues/1290#issuecomment-132455482)
- // Since this was neither working in Handlebars 3 nor in 4.0.5, we only check the column for other browsers.
- expect(err.column).toEqual(5);
- }
- expect(err.lineNumber).toEqual(2);
- }
- });
-
- it('should include the location as enumerable property', () => {
- try {
- compile(' \n {{#if}}\n{{/def}}')();
- expect(true).toEqual(false);
- } catch (err) {
- expect(Object.prototype.propertyIsEnumerable.call(err, 'column')).toEqual(true);
- }
- });
-
- it('can utilize AST instance', () => {
- expect(
- compile({
- type: 'Program',
- body: [{ type: 'ContentStatement', value: 'Hello' }],
- })()
- ).toEqual('Hello');
- });
-
- it('can pass through an empty string', () => {
- expect(compile('')()).toEqual('');
- });
-
- it('should not modify the options.data property(GH-1327)', () => {
- // The `data` property is supposed to be a boolean, but in this test we want to ignore that
- const options = { data: [{ a: 'foo' }, { a: 'bar' }] as unknown as boolean };
- compile('{{#each data}}{{@index}}:{{a}} {{/each}}', options)();
- expect(JSON.stringify(options, null, 2)).toEqual(
- JSON.stringify({ data: [{ a: 'foo' }, { a: 'bar' }] }, null, 2)
- );
- });
-
- it('should not modify the options.knownHelpers property(GH-1327)', () => {
- const options = { knownHelpers: {} };
- compile('{{#each data}}{{@index}}:{{a}} {{/each}}', options)();
- expect(JSON.stringify(options, null, 2)).toEqual(
- JSON.stringify({ knownHelpers: {} }, null, 2)
- );
- });
- });
- });
-});
diff --git a/dev/lib/handlebars/src/spec/index.data.test.ts b/dev/lib/handlebars/src/spec/index.data.test.ts
deleted file mode 100644
index 94d3b51c..00000000
--- a/dev/lib/handlebars/src/spec/index.data.test.ts
+++ /dev/null
@@ -1,269 +0,0 @@
-/*
- * This file is forked from the handlebars project (https://github.com/handlebars-lang/handlebars.js),
- * and may include modifications made by Elasticsearch B.V.
- * Elasticsearch B.V. licenses this file to you under the MIT License.
- * See `packages/kbn-handlebars/LICENSE` for more information.
- */
-
-import Handlebars, { type HelperOptions } from '../..';
-import { expectTemplate } from '../__jest__/test_bench';
-
-describe('data', () => {
- it('passing in data to a compiled function that expects data - works with helpers', () => {
- expectTemplate('{{hello}}')
- .withCompileOptions({ data: true })
- .withHelper('hello', function (this: any, options) {
- return options.data.adjective + ' ' + this.noun;
- })
- .withRuntimeOptions({ data: { adjective: 'happy' } })
- .withInput({ noun: 'cat' })
- .toCompileTo('happy cat');
- });
-
- it('data can be looked up via @foo', () => {
- expectTemplate('{{@hello}}')
- .withRuntimeOptions({ data: { hello: 'hello' } })
- .toCompileTo('hello');
- });
-
- it('deep @foo triggers automatic top-level data', () => {
- global.kbnHandlebarsEnv = Handlebars.create();
- const helpers = Handlebars.createFrame(kbnHandlebarsEnv!.helpers);
-
- helpers.let = function (options: HelperOptions) {
- const frame = Handlebars.createFrame(options.data);
-
- for (const prop in options.hash) {
- if (prop in options.hash) {
- frame[prop] = options.hash[prop];
- }
- }
- return options.fn(this, { data: frame });
- };
-
- expectTemplate(
- '{{#let world="world"}}{{#if foo}}{{#if foo}}Hello {{@world}}{{/if}}{{/if}}{{/let}}'
- )
- .withInput({ foo: true })
- .withHelpers(helpers)
- .toCompileTo('Hello world');
-
- global.kbnHandlebarsEnv = null;
- });
-
- it('parameter data can be looked up via @foo', () => {
- expectTemplate('{{hello @world}}')
- .withRuntimeOptions({ data: { world: 'world' } })
- .withHelper('hello', function (noun) {
- return 'Hello ' + noun;
- })
- .toCompileTo('Hello world');
- });
-
- it('hash values can be looked up via @foo', () => {
- expectTemplate('{{hello noun=@world}}')
- .withRuntimeOptions({ data: { world: 'world' } })
- .withHelper('hello', function (options) {
- return 'Hello ' + options.hash.noun;
- })
- .toCompileTo('Hello world');
- });
-
- it('nested parameter data can be looked up via @foo.bar', () => {
- expectTemplate('{{hello @world.bar}}')
- .withRuntimeOptions({ data: { world: { bar: 'world' } } })
- .withHelper('hello', function (noun) {
- return 'Hello ' + noun;
- })
- .toCompileTo('Hello world');
- });
-
- it('nested parameter data does not fail with @world.bar', () => {
- expectTemplate('{{hello @world.bar}}')
- .withRuntimeOptions({ data: { foo: { bar: 'world' } } })
- .withHelper('hello', function (noun) {
- return 'Hello ' + noun;
- })
- .toCompileTo('Hello undefined');
- });
-
- it('parameter data throws when using complex scope references', () => {
- expectTemplate('{{#goodbyes}}{{text}} cruel {{@foo/../name}}! {{/goodbyes}}').toThrow(Error);
- });
-
- it('data can be functions', () => {
- expectTemplate('{{@hello}}')
- .withRuntimeOptions({
- data: {
- hello() {
- return 'hello';
- },
- },
- })
- .toCompileTo('hello');
- });
-
- it('data can be functions with params', () => {
- expectTemplate('{{@hello "hello"}}')
- .withRuntimeOptions({
- data: {
- hello(arg: any) {
- return arg;
- },
- },
- })
- .toCompileTo('hello');
- });
-
- it('data is inherited downstream', () => {
- expectTemplate(
- '{{#let foo=1 bar=2}}{{#let foo=bar.baz}}{{@bar}}{{@foo}}{{/let}}{{@foo}}{{/let}}'
- )
- .withInput({ bar: { baz: 'hello world' } })
- .withCompileOptions({ data: true })
- .withHelper('let', function (this: any, options) {
- const frame = Handlebars.createFrame(options.data);
- for (const prop in options.hash) {
- if (prop in options.hash) {
- frame[prop] = options.hash[prop];
- }
- }
- return options.fn(this, { data: frame });
- })
- .withRuntimeOptions({ data: {} })
- .toCompileTo('2hello world1');
- });
-
- it('passing in data to a compiled function that expects data - works with helpers in partials', () => {
- expectTemplate('{{>myPartial}}')
- .withCompileOptions({ data: true })
- .withPartial('myPartial', '{{hello}}')
- .withHelper('hello', function (this: any, options: HelperOptions) {
- return options.data.adjective + ' ' + this.noun;
- })
- .withInput({ noun: 'cat' })
- .withRuntimeOptions({ data: { adjective: 'happy' } })
- .toCompileTo('happy cat');
- });
-
- it('passing in data to a compiled function that expects data - works with helpers and parameters', () => {
- expectTemplate('{{hello world}}')
- .withCompileOptions({ data: true })
- .withHelper('hello', function (this: any, noun, options) {
- return options.data.adjective + ' ' + noun + (this.exclaim ? '!' : '');
- })
- .withInput({ exclaim: true, world: 'world' })
- .withRuntimeOptions({ data: { adjective: 'happy' } })
- .toCompileTo('happy world!');
- });
-
- it('passing in data to a compiled function that expects data - works with block helpers', () => {
- expectTemplate('{{#hello}}{{world}}{{/hello}}')
- .withCompileOptions({
- data: true,
- })
- .withHelper('hello', function (this: any, options) {
- return options.fn(this);
- })
- .withHelper('world', function (this: any, options) {
- return options.data.adjective + ' world' + (this.exclaim ? '!' : '');
- })
- .withInput({ exclaim: true })
- .withRuntimeOptions({ data: { adjective: 'happy' } })
- .toCompileTo('happy world!');
- });
-
- it('passing in data to a compiled function that expects data - works with block helpers that use ..', () => {
- expectTemplate('{{#hello}}{{world ../zomg}}{{/hello}}')
- .withCompileOptions({ data: true })
- .withHelper('hello', function (options) {
- return options.fn({ exclaim: '?' });
- })
- .withHelper('world', function (this: any, thing, options) {
- return options.data.adjective + ' ' + thing + (this.exclaim || '');
- })
- .withInput({ exclaim: true, zomg: 'world' })
- .withRuntimeOptions({ data: { adjective: 'happy' } })
- .toCompileTo('happy world?');
- });
-
- it('passing in data to a compiled function that expects data - data is passed to with block helpers where children use ..', () => {
- expectTemplate('{{#hello}}{{world ../zomg}}{{/hello}}')
- .withCompileOptions({ data: true })
- .withHelper('hello', function (options) {
- return options.data.accessData + ' ' + options.fn({ exclaim: '?' });
- })
- .withHelper('world', function (this: any, thing, options) {
- return options.data.adjective + ' ' + thing + (this.exclaim || '');
- })
- .withInput({ exclaim: true, zomg: 'world' })
- .withRuntimeOptions({ data: { adjective: 'happy', accessData: '#win' } })
- .toCompileTo('#win happy world?');
- });
-
- it('you can override inherited data when invoking a helper', () => {
- expectTemplate('{{#hello}}{{world zomg}}{{/hello}}')
- .withCompileOptions({ data: true })
- .withHelper('hello', function (options) {
- return options.fn({ exclaim: '?', zomg: 'world' }, { data: { adjective: 'sad' } });
- })
- .withHelper('world', function (this: any, thing, options) {
- return options.data.adjective + ' ' + thing + (this.exclaim || '');
- })
- .withInput({ exclaim: true, zomg: 'planet' })
- .withRuntimeOptions({ data: { adjective: 'happy' } })
- .toCompileTo('sad world?');
- });
-
- it('you can override inherited data when invoking a helper with depth', () => {
- expectTemplate('{{#hello}}{{world ../zomg}}{{/hello}}')
- .withCompileOptions({ data: true })
- .withHelper('hello', function (options) {
- return options.fn({ exclaim: '?' }, { data: { adjective: 'sad' } });
- })
- .withHelper('world', function (this: any, thing, options) {
- return options.data.adjective + ' ' + thing + (this.exclaim || '');
- })
- .withInput({ exclaim: true, zomg: 'world' })
- .withRuntimeOptions({ data: { adjective: 'happy' } })
- .toCompileTo('sad world?');
- });
-
- describe('@root', () => {
- it('the root context can be looked up via @root', () => {
- expectTemplate('{{@root.foo}}')
- .withInput({ foo: 'hello' })
- .withRuntimeOptions({ data: {} })
- .toCompileTo('hello');
-
- expectTemplate('{{@root.foo}}').withInput({ foo: 'hello' }).toCompileTo('hello');
- });
-
- it('passed root values take priority', () => {
- expectTemplate('{{@root.foo}}')
- .withInput({ foo: 'should not be used' })
- .withRuntimeOptions({ data: { root: { foo: 'hello' } } })
- .toCompileTo('hello');
- });
- });
-
- describe('nesting', () => {
- it('the root context can be looked up via @root', () => {
- expectTemplate(
- '{{#helper}}{{#helper}}{{@./depth}} {{@../depth}} {{@../../depth}}{{/helper}}{{/helper}}'
- )
- .withInput({ foo: 'hello' })
- .withHelper('helper', function (this: any, options) {
- const frame = Handlebars.createFrame(options.data);
- frame.depth = options.data.depth + 1;
- return options.fn(this, { data: frame });
- })
- .withRuntimeOptions({
- data: {
- depth: 0,
- },
- })
- .toCompileTo('2 1 0');
- });
- });
-});
diff --git a/dev/lib/handlebars/src/spec/index.helpers.test.ts b/dev/lib/handlebars/src/spec/index.helpers.test.ts
deleted file mode 100644
index 4cfa39bb..00000000
--- a/dev/lib/handlebars/src/spec/index.helpers.test.ts
+++ /dev/null
@@ -1,958 +0,0 @@
-/*
- * This file is forked from the handlebars project (https://github.com/handlebars-lang/handlebars.js),
- * and may include modifications made by Elasticsearch B.V.
- * Elasticsearch B.V. licenses this file to you under the MIT License.
- * See `packages/kbn-handlebars/LICENSE` for more information.
- */
-
-import Handlebars, { type HelperOptions } from '../..';
-import { expectTemplate } from '../__jest__/test_bench';
-
-beforeEach(() => {
- global.kbnHandlebarsEnv = Handlebars.create();
-});
-
-afterEach(() => {
- global.kbnHandlebarsEnv = null;
-});
-
-describe('helpers', () => {
- it('helper with complex lookup$', () => {
- expectTemplate('{{#goodbyes}}{{{link ../prefix}}}{{/goodbyes}}')
- .withInput({
- prefix: '/root',
- goodbyes: [{ text: 'Goodbye', url: 'goodbye' }],
- })
- .withHelper('link', function (this: any, prefix) {
- return '<a href="' + prefix + '/' + this.url + '">' + this.text + '</a>';
- })
- .toCompileTo('<a href="/root/goodbye">Goodbye</a>');
- });
-
- it('helper for raw block gets raw content', () => {
- expectTemplate('{{{{raw}}}} {{test}} {{{{/raw}}}}')
- .withInput({ test: 'hello' })
- .withHelper('raw', function (options: HelperOptions) {
- return options.fn();
- })
- .toCompileTo(' {{test}} ');
- });
-
- it('helper for raw block gets parameters', () => {
- expectTemplate('{{{{raw 1 2 3}}}} {{test}} {{{{/raw}}}}')
- .withInput({ test: 'hello' })
- .withHelper('raw', function (a, b, c, options: HelperOptions) {
- const ret = options.fn() + a + b + c;
- return ret;
- })
- .toCompileTo(' {{test}} 123');
- });
-
- describe('raw block parsing (with identity helper-function)', () => {
- function runWithIdentityHelper(template: string, expected: string) {
- expectTemplate(template)
- .withHelper('identity', function (options: HelperOptions) {
- return options.fn();
- })
- .toCompileTo(expected);
- }
-
- it('helper for nested raw block gets raw content', () => {
- runWithIdentityHelper(
- '{{{{identity}}}} {{{{b}}}} {{{{/b}}}} {{{{/identity}}}}',
- ' {{{{b}}}} {{{{/b}}}} '
- );
- });
-
- it('helper for nested raw block works with empty content', () => {
- runWithIdentityHelper('{{{{identity}}}}{{{{/identity}}}}', '');
- });
-
- it.skip('helper for nested raw block works if nested raw blocks are broken', () => {
- // This test was introduced in 4.4.4, but it was not the actual problem that lead to the patch release
- // The test is deactivated, because in 3.x this template cases an exception and it also does not work in 4.4.3
- // If anyone can make this template work without breaking everything else, then go for it,
- // but for now, this is just a known bug, that will be documented.
- runWithIdentityHelper(
- '{{{{identity}}}} {{{{a}}}} {{{{ {{{{/ }}}} }}}} {{{{/identity}}}}',
- ' {{{{a}}}} {{{{ {{{{/ }}}} }}}} '
- );
- });
-
- it('helper for nested raw block closes after first matching close', () => {
- runWithIdentityHelper(
- '{{{{identity}}}}abc{{{{/identity}}}} {{{{identity}}}}abc{{{{/identity}}}}',
- 'abc abc'
- );
- });
-
- it('helper for nested raw block throw exception when with missing closing braces', () => {
- const string = '{{{{a}}}} {{{{/a';
- expectTemplate(string).toThrow();
- });
- });
-
- it('helper block with identical context', () => {
- expectTemplate('{{#goodbyes}}{{name}}{{/goodbyes}}')
- .withInput({ name: 'Alan' })
- .withHelper('goodbyes', function (this: any, options: HelperOptions) {
- let out = '';
- const byes = ['Goodbye', 'goodbye', 'GOODBYE'];
- for (let i = 0, j = byes.length; i < j; i++) {
- out += byes[i] + ' ' + options.fn(this) + '! ';
- }
- return out;
- })
- .toCompileTo('Goodbye Alan! goodbye Alan! GOODBYE Alan! ');
- });
-
- it('helper block with complex lookup expression', () => {
- expectTemplate('{{#goodbyes}}{{../name}}{{/goodbyes}}')
- .withInput({ name: 'Alan' })
- .withHelper('goodbyes', function (options: HelperOptions) {
- let out = '';
- const byes = ['Goodbye', 'goodbye', 'GOODBYE'];
- for (let i = 0, j = byes.length; i < j; i++) {
- out += byes[i] + ' ' + options.fn({}) + '! ';
- }
- return out;
- })
- .toCompileTo('Goodbye Alan! goodbye Alan! GOODBYE Alan! ');
- });
-
- it('helper with complex lookup and nested template', () => {
- expectTemplate('{{#goodbyes}}{{#link ../prefix}}{{text}}{{/link}}{{/goodbyes}}')
- .withInput({
- prefix: '/root',
- goodbyes: [{ text: 'Goodbye', url: 'goodbye' }],
- })
- .withHelper('link', function (this: any, prefix, options: HelperOptions) {
- return '<a href="' + prefix + '/' + this.url + '">' + options.fn(this) + '</a>';
- })
- .toCompileTo('<a href="/root/goodbye">Goodbye</a>');
- });
-
- it('helper with complex lookup and nested template in VM+Compiler', () => {
- expectTemplate('{{#goodbyes}}{{#link ../prefix}}{{text}}{{/link}}{{/goodbyes}}')
- .withInput({
- prefix: '/root',
- goodbyes: [{ text: 'Goodbye', url: 'goodbye' }],
- })
- .withHelper('link', function (this: any, prefix, options: HelperOptions) {
- return '<a href="' + prefix + '/' + this.url + '">' + options.fn(this) + '</a>';
- })
- .toCompileTo('<a href="/root/goodbye">Goodbye</a>');
- });
-
- it('helper returning undefined value', () => {
- expectTemplate(' {{nothere}}')
- .withHelpers({
- nothere() {},
- })
- .toCompileTo(' ');
-
- expectTemplate(' {{#nothere}}{{/nothere}}')
- .withHelpers({
- nothere() {},
- })
- .toCompileTo(' ');
- });
-
- it('block helper', () => {
- expectTemplate('{{#goodbyes}}{{text}}! {{/goodbyes}}cruel {{world}}!')
- .withInput({ world: 'world' })
- .withHelper('goodbyes', function (options: HelperOptions) {
- return options.fn({ text: 'GOODBYE' });
- })
- .toCompileTo('GOODBYE! cruel world!');
- });
-
- it('block helper staying in the same context', () => {
- expectTemplate('{{#form}}<p>{{name}}</p>{{/form}}')
- .withInput({ name: 'Yehuda' })
- .withHelper('form', function (this: any, options: HelperOptions) {
- return '<form>' + options.fn(this) + '</form>';
- })
- .toCompileTo('<form><p>Yehuda</p></form>');
- });
-
- it('block helper should have context in this', () => {
- function link(this: any, options: HelperOptions) {
- return '<a href="/people/' + this.id + '">' + options.fn(this) + '</a>';
- }
-
- expectTemplate('<ul>{{#people}}<li>{{#link}}{{name}}{{/link}}</li>{{/people}}</ul>')
- .withInput({
- people: [
- { name: 'Alan', id: 1 },
- { name: 'Yehuda', id: 2 },
- ],
- })
- .withHelper('link', link)
- .toCompileTo(
- '<ul><li><a href="/people/1">Alan</a></li><li><a href="/people/2">Yehuda</a></li></ul>'
- );
- });
-
- it('block helper for undefined value', () => {
- expectTemplate("{{#empty}}shouldn't render{{/empty}}").toCompileTo('');
- });
-
- it('block helper passing a new context', () => {
- expectTemplate('{{#form yehuda}}<p>{{name}}</p>{{/form}}')
- .withInput({ yehuda: { name: 'Yehuda' } })
- .withHelper('form', function (context, options: HelperOptions) {
- return '<form>' + options.fn(context) + '</form>';
- })
- .toCompileTo('<form><p>Yehuda</p></form>');
- });
-
- it('block helper passing a complex path context', () => {
- expectTemplate('{{#form yehuda/cat}}<p>{{name}}</p>{{/form}}')
- .withInput({ yehuda: { name: 'Yehuda', cat: { name: 'Harold' } } })
- .withHelper('form', function (context, options: HelperOptions) {
- return '<form>' + options.fn(context) + '</form>';
- })
- .toCompileTo('<form><p>Harold</p></form>');
- });
-
- it('nested block helpers', () => {
- expectTemplate('{{#form yehuda}}<p>{{name}}</p>{{#link}}Hello{{/link}}{{/form}}')
- .withInput({
- yehuda: { name: 'Yehuda' },
- })
- .withHelper('link', function (this: any, options: HelperOptions) {
- return '<a href="' + this.name + '">' + options.fn(this) + '</a>';
- })
- .withHelper('form', function (context, options: HelperOptions) {
- return '<form>' + options.fn(context) + '</form>';
- })
- .toCompileTo('<form><p>Yehuda</p><a href="Yehuda">Hello</a></form>');
- });
-
- it('block helper inverted sections', () => {
- const string = "{{#list people}}{{name}}{{^}}<em>Nobody's here</em>{{/list}}";
- function list(this: any, context: any, options: HelperOptions) {
- if (context.length > 0) {
- let out = '<ul>';
- for (let i = 0, j = context.length; i < j; i++) {
- out += '<li>';
- out += options.fn(context[i]);
- out += '</li>';
- }
- out += '</ul>';
- return out;
- } else {
- return '<p>' + options.inverse(this) + '</p>';
- }
- }
-
- // the meaning here may be kind of hard to catch, but list.not is always called,
- // so we should see the output of both
- expectTemplate(string)
- .withInput({ people: [{ name: 'Alan' }, { name: 'Yehuda' }] })
- .withHelpers({ list })
- .toCompileTo('<ul><li>Alan</li><li>Yehuda</li></ul>');
-
- expectTemplate(string)
- .withInput({ people: [] })
- .withHelpers({ list })
- .toCompileTo("<p><em>Nobody's here</em></p>");
-
- expectTemplate('{{#list people}}Hello{{^}}{{message}}{{/list}}')
- .withInput({
- people: [],
- message: "Nobody's here",
- })
- .withHelpers({ list })
- .toCompileTo('<p>Nobody&#x27;s here</p>');
- });
-
- it('pathed lambas with parameters', () => {
- const hash = {
- helper: () => 'winning',
- };
- // @ts-expect-error
- hash.hash = hash;
-
- const helpers = {
- './helper': () => 'fail',
- };
-
- expectTemplate('{{./helper 1}}').withInput(hash).withHelpers(helpers).toCompileTo('winning');
- expectTemplate('{{hash/helper 1}}').withInput(hash).withHelpers(helpers).toCompileTo('winning');
- });
-
- describe('helpers hash', () => {
- it('providing a helpers hash', () => {
- expectTemplate('Goodbye {{cruel}} {{world}}!')
- .withInput({ cruel: 'cruel' })
- .withHelpers({
- world() {
- return 'world';
- },
- })
- .toCompileTo('Goodbye cruel world!');
-
- expectTemplate('Goodbye {{#iter}}{{cruel}} {{world}}{{/iter}}!')
- .withInput({ iter: [{ cruel: 'cruel' }] })
- .withHelpers({
- world() {
- return 'world';
- },
- })
- .toCompileTo('Goodbye cruel world!');
- });
-
- it('in cases of conflict, helpers win', () => {
- expectTemplate('{{{lookup}}}')
- .withInput({ lookup: 'Explicit' })
- .withHelpers({
- lookup() {
- return 'helpers';
- },
- })
- .toCompileTo('helpers');
-
- expectTemplate('{{lookup}}')
- .withInput({ lookup: 'Explicit' })
- .withHelpers({
- lookup() {
- return 'helpers';
- },
- })
- .toCompileTo('helpers');
- });
-
- it('the helpers hash is available is nested contexts', () => {
- expectTemplate('{{#outer}}{{#inner}}{{helper}}{{/inner}}{{/outer}}')
- .withInput({ outer: { inner: { unused: [] } } })
- .withHelpers({
- helper() {
- return 'helper';
- },
- })
- .toCompileTo('helper');
- });
-
- it('the helper hash should augment the global hash', () => {
- kbnHandlebarsEnv!.registerHelper('test_helper', function () {
- return 'found it!';
- });
-
- expectTemplate('{{test_helper}} {{#if cruel}}Goodbye {{cruel}} {{world}}!{{/if}}')
- .withInput({ cruel: 'cruel' })
- .withHelpers({
- world() {
- return 'world!';
- },
- })
- .toCompileTo('found it! Goodbye cruel world!!');
- });
- });
-
- describe('registration', () => {
- it('unregisters', () => {
- deleteAllKeys(kbnHandlebarsEnv!.helpers);
-
- kbnHandlebarsEnv!.registerHelper('foo', function () {
- return 'fail';
- });
- expect(kbnHandlebarsEnv!.helpers.foo).toBeDefined();
- kbnHandlebarsEnv!.unregisterHelper('foo');
- expect(kbnHandlebarsEnv!.helpers.foo).toBeUndefined();
- });
-
- it('allows multiple globals', () => {
- const ifHelper = kbnHandlebarsEnv!.helpers.if;
- deleteAllKeys(kbnHandlebarsEnv!.helpers);
-
- kbnHandlebarsEnv!.registerHelper({
- if: ifHelper,
- world() {
- return 'world!';
- },
- testHelper() {
- return 'found it!';
- },
- });
-
- expectTemplate('{{testHelper}} {{#if cruel}}Goodbye {{cruel}} {{world}}!{{/if}}')
- .withInput({ cruel: 'cruel' })
- .toCompileTo('found it! Goodbye cruel world!!');
- });
-
- it('fails with multiple and args', () => {
- expect(() => {
- kbnHandlebarsEnv!.registerHelper(
- // @ts-expect-error TypeScript is complaining about the invalid input just as the thrown error
- {
- world() {
- return 'world!';
- },
- testHelper() {
- return 'found it!';
- },
- },
- {}
- );
- }).toThrow('Arg not supported with multiple helpers');
- });
- });
-
- it('decimal number literals work', () => {
- expectTemplate('Message: {{hello -1.2 1.2}}')
- .withHelper('hello', function (times, times2) {
- if (typeof times !== 'number') {
- times = 'NaN';
- }
- if (typeof times2 !== 'number') {
- times2 = 'NaN';
- }
- return 'Hello ' + times + ' ' + times2 + ' times';
- })
- .toCompileTo('Message: Hello -1.2 1.2 times');
- });
-
- it('negative number literals work', () => {
- expectTemplate('Message: {{hello -12}}')
- .withHelper('hello', function (times) {
- if (typeof times !== 'number') {
- times = 'NaN';
- }
- return 'Hello ' + times + ' times';
- })
- .toCompileTo('Message: Hello -12 times');
- });
-
- describe('String literal parameters', () => {
- it('simple literals work', () => {
- expectTemplate('Message: {{hello "world" 12 true false}}')
- .withHelper('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;
- })
- .toCompileTo('Message: Hello world 12 times: true false');
- });
-
- it('using a quote in the middle of a parameter raises an error', () => {
- expectTemplate('Message: {{hello wo"rld"}}').toThrow(Error);
- });
-
- it('escaping a String is possible', () => {
- expectTemplate('Message: {{{hello "\\"world\\""}}}')
- .withHelper('hello', function (param) {
- return 'Hello ' + param;
- })
- .toCompileTo('Message: Hello "world"');
- });
-
- it("it works with ' marks", () => {
- expectTemplate('Message: {{{hello "Alan\'s world"}}}')
- .withHelper('hello', function (param) {
- return 'Hello ' + param;
- })
- .toCompileTo("Message: Hello Alan's world");
- });
- });
-
- describe('multiple parameters', () => {
- it('simple multi-params work', () => {
- expectTemplate('Message: {{goodbye cruel world}}')
- .withInput({ cruel: 'cruel', world: 'world' })
- .withHelper('goodbye', function (cruel, world) {
- return 'Goodbye ' + cruel + ' ' + world;
- })
- .toCompileTo('Message: Goodbye cruel world');
- });
-
- it('block multi-params work', () => {
- expectTemplate('Message: {{#goodbye cruel world}}{{greeting}} {{adj}} {{noun}}{{/goodbye}}')
- .withInput({ cruel: 'cruel', world: 'world' })
- .withHelper('goodbye', function (cruel, world, options: HelperOptions) {
- return options.fn({ greeting: 'Goodbye', adj: cruel, noun: world });
- })
- .toCompileTo('Message: Goodbye cruel world');
- });
- });
-
- describe('hash', () => {
- it('helpers can take an optional hash', () => {
- expectTemplate('{{goodbye cruel="CRUEL" world="WORLD" times=12}}')
- .withHelper('goodbye', function (options: HelperOptions) {
- return (
- 'GOODBYE ' +
- options.hash.cruel +
- ' ' +
- options.hash.world +
- ' ' +
- options.hash.times +
- ' TIMES'
- );
- })
- .toCompileTo('GOODBYE CRUEL WORLD 12 TIMES');
- });
-
- it('helpers can take an optional hash with booleans', () => {
- function goodbye(options: HelperOptions) {
- if (options.hash.print === true) {
- return 'GOODBYE ' + options.hash.cruel + ' ' + options.hash.world;
- } else if (options.hash.print === false) {
- return 'NOT PRINTING';
- } else {
- return 'THIS SHOULD NOT HAPPEN';
- }
- }
-
- expectTemplate('{{goodbye cruel="CRUEL" world="WORLD" print=true}}')
- .withHelper('goodbye', goodbye)
- .toCompileTo('GOODBYE CRUEL WORLD');
-
- expectTemplate('{{goodbye cruel="CRUEL" world="WORLD" print=false}}')
- .withHelper('goodbye', goodbye)
- .toCompileTo('NOT PRINTING');
- });
-
- it('block helpers can take an optional hash', () => {
- expectTemplate('{{#goodbye cruel="CRUEL" times=12}}world{{/goodbye}}')
- .withHelper('goodbye', function (this: any, options: HelperOptions) {
- return (
- 'GOODBYE ' +
- options.hash.cruel +
- ' ' +
- options.fn(this) +
- ' ' +
- options.hash.times +
- ' TIMES'
- );
- })
- .toCompileTo('GOODBYE CRUEL world 12 TIMES');
- });
-
- it('block helpers can take an optional hash with single quoted stings', () => {
- expectTemplate('{{#goodbye cruel="CRUEL" times=12}}world{{/goodbye}}')
- .withHelper('goodbye', function (this: any, options: HelperOptions) {
- return (
- 'GOODBYE ' +
- options.hash.cruel +
- ' ' +
- options.fn(this) +
- ' ' +
- options.hash.times +
- ' TIMES'
- );
- })
- .toCompileTo('GOODBYE CRUEL world 12 TIMES');
- });
-
- it('block helpers can take an optional hash with booleans', () => {
- function goodbye(this: any, options: HelperOptions) {
- if (options.hash.print === true) {
- return 'GOODBYE ' + options.hash.cruel + ' ' + options.fn(this);
- } else if (options.hash.print === false) {
- return 'NOT PRINTING';
- } else {
- return 'THIS SHOULD NOT HAPPEN';
- }
- }
-
- expectTemplate('{{#goodbye cruel="CRUEL" print=true}}world{{/goodbye}}')
- .withHelper('goodbye', goodbye)
- .toCompileTo('GOODBYE CRUEL world');
-
- expectTemplate('{{#goodbye cruel="CRUEL" print=false}}world{{/goodbye}}')
- .withHelper('goodbye', goodbye)
- .toCompileTo('NOT PRINTING');
- });
- });
-
- describe('helperMissing', () => {
- it('if a context is not found, helperMissing is used', () => {
- expectTemplate('{{hello}} {{link_to world}}').toThrow(/Missing helper: "link_to"/);
- });
-
- it('if a context is not found, custom helperMissing is used', () => {
- expectTemplate('{{hello}} {{link_to world}}')
- .withInput({ hello: 'Hello', world: 'world' })
- .withHelper('helperMissing', function (mesg, options: HelperOptions) {
- if (options.name === 'link_to') {
- return new Handlebars.SafeString('<a>' + mesg + '</a>');
- }
- })
- .toCompileTo('Hello <a>world</a>');
- });
-
- it('if a value is not found, custom helperMissing is used', () => {
- expectTemplate('{{hello}} {{link_to}}')
- .withInput({ hello: 'Hello', world: 'world' })
- .withHelper('helperMissing', function (options: HelperOptions) {
- if (options.name === 'link_to') {
- return new Handlebars.SafeString('<a>winning</a>');
- }
- })
- .toCompileTo('Hello <a>winning</a>');
- });
- });
-
- describe('knownHelpers', () => {
- it('Known helper should render helper', () => {
- expectTemplate('{{hello}}')
- .withCompileOptions({
- knownHelpers: { hello: true },
- })
- .withHelper('hello', function () {
- return 'foo';
- })
- .toCompileTo('foo');
- });
-
- it('Unknown helper in knownHelpers only mode should be passed as undefined', () => {
- expectTemplate('{{typeof hello}}')
- .withCompileOptions({
- knownHelpers: { typeof: true },
- knownHelpersOnly: true,
- })
- .withHelper('typeof', function (arg) {
- return typeof arg;
- })
- .withHelper('hello', function () {
- return 'foo';
- })
- .toCompileTo('undefined');
- });
-
- it('Builtin helpers available in knownHelpers only mode', () => {
- expectTemplate('{{#unless foo}}bar{{/unless}}')
- .withCompileOptions({
- knownHelpersOnly: true,
- })
- .toCompileTo('bar');
- });
-
- it('Field lookup works in knownHelpers only mode', () => {
- expectTemplate('{{foo}}')
- .withCompileOptions({
- knownHelpersOnly: true,
- })
- .withInput({ foo: 'bar' })
- .toCompileTo('bar');
- });
-
- it('Conditional blocks work in knownHelpers only mode', () => {
- expectTemplate('{{#foo}}bar{{/foo}}')
- .withCompileOptions({
- knownHelpersOnly: true,
- })
- .withInput({ foo: 'baz' })
- .toCompileTo('bar');
- });
-
- it('Invert blocks work in knownHelpers only mode', () => {
- expectTemplate('{{^foo}}bar{{/foo}}')
- .withCompileOptions({
- knownHelpersOnly: true,
- })
- .withInput({ foo: false })
- .toCompileTo('bar');
- });
-
- it('Functions are bound to the context in knownHelpers only mode', () => {
- expectTemplate('{{foo}}')
- .withCompileOptions({
- knownHelpersOnly: true,
- })
- .withInput({
- foo() {
- return this.bar;
- },
- bar: 'bar',
- })
- .toCompileTo('bar');
- });
-
- it('Unknown helper call in knownHelpers only mode should throw', () => {
- expectTemplate('{{typeof hello}}')
- .withCompileOptions({ knownHelpersOnly: true })
- .toThrow(Error);
- });
- });
-
- describe('blockHelperMissing', () => {
- it('lambdas are resolved by blockHelperMissing, not handlebars proper', () => {
- expectTemplate('{{#truthy}}yep{{/truthy}}')
- .withInput({
- truthy() {
- return true;
- },
- })
- .toCompileTo('yep');
- });
-
- it('lambdas resolved by blockHelperMissing are bound to the context', () => {
- expectTemplate('{{#truthy}}yep{{/truthy}}')
- .withInput({
- truthy() {
- return this.truthiness();
- },
- truthiness() {
- return false;
- },
- })
- .toCompileTo('');
- });
- });
-
- describe('name field', () => {
- const helpers = {
- blockHelperMissing(...args: any[]) {
- return 'missing: ' + args[args.length - 1].name;
- },
- helperMissing(...args: any[]) {
- return 'helper missing: ' + args[args.length - 1].name;
- },
- helper(...args: any[]) {
- return 'ran: ' + args[args.length - 1].name;
- },
- };
-
- it('should include in ambiguous mustache calls', () => {
- expectTemplate('{{helper}}').withHelpers(helpers).toCompileTo('ran: helper');
- });
-
- it('should include in helper mustache calls', () => {
- expectTemplate('{{helper 1}}').withHelpers(helpers).toCompileTo('ran: helper');
- });
-
- it('should include in ambiguous block calls', () => {
- expectTemplate('{{#helper}}{{/helper}}').withHelpers(helpers).toCompileTo('ran: helper');
- });
-
- it('should include in simple block calls', () => {
- expectTemplate('{{#./helper}}{{/./helper}}')
- .withHelpers(helpers)
- .toCompileTo('missing: ./helper');
- });
-
- it('should include in helper block calls', () => {
- expectTemplate('{{#helper 1}}{{/helper}}').withHelpers(helpers).toCompileTo('ran: helper');
- });
-
- it('should include in known helper calls', () => {
- expectTemplate('{{helper}}')
- .withCompileOptions({
- knownHelpers: { helper: true },
- knownHelpersOnly: true,
- })
- .withHelpers(helpers)
- .toCompileTo('ran: helper');
- });
-
- it('should include full id', () => {
- expectTemplate('{{#foo.helper}}{{/foo.helper}}')
- .withInput({ foo: {} })
- .withHelpers(helpers)
- .toCompileTo('missing: foo.helper');
- });
-
- it('should include full id if a hash is passed', () => {
- expectTemplate('{{#foo.helper bar=baz}}{{/foo.helper}}')
- .withInput({ foo: {} })
- .withHelpers(helpers)
- .toCompileTo('helper missing: foo.helper');
- });
- });
-
- describe('name conflicts', () => {
- it('helpers take precedence over same-named context properties', () => {
- expectTemplate('{{goodbye}} {{cruel world}}')
- .withHelper('goodbye', function (this: any) {
- return this.goodbye.toUpperCase();
- })
- .withHelper('cruel', function (world) {
- return 'cruel ' + world.toUpperCase();
- })
- .withInput({
- goodbye: 'goodbye',
- world: 'world',
- })
- .toCompileTo('GOODBYE cruel WORLD');
- });
-
- it('helpers take precedence over same-named context properties$', () => {
- expectTemplate('{{#goodbye}} {{cruel world}}{{/goodbye}}')
- .withHelper('goodbye', function (this: any, options: HelperOptions) {
- return this.goodbye.toUpperCase() + options.fn(this);
- })
- .withHelper('cruel', function (world) {
- return 'cruel ' + world.toUpperCase();
- })
- .withInput({
- goodbye: 'goodbye',
- world: 'world',
- })
- .toCompileTo('GOODBYE cruel WORLD');
- });
-
- it('Scoped names take precedence over helpers', () => {
- expectTemplate('{{this.goodbye}} {{cruel world}} {{cruel this.goodbye}}')
- .withHelper('goodbye', function (this: any) {
- return this.goodbye.toUpperCase();
- })
- .withHelper('cruel', function (world) {
- return 'cruel ' + world.toUpperCase();
- })
- .withInput({
- goodbye: 'goodbye',
- world: 'world',
- })
- .toCompileTo('goodbye cruel WORLD cruel GOODBYE');
- });
-
- it('Scoped names take precedence over block helpers', () => {
- expectTemplate('{{#goodbye}} {{cruel world}}{{/goodbye}} {{this.goodbye}}')
- .withHelper('goodbye', function (this: any, options: HelperOptions) {
- return this.goodbye.toUpperCase() + options.fn(this);
- })
- .withHelper('cruel', function (world) {
- return 'cruel ' + world.toUpperCase();
- })
- .withInput({
- goodbye: 'goodbye',
- world: 'world',
- })
- .toCompileTo('GOODBYE cruel WORLD goodbye');
- });
- });
-
- describe('block params', () => {
- it('should take presedence over context values', () => {
- expectTemplate('{{#goodbyes as |value|}}{{value}}{{/goodbyes}}{{value}}')
- .withInput({ value: 'foo' })
- .withHelper('goodbyes', function (options: HelperOptions) {
- expect(options.fn.blockParams).toEqual(1);
- return options.fn({ value: 'bar' }, { blockParams: [1, 2] });
- })
- .toCompileTo('1foo');
- });
-
- it('should take presedence over helper values', () => {
- expectTemplate('{{#goodbyes as |value|}}{{value}}{{/goodbyes}}{{value}}')
- .withHelper('value', function () {
- return 'foo';
- })
- .withHelper('goodbyes', function (options: HelperOptions) {
- expect(options.fn.blockParams).toEqual(1);
- return options.fn({}, { blockParams: [1, 2] });
- })
- .toCompileTo('1foo');
- });
-
- it('should not take presedence over pathed values', () => {
- expectTemplate('{{#goodbyes as |value|}}{{./value}}{{/goodbyes}}{{value}}')
- .withInput({ value: 'bar' })
- .withHelper('value', function () {
- return 'foo';
- })
- .withHelper('goodbyes', function (this: any, options: HelperOptions) {
- expect(options.fn.blockParams).toEqual(1);
- return options.fn(this, { blockParams: [1, 2] });
- })
- .toCompileTo('barfoo');
- });
-
- it('should take presednece over parent block params', () => {
- let value: number;
- expectTemplate(
- '{{#goodbyes as |value|}}{{#goodbyes}}{{value}}{{#goodbyes as |value|}}{{value}}{{/goodbyes}}{{/goodbyes}}{{/goodbyes}}{{value}}',
- {
- beforeEach() {
- value = 1;
- },
- }
- )
- .withInput({ value: 'foo' })
- .withHelper('goodbyes', function (options: HelperOptions) {
- return options.fn(
- { value: 'bar' },
- {
- blockParams: options.fn.blockParams === 1 ? [value++, value++] : undefined,
- }
- );
- })
- .toCompileTo('13foo');
- });
-
- it('should allow block params on chained helpers', () => {
- expectTemplate('{{#if bar}}{{else goodbyes as |value|}}{{value}}{{/if}}{{value}}')
- .withInput({ value: 'foo' })
- .withHelper('goodbyes', function (options: HelperOptions) {
- expect(options.fn.blockParams).toEqual(1);
- return options.fn({ value: 'bar' }, { blockParams: [1, 2] });
- })
- .toCompileTo('1foo');
- });
- });
-
- describe('built-in helpers malformed arguments ', () => {
- it('if helper - too few arguments', () => {
- expectTemplate('{{#if}}{{/if}}').toThrow(/#if requires exactly one argument/);
- });
-
- it('if helper - too many arguments, string', () => {
- expectTemplate('{{#if test "string"}}{{/if}}').toThrow(/#if requires exactly one argument/);
- });
-
- it('if helper - too many arguments, undefined', () => {
- expectTemplate('{{#if test undefined}}{{/if}}').toThrow(/#if requires exactly one argument/);
- });
-
- it('if helper - too many arguments, null', () => {
- expectTemplate('{{#if test null}}{{/if}}').toThrow(/#if requires exactly one argument/);
- });
-
- it('unless helper - too few arguments', () => {
- expectTemplate('{{#unless}}{{/unless}}').toThrow(/#unless requires exactly one argument/);
- });
-
- it('unless helper - too many arguments', () => {
- expectTemplate('{{#unless test null}}{{/unless}}').toThrow(
- /#unless requires exactly one argument/
- );
- });
-
- it('with helper - too few arguments', () => {
- expectTemplate('{{#with}}{{/with}}').toThrow(/#with requires exactly one argument/);
- });
-
- it('with helper - too many arguments', () => {
- expectTemplate('{{#with test "string"}}{{/with}}').toThrow(
- /#with requires exactly one argument/
- );
- });
- });
-
- describe('the lookupProperty-option', () => {
- it('should be passed to custom helpers', () => {
- expectTemplate('{{testHelper}}')
- .withHelper('testHelper', function testHelper(this: any, options: HelperOptions) {
- return options.lookupProperty(this, 'testProperty');
- })
- .withInput({ testProperty: 'abc' })
- .toCompileTo('abc');
- });
- });
-});
-
-function deleteAllKeys(obj: { [key: string]: any }) {
- for (const key of Object.keys(obj)) {
- delete obj[key];
- }
-}
diff --git a/dev/lib/handlebars/src/spec/index.partials.test.ts b/dev/lib/handlebars/src/spec/index.partials.test.ts
deleted file mode 100644
index 65930d06..00000000
--- a/dev/lib/handlebars/src/spec/index.partials.test.ts
+++ /dev/null
@@ -1,591 +0,0 @@
-/*
- * This file is forked from the handlebars project (https://github.com/handlebars-lang/handlebars.js),
- * and may include modifications made by Elasticsearch B.V.
- * Elasticsearch B.V. licenses this file to you under the MIT License.
- * See `packages/kbn-handlebars/LICENSE` for more information.
- */
-
-import Handlebars from '../..';
-import { expectTemplate, forEachCompileFunctionName } from '../__jest__/test_bench';
-
-describe('partials', () => {
- it('basic partials', () => {
- const string = 'Dudes: {{#dudes}}{{> dude}}{{/dudes}}';
- const partial = '{{name}} ({{url}}) ';
- const hash = {
- dudes: [
- { name: 'Yehuda', url: 'http://yehuda' },
- { name: 'Alan', url: 'http://alan' },
- ],
- };
-
- expectTemplate(string)
- .withInput(hash)
- .withPartials({ dude: partial })
- .toCompileTo('Dudes: Yehuda (http://yehuda) Alan (http://alan) ');
-
- expectTemplate(string)
- .withInput(hash)
- .withPartials({ dude: partial })
- .withRuntimeOptions({ data: false })
- .withCompileOptions({ data: false })
- .toCompileTo('Dudes: Yehuda (http://yehuda) Alan (http://alan) ');
- });
-
- it('dynamic partials', () => {
- const string = 'Dudes: {{#dudes}}{{> (partial)}}{{/dudes}}';
- const partial = '{{name}} ({{url}}) ';
- const hash = {
- dudes: [
- { name: 'Yehuda', url: 'http://yehuda' },
- { name: 'Alan', url: 'http://alan' },
- ],
- };
- const helpers = {
- partial: () => 'dude',
- };
-
- expectTemplate(string)
- .withInput(hash)
- .withHelpers(helpers)
- .withPartials({ dude: partial })
- .toCompileTo('Dudes: Yehuda (http://yehuda) Alan (http://alan) ');
-
- expectTemplate(string)
- .withInput(hash)
- .withHelpers(helpers)
- .withPartials({ dude: partial })
- .withRuntimeOptions({ data: false })
- .withCompileOptions({ data: false })
- .toCompileTo('Dudes: Yehuda (http://yehuda) Alan (http://alan) ');
- });
-
- it('failing dynamic partials', () => {
- expectTemplate('Dudes: {{#dudes}}{{> (partial)}}{{/dudes}}')
- .withInput({
- dudes: [
- { name: 'Yehuda', url: 'http://yehuda' },
- { name: 'Alan', url: 'http://alan' },
- ],
- })
- .withHelper('partial', () => 'missing')
- .withPartial('dude', '{{name}} ({{url}}) ')
- .toThrow('The partial missing could not be found'); // TODO: Is there a way we can test that the error is of type `Handlebars.Exception`?
- });
-
- it('partials with context', () => {
- expectTemplate('Dudes: {{>dude dudes}}')
- .withInput({
- dudes: [
- { name: 'Yehuda', url: 'http://yehuda' },
- { name: 'Alan', url: 'http://alan' },
- ],
- })
- .withPartial('dude', '{{#this}}{{name}} ({{url}}) {{/this}}')
- .toCompileTo('Dudes: Yehuda (http://yehuda) Alan (http://alan) ');
- });
-
- it('partials with no context', () => {
- const partial = '{{name}} ({{url}}) ';
- const hash = {
- dudes: [
- { name: 'Yehuda', url: 'http://yehuda' },
- { name: 'Alan', url: 'http://alan' },
- ],
- };
-
- expectTemplate('Dudes: {{#dudes}}{{>dude}}{{/dudes}}')
- .withInput(hash)
- .withPartial('dude', partial)
- .withCompileOptions({ explicitPartialContext: true })
- .toCompileTo('Dudes: () () ');
-
- expectTemplate('Dudes: {{#dudes}}{{>dude name="foo"}}{{/dudes}}')
- .withInput(hash)
- .withPartial('dude', partial)
- .withCompileOptions({ explicitPartialContext: true })
- .toCompileTo('Dudes: foo () foo () ');
- });
-
- it('partials with string context', () => {
- expectTemplate('Dudes: {{>dude "dudes"}}')
- .withPartial('dude', '{{.}}')
- .toCompileTo('Dudes: dudes');
- });
-
- it('partials with undefined context', () => {
- expectTemplate('Dudes: {{>dude dudes}}')
- .withPartial('dude', '{{foo}} Empty')
- .toCompileTo('Dudes: Empty');
- });
-
- it('partials with duplicate parameters', () => {
- expectTemplate('Dudes: {{>dude dudes foo bar=baz}}').toThrow(
- 'Unsupported number of partial arguments: 2 - 1:7'
- );
- });
-
- it('partials with parameters', () => {
- expectTemplate('Dudes: {{#dudes}}{{> dude others=..}}{{/dudes}}')
- .withInput({
- foo: 'bar',
- dudes: [
- { name: 'Yehuda', url: 'http://yehuda' },
- { name: 'Alan', url: 'http://alan' },
- ],
- })
- .withPartial('dude', '{{others.foo}}{{name}} ({{url}}) ')
- .toCompileTo('Dudes: barYehuda (http://yehuda) barAlan (http://alan) ');
- });
-
- it('partial in a partial', () => {
- expectTemplate('Dudes: {{#dudes}}{{>dude}}{{/dudes}}')
- .withInput({
- dudes: [
- { name: 'Yehuda', url: 'http://yehuda' },
- { name: 'Alan', url: 'http://alan' },
- ],
- })
- .withPartials({
- dude: '{{name}} {{> url}} ',
- url: '<a href="{{url}}">{{url}}</a>',
- })
- .toCompileTo(
- 'Dudes: Yehuda <a href="http://yehuda">http://yehuda</a> Alan <a href="http://alan">http://alan</a> '
- );
- });
-
- it('rendering undefined partial throws an exception', () => {
- expectTemplate('{{> whatever}}').toThrow('The partial whatever could not be found');
- });
-
- it('registering undefined partial throws an exception', () => {
- global.kbnHandlebarsEnv = Handlebars.create();
-
- expect(() => {
- kbnHandlebarsEnv!.registerPartial('undefined_test', undefined as any);
- }).toThrow('Attempting to register a partial called "undefined_test" as undefined');
-
- global.kbnHandlebarsEnv = null;
- });
-
- it('rendering template partial in vm mode throws an exception', () => {
- expectTemplate('{{> whatever}}').toThrow('The partial whatever could not be found');
- });
-
- it('rendering function partial in vm mode', () => {
- function partial(context: any) {
- return context.name + ' (' + context.url + ') ';
- }
- expectTemplate('Dudes: {{#dudes}}{{> dude}}{{/dudes}}')
- .withInput({
- dudes: [
- { name: 'Yehuda', url: 'http://yehuda' },
- { name: 'Alan', url: 'http://alan' },
- ],
- })
- .withPartial('dude', partial)
- .toCompileTo('Dudes: Yehuda (http://yehuda) Alan (http://alan) ');
- });
-
- it('GH-14: a partial preceding a selector', () => {
- expectTemplate('Dudes: {{>dude}} {{anotherDude}}')
- .withInput({ name: 'Jeepers', anotherDude: 'Creepers' })
- .withPartial('dude', '{{name}}')
- .toCompileTo('Dudes: Jeepers Creepers');
- });
-
- it('Partials with slash paths', () => {
- expectTemplate('Dudes: {{> shared/dude}}')
- .withInput({ name: 'Jeepers', anotherDude: 'Creepers' })
- .withPartial('shared/dude', '{{name}}')
- .toCompileTo('Dudes: Jeepers');
- });
-
- it('Partials with slash and point paths', () => {
- expectTemplate('Dudes: {{> shared/dude.thing}}')
- .withInput({ name: 'Jeepers', anotherDude: 'Creepers' })
- .withPartial('shared/dude.thing', '{{name}}')
- .toCompileTo('Dudes: Jeepers');
- });
-
- it('Global Partials', () => {
- global.kbnHandlebarsEnv = Handlebars.create();
-
- kbnHandlebarsEnv!.registerPartial('globalTest', '{{anotherDude}}');
-
- expectTemplate('Dudes: {{> shared/dude}} {{> globalTest}}')
- .withInput({ name: 'Jeepers', anotherDude: 'Creepers' })
- .withPartial('shared/dude', '{{name}}')
- .toCompileTo('Dudes: Jeepers Creepers');
-
- kbnHandlebarsEnv!.unregisterPartial('globalTest');
- expect(kbnHandlebarsEnv!.partials.globalTest).toBeUndefined();
-
- global.kbnHandlebarsEnv = null;
- });
-
- it('Multiple partial registration', () => {
- global.kbnHandlebarsEnv = Handlebars.create();
-
- kbnHandlebarsEnv!.registerPartial({
- 'shared/dude': '{{name}}',
- globalTest: '{{anotherDude}}',
- });
-
- expectTemplate('Dudes: {{> shared/dude}} {{> globalTest}}')
- .withInput({ name: 'Jeepers', anotherDude: 'Creepers' })
- .withPartial('notused', 'notused') // trick the test bench into running with partials enabled
- .toCompileTo('Dudes: Jeepers Creepers');
-
- global.kbnHandlebarsEnv = null;
- });
-
- it('Partials with integer path', () => {
- expectTemplate('Dudes: {{> 404}}')
- .withInput({ name: 'Jeepers', anotherDude: 'Creepers' })
- .withPartial(404, '{{name}}')
- .toCompileTo('Dudes: Jeepers');
- });
-
- it('Partials with complex path', () => {
- expectTemplate('Dudes: {{> 404/asdf?.bar}}')
- .withInput({ name: 'Jeepers', anotherDude: 'Creepers' })
- .withPartial('404/asdf?.bar', '{{name}}')
- .toCompileTo('Dudes: Jeepers');
- });
-
- it('Partials with escaped', () => {
- expectTemplate('Dudes: {{> [+404/asdf?.bar]}}')
- .withInput({ name: 'Jeepers', anotherDude: 'Creepers' })
- .withPartial('+404/asdf?.bar', '{{name}}')
- .toCompileTo('Dudes: Jeepers');
- });
-
- it('Partials with string', () => {
- expectTemplate("Dudes: {{> '+404/asdf?.bar'}}")
- .withInput({ name: 'Jeepers', anotherDude: 'Creepers' })
- .withPartial('+404/asdf?.bar', '{{name}}')
- .toCompileTo('Dudes: Jeepers');
- });
-
- it('should handle empty partial', () => {
- expectTemplate('Dudes: {{#dudes}}{{> dude}}{{/dudes}}')
- .withInput({
- dudes: [
- { name: 'Yehuda', url: 'http://yehuda' },
- { name: 'Alan', url: 'http://alan' },
- ],
- })
- .withPartial('dude', '')
- .toCompileTo('Dudes: ');
- });
-
- // Skipping test as this only makes sense when there's no `compile` function (i.e. runtime-only mode).
- // We do not support that mode with `@kbn/handlebars`, so there's no need to test it
- it.skip('throw on missing partial', () => {
- const handlebars = Handlebars.create();
- (handlebars.compile as any) = undefined;
- const template = handlebars.precompile('{{> dude}}');
- const render = handlebars.template(eval('(' + template + ')')); // eslint-disable-line no-eval
- expect(() => {
- render(
- {},
- {
- partials: {
- dude: 'fail',
- },
- }
- );
- }).toThrow(/The partial dude could not be compiled/);
- });
-
- describe('partial blocks', () => {
- it('should render partial block as default', () => {
- expectTemplate('{{#> dude}}success{{/dude}}').toCompileTo('success');
- });
-
- it('should execute default block with proper context', () => {
- expectTemplate('{{#> dude context}}{{value}}{{/dude}}')
- .withInput({ context: { value: 'success' } })
- .toCompileTo('success');
- });
-
- it('should propagate block parameters to default block', () => {
- expectTemplate('{{#with context as |me|}}{{#> dude}}{{me.value}}{{/dude}}{{/with}}')
- .withInput({ context: { value: 'success' } })
- .toCompileTo('success');
- });
-
- it('should not use partial block if partial exists', () => {
- expectTemplate('{{#> dude}}fail{{/dude}}')
- .withPartials({ dude: 'success' })
- .toCompileTo('success');
- });
-
- it('should render block from partial', () => {
- expectTemplate('{{#> dude}}success{{/dude}}')
- .withPartials({ dude: '{{> @partial-block }}' })
- .toCompileTo('success');
- });
-
- it('should be able to render the partial-block twice', () => {
- expectTemplate('{{#> dude}}success{{/dude}}')
- .withPartials({ dude: '{{> @partial-block }} {{> @partial-block }}' })
- .toCompileTo('success success');
- });
-
- it('should render block from partial with context', () => {
- expectTemplate('{{#> dude}}{{value}}{{/dude}}')
- .withInput({ context: { value: 'success' } })
- .withPartials({
- dude: '{{#with context}}{{> @partial-block }}{{/with}}',
- })
- .toCompileTo('success');
- });
-
- it('should be able to access the @data frame from a partial-block', () => {
- expectTemplate('{{#> dude}}in-block: {{@root/value}}{{/dude}}')
- .withInput({ value: 'success' })
- .withPartials({
- dude: '<code>before-block: {{@root/value}} {{> @partial-block }}</code>',
- })
- .toCompileTo('<code>before-block: success in-block: success</code>');
- });
-
- it('should allow the #each-helper to be used along with partial-blocks', () => {
- expectTemplate('<template>{{#> list value}}value = {{.}}{{/list}}</template>')
- .withInput({
- value: ['a', 'b', 'c'],
- })
- .withPartials({
- list: '<list>{{#each .}}<item>{{> @partial-block}}</item>{{/each}}</list>',
- })
- .toCompileTo(
- '<template><list><item>value = a</item><item>value = b</item><item>value = c</item></list></template>'
- );
- });
-
- it('should render block from partial with context (twice)', () => {
- expectTemplate('{{#> dude}}{{value}}{{/dude}}')
- .withInput({ context: { value: 'success' } })
- .withPartials({
- dude: '{{#with context}}{{> @partial-block }} {{> @partial-block }}{{/with}}',
- })
- .toCompileTo('success success');
- });
-
- it('should render block from partial with context [2]', () => {
- expectTemplate('{{#> dude}}{{../context/value}}{{/dude}}')
- .withInput({ context: { value: 'success' } })
- .withPartials({
- dude: '{{#with context}}{{> @partial-block }}{{/with}}',
- })
- .toCompileTo('success');
- });
-
- it('should render block from partial with block params', () => {
- expectTemplate('{{#with context as |me|}}{{#> dude}}{{me.value}}{{/dude}}{{/with}}')
- .withInput({ context: { value: 'success' } })
- .withPartials({ dude: '{{> @partial-block }}' })
- .toCompileTo('success');
- });
-
- it('should render nested partial blocks', () => {
- expectTemplate('<template>{{#> outer}}{{value}}{{/outer}}</template>')
- .withInput({ value: 'success' })
- .withPartials({
- outer:
- '<outer>{{#> nested}}<outer-block>{{> @partial-block}}</outer-block>{{/nested}}</outer>',
- nested: '<nested>{{> @partial-block}}</nested>',
- })
- .toCompileTo(
- '<template><outer><nested><outer-block>success</outer-block></nested></outer></template>'
- );
- });
-
- it('should render nested partial blocks at different nesting levels', () => {
- expectTemplate('<template>{{#> outer}}{{value}}{{/outer}}</template>')
- .withInput({ value: 'success' })
- .withPartials({
- outer:
- '<outer>{{#> nested}}<outer-block>{{> @partial-block}}</outer-block>{{/nested}}{{> @partial-block}}</outer>',
- nested: '<nested>{{> @partial-block}}</nested>',
- })
- .toCompileTo(
- '<template><outer><nested><outer-block>success</outer-block></nested>success</outer></template>'
- );
- });
-
- it('should render nested partial blocks at different nesting levels (twice)', () => {
- expectTemplate('<template>{{#> outer}}{{value}}{{/outer}}</template>')
- .withInput({ value: 'success' })
- .withPartials({
- outer:
- '<outer>{{#> nested}}<outer-block>{{> @partial-block}} {{> @partial-block}}</outer-block>{{/nested}}{{> @partial-block}}+{{> @partial-block}}</outer>',
- nested: '<nested>{{> @partial-block}}</nested>',
- })
- .toCompileTo(
- '<template><outer><nested><outer-block>success success</outer-block></nested>success+success</outer></template>'
- );
- });
-
- it('should render nested partial blocks (twice at each level)', () => {
- expectTemplate('<template>{{#> outer}}{{value}}{{/outer}}</template>')
- .withInput({ value: 'success' })
- .withPartials({
- outer:
- '<outer>{{#> nested}}<outer-block>{{> @partial-block}} {{> @partial-block}}</outer-block>{{/nested}}</outer>',
- nested: '<nested>{{> @partial-block}}{{> @partial-block}}</nested>',
- })
- .toCompileTo(
- '<template><outer>' +
- '<nested><outer-block>success success</outer-block><outer-block>success success</outer-block></nested>' +
- '</outer></template>'
- );
- });
- });
-
- describe('inline partials', () => {
- it('should define inline partials for template', () => {
- expectTemplate('{{#*inline "myPartial"}}success{{/inline}}{{> myPartial}}').toCompileTo(
- 'success'
- );
- });
-
- it('should overwrite multiple partials in the same template', () => {
- expectTemplate(
- '{{#*inline "myPartial"}}fail{{/inline}}{{#*inline "myPartial"}}success{{/inline}}{{> myPartial}}'
- ).toCompileTo('success');
- });
-
- it('should define inline partials for block', () => {
- expectTemplate(
- '{{#with .}}{{#*inline "myPartial"}}success{{/inline}}{{> myPartial}}{{/with}}'
- ).toCompileTo('success');
-
- expectTemplate(
- '{{#with .}}{{#*inline "myPartial"}}success{{/inline}}{{/with}}{{> myPartial}}'
- ).toThrow(/myPartial could not/);
- });
-
- it('should override global partials', () => {
- expectTemplate('{{#*inline "myPartial"}}success{{/inline}}{{> myPartial}}')
- .withPartials({
- myPartial: () => 'fail',
- })
- .toCompileTo('success');
- });
-
- it('should override template partials', () => {
- expectTemplate(
- '{{#*inline "myPartial"}}fail{{/inline}}{{#with .}}{{#*inline "myPartial"}}success{{/inline}}{{> myPartial}}{{/with}}'
- ).toCompileTo('success');
- });
-
- it('should override partials down the entire stack', () => {
- expectTemplate(
- '{{#with .}}{{#*inline "myPartial"}}success{{/inline}}{{#with .}}{{#with .}}{{> myPartial}}{{/with}}{{/with}}{{/with}}'
- ).toCompileTo('success');
- });
-
- it('should define inline partials for partial call', () => {
- expectTemplate('{{#*inline "myPartial"}}success{{/inline}}{{> dude}}')
- .withPartials({ dude: '{{> myPartial }}' })
- .toCompileTo('success');
- });
-
- it('should define inline partials in partial block call', () => {
- expectTemplate('{{#> dude}}{{#*inline "myPartial"}}success{{/inline}}{{/dude}}')
- .withPartials({ dude: '{{> myPartial }}' })
- .toCompileTo('success');
- });
-
- it('should render nested inline partials', () => {
- expectTemplate(
- '{{#*inline "outer"}}{{#>inner}}<outer-block>{{>@partial-block}}</outer-block>{{/inner}}{{/inline}}' +
- '{{#*inline "inner"}}<inner>{{>@partial-block}}</inner>{{/inline}}' +
- '{{#>outer}}{{value}}{{/outer}}'
- )
- .withInput({ value: 'success' })
- .toCompileTo('<inner><outer-block>success</outer-block></inner>');
- });
-
- it('should render nested inline partials with partial-blocks on different nesting levels', () => {
- expectTemplate(
- '{{#*inline "outer"}}{{#>inner}}<outer-block>{{>@partial-block}}</outer-block>{{/inner}}{{>@partial-block}}{{/inline}}' +
- '{{#*inline "inner"}}<inner>{{>@partial-block}}</inner>{{/inline}}' +
- '{{#>outer}}{{value}}{{/outer}}'
- )
- .withInput({ value: 'success' })
- .toCompileTo('<inner><outer-block>success</outer-block></inner>success');
- });
-
- it('should render nested inline partials (twice at each level)', () => {
- expectTemplate(
- '{{#*inline "outer"}}{{#>inner}}<outer-block>{{>@partial-block}} {{>@partial-block}}</outer-block>{{/inner}}{{/inline}}' +
- '{{#*inline "inner"}}<inner>{{>@partial-block}}{{>@partial-block}}</inner>{{/inline}}' +
- '{{#>outer}}{{value}}{{/outer}}'
- )
- .withInput({ value: 'success' })
- .toCompileTo(
- '<inner><outer-block>success success</outer-block><outer-block>success success</outer-block></inner>'
- );
- });
- });
-
- forEachCompileFunctionName((compileName) => {
- it(`should pass compiler flags for ${compileName} function`, () => {
- const env = Handlebars.create();
- env.registerPartial('partial', '{{foo}}');
- const compile = env[compileName].bind(env);
- const template = compile('{{foo}} {{> partial}}', { noEscape: true });
- expect(template({ foo: '<' })).toEqual('< <');
- });
- });
-
- describe('standalone partials', () => {
- it('indented partials', () => {
- expectTemplate('Dudes:\n{{#dudes}}\n {{>dude}}\n{{/dudes}}')
- .withInput({
- dudes: [
- { name: 'Yehuda', url: 'http://yehuda' },
- { name: 'Alan', url: 'http://alan' },
- ],
- })
- .withPartial('dude', '{{name}}\n')
- .toCompileTo('Dudes:\n Yehuda\n Alan\n');
- });
-
- it('nested indented partials', () => {
- expectTemplate('Dudes:\n{{#dudes}}\n {{>dude}}\n{{/dudes}}')
- .withInput({
- dudes: [
- { name: 'Yehuda', url: 'http://yehuda' },
- { name: 'Alan', url: 'http://alan' },
- ],
- })
- .withPartials({
- dude: '{{name}}\n {{> url}}',
- url: '{{url}}!\n',
- })
- .toCompileTo('Dudes:\n Yehuda\n http://yehuda!\n Alan\n http://alan!\n');
- });
-
- it('prevent nested indented partials', () => {
- expectTemplate('Dudes:\n{{#dudes}}\n {{>dude}}\n{{/dudes}}')
- .withInput({
- dudes: [
- { name: 'Yehuda', url: 'http://yehuda' },
- { name: 'Alan', url: 'http://alan' },
- ],
- })
- .withPartials({
- dude: '{{name}}\n {{> url}}',
- url: '{{url}}!\n',
- })
- .withCompileOptions({ preventIndent: true })
- .toCompileTo('Dudes:\n Yehuda\n http://yehuda!\n Alan\n http://alan!\n');
- });
- });
-});
diff --git a/dev/lib/handlebars/src/spec/index.regressions.test.ts b/dev/lib/handlebars/src/spec/index.regressions.test.ts
deleted file mode 100644
index fc2065fe..00000000
--- a/dev/lib/handlebars/src/spec/index.regressions.test.ts
+++ /dev/null
@@ -1,379 +0,0 @@
-/*
- * This file is forked from the handlebars project (https://github.com/handlebars-lang/handlebars.js),
- * and may include modifications made by Elasticsearch B.V.
- * Elasticsearch B.V. licenses this file to you under the MIT License.
- * See `packages/kbn-handlebars/LICENSE` for more information.
- */
-
-import Handlebars, { type HelperOptions } from '../..';
-import { expectTemplate, forEachCompileFunctionName } from '../__jest__/test_bench';
-
-describe('Regressions', () => {
- it('GH-94: Cannot read property of undefined', () => {
- expectTemplate('{{#books}}{{title}}{{author.name}}{{/books}}')
- .withInput({
- books: [
- {
- title: 'The origin of species',
- author: {
- name: 'Charles Darwin',
- },
- },
- {
- title: 'Lazarillo de Tormes',
- },
- ],
- })
- .toCompileTo('The origin of speciesCharles DarwinLazarillo de Tormes');
- });
-
- it("GH-150: Inverted sections print when they shouldn't", () => {
- const string = '{{^set}}not set{{/set}} :: {{#set}}set{{/set}}';
- expectTemplate(string).toCompileTo('not set :: ');
- expectTemplate(string).withInput({ set: undefined }).toCompileTo('not set :: ');
- expectTemplate(string).withInput({ set: false }).toCompileTo('not set :: ');
- expectTemplate(string).withInput({ set: true }).toCompileTo(' :: set');
- });
-
- it('GH-158: Using array index twice, breaks the template', () => {
- expectTemplate('{{arr.[0]}}, {{arr.[1]}}')
- .withInput({ arr: [1, 2] })
- .toCompileTo('1, 2');
- });
-
- it("bug reported by @fat where lambdas weren't being properly resolved", () => {
- const string =
- '<strong>This is a slightly more complicated {{thing}}.</strong>.\n' +
- '{{! Just ignore this business. }}\n' +
- 'Check this out:\n' +
- '{{#hasThings}}\n' +
- '<ul>\n' +
- '{{#things}}\n' +
- '<li class={{className}}>{{word}}</li>\n' +
- '{{/things}}</ul>.\n' +
- '{{/hasThings}}\n' +
- '{{^hasThings}}\n' +
- '\n' +
- '<small>Nothing to check out...</small>\n' +
- '{{/hasThings}}';
-
- const data = {
- thing() {
- return 'blah';
- },
- things: [
- { className: 'one', word: '@fat' },
- { className: 'two', word: '@dhg' },
- { className: 'three', word: '@sayrer' },
- ],
- hasThings() {
- return true;
- },
- };
-
- const output =
- '<strong>This is a slightly more complicated blah.</strong>.\n' +
- 'Check this out:\n' +
- '<ul>\n' +
- '<li class=one>@fat</li>\n' +
- '<li class=two>@dhg</li>\n' +
- '<li class=three>@sayrer</li>\n' +
- '</ul>.\n';
-
- expectTemplate(string).withInput(data).toCompileTo(output);
- });
-
- it('GH-408: Multiple loops fail', () => {
- expectTemplate('{{#.}}{{name}}{{/.}}{{#.}}{{name}}{{/.}}{{#.}}{{name}}{{/.}}')
- .withInput([
- { name: 'John Doe', location: { city: 'Chicago' } },
- { name: 'Jane Doe', location: { city: 'New York' } },
- ])
- .toCompileTo('John DoeJane DoeJohn DoeJane DoeJohn DoeJane Doe');
- });
-
- it('GS-428: Nested if else rendering', () => {
- const succeedingTemplate =
- '{{#inverse}} {{#blk}} Unexpected {{/blk}} {{else}} {{#blk}} Expected {{/blk}} {{/inverse}}';
- const failingTemplate =
- '{{#inverse}} {{#blk}} Unexpected {{/blk}} {{else}} {{#blk}} Expected {{/blk}} {{/inverse}}';
-
- const helpers = {
- blk(block: HelperOptions) {
- return block.fn('');
- },
- inverse(block: HelperOptions) {
- return block.inverse('');
- },
- };
-
- expectTemplate(succeedingTemplate).withHelpers(helpers).toCompileTo(' Expected ');
- expectTemplate(failingTemplate).withHelpers(helpers).toCompileTo(' Expected ');
- });
-
- it('GH-458: Scoped this identifier', () => {
- expectTemplate('{{./foo}}').withInput({ foo: 'bar' }).toCompileTo('bar');
- });
-
- it('GH-375: Unicode line terminators', () => {
- expectTemplate('\u2028').toCompileTo('\u2028');
- });
-
- it('GH-534: Object prototype aliases', () => {
- /* eslint-disable no-extend-native */
- // @ts-expect-error
- Object.prototype[0xd834] = true;
-
- expectTemplate('{{foo}}').withInput({ foo: 'bar' }).toCompileTo('bar');
-
- // @ts-expect-error
- delete Object.prototype[0xd834];
- /* eslint-enable no-extend-native */
- });
-
- it('GH-437: Matching escaping', () => {
- expectTemplate('{{{a}}').toThrow(/Parse error on/);
- expectTemplate('{{a}}}').toThrow(/Parse error on/);
- });
-
- it('GH-676: Using array in escaping mustache fails', () => {
- const data = { arr: [1, 2] };
- expectTemplate('{{arr}}').withInput(data).toCompileTo(data.arr.toString());
- });
-
- it('Mustache man page', () => {
- expectTemplate(
- 'Hello {{name}}. You have just won ${{value}}!{{#in_ca}} Well, ${{taxed_value}}, after taxes.{{/in_ca}}'
- )
- .withInput({
- name: 'Chris',
- value: 10000,
- taxed_value: 10000 - 10000 * 0.4,
- in_ca: true,
- })
- .toCompileTo('Hello Chris. You have just won $10000! Well, $6000, after taxes.');
- });
-
- it('GH-731: zero context rendering', () => {
- expectTemplate('{{#foo}} This is {{bar}} ~ {{/foo}}')
- .withInput({
- foo: 0,
- bar: 'OK',
- })
- .toCompileTo(' This is ~ ');
- });
-
- it('GH-820: zero pathed rendering', () => {
- expectTemplate('{{foo.bar}}').withInput({ foo: 0 }).toCompileTo('');
- });
-
- it('GH-837: undefined values for helpers', () => {
- expectTemplate('{{str bar.baz}}')
- .withHelpers({
- str(value) {
- return value + '';
- },
- })
- .toCompileTo('undefined');
- });
-
- it('GH-926: Depths and de-dupe', () => {
- expectTemplate(
- '{{#if dater}}{{#each data}}{{../name}}{{/each}}{{else}}{{#each notData}}{{../name}}{{/each}}{{/if}}'
- )
- .withInput({
- name: 'foo',
- data: [1],
- notData: [1],
- })
- .toCompileTo('foo');
- });
-
- it('GH-1021: Each empty string key', () => {
- expectTemplate('{{#each data}}Key: {{@key}}\n{{/each}}')
- .withInput({
- data: {
- '': 'foo',
- name: 'Chris',
- value: 10000,
- },
- })
- .toCompileTo('Key: \nKey: name\nKey: value\n');
- });
-
- it('GH-1054: Should handle simple safe string responses', () => {
- expectTemplate('{{#wrap}}{{>partial}}{{/wrap}}')
- .withHelpers({
- wrap(options: HelperOptions) {
- return new Handlebars.SafeString(options.fn());
- },
- })
- .withPartials({
- partial: '{{#wrap}}<partial>{{/wrap}}',
- })
- .toCompileTo('<partial>');
- });
-
- it('GH-1065: Sparse arrays', () => {
- const array = [];
- array[1] = 'foo';
- array[3] = 'bar';
- expectTemplate('{{#each array}}{{@index}}{{.}}{{/each}}')
- .withInput({ array })
- .toCompileTo('1foo3bar');
- });
-
- it('GH-1093: Undefined helper context', () => {
- expectTemplate('{{#each obj}}{{{helper}}}{{.}}{{/each}}')
- .withInput({ obj: { foo: undefined, bar: 'bat' } })
- .withHelpers({
- helper(this: any) {
- // It's valid to execute a block against an undefined context, but
- // helpers can not do so, so we expect to have an empty object here;
- for (const name in this) {
- if (Object.prototype.hasOwnProperty.call(this, name)) {
- return 'found';
- }
- }
- // And to make IE happy, check for the known string as length is not enumerated.
- return this === 'bat' ? 'found' : 'not';
- },
- })
- .toCompileTo('notfoundbat');
- });
-
- it('should support multiple levels of inline partials', () => {
- expectTemplate('{{#> layout}}{{#*inline "subcontent"}}subcontent{{/inline}}{{/layout}}')
- .withPartials({
- doctype: 'doctype{{> content}}',
- layout: '{{#> doctype}}{{#*inline "content"}}layout{{> subcontent}}{{/inline}}{{/doctype}}',
- })
- .toCompileTo('doctypelayoutsubcontent');
- });
-
- it('GH-1089: should support failover content in multiple levels of inline partials', () => {
- expectTemplate('{{#> layout}}{{/layout}}')
- .withPartials({
- doctype: 'doctype{{> content}}',
- layout:
- '{{#> doctype}}{{#*inline "content"}}layout{{#> subcontent}}subcontent{{/subcontent}}{{/inline}}{{/doctype}}',
- })
- .toCompileTo('doctypelayoutsubcontent');
- });
-
- it('GH-1099: should support greater than 3 nested levels of inline partials', () => {
- expectTemplate('{{#> layout}}Outer{{/layout}}')
- .withPartials({
- layout: '{{#> inner}}Inner{{/inner}}{{> @partial-block }}',
- inner: '',
- })
- .toCompileTo('Outer');
- });
-
- it('GH-1135 : Context handling within each iteration', () => {
- expectTemplate(
- '{{#each array}}\n' +
- ' 1. IF: {{#if true}}{{../name}}-{{../../name}}-{{../../../name}}{{/if}}\n' +
- ' 2. MYIF: {{#myif true}}{{../name}}={{../../name}}={{../../../name}}{{/myif}}\n' +
- '{{/each}}'
- )
- .withInput({ array: [1], name: 'John' })
- .withHelpers({
- myif(conditional, options: HelperOptions) {
- if (conditional) {
- return options.fn(this);
- } else {
- return options.inverse(this);
- }
- },
- })
- .toCompileTo(' 1. IF: John--\n' + ' 2. MYIF: John==\n');
- });
-
- it('GH-1186: Support block params for existing programs', () => {
- expectTemplate(
- '{{#*inline "test"}}{{> @partial-block }}{{/inline}}' +
- '{{#>test }}{{#each listOne as |item|}}{{ item }}{{/each}}{{/test}}' +
- '{{#>test }}{{#each listTwo as |item|}}{{ item }}{{/each}}{{/test}}'
- )
- .withInput({
- listOne: ['a'],
- listTwo: ['b'],
- })
- .toCompileTo('ab');
- });
-
- it('GH-1319: "unless" breaks when "each" value equals "null"', () => {
- expectTemplate('{{#each list}}{{#unless ./prop}}parent={{../value}} {{/unless}}{{/each}}')
- .withInput({
- value: 'parent',
- list: [null, 'a'],
- })
- .toCompileTo('parent=parent parent=parent ');
- });
-
- it('GH-1341: 4.0.7 release breaks {{#if @partial-block}} usage', () => {
- expectTemplate('template {{>partial}} template')
- .withPartials({
- partialWithBlock: '{{#if @partial-block}} block {{> @partial-block}} block {{/if}}',
- partial: '{{#> partialWithBlock}} partial {{/partialWithBlock}}',
- })
- .toCompileTo('template block partial block template');
- });
-
- it('should allow hash with protected array names', () => {
- expectTemplate('{{helpa length="foo"}}')
- .withInput({ array: [1], name: 'John' })
- .withHelpers({
- helpa(options: HelperOptions) {
- return options.hash.length;
- },
- })
- .toCompileTo('foo');
- });
-
- describe('GH-1598: Performance degradation for partials since v4.3.0', () => {
- let newHandlebarsInstance: typeof Handlebars;
- let spy: jest.SpyInstance;
- beforeEach(() => {
- newHandlebarsInstance = Handlebars.create();
- });
- afterEach(() => {
- spy.mockRestore();
- });
-
- forEachCompileFunctionName((compileName) => {
- it(`should only compile global partials once when calling #${compileName}`, () => {
- const compile = newHandlebarsInstance[compileName].bind(newHandlebarsInstance);
- let calls;
- switch (compileName) {
- case 'compile':
- spy = jest.spyOn(newHandlebarsInstance, 'template');
- calls = 3;
- break;
- case 'compileAST':
- spy = jest.spyOn(newHandlebarsInstance, 'compileAST');
- calls = 2;
- break;
- }
- newHandlebarsInstance.registerPartial({
- dude: 'I am a partial',
- });
- const string = 'Dudes: {{> dude}} {{> dude}}';
- compile(string)(); // This should compile template + partial once
- compile(string)(); // This should only compile template
- expect(spy).toHaveBeenCalledTimes(calls);
- spy.mockRestore();
- });
- });
- });
-
- describe("GH-1639: TypeError: Cannot read property 'apply' of undefined\" when handlebars version > 4.6.0 (undocumented, deprecated usage)", () => {
- it('should treat undefined helpers like non-existing helpers', () => {
- expectTemplate('{{foo}}')
- .withHelper('foo', undefined as any)
- .withInput({ foo: 'bar' })
- .toCompileTo('bar');
- });
- });
-});
diff --git a/dev/lib/handlebars/src/spec/index.security.test.ts b/dev/lib/handlebars/src/spec/index.security.test.ts
deleted file mode 100644
index 878a0931..00000000
--- a/dev/lib/handlebars/src/spec/index.security.test.ts
+++ /dev/null
@@ -1,132 +0,0 @@
-/*
- * This file is forked from the handlebars project (https://github.com/handlebars-lang/handlebars.js),
- * and may include modifications made by Elasticsearch B.V.
- * Elasticsearch B.V. licenses this file to you under the MIT License.
- * See `packages/kbn-handlebars/LICENSE` for more information.
- */
-
-import Handlebars from '../..';
-import { expectTemplate } from '../__jest__/test_bench';
-
-describe('security issues', () => {
- describe('GH-1495: Prevent Remote Code Execution via constructor', () => {
- it('should not allow constructors to be accessed', () => {
- expectTemplate('{{lookup (lookup this "constructor") "name"}}').withInput({}).toCompileTo('');
- expectTemplate('{{constructor.name}}').withInput({}).toCompileTo('');
- });
-
- it('GH-1603: should not allow constructors to be accessed (lookup via toString)', () => {
- expectTemplate('{{lookup (lookup this (list "constructor")) "name"}}')
- .withInput({})
- .withHelper('list', function (element) {
- return [element];
- })
- .toCompileTo('');
- });
-
- it('should allow the "constructor" property to be accessed if it is an "ownProperty"', () => {
- expectTemplate('{{constructor.name}}')
- .withInput({ constructor: { name: 'here we go' } })
- .toCompileTo('here we go');
-
- expectTemplate('{{lookup (lookup this "constructor") "name"}}')
- .withInput({ constructor: { name: 'here we go' } })
- .toCompileTo('here we go');
- });
-
- it('should allow the "constructor" property to be accessed if it is an "own property"', () => {
- expectTemplate('{{lookup (lookup this "constructor") "name"}}')
- .withInput({ constructor: { name: 'here we go' } })
- .toCompileTo('here we go');
- });
- });
-
- describe('GH-1558: Prevent explicit call of helperMissing-helpers', () => {
- describe('without the option "allowExplicitCallOfHelperMissing"', () => {
- it('should throw an exception when calling "{{helperMissing}}" ', () => {
- expectTemplate('{{helperMissing}}').toThrow(Error);
- });
-
- it('should throw an exception when calling "{{#helperMissing}}{{/helperMissing}}" ', () => {
- expectTemplate('{{#helperMissing}}{{/helperMissing}}').toThrow(Error);
- });
-
- it('should throw an exception when calling "{{blockHelperMissing "abc" .}}" ', () => {
- const functionCalls = [];
- expect(() => {
- const template = Handlebars.compile('{{blockHelperMissing "abc" .}}');
- template({
- fn() {
- functionCalls.push('called');
- },
- });
- }).toThrow(Error);
- expect(functionCalls.length).toEqual(0);
- });
-
- it('should throw an exception when calling "{{#blockHelperMissing .}}{{/blockHelperMissing}}"', () => {
- expectTemplate('{{#blockHelperMissing .}}{{/blockHelperMissing}}')
- .withInput({
- fn() {
- return 'functionInData';
- },
- })
- .toThrow(Error);
- });
- });
- });
-
- describe('GH-1563', () => {
- it('should not allow to access constructor after overriding via __defineGetter__', () => {
- // @ts-expect-error
- if ({}.__defineGetter__ == null || {}.__lookupGetter__ == null) {
- return; // Browser does not support this exploit anyway
- }
- expectTemplate(
- '{{__defineGetter__ "undefined" valueOf }}' +
- '{{#with __lookupGetter__ }}' +
- '{{__defineGetter__ "propertyIsEnumerable" (this.bind (this.bind 1)) }}' +
- '{{constructor.name}}' +
- '{{/with}}'
- )
- .withInput({})
- .toThrow(/Missing helper: "__defineGetter__"/);
- });
- });
-
- describe('GH-1595: dangerous properties', () => {
- const templates = [
- '{{constructor}}',
- '{{__defineGetter__}}',
- '{{__defineSetter__}}',
- '{{__lookupGetter__}}',
- '{{__proto__}}',
- '{{lookup this "constructor"}}',
- '{{lookup this "__defineGetter__"}}',
- '{{lookup this "__defineSetter__"}}',
- '{{lookup this "__lookupGetter__"}}',
- '{{lookup this "__proto__"}}',
- ];
-
- templates.forEach((template) => {
- describe('access should be denied to ' + template, () => {
- it('by default', () => {
- expectTemplate(template).withInput({}).toCompileTo('');
- });
- });
- });
- });
-
- describe('escapes template variables', () => {
- it('in default mode', () => {
- expectTemplate("{{'a\\b'}}").withCompileOptions().withInput({ 'a\\b': 'c' }).toCompileTo('c');
- });
-
- it('in strict mode', () => {
- expectTemplate("{{'a\\b'}}")
- .withCompileOptions({ strict: true })
- .withInput({ 'a\\b': 'c' })
- .toCompileTo('c');
- });
- });
-});
diff --git a/dev/lib/handlebars/src/spec/index.strict.test.ts b/dev/lib/handlebars/src/spec/index.strict.test.ts
deleted file mode 100644
index a8f294b9..00000000
--- a/dev/lib/handlebars/src/spec/index.strict.test.ts
+++ /dev/null
@@ -1,164 +0,0 @@
-/*
- * This file is forked from the handlebars project (https://github.com/handlebars-lang/handlebars.js),
- * and may include modifications made by Elasticsearch B.V.
- * Elasticsearch B.V. licenses this file to you under the MIT License.
- * See `packages/kbn-handlebars/LICENSE` for more information.
- */
-
-import { expectTemplate } from '../__jest__/test_bench';
-
-describe('strict', () => {
- describe('strict mode', () => {
- it('should error on missing property lookup', () => {
- expectTemplate('{{hello}}')
- .withCompileOptions({ strict: true })
- .toThrow(/"hello" not defined in/);
- });
-
- it('should error on missing child', () => {
- expectTemplate('{{hello.bar}}')
- .withCompileOptions({ strict: true })
- .withInput({ hello: { bar: 'foo' } })
- .toCompileTo('foo');
-
- expectTemplate('{{hello.bar}}')
- .withCompileOptions({ strict: true })
- .withInput({ hello: {} })
- .toThrow(/"bar" not defined in/);
- });
-
- it('should handle explicit undefined', () => {
- expectTemplate('{{hello.bar}}')
- .withCompileOptions({ strict: true })
- .withInput({ hello: { bar: undefined } })
- .toCompileTo('');
- });
-
- it('should error on missing property lookup in known helpers mode', () => {
- expectTemplate('{{hello}}')
- .withCompileOptions({
- strict: true,
- knownHelpersOnly: true,
- })
- .toThrow(/"hello" not defined in/);
- });
-
- it('should error on missing context', () => {
- expectTemplate('{{hello}}').withCompileOptions({ strict: true }).toThrow(Error);
- });
-
- it('should error on missing data lookup', () => {
- const xt = expectTemplate('{{@hello}}').withCompileOptions({
- strict: true,
- });
-
- xt.toThrow(Error);
-
- xt.withRuntimeOptions({ data: { hello: 'foo' } }).toCompileTo('foo');
- });
-
- it('should not run helperMissing for helper calls', () => {
- expectTemplate('{{hello foo}}')
- .withCompileOptions({ strict: true })
- .withInput({ foo: true })
- .toThrow(/"hello" not defined in/);
-
- expectTemplate('{{#hello foo}}{{/hello}}')
- .withCompileOptions({ strict: true })
- .withInput({ foo: true })
- .toThrow(/"hello" not defined in/);
- });
-
- it('should throw on ambiguous blocks', () => {
- expectTemplate('{{#hello}}{{/hello}}')
- .withCompileOptions({ strict: true })
- .toThrow(/"hello" not defined in/);
-
- expectTemplate('{{^hello}}{{/hello}}')
- .withCompileOptions({ strict: true })
- .toThrow(/"hello" not defined in/);
-
- expectTemplate('{{#hello.bar}}{{/hello.bar}}')
- .withCompileOptions({ strict: true })
- .withInput({ hello: {} })
- .toThrow(/"bar" not defined in/);
- });
-
- it('should allow undefined parameters when passed to helpers', () => {
- expectTemplate('{{#unless foo}}success{{/unless}}')
- .withCompileOptions({ strict: true })
- .toCompileTo('success');
- });
-
- it('should allow undefined hash when passed to helpers', () => {
- expectTemplate('{{helper value=@foo}}')
- .withCompileOptions({
- strict: true,
- })
- .withHelpers({
- helper(options) {
- expect('value' in options.hash).toEqual(true);
- expect(options.hash.value).toBeUndefined();
- return 'success';
- },
- })
- .toCompileTo('success');
- });
-
- it('should show error location on missing property lookup', () => {
- expectTemplate('\n\n\n {{hello}}')
- .withCompileOptions({ strict: true })
- .toThrow('"hello" not defined in [object Object] - 4:5');
- });
-
- it('should error contains correct location properties on missing property lookup', () => {
- try {
- expectTemplate('\n\n\n {{hello}}')
- .withCompileOptions({ strict: true })
- .toCompileTo('throw before asserting this');
- } catch (error) {
- expect(error.lineNumber).toEqual(4);
- expect(error.endLineNumber).toEqual(4);
- expect(error.column).toEqual(5);
- expect(error.endColumn).toEqual(10);
- }
- });
- });
-
- describe('assume objects', () => {
- it('should ignore missing property', () => {
- expectTemplate('{{hello}}').withCompileOptions({ assumeObjects: true }).toCompileTo('');
- });
-
- it('should ignore missing child', () => {
- expectTemplate('{{hello.bar}}')
- .withCompileOptions({ assumeObjects: true })
- .withInput({ hello: {} })
- .toCompileTo('');
- });
-
- it('should error on missing object', () => {
- expectTemplate('{{hello.bar}}').withCompileOptions({ assumeObjects: true }).toThrow(Error);
- });
-
- it('should error on missing context', () => {
- expectTemplate('{{hello}}')
- .withCompileOptions({ assumeObjects: true })
- .withInput(undefined)
- .toThrow(Error);
- });
-
- it('should error on missing data lookup', () => {
- expectTemplate('{{@hello.bar}}')
- .withCompileOptions({ assumeObjects: true })
- .withInput(undefined)
- .toThrow(Error);
- });
-
- it('should execute blockHelperMissing', () => {
- expectTemplate('{{^hello}}foo{{/hello}}')
- .withCompileOptions({ assumeObjects: true })
- .toCompileTo('foo');
- });
- });
-});
diff --git a/dev/lib/handlebars/src/spec/index.subexpressions.test.ts b/dev/lib/handlebars/src/spec/index.subexpressions.test.ts
deleted file mode 100644
index 4dee24b7..00000000
--- a/dev/lib/handlebars/src/spec/index.subexpressions.test.ts
+++ /dev/null
@@ -1,214 +0,0 @@
-/*
- * This file is forked from the handlebars project (https://github.com/handlebars-lang/handlebars.js),
- * and may include modifications made by Elasticsearch B.V.
- * Elasticsearch B.V. licenses this file to you under the MIT License.
- * See `packages/kbn-handlebars/LICENSE` for more information.
- */
-
-import Handlebars, { type HelperOptions } from '../..';
-import { expectTemplate } from '../__jest__/test_bench';
-
-describe('subexpressions', () => {
- it('arg-less helper', () => {
- expectTemplate('{{foo (bar)}}!')
- .withHelpers({
- foo(val) {
- return val + val;
- },
- bar() {
- return 'LOL';
- },
- })
- .toCompileTo('LOLLOL!');
- });
-
- it('helper w args', () => {
- expectTemplate('{{blog (equal a b)}}')
- .withInput({ bar: 'LOL' })
- .withHelpers({
- blog(val) {
- return 'val is ' + val;
- },
- equal(x, y) {
- return x === y;
- },
- })
- .toCompileTo('val is true');
- });
-
- it('mixed paths and helpers', () => {
- expectTemplate('{{blog baz.bat (equal a b) baz.bar}}')
- .withInput({ bar: 'LOL', baz: { bat: 'foo!', bar: 'bar!' } })
- .withHelpers({
- blog(val, that, theOther) {
- return 'val is ' + val + ', ' + that + ' and ' + theOther;
- },
- equal(x, y) {
- return x === y;
- },
- })
- .toCompileTo('val is foo!, true and bar!');
- });
-
- it('supports much nesting', () => {
- expectTemplate('{{blog (equal (equal true true) true)}}')
- .withInput({ bar: 'LOL' })
- .withHelpers({
- blog(val) {
- return 'val is ' + val;
- },
- equal(x, y) {
- return x === y;
- },
- })
- .toCompileTo('val is true');
- });
-
- it('GH-800 : Complex subexpressions', () => {
- const context = { a: 'a', b: 'b', c: { c: 'c' }, d: 'd', e: { e: 'e' } };
- const helpers = {
- dash(a: any, b: any) {
- return a + '-' + b;
- },
- concat(a: any, b: any) {
- return a + b;
- },
- };
-
- expectTemplate("{{dash 'abc' (concat a b)}}")
- .withInput(context)
- .withHelpers(helpers)
- .toCompileTo('abc-ab');
-
- expectTemplate('{{dash d (concat a b)}}')
- .withInput(context)
- .withHelpers(helpers)
- .toCompileTo('d-ab');
-
- expectTemplate('{{dash c.c (concat a b)}}')
- .withInput(context)
- .withHelpers(helpers)
- .toCompileTo('c-ab');
-
- expectTemplate('{{dash (concat a b) c.c}}')
- .withInput(context)
- .withHelpers(helpers)
- .toCompileTo('ab-c');
-
- expectTemplate('{{dash (concat a e.e) c.c}}')
- .withInput(context)
- .withHelpers(helpers)
- .toCompileTo('ae-c');
- });
-
- it('provides each nested helper invocation its own options hash', () => {
- let lastOptions: HelperOptions;
- const helpers = {
- equal(x: any, y: any, options: HelperOptions) {
- if (!options || options === lastOptions) {
- throw new Error('options hash was reused');
- }
- lastOptions = options;
- return x === y;
- },
- };
- expectTemplate('{{equal (equal true true) true}}').withHelpers(helpers).toCompileTo('true');
- });
-
- it('with hashes', () => {
- expectTemplate("{{blog (equal (equal true true) true fun='yes')}}")
- .withInput({ bar: 'LOL' })
- .withHelpers({
- blog(val) {
- return 'val is ' + val;
- },
- equal(x, y) {
- return x === y;
- },
- })
- .toCompileTo('val is true');
- });
-
- it('as hashes', () => {
- expectTemplate("{{blog fun=(equal (blog fun=1) 'val is 1')}}")
- .withHelpers({
- blog(options) {
- return 'val is ' + options.hash.fun;
- },
- equal(x, y) {
- return x === y;
- },
- })
- .toCompileTo('val is true');
- });
-
- it('multiple subexpressions in a hash', () => {
- expectTemplate('{{input aria-label=(t "Name") placeholder=(t "Example User")}}')
- .withHelpers({
- input(options) {
- const hash = options.hash;
- const ariaLabel = Handlebars.Utils.escapeExpression(hash['aria-label']);
- const placeholder = Handlebars.Utils.escapeExpression(hash.placeholder);
- return new Handlebars.SafeString(
- '<input aria-label="' + ariaLabel + '" placeholder="' + placeholder + '" />'
- );
- },
- t(defaultString) {
- return new Handlebars.SafeString(defaultString);
- },
- })
- .toCompileTo('<input aria-label="Name" placeholder="Example User" />');
- });
-
- it('multiple subexpressions in a hash with context', () => {
- expectTemplate('{{input aria-label=(t item.field) placeholder=(t item.placeholder)}}')
- .withInput({
- item: {
- field: 'Name',
- placeholder: 'Example User',
- },
- })
- .withHelpers({
- input(options) {
- const hash = options.hash;
- const ariaLabel = Handlebars.Utils.escapeExpression(hash['aria-label']);
- const placeholder = Handlebars.Utils.escapeExpression(hash.placeholder);
- return new Handlebars.SafeString(
- '<input aria-label="' + ariaLabel + '" placeholder="' + placeholder + '" />'
- );
- },
- t(defaultString) {
- return new Handlebars.SafeString(defaultString);
- },
- })
- .toCompileTo('<input aria-label="Name" placeholder="Example User" />');
- });
-
- it('subexpression functions on the context', () => {
- expectTemplate('{{foo (bar)}}!')
- .withInput({
- bar() {
- return 'LOL';
- },
- })
- .withHelpers({
- foo(val) {
- return val + val;
- },
- })
- .toCompileTo('LOLLOL!');
- });
-
- it("subexpressions can't just be property lookups", () => {
- expectTemplate('{{foo (bar)}}!')
- .withInput({
- bar: 'LOL',
- })
- .withHelpers({
- foo(val) {
- return val + val;
- },
- })
- .toThrow();
- });
-});
diff --git a/dev/lib/handlebars/src/spec/index.utils.test.ts b/dev/lib/handlebars/src/spec/index.utils.test.ts
deleted file mode 100644
index 6350bc7c..00000000
--- a/dev/lib/handlebars/src/spec/index.utils.test.ts
+++ /dev/null
@@ -1,24 +0,0 @@
-/*
- * This file is forked from the handlebars project (https://github.com/handlebars-lang/handlebars.js),
- * and may include modifications made by Elasticsearch B.V.
- * Elasticsearch B.V. licenses this file to you under the MIT License.
- * See `packages/kbn-handlebars/LICENSE` for more information.
- */
-
-import Handlebars from '../..';
-import { expectTemplate } from '../__jest__/test_bench';
-
-describe('utils', function () {
- describe('#SafeString', function () {
- it('constructing a safestring from a string and checking its type', function () {
- const safe = new Handlebars.SafeString('testing 1, 2, 3');
- expect(safe).toBeInstanceOf(Handlebars.SafeString);
- expect(safe.toString()).toEqual('testing 1, 2, 3');
- });
-
- it('it should not escape SafeString properties', function () {
- const name = new Handlebars.SafeString('<em>Sean O&#x27;Malley</em>');
- expectTemplate('{{name}}').withInput({ name }).toCompileTo('<em>Sean O&#x27;Malley</em>');
- });
- });
-});
diff --git a/dev/lib/handlebars/src/spec/index.whitespace_control.test.ts b/dev/lib/handlebars/src/spec/index.whitespace_control.test.ts
deleted file mode 100644
index 1f7cf019..00000000
--- a/dev/lib/handlebars/src/spec/index.whitespace_control.test.ts
+++ /dev/null
@@ -1,88 +0,0 @@
-/*
- * This file is forked from the handlebars project (https://github.com/handlebars-lang/handlebars.js),
- * and may include modifications made by Elasticsearch B.V.
- * Elasticsearch B.V. licenses this file to you under the MIT License.
- * See `packages/kbn-handlebars/LICENSE` for more information.
- */
-
-import { expectTemplate } from '../__jest__/test_bench';
-
-describe('whitespace control', () => {
- it('should strip whitespace around mustache calls', () => {
- const hash = { foo: 'bar<' };
- expectTemplate(' {{~foo~}} ').withInput(hash).toCompileTo('bar&lt;');
- expectTemplate(' {{~foo}} ').withInput(hash).toCompileTo('bar&lt; ');
- expectTemplate(' {{foo~}} ').withInput(hash).toCompileTo(' bar&lt;');
- expectTemplate(' {{~&foo~}} ').withInput(hash).toCompileTo('bar<');
- expectTemplate(' {{~{foo}~}} ').withInput(hash).toCompileTo('bar<');
- expectTemplate('1\n{{foo~}} \n\n 23\n{{bar}}4').toCompileTo('1\n23\n4');
- });
-
- describe('blocks', () => {
- it('should strip whitespace around simple block calls', () => {
- const hash = { foo: 'bar<' };
-
- expectTemplate(' {{~#if foo~}} bar {{~/if~}} ').withInput(hash).toCompileTo('bar');
- expectTemplate(' {{#if foo~}} bar {{/if~}} ').withInput(hash).toCompileTo(' bar ');
- expectTemplate(' {{~#if foo}} bar {{~/if}} ').withInput(hash).toCompileTo(' bar ');
- expectTemplate(' {{#if foo}} bar {{/if}} ').withInput(hash).toCompileTo(' bar ');
-
- expectTemplate(' \n\n{{~#if foo~}} \n\nbar \n\n{{~/if~}}\n\n ')
- .withInput(hash)
- .toCompileTo('bar');
-
- expectTemplate(' a\n\n{{~#if foo~}} \n\nbar \n\n{{~/if~}}\n\na ')
- .withInput(hash)
- .toCompileTo(' abara ');
- });
-
- it('should strip whitespace around inverse block calls', () => {
- expectTemplate(' {{~^if foo~}} bar {{~/if~}} ').toCompileTo('bar');
- expectTemplate(' {{^if foo~}} bar {{/if~}} ').toCompileTo(' bar ');
- expectTemplate(' {{~^if foo}} bar {{~/if}} ').toCompileTo(' bar ');
- expectTemplate(' {{^if foo}} bar {{/if}} ').toCompileTo(' bar ');
- expectTemplate(' \n\n{{~^if foo~}} \n\nbar \n\n{{~/if~}}\n\n ').toCompileTo('bar');
- });
-
- it('should strip whitespace around complex block calls', () => {
- const hash = { foo: 'bar<' };
-
- expectTemplate('{{#if foo~}} bar {{~^~}} baz {{~/if}}').withInput(hash).toCompileTo('bar');
- expectTemplate('{{#if foo~}} bar {{^~}} baz {{/if}}').withInput(hash).toCompileTo('bar ');
- expectTemplate('{{#if foo}} bar {{~^~}} baz {{~/if}}').withInput(hash).toCompileTo(' bar');
- expectTemplate('{{#if foo}} bar {{^~}} baz {{/if}}').withInput(hash).toCompileTo(' bar ');
- expectTemplate('{{#if foo~}} bar {{~else~}} baz {{~/if}}').withInput(hash).toCompileTo('bar');
-
- expectTemplate('\n\n{{~#if foo~}} \n\nbar \n\n{{~^~}} \n\nbaz \n\n{{~/if~}}\n\n')
- .withInput(hash)
- .toCompileTo('bar');
-
- expectTemplate('\n\n{{~#if foo~}} \n\n{{{foo}}} \n\n{{~^~}} \n\nbaz \n\n{{~/if~}}\n\n')
- .withInput(hash)
- .toCompileTo('bar<');
-
- expectTemplate('{{#if foo~}} bar {{~^~}} baz {{~/if}}').toCompileTo('baz');
- expectTemplate('{{#if foo}} bar {{~^~}} baz {{/if}}').toCompileTo('baz ');
- expectTemplate('{{#if foo~}} bar {{~^}} baz {{~/if}}').toCompileTo(' baz');
- expectTemplate('{{#if foo~}} bar {{~^}} baz {{/if}}').toCompileTo(' baz ');
- expectTemplate('{{#if foo~}} bar {{~else~}} baz {{~/if}}').toCompileTo('baz');
- expectTemplate('\n\n{{~#if foo~}} \n\nbar \n\n{{~^~}} \n\nbaz \n\n{{~/if~}}\n\n').toCompileTo(
- 'baz'
- );
- });
- });
-
- it('should strip whitespace around partials', () => {
- expectTemplate('foo {{~> dude~}} ').withPartials({ dude: 'bar' }).toCompileTo('foobar');
- expectTemplate('foo {{> dude~}} ').withPartials({ dude: 'bar' }).toCompileTo('foo bar');
- expectTemplate('foo {{> dude}} ').withPartials({ dude: 'bar' }).toCompileTo('foo bar ');
- expectTemplate('foo\n {{~> dude}} ').withPartials({ dude: 'bar' }).toCompileTo('foobar');
- expectTemplate('foo\n {{> dude}} ').withPartials({ dude: 'bar' }).toCompileTo('foo\n bar');
- });
-
- it('should only strip whitespace once', () => {
- expectTemplate(' {{~foo~}} {{foo}} {{foo}} ')
- .withInput({ foo: 'bar' })
- .toCompileTo('barbar bar ');
- });
-});