Remove async tests

This commit is contained in:
Anton Liaposhchenko 2024-12-28 20:30:35 +02:00
parent 94b2347867
commit 2202020fba
100 changed files with 235 additions and 1135 deletions
src/test/tests
integration
TestBase.ts
comparison
deprecation
exceptions
expressions
filters
functions
globals
implicit-auto-escaping
operators
parsing
sandboxing
source-map
tags
test.ts
tests
unit

@ -1,26 +1,25 @@
import * as tape from 'tape';
import {SinonStub, stub} from 'sinon';
import {
createEnvironment,
createSynchronousEnvironment,
TwingEnvironmentOptions,
TwingSynchronousEnvironmentOptions
} from "../../../main/lib/environment";
import {createPrintNode} from "../../../main/lib/node/print";
import {createConstantNode} from "../../../main/lib/node/expression/constant";
import {TwingExtension, TwingSynchronousExtension} from "../../../main/lib/extension";
import {createFilter, createSynchronousFilter} from "../../../main/lib/filter";
import {createFunction, createSynchronousFunction} from "../../../main/lib/function";
import {createSynchronousTest, createTest} from "../../../main/lib/test";
import {TwingSynchronousExtension} from "../../../main/lib/extension";
import {createSynchronousFilter} from "../../../main/lib/filter";
import {createSynchronousFunction} from "../../../main/lib/function";
import {createSynchronousTest} from "../../../main/lib/test";
import {createSandboxSecurityPolicy} from "../../../main/lib/sandbox/security-policy";
import {createArrayLoader, createSynchronousArrayLoader} from "../../../main/lib/loader/array";
import {escape, escapeSynchronously} from "../../../main/lib/extension/core/filters/escape";
import {createSynchronousArrayLoader} from "../../../main/lib/loader/array";
import {escapeSynchronously} from "../../../main/lib/extension/core/filters/escape";
import {IntegrationTest} from "./test";
import {TwingTagHandler} from "../../../main/lib/tag-handler";
import {MappingItem, RawSourceMap, SourceMapConsumer} from "source-map";
import {TwingLoader, TwingSynchronousLoader} from "../../../main/lib/loader";
import {Settings} from "luxon";
import type {TwingExecutionContext, TwingSynchronousExecutionContext} from "../../../main/lib/execution-context";
import type {TwingSynchronousExecutionContext} from "../../../main/lib/execution-context";
const createSectionTokenParser = (): TwingTagHandler => {
return {
@ -35,131 +34,6 @@ const createSectionTokenParser = (): TwingTagHandler => {
};
};
class TwingTestExtension implements TwingExtension {
static staticCall(_executionContext: TwingExecutionContext, value: string) {
return Promise.resolve(`*${value}*`);
}
static __callStatic(method: string, ...arguments_: any[]) {
if (method !== 'magicStaticCall') {
throw new Error('Unexpected call to __callStatic');
}
return Promise.resolve('static_magic_' + arguments_[0]);
}
get nodeVisitors() {
return [];
}
get operators() {
return [];
}
get sourceMapNodeFactories() {
return [];
}
get tagHandlers() {
return [
createSectionTokenParser()
];
}
get filters() {
return [
createFilter('escape_and_nl2br', escape_and_nl2br, []),
// name this filter "nl2br_" to allow the core "nl2br" filter to be tested
createFilter('nl2br_', nl2br, [{
name: 'separator'
}]),
createFilter('§', this.sectionFilter, []),
createFilter('escape_something', escape_something, []),
createFilter('preserves_safety', preserves_safety, []),
createFilter('static_call_string', TwingTestExtension.staticCall, []),
createFilter('static_call_array', TwingTestExtension.staticCall, []),
createFilter('magic_call_string', function () {
return TwingTestExtension.__callStatic('magicStaticCall', arguments);
}, []),
createFilter('magic_call_array', function () {
return TwingTestExtension.__callStatic('magicStaticCall', arguments);
}, []),
createFilter('*_path', dynamic_path, []),
createFilter('*_foo_*_bar', dynamic_foo, []),
createFilter('anon_foo', (_executionContext: TwingExecutionContext, name: string) => {
return Promise.resolve('*' + name + '*');
}, []),
];
}
get functions() {
return [
createFunction('§', this.sectionFunction, [{
name: 'value'
}]),
createFunction('safe_br', this.br, []),
createFunction('unsafe_br', this.br, []),
createFunction('static_call_string', TwingTestExtension.staticCall, [{
name: 'value'
}]),
createFunction('static_call_array', TwingTestExtension.staticCall, [{
name: 'value'
}]),
createFunction('*_path', dynamic_path, [{
name: 'item'
}]),
createFunction('*_foo_*_bar', dynamic_foo, [{
name: 'item'
}]),
createFunction('anon_foo', (_executionContext: TwingExecutionContext, name: string) => {
return Promise.resolve('*' + name + '*');
}, [{
name: 'name'
}]),
createFunction('createObject', (_executionContext: TwingExecutionContext, attributes: Map<string, any>) => {
const object: {
[p: string]: any
} = {};
for (let [key, value] of attributes) {
object[key] = value;
}
return Promise.resolve(object);
}, [{
name: 'attributes'
}])
];
}
get tests() {
return [
createTest('multi word', this.is_multi_word, []),
createTest('test_*', this.dynamic_test, [])
];
}
sectionFilter(_executionContext: TwingExecutionContext, value: string) {
return Promise.resolve(`§${value}§`);
}
sectionFunction(_executionContext: TwingExecutionContext, value: string) {
return Promise.resolve(`§${value}§`);
}
br() {
return Promise.resolve('<br />');
}
is_multi_word(_executionContext: TwingExecutionContext, value: string) {
return Promise.resolve(value.indexOf(' ') > -1);
}
dynamic_test(_executionContext: TwingExecutionContext, element: any, item: any) {
return Promise.resolve(element === item);
}
}
class TwingSynchronousTestExtension implements TwingSynchronousExtension {
static staticCall(_executionContext: TwingSynchronousExecutionContext, value: string) {
return `*${value}*`;
@ -204,10 +78,10 @@ class TwingSynchronousTestExtension implements TwingSynchronousExtension {
createSynchronousFilter('static_call_string', TwingSynchronousTestExtension.staticCall, []),
createSynchronousFilter('static_call_array', TwingSynchronousTestExtension.staticCall, []),
createSynchronousFilter('magic_call_string', function () {
return TwingTestExtension.__callStatic('magicStaticCall', arguments);
return TwingSynchronousTestExtension.__callStatic('magicStaticCall', arguments);
}, []),
createSynchronousFilter('magic_call_array', function () {
return TwingTestExtension.__callStatic('magicStaticCall', arguments);
return TwingSynchronousTestExtension.__callStatic('magicStaticCall', arguments);
}, []),
createSynchronousFilter('*_path', synchronous_dynamic_path, []),
createSynchronousFilter('*_foo_*_bar', synchronous_dynamic_foo, []),
@ -347,14 +221,6 @@ export default abstract class {
}
}
/**
* nl2br which also escapes, for testing escaper filters.
*/
function escape_and_nl2br(executionContext: TwingExecutionContext, value: string, sep = '<br />') {
return escape(executionContext, value, 'html').then((result) => {
return nl2br(executionContext, result?.toString() || '', sep);
});
}
function synchronous_escape_and_nl2br(executionContext: TwingSynchronousExecutionContext, value: string, sep = '<br />') {
const result = escapeSynchronously(executionContext, value, 'html');
@ -362,45 +228,22 @@ function synchronous_escape_and_nl2br(executionContext: TwingSynchronousExecutio
return synchronous_nl2br(executionContext, result?.toString() || '', sep);
}
/**
* nl2br only, for testing filters with pre_escape.
*/
function nl2br(_executionContext: TwingExecutionContext, value: string, sep = '<br />') {
return Promise.resolve(value.replace('\n', `${sep}\n`));
}
function synchronous_nl2br(_executionContext: TwingSynchronousExecutionContext, value: string, sep = '<br />') {
return value.replace('\n', `${sep}\n`);
}
function escape_something(_executionContext: TwingExecutionContext, value: string) {
return Promise.resolve(value.toUpperCase());
}
function synchronous_escape_something(_executionContext: TwingSynchronousExecutionContext, value: string) {
return value.toUpperCase();
}
function preserves_safety(_executionContext: TwingExecutionContext, value: string) {
return Promise.resolve(value.toUpperCase());
}
function synchronous_preserves_safety(_executionContext: TwingSynchronousExecutionContext, value: string) {
return value.toUpperCase();
}
function dynamic_path(_executionContext: TwingExecutionContext, element: string, item: string) {
return Promise.resolve(element + '/' + item);
}
function synchronous_dynamic_path(_executionContext: TwingSynchronousExecutionContext, element: string, item: string) {
return element + '/' + item;
}
function dynamic_foo(_executionContext: TwingExecutionContext, foo: string, bar: string, item: string) {
return Promise.resolve(foo + '/' + bar + '/' + item);
}
function synchronous_dynamic_foo(_executionContext: TwingSynchronousExecutionContext, foo: string, bar: string, item: string) {
return foo + '/' + bar + '/' + item;
}
@ -418,17 +261,12 @@ export const runTest = async (
Settings.defaultZoneName = "Europe/Paris";
let {
additionalFilters,
additionalSynchronousFilters,
additionalFunctions,
additionalSynchronousFunctions,
additionalNodeVisitors,
additionalTests,
additionalSynchronousTests,
description,
context,
synchronousContext,
environmentOptions,
synchronousEnvironmentOptions,
expectation,
expectedErrorMessage,
@ -446,157 +284,6 @@ export const runTest = async (
} = integrationTest;
tape(description, ({test}) => {
test('asynchronously', ({fail, same, end}) => {
let loader: TwingLoader;
if (!isATestWithALoader(integrationTest)) {
loader = createArrayLoader(integrationTest.templates);
}
else {
loader = integrationTest.loader;
}
if (environmentOptions === undefined) {
environmentOptions = {};
}
if (environmentOptions.parserOptions === undefined) {
environmentOptions.parserOptions = {};
}
if (environmentOptions.parserOptions.level === undefined) {
environmentOptions.parserOptions.level = 2;
}
if (strict === undefined) {
strict = true;
}
let environment = createEnvironment(loader, Object.assign({}, <TwingEnvironmentOptions>{
sandboxPolicy: sandboxPolicy || createSandboxSecurityPolicy({
allowedTags: sandboxSecurityPolicyTags,
allowedFilters: sandboxSecurityPolicyFilters,
allowedFunctions: sandboxSecurityPolicyFunctions,
allowedMethods: sandboxSecurityPolicyMethods,
allowedProperties: sandboxSecurityPolicyProperties
}),
emitsSourceMap: expectedSourceMapMappings !== undefined
}, environmentOptions));
environment.addExtension(new TwingTestExtension());
environment.registerEscapingStrategy((value) => `custom ${value}`, 'custom');
environment.addExtension({
filters: additionalFilters || [],
functions: additionalFunctions || [],
nodeVisitors: additionalNodeVisitors || [],
tagHandlers: [],
tests: additionalTests || [],
operators: []
});
let consoleStub: SinonStub | null = null;
let consoleData: string[] = [];
if (expectedDeprecationMessages) {
consoleStub = stub(console, 'warn').callsFake((data: string) => {
consoleData.push(data);
});
}
return (context ? Promise.resolve(context) : Promise.resolve({})).then(async (context: Record<string, any>) => {
if (!expectedErrorMessage) {
try {
console.time(description);
let actual: string;
let sourceMap: RawSourceMap | null = null;
if (expectedSourceMapMappings !== undefined) {
const result = await environment.renderWithSourceMap('index.twig', context, {
sandboxed,
strict
});
actual = result.data;
sourceMap = result.sourceMap;
}
else {
actual = await environment.render('index.twig', context, {
sandboxed,
strict
});
}
console.timeEnd(description);
if (expectation !== undefined) {
same(actual, expectation, `${description}: renders as expected`);
}
if (trimmedExpectation !== undefined) {
same(actual.trim(), trimmedExpectation.trim(), `${description}: trimmed, renders as expected`);
}
if (consoleStub) {
consoleStub.restore();
same(consoleData, expectedDeprecationMessages, `${description}: outputs deprecation warnings`);
}
if (sourceMap !== null) {
const mappings: Array<MappingItem> = [];
const consumer = new SourceMapConsumer(sourceMap);
consumer.eachMapping(({
source,
generatedLine,
generatedColumn,
originalLine,
originalColumn,
name
}) => {
mappings.push({
source,
generatedLine,
generatedColumn,
originalLine,
originalColumn,
name
});
})
same(mappings, expectedSourceMapMappings);
}
} catch (e) {
console.log(e);
console.timeEnd(description);
fail(`${description}: should not throw an error (${e})`);
}
}
else {
try {
console.time(description);
await environment.render('index.twig', context, {
sandboxed,
strict
});
fail(`${description}: should throw an error`);
} catch (error: any) {
console.timeEnd(description);
same(`${error.name}: ${error.message}`, expectedErrorMessage, `${description}: throws error`);
}
}
end();
});
});
test('synchronously', ({fail, same, end}) => {
let loader: TwingSynchronousLoader;
@ -655,7 +342,7 @@ export const runTest = async (
});
}
const actualContext = synchronousContext || context || {};
const actualContext = synchronousContext || {};
if (!expectedErrorMessage) {
try {

@ -30,7 +30,7 @@ runTest({
0
0
`,
context:{
synchronousContext:{
array: []
}
});

@ -32,7 +32,7 @@ runTest({
{{ true == foo ? 1 : 0 }}
`
},
context: {
synchronousContext: {
foo: new (class{})
},
trimmedExpectation: `

@ -12,7 +12,7 @@ runTest({
1
0
`,
context: {
synchronousContext: {
foo: {},
bar: {}
}

@ -1,15 +1,8 @@
import {runTest} from "../TestBase";
import {createFilter, createSynchronousFilter} from "../../../../main/lib/filter";
import {createSynchronousFilter} from "../../../../main/lib/filter";
runTest({
description: 'deprecated filter',
additionalFilters: [
createFilter('foo', (operand) => {
return Promise.resolve(operand);
}, [], {
deprecated: true
})
],
additionalSynchronousFilters: [
createSynchronousFilter('foo', (operand) => {
return operand;
@ -27,14 +20,6 @@ runTest({
runTest({
description: 'deprecated filter with alternative',
additionalFilters: [
createFilter('foo', (operand) => {
return Promise.resolve(operand);
}, [], {
deprecated: true,
alternative: 'bar'
})
],
additionalSynchronousFilters: [
createSynchronousFilter('foo', (operand) => {
return operand;
@ -53,14 +38,6 @@ runTest({
runTest({
description: 'deprecated filter with alternative and version',
additionalFilters: [
createFilter('foo', (operand) => {
return Promise.resolve(operand);
}, [], {
deprecated: 'x.y.z',
alternative: 'bar'
})
],
additionalSynchronousFilters: [
createSynchronousFilter('foo', (operand) => {
return operand;

@ -1,15 +1,8 @@
import {runTest} from "../TestBase";
import {createFunction, createSynchronousFunction} from "../../../../main/lib/function";
import {createSynchronousFunction} from "../../../../main/lib/function";
runTest({
description: 'deprecated function',
additionalFunctions: [
createFunction('foo', () => {
return Promise.resolve('');
}, [], {
deprecated: true,
})
],
additionalSynchronousFunctions: [
createSynchronousFunction('foo', () => {
return '';
@ -27,14 +20,6 @@ runTest({
runTest({
description: 'deprecated function with alternative',
additionalFunctions: [
createFunction('foo', () => {
return Promise.resolve('');
}, [], {
deprecated: true,
alternative: 'bar'
})
],
additionalSynchronousFunctions: [
createSynchronousFunction('foo', () => {
return '';
@ -53,14 +38,6 @@ runTest({
runTest({
description: 'deprecated function with alternative and version',
additionalFunctions: [
createFunction('foo', () => {
return Promise.resolve('');
}, [], {
deprecated: 'x.y.z',
alternative: 'bar'
})
],
additionalSynchronousFunctions: [
createSynchronousFunction('foo', () => {
return '';

@ -8,7 +8,7 @@ runTest({
`
},
expectedErrorMessage: 'TwingRuntimeError: I am Error in "index.twig" at line 2, column 4',
context: {
synchronousContext: {
foo: {
bar: () => {
throw new Error('I am Error');

@ -1,13 +1,13 @@
import {runTest} from "../../TestBase";
import {TwingCache} from "../../../../../main/lib/cache";
import {TwingSynchronousCache} from "../../../../../main/lib/cache";
import {TwingTemplateNode} from "../../../../../main/lib/node/template";
const createCache = (): TwingCache => {
const createCache = (): TwingSynchronousCache => {
const container = new Map<string, TwingTemplateNode>();
return {
getTimestamp: () => Promise.resolve(Number.POSITIVE_INFINITY),
load: (key) => Promise.resolve(container.get(key) || null),
getTimestamp: () => Number.POSITIVE_INFINITY,
load: (key) => container.get(key) || null,
write: (key, content) => Promise.resolve(container.set(key, content)).then()
};
};
@ -31,11 +31,11 @@ for (const [name, context, errorMessage] of testCases) {
templates: {
"index.twig": `{{ foo["bar"] }}`
},
context,
synchronousContext: context,
trimmedExpectation: strictVariables ? undefined : '',
expectedErrorMessage: strictVariables ? errorMessage : undefined,
strict: strictVariables,
environmentOptions: {
synchronousEnvironmentOptions: {
cache
}
});

@ -6,7 +6,7 @@ for (const testCase of [['true', '1'], ['false', '0']]) {
templates: {
"index.twig": `{{ foo[${testCase[0]}] }}`
},
context: {
synchronousContext: {
foo: {
0: '0',
1: '1'

@ -6,7 +6,7 @@ for (const testCase of [['0.1', '0'], ['1.6', '1']]) {
templates: {
"index.twig": `{{ foo[${testCase[0]}] }}`
},
context: {
synchronousContext: {
foo: {
0: '0',
1: '1'

@ -8,7 +8,7 @@ runTest({
{{ null.bar is defined ? 'KO' : 'OK' }}
`
},
context: {
synchronousContext: {
foo: new (class {
get bar() {
return 'foo.bar';

@ -5,7 +5,7 @@ runTest({
templates: {
"index.twig": `{{ foo.bar }}`
},
context: {
synchronousContext: {
foo: new (class {
get bar() {
return 'foo.bar';

@ -1,15 +1,15 @@
import {runTest} from "../../TestBase";
import {TwingCache} from "../../../../../main/lib/cache";
import {TwingSynchronousCache} from "../../../../../main/lib/cache";
import {createSandboxSecurityPolicy} from "../../../../../main/lib/sandbox/security-policy";
import {TwingTemplateNode} from "../../../../../main/lib/node/template";
const createCache = (): TwingCache => {
const createCache = (): TwingSynchronousCache => {
const container = new Map<string, TwingTemplateNode>();
return {
getTimestamp: () => Promise.resolve(Number.POSITIVE_INFINITY),
load: (key) => Promise.resolve(container.get(key) || null),
write: (key, content) => Promise.resolve(container.set(key, content)).then()
getTimestamp: () => Number.POSITIVE_INFINITY,
load: (key) => container.get(key) || null,
write: (key, content) => container.set(key, content)
};
};
@ -32,11 +32,11 @@ for (const [name, context, errorMessage] of testCases) {
templates: {
"index.twig": `{{ foo.bar }}`
},
context,
synchronousContext: context,
trimmedExpectation: sandboxed ? undefined : 'bar',
expectedErrorMessage: sandboxed ? errorMessage : undefined,
sandboxed,
environmentOptions: {
synchronousEnvironmentOptions: {
cache,
sandboxPolicy: createSandboxSecurityPolicy()
}

@ -1,14 +1,14 @@
import {runTest} from "../../TestBase";
import {TwingCache} from "../../../../../main/lib/cache";
import {TwingSynchronousCache} from "../../../../../main/lib/cache";
import {TwingTemplateNode} from "../../../../../main/lib";
const createCache = (): TwingCache => {
const createCache = (): TwingSynchronousCache => {
const container = new Map<string, TwingTemplateNode>();
return {
getTimestamp: () => Promise.resolve(Number.POSITIVE_INFINITY),
load: (key) => Promise.resolve(container.get(key) || null),
write: (key, content) => Promise.resolve(container.set(key, content)).then()
getTimestamp: () => Number.POSITIVE_INFINITY,
load: (key) => container.get(key) || null,
write: (key, content) => container.set(key, content)
};
};
@ -31,11 +31,11 @@ for (const [name, context, errorMessage] of testCases) {
templates: {
"index.twig": `{{ foo.bar }}`
},
context,
synchronousContext: context,
trimmedExpectation: strictVariables ? undefined : '',
expectedErrorMessage: strictVariables ? errorMessage : undefined,
strict: strictVariables,
environmentOptions: {
synchronousEnvironmentOptions: {
cache
}
});

@ -9,7 +9,7 @@ runTest({
{{ null.bar() is defined ? 'KO' : 'OK' }}
`
},
context: {
synchronousContext: {
foo: {
bar: () => {
return 'foo.bar';

@ -1,15 +1,15 @@
import {runTest} from "../../TestBase";
import {TwingCache} from "../../../../../main/lib/cache";
import {TwingSynchronousCache} from "../../../../../main/lib/cache";
import {createSandboxSecurityPolicy} from "../../../../../main/lib/sandbox/security-policy";
import {TwingTemplateNode} from "../../../../../main/lib/node/template";
const createCache = (): TwingCache => {
const createCache = (): TwingSynchronousCache => {
const container = new Map<string, TwingTemplateNode>();
return {
getTimestamp: () => Promise.resolve(Number.POSITIVE_INFINITY),
load: (key) => Promise.resolve(container.get(key) || null),
write: (key, content) => Promise.resolve(container.set(key, content)).then()
getTimestamp: () => Number.POSITIVE_INFINITY,
load: (key) => container.get(key) || null,
write: (key, content) => container.set(key, content)
};
};
@ -32,11 +32,11 @@ for (const [name, context, errorMessage] of testCases) {
templates: {
"index.twig": `{{ foo.bar() }}`
},
context,
synchronousContext: context,
trimmedExpectation: sandboxed ? undefined : 'bar',
expectedErrorMessage: sandboxed ? errorMessage : undefined,
sandboxed,
environmentOptions: {
synchronousEnvironmentOptions: {
cache,
sandboxPolicy: createSandboxSecurityPolicy()
}

@ -1,14 +1,14 @@
import {runTest} from "../../TestBase";
import {TwingCache} from "../../../../../main/lib/cache";
import {TwingSynchronousCache} from "../../../../../main/lib/cache";
import {TwingTemplateNode} from "../../../../../main/lib/node/template";
const createCache = (): TwingCache => {
const createCache = (): TwingSynchronousCache => {
const container = new Map<string, TwingTemplateNode>();
return {
getTimestamp: () => Promise.resolve(Number.POSITIVE_INFINITY),
load: (key) => Promise.resolve(container.get(key) || null),
write: (key, content) => Promise.resolve(container.set(key, content)).then()
getTimestamp: () => Number.POSITIVE_INFINITY,
load: (key) => container.get(key) || null,
write: (key, content) => container.set(key, content)
};
};
@ -29,11 +29,11 @@ for (const [name, context, errorMessage] of testCases) {
templates: {
"index.twig": `{{ foo.bar() }}`
},
context,
synchronousContext: context,
trimmedExpectation: strictVariables ? undefined : '',
expectedErrorMessage: strictVariables ? errorMessage : undefined,
strict: strictVariables,
environmentOptions: {
synchronousEnvironmentOptions: {
cache
}
});

@ -138,7 +138,7 @@ runTest({
templates: {
"index.twig": `{{ foo.foo }}`
},
context: {
synchronousContext: {
foo: new AnotherFoo()
},
trimmedExpectation: `foo`
@ -149,7 +149,7 @@ runTest({
templates: {
"index.twig": `{{ foo.bar }}`
},
context: {
synchronousContext: {
foo: new AnotherFoo()
},
trimmedExpectation: `getBar`
@ -160,7 +160,7 @@ runTest({
templates: {
"index.twig": `{{ foo.Oof }}`
},
context: {
synchronousContext: {
foo: new AnotherFoo()
},
trimmedExpectation: `isOof`
@ -171,7 +171,7 @@ runTest({
templates: {
"index.twig": `{{ foo.fooBar }}`
},
context: {
synchronousContext: {
foo: new AnotherFoo()
},
trimmedExpectation: `hasFooBar`
@ -182,7 +182,7 @@ runTest({
templates: {
"index.twig": `{{ foo.getfoo }} {{ foo.GeTfOo }}`
},
context: {
synchronousContext: {
foo: new AnotherFoo()
},
trimmedExpectation: `getFoo getFoo`

@ -7,11 +7,6 @@ runTest({
{% set a = ...[1, 2] %}
`
},
environmentOptions: {
parserOptions: {
level: 3
}
},
synchronousEnvironmentOptions: {
parserOptions: {
level: 3

@ -12,11 +12,6 @@ runTest({
{{ [1, 2, ...iterableNumbers, 0, ...moreNumbers]|join(',') }}
`
},
environmentOptions: {
parserOptions: {
level: 3
}
},
synchronousEnvironmentOptions: {
parserOptions: {
level: 3

@ -25,14 +25,9 @@ runTest({
{% endfor %}
`
},
context: {
synchronousContext: {
jsMap: new Map([['favoriteShoes', 'barefoot']])
},
environmentOptions: {
parserOptions: {
level: 3
}
},
synchronousEnvironmentOptions: {
parserOptions: {
level: 3

@ -19,7 +19,7 @@ NO1
foo<br />
foo-bar
`,
context: {
synchronousContext: {
foo: 'foo',
bar: 'bar'
}

@ -7,7 +7,7 @@ runTest({
"index.twig": '{% autoescape %}{{ foo|escape("html") }}{% endautoescape %}'
},
expectation: '<br/>',
context: {
synchronousContext: {
foo: createMarkup('<br/>', "UTF-8")
}
});

@ -23,7 +23,7 @@ for (const [value, expectation] of owaspTestCases) {
"index.twig": `{{ value|escape("css") == value ? "true" : "false" }}`
},
trimmedExpectation: `${expectation}`,
context: {
synchronousContext: {
value
}
});
@ -67,7 +67,7 @@ for (const key in specialCharacters) {
"index.twig": `{{ key|escape("css") }}`
},
trimmedExpectation: `${value}`,
context: {
synchronousContext: {
key
}
});

@ -24,7 +24,7 @@ for (const [value, expectation] of owaspTestCases) {
"index.twig": `{{ value|escape("html_attr") == value ? "true" : "false" }}`
},
trimmedExpectation: `${expectation}`,
context: {
synchronousContext: {
value
}
});
@ -70,7 +70,7 @@ for (const key in specialCharacters) {
"index.twig": `{{ key|escape("html_attr") }}`
},
trimmedExpectation: `${value}`,
context: {
synchronousContext: {
key
}
});

@ -24,7 +24,7 @@ for (const [value, expectation] of owaspTestCases) {
"index.twig": `{{ value|escape("js") == value ? "true" : "false" }}`
},
trimmedExpectation: `${expectation}`,
context: {
synchronousContext: {
value
}
});
@ -72,7 +72,7 @@ for (const key in specialCharacters) {
"index.twig": `{{ key|escape("js") }}`
},
trimmedExpectation: `${value}`,
context: {
synchronousContext: {
key
}
});

@ -44,7 +44,7 @@ for (const key in specialCharacters) {
"index.twig": `{{ key|escape("url") }}`
},
trimmedExpectation: `${value}`,
context: {
synchronousContext: {
key
}
});

@ -10,7 +10,7 @@ runTest({
{{ jsArray|length }}
`
},
context: {
synchronousContext: {
jsArray: [1, 2]
},
expectation: `

@ -10,7 +10,7 @@ runTest({
{{ jsMap|length }}
`
},
context: {
synchronousContext: {
jsMap: new Map([[0, 1], [1, 2]])
},
expectation: `

@ -13,7 +13,7 @@ runTest({
foo
foo
`,
context: {
synchronousContext: {
markup: createMarkup(('FoO'), "UTF-8")
}
});

@ -15,7 +15,7 @@ content
<div>content</div>
<div><span>content</span></div>
`,
context: {
synchronousContext: {
markup: createMarkup(('FoO'), "UTF-8")
}
});

@ -13,7 +13,7 @@ runTest({
Foo
Foo
`,
context: {
synchronousContext: {
markup: createMarkup(('FoO'), "UTF-8")
}
});

@ -13,7 +13,7 @@ runTest({
FOO
FOO
`,
context: {
synchronousContext: {
markup: createMarkup(('FoO'), "UTF-8")
}
});

@ -1,5 +1,5 @@
import {runTest} from "../TestBase";
import {createFilter, createSynchronousFilter} from "../../../../main/lib/filter";
import {createSynchronousFilter} from "../../../../main/lib/filter";
runTest({
description: 'variadic filter',
@ -11,13 +11,6 @@ runTest({
{{ "5"|variadic(1,foo = 2) }}
`
},
additionalFilters: [
createFilter('variadic', (_executionContext, operand: string, ...values: Array<number>) => {
return Promise.resolve(`${operand}=>${values.join(',')}`);
}, [], {
is_variadic: true
})
],
additionalSynchronousFilters: [
createSynchronousFilter('variadic', (_executionContext, operand: string, ...values: Array<number>) => {
return `${operand}=>${values.join(',')}`;

@ -1,5 +1,5 @@
import {runTest} from "../TestBase";
import {createFilter, createSynchronousFilter} from "../../../../main/lib/filter";
import {createSynchronousFilter} from "../../../../main/lib/filter";
runTest({
description: 'filter with duplicated arguments',
@ -8,15 +8,6 @@ runTest({
{{ "5"|foo(1,foo=2) }}
`
},
additionalFilters: [
createFilter('foo', () => {
return Promise.resolve('wrong');
}, [
{
name: 'foo'
}
])
],
additionalSynchronousFilters: [
createSynchronousFilter('foo', () => {
return 'wrong';

@ -1,5 +1,5 @@
import {runTest} from "../TestBase";
import {createFilter, createSynchronousFilter} from "../../../../main/lib/filter";
import {createSynchronousFilter} from "../../../../main/lib/filter";
runTest({
description: 'filter with missing required arguments',
@ -8,15 +8,6 @@ runTest({
{{ "5"|foo() }}
`
},
additionalFilters: [
createFilter('foo', () => {
return Promise.resolve('wrong');
}, [
{
name: 'foo'
}
])
],
additionalSynchronousFilters: [
createSynchronousFilter('foo', () => {
return 'wrong';

@ -1,5 +1,5 @@
import {runTest} from "../TestBase";
import {createFilter, createSynchronousFilter} from "../../../../main/lib/filter";
import {createSynchronousFilter} from "../../../../main/lib/filter";
runTest({
description: 'filter with positional argument after named argument',
@ -8,17 +8,6 @@ runTest({
{{ "5"|foo(1,bar = 2,3) }}
`
},
additionalFilters: [
createFilter('foo', () => {
return Promise.resolve('wrong');
}, [
{
name: 'bar'
}
], {
is_variadic: true
})
],
additionalSynchronousFilters: [
createSynchronousFilter('foo', () => {
return 'wrong';

@ -1,5 +1,5 @@
import {runTest} from "../TestBase";
import {createFilter, createSynchronousFilter} from "../../../../main/lib/filter";
import {createSynchronousFilter} from "../../../../main/lib/filter";
runTest({
description: 'filter with unknown argument',
@ -8,11 +8,6 @@ runTest({
{{ "5"|foo(foo=1) }}
`
},
additionalFilters: [
createFilter('foo', () => {
return Promise.resolve('wrong');
}, [])
],
additionalSynchronousFilters: [
createSynchronousFilter('foo', () => {
return 'wrong';
@ -28,11 +23,6 @@ runTest({
{{ "5"|foo(foo=1,bar=2) }}
`
},
additionalFilters: [
createFilter('foo', () => {
return Promise.resolve('wrong');
}, [])
],
additionalSynchronousFilters: [
createSynchronousFilter('foo', () => {
return 'wrong';

@ -1,6 +1,6 @@
import {runTest} from "../../TestBase";
import {createEnvironment, createSynchronousEnvironment} from "../../../../../main/lib/environment";
import {createArrayLoader, createSynchronousArrayLoader} from "../../../../../main/lib/loader/array";
import {createSynchronousEnvironment} from "../../../../../main/lib/environment";
import {createSynchronousArrayLoader} from "../../../../../main/lib/loader/array";
const template = createSynchronousEnvironment(createSynchronousArrayLoader({
'included.twig': `
@ -27,22 +27,6 @@ FOO
FOO
NOT FOO
`,
context: new Promise((resolve) => {
const environment = createEnvironment(
createArrayLoader({
'included.twig': `
{% block foo %}FOO{% endblock %}`
})
);
resolve(environment.loadTemplate('included.twig')
.then((template) => {
return {
included_loaded: template,
included_loaded_internal: template
}
}));
}),
synchronousContext: {
included_loaded: template,
included_loaded_internal: template

@ -8,7 +8,7 @@ runTest({
{{ date|date(timezone="UTC") }}
`
},
context: {
synchronousContext: {
date: new Date('2000-01-01T00:00:00Z') // this is UTC
},
trimmedExpectation: 'January 1, 2000 00:00'

@ -8,7 +8,7 @@ runTest({
{{ dump(function2) }}
`
},
context: {
synchronousContext: {
function: () => {
},
function2: (value: any) => {

@ -8,7 +8,7 @@ runTest({
{{ dump(functions.undefined()) }}
`
},
context: {
synchronousContext: {
functions: {
undefined: () => undefined
}

@ -40,11 +40,6 @@ runTest({
{{ foo|e }}`
},
sandboxed: true,
environmentOptions: {
sandboxPolicy: createSandboxSecurityPolicy({
allowedFunctions: ['include']
})
},
synchronousEnvironmentOptions: {
sandboxPolicy: createSandboxSecurityPolicy({
allowedFunctions: ['include']

@ -1,7 +1,7 @@
import TestBase, {runTest} from "../../TestBase";
import {createIntegrationTest} from "../../test";
import {createEnvironment, createSynchronousEnvironment} from "../../../../../main/lib/environment";
import {createArrayLoader, createSynchronousArrayLoader} from "../../../../../main/lib/loader/array";
import {createSynchronousEnvironment} from "../../../../../main/lib/environment";
import {createSynchronousArrayLoader} from "../../../../../main/lib/loader/array";
class Test extends TestBase {
getDescription() {
@ -22,22 +22,6 @@ BAR`
BAR FOO
`;
}
async getContext() {
const environment = createEnvironment(
createArrayLoader({
'foo.twig': `
BAR`
})
);
return environment.loadTemplate('foo.twig')
.then((template) => {
return {
foo: template
}
});
}
getSynchronousContext(): any {
const environment = createSynchronousEnvironment(

@ -10,6 +10,6 @@ runTest({
{% if (value == "a") %}OK{% endif %}
`
},
context: {},
synchronousContext: {},
trimmedExpectation: `OKOK`
});

@ -10,7 +10,7 @@ runTest({
{{ random(emptyBuffer) == "" }}
`
},
context: {
synchronousContext: {
buffer: Buffer.from('Äé'),
emptyBuffer: Buffer.from('')
},
@ -28,12 +28,9 @@ runTest({
{{ random1 == "Ä"|convert_encoding('ISO-8859-1', 'UTF-8') or random1 == "é"|convert_encoding('ISO-8859-1', 'UTF-8') }}
`
},
context: {
synchronousContext: {
buffer: iconv('UTF-8', 'ISO-8859-1', Buffer.from('Äé')),
},
environmentOptions: {
charset: 'ISO-8859-1'
},
synchronousEnvironmentOptions: {
charset: 'ISO-8859-1'
},

@ -21,7 +21,7 @@ runTest({
{{ random1 == foo }}
`
},
context: {
synchronousContext: {
foo: new (class {})
},
trimmedExpectation: `

@ -5,19 +5,13 @@ runTest({
templates: {
"index.twig": `{{ foo }}, {{ bar }}`
},
environmentOptions: {
globals: {
foo: 'foo from globals',
bar: 'bar from globals'
}
},
synchronousEnvironmentOptions: {
globals: {
foo: 'foo from globals',
bar: 'bar from globals'
}
},
context: {
synchronousContext: {
bar: 'bar from context'
},
expectation: `foo from globals, bar from context`

@ -6,11 +6,6 @@ runTest({
"index.twig": `{{ include("foo") }}`,
'foo': `{{ title }}`
},
environmentOptions: {
globals: {
title: 'foo'
}
},
synchronousEnvironmentOptions: {
globals: {
title: 'foo'
@ -25,11 +20,6 @@ runTest({
"index.twig": `{{ include("foo", {title: "bar"}) }}`,
'foo': `{{ title }}`
},
environmentOptions: {
globals: {
title: 'foo'
}
},
synchronousEnvironmentOptions: {
globals: {
title: 'foo'
@ -44,11 +34,6 @@ runTest({
"index.twig": `{{ include("foo", {}, true) }}`,
'foo': `{{ title }}`
},
environmentOptions: {
globals: {
title: 'foo'
}
},
synchronousEnvironmentOptions: {
globals: {
title: 'foo'

@ -12,9 +12,6 @@ runTest({
{{ br }}
`
},
environmentOptions: {
autoEscapingStrategy: 'html'
},
synchronousEnvironmentOptions: {
autoEscapingStrategy: 'html'
},

@ -13,11 +13,6 @@ runTest({
{{ 5 has every null ? '5 has' : '5 has not' }} every null whatever it means
`
},
environmentOptions: {
parserOptions: {
level: 3
}
},
synchronousEnvironmentOptions: {
parserOptions: {
level: 3

@ -7,11 +7,6 @@ runTest({
{{ [] has every v => true }}
`
},
environmentOptions: {
parserOptions: {
level: 2
}
},
synchronousEnvironmentOptions: {
parserOptions: {
level: 2

@ -15,11 +15,6 @@ runTest({
{{ 5 has some null ? '5 has' : '5 has not' }} some null whatever it means
`
},
environmentOptions: {
parserOptions: {
level: 3
}
},
synchronousEnvironmentOptions: {
parserOptions: {
level: 3

@ -7,11 +7,6 @@ runTest({
{{ [] has some v => true }}
`
},
environmentOptions: {
parserOptions: {
level: 2
}
},
synchronousEnvironmentOptions: {
parserOptions: {
level: 2

@ -5,12 +5,6 @@ runTest({
templates: {
"index.twig": '{{ 5|unknown }}'
},
environmentOptions: {
parserOptions: {
strict: false,
level: 2
}
},
synchronousEnvironmentOptions: {
parserOptions: {
strict: false,

@ -2,12 +2,6 @@ import {runTest} from "../../TestBase";
runTest({
description: 'Unknown function throws a parsing error on strict mode',
environmentOptions: {
parserOptions: {
strict: false,
level: 2
}
},
synchronousEnvironmentOptions: {
parserOptions: {
strict: false,

@ -2,12 +2,6 @@ import {runTest} from "../../TestBase";
runTest({
description: 'Unknown one-word test throws a parsing error on strict mode',
environmentOptions: {
parserOptions: {
strict: false,
level: 2
}
},
synchronousEnvironmentOptions: {
parserOptions: {
strict: false,
@ -22,12 +16,6 @@ runTest({
runTest({
description: 'Unknown two-words test throws a parsing error on strict mode',
environmentOptions: {
parserOptions: {
strict: false,
level: 2
}
},
synchronousEnvironmentOptions: {
parserOptions: {
strict: false,

@ -5,12 +5,6 @@ runTest({
templates: {
"index.twig": '{{ 5|unknown }}'
},
environmentOptions: {
parserOptions: {
strict: true,
level: 2
}
},
synchronousEnvironmentOptions: {
parserOptions: {
strict: true,

@ -2,12 +2,6 @@ import {runTest} from "../../TestBase";
runTest({
description: 'Unknown function throws a parsing error on strict mode',
environmentOptions: {
parserOptions: {
strict: true,
level: 2
}
},
synchronousEnvironmentOptions: {
parserOptions: {
strict: true,

@ -2,12 +2,6 @@ import {runTest} from "../../TestBase";
runTest({
description: 'Unknown one-word test throws a parsing error on strict mode',
environmentOptions: {
parserOptions: {
strict: true,
level: 2
}
},
synchronousEnvironmentOptions: {
parserOptions: {
strict: true,
@ -22,12 +16,6 @@ runTest({
runTest({
description: 'Unknown two-words test throws a parsing error on strict mode',
environmentOptions: {
parserOptions: {
strict: true,
level: 2
}
},
synchronousEnvironmentOptions: {
parserOptions: {
strict: true,

@ -5,7 +5,7 @@ runTest({
templates: {
"index.twig": `{% do 5 %}{{ foo }}`
},
context: {
synchronousContext: {
foo: {
toString: () => 'foo'
}

@ -6,7 +6,7 @@ runTest({
templates: {
"index.twig": `{% do 5 %}{{ foo }}`
},
context: {
synchronousContext: {
foo: {
toString: () => 'foo'
}

@ -11,7 +11,7 @@ runTest({
sandboxSecurityPolicyMethods: new Map([
[Object, ['bar']]
]),
context: {
synchronousContext: {
foo: {
bar: () => 5
}
@ -27,7 +27,7 @@ runTest({
`
},
sandboxed: true,
context: {
synchronousContext: {
foo: {
bar: () => 5
}

@ -11,7 +11,7 @@ runTest({
sandboxSecurityPolicyProperties: new Map([
[Object, ['bar']]
]),
context: {
synchronousContext: {
foo: {
bar: 5
}
@ -27,7 +27,7 @@ runTest({
`
},
sandboxed: true,
context: {
synchronousContext: {
foo: {
bar: 5
}

@ -6,7 +6,7 @@ runTest({
'index.twig': `{{ 5 }}
{{ 5 }}{{ 5 }}`
},
context: {},
synchronousContext: {},
expectedSourceMapMappings: [{
source: 'index.twig',
generatedLine: 1,

@ -16,7 +16,7 @@ runTest({
description: '"spaceless" node source map',
loader,
synchronousLoader,
context: {},
synchronousContext: {},
expectedSourceMapMappings: [{
source: 'foo/bar',
generatedLine: 1,

@ -1,12 +1,8 @@
import {runTest} from "../TestBase";
import {createFunction} from "../../../../main/lib";
import {createSynchronousFunction} from "../../../../main/lib/function";
runTest({
description: "Source map supports custom function",
additionalFunctions: [
createFunction('foo', () => Promise.resolve('foo'), [])
],
additionalSynchronousFunctions: [
createSynchronousFunction('foo', () => 'foo', [])
],

@ -27,7 +27,7 @@ Skeleton content
Skeleton 2 content
{%- endblock %}`
},
context: {},
synchronousContext: {},
expectedSourceMapMappings: [
{source: 'index.twig', generatedLine: 1, generatedColumn: 0, originalLine: 3, originalColumn: 0, name: 'text'},
{source: 'skeleton.twig', generatedLine: 1, generatedColumn: 16, originalLine: 2, originalColumn: 0, name: 'text'},

@ -15,7 +15,7 @@ runTest({
{{ "partial 2 content" }}
`
},
context: {},
synchronousContext: {},
expectedSourceMapMappings: [
{source: 'index.twig', generatedLine: 1, generatedColumn: 0, originalLine: 1, originalColumn: 0, name: 'text'},
{source: 'partial.twig', generatedLine: 2, generatedColumn: 0, originalLine: 1, originalColumn: 0, name: 'text'},
@ -56,7 +56,7 @@ runTest({
{{ "partial 2 content" }}
`
},
context: {},
synchronousContext: {},
expectedSourceMapMappings: [
{source: 'index.twig', generatedLine: 1, generatedColumn: 0, originalLine: 1, originalColumn: 0, name: 'text'},
{source: 'partial.twig', generatedLine: 2, generatedColumn: 0, originalLine: 1, originalColumn: 0, name: 'text'},

@ -11,7 +11,7 @@ Child content + {{ parent() }} + something after
Parent content
{%- endblock %}`
},
context: {},
synchronousContext: {},
expectedSourceMapMappings: [
{source: 'index.twig', generatedLine: 1, generatedColumn: 0, originalLine: 3, originalColumn: 0, name: 'text'},
{source: 'parent.twig', generatedLine: 1, generatedColumn: 16, originalLine: 2, originalColumn: 0, name: 'text'},

@ -1,5 +1,5 @@
import {runTest} from "../../TestBase";
import {createFilter, createSynchronousFilter} from "../../../../../main/lib/filter";
import {createSynchronousFilter} from "../../../../../main/lib/filter";
runTest({
description: '"apply" tag with filter arguments',
@ -12,13 +12,6 @@ hangar
},
expectation: `
Hangar 18`,
additionalFilters: [
createFilter('append', (_executionContext, operand: any, index: number) => {
return Promise.resolve(`${operand} ${index}`);
}, [{
name: 'index'
}])
],
additionalSynchronousFilters: [
createSynchronousFilter('append', (_executionContext, operand: any, index: number) => {
return `${operand} ${index}`;

@ -14,7 +14,7 @@ runTest({
{% endautoescape %}
`
},
context: {
synchronousContext: {
'someVar': '<br />',
},
trimmedExpectation: `

@ -14,7 +14,7 @@ runTest({
{% endautoescape %}
`
},
context: {
synchronousContext: {
someVar: '<br />'
},
trimmedExpectation: `

@ -14,7 +14,7 @@ runTest({
'index.twig': `
{% autoescape "bar" %}{{ foo }}{% endautoescape %}`
},
context: {
synchronousContext: {
foo: 'html'
},
expectedErrorMessage: 'TwingRuntimeError: Invalid escaping strategy "bar" (valid ones: css, custom, html, html_attr, js, url) in "index.twig" at line 2, column 26.'

@ -9,7 +9,7 @@ runTest({
{% endautoescape %}
`
},
context: {
synchronousContext: {
strategy: 'html'
},
expectedErrorMessage: 'TwingParsingError: An escaping strategy must be a string or false in "index.twig" at line 2, column 15.'

@ -14,11 +14,6 @@ runTest({
'layout.twig': `
{% block content %}{% endblock %}`
},
environmentOptions: {
parserOptions: {
level: 3
}
},
synchronousEnvironmentOptions: {
parserOptions: {
level: 3

@ -5,7 +5,7 @@ runTest({
templates: {
'index.twig': '{% deprecated foo %}'
},
context: {
synchronousContext: {
foo: 'bar'
},
expectedDeprecationMessages: [

@ -1,6 +1,6 @@
import {runTest} from "../../TestBase";
import {createEnvironment, createSynchronousEnvironment} from "../../../../../main/lib/environment";
import {createArrayLoader, createSynchronousArrayLoader} from "../../../../../main/lib/loader/array";
import {createSynchronousEnvironment} from "../../../../../main/lib/environment";
import {createSynchronousArrayLoader} from "../../../../../main/lib/loader/array";
runTest({
description: '"extends" tag with template instance',
@ -17,22 +17,6 @@ runTest({
trimmedExpectation: `
BARFOO
`,
context: new Promise((resolve) => {
const environment = createEnvironment(
createArrayLoader({
'foo.twig': `
{% block content %}BAR{% endblock %}`
})
);
resolve(environment.loadTemplate('foo.twig')
.then((template) => {
return {
foo: template
}
})
);
}),
synchronousContext: {
foo: createSynchronousEnvironment(
createSynchronousArrayLoader({
@ -58,22 +42,6 @@ runTest({
trimmedExpectation: `
BARFOO
`,
context: new Promise((resolve) => {
const environment = createEnvironment(
createArrayLoader({
'foo.twig': `
{% block content %}BAR{% endblock %}`
})
);
resolve(environment.loadTemplate('foo.twig')
.then((template) => {
return {
foo: template
};
})
);
}),
synchronousContext: {
foo: createSynchronousEnvironment(
createSynchronousArrayLoader({

@ -5,16 +5,11 @@ runTest({
templates: {
"index.twig": '{% filter upper %}{% endfilter %}'
},
environmentOptions: {
synchronousEnvironmentOptions: {
parserOptions: {
strict: true,
level: 3
}
},
synchronousEnvironmentOptions: {
parserOptions: {
level: 3
}
},
expectedErrorMessage: 'TwingParsingError: Unknown "filter" tag in "index.twig" at line 1, column 4.'
});

@ -2,11 +2,6 @@ import {runTest} from "../../TestBase";
runTest({
description: '"filter" tag referencing an unknown filter',
environmentOptions: {
parserOptions: {
strict: false
}
},
synchronousEnvironmentOptions: {
parserOptions: {
strict: false

@ -6,7 +6,7 @@ runTest({
"index.twig": `{% for key, value in _context %}{{ key }}{{ value }}{% endfor %}`
},
expectation: `foofoo valuebarbar value_parent[object Object]`,
context: {
synchronousContext: {
foo: 'foo value',
bar: 'bar value'
}

@ -9,11 +9,6 @@ runTest({
{% endfor %}
`
},
environmentOptions: {
parserOptions: {
level: 3
}
},
synchronousEnvironmentOptions: {
parserOptions: {
level: 3

@ -9,7 +9,7 @@ runTest({
},
expectation: `
true`,
context: {
synchronousContext: {
a: true,
b: true
}

@ -1,6 +1,6 @@
import {runTest} from "../../TestBase";
import {createEnvironment, createSynchronousEnvironment} from "../../../../../main/lib/environment";
import {createArrayLoader, createSynchronousArrayLoader} from "../../../../../main/lib/loader/array";
import {createSynchronousEnvironment} from "../../../../../main/lib/environment";
import {createSynchronousArrayLoader} from "../../../../../main/lib/loader/array";
runTest(({
description: '"include" tag with a template instance',
@ -10,22 +10,6 @@ BAR`,
'index.twig': `
{% include foo %} FOO`
},
context: new Promise((resolve) => {
const environment = createEnvironment(
createArrayLoader({
'foo.twig': `
BAR`
})
)
resolve(environment.loadTemplate('foo.twig')
.then((template) => {
return {
foo: template
};
})
);
}),
synchronousContext: {
foo: createSynchronousEnvironment(
createSynchronousArrayLoader({
@ -46,21 +30,6 @@ BAR`,
'index.twig': `
{% include foo %} FOO`
},
context: new Promise((resolve) => {
const environment = createEnvironment(
createArrayLoader({
'foo.twig': `
BAR`
})
)
resolve(environment.loadTemplate('foo.twig')
.then((template) => {
return {
foo: template
};
}));
}),
synchronousContext: {
foo: createSynchronousEnvironment(
createSynchronousArrayLoader({

@ -19,7 +19,7 @@ runTest({
{% block foo %}
{% endblock %}`
},
context: {},
synchronousContext: {},
trimmedExpectation: '',
expectedErrorMessage: 'TwingParsingError: Unknown function "input" in "index.twig" at line 6, column 12.'
});

@ -17,7 +17,7 @@ runTest({
{% endmacro %}
`
},
context: {},
synchronousContext: {},
trimmedExpectation: '',
expectedErrorMessage: 'TwingParsingError: Unknown function "linput" in "index.twig" at line 6, column 13.'
});

@ -14,7 +14,7 @@ runTest({
sandboxSecurityPolicyFilters: ['upper'],
sandboxSecurityPolicyTags: ['sandbox', 'include'],
sandboxed: true,
context: {
synchronousContext: {
foo: {
bar: 'foo.bar'
}

@ -5,12 +5,6 @@ runTest({
templates: {
"index.twig": '{% spaceless %}{% endspaceless %}'
},
environmentOptions: {
parserOptions: {
strict: true,
level: 3
}
},
synchronousEnvironmentOptions: {
parserOptions: {
strict: true,

@ -5,7 +5,7 @@ runTest({
templates: {
'index.twig': '{% use foo %}'
},
context: {
synchronousContext: {
foo: 'foo.twig'
},
expectedErrorMessage: 'TwingParsingError: The template references in a "use" statement must be a string in "index.twig" at line 1, column 4.'

@ -1,25 +1,20 @@
import TestBase from "./TestBase";
import {TwingEnvironmentOptions, TwingSynchronousEnvironmentOptions} from "../../../main/lib/environment";
import {TwingSynchronousEnvironmentOptions} from "../../../main/lib/environment";
import {MappingItem} from "source-map";
import {TwingFilter, TwingSynchronousFilter} from "../../../main/lib/filter";
import {TwingFunction, TwingSynchronousFunction} from "../../../main/lib/function";
import {TwingSynchronousTest, TwingTest} from "../../../main/lib/test";
import {TwingSynchronousFilter} from "../../../main/lib/filter";
import {TwingSynchronousFunction} from "../../../main/lib/function";
import {TwingSynchronousTest} from "../../../main/lib/test";
import {TwingNodeVisitor} from "../../../main/lib/node-visitor";
import {TwingLoader, TwingSynchronousLoader} from "../../../main/lib/loader";
import {TwingSandboxSecurityPolicy} from "../../../main/lib/sandbox/security-policy";
export type IntegrationTest = {
additionalFilters?: Array<TwingFilter>;
additionalSynchronousFilters?: Array<TwingSynchronousFilter>;
additionalFunctions?: Array<TwingFunction>;
additionalSynchronousFunctions?: Array<TwingSynchronousFunction>;
additionalNodeVisitors?: Array<TwingNodeVisitor>;
additionalTests?: Array<TwingTest>;
additionalSynchronousTests?: Array<TwingSynchronousTest>;
context?: Record<string, any> | Promise<Record<string, any>>;
synchronousContext?: Record<string, any>;
description: string;
environmentOptions?: TwingEnvironmentOptions;
synchronousEnvironmentOptions?: TwingSynchronousEnvironmentOptions;
expectedErrorMessage?: string | null;
expectedDeprecationMessages?: Array<string> | null;
@ -49,12 +44,10 @@ export const createIntegrationTest = (
): IntegrationTest => {
return {
description: testInstance.getDescription(),
context: Promise.resolve(testInstance.getContext()),
synchronousContext: testInstance.getSynchronousContext() || testInstance.getContext(),
trimmedExpectation: testInstance.getExpected(),
templates: testInstance.getTemplates() as any,
expectedErrorMessage: testInstance.getExpectedErrorMessage(),
environmentOptions: testInstance.getEnvironmentOptions(),
synchronousEnvironmentOptions: testInstance.getSynchronousEnvironmentOptions(),
globals: testInstance.getGlobals(),
sandboxSecurityPolicyTags: testInstance.getSandboxSecurityPolicyTags(),

@ -1,15 +1,8 @@
import {runTest} from "../TestBase";
import {createSynchronousTest, createTest} from "../../../../main/lib/test";
import {createSynchronousTest} from "../../../../main/lib/test";
runTest({
description: 'deprecated test',
additionalTests: [
createTest('foo', () => {
return Promise.resolve(true);
}, [], {
deprecated: true
})
],
additionalSynchronousTests: [
createSynchronousTest('foo', () => {
return true;
@ -27,14 +20,6 @@ runTest({
runTest({
description: 'deprecated test with alternative',
additionalTests: [
createTest('foo', () => {
return Promise.resolve(true);
}, [], {
deprecated: true,
alternative: 'bar'
})
],
additionalSynchronousTests: [
createSynchronousTest('foo', () => {
return true;
@ -53,14 +38,6 @@ runTest({
runTest({
description: 'deprecated test with alternative and version',
additionalTests: [
createTest('foo', () => {
return Promise.resolve(true);
}, [], {
deprecated: 'x.y.z',
alternative: 'bar'
})
],
additionalSynchronousTests: [
createSynchronousTest('foo', () => {
return true;

@ -1,7 +1,7 @@
import TestBase, {runTest} from "../../TestBase";
import {createIntegrationTest} from "../../test";
import {createEnvironment, createSynchronousEnvironment} from "../../../../../main/lib/environment";
import {createArrayLoader, createSynchronousArrayLoader} from "../../../../../main/lib/loader/array";
import {createSynchronousEnvironment} from "../../../../../main/lib/environment";
import {createSynchronousArrayLoader} from "../../../../../main/lib/loader/array";
class Test extends TestBase {
getName() {
@ -30,23 +30,6 @@ ok
ok
`;
}
async getContext() {
const environment = createEnvironment(
createArrayLoader({
'included.twig': `
{% block foo %}FOO{% endblock %}`
})
);
return environment.loadTemplate('included.twig')
.then((template) => {
return {
included_loaded: template,
included_loaded_internal: template
};
});
}
getSynchronousContext(): Record<string, any> | null {
const environment = createSynchronousEnvironment(createSynchronousArrayLoader({

@ -5,7 +5,7 @@ runTest({
templates: {
"index.twig": '{{ foo is empty ? "OK" : "KO" }}'
},
context: {
synchronousContext: {
foo: {}
},
trimmedExpectation: 'OK'

@ -7,9 +7,9 @@ tape('library index', ({same, end}) => {
'createTemplateLoadingError',
'createRuntimeError',
'createParsingError',
'createFilesystemLoader', 'createSynchronousFilesystemLoader',
'createArrayLoader', 'createSynchronousArrayLoader',
'createChainLoader', 'createSynchronousChainLoader',
'createSynchronousFilesystemLoader',
'createSynchronousArrayLoader',
'createSynchronousChainLoader',
'createMarkup', 'isAMarkup',
'createApplyNode',
'createAutoEscapeNode',
@ -115,19 +115,19 @@ tape('library index', ({same, end}) => {
'createNode',
'getChildren',
'getChildrenCount',
'createEnvironment', 'createSynchronousEnvironment',
'createSynchronousEnvironment',
'createExtensionSet',
'createFilter', 'createSynchronousFilter',
'createFunction', 'createSynchronousFunction',
'createSynchronousFilter',
'createSynchronousFunction',
'createLexer',
'createOperator',
'createSandboxSecurityPolicy',
'createSource',
'createSourceMapRuntime',
'createTemplate', 'createSynchronousTemplate',
'createTest', 'createSynchronousTest',
'executeNode', 'executeNodeSynchronously',
'createTemplateLoader', 'createSynchronousTemplateLoader',
'createSynchronousTemplate',
'createSynchronousTest',
'executeNodeSynchronously',
'createSynchronousTemplateLoader',
'createContext',
'createOutputBuffer'
];

@ -1,48 +1,11 @@
import * as tape from "tape";
import {createEnvironment, createSynchronousEnvironment} from "../../../../../main/lib/environment";
import {createArrayLoader, createSynchronousArrayLoader} from "../../../../../main/lib/loader/array";
import {createSynchronousEnvironment} from "../../../../../main/lib/environment";
import {createSynchronousArrayLoader} from "../../../../../main/lib/loader/array";
import {Settings} from "luxon";
// todo: unit test every property because this is the public API
import "./loader";
tape('createEnvironment ', ({test}) => {
test('options', ({test}) => {
test('apply default values', ({same, end}) => {
Settings.defaultZoneName = "Europe/Paris";
const environment = createEnvironment(createArrayLoader({}));
same(environment.charset, 'UTF-8');
same(environment.dateFormat, 'F j, Y H:i');
same(environment.numberFormat, {
decimalPoint: '.',
numberOfDecimals: 0,
thousandSeparator: ','
});
same(environment.timezone, 'Europe/Paris');
end();
});
});
test('render', ({test}) => {
test('throws on not found template', ({same, fail, end}) => {
const environment = createEnvironment(
createArrayLoader({})
);
return environment.render('foo', {})
.then(() => fail)
.catch((error: any) => {
same((error as Error).name, 'Error');
same((error as Error).message, 'Unable to find template "foo".');
})
.finally(end);
})
});
});
tape('createSynchronousEnvironment ', ({test}) => {
test('options', ({test}) => {
test('apply default values', ({same, end}) => {

@ -1,10 +1,10 @@
import * as tape from "tape";
import {createEnvironment} from "../../../../../main/lib/environment";
import {createArrayLoader} from "../../../../../main/lib/loader/array";
import {createSynchronousEnvironment} from "../../../../../main/lib/environment";
import {createSynchronousArrayLoader} from "../../../../../main/lib/loader/array";
tape('createEnvironment::loader', ({same, end}) => {
const loader = createArrayLoader({});
const environment = createEnvironment(loader);
const loader = createSynchronousArrayLoader({});
const environment = createSynchronousEnvironment(loader);
same(environment.loader, loader);

@ -1,28 +1,14 @@
import * as tape from "tape";
import {createEnvironment, createSynchronousEnvironment} from "../../../../../main/lib/environment";
import {createSynchronousEnvironment} from "../../../../../main/lib/environment";
import {
createFilesystemLoader,
createSynchronousFilesystemLoader,
TwingFilesystemLoaderFilesystem, TwingSynchronousFilesystemLoaderFilesystem
TwingSynchronousFilesystemLoaderFilesystem
} from "../../../../../main/lib/loader/filesystem";
import {spy, stub} from "sinon";
import {createSynchronousTemplateLoader, createTemplateLoader} from "../../../../../main/lib/template-loader";
import {createArrayLoader, createSynchronousArrayLoader} from "../../../../../main/lib/loader/array";
import type {TwingCache, TwingSynchronousCache} from "../../../../../main/lib/cache";
import {createSynchronousTemplateLoader} from "../../../../../main/lib/template-loader";
import {createSynchronousArrayLoader} from "../../../../../main/lib/loader/array";
import type {TwingSynchronousCache} from "../../../../../main/lib/cache";
const createMockCache = (): TwingCache => {
return {
write: () => {
return Promise.resolve();
},
load: () => {
return Promise.resolve(null);
},
getTimestamp: () => {
return Promise.resolve(0);
}
};
};
const createMockSynchronousCache = (): TwingSynchronousCache => {
return {
@ -38,120 +24,6 @@ const createMockSynchronousCache = (): TwingSynchronousCache => {
};
};
tape('createTemplateLoader::()', ({test}) => {
test('cache the loaded template under it fully qualified name', ({same, end}) => {
const fileSystem: TwingFilesystemLoaderFilesystem = {
readFile(_path, callback) {
callback(null, Buffer.from(''));
},
stat(path, callback) {
callback(null, path === 'foo/bar' ? {
isFile() {
return true;
},
mtime: new Date(0)
} : null);
}
};
const loader = createFilesystemLoader(fileSystem);
loader.addPath('foo', '@Foo');
loader.addPath('foo', 'Bar');
const environment = createEnvironment(loader);
const loadTemplate = createTemplateLoader(environment);
const getSourceSpy = spy(loader, "getSource");
return loadTemplate('@Foo/bar', null)
.then(() => {
return Promise.all([
loadTemplate('foo/bar', null),
loadTemplate('./foo/bar', null),
loadTemplate('../foo/bar', 'there/index.html'),
loadTemplate('Bar/bar', null),
]).then(() => {
same(getSourceSpy.callCount, 1);
});
})
.finally(end);
});
test('hits the loader when the templates is considered as dirty', ({same, end}) => {
const loader = createArrayLoader({
foo: 'bar'
});
const cache = createMockCache();
stub(loader, "isFresh").resolves(false);
const loadSpy = spy(cache, "load");
const getSourceSpy = spy(loader, "getSource");
const environment = createEnvironment(
loader,
{
cache
}
);
const loadTemplate = createTemplateLoader(environment);
return loadTemplate('foo', null)
.then(() => {
return loadTemplate('foo', null);
})
.then(() => {
return loadTemplate('foo', null);
})
.then((template) => {
return template?.render(environment, {});
})
.then((content) => {
same(content, 'bar');
same(loadSpy.callCount, 0);
same(getSourceSpy.callCount, 1);
})
.finally(end);
});
test('hits the cache when the templates is considered as fresh', ({same, end}) => {
const loader = createArrayLoader({
foo: 'bar'
});
const cache = createMockCache();
const loadSpy = spy(cache, "load");
const getSourceSpy = spy(loader, "getSource");
const environment = createEnvironment(
loader,
{
cache
}
);
const loadTemplate = createTemplateLoader(environment);
return loadTemplate('foo', null)
.then(() => {
return loadTemplate('foo', null);
})
.then(() => {
stub(loader, "isFresh").resolves(true);
return loadTemplate('foo', null);
})
.then((template) => {
return template?.render(environment, {});
})
.then((content) => {
same(content, 'bar');
same(loadSpy.callCount, 1);
same(getSourceSpy.callCount, 1);
})
.finally(end);
});
});
tape('createTemplateLoader::()', ({test}) => {
test('cache the loaded template under it fully qualified name', ({same, end}) => {
const fileSystem: TwingSynchronousFilesystemLoaderFilesystem = {

@ -1,54 +1,8 @@
import * as tape from "tape";
import {createEnvironment, createSynchronousEnvironment} from "../../../../../main/lib/environment";
import {createArrayLoader, createSynchronousArrayLoader} from "../../../../../main/lib/loader/array";
import {createSynchronousEnvironment} from "../../../../../main/lib/environment";
import {createSynchronousArrayLoader} from "../../../../../main/lib/loader/array";
import {TwingTemplateNode} from "../../../../../main/lib/node/template";
import {createSynchronousTemplate, createTemplate} from "../../../../../main/lib/template";
tape('createTemplate => ::ast', ({test}) => {
test('without embedded templates', ({same, end}) => {
const environment = createEnvironment(createArrayLoader({
'index': `{{ 5 + 5 }}`
}));
return environment.loadTemplate('index')
.then(({ast}) => {
const serializedAST = JSON.stringify(ast);
const deserializedAST: TwingTemplateNode = JSON.parse(serializedAST);
const template = createTemplate(deserializedAST);
return template.render(environment, {})
.then((output) => {
same(output, '10');
})
.finally(end);
});
});
test('with embedded templates', ({same, end}) => {
const environment = createEnvironment(createArrayLoader({
'index': `{% embed "embed1" %}
{% block foo %}Foo{% endblock %}
{% endembed %}`,
'embed1': `{% block foo %}{% embed "embed2" %}
{% block foo %}Foo{% endblock %}
{% endembed %}{% endblock %}`,
'embed2': `{% block foo %}{% endblock %}`
}));
return environment.loadTemplate('index')
.then(({ast}) => {
const serializedAST = JSON.stringify(ast);
const deserializedAST: TwingTemplateNode = JSON.parse(serializedAST);
const template = createTemplate(deserializedAST);
return template.render(environment, {})
.then((output) => {
same(output, 'Foo');
})
.finally(end);
});
});
});
import {createSynchronousTemplate} from "../../../../../main/lib/template";
tape('createTemplate => ::ast', ({test}) => {
test('without embedded templates', ({same, end}) => {

@ -1,23 +1,22 @@
import { spy } from "sinon";
import * as tape from "tape";
import {createEnvironment} from "../../../../../main/lib/environment";
import {createArrayLoader} from "../../../../../main/lib/loader/array";
import {createTemplate} from "../../../../../main/lib/template";
import {createTemplateNode} from "../../../../../main/lib/node/template";
import {createSynchronousEnvironment} from "../../../../../main/lib/environment";
import {createSynchronousArrayLoader} from "../../../../../main/lib/loader/array";
import {createBaseNode} from "../../../../../main/lib/node";
import {createSource} from "../../../../../main/lib/source";
import {spy} from "sinon";
import {createOutputBuffer} from "../../../../../main/lib/output-buffer";
import {createContext} from "../../../../../main/lib/context";
import {createSourceMapRuntime} from "../../../../../main/lib/source-map-runtime";
import {executeNode, type TwingNodeExecutor} from "../../../../../main/lib/node-executor";
import {executeNodeSynchronously, TwingSynchronousNodeExecutor} from "../../../../../main/lib/node-executor";
import {createTemplateNode} from "../../../../../main/lib/node/template";
import {createTextNode} from "../../../../../main/lib/node/text";
import {createVerbatimNode} from "../../../../../main/lib/node/verbatim";
import {createTemplateLoader, type TwingTemplateLoader} from "../../../../../main/lib/template-loader";
import {createOutputBuffer} from "../../../../../main/lib/output-buffer";
import {createSource} from "../../../../../main/lib/source";
import {createSourceMapRuntime} from "../../../../../main/lib/source-map-runtime";
import {createSynchronousTemplate} from "../../../../../main/lib/template";
import {createSynchronousTemplateLoader, TwingSynchronousTemplateLoader} from "../../../../../main/lib/template-loader";
tape('createTemplate => ::execute', ({test}) => {
test('executes the AST according to the passed options', ({test}) => {
test('when no options is passed', ({same, end}) => {
const environment = createEnvironment(createArrayLoader({}));
const environment = createSynchronousEnvironment(createSynchronousArrayLoader({}));
const ast = createTemplateNode(
createBaseNode(null, {}, {
content: createBaseNode(null)
@ -31,26 +30,28 @@ tape('createTemplate => ::execute', ({test}) => {
1, 1
);
const executeNodeSpy = spy(executeNode);
const executeNodeSpy = spy(executeNodeSynchronously);
const template = createTemplate(ast);
const template = createSynchronousTemplate(ast);
return template.execute(
template.execute(
environment,
createContext(),
new Map(),
new Map(),
createOutputBuffer(),
{
nodeExecutor: executeNodeSpy
}
).then(() => {
same(executeNodeSpy.firstCall.args[1].sandboxed, false);
same(executeNodeSpy.firstCall.args[1].sourceMapRuntime, undefined);
}).finally(end);
)
same(executeNodeSpy.firstCall.args[1].sandboxed, false);
same(executeNodeSpy.firstCall.args[1].sourceMapRuntime, undefined);
end()
});
test('when some options are passed', ({same, end}) => {
const environment = createEnvironment(createArrayLoader({}));
const environment = createSynchronousEnvironment(createSynchronousArrayLoader({}));
const ast = createTemplateNode(
createBaseNode(null, {}, {
content: createBaseNode(null)
@ -64,15 +65,15 @@ tape('createTemplate => ::execute', ({test}) => {
1, 1
);
const executeNodeSpy = spy(executeNode);
const executeNodeSpy = spy(executeNodeSynchronously);
const template = createTemplate(ast);
const template = createSynchronousTemplate(ast);
const sourceMapRuntime = createSourceMapRuntime();
return template.execute(
template.execute(
environment,
createContext(),
new Map(),
new Map(),
createOutputBuffer(),
{
@ -80,15 +81,17 @@ tape('createTemplate => ::execute', ({test}) => {
sandboxed: true,
sourceMapRuntime
}
).then(() => {
same(executeNodeSpy.firstCall.args[1].sandboxed, true);
same(executeNodeSpy.firstCall.args[1].sourceMapRuntime, sourceMapRuntime);
}).finally(end);
)
same(executeNodeSpy.firstCall.args[1].sandboxed, true);
same(executeNodeSpy.firstCall.args[1].sourceMapRuntime, sourceMapRuntime);
end();
});
});
test('honors the passed node executor', ({same, end}) => {
const environment = createEnvironment(createArrayLoader({}));
const environment = createSynchronousEnvironment(createSynchronousArrayLoader({}));
const ast = createTemplateNode(
createBaseNode(null, {}, {
content: createBaseNode(null, {}, {
@ -105,89 +108,95 @@ tape('createTemplate => ::execute', ({test}) => {
1, 1
);
const nodeExecutor: TwingNodeExecutor = (node, executionContext) => {
const nodeExecutor: TwingSynchronousNodeExecutor = (node, executionContext) => {
if (node.type === "text") {
executionContext.outputBuffer.echo('foo');
return Promise.resolve();
}
else {
return executeNode(node, executionContext);
return executeNodeSynchronously(node, executionContext);
}
};
const template = createTemplate(ast);
const template = createSynchronousTemplate(ast);
const outputBuffer = createOutputBuffer();
outputBuffer.start();
return template.execute(environment, createContext(), new Map(), outputBuffer, {
template.execute(environment, new Map(), new Map(), outputBuffer, {
nodeExecutor
}).then(() => {
same(outputBuffer.getContents(), 'foo5');
}).finally(end);
})
same(outputBuffer.getContents(), 'foo5');
end();
});
test('honors the passed template loader', ({same, end}) => {
const environment = createEnvironment(createArrayLoader({
const environment = createSynchronousEnvironment(createSynchronousArrayLoader({
bar: 'BAR',
foo: 'FOO'
}));
const ast = environment.parse(environment.tokenize(createSource('index', `{{ include("foo") }}{{ include("bar") }}`)));
const loadedTemplates: Array<string> = [];
const baseTemplateLoader = createTemplateLoader(environment);
const baseTemplateLoader = createSynchronousTemplateLoader(environment);
const templateLoader: TwingTemplateLoader = (name, from) => {
const templateLoader: TwingSynchronousTemplateLoader = (name, from) => {
loadedTemplates.push(`${from}::${name}`);
return baseTemplateLoader(name, from);
}
const template = createTemplate(ast);
const template = createSynchronousTemplate(ast);
const outputBuffer = createOutputBuffer();
outputBuffer.start();
return template.execute(
template.execute(
environment,
createContext(),
new Map(),
new Map(),
outputBuffer,
{
templateLoader
}
).then(() => {
same(loadedTemplates, [
'index::foo',
'index::bar'
]);
}).finally(end);
);
same(loadedTemplates, [
'index::foo',
'index::bar'
]);
end();
});
test('with some blocks', async ({same, end}) => {
const environment = createEnvironment(createArrayLoader({
test('with some blocks', ({same, end}) => {
const environment = createSynchronousEnvironment(createSynchronousArrayLoader({
index: '{{ block("foo") }}, {{ block("aliased-bar") }}',
blocks: `{% block foo %}foo block content{% endblock %}{% block bar %}bar block content{% endblock %}`
}));
const template = await environment.loadTemplate('index');
const template = environment.loadTemplate('index');
const outputBuffer = createOutputBuffer();
outputBuffer.start();
const blockTemplate = await environment.loadTemplate('blocks');
const blockTemplate = environment.loadTemplate('blocks');
return template.execute(
template.execute(
environment,
createContext(),
new Map(),
new Map([
['foo', [blockTemplate, 'foo']],
['aliased-bar', [blockTemplate, 'bar']]
]),
outputBuffer
).then(() => {
same(outputBuffer.getContents(), 'foo block content, bar block content');
}).finally(end);
)
same(outputBuffer.getContents(), 'foo block content, bar block content');
end();
});
});

@ -1,19 +1,18 @@
import * as tape from "tape";
import {createEnvironment} from "../../../../../main/lib/environment";
import {createArrayLoader} from "../../../../../main/lib/loader/array";
import {createTemplate} from "../../../../../main/lib/template";
import {createSynchronousEnvironment} from "../../../../../main/lib/environment";
import {createSynchronousArrayLoader} from "../../../../../main/lib/loader/array";
import {createSynchronousTemplate} from "../../../../../main/lib/template";
import {createTemplateNode} from "../../../../../main/lib/node/template";
import {createBaseNode} from "../../../../../main/lib/node";
import {createSource} from "../../../../../main/lib/source";
import {createOutputBuffer} from "../../../../../main/lib/output-buffer";
import {createContext} from "../../../../../main/lib/context";
import {executeNode, type TwingNodeExecutor} from "../../../../../main/lib/node-executor";
import {executeNodeSynchronously, TwingSynchronousNodeExecutor} from "../../../../../main/lib/node-executor";
import {createTextNode} from "../../../../../main/lib/node/text";
import {createVerbatimNode} from "../../../../../main/lib/node/verbatim";
tape('createTemplate => ::render', ({test}) => {
test('honors the passed node executor', ({same, end}) => {
const environment = createEnvironment(createArrayLoader({}));
const environment = createSynchronousEnvironment(createSynchronousArrayLoader({}));
const ast = createTemplateNode(
createBaseNode(null, {}, {
content: createBaseNode(null, {}, {
@ -30,26 +29,28 @@ tape('createTemplate => ::render', ({test}) => {
1, 1
);
const nodeExecutor: TwingNodeExecutor = (node, executionContext) => {
const nodeExecutor: TwingSynchronousNodeExecutor = (node, executionContext) => {
if (node.type === "text") {
executionContext.outputBuffer.echo('foo');
return Promise.resolve();
} else {
return executeNode(node, executionContext);
return executeNodeSynchronously(node, executionContext);
}
};
const template = createTemplate(ast);
const template = createSynchronousTemplate(ast);
const outputBuffer = createOutputBuffer();
outputBuffer.start();
return template.render(environment, createContext(), {
template.render(environment, new Map(), {
outputBuffer,
nodeExecutor
}).then(() => {
same(outputBuffer.getContents(), 'foo5');
}).finally(end);
})
same(outputBuffer.getContents(), 'foo5');
end()
});
});