UNPKG

@cloudpss/template

Version:

String and object template engine for Node.js and the browser.

309 lines (302 loc) 12.1 kB
import { randomBytes, randomFillSync } from 'node:crypto'; import { setTimeout as timeout } from 'node:timers/promises'; import { template } from '@cloudpss/template'; describe('template', () => { it('should handle primitives', () => { expect(template('')()).toEqual(''); expect(template('Hello, world!')()).toEqual('Hello, world!'); expect(template(1)()).toEqual(1); expect(template(1.1)()).toEqual(1.1); expect(template(Number.NaN)()).toEqual(Number.NaN); expect(template(Number.POSITIVE_INFINITY)()).toEqual(Number.POSITIVE_INFINITY); expect(template(Number.NEGATIVE_INFINITY)()).toEqual(Number.NEGATIVE_INFINITY); expect(template(Number.EPSILON)()).toEqual(Number.EPSILON); expect(template(true)()).toEqual(true); expect(template(false)()).toEqual(false); expect(template(null)()).toEqual(null); expect(template(undefined)()).toEqual(undefined); expect(template(1n)()).toEqual(1n); expect( template({ string: 'Hello, world!', number: 1, boolean: true, null: null, undefined: undefined, bigint: 1n, })(), ).toEqual({ string: 'Hello, world!', number: 1, boolean: true, null: null, undefined: undefined, bigint: 1n, }); }); it('should ignore symbols', () => { expect(template(Symbol(''))()).toEqual(undefined); expect( template({ key: Symbol(''), [Symbol('')]: 1, })(), ).toEqual({ key: undefined, }); }); it('should ignore functions', () => { expect(template(() => 1)()).toEqual(undefined); expect( template({ key: () => 1, })(), ).toEqual({ key: undefined, }); }); it('should copy arrays', () => { const arr = [1, 2, {}]; const result = template(arr)(); expect(result).toEqual(arr); expect(result).not.toBe(arr); expect(result[2]).not.toBe(arr[2]); }); it('should copy objects', () => { const obj = { a: 1, b: 2, c: {} }; const result = template(obj)(); expect(result).toEqual(obj); expect(result).not.toBe(obj); expect(result.c).not.toBe(obj.c); }); it('should copy dates', () => { const date = new Date(); const result = template(date)(); expect(result).toEqual(date); expect(result).not.toBe(date); }); it('should copy regexps', () => { const regexp = /./gi; const result = template(regexp)(); expect(result).toEqual(regexp); expect(result).not.toBe(regexp); }); describe('should copy errors', () => { it('Error', () => { const error = new Error('test error'); const result = template(error)(); expect(result).toBeInstanceOf(Error); expect(result.name).toBe(error.name); expect(result.message).toBe(error.message); expect(result).not.toBe(error); }); it('TypeError', () => { const error = new TypeError('test error'); const result = template(error)(); expect(result).toBeInstanceOf(TypeError); expect(result.name).toBe(error.name); expect(result.message).toBe(error.message); expect(result).not.toBe(error); }); it('SyntaxError with custom name', () => { const error = new SyntaxError('test error'); error.name = 'CustomError'; const result = template(error)(); expect(result).toBeInstanceOf(SyntaxError); expect(result.name).toBe(error.name); expect(result.message).toBe(error.message); expect(result).not.toBe(error); }); it('DOMException', () => { const error = new DOMException('test error', 'AbortError'); const result = template(error)(); expect(result).toBeInstanceOf(DOMException); expect(result.name).toBe(error.name); expect(result.message).toBe(error.message); expect(result.code).toBe(error.code); expect(result).not.toBe(error); }); it('CustomError', () => { const error = new (class CustomError extends Error {})('test error'); const result = template(error)(); expect(result).toBeInstanceOf(Error); expect(result.name).toBe(error.name); expect(result.message).toBe(error.message); expect(result).not.toBe(error); }); }); describe('should copy ArrayBuffers', () => { it('ArrayBuffer', () => { const { buffer } = randomBytes(16); const t = template(buffer); const result1 = t(); const result2 = t(); expect(new Uint8Array(result1)).toEqual(new Uint8Array(buffer)); expect(new Uint8Array(result2)).toEqual(new Uint8Array(buffer)); expect(result1).not.toBe(buffer); expect(result2).not.toBe(buffer); expect(result1).not.toBe(result2); }); it('SharedArrayBuffer', () => { const buffer = new SharedArrayBuffer(16); randomFillSync(new Uint8Array(buffer)); const result = template(buffer)(); expect(new Uint8Array(result)).toEqual(new Uint8Array(buffer)); expect(result).toBeInstanceOf(SharedArrayBuffer); expect(result).not.toBe(buffer); }); }); describe('should copy ArrayBufferViews', () => { it('DataView', () => { const { buffer } = randomBytes(64); const view = new DataView(buffer, 32, 16); const result = template(view)(); expect(result).toEqual(view); expect(result).not.toBe(view); expect(new Uint8Array(result.buffer)).toEqual(new Uint8Array(buffer, view.byteOffset, view.byteLength)); expect(result.buffer).not.toBe(buffer); }); it('Uint8Array', () => { const { buffer } = randomBytes(64); const view = new Uint8Array(buffer, 32, 16); const result = template(view)(); expect(result).toEqual(view); expect(result).not.toBe(view); expect(new Uint8Array(result.buffer)).toEqual(new Uint8Array(buffer, view.byteOffset, view.byteLength)); expect(result.buffer).not.toBe(buffer); }); it('Float64Array', () => { const { buffer } = randomBytes(64); const view = new Float64Array(buffer, 32, 1); const result = template(view)(); expect(result).toEqual(view); expect(result).not.toBe(view); expect(new Uint8Array(result.buffer)).toEqual(new Uint8Array(buffer, view.byteOffset, view.byteLength)); expect(result.buffer).not.toBe(buffer); }); it('BigInt64Array', () => { const { buffer } = randomBytes(64); const view = new BigInt64Array(buffer, 32, 1); const t = template(view); const result1 = t(); const result2 = t(); expect(result1).toEqual(view); expect(result1).not.toBe(view); expect(result2).toEqual(view); expect(result2).not.toBe(view); expect(result2).not.toBe(result1); expect(new Uint8Array(result1.buffer)).toEqual(new Uint8Array(buffer, view.byteOffset, view.byteLength)); expect(new Uint8Array(result2.buffer)).toEqual(new Uint8Array(buffer, view.byteOffset, view.byteLength)); expect(result1.buffer).not.toBe(buffer); expect(result2.buffer).not.toBe(buffer); expect(result1.buffer).not.toBe(result2.buffer); }); }); it('should build interpolated strings', () => { const t = template({ hello: '$Hello, ${name}!', not: 'Hello $name', array: ['$$name', '$not'] }); const result = t({ name: 'world', }); expect(result).toEqual({ hello: 'Hello, world!', not: 'Hello $name', array: ['world', 'not'], }); }); it('should ignore properties on the prototype chain', () => { const obj = Object.create({ prototype: 'chain' }); obj.own = 'property'; const result = template(obj)(); expect(result).toEqual({ own: 'property' }); }); it('should ignore non-enumerable properties', () => { const obj = Object.create(null, { enumerable: { value: 'property', enumerable: false, }, }); const result = template(obj)(); expect(result).toEqual({}); }); describe('should work while objectKeyMode is', () => { it('template', () => { const obj = { $: 1, _: 2, '=x': 3, $$y: 4 }; const result = template(obj, { objectKeyMode: 'template' })({ x: 11n, y: 12 }); expect(result).toEqual({ $: 1, _: 2, 11: 3, 12: 4, }); }); it('ignore', () => { const obj = { $: 1, _: 2, '=x': 3, $$y: 4 }; const result = template(obj, { objectKeyMode: 'ignore' })({ x: 11n, y: 12 }); expect(result).toEqual({ $: 1, _: 2, '=x': 3, $$y: 4, }); }); }); describe('should work with default evaluator', () => { it('with formula', () => { const obj = { a: '=a', b: '=b' }; const result = template(obj)({ a: 1 }); expect(result).toEqual({ a: 1, b: undefined }); }); it('with interpolation', () => { const obj = { a: '$$a', b: '$$b' }; const result = template(obj)({ a: 1 }); expect(result).toEqual({ a: '1', b: '' }); }); }); describe('should work with custom evaluator', () => { it('with formula', () => { const obj = { a: '=a', b: '=b' }; const result = template(obj, { evaluator: { inject: { a: 'abcd', b: 11n }, compile: (expression) => `evaluator.${expression}`, }, })({ a: 1 }); expect(result).toEqual({ a: 'abcd', b: 11n }); }); it('with interpolation', () => { const obj = { a: '$$a', b: '$$b' }; const result = template(obj, { evaluator: { inject: { a: 'abcd', b: 11n }, compile: (expression) => `evaluator.${expression}`, }, })({ a: 1 }); expect(result).toEqual({ a: 'abcd', b: '11' }); }); }); describe('should work with async evaluator', () => { it('with formula', async () => { const obj = { a: '=a', b: '=b' }; const result = template(obj, { evaluator: { inject: { a: 'abcd', b: 11n, timeout }, async: true, compile: (expression) => `await evaluator.timeout(100, evaluator.${expression})`, }, })({ a: 1 }); await expect(result).resolves.toEqual({ a: 'abcd', b: 11n }); }); it('with interpolation', async () => { const obj = { a: '$$a', b: '$$b' }; const result = template(obj, { evaluator: { inject: { a: 'abcd', b: 11n, timeout }, async: true, compile: (expression) => `await evaluator.timeout(100, evaluator.${expression})`, }, })({ a: 1 }); await expect(result).resolves.toEqual({ a: 'abcd', b: '11' }); }); }); });