[8.6] [@kbn/handlebars] Simplify the way upstream changes are discovered (#150024) (#150142)

# Backport

This will backport the following commits from `main` to `8.6`:
- [[@kbn/handlebars] Simplify the way upstream changes are discovered
(#150024)](https://github.com/elastic/kibana/pull/150024)

<!--- Backport version: 8.9.7 -->

### Questions ?
Please refer to the [Backport tool
documentation](https://github.com/sqren/backport)

<!--BACKPORT [{"author":{"name":"Thomas
Watson","email":"watson@elastic.co"},"sourceCommit":{"committedDate":"2023-02-02T09:56:56Z","message":"[@kbn/handlebars]
Simplify the way upstream changes are discovered (#150024)\n\nPreviously
a custom diff of the files inside the upstream `spec`\r\ndirectory was
kept up-to-date in this package `.patches` directory. This\r\nprocess
was very tedious and wasn't providing much value.\r\n\r\nIn this commit
I've simplified the process tremendously and simply rely on\r\nchecking
if there are any new commits upstream and then allow the\r\ndeveloper to
manually check for relevant changes. This is something they\r\nneeded to
do with the old system regardless. Here the code is just
much\r\nsimpler.\r\n\r\n---------\r\n\r\nCo-authored-by: Aleh Zasypkin
<aleh.zasypkin@gmail.com>","sha":"975452987919dff1dbfc1ae48100763385c09c36","branchLabelMapping":{"^v8.7.0$":"main","^v(\\d+).(\\d+).\\d+$":"$1.$2"}},"sourcePullRequest":{"labels":["release_note:skip","backport:prev-minor","v8.7.0"],"number":150024,"url":"https://github.com/elastic/kibana/pull/150024","mergeCommit":{"message":"[@kbn/handlebars]
Simplify the way upstream changes are discovered (#150024)\n\nPreviously
a custom diff of the files inside the upstream `spec`\r\ndirectory was
kept up-to-date in this package `.patches` directory. This\r\nprocess
was very tedious and wasn't providing much value.\r\n\r\nIn this commit
I've simplified the process tremendously and simply rely on\r\nchecking
if there are any new commits upstream and then allow the\r\ndeveloper to
manually check for relevant changes. This is something they\r\nneeded to
do with the old system regardless. Here the code is just
much\r\nsimpler.\r\n\r\n---------\r\n\r\nCo-authored-by: Aleh Zasypkin
<aleh.zasypkin@gmail.com>","sha":"975452987919dff1dbfc1ae48100763385c09c36"}},"sourceBranch":"main","suggestedTargetBranches":[],"targetPullRequestStates":[{"branch":"main","label":"v8.7.0","labelRegex":"^v8.7.0$","isSourceBranch":true,"state":"MERGED","url":"https://github.com/elastic/kibana/pull/150024","number":150024,"mergeCommit":{"message":"[@kbn/handlebars]
Simplify the way upstream changes are discovered (#150024)\n\nPreviously
a custom diff of the files inside the upstream `spec`\r\ndirectory was
kept up-to-date in this package `.patches` directory. This\r\nprocess
was very tedious and wasn't providing much value.\r\n\r\nIn this commit
I've simplified the process tremendously and simply rely on\r\nchecking
if there are any new commits upstream and then allow the\r\ndeveloper to
manually check for relevant changes. This is something they\r\nneeded to
do with the old system regardless. Here the code is just
much\r\nsimpler.\r\n\r\n---------\r\n\r\nCo-authored-by: Aleh Zasypkin
<aleh.zasypkin@gmail.com>","sha":"975452987919dff1dbfc1ae48100763385c09c36"}}]}]
BACKPORT-->

Co-authored-by: Thomas Watson <watson@elastic.co>
This commit is contained in:
Kibana Machine 2023-02-02 08:10:05 -05:00 committed by GitHub
parent 9eed05b447
commit 4f51ca24ba
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
33 changed files with 78 additions and 5626 deletions

View file

@ -4,5 +4,5 @@ set -euo pipefail
source .buildkite/scripts/common/util.sh
echo '--- Checking for @kbn/handlebars test changes'
packages/kbn-handlebars/scripts/check_for_test_changes.sh
echo '--- Checking for @kbn/handlebars upstream updates'
packages/kbn-handlebars/scripts/check_for_upstream_updates.sh

View file

@ -520,7 +520,7 @@ module.exports = {
},
},
{
files: ['packages/kbn-handlebars/src/upstream/**/*.{js,mjs,ts,tsx}'],
files: ['packages/kbn-handlebars/src/spec/**/*.{js,mjs,ts,tsx}'],
rules: {
'@kbn/eslint/require-license-header': [
'error',

View file

@ -1 +0,0 @@
.tmp

View file

@ -1,608 +0,0 @@
1c1,6
< global.handlebarsEnv = null;
---
> /*
> * 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.
> */
3,5c8,9
< beforeEach(function() {
< global.handlebarsEnv = Handlebars.create();
< });
---
> import Handlebars from '../..';
> import { expectTemplate } from '../__jest__/test_bench';
7,11c11,13
< describe('basic context', function() {
< it('most basic', function() {
< expectTemplate('{{foo}}')
< .withInput({ foo: 'foo' })
< .toCompileTo('foo');
---
> describe('basic context', () => {
> it('most basic', () => {
> expectTemplate('{{foo}}').withInput({ foo: 'foo' }).toCompileTo('foo');
14,33c16,21
< it('escaping', function() {
< 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('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');
36c24
< it('compiling with a basic context', function() {
---
> it('compiling with a basic context', () => {
40c28
< world: 'world'
---
> world: 'world',
42d29
< .withMessage('It works if all the required keys are provided')
46,49c33,34
< it('compiling with a string context', function() {
< expectTemplate('{{.}}{{length}}')
< .withInput('bye')
< .toCompileTo('bye3');
---
> it('compiling with a string context', () => {
> expectTemplate('{{.}}{{length}}').withInput('bye').toCompileTo('bye3');
52c37
< it('compiling with an undefined context', function() {
---
> it('compiling with an undefined context', () => {
62c47
< it('comments', function() {
---
> it('comments', () => {
66c51
< world: 'world'
---
> world: 'world',
68d52
< .withMessage('comments are ignored')
72,76c56
<
< expectTemplate(' {{~!-- long-comment --~}} blah').toCompileTo(
< 'blah'
< );
<
---
> expectTemplate(' {{~!-- long-comment --~}} blah').toCompileTo('blah');
78,82c58
<
< expectTemplate(' {{!-- long-comment --~}} blah').toCompileTo(
< ' blah'
< );
<
---
> expectTemplate(' {{!-- long-comment --~}} blah').toCompileTo(' blah');
84,87c60
<
< expectTemplate(' {{~!-- long-comment --}} blah').toCompileTo(
< ' blah'
< );
---
> expectTemplate(' {{~!-- long-comment --}} blah').toCompileTo(' blah');
90,91c63,64
< it('boolean', function() {
< var string = '{{#goodbye}}GOODBYE {{/goodbye}}cruel {{world}}!';
---
> it('boolean', () => {
> const string = '{{#goodbye}}GOODBYE {{/goodbye}}cruel {{world}}!';
95c68
< world: 'world'
---
> world: 'world',
97d69
< .withMessage('booleans show the contents when true')
103c75
< world: 'world'
---
> world: 'world',
105d76
< .withMessage('booleans do not show the contents when false')
109c80
< it('zeros', function() {
---
> it('zeros', () => {
113c84
< num2: 0
---
> num2: 0,
117,119c88
< expectTemplate('num: {{.}}')
< .withInput(0)
< .toCompileTo('num: 0');
---
> expectTemplate('num: {{.}}').withInput(0).toCompileTo('num: 0');
126c95
< it('false', function() {
---
> it('false', () => {
131c100
< val2: new Boolean(false)
---
> val2: new Boolean(false),
135,137c104
< expectTemplate('val: {{.}}')
< .withInput(false)
< .toCompileTo('val: false');
---
> expectTemplate('val: {{.}}').withInput(false).toCompileTo('val: false');
146c113
< val2: new Boolean(false)
---
> val2: new Boolean(false),
156c123
< it('should handle undefined and null', function() {
---
> it('should handle undefined and null', () => {
159,167c126,128
< awesome: function(_undefined, _null, options) {
< return (
< (_undefined === undefined) +
< ' ' +
< (_null === null) +
< ' ' +
< typeof options
< );
< }
---
> awesome(_undefined: any, _null: any, options: any) {
> return (_undefined === undefined) + ' ' + (_null === null) + ' ' + typeof options;
> },
173c134
< undefined: function() {
---
> undefined() {
175c136
< }
---
> },
181c142
< null: function() {
---
> null() {
183c144
< }
---
> },
188c149
< it('newlines', function() {
---
> it('newlines', () => {
190d150
<
194,216c154,159
< it('escaping text', function() {
< expectTemplate("Awesome's")
< .withMessage(
< "text is escaped so that it doesn't get caught on single quotes"
< )
< .toCompileTo("Awesome's");
<
< expectTemplate('Awesome\\')
< .withMessage("text is escaped so that the closing quote can't be ignored")
< .toCompileTo('Awesome\\');
<
< expectTemplate('Awesome\\\\ foo')
< .withMessage("text is escaped so that it doesn't mess up backslashes")
< .toCompileTo('Awesome\\\\ foo');
<
< expectTemplate('Awesome {{foo}}')
< .withInput({ foo: '\\' })
< .withMessage("text is escaped so that it doesn't mess up backslashes")
< .toCompileTo('Awesome \\');
<
< expectTemplate(" ' ' ")
< .withMessage('double quotes never produce invalid javascript')
< .toCompileTo(" ' ' ");
---
> 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(" ' ' ");
219,223c162,163
< it('escaping expressions', function() {
< expectTemplate('{{{awesome}}}')
< .withInput({ awesome: "&'\\<>" })
< .withMessage("expressions with 3 handlebars aren't escaped")
< .toCompileTo("&'\\<>");
---
> it('escaping expressions', () => {
> expectTemplate('{{{awesome}}}').withInput({ awesome: "&'\\<>" }).toCompileTo("&'\\<>");
225,228c165
< expectTemplate('{{&awesome}}')
< .withInput({ awesome: "&'\\<>" })
< .withMessage("expressions with {{& handlebars aren't escaped")
< .toCompileTo("&'\\<>");
---
> expectTemplate('{{&awesome}}').withInput({ awesome: "&'\\<>" }).toCompileTo("&'\\<>");
232d168
< .withMessage('by default expressions should be escaped')
237d172
< .withMessage('escaping should properly handle amperstands')
241c176
< it("functions returning safestrings shouldn't be escaped", function() {
---
> it("functions returning safestrings shouldn't be escaped", () => {
244c179
< awesome: function() {
---
> awesome() {
246c181
< }
---
> },
248d182
< .withMessage("functions returning safestrings aren't escaped")
252c186
< it('functions', function() {
---
> it('functions', () => {
255c189
< awesome: function() {
---
> awesome() {
257c191
< }
---
> },
259d192
< .withMessage('functions are called and render their output')
264c197
< awesome: function() {
---
> awesome() {
267c200
< more: 'More awesome'
---
> more: 'More awesome',
269d201
< .withMessage('functions are bound to the context')
273c205
< it('functions with context argument', function() {
---
> it('functions with context argument', () => {
276c208
< awesome: function(context) {
---
> awesome(context: any) {
279c211
< frank: 'Frank'
---
> frank: 'Frank',
281d212
< .withMessage('functions are called with context arguments')
285c216
< it('pathed functions with context argument', function() {
---
> it('pathed functions with context argument', () => {
289c220
< awesome: function(context) {
---
> awesome(context: any) {
291c222
< }
---
> },
293c224
< frank: 'Frank'
---
> frank: 'Frank',
295d225
< .withMessage('functions are called with context arguments')
299c229
< it('depthed functions with context argument', function() {
---
> it('depthed functions with context argument', () => {
302c232
< awesome: function(context) {
---
> awesome(context: any) {
305c235
< frank: 'Frank'
---
> frank: 'Frank',
307d236
< .withMessage('functions are called with context arguments')
311c240
< it('block functions with context argument', function() {
---
> it('block functions with context argument', () => {
314c243
< awesome: function(context, options) {
---
> awesome(context: any, options: any) {
316c245
< }
---
> },
318d246
< .withMessage('block functions are called with context and options')
322,325c250,251
< it('depthed block functions with context argument', function() {
< expectTemplate(
< '{{#with value}}{{#../awesome 1}}inner {{.}}{{/../awesome}}{{/with}}'
< )
---
> it('depthed block functions with context argument', () => {
> expectTemplate('{{#with value}}{{#../awesome 1}}inner {{.}}{{/../awesome}}{{/with}}')
328c254
< awesome: function(context, options) {
---
> awesome(context: any, options: any) {
330c256
< }
---
> },
332d257
< .withMessage('block functions are called with context and options')
336c261
< it('block functions without context argument', function() {
---
> it('block functions without context argument', () => {
339c264
< awesome: function(options) {
---
> awesome(options: any) {
341c266
< }
---
> },
343d267
< .withMessage('block functions are called with options')
347c271
< it('pathed block functions without context argument', function() {
---
> it('pathed block functions without context argument', () => {
351c275
< awesome: function() {
---
> awesome() {
353,354c277,278
< }
< }
---
> },
> },
356d279
< .withMessage('block functions are called with options')
360,363c283,284
< it('depthed block functions without context argument', function() {
< expectTemplate(
< '{{#with value}}{{#../awesome}}inner{{/../awesome}}{{/with}}'
< )
---
> it('depthed block functions without context argument', () => {
> expectTemplate('{{#with value}}{{#../awesome}}inner{{/../awesome}}{{/with}}')
366c287
< awesome: function() {
---
> awesome() {
368c289
< }
---
> },
370d290
< .withMessage('block functions are called with options')
374,378c294,295
< it('paths with hyphens', function() {
< expectTemplate('{{foo-bar}}')
< .withInput({ 'foo-bar': 'baz' })
< .withMessage('Paths can contain hyphens (-)')
< .toCompileTo('baz');
---
> it('paths with hyphens', () => {
> expectTemplate('{{foo-bar}}').withInput({ 'foo-bar': 'baz' }).toCompileTo('baz');
382d298
< .withMessage('Paths can contain hyphens (-)')
387d302
< .withMessage('Paths can contain hyphens (-)')
391c306
< it('nested paths', function() {
---
> it('nested paths', () => {
394d308
< .withMessage('Nested paths access nested objects')
398c312
< it('nested paths with empty string value', function() {
---
> it('nested paths with empty string value', () => {
401d314
< .withMessage('Nested paths access nested objects with empty string')
405c318
< it('literal paths', function() {
---
> it('literal paths', () => {
408d320
< .withMessage('Literal paths can be used')
413d324
< .withMessage('Literal paths can be used')
417c328
< it('literal references', function() {
---
> it('literal references', () => {
443c354
< it("that current context path ({{.}}) doesn't hit helpers", function() {
---
> it("that current context path ({{.}}) doesn't hit helpers", () => {
445a357
> // @ts-expect-error Setting the helper to a string instead of a function doesn't make sense normally, but here it doesn't matter
450c362
< it('complex but empty paths', function() {
---
> it('complex but empty paths', () => {
455,457c367
< expectTemplate('{{person/name}}')
< .withInput({ person: {} })
< .toCompileTo('');
---
> expectTemplate('{{person/name}}').withInput({ person: {} }).toCompileTo('');
460c370
< it('this keyword in paths', function() {
---
> it('this keyword in paths', () => {
463d372
< .withMessage('This keyword in paths evaluates to current context')
468c377
< hellos: [{ text: 'hello' }, { text: 'Hello' }, { text: 'HELLO' }]
---
> hellos: [{ text: 'hello' }, { text: 'Hello' }, { text: 'HELLO' }],
470d378
< .withMessage('This keyword evaluates in more complex paths')
474c382
< it('this keyword nested inside path', function() {
---
> it('this keyword nested inside path', () => {
476d383
< Error,
480,482c387
< expectTemplate('{{[this]}}')
< .withInput({ this: 'bar' })
< .toCompileTo('bar');
---
> expectTemplate('{{[this]}}').withInput({ this: 'bar' }).toCompileTo('bar');
489,491c394,396
< it('this keyword in helpers', function() {
< var helpers = {
< foo: function(value) {
---
> it('this keyword in helpers', () => {
> const helpers = {
> foo(value: any) {
493c398
< }
---
> },
499d403
< .withMessage('This keyword in paths evaluates to current context')
504c408
< hellos: [{ text: 'hello' }, { text: 'Hello' }, { text: 'HELLO' }]
---
> hellos: [{ text: 'hello' }, { text: 'Hello' }, { text: 'HELLO' }],
507d410
< .withMessage('This keyword evaluates in more complex paths')
511c414
< it('this keyword nested inside helpers param', function() {
---
> it('this keyword nested inside helpers param', () => {
513d415
< Error,
519c421
< foo: function(value) {
---
> foo(value: any) {
522c424
< this: 'bar'
---
> this: 'bar',
528c430
< foo: function(value) {
---
> foo(value: any) {
531c433
< text: { this: 'bar' }
---
> text: { this: 'bar' },
536c438
< it('pass string literals', function() {
---
> it('pass string literals', () => {
538,541c440
<
< expectTemplate('{{"foo"}}')
< .withInput({ foo: 'bar' })
< .toCompileTo('bar');
---
> expectTemplate('{{"foo"}}').withInput({ foo: 'bar' }).toCompileTo('bar');
545c444
< foo: ['bar', 'baz']
---
> foo: ['bar', 'baz'],
550c449
< it('pass number literals', function() {
---
> it('pass number literals', () => {
552,556c451
<
< expectTemplate('{{12}}')
< .withInput({ '12': 'bar' })
< .toCompileTo('bar');
<
---
> expectTemplate('{{12}}').withInput({ '12': 'bar' }).toCompileTo('bar');
558,562c453
<
< expectTemplate('{{12.34}}')
< .withInput({ '12.34': 'bar' })
< .toCompileTo('bar');
<
---
> expectTemplate('{{12.34}}').withInput({ '12.34': 'bar' }).toCompileTo('bar');
565c456
< '12.34': function(arg) {
---
> '12.34'(arg: any) {
567c458
< }
---
> },
572c463
< it('pass boolean literals', function() {
---
> it('pass boolean literals', () => {
574,581c465,466
<
< expectTemplate('{{true}}')
< .withInput({ '': 'foo' })
< .toCompileTo('');
<
< expectTemplate('{{false}}')
< .withInput({ false: 'foo' })
< .toCompileTo('foo');
---
> expectTemplate('{{true}}').withInput({ '': 'foo' }).toCompileTo('');
> expectTemplate('{{false}}').withInput({ false: 'foo' }).toCompileTo('foo');
584c469
< it('should handle literals in subexpression', function() {
---
> it('should handle literals in subexpression', () => {
587c472
< false: function() {
---
> false() {
589c474
< }
---
> },
591c476
< .withHelper('foo', function(arg) {
---
> .withHelper('foo', function (arg) {

View file

@ -1,534 +0,0 @@
1,3c1,13
< describe('blocks', function() {
< it('array', function() {
< var string = '{{#goodbyes}}{{text}}! {{/goodbyes}}cruel {{world}}!';
---
> /*
> * 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('blocks', () => {
> it('array', () => {
> const string = '{{#goodbyes}}{{text}}! {{/goodbyes}}cruel {{world}}!';
7,12c17,18
< goodbyes: [
< { text: 'goodbye' },
< { text: 'Goodbye' },
< { text: 'GOODBYE' }
< ],
< world: 'world'
---
> goodbyes: [{ text: 'goodbye' }, { text: 'Goodbye' }, { text: 'GOODBYE' }],
> world: 'world',
14d19
< .withMessage('Arrays iterate over the contents when not empty')
20c25
< world: 'world'
---
> world: 'world',
22d26
< .withMessage('Arrays ignore the contents when empty')
26,29c30,31
< it('array without data', function() {
< expectTemplate(
< '{{#goodbyes}}{{text}}{{/goodbyes}} {{#goodbyes}}{{text}}{{/goodbyes}}'
< )
---
> it('array without data', () => {
> expectTemplate('{{#goodbyes}}{{text}}{{/goodbyes}} {{#goodbyes}}{{text}}{{/goodbyes}}')
31,36c33,34
< goodbyes: [
< { text: 'goodbye' },
< { text: 'Goodbye' },
< { text: 'GOODBYE' }
< ],
< world: 'world'
---
> goodbyes: [{ text: 'goodbye' }, { text: 'Goodbye' }, { text: 'GOODBYE' }],
> world: 'world',
38d35
< .withCompileOptions({ compat: false })
42,45c39,40
< it('array with @index', function() {
< expectTemplate(
< '{{#goodbyes}}{{@index}}. {{text}}! {{/goodbyes}}cruel {{world}}!'
< )
---
> it('array with @index', () => {
> expectTemplate('{{#goodbyes}}{{@index}}. {{text}}! {{/goodbyes}}cruel {{world}}!')
47,52c42,43
< goodbyes: [
< { text: 'goodbye' },
< { text: 'Goodbye' },
< { text: 'GOODBYE' }
< ],
< world: 'world'
---
> goodbyes: [{ text: 'goodbye' }, { text: 'Goodbye' }, { text: 'GOODBYE' }],
> world: 'world',
54d44
< .withMessage('The @index variable is used')
58,59c48,49
< it('empty block', function() {
< var string = '{{#goodbyes}}{{/goodbyes}}cruel {{world}}!';
---
> it('empty block', () => {
> const string = '{{#goodbyes}}{{/goodbyes}}cruel {{world}}!';
63,68c53,54
< goodbyes: [
< { text: 'goodbye' },
< { text: 'Goodbye' },
< { text: 'GOODBYE' }
< ],
< world: 'world'
---
> goodbyes: [{ text: 'goodbye' }, { text: 'Goodbye' }, { text: 'GOODBYE' }],
> world: 'world',
70d55
< .withMessage('Arrays iterate over the contents when not empty')
76c61
< world: 'world'
---
> world: 'world',
78d62
< .withMessage('Arrays ignore the contents when empty')
82c66
< it('block with complex lookup', function() {
---
> it('block with complex lookup', () => {
86,90c70
< goodbyes: [
< { text: 'goodbye' },
< { text: 'Goodbye' },
< { text: 'GOODBYE' }
< ]
---
> goodbyes: [{ text: 'goodbye' }, { text: 'Goodbye' }, { text: 'GOODBYE' }],
92,97c72
< .withMessage(
< 'Templates can access variables in contexts up the stack with relative path syntax'
< )
< .toCompileTo(
< 'goodbye cruel Alan! Goodbye cruel Alan! GOODBYE cruel Alan! '
< );
---
> .toCompileTo('goodbye cruel Alan! Goodbye cruel Alan! GOODBYE cruel Alan! ');
100c75
< it('multiple blocks with complex lookup', function() {
---
> it('multiple blocks with complex lookup', () => {
104,108c79
< goodbyes: [
< { text: 'goodbye' },
< { text: 'Goodbye' },
< { text: 'GOODBYE' }
< ]
---
> goodbyes: [{ text: 'goodbye' }, { text: 'Goodbye' }, { text: 'GOODBYE' }],
113,116c84,85
< it('block with complex lookup using nested context', function() {
< expectTemplate(
< '{{#goodbyes}}{{text}} cruel {{foo/../name}}! {{/goodbyes}}'
< ).toThrow(Error);
---
> it('block with complex lookup using nested context', () => {
> expectTemplate('{{#goodbyes}}{{text}} cruel {{foo/../name}}! {{/goodbyes}}').toThrow(Error);
119c88
< it('block with deep nested complex lookup', function() {
---
> it('block with deep nested complex lookup', () => {
125c94
< outer: [{ sibling: 'sad', inner: [{ text: 'goodbye' }] }]
---
> outer: [{ sibling: 'sad', inner: [{ text: 'goodbye' }] }],
130,133c99,100
< it('works with cached blocks', function() {
< expectTemplate(
< '{{#each person}}{{#with .}}{{first}} {{last}}{{/with}}{{/each}}'
< )
---
> it('works with cached blocks', () => {
> expectTemplate('{{#each person}}{{#with .}}{{first}} {{last}}{{/with}}{{/each}}')
138,139c105,106
< { first: 'Alan', last: 'Johnson' }
< ]
---
> { first: 'Alan', last: 'Johnson' },
> ],
144,145c111,112
< describe('inverted sections', function() {
< it('inverted sections with unset value', function() {
---
> describe('inverted sections', () => {
> it('inverted sections with unset value', () => {
148,150c115
< )
< .withMessage("Inverted section rendered when value isn't set.")
< .toCompileTo('Right On!');
---
> ).toCompileTo('Right On!');
153,156c118,119
< it('inverted section with false value', function() {
< expectTemplate(
< '{{#goodbyes}}{{this}}{{/goodbyes}}{{^goodbyes}}Right On!{{/goodbyes}}'
< )
---
> it('inverted section with false value', () => {
> expectTemplate('{{#goodbyes}}{{this}}{{/goodbyes}}{{^goodbyes}}Right On!{{/goodbyes}}')
158d120
< .withMessage('Inverted section rendered when value is false.')
162,165c124,125
< it('inverted section with empty set', function() {
< expectTemplate(
< '{{#goodbyes}}{{this}}{{/goodbyes}}{{^goodbyes}}Right On!{{/goodbyes}}'
< )
---
> it('inverted section with empty set', () => {
> expectTemplate('{{#goodbyes}}{{this}}{{/goodbyes}}{{^goodbyes}}Right On!{{/goodbyes}}')
167d126
< .withMessage('Inverted section rendered when value is empty set.')
171c130
< it('block inverted sections', function() {
---
> it('block inverted sections', () => {
177c136
< it('chained inverted sections', function() {
---
> it('chained inverted sections', () => {
188,190c147
< expectTemplate(
< '{{#people}}{{name}}{{else if none}}{{none}}{{else}}fail{{/people}}'
< )
---
> expectTemplate('{{#people}}{{name}}{{else if none}}{{none}}{{else}}fail{{/people}}')
195,198c152,153
< it('chained inverted sections with mismatch', function() {
< expectTemplate(
< '{{#people}}{{name}}{{else if none}}{{none}}{{/if}}'
< ).toThrow(Error);
---
> it('chained inverted sections with mismatch', () => {
> expectTemplate('{{#people}}{{name}}{{else if none}}{{none}}{{/if}}').toThrow(Error);
201c156
< it('block inverted sections with empty arrays', function() {
---
> it('block inverted sections with empty arrays', () => {
205c160
< people: []
---
> people: [],
211,212c166,167
< describe('standalone sections', function() {
< it('block standalone else sections', function() {
---
> describe('standalone sections', () => {
> it('block standalone else sections', () => {
226,241c181,182
< it('block standalone else sections can be disabled', function() {
< expectTemplate('{{#people}}\n{{name}}\n{{^}}\n{{none}}\n{{/people}}\n')
< .withInput({ none: 'No people' })
< .withCompileOptions({ ignoreStandalone: true })
< .toCompileTo('\nNo people\n\n');
<
< expectTemplate('{{#none}}\n{{.}}\n{{^}}\nFail\n{{/none}}\n')
< .withInput({ none: 'No people' })
< .withCompileOptions({ ignoreStandalone: true })
< .toCompileTo('\nNo people\n\n');
< });
<
< it('block standalone chained else sections', function() {
< expectTemplate(
< '{{#people}}\n{{name}}\n{{else if none}}\n{{none}}\n{{/people}}\n'
< )
---
> it('block standalone chained else sections', () => {
> expectTemplate('{{#people}}\n{{name}}\n{{else if none}}\n{{none}}\n{{/people}}\n')
245,247c186
< expectTemplate(
< '{{#people}}\n{{name}}\n{{else if none}}\n{{none}}\n{{^}}\n{{/people}}\n'
< )
---
> expectTemplate('{{#people}}\n{{name}}\n{{else if none}}\n{{none}}\n{{^}}\n{{/people}}\n')
252c191
< it('should handle nesting', function() {
---
> it('should handle nesting', () => {
255c194
< data: [1, 3, 5]
---
> data: [1, 3, 5],
261,297c200,201
< describe('compat mode', function() {
< it('block with deep recursive lookup lookup', function() {
< expectTemplate(
< '{{#outer}}Goodbye {{#inner}}cruel {{omg}}{{/inner}}{{/outer}}'
< )
< .withInput({ omg: 'OMG!', outer: [{ inner: [{ text: 'goodbye' }] }] })
< .withCompileOptions({ compat: true })
< .toCompileTo('Goodbye cruel OMG!');
< });
<
< it('block with deep recursive pathed lookup', function() {
< expectTemplate(
< '{{#outer}}Goodbye {{#inner}}cruel {{omg.yes}}{{/inner}}{{/outer}}'
< )
< .withInput({
< omg: { yes: 'OMG!' },
< outer: [{ inner: [{ yes: 'no', text: 'goodbye' }] }]
< })
< .withCompileOptions({ compat: true })
< .toCompileTo('Goodbye cruel OMG!');
< });
<
< it('block with missed recursive lookup', function() {
< expectTemplate(
< '{{#outer}}Goodbye {{#inner}}cruel {{omg.yes}}{{/inner}}{{/outer}}'
< )
< .withInput({
< omg: { no: 'OMG!' },
< outer: [{ inner: [{ yes: 'no', text: 'goodbye' }] }]
< })
< .withCompileOptions({ compat: true })
< .toCompileTo('Goodbye cruel ');
< });
< });
<
< describe('decorators', function() {
< it('should apply mustache decorators', function() {
---
> describe('decorators', () => {
> it('should apply mustache decorators', () => {
299,300c203,204
< .withHelper('helper', function(options) {
< return options.fn.run;
---
> .withHelper('helper', function (options: Handlebars.HelperOptions) {
> return (options.fn as any).run;
302,303c206,207
< .withDecorator('decorator', function(fn) {
< fn.run = 'success';
---
> .withDecorator('decorator', function (fn) {
> (fn as any).run = 'success';
309c213
< it('should apply allow undefined return', function() {
---
> it('should apply allow undefined return', () => {
311,312c215,216
< .withHelper('helper', function(options) {
< return options.fn() + options.fn.run;
---
> .withHelper('helper', function (options: Handlebars.HelperOptions) {
> return options.fn() + (options.fn as any).run;
314,315c218,219
< .withDecorator('decorator', function(fn) {
< fn.run = 'cess';
---
> .withDecorator('decorator', function (fn) {
> (fn as any).run = 'cess';
320,325c224,227
< it('should apply block decorators', function() {
< expectTemplate(
< '{{#helper}}{{#*decorator}}success{{/decorator}}{{/helper}}'
< )
< .withHelper('helper', function(options) {
< return options.fn.run;
---
> it('should apply block decorators', () => {
> expectTemplate('{{#helper}}{{#*decorator}}success{{/decorator}}{{/helper}}')
> .withHelper('helper', function (options: Handlebars.HelperOptions) {
> return (options.fn as any).run;
327,328c229,230
< .withDecorator('decorator', function(fn, props, container, options) {
< fn.run = options.fn();
---
> .withDecorator('decorator', function (fn, props, container, options) {
> (fn as any).run = options.fn();
334c236
< it('should support nested decorators', function() {
---
> it('should support nested decorators', () => {
338,339c240,241
< .withHelper('helper', function(options) {
< return options.fn.run;
---
> .withHelper('helper', function (options: Handlebars.HelperOptions) {
> return (options.fn as any).run;
342,343c244,245
< decorator: function(fn, props, container, options) {
< fn.run = options.fn.nested + options.fn();
---
> decorator(fn, props, container, options) {
> (fn as any).run = options.fn.nested + options.fn();
346c248
< nested: function(fn, props, container, options) {
---
> nested(fn, props, container, options) {
348c250
< }
---
> },
353c255
< it('should apply multiple decorators', function() {
---
> it('should apply multiple decorators', () => {
357,358c259,260
< .withHelper('helper', function(options) {
< return options.fn.run;
---
> .withHelper('helper', function (options: Handlebars.HelperOptions) {
> return (options.fn as any).run;
360,361c262,263
< .withDecorator('decorator', function(fn, props, container, options) {
< fn.run = (fn.run || '') + options.fn();
---
> .withDecorator('decorator', function (fn, props, container, options) {
> (fn as any).run = ((fn as any).run || '') + options.fn();
367c269
< it('should access parent variables', function() {
---
> it('should access parent variables', () => {
369,370c271,272
< .withHelper('helper', function(options) {
< return options.fn.run;
---
> .withHelper('helper', function (options: Handlebars.HelperOptions) {
> return (options.fn as any).run;
372,373c274,275
< .withDecorator('decorator', function(fn, props, container, options) {
< fn.run = options.args;
---
> .withDecorator('decorator', function (fn, props, container, options) {
> (fn as any).run = options.args;
380,381c282,283
< it('should work with root program', function() {
< var run;
---
> it('should work with root program', () => {
> let run;
383,384c285,286
< .withDecorator('decorator', function(fn, props, container, options) {
< equals(options.args[0], 'success');
---
> .withDecorator('decorator', function (fn, props, container, options) {
> expect(options.args[0]).toEqual('success');
390c292
< equals(run, true);
---
> expect(run).toEqual(true);
393,394c295,296
< it('should fail when accessing variables from root', function() {
< var run;
---
> it('should fail when accessing variables from root', () => {
> let run;
396,397c298,299
< .withDecorator('decorator', function(fn, props, container, options) {
< equals(options.args[0], undefined);
---
> .withDecorator('decorator', function (fn, props, container, options) {
> expect(options.args[0]).toBeUndefined();
403c305
< equals(run, true);
---
> expect(run).toEqual(true);
406,408c308,311
< describe('registration', function() {
< it('unregisters', function() {
< handlebarsEnv.decorators = {};
---
> describe('registration', () => {
> beforeEach(() => {
> global.kbnHandlebarsEnv = Handlebars.create();
> });
410c313,321
< handlebarsEnv.registerDecorator('foo', function() {
---
> 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 () {
414,416c325,327
< equals(!!handlebarsEnv.decorators.foo, true);
< handlebarsEnv.unregisterDecorator('foo');
< equals(handlebarsEnv.decorators.foo, undefined);
---
> expect(!!kbnHandlebarsEnv!.decorators.foo).toEqual(true);
> kbnHandlebarsEnv!.unregisterDecorator('foo');
> expect(kbnHandlebarsEnv!.decorators.foo).toBeUndefined();
419,420c330,332
< it('allows multiple globals', function() {
< handlebarsEnv.decorators = {};
---
> it('allows multiple globals', () => {
> // @ts-expect-error: Cannot assign to 'decorators' because it is a read-only property.
> kbnHandlebarsEnv!.decorators = {};
422,424c334,337
< handlebarsEnv.registerDecorator({
< foo: function() {},
< bar: function() {}
---
> // @ts-expect-error: Expected 2 arguments, but got 1.
> kbnHandlebarsEnv!.registerDecorator({
> foo() {},
> bar() {},
427,432c340,345
< equals(!!handlebarsEnv.decorators.foo, true);
< equals(!!handlebarsEnv.decorators.bar, true);
< handlebarsEnv.unregisterDecorator('foo');
< handlebarsEnv.unregisterDecorator('bar');
< equals(handlebarsEnv.decorators.foo, undefined);
< equals(handlebarsEnv.decorators.bar, undefined);
---
> 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();
435,445c348,354
< it('fails with multiple and args', function() {
< shouldThrow(
< function() {
< handlebarsEnv.registerDecorator(
< {
< world: function() {
< return 'world!';
< },
< testHelper: function() {
< return 'found it!';
< }
---
> 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!';
447,452c356,362
< {}
< );
< },
< Error,
< 'Arg not supported with multiple decorators'
< );
---
> testHelper() {
> return 'found it!';
> },
> },
> {}
> );
> }).toThrow('Arg not supported with multiple decorators');

View file

@ -1,937 +0,0 @@
1,4c1,16
< describe('builtin helpers', function() {
< describe('#if', function() {
< it('if', function() {
< var string = '{{#if goodbye}}GOODBYE {{/if}}cruel {{world}}!';
---
> /*
> * 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}}!';
9c21
< world: 'world'
---
> world: 'world',
11d22
< .withMessage('if with boolean argument shows the contents when true')
17c28
< world: 'world'
---
> world: 'world',
19d29
< .withMessage('if with string argument shows the contents')
25c35
< world: 'world'
---
> world: 'world',
27,29d36
< .withMessage(
< 'if with boolean argument does not show the contents when false'
< )
32,35c39
< expectTemplate(string)
< .withInput({ world: 'world' })
< .withMessage('if with undefined does not show the contents')
< .toCompileTo('cruel world!');
---
> expectTemplate(string).withInput({ world: 'world' }).toCompileTo('cruel world!');
40c44
< world: 'world'
---
> world: 'world',
42d45
< .withMessage('if with non-empty array shows the contents')
48c51
< world: 'world'
---
> world: 'world',
50d52
< .withMessage('if with empty array does not show the contents')
56c58
< world: 'world'
---
> world: 'world',
58d59
< .withMessage('if with zero does not show the contents')
61,63c62
< expectTemplate(
< '{{#if goodbye includeZero=true}}GOODBYE {{/if}}cruel {{world}}!'
< )
---
> expectTemplate('{{#if goodbye includeZero=true}}GOODBYE {{/if}}cruel {{world}}!')
66c65
< world: 'world'
---
> world: 'world',
68d66
< .withMessage('if with zero does not show the contents')
72,73c70,71
< it('if with function argument', function() {
< var string = '{{#if goodbye}}GOODBYE {{/if}}cruel {{world}}!';
---
> it('if with function argument', () => {
> const string = '{{#if goodbye}}GOODBYE {{/if}}cruel {{world}}!';
77c75
< goodbye: function() {
---
> goodbye() {
80c78
< world: 'world'
---
> world: 'world',
82,84d79
< .withMessage(
< 'if with function shows the contents when function returns true'
< )
89c84
< goodbye: function() {
---
> goodbye() {
92c87
< world: 'world'
---
> world: 'world',
94,96d88
< .withMessage(
< 'if with function shows the contents when function returns string'
< )
101c93
< goodbye: function() {
---
> goodbye() {
104c96
< world: 'world'
---
> world: 'world',
106,108d97
< .withMessage(
< 'if with function does not show the contents when returns false'
< )
113c102
< goodbye: function() {
---
> goodbye() {
116c105
< world: 'world'
---
> world: 'world',
118,120d106
< .withMessage(
< 'if with function does not show the contents when returns undefined'
< )
124,127c110,111
< it('should not change the depth list', function() {
< expectTemplate(
< '{{#with foo}}{{#if goodbye}}GOODBYE cruel {{../world}}!{{/if}}{{/with}}'
< )
---
> it('should not change the depth list', () => {
> expectTemplate('{{#with foo}}{{#if goodbye}}GOODBYE cruel {{../world}}!{{/if}}{{/with}}')
130c114
< world: 'world'
---
> world: 'world',
136,137c120,121
< describe('#with', function() {
< it('with', function() {
---
> describe('#with', () => {
> it('with', () => {
142,143c126,127
< last: 'Johnson'
< }
---
> last: 'Johnson',
> },
148c132
< it('with with function argument', function() {
---
> it('with with function argument', () => {
151c135
< person: function() {
---
> person() {
154c138
< last: 'Johnson'
---
> last: 'Johnson',
156c140
< }
---
> },
161c145
< it('with with else', function() {
---
> it('with with else', () => {
167c151
< it('with provides block parameter', function() {
---
> it('with provides block parameter', () => {
172,173c156,157
< last: 'Johnson'
< }
---
> last: 'Johnson',
> },
178c162
< it('works when data is disabled', function() {
---
> it('works when data is disabled', () => {
186,194c170,172
< describe('#each', function() {
< beforeEach(function() {
< handlebarsEnv.registerHelper('detectDataInsideEach', function(options) {
< return options.data && options.data.exclaim;
< });
< });
<
< it('each', function() {
< var string = '{{#each goodbyes}}{{text}}! {{/each}}cruel {{world}}!';
---
> describe('#each', () => {
> it('each', () => {
> const string = '{{#each goodbyes}}{{text}}! {{/each}}cruel {{world}}!';
198,203c176,177
< goodbyes: [
< { text: 'goodbye' },
< { text: 'Goodbye' },
< { text: 'GOODBYE' }
< ],
< world: 'world'
---
> goodbyes: [{ text: 'goodbye' }, { text: 'Goodbye' }, { text: 'GOODBYE' }],
> world: 'world',
205,207d178
< .withMessage(
< 'each with array argument iterates over the contents when not empty'
< )
213c184
< world: 'world'
---
> world: 'world',
215d185
< .withMessage('each with array argument ignores the contents when empty')
219c189
< it('each without data', function() {
---
> it('each without data', () => {
222,227c192,193
< goodbyes: [
< { text: 'goodbye' },
< { text: 'Goodbye' },
< { text: 'GOODBYE' }
< ],
< world: 'world'
---
> goodbyes: [{ text: 'goodbye' }, { text: 'Goodbye' }, { text: 'GOODBYE' }],
> world: 'world',
240c206
< it('each without context', function() {
---
> it('each without context', () => {
246,248c212,213
< it('each with an object and @key', function() {
< var string =
< '{{#each goodbyes}}{{@key}}. {{text}}! {{/each}}cruel {{world}}!';
---
> it('each with an object and @key', () => {
> const string = '{{#each goodbyes}}{{@key}}. {{text}}! {{/each}}cruel {{world}}!';
250c215
< function Clazz() {
---
> function Clazz(this: any) {
255c220
< var hash = { goodbyes: new Clazz(), world: 'world' };
---
> const hash = { goodbyes: new (Clazz as any)(), world: 'world' };
260,270c225,233
< var actual = compileWithPartials(string, hash);
< var expected1 =
< '&lt;b&gt;#1&lt;/b&gt;. goodbye! 2. GOODBYE! cruel world!';
< var expected2 =
< '2. GOODBYE! &lt;b&gt;#1&lt;/b&gt;. goodbye! cruel world!';
<
< equals(
< actual === expected1 || actual === expected2,
< true,
< 'each with object argument iterates over the contents when not empty'
< );
---
> 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!');
> }
275c238
< world: 'world'
---
> world: 'world',
280,283c243,244
< it('each with @index', function() {
< expectTemplate(
< '{{#each goodbyes}}{{@index}}. {{text}}! {{/each}}cruel {{world}}!'
< )
---
> it('each with @index', () => {
> expectTemplate('{{#each goodbyes}}{{@index}}. {{text}}! {{/each}}cruel {{world}}!')
285,290c246,247
< goodbyes: [
< { text: 'goodbye' },
< { text: 'Goodbye' },
< { text: 'GOODBYE' }
< ],
< world: 'world'
---
> goodbyes: [{ text: 'goodbye' }, { text: 'Goodbye' }, { text: 'GOODBYE' }],
> world: 'world',
292d248
< .withMessage('The @index variable is used')
296c252
< it('each with nested @index', function() {
---
> it('each with nested @index', () => {
301,306c257,258
< goodbyes: [
< { text: 'goodbye' },
< { text: 'Goodbye' },
< { text: 'GOODBYE' }
< ],
< world: 'world'
---
> goodbyes: [{ text: 'goodbye' }, { text: 'Goodbye' }, { text: 'GOODBYE' }],
> world: 'world',
308d259
< .withMessage('The @index variable is used')
314c265
< it('each with block params', function() {
---
> it('each with block params', () => {
320c271
< world: 'world'
---
> world: 'world',
322,324c273
< .toCompileTo(
< '0. goodbye! 0 0 0 1 After 0 1. Goodbye! 1 0 1 1 After 1 cruel world!'
< );
---
> .toCompileTo('0. goodbye! 0 0 0 1 After 0 1. Goodbye! 1 0 1 1 After 1 cruel world!');
327,330c276,284
< it('each with block params and strict compilation', function() {
< expectTemplate(
< '{{#each goodbyes as |value index|}}{{index}}. {{value.text}}!{{/each}}'
< )
---
> // 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}}')
336,339c290,291
< it('each object with @index', function() {
< expectTemplate(
< '{{#each goodbyes}}{{@index}}. {{text}}! {{/each}}cruel {{world}}!'
< )
---
> it('each object with @index', () => {
> expectTemplate('{{#each goodbyes}}{{@index}}. {{text}}! {{/each}}cruel {{world}}!')
344c296
< c: { text: 'GOODBYE' }
---
> c: { text: 'GOODBYE' },
346c298
< world: 'world'
---
> world: 'world',
348d299
< .withMessage('The @index variable is used')
352,355c303,304
< it('each with @first', function() {
< expectTemplate(
< '{{#each goodbyes}}{{#if @first}}{{text}}! {{/if}}{{/each}}cruel {{world}}!'
< )
---
> it('each with @first', () => {
> expectTemplate('{{#each goodbyes}}{{#if @first}}{{text}}! {{/if}}{{/each}}cruel {{world}}!')
357,362c306,307
< goodbyes: [
< { text: 'goodbye' },
< { text: 'Goodbye' },
< { text: 'GOODBYE' }
< ],
< world: 'world'
---
> goodbyes: [{ text: 'goodbye' }, { text: 'Goodbye' }, { text: 'GOODBYE' }],
> world: 'world',
364d308
< .withMessage('The @first variable is used')
368c312
< it('each with nested @first', function() {
---
> it('each with nested @first', () => {
373,378c317,318
< goodbyes: [
< { text: 'goodbye' },
< { text: 'Goodbye' },
< { text: 'GOODBYE' }
< ],
< world: 'world'
---
> goodbyes: [{ text: 'goodbye' }, { text: 'Goodbye' }, { text: 'GOODBYE' }],
> world: 'world',
380,383c320
< .withMessage('The @first variable is used')
< .toCompileTo(
< '(goodbye! goodbye! goodbye!) (goodbye!) (goodbye!) cruel world!'
< );
---
> .toCompileTo('(goodbye! goodbye! goodbye!) (goodbye!) (goodbye!) cruel world!');
386,389c323,324
< it('each object with @first', function() {
< expectTemplate(
< '{{#each goodbyes}}{{#if @first}}{{text}}! {{/if}}{{/each}}cruel {{world}}!'
< )
---
> it('each object with @first', () => {
> expectTemplate('{{#each goodbyes}}{{#if @first}}{{text}}! {{/if}}{{/each}}cruel {{world}}!')
392c327
< world: 'world'
---
> world: 'world',
394d328
< .withMessage('The @first variable is used')
398,401c332,333
< it('each with @last', function() {
< expectTemplate(
< '{{#each goodbyes}}{{#if @last}}{{text}}! {{/if}}{{/each}}cruel {{world}}!'
< )
---
> it('each with @last', () => {
> expectTemplate('{{#each goodbyes}}{{#if @last}}{{text}}! {{/if}}{{/each}}cruel {{world}}!')
403,408c335,336
< goodbyes: [
< { text: 'goodbye' },
< { text: 'Goodbye' },
< { text: 'GOODBYE' }
< ],
< world: 'world'
---
> goodbyes: [{ text: 'goodbye' }, { text: 'Goodbye' }, { text: 'GOODBYE' }],
> world: 'world',
410d337
< .withMessage('The @last variable is used')
414,417c341,342
< it('each object with @last', function() {
< expectTemplate(
< '{{#each goodbyes}}{{#if @last}}{{text}}! {{/if}}{{/each}}cruel {{world}}!'
< )
---
> it('each object with @last', () => {
> expectTemplate('{{#each goodbyes}}{{#if @last}}{{text}}! {{/if}}{{/each}}cruel {{world}}!')
420c345
< world: 'world'
---
> world: 'world',
422d346
< .withMessage('The @last variable is used')
426c350
< it('each with nested @last', function() {
---
> it('each with nested @last', () => {
431,436c355,356
< goodbyes: [
< { text: 'goodbye' },
< { text: 'Goodbye' },
< { text: 'GOODBYE' }
< ],
< world: 'world'
---
> goodbyes: [{ text: 'goodbye' }, { text: 'Goodbye' }, { text: 'GOODBYE' }],
> world: 'world',
438,441c358
< .withMessage('The @last variable is used')
< .toCompileTo(
< '(GOODBYE!) (GOODBYE!) (GOODBYE! GOODBYE! GOODBYE!) cruel world!'
< );
---
> .toCompileTo('(GOODBYE!) (GOODBYE!) (GOODBYE! GOODBYE! GOODBYE!) cruel world!');
444,445c361,362
< it('each with function argument', function() {
< var string = '{{#each goodbyes}}{{text}}! {{/each}}cruel {{world}}!';
---
> it('each with function argument', () => {
> const string = '{{#each goodbyes}}{{text}}! {{/each}}cruel {{world}}!';
449,454c366,367
< goodbyes: function() {
< return [
< { text: 'goodbye' },
< { text: 'Goodbye' },
< { text: 'GOODBYE' }
< ];
---
> goodbyes() {
> return [{ text: 'goodbye' }, { text: 'Goodbye' }, { text: 'GOODBYE' }];
456c369
< world: 'world'
---
> world: 'world',
458,460d370
< .withMessage(
< 'each with array function argument iterates over the contents when not empty'
< )
466c376
< world: 'world'
---
> world: 'world',
468,470d377
< .withMessage(
< 'each with array function argument ignores the contents when empty'
< )
474,477c381,382
< it('each object when last key is an empty string', function() {
< expectTemplate(
< '{{#each goodbyes}}{{@index}}. {{text}}! {{/each}}cruel {{world}}!'
< )
---
> it('each object when last key is an empty string', () => {
> expectTemplate('{{#each goodbyes}}{{@index}}. {{text}}! {{/each}}cruel {{world}}!')
482c387
< '': { text: 'GOODBYE' }
---
> '': { text: 'GOODBYE' },
484c389
< world: 'world'
---
> world: 'world',
486d390
< .withMessage('Empty string key is not skipped')
490,493c394,395
< it('data passed to helpers', function() {
< expectTemplate(
< '{{#each letters}}{{this}}{{detectDataInsideEach}}{{/each}}'
< )
---
> it('data passed to helpers', () => {
> expectTemplate('{{#each letters}}{{this}}{{detectDataInsideEach}}{{/each}}')
495c397,399
< .withMessage('should output data')
---
> .withHelper('detectDataInsideEach', function (options) {
> return options.data && options.data.exclaim;
> })
498,499c402,403
< exclaim: '!'
< }
---
> exclaim: '!',
> },
504,508c408,409
< it('each on implicit context', function() {
< expectTemplate('{{#each}}{{text}}! {{/each}}cruel world!').toThrow(
< handlebarsEnv.Exception,
< 'Must pass iterator to #each'
< );
---
> it('each on implicit context', () => {
> expectTemplate('{{#each}}{{text}}! {{/each}}cruel world!').toThrow(Handlebars.Exception);
511,513c412,417
< if (global.Symbol && global.Symbol.iterator) {
< it('each on iterable', function() {
< function Iterator(arr) {
---
> it('each on iterable', () => {
> class Iterator {
> private arr: any[];
> private index: number = 0;
>
> constructor(arr: any[]) {
515d418
< this.index = 0;
517,519c420,423
< Iterator.prototype.next = function() {
< var value = this.arr[this.index];
< var done = this.index === this.arr.length;
---
>
> next() {
> const value = this.arr[this.index];
> const done = this.index === this.arr.length;
523,525c427,434
< return { value: value, done: done };
< };
< function Iterable(arr) {
---
> return { value, done };
> }
> }
>
> class Iterable {
> private arr: any[];
>
> constructor(arr: any[]) {
528c437,438
< Iterable.prototype[global.Symbol.iterator] = function() {
---
>
> [Symbol.iterator]() {
530,531c440,441
< };
< var string = '{{#each goodbyes}}{{text}}! {{/each}}cruel {{world}}!';
---
> }
> }
533,545c443
< expectTemplate(string)
< .withInput({
< goodbyes: new Iterable([
< { text: 'goodbye' },
< { text: 'Goodbye' },
< { text: 'GOODBYE' }
< ]),
< world: 'world'
< })
< .withMessage(
< 'each with array argument iterates over the contents when not empty'
< )
< .toCompileTo('goodbye! Goodbye! GOODBYE! cruel world!');
---
> const string = '{{#each goodbyes}}{{text}}! {{/each}}cruel {{world}}!';
547,557c445,458
< expectTemplate(string)
< .withInput({
< goodbyes: new Iterable([]),
< world: 'world'
< })
< .withMessage(
< 'each with array argument ignores the contents when empty'
< )
< .toCompileTo('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!');
> });
560c461
< describe('#log', function() {
---
> describe('#log', function () {
562,564c463,465
< if (typeof console === 'undefined') {
< return;
< }
---
> let $log: typeof console.log;
> let $info: typeof console.info;
> let $error: typeof console.error;
566,567c467
< var $log, $info, $error;
< beforeEach(function() {
---
> beforeEach(function () {
570a471,472
>
> global.kbnHandlebarsEnv = Handlebars.create();
572c474,475
< afterEach(function() {
---
>
> afterEach(function () {
575a479,480
>
> global.kbnHandlebarsEnv = null;
578,580c483,486
< it('should call logger at default level', function() {
< var levelArg, logArg;
< handlebarsEnv.log = function(level, arg) {
---
> it('should call logger at default level', function () {
> let levelArg;
> let logArg;
> kbnHandlebarsEnv!.log = function (level, arg) {
585,590c491,493
< expectTemplate('{{log blah}}')
< .withInput({ blah: 'whee' })
< .withMessage('log should not display')
< .toCompileTo('');
< equals(1, levelArg, 'should call log with 1');
< equals('whee', logArg, "should call log with 'whee'");
---
> expectTemplate('{{log blah}}').withInput({ blah: 'whee' }).toCompileTo('');
> expect(1).toEqual(levelArg);
> expect('whee').toEqual(logArg);
593,595c496,499
< it('should call logger at data level', function() {
< var levelArg, logArg;
< handlebarsEnv.log = function(level, arg) {
---
> it('should call logger at data level', function () {
> let levelArg;
> let logArg;
> kbnHandlebarsEnv!.log = function (level, arg) {
605,606c509,510
< equals('03', levelArg);
< equals('whee', logArg);
---
> expect('03').toEqual(levelArg);
> expect('whee').toEqual(logArg);
609,610c513,515
< it('should output to info', function() {
< var called;
---
> it('should output to info', function () {
> let calls = 0;
> const callsExpected = process.env.AST || process.env.EVAL ? 1 : 2;
612,616c517,523
< console.info = function(info) {
< equals('whee', info);
< called = true;
< console.info = $info;
< console.log = $log;
---
> console.info = function (info) {
> expect('whee').toEqual(info);
> calls++;
> if (calls === callsExpected) {
> console.info = $info;
> console.log = $log;
> }
618,622c525,531
< console.log = function(log) {
< equals('whee', log);
< called = true;
< console.info = $info;
< console.log = $log;
---
> console.log = function (log) {
> expect('whee').toEqual(log);
> calls++;
> if (calls === callsExpected) {
> console.info = $info;
> console.log = $log;
> }
625,628c534,535
< expectTemplate('{{log blah}}')
< .withInput({ blah: 'whee' })
< .toCompileTo('');
< equals(true, called);
---
> expectTemplate('{{log blah}}').withInput({ blah: 'whee' }).toCompileTo('');
> expect(calls).toEqual(callsExpected);
631,632c538,540
< it('should log at data level', function() {
< var called;
---
> it('should log at data level', function () {
> let calls = 0;
> const callsExpected = process.env.AST || process.env.EVAL ? 1 : 2;
634,637c542,545
< console.error = function(log) {
< equals('whee', log);
< called = true;
< console.error = $error;
---
> console.error = function (log) {
> expect('whee').toEqual(log);
> calls++;
> if (calls === callsExpected) console.error = $error;
645c553
< equals(true, called);
---
> expect(calls).toEqual(callsExpected);
648,649c556,558
< it('should handle missing logger', function() {
< var called = false;
---
> it('should handle missing logger', function () {
> let calls = 0;
> const callsExpected = process.env.AST || process.env.EVAL ? 1 : 2;
650a560
> // @ts-expect-error
652,655c562,565
< console.log = function(log) {
< equals('whee', log);
< called = true;
< console.log = $log;
---
> console.log = function (log) {
> expect('whee').toEqual(log);
> calls++;
> if (calls === callsExpected) console.log = $log;
663c573
< equals(true, called);
---
> expect(calls).toEqual(callsExpected);
666,667c576,578
< it('should handle string log levels', function() {
< var called;
---
> it('should handle string log levels', function () {
> let calls = 0;
> const callsExpected = process.env.AST || process.env.EVAL ? 1 : 2;
669,671c580,582
< console.error = function(log) {
< equals('whee', log);
< called = true;
---
> console.error = function (log) {
> expect('whee').toEqual(log);
> calls++;
679c590
< equals(true, called);
---
> expect(calls).toEqual(callsExpected);
681c592
< called = false;
---
> calls = 0;
688c599
< equals(true, called);
---
> expect(calls).toEqual(callsExpected);
691,692c602,604
< it('should handle hash log levels', function() {
< var called;
---
> it('should handle hash log levels [1]', function () {
> let calls = 0;
> const callsExpected = process.env.AST || process.env.EVAL ? 1 : 2;
694,696c606,608
< console.error = function(log) {
< equals('whee', log);
< called = true;
---
> console.error = function (log) {
> expect('whee').toEqual(log);
> calls++;
699,702c611,612
< expectTemplate('{{log blah level="error"}}')
< .withInput({ blah: 'whee' })
< .toCompileTo('');
< equals(true, called);
---
> expectTemplate('{{log blah level="error"}}').withInput({ blah: 'whee' }).toCompileTo('');
> expect(calls).toEqual(callsExpected);
705,706c615,616
< it('should handle hash log levels', function() {
< var called = false;
---
> it('should handle hash log levels [2]', function () {
> let called = false;
708,711c618,625
< console.info = console.log = console.error = console.debug = function() {
< called = true;
< console.info = console.log = console.error = console.debug = $log;
< };
---
> console.info =
> console.log =
> console.error =
> console.debug =
> function () {
> called = true;
> console.info = console.log = console.error = console.debug = $log;
> };
713,716c627,628
< expectTemplate('{{log blah level="debug"}}')
< .withInput({ blah: 'whee' })
< .toCompileTo('');
< equals(false, called);
---
> expectTemplate('{{log blah level="debug"}}').withInput({ blah: 'whee' }).toCompileTo('');
> expect(false).toEqual(called);
719,720c631,633
< it('should pass multiple log arguments', function() {
< var called;
---
> it('should pass multiple log arguments', function () {
> let calls = 0;
> const callsExpected = process.env.AST || process.env.EVAL ? 1 : 2;
722,727c635,640
< console.info = console.log = function(log1, log2, log3) {
< equals('whee', log1);
< equals('foo', log2);
< equals(1, log3);
< called = true;
< console.log = $log;
---
> 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;
730,733c643,644
< expectTemplate('{{log blah "foo" 1}}')
< .withInput({ blah: 'whee' })
< .toCompileTo('');
< equals(true, called);
---
> expectTemplate('{{log blah "foo" 1}}').withInput({ blah: 'whee' }).toCompileTo('');
> expect(calls).toEqual(callsExpected);
736,737c647,649
< it('should pass zero log arguments', function() {
< var called;
---
> it('should pass zero log arguments', function () {
> let calls = 0;
> const callsExpected = process.env.AST || process.env.EVAL ? 1 : 2;
739,742c651,654
< console.info = console.log = function() {
< expect(arguments.length).to.equal(0);
< called = true;
< console.log = $log;
---
> console.info = console.log = function () {
> expect(arguments.length).toEqual(0);
> calls++;
> if (calls === callsExpected) console.log = $log;
745,748c657,658
< expectTemplate('{{log}}')
< .withInput({ blah: 'whee' })
< .toCompileTo('');
< expect(called).to.be.true();
---
> expectTemplate('{{log}}').withInput({ blah: 'whee' }).toCompileTo('');
> expect(calls).toEqual(callsExpected);
753,754c663,664
< describe('#lookup', function() {
< it('should lookup arbitrary content', function() {
---
> describe('#lookup', () => {
> it('should lookup arbitrary content', () => {
760c670
< it('should not fail on undefined value', function() {
---
> it('should not fail on undefined value', () => {

View file

@ -1,269 +0,0 @@
1,10c1,6
< describe('compiler', function() {
< if (!Handlebars.compile) {
< return;
< }
<
< describe('#equals', function() {
< function compile(string) {
< var ast = Handlebars.parse(string);
< return new Handlebars.Compiler().compile(ast, {});
< }
---
> /*
> * 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.
> */
12,60c8,9
< it('should treat as equal', function() {
< equal(compile('foo').equals(compile('foo')), true);
< equal(compile('{{foo}}').equals(compile('{{foo}}')), true);
< equal(compile('{{foo.bar}}').equals(compile('{{foo.bar}}')), true);
< equal(
< compile('{{foo.bar baz "foo" true false bat=1}}').equals(
< compile('{{foo.bar baz "foo" true false bat=1}}')
< ),
< true
< );
< equal(
< compile('{{foo.bar (baz bat=1)}}').equals(
< compile('{{foo.bar (baz bat=1)}}')
< ),
< true
< );
< equal(
< compile('{{#foo}} {{/foo}}').equals(compile('{{#foo}} {{/foo}}')),
< true
< );
< });
< it('should treat as not equal', function() {
< equal(compile('foo').equals(compile('bar')), false);
< equal(compile('{{foo}}').equals(compile('{{bar}}')), false);
< equal(compile('{{foo.bar}}').equals(compile('{{bar.bar}}')), false);
< equal(
< compile('{{foo.bar baz bat=1}}').equals(
< compile('{{foo.bar bar bat=1}}')
< ),
< false
< );
< equal(
< compile('{{foo.bar (baz bat=1)}}').equals(
< compile('{{foo.bar (bar bat=1)}}')
< ),
< false
< );
< equal(
< compile('{{#foo}} {{/foo}}').equals(compile('{{#bar}} {{/bar}}')),
< false
< );
< equal(
< compile('{{#foo}} {{/foo}}').equals(
< compile('{{#foo}} {{foo}}{{/foo}}')
< ),
< false
< );
< });
< });
---
> import Handlebars from '../..';
> import { forEachCompileFunctionName } from '../__jest__/test_bench';
62,78c11,13
< describe('#compile', function() {
< it('should fail with invalid input', function() {
< shouldThrow(
< function() {
< Handlebars.compile(null);
< },
< Error,
< 'You must pass a string or Handlebars AST to Handlebars.compile. You passed null'
< );
< shouldThrow(
< function() {
< Handlebars.compile({});
< },
< Error,
< 'You must pass a string or Handlebars AST to Handlebars.compile. You passed [object Object]'
< );
< });
---
> describe('compiler', () => {
> forEachCompileFunctionName((compileName) => {
> const compile = Handlebars[compileName].bind(Handlebars);
80,92c15,20
< it('should include the location in the error (row and column)', function() {
< try {
< Handlebars.compile(' \n {{#if}}\n{{/def}}')();
< equal(
< true,
< false,
< 'Statement must throw exception. This line should not be executed.'
< );
< } catch (err) {
< equal(
< err.message,
< "if doesn't match def - 2:5",
< 'Checking error message'
---
> 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`
94,102d21
< 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.
< equal(err.column, 5, 'Checking error column');
< }
< equal(err.lineNumber, 2, 'Checking error row');
< }
< });
104,116c23,26
< it('should include the location as enumerable property', function() {
< try {
< Handlebars.compile(' \n {{#if}}\n{{/def}}')();
< equal(
< true,
< false,
< 'Statement must throw exception. This line should not be executed.'
< );
< } catch (err) {
< equal(
< Object.prototype.propertyIsEnumerable.call(err, 'column'),
< true,
< 'Checking error column'
---
> expect(function () {
> compile({});
> }).toThrow(
> `You must pass a string or Handlebars AST to Handlebars.${compileName}. You passed [object Object]`
118,129c28
< }
< });
<
< it('can utilize AST instance', function() {
< equal(
< Handlebars.compile({
< type: 'Program',
< body: [{ type: 'ContentStatement', value: 'Hello' }]
< })(),
< 'Hello'
< );
< });
---
> });
131,133c30,44
< it('can pass through an empty string', function() {
< equal(Handlebars.compile('')(), '');
< });
---
> 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);
> }
> });
135,142c46,53
< it('should not modify the options.data property(GH-1327)', function() {
< var options = { data: [{ a: 'foo' }, { a: 'bar' }] };
< Handlebars.compile('{{#each data}}{{@index}}:{{a}} {{/each}}', options)();
< equal(
< JSON.stringify(options, 0, 2),
< JSON.stringify({ data: [{ a: 'foo' }, { a: 'bar' }] }, 0, 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);
> }
> });
144,152c55,62
< it('should not modify the options.knownHelpers property(GH-1327)', function() {
< var options = { knownHelpers: {} };
< Handlebars.compile('{{#each data}}{{@index}}:{{a}} {{/each}}', options)();
< equal(
< JSON.stringify(options, 0, 2),
< JSON.stringify({ knownHelpers: {} }, 0, 2)
< );
< });
< });
---
> it('can utilize AST instance', () => {
> expect(
> compile({
> type: 'Program',
> body: [{ type: 'ContentStatement', value: 'Hello' }],
> })()
> ).toEqual('Hello');
> });
154,170c64,66
< describe('#precompile', function() {
< it('should fail with invalid input', function() {
< shouldThrow(
< function() {
< Handlebars.precompile(null);
< },
< Error,
< 'You must pass a string or Handlebars AST to Handlebars.precompile. You passed null'
< );
< shouldThrow(
< function() {
< Handlebars.precompile({});
< },
< Error,
< 'You must pass a string or Handlebars AST to Handlebars.precompile. You passed [object Object]'
< );
< });
---
> it('can pass through an empty string', () => {
> expect(compile('')()).toEqual('');
> });
172,182c68,75
< it('can utilize AST instance', function() {
< equal(
< /return "Hello"/.test(
< Handlebars.precompile({
< type: 'Program',
< body: [{ type: 'ContentStatement', value: 'Hello' }]
< })
< ),
< true
< );
< });
---
> 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)
> );
> });
184,185c77,83
< it('can pass through an empty string', function() {
< equal(/return ""/.test(Handlebars.precompile('')), true);
---
> 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)
> );
> });

View file

@ -1,276 +0,0 @@
1,2c1,12
< describe('data', function() {
< it('passing in data to a compiled function that expects data - works with helpers', function() {
---
> /*
> * 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('data', () => {
> it('passing in data to a compiled function that expects data - works with helpers', () => {
5c15
< .withHelper('hello', function(options) {
---
> .withHelper('hello', function (this: any, options) {
10d19
< .withMessage('Data output by helper')
14c23
< it('data can be looked up via @foo', function() {
---
> it('data can be looked up via @foo', () => {
17d25
< .withMessage('@foo retrieves template data')
21,22c29,31
< it('deep @foo triggers automatic top-level data', function() {
< var helpers = Handlebars.createFrame(handlebarsEnv.helpers);
---
> it('deep @foo triggers automatic top-level data', () => {
> global.kbnHandlebarsEnv = Handlebars.create();
> const helpers = Handlebars.createFrame(kbnHandlebarsEnv!.helpers);
24,25c33,34
< helpers.let = function(options) {
< var frame = Handlebars.createFrame(options.data);
---
> helpers.let = function (options: Handlebars.HelperOptions) {
> const frame = Handlebars.createFrame(options.data);
27c36
< for (var prop in options.hash) {
---
> for (const prop in options.hash) {
40d48
< .withMessage('Automatic data was triggered')
41a50,51
>
> global.kbnHandlebarsEnv = null;
44c54
< it('parameter data can be looked up via @foo', function() {
---
> it('parameter data can be looked up via @foo', () => {
47c57
< .withHelper('hello', function(noun) {
---
> .withHelper('hello', function (noun) {
50d59
< .withMessage('@foo as a parameter retrieves template data')
54c63
< it('hash values can be looked up via @foo', function() {
---
> it('hash values can be looked up via @foo', () => {
57c66
< .withHelper('hello', function(options) {
---
> .withHelper('hello', function (options) {
60d68
< .withMessage('@foo as a parameter retrieves template data')
64c72
< it('nested parameter data can be looked up via @foo.bar', function() {
---
> it('nested parameter data can be looked up via @foo.bar', () => {
67c75
< .withHelper('hello', function(noun) {
---
> .withHelper('hello', function (noun) {
70d77
< .withMessage('@foo as a parameter retrieves template data')
74c81
< it('nested parameter data does not fail with @world.bar', function() {
---
> it('nested parameter data does not fail with @world.bar', () => {
77c84
< .withHelper('hello', function(noun) {
---
> .withHelper('hello', function (noun) {
80d86
< .withMessage('@foo as a parameter retrieves template data')
84,87c90,91
< it('parameter data throws when using complex scope references', function() {
< expectTemplate(
< '{{#goodbyes}}{{text}} cruel {{@foo/../name}}! {{/goodbyes}}'
< ).toThrow(Error);
---
> it('parameter data throws when using complex scope references', () => {
> expectTemplate('{{#goodbyes}}{{text}} cruel {{@foo/../name}}! {{/goodbyes}}').toThrow(Error);
90c94
< it('data can be functions', function() {
---
> it('data can be functions', () => {
94c98
< hello: function() {
---
> hello() {
96,97c100,101
< }
< }
---
> },
> },
102c106
< it('data can be functions with params', function() {
---
> it('data can be functions with params', () => {
106c110
< hello: function(arg) {
---
> hello(arg: any) {
108,109c112,113
< }
< }
---
> },
> },
114c118
< it('data is inherited downstream', function() {
---
> it('data is inherited downstream', () => {
120,122c124,126
< .withHelper('let', function(options) {
< var frame = Handlebars.createFrame(options.data);
< for (var prop in options.hash) {
---
> .withHelper('let', function (this: any, options) {
> const frame = Handlebars.createFrame(options.data);
> for (const prop in options.hash) {
130d133
< .withMessage('data variables are inherited downstream')
134,147c137
< it('passing in data to a compiled function that expects data - works with helpers in partials', function() {
< expectTemplate('{{>myPartial}}')
< .withCompileOptions({ data: true })
< .withPartial('myPartial', '{{hello}}')
< .withHelper('hello', function(options) {
< return options.data.adjective + ' ' + this.noun;
< })
< .withInput({ noun: 'cat' })
< .withRuntimeOptions({ data: { adjective: 'happy' } })
< .withMessage('Data output by helper inside partial')
< .toCompileTo('happy cat');
< });
<
< it('passing in data to a compiled function that expects data - works with helpers and parameters', function() {
---
> it('passing in data to a compiled function that expects data - works with helpers and parameters', () => {
150c140
< .withHelper('hello', function(noun, options) {
---
> .withHelper('hello', function (this: any, noun, options) {
155d144
< .withMessage('Data output by helper')
159c148
< it('passing in data to a compiled function that expects data - works with block helpers', function() {
---
> it('passing in data to a compiled function that expects data - works with block helpers', () => {
162c151
< data: true
---
> data: true,
164c153
< .withHelper('hello', function(options) {
---
> .withHelper('hello', function (this: any, options) {
167c156
< .withHelper('world', function(options) {
---
> .withHelper('world', function (this: any, options) {
172d160
< .withMessage('Data output by helper')
176c164
< it('passing in data to a compiled function that expects data - works with block helpers that use ..', function() {
---
> it('passing in data to a compiled function that expects data - works with block helpers that use ..', () => {
179c167
< .withHelper('hello', function(options) {
---
> .withHelper('hello', function (options) {
182c170
< .withHelper('world', function(thing, options) {
---
> .withHelper('world', function (this: any, thing, options) {
187d174
< .withMessage('Data output by helper')
191c178
< it('passing in data to a compiled function that expects data - data is passed to with block helpers where children use ..', function() {
---
> it('passing in data to a compiled function that expects data - data is passed to with block helpers where children use ..', () => {
194c181
< .withHelper('hello', function(options) {
---
> .withHelper('hello', function (options) {
197c184
< .withHelper('world', function(thing, options) {
---
> .withHelper('world', function (this: any, thing, options) {
202d188
< .withMessage('Data output by helper')
206c192
< it('you can override inherited data when invoking a helper', function() {
---
> it('you can override inherited data when invoking a helper', () => {
209,213c195,196
< .withHelper('hello', function(options) {
< return options.fn(
< { exclaim: '?', zomg: 'world' },
< { data: { adjective: 'sad' } }
< );
---
> .withHelper('hello', function (options) {
> return options.fn({ exclaim: '?', zomg: 'world' }, { data: { adjective: 'sad' } });
215c198
< .withHelper('world', function(thing, options) {
---
> .withHelper('world', function (this: any, thing, options) {
220d202
< .withMessage('Overriden data output by helper')
224c206
< it('you can override inherited data when invoking a helper with depth', function() {
---
> it('you can override inherited data when invoking a helper with depth', () => {
227c209
< .withHelper('hello', function(options) {
---
> .withHelper('hello', function (options) {
230c212
< .withHelper('world', function(thing, options) {
---
> .withHelper('world', function (this: any, thing, options) {
235d216
< .withMessage('Overriden data output by helper')
239,240c220,221
< describe('@root', function() {
< it('the root context can be looked up via @root', function() {
---
> describe('@root', () => {
> it('the root context can be looked up via @root', () => {
246,248c227
< expectTemplate('{{@root.foo}}')
< .withInput({ foo: 'hello' })
< .toCompileTo('hello');
---
> expectTemplate('{{@root.foo}}').withInput({ foo: 'hello' }).toCompileTo('hello');
251c230
< it('passed root values take priority', function() {
---
> it('passed root values take priority', () => {
259,260c238,239
< describe('nesting', function() {
< it('the root context can be looked up via @root', function() {
---
> describe('nesting', () => {
> it('the root context can be looked up via @root', () => {
265,266c244,245
< .withHelper('helper', function(options) {
< var frame = Handlebars.createFrame(options.data);
---
> .withHelper('helper', function (this: any, options) {
> const frame = Handlebars.createFrame(options.data);
272,273c251,252
< depth: 0
< }
---
> depth: 0,
> },

File diff suppressed because it is too large Load diff

View file

@ -1,519 +0,0 @@
1,2c1,12
< describe('Regressions', function() {
< it('GH-94: Cannot read property of undefined', function() {
---
> /*
> * 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('Regressions', () => {
> it('GH-94: Cannot read property of undefined', () => {
9,10c19,20
< name: 'Charles Darwin'
< }
---
> name: 'Charles Darwin',
> },
13,15c23,25
< title: 'Lazarillo de Tormes'
< }
< ]
---
> title: 'Lazarillo de Tormes',
> },
> ],
17d26
< .withMessage('Renders without an undefined property error')
21,43c30,35
< it("GH-150: Inverted sections print when they shouldn't", function() {
< var string = '{{^set}}not set{{/set}} :: {{#set}}set{{/set}}';
<
< expectTemplate(string)
< .withMessage(
< "inverted sections run when property isn't present in context"
< )
< .toCompileTo('not set :: ');
<
< expectTemplate(string)
< .withInput({ set: undefined })
< .withMessage('inverted sections run when property is undefined')
< .toCompileTo('not set :: ');
<
< expectTemplate(string)
< .withInput({ set: false })
< .withMessage('inverted sections run when property is false')
< .toCompileTo('not set :: ');
<
< expectTemplate(string)
< .withInput({ set: true })
< .withMessage("inverted sections don't run when property is true")
< .toCompileTo(' :: set');
---
> 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');
46c38
< it('GH-158: Using array index twice, breaks the template', function() {
---
> it('GH-158: Using array index twice, breaks the template', () => {
49d40
< .withMessage('it works as expected')
53,54c44,45
< it("bug reported by @fat where lambdas weren't being properly resolved", function() {
< var string =
---
> it("bug reported by @fat where lambdas weren't being properly resolved", () => {
> const string =
69,70c60,61
< var data = {
< thing: function() {
---
> const data = {
> thing() {
76c67
< { className: 'three', word: '@sayrer' }
---
> { className: 'three', word: '@sayrer' },
78c69
< hasThings: function() {
---
> hasThings() {
80c71
< }
---
> },
83c74
< var output =
---
> const output =
92,94c83
< expectTemplate(string)
< .withInput(data)
< .toCompileTo(output);
---
> expectTemplate(string).withInput(data).toCompileTo(output);
97,100c86,87
< it('GH-408: Multiple loops fail', function() {
< expectTemplate(
< '{{#.}}{{name}}{{/.}}{{#.}}{{name}}{{/.}}{{#.}}{{name}}{{/.}}'
< )
---
> it('GH-408: Multiple loops fail', () => {
> expectTemplate('{{#.}}{{name}}{{/.}}{{#.}}{{name}}{{/.}}{{#.}}{{name}}{{/.}}')
103c90
< { name: 'Jane Doe', location: { city: 'New York' } }
---
> { name: 'Jane Doe', location: { city: 'New York' } },
105d91
< .withMessage('It should output multiple times')
109,110c95,96
< it('GS-428: Nested if else rendering', function() {
< var succeedingTemplate =
---
> it('GS-428: Nested if else rendering', () => {
> const succeedingTemplate =
112c98
< var failingTemplate =
---
> const failingTemplate =
115,116c101,102
< var helpers = {
< blk: function(block) {
---
> const helpers = {
> blk(block: Handlebars.HelperOptions) {
119c105
< inverse: function(block) {
---
> inverse(block: Handlebars.HelperOptions) {
121c107
< }
---
> },
124,130c110,111
< expectTemplate(succeedingTemplate)
< .withHelpers(helpers)
< .toCompileTo(' Expected ');
<
< expectTemplate(failingTemplate)
< .withHelpers(helpers)
< .toCompileTo(' Expected ');
---
> expectTemplate(succeedingTemplate).withHelpers(helpers).toCompileTo(' Expected ');
> expectTemplate(failingTemplate).withHelpers(helpers).toCompileTo(' Expected ');
133,136c114,115
< it('GH-458: Scoped this identifier', function() {
< expectTemplate('{{./foo}}')
< .withInput({ foo: 'bar' })
< .toCompileTo('bar');
---
> it('GH-458: Scoped this identifier', () => {
> expectTemplate('{{./foo}}').withInput({ foo: 'bar' }).toCompileTo('bar');
139c118
< it('GH-375: Unicode line terminators', function() {
---
> it('GH-375: Unicode line terminators', () => {
143c122
< it('GH-534: Object prototype aliases', function() {
---
> it('GH-534: Object prototype aliases', () => {
144a124
> // @ts-expect-error
147,149c127
< expectTemplate('{{foo}}')
< .withInput({ foo: 'bar' })
< .toCompileTo('bar');
---
> expectTemplate('{{foo}}').withInput({ foo: 'bar' }).toCompileTo('bar');
150a129
> // @ts-expect-error
155,157c134,136
< it('GH-437: Matching escaping', function() {
< expectTemplate('{{{a}}').toThrow(Error, /Parse error on/);
< expectTemplate('{{a}}}').toThrow(Error, /Parse error on/);
---
> it('GH-437: Matching escaping', () => {
> expectTemplate('{{{a}}').toThrow(/Parse error on/);
> expectTemplate('{{a}}}').toThrow(/Parse error on/);
160,166c139,141
< it('GH-676: Using array in escaping mustache fails', function() {
< var data = { arr: [1, 2] };
<
< expectTemplate('{{arr}}')
< .withInput(data)
< .withMessage('it works as expected')
< .toCompileTo(data.arr.toString());
---
> it('GH-676: Using array in escaping mustache fails', () => {
> const data = { arr: [1, 2] };
> expectTemplate('{{arr}}').withInput(data).toCompileTo(data.arr.toString());
169c144
< it('Mustache man page', function() {
---
> it('Mustache man page', () => {
177c152
< in_ca: true
---
> in_ca: true,
179,182c154
< .withMessage('the hello world mustache example works')
< .toCompileTo(
< 'Hello Chris. You have just won $10000! Well, $6000, after taxes.'
< );
---
> .toCompileTo('Hello Chris. You have just won $10000! Well, $6000, after taxes.');
185c157
< it('GH-731: zero context rendering', function() {
---
> it('GH-731: zero context rendering', () => {
189c161
< bar: 'OK'
---
> bar: 'OK',
194,197c166,167
< it('GH-820: zero pathed rendering', function() {
< expectTemplate('{{foo.bar}}')
< .withInput({ foo: 0 })
< .toCompileTo('');
---
> it('GH-820: zero pathed rendering', () => {
> expectTemplate('{{foo.bar}}').withInput({ foo: 0 }).toCompileTo('');
200c170
< it('GH-837: undefined values for helpers', function() {
---
> it('GH-837: undefined values for helpers', () => {
203c173
< str: function(value) {
---
> str(value) {
205c175
< }
---
> },
210c180
< it('GH-926: Depths and de-dupe', function() {
---
> it('GH-926: Depths and de-dupe', () => {
217c187
< notData: [1]
---
> notData: [1],
222c192
< it('GH-1021: Each empty string key', function() {
---
> it('GH-1021: Each empty string key', () => {
228,229c198,199
< value: 10000
< }
---
> value: 10000,
> },
234,248c204,205
< it('GH-1054: Should handle simple safe string responses', function() {
< expectTemplate('{{#wrap}}{{>partial}}{{/wrap}}')
< .withHelpers({
< wrap: function(options) {
< return new Handlebars.SafeString(options.fn());
< }
< })
< .withPartials({
< partial: '{{#wrap}}<partial>{{/wrap}}'
< })
< .toCompileTo('<partial>');
< });
<
< it('GH-1065: Sparse arrays', function() {
< var array = [];
---
> it('GH-1065: Sparse arrays', () => {
> const array = [];
252c209
< .withInput({ array: array })
---
> .withInput({ array })
256c213
< it('GH-1093: Undefined helper context', function() {
---
> it('GH-1093: Undefined helper context', () => {
260c217
< helper: function() {
---
> helper(this: any) {
263c220
< for (var name in this) {
---
> for (const name in this) {
270c227
< }
---
> },
275,306c232
< it('should support multiple levels of inline partials', function() {
< 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', function() {
< 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', function() {
< expectTemplate('{{#> layout}}Outer{{/layout}}')
< .withPartials({
< layout: '{{#> inner}}Inner{{/inner}}{{> @partial-block }}',
< inner: ''
< })
< .toCompileTo('Outer');
< });
<
< it('GH-1135 : Context handling within each iteration', function() {
---
> it('GH-1135 : Context handling within each iteration', () => {
315c241
< myif: function(conditional, options) {
---
> myif(conditional, options: Handlebars.HelperOptions) {
321c247
< }
---
> },
326,343c252,253
< it('GH-1186: Support block params for existing programs', function() {
< 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']
< })
< .withMessage('')
< .toCompileTo('ab');
< });
<
< it('GH-1319: "unless" breaks when "each" value equals "null"', function() {
< expectTemplate(
< '{{#each list}}{{#unless ./prop}}parent={{../value}} {{/unless}}{{/each}}'
< )
---
> it('GH-1319: "unless" breaks when "each" value equals "null"', () => {
> expectTemplate('{{#each list}}{{#unless ./prop}}parent={{../value}} {{/unless}}{{/each}}')
346c256
< list: [null, 'a']
---
> list: [null, 'a'],
348d257
< .withMessage('')
352,457c261
< it('GH-1341: 4.0.7 release breaks {{#if @partial-block}} usage', function() {
< expectTemplate('template {{>partial}} template')
< .withPartials({
< partialWithBlock:
< '{{#if @partial-block}} block {{> @partial-block}} block {{/if}}',
< partial: '{{#> partialWithBlock}} partial {{/partialWithBlock}}'
< })
< .toCompileTo('template block partial block template');
< });
<
< describe('GH-1561: 4.3.x should still work with precompiled templates from 4.0.0 <= x < 4.3.0', function() {
< it('should compile and execute templates', function() {
< var newHandlebarsInstance = Handlebars.create();
<
< registerTemplate(newHandlebarsInstance, compiledTemplateVersion7());
< newHandlebarsInstance.registerHelper('loud', function(value) {
< return value.toUpperCase();
< });
< var result = newHandlebarsInstance.templates['test.hbs']({
< name: 'yehuda'
< });
< equals(result.trim(), 'YEHUDA');
< });
<
< it('should call "helperMissing" if a helper is missing', function() {
< var newHandlebarsInstance = Handlebars.create();
<
< shouldThrow(
< function() {
< registerTemplate(newHandlebarsInstance, compiledTemplateVersion7());
< newHandlebarsInstance.templates['test.hbs']({});
< },
< Handlebars.Exception,
< 'Missing helper: "loud"'
< );
< });
<
< it('should pass "options.lookupProperty" to "lookup"-helper, even with old templates', function() {
< var newHandlebarsInstance = Handlebars.create();
< registerTemplate(
< newHandlebarsInstance,
< compiledTemplateVersion7_usingLookupHelper()
< );
<
< newHandlebarsInstance.templates['test.hbs']({});
<
< expect(
< newHandlebarsInstance.templates['test.hbs']({
< property: 'a',
< test: { a: 'b' }
< })
< ).to.equal('b');
< });
<
< function registerTemplate(Handlebars, compileTemplate) {
< var template = Handlebars.template,
< templates = (Handlebars.templates = Handlebars.templates || {});
< templates['test.hbs'] = template(compileTemplate);
< }
<
< function compiledTemplateVersion7() {
< return {
< compiler: [7, '>= 4.0.0'],
< main: function(container, depth0, helpers, partials, data) {
< return (
< container.escapeExpression(
< (
< helpers.loud ||
< (depth0 && depth0.loud) ||
< helpers.helperMissing
< ).call(
< depth0 != null ? depth0 : container.nullContext || {},
< depth0 != null ? depth0.name : depth0,
< { name: 'loud', hash: {}, data: data }
< )
< ) + '\n\n'
< );
< },
< useData: true
< };
< }
<
< function compiledTemplateVersion7_usingLookupHelper() {
< // This is the compiled version of "{{lookup test property}}"
< return {
< compiler: [7, '>= 4.0.0'],
< main: function(container, depth0, helpers, partials, data) {
< return container.escapeExpression(
< helpers.lookup.call(
< depth0 != null ? depth0 : container.nullContext || {},
< depth0 != null ? depth0.test : depth0,
< depth0 != null ? depth0.property : depth0,
< {
< name: 'lookup',
< hash: {},
< data: data
< }
< )
< );
< },
< useData: true
< };
< }
< });
<
< it('should allow hash with protected array names', function() {
---
> it('should allow hash with protected array names', () => {
461c265
< helpa: function(options) {
---
> helpa(options: Handlebars.HelperOptions) {
463c267
< }
---
> },
468,496c272,273
< describe('GH-1598: Performance degradation for partials since v4.3.0', function() {
< // Do not run test for runs without compiler
< if (!Handlebars.compile) {
< return;
< }
<
< var newHandlebarsInstance;
< beforeEach(function() {
< newHandlebarsInstance = Handlebars.create();
< });
< afterEach(function() {
< sinon.restore();
< });
<
< it('should only compile global partials once', function() {
< var templateSpy = sinon.spy(newHandlebarsInstance, 'template');
< newHandlebarsInstance.registerPartial({
< dude: 'I am a partial'
< });
< var string = 'Dudes: {{> dude}} {{> dude}}';
< newHandlebarsInstance.compile(string)(); // This should compile template + partial once
< newHandlebarsInstance.compile(string)(); // This should only compile template
< equal(templateSpy.callCount, 3);
< sinon.restore();
< });
< });
<
< describe("GH-1639: TypeError: Cannot read property 'apply' of undefined\" when handlebars version > 4.6.0 (undocumented, deprecated usage)", function() {
< it('should treat undefined helpers like non-existing helpers', function() {
---
> 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', () => {

View file

@ -1,443 +0,0 @@
1,6c1,6
< describe('security issues', function() {
< describe('GH-1495: Prevent Remote Code Execution via constructor', function() {
< it('should not allow constructors to be accessed', function() {
< expectTemplate('{{lookup (lookup this "constructor") "name"}}')
< .withInput({})
< .toCompileTo('');
---
> /*
> * 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.
> */
8,10c8,15
< expectTemplate('{{constructor.name}}')
< .withInput({})
< .toCompileTo('');
---
> 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('');
13c18
< it('GH-1603: should not allow constructors to be accessed (lookup via toString)', function() {
---
> it('GH-1603: should not allow constructors to be accessed (lookup via toString)', () => {
16c21
< .withHelper('list', function(element) {
---
> .withHelper('list', function (element) {
22c27
< it('should allow the "constructor" property to be accessed if it is an "ownProperty"', function() {
---
> it('should allow the "constructor" property to be accessed if it is an "ownProperty"', () => {
32c37
< it('should allow the "constructor" property to be accessed if it is an "own property"', function() {
---
> it('should allow the "constructor" property to be accessed if it is an "own property"', () => {
39,45c44,46
< describe('GH-1558: Prevent explicit call of helperMissing-helpers', function() {
< if (!Handlebars.compile) {
< return;
< }
<
< describe('without the option "allowExplicitCallOfHelperMissing"', function() {
< it('should throw an exception when calling "{{helperMissing}}" ', function() {
---
> describe('GH-1558: Prevent explicit call of helperMissing-helpers', () => {
> describe('without the option "allowExplicitCallOfHelperMissing"', () => {
> it('should throw an exception when calling "{{helperMissing}}" ', () => {
49c50
< it('should throw an exception when calling "{{#helperMissing}}{{/helperMissing}}" ', function() {
---
> it('should throw an exception when calling "{{#helperMissing}}{{/helperMissing}}" ', () => {
53,56c54,57
< it('should throw an exception when calling "{{blockHelperMissing "abc" .}}" ', function() {
< var functionCalls = [];
< expect(function() {
< var template = Handlebars.compile('{{blockHelperMissing "abc" .}}');
---
> it('should throw an exception when calling "{{blockHelperMissing "abc" .}}" ', () => {
> const functionCalls = [];
> expect(() => {
> const template = Handlebars.compile('{{blockHelperMissing "abc" .}}');
58c59
< fn: function() {
---
> fn() {
60c61
< }
---
> },
62,63c63,64
< }).to.throw(Error);
< expect(functionCalls.length).to.equal(0);
---
> }).toThrow(Error);
> expect(functionCalls.length).toEqual(0);
66c67
< it('should throw an exception when calling "{{#blockHelperMissing .}}{{/blockHelperMissing}}"', function() {
---
> it('should throw an exception when calling "{{#blockHelperMissing .}}{{/blockHelperMissing}}"', () => {
69c70
< fn: function() {
---
> fn() {
71c72
< }
---
> },
76,110d76
<
< describe('with the option "allowCallsToHelperMissing" set to true', function() {
< it('should not throw an exception when calling "{{helperMissing}}" ', function() {
< var template = Handlebars.compile('{{helperMissing}}');
< template({}, { allowCallsToHelperMissing: true });
< });
<
< it('should not throw an exception when calling "{{#helperMissing}}{{/helperMissing}}" ', function() {
< var template = Handlebars.compile(
< '{{#helperMissing}}{{/helperMissing}}'
< );
< template({}, { allowCallsToHelperMissing: true });
< });
<
< it('should not throw an exception when calling "{{blockHelperMissing "abc" .}}" ', function() {
< var functionCalls = [];
< var template = Handlebars.compile('{{blockHelperMissing "abc" .}}');
< template(
< {
< fn: function() {
< functionCalls.push('called');
< }
< },
< { allowCallsToHelperMissing: true }
< );
< equals(functionCalls.length, 1);
< });
<
< it('should not throw an exception when calling "{{#blockHelperMissing .}}{{/blockHelperMissing}}"', function() {
< var template = Handlebars.compile(
< '{{#blockHelperMissing true}}sdads{{/blockHelperMissing}}'
< );
< template({}, { allowCallsToHelperMissing: true });
< });
< });
113,114c79,81
< describe('GH-1563', function() {
< it('should not allow to access constructor after overriding via __defineGetter__', function() {
---
> describe('GH-1563', () => {
> it('should not allow to access constructor after overriding via __defineGetter__', () => {
> // @ts-expect-error
116c83
< return this.skip(); // Browser does not support this exploit anyway
---
> return; // Browser does not support this exploit anyway
130,131c97,98
< describe('GH-1595: dangerous properties', function() {
< var templates = [
---
> describe('GH-1595: dangerous properties', () => {
> const templates = [
141c108
< '{{lookup this "__proto__"}}'
---
> '{{lookup this "__proto__"}}',
144,382c111,114
< templates.forEach(function(template) {
< describe('access should be denied to ' + template, function() {
< it('by default', function() {
< expectTemplate(template)
< .withInput({})
< .toCompileTo('');
< });
< it(' with proto-access enabled', function() {
< expectTemplate(template)
< .withInput({})
< .withRuntimeOptions({
< allowProtoPropertiesByDefault: true,
< allowProtoMethodsByDefault: true
< })
< .toCompileTo('');
< });
< });
< });
< });
< describe('GH-1631: disallow access to prototype functions', function() {
< function TestClass() {}
<
< TestClass.prototype.aProperty = 'propertyValue';
< TestClass.prototype.aMethod = function() {
< return 'returnValue';
< };
<
< beforeEach(function() {
< handlebarsEnv.resetLoggedPropertyAccesses();
< });
<
< afterEach(function() {
< sinon.restore();
< });
<
< describe('control access to prototype methods via "allowedProtoMethods"', function() {
< checkProtoMethodAccess({});
<
< describe('in compat mode', function() {
< checkProtoMethodAccess({ compat: true });
< });
<
< function checkProtoMethodAccess(compileOptions) {
< it('should be prohibited by default and log a warning', function() {
< var spy = sinon.spy(console, 'error');
<
< expectTemplate('{{aMethod}}')
< .withInput(new TestClass())
< .withCompileOptions(compileOptions)
< .toCompileTo('');
<
< expect(spy.calledOnce).to.be.true();
< expect(spy.args[0][0]).to.match(/Handlebars: Access has been denied/);
< });
<
< it('should only log the warning once', function() {
< var spy = sinon.spy(console, 'error');
<
< expectTemplate('{{aMethod}}')
< .withInput(new TestClass())
< .withCompileOptions(compileOptions)
< .toCompileTo('');
<
< expectTemplate('{{aMethod}}')
< .withInput(new TestClass())
< .withCompileOptions(compileOptions)
< .toCompileTo('');
<
< expect(spy.calledOnce).to.be.true();
< expect(spy.args[0][0]).to.match(/Handlebars: Access has been denied/);
< });
<
< it('can be allowed, which disables the warning', function() {
< var spy = sinon.spy(console, 'error');
<
< expectTemplate('{{aMethod}}')
< .withInput(new TestClass())
< .withCompileOptions(compileOptions)
< .withRuntimeOptions({
< allowedProtoMethods: {
< aMethod: true
< }
< })
< .toCompileTo('returnValue');
<
< expect(spy.callCount).to.equal(0);
< });
<
< it('can be turned on by default, which disables the warning', function() {
< var spy = sinon.spy(console, 'error');
<
< expectTemplate('{{aMethod}}')
< .withInput(new TestClass())
< .withCompileOptions(compileOptions)
< .withRuntimeOptions({
< allowProtoMethodsByDefault: true
< })
< .toCompileTo('returnValue');
<
< expect(spy.callCount).to.equal(0);
< });
<
< it('can be turned off by default, which disables the warning', function() {
< var spy = sinon.spy(console, 'error');
<
< expectTemplate('{{aMethod}}')
< .withInput(new TestClass())
< .withCompileOptions(compileOptions)
< .withRuntimeOptions({
< allowProtoMethodsByDefault: false
< })
< .toCompileTo('');
<
< expect(spy.callCount).to.equal(0);
< });
<
< it('can be turned off, if turned on by default', function() {
< expectTemplate('{{aMethod}}')
< .withInput(new TestClass())
< .withCompileOptions(compileOptions)
< .withRuntimeOptions({
< allowProtoMethodsByDefault: true,
< allowedProtoMethods: {
< aMethod: false
< }
< })
< .toCompileTo('');
< });
< }
<
< it('should cause the recursive lookup by default (in "compat" mode)', function() {
< expectTemplate('{{#aString}}{{trim}}{{/aString}}')
< .withInput({ aString: ' abc ', trim: 'trim' })
< .withCompileOptions({ compat: true })
< .toCompileTo('trim');
< });
<
< it('should not cause the recursive lookup if allowed through options(in "compat" mode)', function() {
< expectTemplate('{{#aString}}{{trim}}{{/aString}}')
< .withInput({ aString: ' abc ', trim: 'trim' })
< .withCompileOptions({ compat: true })
< .withRuntimeOptions({
< allowedProtoMethods: {
< trim: true
< }
< })
< .toCompileTo('abc');
< });
< });
<
< describe('control access to prototype non-methods via "allowedProtoProperties" and "allowProtoPropertiesByDefault', function() {
< checkProtoPropertyAccess({});
<
< describe('in compat-mode', function() {
< checkProtoPropertyAccess({ compat: true });
< });
<
< describe('in strict-mode', function() {
< checkProtoPropertyAccess({ strict: true });
< });
<
< function checkProtoPropertyAccess(compileOptions) {
< it('should be prohibited by default and log a warning', function() {
< var spy = sinon.spy(console, 'error');
<
< expectTemplate('{{aProperty}}')
< .withInput(new TestClass())
< .withCompileOptions(compileOptions)
< .toCompileTo('');
<
< expect(spy.calledOnce).to.be.true();
< expect(spy.args[0][0]).to.match(/Handlebars: Access has been denied/);
< });
<
< it('can be explicitly prohibited by default, which disables the warning', function() {
< var spy = sinon.spy(console, 'error');
<
< expectTemplate('{{aProperty}}')
< .withInput(new TestClass())
< .withCompileOptions(compileOptions)
< .withRuntimeOptions({
< allowProtoPropertiesByDefault: false
< })
< .toCompileTo('');
<
< expect(spy.callCount).to.equal(0);
< });
<
< it('can be turned on, which disables the warning', function() {
< var spy = sinon.spy(console, 'error');
<
< expectTemplate('{{aProperty}}')
< .withInput(new TestClass())
< .withCompileOptions(compileOptions)
< .withRuntimeOptions({
< allowedProtoProperties: {
< aProperty: true
< }
< })
< .toCompileTo('propertyValue');
<
< expect(spy.callCount).to.equal(0);
< });
<
< it('can be turned on by default, which disables the warning', function() {
< var spy = sinon.spy(console, 'error');
<
< expectTemplate('{{aProperty}}')
< .withInput(new TestClass())
< .withCompileOptions(compileOptions)
< .withRuntimeOptions({
< allowProtoPropertiesByDefault: true
< })
< .toCompileTo('propertyValue');
<
< expect(spy.callCount).to.equal(0);
< });
<
< it('can be turned off, if turned on by default', function() {
< expectTemplate('{{aProperty}}')
< .withInput(new TestClass())
< .withCompileOptions(compileOptions)
< .withRuntimeOptions({
< allowProtoPropertiesByDefault: true,
< allowedProtoProperties: {
< aProperty: false
< }
< })
< .toCompileTo('');
< });
< }
< });
<
< describe('compatibility with old runtimes, that do not provide the function "container.lookupProperty"', function() {
< beforeEach(function simulateRuntimeWithoutLookupProperty() {
< var oldTemplateMethod = handlebarsEnv.template;
< sinon.replace(handlebarsEnv, 'template', function(templateSpec) {
< templateSpec.main = wrapToAdjustContainer(templateSpec.main);
< return oldTemplateMethod.call(this, templateSpec);
---
> templates.forEach((template) => {
> describe('access should be denied to ' + template, () => {
> it('by default', () => {
> expectTemplate(template).withInput({}).toCompileTo('');
385,400d116
<
< afterEach(function() {
< sinon.restore();
< });
<
< it('should work with simple properties', function() {
< expectTemplate('{{aProperty}}')
< .withInput({ aProperty: 'propertyValue' })
< .toCompileTo('propertyValue');
< });
<
< it('should work with Array.prototype.length', function() {
< expectTemplate('{{anArray.length}}')
< .withInput({ anArray: ['a', 'b', 'c'] })
< .toCompileTo('3');
< });
404,409c120,122
< describe('escapes template variables', function() {
< it('in compat mode', function() {
< expectTemplate("{{'a\\b'}}")
< .withCompileOptions({ compat: true })
< .withInput({ 'a\\b': 'c' })
< .toCompileTo('c');
---
> describe('escapes template variables', () => {
> it('in default mode', () => {
> expectTemplate("{{'a\\b'}}").withCompileOptions().withInput({ 'a\\b': 'c' }).toCompileTo('c');
412,418c125
< it('in default mode', function() {
< expectTemplate("{{'a\\b'}}")
< .withCompileOptions()
< .withInput({ 'a\\b': 'c' })
< .toCompileTo('c');
< });
< it('in default mode', function() {
---
> it('in strict mode', () => {
426,432d132
<
< function wrapToAdjustContainer(precompiledTemplateFunction) {
< return function templateFunctionWrapper(container /*, more args */) {
< delete container.lookupProperty;
< return precompiledTemplateFunction.apply(this, arguments);
< };
< }

View file

@ -1,180 +0,0 @@
1c1,6
< var Exception = Handlebars.Exception;
---
> /*
> * 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.
> */
3,5c8,12
< describe('strict', function() {
< describe('strict mode', function() {
< it('should error on missing property lookup', function() {
---
> import { expectTemplate } from '../__jest__/test_bench';
>
> describe('strict', () => {
> describe('strict mode', () => {
> it('should error on missing property lookup', () => {
8c15
< .toThrow(Exception, /"hello" not defined in/);
---
> .toThrow(/"hello" not defined in/);
11c18
< it('should error on missing child', function() {
---
> it('should error on missing child', () => {
20c27
< .toThrow(Exception, /"bar" not defined in/);
---
> .toThrow(/"bar" not defined in/);
23c30
< it('should handle explicit undefined', function() {
---
> it('should handle explicit undefined', () => {
30c37
< it('should error on missing property lookup in known helpers mode', function() {
---
> it('should error on missing property lookup in known helpers mode', () => {
34c41
< knownHelpersOnly: true
---
> knownHelpersOnly: true,
36c43
< .toThrow(Exception, /"hello" not defined in/);
---
> .toThrow(/"hello" not defined in/);
39,42c46,47
< it('should error on missing context', function() {
< expectTemplate('{{hello}}')
< .withCompileOptions({ strict: true })
< .toThrow(Error);
---
> it('should error on missing context', () => {
> expectTemplate('{{hello}}').withCompileOptions({ strict: true }).toThrow(Error);
45,47c50,52
< it('should error on missing data lookup', function() {
< var xt = expectTemplate('{{@hello}}').withCompileOptions({
< strict: true
---
> it('should error on missing data lookup', () => {
> const xt = expectTemplate('{{@hello}}').withCompileOptions({
> strict: true,
55c60
< it('should not run helperMissing for helper calls', function() {
---
> it('should not run helperMissing for helper calls', () => {
59c64
< .toThrow(Exception, /"hello" not defined in/);
---
> .toThrow(/"hello" not defined in/);
64c69
< .toThrow(Exception, /"hello" not defined in/);
---
> .toThrow(/"hello" not defined in/);
67c72
< it('should throw on ambiguous blocks', function() {
---
> it('should throw on ambiguous blocks', () => {
70c75
< .toThrow(Exception, /"hello" not defined in/);
---
> .toThrow(/"hello" not defined in/);
74c79
< .toThrow(Exception, /"hello" not defined in/);
---
> .toThrow(/"hello" not defined in/);
79c84
< .toThrow(Exception, /"bar" not defined in/);
---
> .toThrow(/"bar" not defined in/);
82c87
< it('should allow undefined parameters when passed to helpers', function() {
---
> it('should allow undefined parameters when passed to helpers', () => {
88c93
< it('should allow undefined hash when passed to helpers', function() {
---
> it('should allow undefined hash when passed to helpers', () => {
91c96
< strict: true
---
> strict: true,
94,96c99,101
< helper: function(options) {
< equals('value' in options.hash, true);
< equals(options.hash.value, undefined);
---
> helper(options) {
> expect('value' in options.hash).toEqual(true);
> expect(options.hash.value).toBeUndefined();
98c103
< }
---
> },
103c108
< it('should show error location on missing property lookup', function() {
---
> it('should show error location on missing property lookup', () => {
106c111
< .toThrow(Exception, '"hello" not defined in [object Object] - 4:5');
---
> .toThrow('"hello" not defined in [object Object] - 4:5');
109c114
< it('should error contains correct location properties on missing property lookup', function() {
---
> it('should error contains correct location properties on missing property lookup', () => {
111,114c116,118
< var template = CompilerContext.compile('\n\n\n {{hello}}', {
< strict: true
< });
< template({});
---
> expectTemplate('\n\n\n {{hello}}')
> .withCompileOptions({ strict: true })
> .toCompileTo('throw before asserting this');
116,119c120,123
< equals(error.lineNumber, 4);
< equals(error.endLineNumber, 4);
< equals(error.column, 5);
< equals(error.endColumn, 10);
---
> expect(error.lineNumber).toEqual(4);
> expect(error.endLineNumber).toEqual(4);
> expect(error.column).toEqual(5);
> expect(error.endColumn).toEqual(10);
124,128c128,130
< describe('assume objects', function() {
< it('should ignore missing property', function() {
< expectTemplate('{{hello}}')
< .withCompileOptions({ assumeObjects: true })
< .toCompileTo('');
---
> describe('assume objects', () => {
> it('should ignore missing property', () => {
> expectTemplate('{{hello}}').withCompileOptions({ assumeObjects: true }).toCompileTo('');
131c133
< it('should ignore missing child', function() {
---
> it('should ignore missing child', () => {
138,141c140,141
< it('should error on missing object', function() {
< expectTemplate('{{hello.bar}}')
< .withCompileOptions({ assumeObjects: true })
< .toThrow(Error);
---
> it('should error on missing object', () => {
> expectTemplate('{{hello.bar}}').withCompileOptions({ assumeObjects: true }).toThrow(Error);
144c144
< it('should error on missing context', function() {
---
> it('should error on missing context', () => {
151c151
< it('should error on missing data lookup', function() {
---
> it('should error on missing data lookup', () => {
158c158
< it('should execute blockHelperMissing', function() {
---
> it('should execute blockHelperMissing', () => {

View file

@ -1,318 +0,0 @@
1,2c1,12
< describe('subexpressions', function() {
< it('arg-less helper', function() {
---
> /*
> * 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('subexpressions', () => {
> it('arg-less helper', () => {
5c15
< foo: function(val) {
---
> foo(val) {
8c18
< bar: function() {
---
> bar() {
10c20
< }
---
> },
15c25
< it('helper w args', function() {
---
> it('helper w args', () => {
19c29
< blog: function(val) {
---
> blog(val) {
22c32
< equal: function(x, y) {
---
> equal(x, y) {
24c34
< }
---
> },
29c39
< it('mixed paths and helpers', function() {
---
> it('mixed paths and helpers', () => {
33c43
< blog: function(val, that, theOther) {
---
> blog(val, that, theOther) {
36c46
< equal: function(x, y) {
---
> equal(x, y) {
38c48
< }
---
> },
43c53
< it('supports much nesting', function() {
---
> it('supports much nesting', () => {
47c57
< blog: function(val) {
---
> blog(val) {
50c60
< equal: function(x, y) {
---
> equal(x, y) {
52c62
< }
---
> },
57,60c67,70
< it('GH-800 : Complex subexpressions', function() {
< var context = { a: 'a', b: 'b', c: { c: 'c' }, d: 'd', e: { e: 'e' } };
< var helpers = {
< dash: function(a, b) {
---
> 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) {
63c73
< concat: function(a, b) {
---
> concat(a: any, b: any) {
65c75
< }
---
> },
94,97c104,107
< it('provides each nested helper invocation its own options hash', function() {
< var lastOptions = null;
< var helpers = {
< equal: function(x, y, options) {
---
> it('provides each nested helper invocation its own options hash', () => {
> let lastOptions: Handlebars.HelperOptions;
> const helpers = {
> equal(x: any, y: any, options: Handlebars.HelperOptions) {
103c113
< }
---
> },
105,107c115
< expectTemplate('{{equal (equal true true) true}}')
< .withHelpers(helpers)
< .toCompileTo('true');
---
> expectTemplate('{{equal (equal true true) true}}').withHelpers(helpers).toCompileTo('true');
110c118
< it('with hashes', function() {
---
> it('with hashes', () => {
114c122
< blog: function(val) {
---
> blog(val) {
117c125
< equal: function(x, y) {
---
> equal(x, y) {
119c127
< }
---
> },
124c132
< it('as hashes', function() {
---
> it('as hashes', () => {
127c135
< blog: function(options) {
---
> blog(options) {
130c138
< equal: function(x, y) {
---
> equal(x, y) {
132c140
< }
---
> },
137,140c145,146
< it('multiple subexpressions in a hash', function() {
< expectTemplate(
< '{{input aria-label=(t "Name") placeholder=(t "Example User")}}'
< )
---
> it('multiple subexpressions in a hash', () => {
> expectTemplate('{{input aria-label=(t "Name") placeholder=(t "Example User")}}')
142,145c148,151
< input: function(options) {
< var hash = options.hash;
< var ariaLabel = Handlebars.Utils.escapeExpression(hash['aria-label']);
< var placeholder = Handlebars.Utils.escapeExpression(hash.placeholder);
---
> input(options) {
> const hash = options.hash;
> const ariaLabel = Handlebars.Utils.escapeExpression(hash['aria-label']);
> const placeholder = Handlebars.Utils.escapeExpression(hash.placeholder);
147,151c153
< '<input aria-label="' +
< ariaLabel +
< '" placeholder="' +
< placeholder +
< '" />'
---
> '<input aria-label="' + ariaLabel + '" placeholder="' + placeholder + '" />'
154c156
< t: function(defaultString) {
---
> t(defaultString) {
156c158
< }
---
> },
161,164c163,164
< it('multiple subexpressions in a hash with context', function() {
< expectTemplate(
< '{{input aria-label=(t item.field) placeholder=(t item.placeholder)}}'
< )
---
> it('multiple subexpressions in a hash with context', () => {
> expectTemplate('{{input aria-label=(t item.field) placeholder=(t item.placeholder)}}')
168,169c168,169
< placeholder: 'Example User'
< }
---
> placeholder: 'Example User',
> },
172,175c172,175
< input: function(options) {
< var hash = options.hash;
< var ariaLabel = Handlebars.Utils.escapeExpression(hash['aria-label']);
< var placeholder = Handlebars.Utils.escapeExpression(hash.placeholder);
---
> input(options) {
> const hash = options.hash;
> const ariaLabel = Handlebars.Utils.escapeExpression(hash['aria-label']);
> const placeholder = Handlebars.Utils.escapeExpression(hash.placeholder);
177,181c177
< '<input aria-label="' +
< ariaLabel +
< '" placeholder="' +
< placeholder +
< '" />'
---
> '<input aria-label="' + ariaLabel + '" placeholder="' + placeholder + '" />'
184c180
< t: function(defaultString) {
---
> t(defaultString) {
186,242d181
< }
< })
< .toCompileTo('<input aria-label="Name" placeholder="Example User" />');
< });
<
< it('in string params mode,', function() {
< expectTemplate('{{snog (blorg foo x=y) yeah a=b}}')
< .withCompileOptions({ stringParams: true })
< .withHelpers({
< snog: function(a, b, options) {
< equals(a, 'foo');
< equals(
< options.types.length,
< 2,
< 'string params for outer helper processed correctly'
< );
< equals(
< options.types[0],
< 'SubExpression',
< 'string params for outer helper processed correctly'
< );
< equals(
< options.types[1],
< 'PathExpression',
< 'string params for outer helper processed correctly'
< );
< return a + b;
< },
<
< blorg: function(a, options) {
< equals(
< options.types.length,
< 1,
< 'string params for inner helper processed correctly'
< );
< equals(
< options.types[0],
< 'PathExpression',
< 'string params for inner helper processed correctly'
< );
< return a;
< }
< })
< .withInput({
< foo: {},
< yeah: {}
< })
< .toCompileTo('fooyeah');
< });
<
< it('as hashes in string params mode', function() {
< expectTemplate('{{blog fun=(bork)}}')
< .withCompileOptions({ stringParams: true })
< .withHelpers({
< blog: function(options) {
< equals(options.hashTypes.fun, 'SubExpression');
< return 'val is ' + options.hash.fun;
244,246d182
< bork: function() {
< return 'BORK';
< }
248c184
< .toCompileTo('val is BORK');
---
> .toCompileTo('<input aria-label="Name" placeholder="Example User" />');
251c187
< it('subexpression functions on the context', function() {
---
> it('subexpression functions on the context', () => {
254c190
< bar: function() {
---
> bar() {
256c192
< }
---
> },
259c195
< foo: function(val) {
---
> foo(val) {
261c197
< }
---
> },
266c202
< it("subexpressions can't just be property lookups", function() {
---
> it("subexpressions can't just be property lookups", () => {
269c205
< bar: 'LOL'
---
> bar: 'LOL',
272c208
< foo: function(val) {
---
> foo(val) {
274c210
< }
---
> },

View file

@ -1,108 +0,0 @@
1,55c1,6
< describe('utils', function() {
< describe('#SafeString', function() {
< it('constructing a safestring from a string and checking its type', function() {
< var safe = new Handlebars.SafeString('testing 1, 2, 3');
< if (!(safe instanceof Handlebars.SafeString)) {
< throw new Error('Must be instance of SafeString');
< }
< equals(
< safe.toString(),
< 'testing 1, 2, 3',
< 'SafeString is equivalent to its underlying string'
< );
< });
<
< it('it should not escape SafeString properties', function() {
< var name = new Handlebars.SafeString('<em>Sean O&#x27;Malley</em>');
<
< expectTemplate('{{name}}')
< .withInput({ name: name })
< .toCompileTo('<em>Sean O&#x27;Malley</em>');
< });
< });
<
< describe('#escapeExpression', function() {
< it('shouhld escape html', function() {
< equals(
< Handlebars.Utils.escapeExpression('foo<&"\'>'),
< 'foo&lt;&amp;&quot;&#x27;&gt;'
< );
< equals(Handlebars.Utils.escapeExpression('foo='), 'foo&#x3D;');
< });
< it('should not escape SafeString', function() {
< var string = new Handlebars.SafeString('foo<&"\'>');
< equals(Handlebars.Utils.escapeExpression(string), 'foo<&"\'>');
<
< var obj = {
< toHTML: function() {
< return 'foo<&"\'>';
< }
< };
< equals(Handlebars.Utils.escapeExpression(obj), 'foo<&"\'>');
< });
< it('should handle falsy', function() {
< equals(Handlebars.Utils.escapeExpression(''), '');
< equals(Handlebars.Utils.escapeExpression(undefined), '');
< equals(Handlebars.Utils.escapeExpression(null), '');
<
< equals(Handlebars.Utils.escapeExpression(false), 'false');
< equals(Handlebars.Utils.escapeExpression(0), '0');
< });
< it('should handle empty objects', function() {
< equals(Handlebars.Utils.escapeExpression({}), {}.toString());
< equals(Handlebars.Utils.escapeExpression([]), [].toString());
< });
< });
---
> /*
> * 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.
> */
57,64c8,9
< describe('#isEmpty', function() {
< it('should not be empty', function() {
< equals(Handlebars.Utils.isEmpty(undefined), true);
< equals(Handlebars.Utils.isEmpty(null), true);
< equals(Handlebars.Utils.isEmpty(false), true);
< equals(Handlebars.Utils.isEmpty(''), true);
< equals(Handlebars.Utils.isEmpty([]), true);
< });
---
> import Handlebars from '../..';
> import { expectTemplate } from '../__jest__/test_bench';
66,70c11,16
< it('should be empty', function() {
< equals(Handlebars.Utils.isEmpty(0), false);
< equals(Handlebars.Utils.isEmpty([1]), false);
< equals(Handlebars.Utils.isEmpty('foo'), false);
< equals(Handlebars.Utils.isEmpty({ bar: 1 }), false);
---
> 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');
72,83d17
< });
<
< describe('#extend', function() {
< it('should ignore prototype values', function() {
< function A() {
< this.a = 1;
< }
< A.prototype.b = 4;
<
< var b = { b: 2 };
<
< Handlebars.Utils.extend(b, new A());
85,86c19,21
< equals(b.a, 1);
< equals(b.b, 2);
---
> 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>');

View file

@ -1,186 +0,0 @@
1,19c1,6
< describe('whitespace control', function() {
< it('should strip whitespace around mustache calls', function() {
< var 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<');
---
> /*
> * 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.
> */
21,23c8
< expectTemplate(' {{~{foo}~}} ')
< .withInput(hash)
< .toCompileTo('bar<');
---
> import { expectTemplate } from '../__jest__/test_bench';
24a10,17
> 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<');
28,42c21,23
< describe('blocks', function() {
< it('should strip whitespace around simple block calls', function() {
< var 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 ');
---
> describe('blocks', () => {
> it('should strip whitespace around simple block calls', () => {
> const hash = { foo: 'bar<' };
44,46c25,28
< 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(' {{#if foo}} bar {{/if}} ').withInput(hash).toCompileTo(' bar ');
57c39
< it('should strip whitespace around inverse block calls', function() {
---
> it('should strip whitespace around inverse block calls', () => {
59d40
<
61d41
<
63d42
<
65,68c44
<
< expectTemplate(
< ' \n\n{{~^if foo~}} \n\nbar \n\n{{~/if~}}\n\n '
< ).toCompileTo('bar');
---
> expectTemplate(' \n\n{{~^if foo~}} \n\nbar \n\n{{~/if~}}\n\n ').toCompileTo('bar');
71,88c47,48
< it('should strip whitespace around complex block calls', function() {
< var 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 ');
---
> it('should strip whitespace around complex block calls', () => {
> const hash = { foo: 'bar<' };
90,92c50,54
< expectTemplate('{{#if foo~}} bar {{~else~}} 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 {{^~}} baz {{/if}}').withInput(hash).toCompileTo(' bar ');
> expectTemplate('{{#if foo~}} bar {{~else~}} baz {{~/if}}').withInput(hash).toCompileTo('bar');
94,96c56
< expectTemplate(
< '\n\n{{~#if foo~}} \n\nbar \n\n{{~^~}} \n\nbaz \n\n{{~/if~}}\n\n'
< )
---
> expectTemplate('\n\n{{~#if foo~}} \n\nbar \n\n{{~^~}} \n\nbaz \n\n{{~/if~}}\n\n')
100,102c60
< expectTemplate(
< '\n\n{{~#if foo~}} \n\n{{{foo}}} \n\n{{~^~}} \n\nbaz \n\n{{~/if~}}\n\n'
< )
---
> expectTemplate('\n\n{{~#if foo~}} \n\n{{{foo}}} \n\n{{~^~}} \n\nbaz \n\n{{~/if~}}\n\n')
106,109c64
< expectTemplate('{{#if foo~}} bar {{~^~}} baz {{~/if}}').toCompileTo(
< 'baz'
< );
<
---
> expectTemplate('{{#if foo~}} bar {{~^~}} baz {{~/if}}').toCompileTo('baz');
111,120c66,69
<
< expectTemplate('{{#if foo~}} bar {{~^}} baz {{~/if}}').toCompileTo(
< ' baz'
< );
<
< expectTemplate('{{#if foo~}} bar {{~^}} baz {{/if}}').toCompileTo(
< ' baz '
< );
<
< expectTemplate('{{#if foo~}} bar {{~else~}} baz {{~/if}}').toCompileTo(
---
> 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(
123,126d71
<
< expectTemplate(
< '\n\n{{~#if foo~}} \n\nbar \n\n{{~^~}} \n\nbaz \n\n{{~/if~}}\n\n'
< ).toCompileTo('baz');
130,152c75
< it('should strip whitespace around partials', function() {
< 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', function() {
---
> it('should only strip whitespace once', () => {

View file

@ -77,25 +77,21 @@ By default, each test will run both the original `handlebars` code and the modif
## Development
Some of the tests have been copied from the upstream `handlebars` project and modified to fit our use-case, test-suite, and coding conventions. They are all located under the `packages/kbn-handlebars/src/upstream` directory. To check if any of the copied files have received updates upstream that we might want to include in our copies, you can run the following script:
Some of the tests have been copied from the upstream `handlebars` project and modified to fit our use-case, test-suite, and coding conventions. They are all located under the `packages/kbn-handlebars/src/spec` directory. To check if any of the copied files have received updates upstream that we might want to include in our copies, you can run the following script:
```sh
./packages/kbn-handlebars/scripts/check_for_test_changes.sh
./packages/kbn-handlebars/scripts/check_for_upstream_updates.sh
```
If the script outputs a diff for a given file, it means that this file has been updated.
_Note: that this will look for changes in the `4.x` branch of the `handlebars.js` repo only. Changes in the `master` branch are ignored._
_Note: This will look for changes in the `4.x` branch of the `handlebars.js` repo only. Changes in the `master` branch are ignored._
Once all updates have been manually merged with our versions of the files, run the following script to "lock" us into the new updates:
```sh
./packages/kbn-handlebars/scripts/update_test_patches.sh
./packages/kbn-handlebars/scripts/update_upstream_git_hash.sh
```
This will update the `.patch` files inside the `packages/kbn-handlebars/.patches` directory. Make sure to commit those changes.
_Note: If we manually make changes to our test files in the `upstream` directory, we need to run the `update_test_patches.sh` script as well._
This will update file `packages/kbn-handlebars/src/spec/.upstream_git_hash`. Make sure to commit changes to this file as well.
## Debugging

View file

@ -1,88 +0,0 @@
#!/usr/bin/env bash
set -e
TMP=.tmp-handlebars
# Try to detect Windows environment (I've not tested this!)
if [[ "$OSTYPE" == "msys" ]]; then
# Windows environment
DEVNULL=NUL
else
# Everything else (including Cygwin on Windows)
DEVNULL=/dev/null
fi
function cleanup {
rm -fr $TMP
}
trap cleanup EXIT
rm -fr $TMP
mkdir $TMP
echo "Cloning handlebars repo..."
git clone -q --depth 1 https://github.com/handlebars-lang/handlebars.js.git -b 4.x $TMP/handlebars
files=(packages/kbn-handlebars/src/upstream/index.*.test.ts)
for file in "${files[@]}"
do
tmp=${file#*.} # remove anything before first period
file=${tmp%.test.ts} # remove trailing .test.ts
echo "Checking for changes to spec/$file.js..."
set +e
diff -d --strip-trailing-cr $TMP/handlebars/spec/$file.js packages/kbn-handlebars/src/upstream/index.$file.test.ts > $TMP/$file.patch
error=$?
set -e
if [ $error -gt 1 ]
then
echo "The diff command encountered an unexpected error!"
exit $error
fi
set +e
diff -d --strip-trailing-cr $TMP/$file.patch packages/kbn-handlebars/.patches/$file.patch > $DEVNULL
error=$?
set -e
if [ $error -gt 1 ]
then
echo "The diff command encountered an unexpected error!"
exit $error
elif [ $error -gt 0 ]
then
echo
echo "The following files contain unexpected differences:"
echo
echo " Upstream: spec/$file.js"
echo " Downstream: packages/kbn-handlebars/src/upstream/index.$file.test.ts"
echo
echo "This can happen if either the upstream or the downstream version has been"
echo "updated without our patch files being kept up to date."
echo
echo "To resolve this issue, do the following:"
echo
echo " 1. Check the '4.x' branch of the upstream git repository to see if the file"
echo " has been updated. If so, please ensure that our copy of the file is kept in"
echo " sync. You can view the recent upstream commits to this file here:"
echo
echo " https://github.com/handlebars-lang/handlebars.js/commits/4.x/spec/$file.js"
echo
echo " 2. Update our patch files by running the following script. This is also"
echo " necessary even if it's only the downstream file that has been updated:"
echo
echo " ./packages/kbn-handlebars/scripts/update_test_patches.sh $file"
echo
echo " 3. Commit the changes to the updated patch file and execute this script again"
echo " until everything passes:"
echo
echo " ./packages/kbn-handlebars/scripts/check_for_test_changes.sh"
echo
exit $error
fi
done
echo "No changes found :)"

View file

@ -0,0 +1,45 @@
#!/usr/bin/env bash
set -e
TMP=.tmp-handlebars
HASH_FILE=packages/kbn-handlebars/src/spec/.upstream_git_hash
function cleanup {
rm -fr $TMP
}
trap cleanup EXIT
rm -fr $TMP
mkdir $TMP
echo "Cloning handlebars repo..."
git clone -q --depth 1 https://github.com/handlebars-lang/handlebars.js.git -b 4.x $TMP
echo "Looking for updates..."
hash=$(git -C $TMP rev-parse HEAD)
expected_hash=$(cat $HASH_FILE)
if [ "$hash" = "$expected_hash" ]; then
echo "You're all up to date :)"
else
echo
echo "New changes has been committed to the '4.x' branch in the upstream git repository"
echo
echo "To resolve this issue, do the following:"
echo
echo " 1. Investigate the commits in the '4.x' branch of the upstream git repository."
echo " If files inside the 'spec' folder has been updated, sync those updates with"
echo " our local versions of these files (located in"
echo " 'packages/kbn-handlebars/src/spec')."
echo
echo " https://github.com/handlebars-lang/handlebars.js/compare/$hash...4.x"
echo
echo " 2. Execute the following script and commit the updated '$HASH_FILE'"
echo " file including any changes you made to our own spec files."
echo
echo " ./packages/kbn-handlebars/scripts/update_upstream_git_hash.sh"
echo
exit 1
fi

View file

@ -1,39 +0,0 @@
#!/usr/bin/env bash
set -e
TMP=.tmp-handlebars
function cleanup {
rm -fr $TMP
}
trap cleanup EXIT
rm -fr $TMP
mkdir $TMP
echo "Cloning handlebars repo..."
git clone -q --depth 1 https://github.com/handlebars-lang/handlebars.js.git -b 4.x $TMP/handlebars
if [ -z "$1" ]
then
# No argument given: Update all patch files
files=(packages/kbn-handlebars/src/upstream/index.*.test.ts)
else
# Argument detected: Update only the requested patch file
files=(packages/kbn-handlebars/src/upstream/index.$1.test.ts)
fi
for file in "${files[@]}"
do
tmp=${file#*.} # remove anything before first period
file=${tmp%.test.ts} # remove trailing .test.ts
echo "Overwriting stored patch file for spec/$file.js..."
set +e
diff -d --strip-trailing-cr $TMP/handlebars/spec/$file.js packages/kbn-handlebars/src/upstream/index.$file.test.ts > packages/kbn-handlebars/.patches/$file.patch
set -e
done
echo "All patches updated :)"

View file

@ -0,0 +1,24 @@
#!/usr/bin/env bash
set -e
TMP=.tmp-handlebars
HASH_FILE=packages/kbn-handlebars/src/spec/.upstream_git_hash
function cleanup {
rm -fr $TMP
}
trap cleanup EXIT
rm -fr $TMP
mkdir $TMP
echo "Cloning handlebars repo..."
git clone -q --depth 1 https://github.com/handlebars-lang/handlebars.js.git -b 4.x $TMP
echo "Updating hash file..."
git -C $TMP rev-parse HEAD | tr -d '\n' > $HASH_FILE
git add $HASH_FILE
echo "Done! - Don't forget to commit any changes to $HASH_FILE"

View file

@ -0,0 +1 @@
c65c6cce3f626e4896a9d59250f0908be695adae