aboutsummaryrefslogtreecommitdiff
path: root/dev/lib/handlebars/index.test.ts
diff options
context:
space:
mode:
Diffstat (limited to 'dev/lib/handlebars/index.test.ts')
-rw-r--r--dev/lib/handlebars/index.test.ts567
1 files changed, 0 insertions, 567 deletions
diff --git a/dev/lib/handlebars/index.test.ts b/dev/lib/handlebars/index.test.ts
deleted file mode 100644
index ed607db1..00000000
--- a/dev/lib/handlebars/index.test.ts
+++ /dev/null
@@ -1,567 +0,0 @@
-/*
- * Elasticsearch B.V licenses this file to you under the MIT License.
- * See `packages/kbn-handlebars/LICENSE` for more information.
- */
-
-/**
- * ABOUT THIS FILE:
- *
- * This file is for tests not copied from the upstream handlebars project, but
- * tests that we feel are needed in order to fully cover our use-cases.
- */
-
-import Handlebars from '.';
-import type { HelperOptions, TemplateDelegate } from './src/types';
-import { expectTemplate, forEachCompileFunctionName } from './src/__jest__/test_bench';
-
-it('Handlebars.create', () => {
- expect(Handlebars.create()).toMatchSnapshot();
-});
-
-describe('Handlebars.compileAST', () => {
- describe('compiler options', () => {
- it('noEscape', () => {
- expectTemplate('{{value}}').withInput({ value: '<foo>' }).toCompileTo('&lt;foo&gt;');
-
- expectTemplate('{{value}}')
- .withCompileOptions({ noEscape: false })
- .withInput({ value: '<foo>' })
- .toCompileTo('&lt;foo&gt;');
-
- expectTemplate('{{value}}')
- .withCompileOptions({ noEscape: true })
- .withInput({ value: '<foo>' })
- .toCompileTo('<foo>');
- });
- });
-
- it('invalid template', () => {
- expectTemplate('{{value').withInput({ value: 42 }).toThrow(`Parse error on line 1:
-{{value
---^
-Expecting 'ID', 'STRING', 'NUMBER', 'BOOLEAN', 'UNDEFINED', 'NULL', 'DATA', got 'INVALID'`);
- });
-
- if (!process.env.EVAL) {
- it('reassign', () => {
- const fn = Handlebars.compileAST;
- expect(fn('{{value}}')({ value: 42 })).toEqual('42');
- });
- }
-});
-
-// Extra "helpers" tests
-describe('helpers', () => {
- it('Only provide options.fn/inverse to block helpers', () => {
- function toHaveProperties(...args: any[]) {
- toHaveProperties.calls++;
- const options = args[args.length - 1];
- expect(options).toHaveProperty('fn');
- expect(options).toHaveProperty('inverse');
- return 42;
- }
- toHaveProperties.calls = 0;
-
- function toNotHaveProperties(...args: any[]) {
- toNotHaveProperties.calls++;
- const options = args[args.length - 1];
- expect(options).not.toHaveProperty('fn');
- expect(options).not.toHaveProperty('inverse');
- return 42;
- }
- toNotHaveProperties.calls = 0;
-
- const nonBlockTemplates = ['{{foo}}', '{{foo 1 2}}'];
- const blockTemplates = ['{{#foo}}42{{/foo}}', '{{#foo 1 2}}42{{/foo}}'];
-
- for (const template of nonBlockTemplates) {
- expectTemplate(template)
- .withInput({
- foo: toNotHaveProperties,
- })
- .toCompileTo('42');
-
- expectTemplate(template).withHelper('foo', toNotHaveProperties).toCompileTo('42');
- }
-
- for (const template of blockTemplates) {
- expectTemplate(template)
- .withInput({
- foo: toHaveProperties,
- })
- .toCompileTo('42');
-
- expectTemplate(template).withHelper('foo', toHaveProperties).toCompileTo('42');
- }
-
- const factor = process.env.AST || process.env.EVAL ? 1 : 2;
- expect(toNotHaveProperties.calls).toEqual(nonBlockTemplates.length * 2 * factor);
- expect(toHaveProperties.calls).toEqual(blockTemplates.length * 2 * factor);
- });
-
- it('should pass expected "this" to helper functions (without input)', () => {
- expectTemplate('{{hello "world" 12 true false}}')
- .withHelper('hello', function (this: any, ...args: any[]) {
- expect(this).toMatchInlineSnapshot(`Object {}`);
- })
- .toCompileTo('');
- });
-
- it('should pass expected "this" to helper functions (with input)', () => {
- expectTemplate('{{hello "world" 12 true false}}')
- .withHelper('hello', function (this: any, ...args: any[]) {
- expect(this).toMatchInlineSnapshot(`
- Object {
- "people": Array [
- Object {
- "id": 1,
- "name": "Alan",
- },
- Object {
- "id": 2,
- "name": "Yehuda",
- },
- ],
- }
- `);
- })
- .withInput({
- people: [
- { name: 'Alan', id: 1 },
- { name: 'Yehuda', id: 2 },
- ],
- })
- .toCompileTo('');
- });
-
- it('should pass expected "this" and arguments to helper functions (non-block helper)', () => {
- expectTemplate('{{hello "world" 12 true false}}')
- .withHelper('hello', function (this: any, ...args: any[]) {
- expect(args).toMatchInlineSnapshot(`
- Array [
- "world",
- 12,
- true,
- false,
- Object {
- "data": Object {
- "root": Object {
- "people": Array [
- Object {
- "id": 1,
- "name": "Alan",
- },
- Object {
- "id": 2,
- "name": "Yehuda",
- },
- ],
- },
- },
- "hash": Object {},
- "loc": Object {
- "end": Object {
- "column": 31,
- "line": 1,
- },
- "start": Object {
- "column": 0,
- "line": 1,
- },
- },
- "lookupProperty": [Function],
- "name": "hello",
- },
- ]
- `);
- })
- .withInput({
- people: [
- { name: 'Alan', id: 1 },
- { name: 'Yehuda', id: 2 },
- ],
- })
- .toCompileTo('');
- });
-
- it('should pass expected "this" and arguments to helper functions (block helper)', () => {
- expectTemplate('{{#hello "world" 12 true false}}{{/hello}}')
- .withHelper('hello', function (this: any, ...args: any[]) {
- expect(args).toMatchInlineSnapshot(`
- Array [
- "world",
- 12,
- true,
- false,
- Object {
- "data": Object {
- "root": Object {
- "people": Array [
- Object {
- "id": 1,
- "name": "Alan",
- },
- Object {
- "id": 2,
- "name": "Yehuda",
- },
- ],
- },
- },
- "fn": [Function],
- "hash": Object {},
- "inverse": [Function],
- "loc": Object {
- "end": Object {
- "column": 42,
- "line": 1,
- },
- "start": Object {
- "column": 0,
- "line": 1,
- },
- },
- "lookupProperty": [Function],
- "name": "hello",
- },
- ]
- `);
- })
- .withInput({
- people: [
- { name: 'Alan', id: 1 },
- { name: 'Yehuda', id: 2 },
- ],
- })
- .toCompileTo('');
- });
-});
-
-// Extra "blocks" tests
-describe('blocks', () => {
- describe('decorators', () => {
- it('should only call decorator once', () => {
- let calls = 0;
- const callsExpected = process.env.AST || process.env.EVAL ? 1 : 2;
- expectTemplate('{{#helper}}{{*decorator}}{{/helper}}')
- .withHelper('helper', () => {})
- .withDecorator('decorator', () => {
- calls++;
- })
- .toCompileTo('');
- expect(calls).toEqual(callsExpected);
- });
-
- forEachCompileFunctionName((compileName) => {
- it(`should call decorator again if render function is called again for #${compileName}`, () => {
- global.kbnHandlebarsEnv = Handlebars.create();
-
- kbnHandlebarsEnv!.registerDecorator('decorator', () => {
- calls++;
- });
-
- const compile = kbnHandlebarsEnv![compileName].bind(kbnHandlebarsEnv);
- const render = compile('{{*decorator}}');
-
- let calls = 0;
- expect(render()).toEqual('');
- expect(calls).toEqual(1);
-
- calls = 0;
- expect(render()).toEqual('');
- expect(calls).toEqual(1);
-
- global.kbnHandlebarsEnv = null;
- });
- });
-
- it('should pass expected options to nested decorator', () => {
- expectTemplate('{{#helper}}{{*decorator foo}}{{/helper}}')
- .withHelper('helper', () => {})
- .withDecorator('decorator', function (fn, props, container, options) {
- expect(options).toMatchInlineSnapshot(`
- Object {
- "args": Array [
- "bar",
- ],
- "data": Object {
- "root": Object {
- "foo": "bar",
- },
- },
- "hash": Object {},
- "loc": Object {
- "end": Object {
- "column": 29,
- "line": 1,
- },
- "start": Object {
- "column": 11,
- "line": 1,
- },
- },
- "name": "decorator",
- }
- `);
- })
- .withInput({ foo: 'bar' })
- .toCompileTo('');
- });
-
- it('should pass expected options to root decorator with no args', () => {
- expectTemplate('{{*decorator}}')
- .withDecorator('decorator', function (fn, props, container, options) {
- expect(options).toMatchInlineSnapshot(`
- Object {
- "args": Array [],
- "data": Object {
- "root": Object {
- "foo": "bar",
- },
- },
- "hash": Object {},
- "loc": Object {
- "end": Object {
- "column": 14,
- "line": 1,
- },
- "start": Object {
- "column": 0,
- "line": 1,
- },
- },
- "name": "decorator",
- }
- `);
- })
- .withInput({ foo: 'bar' })
- .toCompileTo('');
- });
-
- it('should pass expected options to root decorator with one arg', () => {
- expectTemplate('{{*decorator foo}}')
- .withDecorator('decorator', function (fn, props, container, options) {
- expect(options).toMatchInlineSnapshot(`
- Object {
- "args": Array [
- undefined,
- ],
- "data": Object {
- "root": Object {
- "foo": "bar",
- },
- },
- "hash": Object {},
- "loc": Object {
- "end": Object {
- "column": 18,
- "line": 1,
- },
- "start": Object {
- "column": 0,
- "line": 1,
- },
- },
- "name": "decorator",
- }
- `);
- })
- .withInput({ foo: 'bar' })
- .toCompileTo('');
- });
-
- describe('return values', () => {
- for (const [desc, template, result] of [
- ['non-block', '{{*decorator}}cont{{*decorator}}ent', 'content'],
- ['block', '{{#*decorator}}con{{/decorator}}tent', 'tent'],
- ]) {
- describe(desc, () => {
- const falsy = [undefined, null, false, 0, ''];
- const truthy = [true, 42, 'foo', {}];
-
- // Falsy return values from decorators are simply ignored and the
- // execution falls back to default behavior which is to render the
- // other parts of the template.
- for (const value of falsy) {
- it(`falsy value (type ${typeof value}): ${JSON.stringify(value)}`, () => {
- expectTemplate(template)
- .withDecorator('decorator', () => value)
- .toCompileTo(result);
- });
- }
-
- // Truthy return values from decorators are expected to be functions
- // and the program will attempt to call them. We expect an error to
- // be thrown in this case.
- for (const value of truthy) {
- it(`non-falsy value (type ${typeof value}): ${JSON.stringify(value)}`, () => {
- expectTemplate(template)
- .withDecorator('decorator', () => value)
- .toThrow('is not a function');
- });
- }
-
- // If the decorator return value is a custom function, its return
- // value will be the final content of the template.
- for (const value of [...falsy, ...truthy]) {
- it(`function returning ${typeof value}: ${JSON.stringify(value)}`, () => {
- expectTemplate(template)
- .withDecorator('decorator', () => () => value)
- .toCompileTo(value as string);
- });
- }
- });
- }
- });
-
- describe('custom return function should be called with expected arguments and its return value should be rendered in the template', () => {
- it('root decorator', () => {
- expectTemplate('{{*decorator}}world')
- .withInput({ me: 'my' })
- .withDecorator(
- 'decorator',
- (fn): TemplateDelegate =>
- (context, options) => {
- expect(context).toMatchInlineSnapshot(`
- Object {
- "me": "my",
- }
- `);
- expect(options).toMatchInlineSnapshot(`
- Object {
- "decorators": Object {
- "decorator": [Function],
- },
- "helpers": Object {},
- "partials": Object {},
- }
- `);
- return `hello ${context.me} ${fn()}!`;
- }
- )
- .toCompileTo('hello my world!');
- });
-
- it('decorator nested inside of array-helper', () => {
- expectTemplate('{{#arr}}{{*decorator}}world{{/arr}}')
- .withInput({ arr: ['my'] })
- .withDecorator(
- 'decorator',
- (fn): TemplateDelegate =>
- (context, options) => {
- expect(context).toMatchInlineSnapshot(`"my"`);
- expect(options).toMatchInlineSnapshot(`
- Object {
- "blockParams": Array [
- "my",
- 0,
- ],
- "data": Object {
- "_parent": Object {
- "root": Object {
- "arr": Array [
- "my",
- ],
- },
- },
- "first": true,
- "index": 0,
- "key": 0,
- "last": true,
- "root": Object {
- "arr": Array [
- "my",
- ],
- },
- },
- }
- `);
- return `hello ${context} ${fn()}!`;
- }
- )
- .toCompileTo('hello my world!');
- });
-
- it('decorator nested inside of custom helper', () => {
- expectTemplate('{{#helper}}{{*decorator}}world{{/helper}}')
- .withHelper('helper', function (options: HelperOptions) {
- return options.fn('my', { foo: 'bar' } as any);
- })
- .withDecorator(
- 'decorator',
- (fn): TemplateDelegate =>
- (context, options) => {
- expect(context).toMatchInlineSnapshot(`"my"`);
- expect(options).toMatchInlineSnapshot(`
- Object {
- "foo": "bar",
- }
- `);
- return `hello ${context} ${fn()}!`;
- }
- )
- .toCompileTo('hello my world!');
- });
- });
-
- it('should call multiple decorators in the same program body in the expected order and get the expected output', () => {
- let decoratorCall = 0;
- let progCall = 0;
- expectTemplate('{{*decorator}}con{{*decorator}}tent', {
- beforeRender() {
- // ensure the counters are reset between EVAL/AST render calls
- decoratorCall = 0;
- progCall = 0;
- },
- })
- .withInput({
- decoratorCall: 0,
- progCall: 0,
- })
- .withDecorator('decorator', (fn) => {
- const decoratorCallOrder = ++decoratorCall;
- const ret: TemplateDelegate = () => {
- const progCallOrder = ++progCall;
- return `(decorator: ${decoratorCallOrder}, prog: ${progCallOrder}, fn: "${fn()}")`;
- };
- return ret;
- })
- .toCompileTo('(decorator: 2, prog: 1, fn: "(decorator: 1, prog: 2, fn: "content")")');
- });
-
- describe('registration', () => {
- beforeEach(() => {
- global.kbnHandlebarsEnv = Handlebars.create();
- });
-
- afterEach(() => {
- global.kbnHandlebarsEnv = null;
- });
-
- it('should be able to call decorators registered using the `registerDecorator` function', () => {
- let calls = 0;
- const callsExpected = process.env.AST || process.env.EVAL ? 1 : 2;
-
- kbnHandlebarsEnv!.registerDecorator('decorator', () => {
- calls++;
- });
-
- expectTemplate('{{*decorator}}').toCompileTo('');
- expect(calls).toEqual(callsExpected);
- });
-
- it('should not be able to call decorators unregistered using the `unregisterDecorator` function', () => {
- let calls = 0;
-
- kbnHandlebarsEnv!.registerDecorator('decorator', () => {
- calls++;
- });
-
- kbnHandlebarsEnv!.unregisterDecorator('decorator');
-
- expectTemplate('{{*decorator}}').toThrow('lookupProperty(...) is not a function');
- expect(calls).toEqual(0);
- });
- });
- });
-});