From ef79eab44bfd000792c610b968b5ceefd41e76a0 Mon Sep 17 00:00:00 2001 From: Darius Jahandarie Date: Sat, 4 Nov 2023 18:45:57 +0900 Subject: Modernize codebase - Use ES modules - Remove vendored libs and build them from npm using esbuild - Switch from JSZip to zip.js --- dev/lib/handlebars/src/spec/index.basic.test.ts | 481 ++++++++++++++++++++++++ 1 file changed, 481 insertions(+) create mode 100644 dev/lib/handlebars/src/spec/index.basic.test.ts (limited to 'dev/lib/handlebars/src/spec/index.basic.test.ts') diff --git a/dev/lib/handlebars/src/spec/index.basic.test.ts b/dev/lib/handlebars/src/spec/index.basic.test.ts new file mode 100644 index 00000000..6acf3ae9 --- /dev/null +++ b/dev/lib/handlebars/src/spec/index.basic.test.ts @@ -0,0 +1,481 @@ +/* + * This file is forked from the handlebars project (https://github.com/handlebars-lang/handlebars.js), + * and may include modifications made by Elasticsearch B.V. + * Elasticsearch B.V. licenses this file to you under the MIT License. + * See `packages/kbn-handlebars/LICENSE` for more information. + */ + +import Handlebars from '../..'; +import { expectTemplate } from '../__jest__/test_bench'; + +describe('basic context', () => { + it('most basic', () => { + expectTemplate('{{foo}}').withInput({ foo: 'foo' }).toCompileTo('foo'); + }); + + it('escaping', () => { + expectTemplate('\\{{foo}}').withInput({ foo: 'food' }).toCompileTo('{{foo}}'); + expectTemplate('content \\{{foo}}').withInput({ foo: 'food' }).toCompileTo('content {{foo}}'); + expectTemplate('\\\\{{foo}}').withInput({ foo: 'food' }).toCompileTo('\\food'); + expectTemplate('content \\\\{{foo}}').withInput({ foo: 'food' }).toCompileTo('content \\food'); + expectTemplate('\\\\ {{foo}}').withInput({ foo: 'food' }).toCompileTo('\\\\ food'); + }); + + it('compiling with a basic context', () => { + expectTemplate('Goodbye\n{{cruel}}\n{{world}}!') + .withInput({ + cruel: 'cruel', + world: 'world', + }) + .toCompileTo('Goodbye\ncruel\nworld!'); + }); + + it('compiling with a string context', () => { + expectTemplate('{{.}}{{length}}').withInput('bye').toCompileTo('bye3'); + }); + + it('compiling with an undefined context', () => { + expectTemplate('Goodbye\n{{cruel}}\n{{world.bar}}!') + .withInput(undefined) + .toCompileTo('Goodbye\n\n!'); + + expectTemplate('{{#unless foo}}Goodbye{{../test}}{{test2}}{{/unless}}') + .withInput(undefined) + .toCompileTo('Goodbye'); + }); + + it('comments', () => { + expectTemplate('{{! Goodbye}}Goodbye\n{{cruel}}\n{{world}}!') + .withInput({ + cruel: 'cruel', + world: 'world', + }) + .toCompileTo('Goodbye\ncruel\nworld!'); + + expectTemplate(' {{~! comment ~}} blah').toCompileTo('blah'); + expectTemplate(' {{~!-- long-comment --~}} blah').toCompileTo('blah'); + expectTemplate(' {{! comment ~}} blah').toCompileTo(' blah'); + expectTemplate(' {{!-- long-comment --~}} blah').toCompileTo(' blah'); + expectTemplate(' {{~! comment}} blah').toCompileTo(' blah'); + expectTemplate(' {{~!-- long-comment --}} blah').toCompileTo(' blah'); + }); + + it('boolean', () => { + const string = '{{#goodbye}}GOODBYE {{/goodbye}}cruel {{world}}!'; + expectTemplate(string) + .withInput({ + goodbye: true, + world: 'world', + }) + .toCompileTo('GOODBYE cruel world!'); + + expectTemplate(string) + .withInput({ + goodbye: false, + world: 'world', + }) + .toCompileTo('cruel world!'); + }); + + it('zeros', () => { + expectTemplate('num1: {{num1}}, num2: {{num2}}') + .withInput({ + num1: 42, + num2: 0, + }) + .toCompileTo('num1: 42, num2: 0'); + + expectTemplate('num: {{.}}').withInput(0).toCompileTo('num: 0'); + + expectTemplate('num: {{num1/num2}}') + .withInput({ num1: { num2: 0 } }) + .toCompileTo('num: 0'); + }); + + it('false', () => { + /* eslint-disable no-new-wrappers */ + expectTemplate('val1: {{val1}}, val2: {{val2}}') + .withInput({ + val1: false, + val2: new Boolean(false), + }) + .toCompileTo('val1: false, val2: false'); + + expectTemplate('val: {{.}}').withInput(false).toCompileTo('val: false'); + + expectTemplate('val: {{val1/val2}}') + .withInput({ val1: { val2: false } }) + .toCompileTo('val: false'); + + expectTemplate('val1: {{{val1}}}, val2: {{{val2}}}') + .withInput({ + val1: false, + val2: new Boolean(false), + }) + .toCompileTo('val1: false, val2: false'); + + expectTemplate('val: {{{val1/val2}}}') + .withInput({ val1: { val2: false } }) + .toCompileTo('val: false'); + /* eslint-enable */ + }); + + it('should handle undefined and null', () => { + expectTemplate('{{awesome undefined null}}') + .withInput({ + awesome(_undefined: any, _null: any, options: any) { + return (_undefined === undefined) + ' ' + (_null === null) + ' ' + typeof options; + }, + }) + .toCompileTo('true true object'); + + expectTemplate('{{undefined}}') + .withInput({ + undefined() { + return 'undefined!'; + }, + }) + .toCompileTo('undefined!'); + + expectTemplate('{{null}}') + .withInput({ + null() { + return 'null!'; + }, + }) + .toCompileTo('null!'); + }); + + it('newlines', () => { + expectTemplate("Alan's\nTest").toCompileTo("Alan's\nTest"); + expectTemplate("Alan's\rTest").toCompileTo("Alan's\rTest"); + }); + + it('escaping text', () => { + expectTemplate("Awesome's").toCompileTo("Awesome's"); + expectTemplate('Awesome\\').toCompileTo('Awesome\\'); + expectTemplate('Awesome\\\\ foo').toCompileTo('Awesome\\\\ foo'); + expectTemplate('Awesome {{foo}}').withInput({ foo: '\\' }).toCompileTo('Awesome \\'); + expectTemplate(" ' ' ").toCompileTo(" ' ' "); + }); + + it('escaping expressions', () => { + expectTemplate('{{{awesome}}}').withInput({ awesome: "&'\\<>" }).toCompileTo("&'\\<>"); + + expectTemplate('{{&awesome}}').withInput({ awesome: "&'\\<>" }).toCompileTo("&'\\<>"); + + expectTemplate('{{awesome}}') + .withInput({ awesome: '&"\'`\\<>' }) + .toCompileTo('&"'`\\<>'); + + expectTemplate('{{awesome}}') + .withInput({ awesome: 'Escaped, looks like: <b>' }) + .toCompileTo('Escaped, <b> looks like: &lt;b&gt;'); + }); + + it("functions returning safestrings shouldn't be escaped", () => { + expectTemplate('{{awesome}}') + .withInput({ + awesome() { + return new Handlebars.SafeString("&'\\<>"); + }, + }) + .toCompileTo("&'\\<>"); + }); + + it('functions', () => { + expectTemplate('{{awesome}}') + .withInput({ + awesome() { + return 'Awesome'; + }, + }) + .toCompileTo('Awesome'); + + expectTemplate('{{awesome}}') + .withInput({ + awesome() { + return this.more; + }, + more: 'More awesome', + }) + .toCompileTo('More awesome'); + }); + + it('functions with context argument', () => { + expectTemplate('{{awesome frank}}') + .withInput({ + awesome(context: any) { + return context; + }, + frank: 'Frank', + }) + .toCompileTo('Frank'); + }); + + it('pathed functions with context argument', () => { + expectTemplate('{{bar.awesome frank}}') + .withInput({ + bar: { + awesome(context: any) { + return context; + }, + }, + frank: 'Frank', + }) + .toCompileTo('Frank'); + }); + + it('depthed functions with context argument', () => { + expectTemplate('{{#with frank}}{{../awesome .}}{{/with}}') + .withInput({ + awesome(context: any) { + return context; + }, + frank: 'Frank', + }) + .toCompileTo('Frank'); + }); + + it('block functions with context argument', () => { + expectTemplate('{{#awesome 1}}inner {{.}}{{/awesome}}') + .withInput({ + awesome(context: any, options: any) { + return options.fn(context); + }, + }) + .toCompileTo('inner 1'); + }); + + it('depthed block functions with context argument', () => { + expectTemplate('{{#with value}}{{#../awesome 1}}inner {{.}}{{/../awesome}}{{/with}}') + .withInput({ + value: true, + awesome(context: any, options: any) { + return options.fn(context); + }, + }) + .toCompileTo('inner 1'); + }); + + it('block functions without context argument', () => { + expectTemplate('{{#awesome}}inner{{/awesome}}') + .withInput({ + awesome(options: any) { + return options.fn(this); + }, + }) + .toCompileTo('inner'); + }); + + it('pathed block functions without context argument', () => { + expectTemplate('{{#foo.awesome}}inner{{/foo.awesome}}') + .withInput({ + foo: { + awesome() { + return this; + }, + }, + }) + .toCompileTo('inner'); + }); + + it('depthed block functions without context argument', () => { + expectTemplate('{{#with value}}{{#../awesome}}inner{{/../awesome}}{{/with}}') + .withInput({ + value: true, + awesome() { + return this; + }, + }) + .toCompileTo('inner'); + }); + + it('paths with hyphens', () => { + expectTemplate('{{foo-bar}}').withInput({ 'foo-bar': 'baz' }).toCompileTo('baz'); + + expectTemplate('{{foo.foo-bar}}') + .withInput({ foo: { 'foo-bar': 'baz' } }) + .toCompileTo('baz'); + + expectTemplate('{{foo/foo-bar}}') + .withInput({ foo: { 'foo-bar': 'baz' } }) + .toCompileTo('baz'); + }); + + it('nested paths', () => { + expectTemplate('Goodbye {{alan/expression}} world!') + .withInput({ alan: { expression: 'beautiful' } }) + .toCompileTo('Goodbye beautiful world!'); + }); + + it('nested paths with empty string value', () => { + expectTemplate('Goodbye {{alan/expression}} world!') + .withInput({ alan: { expression: '' } }) + .toCompileTo('Goodbye world!'); + }); + + it('literal paths', () => { + expectTemplate('Goodbye {{[@alan]/expression}} world!') + .withInput({ '@alan': { expression: 'beautiful' } }) + .toCompileTo('Goodbye beautiful world!'); + + expectTemplate('Goodbye {{[foo bar]/expression}} world!') + .withInput({ 'foo bar': { expression: 'beautiful' } }) + .toCompileTo('Goodbye beautiful world!'); + }); + + it('literal references', () => { + expectTemplate('Goodbye {{[foo bar]}} world!') + .withInput({ 'foo bar': 'beautiful' }) + .toCompileTo('Goodbye beautiful world!'); + + expectTemplate('Goodbye {{"foo bar"}} world!') + .withInput({ 'foo bar': 'beautiful' }) + .toCompileTo('Goodbye beautiful world!'); + + expectTemplate("Goodbye {{'foo bar'}} world!") + .withInput({ 'foo bar': 'beautiful' }) + .toCompileTo('Goodbye beautiful world!'); + + expectTemplate('Goodbye {{"foo[bar"}} world!') + .withInput({ 'foo[bar': 'beautiful' }) + .toCompileTo('Goodbye beautiful world!'); + + expectTemplate('Goodbye {{"foo\'bar"}} world!') + .withInput({ "foo'bar": 'beautiful' }) + .toCompileTo('Goodbye beautiful world!'); + + expectTemplate("Goodbye {{'foo\"bar'}} world!") + .withInput({ 'foo"bar': 'beautiful' }) + .toCompileTo('Goodbye beautiful world!'); + }); + + it("that current context path ({{.}}) doesn't hit helpers", () => { + expectTemplate('test: {{.}}') + .withInput(null) + // @ts-expect-error Setting the helper to a string instead of a function doesn't make sense normally, but here it doesn't matter + .withHelpers({ helper: 'awesome' }) + .toCompileTo('test: '); + }); + + it('complex but empty paths', () => { + expectTemplate('{{person/name}}') + .withInput({ person: { name: null } }) + .toCompileTo(''); + + expectTemplate('{{person/name}}').withInput({ person: {} }).toCompileTo(''); + }); + + it('this keyword in paths', () => { + expectTemplate('{{#goodbyes}}{{this}}{{/goodbyes}}') + .withInput({ goodbyes: ['goodbye', 'Goodbye', 'GOODBYE'] }) + .toCompileTo('goodbyeGoodbyeGOODBYE'); + + expectTemplate('{{#hellos}}{{this/text}}{{/hellos}}') + .withInput({ + hellos: [{ text: 'hello' }, { text: 'Hello' }, { text: 'HELLO' }], + }) + .toCompileTo('helloHelloHELLO'); + }); + + it('this keyword nested inside path', () => { + expectTemplate('{{#hellos}}{{text/this/foo}}{{/hellos}}').toThrow( + 'Invalid path: text/this - 1:13' + ); + + expectTemplate('{{[this]}}').withInput({ this: 'bar' }).toCompileTo('bar'); + + expectTemplate('{{text/[this]}}') + .withInput({ text: { this: 'bar' } }) + .toCompileTo('bar'); + }); + + it('this keyword in helpers', () => { + const helpers = { + foo(value: any) { + return 'bar ' + value; + }, + }; + + expectTemplate('{{#goodbyes}}{{foo this}}{{/goodbyes}}') + .withInput({ goodbyes: ['goodbye', 'Goodbye', 'GOODBYE'] }) + .withHelpers(helpers) + .toCompileTo('bar goodbyebar Goodbyebar GOODBYE'); + + expectTemplate('{{#hellos}}{{foo this/text}}{{/hellos}}') + .withInput({ + hellos: [{ text: 'hello' }, { text: 'Hello' }, { text: 'HELLO' }], + }) + .withHelpers(helpers) + .toCompileTo('bar hellobar Hellobar HELLO'); + }); + + it('this keyword nested inside helpers param', () => { + expectTemplate('{{#hellos}}{{foo text/this/foo}}{{/hellos}}').toThrow( + 'Invalid path: text/this - 1:17' + ); + + expectTemplate('{{foo [this]}}') + .withInput({ + foo(value: any) { + return value; + }, + this: 'bar', + }) + .toCompileTo('bar'); + + expectTemplate('{{foo text/[this]}}') + .withInput({ + foo(value: any) { + return value; + }, + text: { this: 'bar' }, + }) + .toCompileTo('bar'); + }); + + it('pass string literals', () => { + expectTemplate('{{"foo"}}').toCompileTo(''); + expectTemplate('{{"foo"}}').withInput({ foo: 'bar' }).toCompileTo('bar'); + + expectTemplate('{{#"foo"}}{{.}}{{/"foo"}}') + .withInput({ + foo: ['bar', 'baz'], + }) + .toCompileTo('barbaz'); + }); + + it('pass number literals', () => { + expectTemplate('{{12}}').toCompileTo(''); + expectTemplate('{{12}}').withInput({ '12': 'bar' }).toCompileTo('bar'); + expectTemplate('{{12.34}}').toCompileTo(''); + expectTemplate('{{12.34}}').withInput({ '12.34': 'bar' }).toCompileTo('bar'); + expectTemplate('{{12.34 1}}') + .withInput({ + '12.34'(arg: any) { + return 'bar' + arg; + }, + }) + .toCompileTo('bar1'); + }); + + it('pass boolean literals', () => { + expectTemplate('{{true}}').toCompileTo(''); + expectTemplate('{{true}}').withInput({ '': 'foo' }).toCompileTo(''); + expectTemplate('{{false}}').withInput({ false: 'foo' }).toCompileTo('foo'); + }); + + it('should handle literals in subexpression', () => { + expectTemplate('{{foo (false)}}') + .withInput({ + false() { + return 'bar'; + }, + }) + .withHelper('foo', function (arg) { + return arg; + }) + .toCompileTo('bar'); + }); +}); -- cgit v1.2.3