add tstyche tests
This commit is contained in:
Generated
+35
-1
@@ -39,6 +39,7 @@
|
||||
"oxlint": "^1.51.0",
|
||||
"semver": "^5.0.1",
|
||||
"tinybench": "^6.0.0",
|
||||
"tstyche": "^6.2.0",
|
||||
"typescript": "^5.9.3",
|
||||
"uglify-js": "^3.19.3",
|
||||
"vitest": "^4.0.18"
|
||||
@@ -13211,6 +13212,30 @@
|
||||
"integrity": "sha512-aZW88SY8kQbU7gpV19lN24LtXh/yD4ZZg6qieAJDDg+YBsJcSmLGK9QpnUjAKVG/xefmvJGd1WUmfpT/g6AJGA==",
|
||||
"dev": true
|
||||
},
|
||||
"node_modules/tstyche": {
|
||||
"version": "6.2.0",
|
||||
"resolved": "https://registry.npmjs.org/tstyche/-/tstyche-6.2.0.tgz",
|
||||
"integrity": "sha512-WBiB6fGGsmQCFFRGwYaKq528pCLJ0CUUdrIdUm2rnVJ1Ineskbrv9uQADWsD3uHndEV/Hesdx1Gj8PdkBahxlQ==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"bin": {
|
||||
"tstyche": "dist/bin.js"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=22.12"
|
||||
},
|
||||
"funding": {
|
||||
"url": "https://github.com/tstyche/tstyche?sponsor=1"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"typescript": ">=5.4"
|
||||
},
|
||||
"peerDependenciesMeta": {
|
||||
"typescript": {
|
||||
"optional": true
|
||||
}
|
||||
}
|
||||
},
|
||||
"node_modules/type-check": {
|
||||
"version": "0.4.0",
|
||||
"resolved": "https://registry.npmjs.org/type-check/-/type-check-0.4.0.tgz",
|
||||
@@ -13244,6 +13269,7 @@
|
||||
"integrity": "sha512-jl1vZzPDinLr9eUt3J/t7V6FgNEw9QjvBPdysz9KfQDD41fQrC2Y4vKQdiaUpFT4bXlb1RHhLpp8wtm6M5TgSw==",
|
||||
"dev": true,
|
||||
"license": "Apache-2.0",
|
||||
"peer": true,
|
||||
"bin": {
|
||||
"tsc": "bin/tsc",
|
||||
"tsserver": "bin/tsserver"
|
||||
@@ -22876,6 +22902,13 @@
|
||||
"integrity": "sha512-aZW88SY8kQbU7gpV19lN24LtXh/yD4ZZg6qieAJDDg+YBsJcSmLGK9QpnUjAKVG/xefmvJGd1WUmfpT/g6AJGA==",
|
||||
"dev": true
|
||||
},
|
||||
"tstyche": {
|
||||
"version": "6.2.0",
|
||||
"resolved": "https://registry.npmjs.org/tstyche/-/tstyche-6.2.0.tgz",
|
||||
"integrity": "sha512-WBiB6fGGsmQCFFRGwYaKq528pCLJ0CUUdrIdUm2rnVJ1Ineskbrv9uQADWsD3uHndEV/Hesdx1Gj8PdkBahxlQ==",
|
||||
"dev": true,
|
||||
"requires": {}
|
||||
},
|
||||
"type-check": {
|
||||
"version": "0.4.0",
|
||||
"resolved": "https://registry.npmjs.org/type-check/-/type-check-0.4.0.tgz",
|
||||
@@ -22899,7 +22932,8 @@
|
||||
"version": "5.9.3",
|
||||
"resolved": "https://registry.npmjs.org/typescript/-/typescript-5.9.3.tgz",
|
||||
"integrity": "sha512-jl1vZzPDinLr9eUt3J/t7V6FgNEw9QjvBPdysz9KfQDD41fQrC2Y4vKQdiaUpFT4bXlb1RHhLpp8wtm6M5TgSw==",
|
||||
"dev": true
|
||||
"dev": true,
|
||||
"peer": true
|
||||
},
|
||||
"uglify-js": {
|
||||
"version": "3.19.3",
|
||||
|
||||
+2
-1
@@ -42,7 +42,7 @@
|
||||
"lint": "npm run lint:oxlint && npm run lint:format && npm run lint:types && npm run lint:compat",
|
||||
"lint:oxlint": "oxlint --max-warnings 0 .",
|
||||
"lint:format": "oxfmt --check .",
|
||||
"lint:types": "tsc --noEmit --project types",
|
||||
"lint:types": "tsc --noEmit --project types && tstyche",
|
||||
"lint:compat": "eslint",
|
||||
"test": "npm run build && vitest run --project node --project tasks --project rspack --coverage",
|
||||
"test:browser": "vitest run --project browser",
|
||||
@@ -85,6 +85,7 @@
|
||||
"oxlint": "^1.51.0",
|
||||
"semver": "^5.0.1",
|
||||
"tinybench": "^6.0.0",
|
||||
"tstyche": "^6.2.0",
|
||||
"typescript": "^5.9.3",
|
||||
"uglify-js": "^3.19.3",
|
||||
"vitest": "^4.0.18"
|
||||
|
||||
+3
-2
@@ -25,7 +25,7 @@ describe('source-map', function () {
|
||||
expect(template.map).toBeTruthy();
|
||||
}
|
||||
});
|
||||
it('should map source properly', function () {
|
||||
it('should map source properly', async function () {
|
||||
var templateSource =
|
||||
' b{{hello}} \n {{bar}}a {{#block arg hash=(subex 1 subval)}}{{/block}}',
|
||||
template = Handlebars.precompile(templateSource, {
|
||||
@@ -34,7 +34,7 @@ describe('source-map', function () {
|
||||
});
|
||||
|
||||
if (template.map) {
|
||||
var consumer = new SourceMapConsumer(template.map),
|
||||
var consumer = await new SourceMapConsumer(template.map),
|
||||
lines = template.code.split('\n'),
|
||||
srcLines = templateSource.split('\n'),
|
||||
generated = grepLine('" b"', lines),
|
||||
@@ -43,6 +43,7 @@ describe('source-map', function () {
|
||||
var mapped = consumer.originalPositionFor(generated);
|
||||
expect(mapped.line).toBe(source.line);
|
||||
expect(mapped.column).toBe(source.column);
|
||||
consumer.destroy();
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
@@ -1,256 +0,0 @@
|
||||
const childProcess = require('child_process');
|
||||
const fs = require('fs');
|
||||
const os = require('os');
|
||||
const path = require('path');
|
||||
|
||||
const testCases = [
|
||||
{
|
||||
binInputParameters: ['-a', 'spec/artifacts/empty.handlebars'],
|
||||
outputLocation: 'stdout',
|
||||
expectedOutputSpec: './spec/expected/empty.amd.js',
|
||||
},
|
||||
{
|
||||
binInputParameters: [
|
||||
'-a',
|
||||
'-f',
|
||||
'TEST_OUTPUT',
|
||||
'spec/artifacts/empty.handlebars',
|
||||
],
|
||||
outputLocation: 'TEST_OUTPUT',
|
||||
expectedOutputSpec: './spec/expected/empty.amd.js',
|
||||
},
|
||||
{
|
||||
binInputParameters: [
|
||||
'-a',
|
||||
'-n',
|
||||
'CustomNamespace.templates',
|
||||
'spec/artifacts/empty.handlebars',
|
||||
],
|
||||
outputLocation: 'stdout',
|
||||
expectedOutputSpec: './spec/expected/empty.amd.namespace.js',
|
||||
},
|
||||
{
|
||||
binInputParameters: [
|
||||
'-a',
|
||||
'--namespace',
|
||||
'CustomNamespace.templates',
|
||||
'spec/artifacts/empty.handlebars',
|
||||
],
|
||||
outputLocation: 'stdout',
|
||||
expectedOutputSpec: './spec/expected/empty.amd.namespace.js',
|
||||
},
|
||||
{
|
||||
binInputParameters: ['-a', '-s', 'spec/artifacts/empty.handlebars'],
|
||||
outputLocation: 'stdout',
|
||||
expectedOutputSpec: './spec/expected/empty.amd.simple.js',
|
||||
},
|
||||
{
|
||||
binInputParameters: ['-a', '-m', 'spec/artifacts/empty.handlebars'],
|
||||
outputLocation: 'stdout',
|
||||
expectedOutputSpec: './spec/expected/empty.amd.min.js',
|
||||
},
|
||||
{
|
||||
binInputParameters: [
|
||||
'spec/artifacts/known.helpers.handlebars',
|
||||
'-a',
|
||||
'-k',
|
||||
'someHelper',
|
||||
'-k',
|
||||
'anotherHelper',
|
||||
'-o',
|
||||
],
|
||||
outputLocation: 'stdout',
|
||||
expectedOutputSpec: './spec/expected/non.empty.amd.known.helper.js',
|
||||
},
|
||||
{
|
||||
binInputParameters: ['--help'],
|
||||
outputLocation: 'stdout',
|
||||
expectedOutputSpec: './spec/expected/help.menu.txt',
|
||||
},
|
||||
{
|
||||
binInputParameters: ['-v'],
|
||||
outputLocation: 'stdout',
|
||||
expectedOutput: require('../../lib').VERSION,
|
||||
},
|
||||
{
|
||||
binInputParameters: [
|
||||
'-a',
|
||||
'-e',
|
||||
'hbs',
|
||||
'./spec/artifacts/non.default.extension.hbs',
|
||||
],
|
||||
outputLocation: 'stdout',
|
||||
expectedOutputSpec: './spec/expected/non.default.extension.amd.js',
|
||||
},
|
||||
{
|
||||
binInputParameters: [
|
||||
'-a',
|
||||
'-p',
|
||||
'./spec/artifacts/partial.template.handlebars',
|
||||
],
|
||||
outputLocation: 'stdout',
|
||||
expectedOutputSpec: './spec/expected/partial.template.js',
|
||||
},
|
||||
{
|
||||
binInputParameters: ['spec/artifacts/empty.handlebars', '-c'],
|
||||
outputLocation: 'stdout',
|
||||
expectedOutputSpec: './spec/expected/empty.common.js',
|
||||
},
|
||||
{
|
||||
binInputParameters: [
|
||||
'spec/artifacts/empty.handlebars',
|
||||
'spec/artifacts/empty.handlebars',
|
||||
'-a',
|
||||
'-n',
|
||||
'someNameSpace',
|
||||
],
|
||||
outputLocation: 'stdout',
|
||||
expectedOutputSpec: './spec/expected/namespace.amd.js',
|
||||
},
|
||||
{
|
||||
binInputParameters: [
|
||||
'spec/artifacts/empty.handlebars',
|
||||
'-h',
|
||||
'some-path/',
|
||||
'-a',
|
||||
],
|
||||
outputLocation: 'stdout',
|
||||
expectedOutputSpec: './spec/expected/handlebar.path.amd.js',
|
||||
},
|
||||
{
|
||||
binInputParameters: [
|
||||
'spec/artifacts/partial.template.handlebars',
|
||||
'-r',
|
||||
'spec',
|
||||
'-a',
|
||||
],
|
||||
outputLocation: 'stdout',
|
||||
expectedOutputSpec: './spec/expected/empty.root.amd.js',
|
||||
},
|
||||
{
|
||||
binInputParameters: [
|
||||
'-i',
|
||||
'<div>1</div>',
|
||||
'-i',
|
||||
'<div>2</div>',
|
||||
'-N',
|
||||
'firstTemplate',
|
||||
'-N',
|
||||
'secondTemplate',
|
||||
'-a',
|
||||
],
|
||||
outputLocation: 'stdout',
|
||||
expectedOutputSpec: './spec/expected/empty.name.amd.js',
|
||||
},
|
||||
{
|
||||
binInputParameters: [
|
||||
'-i',
|
||||
'<div>1</div>',
|
||||
'-a',
|
||||
'-m',
|
||||
'-N',
|
||||
'test',
|
||||
'--map',
|
||||
'./spec/tmp/source.map.amd.txt',
|
||||
],
|
||||
outputLocation: 'stdout',
|
||||
expectedOutputSpec: './spec/expected/source.map.amd.js',
|
||||
},
|
||||
{
|
||||
binInputParameters: ['./spec/artifacts/bom.handlebars', '-b', '-a'],
|
||||
outputLocation: 'stdout',
|
||||
expectedOutputSpec: './spec/expected/bom.amd.js',
|
||||
},
|
||||
// Issue #1673
|
||||
{
|
||||
binInputParameters: [
|
||||
'--amd',
|
||||
'--no-amd',
|
||||
'spec/artifacts/empty.handlebars',
|
||||
],
|
||||
outputLocation: 'stdout',
|
||||
expectedOutputSpec: './spec/expected/empty.common.js',
|
||||
},
|
||||
];
|
||||
|
||||
expect.extend({
|
||||
toEqualWithRelaxedSpace(received, expected) {
|
||||
const normalize = (str) =>
|
||||
typeof str === 'string'
|
||||
? str
|
||||
.replace(/\r\n/g, '\n')
|
||||
.split('\n')
|
||||
.map((line) => line.replace(/\s+/g, ' ').trim())
|
||||
.filter((line) => line.length > 0)
|
||||
.join('\n')
|
||||
.trim()
|
||||
: str;
|
||||
|
||||
const normalizedReceived = normalize(received);
|
||||
const normalizedExpected = normalize(expected);
|
||||
const pass = normalizedReceived === normalizedExpected;
|
||||
|
||||
return {
|
||||
pass,
|
||||
message: () =>
|
||||
`Expected output to match with relaxed whitespace.\n\n` +
|
||||
`Expected:\n${normalizedExpected}\n\nReceived:\n${normalizedReceived}`,
|
||||
};
|
||||
},
|
||||
});
|
||||
|
||||
describe('bin/handlebars', function () {
|
||||
testCases.forEach(
|
||||
(
|
||||
{
|
||||
binInputParameters,
|
||||
outputLocation,
|
||||
expectedOutputSpec,
|
||||
expectedOutput,
|
||||
},
|
||||
index
|
||||
) => {
|
||||
it(`test case ${index}: handlebars ${binInputParameters.join(
|
||||
' '
|
||||
)}`, function () {
|
||||
const stdout = executeBinHandlebars(...binInputParameters);
|
||||
|
||||
if (!expectedOutput && expectedOutputSpec) {
|
||||
expectedOutput = fs.readFileSync(expectedOutputSpec, 'utf-8');
|
||||
}
|
||||
|
||||
const useStdout = outputLocation === 'stdout';
|
||||
const actualOutput = useStdout
|
||||
? stdout
|
||||
: fs.readFileSync(outputLocation, 'utf-8');
|
||||
|
||||
if (!useStdout) {
|
||||
fs.unlinkSync(outputLocation);
|
||||
}
|
||||
|
||||
expect(actualOutput).toEqualWithRelaxedSpace(expectedOutput);
|
||||
});
|
||||
}
|
||||
);
|
||||
});
|
||||
|
||||
// helper functions
|
||||
|
||||
function executeBinHandlebars(...args) {
|
||||
if (os.platform() === 'win32') {
|
||||
// On Windows, the executable handlebars.js file cannot be run directly
|
||||
const nodeJs = process.argv[0];
|
||||
return execFilesSyncUtf8(nodeJs, ['./bin/handlebars.js'].concat(args));
|
||||
}
|
||||
return execFilesSyncUtf8('./bin/handlebars.js', args);
|
||||
}
|
||||
|
||||
function execFilesSyncUtf8(command, args) {
|
||||
const env = process.env;
|
||||
env.PATH = addPathToNodeJs(env.PATH);
|
||||
return childProcess.execFileSync(command, args, { encoding: 'utf-8', env });
|
||||
}
|
||||
|
||||
function addPathToNodeJs(pathEnvironment) {
|
||||
return path.dirname(process.argv0) + path.delimiter + pathEnvironment;
|
||||
}
|
||||
+138
-100
@@ -1,87 +1,128 @@
|
||||
const fs = require('fs');
|
||||
const { execCommand, FileTestHelper } = require('cli-testlab');
|
||||
const Handlebars = require('../../lib');
|
||||
|
||||
const cli = 'node ./bin/handlebars.js';
|
||||
|
||||
describe('bin/handlebars (cli-testlab)', function () {
|
||||
expect.extend({
|
||||
toEqualWithRelaxedSpace(received, expected) {
|
||||
const normalize = (str) =>
|
||||
typeof str === 'string'
|
||||
? str
|
||||
.replace(/\r\n/g, '\n')
|
||||
.split('\n')
|
||||
.map((line) => line.replace(/\s+/g, ' ').trim())
|
||||
.filter((line) => line.length > 0)
|
||||
.join('\n')
|
||||
.trim()
|
||||
: str;
|
||||
|
||||
const normalizedReceived = normalize(received);
|
||||
const normalizedExpected = normalize(expected);
|
||||
const pass = normalizedReceived === normalizedExpected;
|
||||
|
||||
return {
|
||||
pass,
|
||||
message: () =>
|
||||
`Expected output to match with relaxed whitespace.\n\n` +
|
||||
`Expected:\n${normalizedExpected}\n\nReceived:\n${normalizedReceived}`,
|
||||
};
|
||||
},
|
||||
});
|
||||
|
||||
function expectedFile(specPath) {
|
||||
return fs.readFileSync(specPath, 'utf-8');
|
||||
}
|
||||
|
||||
describe('bin/handlebars', function () {
|
||||
describe('help and version', function () {
|
||||
it('should display help menu with --help', async function () {
|
||||
await execCommand(`${cli} --help`, {
|
||||
expectedOutput: [
|
||||
'Precompile handlebar templates.',
|
||||
'Usage:',
|
||||
'--output',
|
||||
'--amd',
|
||||
'--commonjs',
|
||||
'--partial',
|
||||
'--extension',
|
||||
'--bom',
|
||||
],
|
||||
});
|
||||
it('--help displays help menu', async function () {
|
||||
const result = await execCommand(`${cli} --help`);
|
||||
expect(result.stdout).toEqualWithRelaxedSpace(
|
||||
expectedFile('./spec/expected/help.menu.txt')
|
||||
);
|
||||
});
|
||||
|
||||
it('should display version with -v', async function () {
|
||||
it('no arguments displays help menu', async function () {
|
||||
const result = await execCommand(`${cli}`);
|
||||
expect(result.stdout).toEqualWithRelaxedSpace(
|
||||
expectedFile('./spec/expected/help.menu.txt')
|
||||
);
|
||||
});
|
||||
|
||||
it('-v prints the compiler version', async function () {
|
||||
await execCommand(`${cli} -v`, {
|
||||
expectedOutput: Handlebars.VERSION,
|
||||
});
|
||||
});
|
||||
|
||||
it('should display help when no arguments are provided', async function () {
|
||||
await execCommand(`${cli}`, {
|
||||
expectedOutput: 'Precompile handlebar templates.',
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('precompilation output modes', function () {
|
||||
it('should precompile a template in AMD mode', async function () {
|
||||
describe('AMD output', function () {
|
||||
it('-a produces AMD output', async function () {
|
||||
const result = await execCommand(
|
||||
`${cli} -a spec/artifacts/empty.handlebars`
|
||||
);
|
||||
expect(result.stdout).toContain("define(['handlebars.runtime']");
|
||||
expect(result.stdout).toContain("templates['empty']");
|
||||
});
|
||||
|
||||
it('should precompile a template in CommonJS mode', async function () {
|
||||
const result = await execCommand(
|
||||
`${cli} spec/artifacts/empty.handlebars -c`
|
||||
expect(result.stdout).toEqualWithRelaxedSpace(
|
||||
expectedFile('./spec/expected/empty.amd.js')
|
||||
);
|
||||
expect(result.stdout).toContain('Handlebars.template');
|
||||
expect(result.stdout).not.toContain("define(['handlebars.runtime']");
|
||||
});
|
||||
|
||||
it('should precompile in simple mode', async function () {
|
||||
it('-a -s produces simple AMD output', async function () {
|
||||
const result = await execCommand(
|
||||
`${cli} -a -s spec/artifacts/empty.handlebars`
|
||||
);
|
||||
expect(result.stdout).toContain('"compiler"');
|
||||
expect(result.stdout).toContain('"main"');
|
||||
expect(result.stdout).not.toContain("templates['empty']");
|
||||
expect(result.stdout).toEqualWithRelaxedSpace(
|
||||
expectedFile('./spec/expected/empty.amd.simple.js')
|
||||
);
|
||||
});
|
||||
|
||||
it('should precompile with minification', async function () {
|
||||
it('-a -m produces minified AMD output', async function () {
|
||||
const result = await execCommand(
|
||||
`${cli} -a -m spec/artifacts/empty.handlebars`
|
||||
);
|
||||
expect(result.stdout).toContain('define(');
|
||||
expect(result.stdout).toContain('handlebars.runtime');
|
||||
expect(result.stdout).toEqualWithRelaxedSpace(
|
||||
expectedFile('./spec/expected/empty.amd.min.js')
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
describe('custom namespace', function () {
|
||||
it('should use custom namespace with -n', async function () {
|
||||
describe('CommonJS output', function () {
|
||||
it('-c produces CommonJS output', async function () {
|
||||
const result = await execCommand(
|
||||
`${cli} spec/artifacts/empty.handlebars -c`
|
||||
);
|
||||
expect(result.stdout).toEqualWithRelaxedSpace(
|
||||
expectedFile('./spec/expected/empty.common.js')
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
describe('namespace', function () {
|
||||
it('-n sets custom namespace', async function () {
|
||||
const result = await execCommand(
|
||||
`${cli} -a -n CustomNamespace.templates spec/artifacts/empty.handlebars`
|
||||
);
|
||||
expect(result.stdout).toContain('CustomNamespace.templates');
|
||||
expect(result.stdout).not.toContain('Handlebars.templates');
|
||||
expect(result.stdout).toEqualWithRelaxedSpace(
|
||||
expectedFile('./spec/expected/empty.amd.namespace.js')
|
||||
);
|
||||
});
|
||||
|
||||
it('should use custom namespace with --namespace', async function () {
|
||||
it('--namespace sets custom namespace', async function () {
|
||||
const result = await execCommand(
|
||||
`${cli} -a --namespace CustomNamespace.templates spec/artifacts/empty.handlebars`
|
||||
);
|
||||
expect(result.stdout).toContain('CustomNamespace.templates');
|
||||
expect(result.stdout).toEqualWithRelaxedSpace(
|
||||
expectedFile('./spec/expected/empty.amd.namespace.js')
|
||||
);
|
||||
});
|
||||
|
||||
it('multiple files share a namespace', async function () {
|
||||
const result = await execCommand(
|
||||
`${cli} spec/artifacts/empty.handlebars spec/artifacts/empty.handlebars -a -n someNameSpace`
|
||||
);
|
||||
expect(result.stdout).toEqualWithRelaxedSpace(
|
||||
expectedFile('./spec/expected/namespace.amd.js')
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
@@ -97,8 +138,8 @@ describe('bin/handlebars (cli-testlab)', function () {
|
||||
files.cleanup();
|
||||
});
|
||||
|
||||
it('should write output to a file with -f', async function () {
|
||||
const outputFile = 'tmp/cli-testlab-output.js';
|
||||
it('-f writes output to a file', async function () {
|
||||
const outputFile = 'tmp/cli-test-output.js';
|
||||
files.registerForCleanup(outputFile);
|
||||
|
||||
await execCommand(
|
||||
@@ -107,21 +148,22 @@ describe('bin/handlebars (cli-testlab)', function () {
|
||||
|
||||
expect(files.fileExists(outputFile)).toBe(true);
|
||||
const content = files.getFileTextContent(outputFile);
|
||||
expect(content).toContain("define(['handlebars.runtime']");
|
||||
expect(content).toContain("templates['empty']");
|
||||
expect(content).toEqualWithRelaxedSpace(
|
||||
expectedFile('./spec/expected/empty.amd.js')
|
||||
);
|
||||
});
|
||||
|
||||
it('should generate source map file with --map', async function () {
|
||||
const mapFile = 'tmp/cli-testlab-source.map';
|
||||
it('--map writes source map and appends sourceMappingURL', async function () {
|
||||
const mapFile = 'tmp/cli-test-source.map';
|
||||
files.registerForCleanup(mapFile);
|
||||
|
||||
await execCommand(
|
||||
const result = await execCommand(
|
||||
`${cli} -i "<div>1</div>" -a -m -N test --map ${mapFile}`
|
||||
);
|
||||
|
||||
expect(result.stdout).toContain('sourceMappingURL=');
|
||||
expect(files.fileExists(mapFile)).toBe(true);
|
||||
const mapContent = files.getFileTextContent(mapFile);
|
||||
const parsed = JSON.parse(mapContent);
|
||||
const parsed = JSON.parse(files.getFileTextContent(mapFile));
|
||||
expect(parsed).toHaveProperty('version', 3);
|
||||
expect(parsed).toHaveProperty('sources');
|
||||
expect(parsed).toHaveProperty('mappings');
|
||||
@@ -129,39 +171,63 @@ describe('bin/handlebars (cli-testlab)', function () {
|
||||
});
|
||||
|
||||
describe('template options', function () {
|
||||
it('should support custom extension with -e', async function () {
|
||||
it('-e sets custom extension', async function () {
|
||||
const result = await execCommand(
|
||||
`${cli} -a -e hbs ./spec/artifacts/non.default.extension.hbs`
|
||||
);
|
||||
expect(result.stdout).toContain("define(['handlebars.runtime']");
|
||||
expect(result.stdout).toContain("templates['non.default.extension']");
|
||||
expect(result.stdout).toEqualWithRelaxedSpace(
|
||||
expectedFile('./spec/expected/non.default.extension.amd.js')
|
||||
);
|
||||
});
|
||||
|
||||
it('should compile as partial with -p', async function () {
|
||||
it('-p compiles as partial', async function () {
|
||||
const result = await execCommand(
|
||||
`${cli} -a -p ./spec/artifacts/partial.template.handlebars`
|
||||
);
|
||||
expect(result.stdout).toContain('Handlebars.partials');
|
||||
expect(result.stdout).toEqualWithRelaxedSpace(
|
||||
expectedFile('./spec/expected/partial.template.js')
|
||||
);
|
||||
});
|
||||
|
||||
it('should strip root from template names with -r', async function () {
|
||||
it('-r strips root from template names', async function () {
|
||||
const result = await execCommand(
|
||||
`${cli} spec/artifacts/partial.template.handlebars -r spec -a`
|
||||
);
|
||||
expect(result.stdout).not.toContain("templates['spec/");
|
||||
expect(result.stdout).toEqualWithRelaxedSpace(
|
||||
expectedFile('./spec/expected/empty.root.amd.js')
|
||||
);
|
||||
});
|
||||
|
||||
it('should strip BOM with -b', async function () {
|
||||
it('-b strips BOM', async function () {
|
||||
const result = await execCommand(
|
||||
`${cli} ./spec/artifacts/bom.handlebars -b -a`
|
||||
);
|
||||
expect(result.stdout).not.toContain('\uFEFF');
|
||||
expect(result.stdout).toContain("define(['handlebars.runtime']");
|
||||
expect(result.stdout).toEqualWithRelaxedSpace(
|
||||
expectedFile('./spec/expected/bom.amd.js')
|
||||
);
|
||||
});
|
||||
|
||||
it('-h sets custom handlebar path', async function () {
|
||||
const result = await execCommand(
|
||||
`${cli} spec/artifacts/empty.handlebars -h some-path/ -a`
|
||||
);
|
||||
expect(result.stdout).toEqualWithRelaxedSpace(
|
||||
expectedFile('./spec/expected/handlebar.path.amd.js')
|
||||
);
|
||||
});
|
||||
|
||||
it('-k -o sets known helpers only', async function () {
|
||||
const result = await execCommand(
|
||||
`${cli} spec/artifacts/known.helpers.handlebars -a -k someHelper -k anotherHelper -o`
|
||||
);
|
||||
expect(result.stdout).toEqualWithRelaxedSpace(
|
||||
expectedFile('./spec/expected/non.empty.amd.known.helper.js')
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
describe('inline templates', function () {
|
||||
it('should compile inline template with -i', async function () {
|
||||
it('-i compiles inline template', async function () {
|
||||
const result = await execCommand(
|
||||
`${cli} -i "<div>hello</div>" -a -N myTemplate`
|
||||
);
|
||||
@@ -169,52 +235,24 @@ describe('bin/handlebars (cli-testlab)', function () {
|
||||
expect(result.stdout).toContain("templates['myTemplate']");
|
||||
});
|
||||
|
||||
it('should compile multiple inline templates', async function () {
|
||||
it('-i compiles multiple inline templates with -N', async function () {
|
||||
const result = await execCommand(
|
||||
`${cli} -i "<div>1</div>" -i "<div>2</div>" -N first -N second -a`
|
||||
`${cli} -i "<div>1</div>" -i "<div>2</div>" -N firstTemplate -N secondTemplate -a`
|
||||
);
|
||||
expect(result.stdout).toContain("templates['first']");
|
||||
expect(result.stdout).toContain("templates['second']");
|
||||
});
|
||||
});
|
||||
|
||||
describe('known helpers', function () {
|
||||
it('should accept known helpers with -k', async function () {
|
||||
const result = await execCommand(
|
||||
`${cli} spec/artifacts/known.helpers.handlebars -a -k someHelper -k anotherHelper -o`
|
||||
);
|
||||
expect(result.stdout).toContain("define(['handlebars.runtime']");
|
||||
});
|
||||
});
|
||||
|
||||
describe('handlebar path', function () {
|
||||
it('should accept custom handlebar path with -h', async function () {
|
||||
const result = await execCommand(
|
||||
`${cli} spec/artifacts/empty.handlebars -h some-path/ -a`
|
||||
);
|
||||
expect(result.stdout).toContain(
|
||||
"define(['some-path/handlebars.runtime']"
|
||||
expect(result.stdout).toEqualWithRelaxedSpace(
|
||||
expectedFile('./spec/expected/empty.name.amd.js')
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
describe('negated boolean flags', function () {
|
||||
it('should support --no-amd to negate --amd (issue #1673)', async function () {
|
||||
it('--no-amd negates --amd (issue #1673)', async function () {
|
||||
const result = await execCommand(
|
||||
`${cli} --amd --no-amd spec/artifacts/empty.handlebars`
|
||||
);
|
||||
expect(result.stdout).not.toContain("define(['handlebars.runtime']");
|
||||
expect(result.stdout).toContain('Handlebars.template');
|
||||
});
|
||||
});
|
||||
|
||||
describe('multiple files', function () {
|
||||
it('should precompile multiple files into a single output', async function () {
|
||||
const result = await execCommand(
|
||||
`${cli} spec/artifacts/empty.handlebars spec/artifacts/empty.handlebars -a -n someNameSpace`
|
||||
expect(result.stdout).toEqualWithRelaxedSpace(
|
||||
expectedFile('./spec/expected/empty.common.js')
|
||||
);
|
||||
expect(result.stdout).toContain('someNameSpace');
|
||||
expect(result.stdout).toContain("define(['handlebars.runtime']");
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
@@ -0,0 +1,4 @@
|
||||
{
|
||||
"testFileMatch": ["types/__typetests__/**/*.tst.ts"],
|
||||
"tsconfig": "./types/tsconfig.json"
|
||||
}
|
||||
@@ -0,0 +1,645 @@
|
||||
import { expect, test, describe } from 'tstyche';
|
||||
import Handlebars from 'handlebars';
|
||||
import {
|
||||
HandlebarsTemplatable,
|
||||
HandlebarsTemplateDelegate,
|
||||
HandlebarsTemplates,
|
||||
TemplateSpecification,
|
||||
CompileOptions,
|
||||
PrecompileOptions,
|
||||
RuntimeOptions,
|
||||
KnownHelpers,
|
||||
BuiltinHelperName,
|
||||
CustomHelperName,
|
||||
Logger,
|
||||
CompilerInfo,
|
||||
hbs,
|
||||
} from 'handlebars';
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// Handlebars.compile
|
||||
// ---------------------------------------------------------------------------
|
||||
describe('Handlebars.compile', () => {
|
||||
test('returns a HandlebarsTemplateDelegate', () => {
|
||||
expect(Handlebars.compile('{{name}}')).type.toBe<
|
||||
HandlebarsTemplateDelegate<any>
|
||||
>();
|
||||
});
|
||||
|
||||
test('returns a typed delegate when generic is specified', () => {
|
||||
expect(Handlebars.compile<{ name: string }>('{{name}}')).type.toBe<
|
||||
HandlebarsTemplateDelegate<{ name: string }>
|
||||
>();
|
||||
});
|
||||
|
||||
test('compiled template returns string', () => {
|
||||
const template = Handlebars.compile('{{name}}');
|
||||
expect(template({})).type.toBe<string>();
|
||||
});
|
||||
|
||||
test('accepts CompileOptions', () => {
|
||||
expect(
|
||||
Handlebars.compile('test', { strict: true, noEscape: true })
|
||||
).type.not.toRaiseError();
|
||||
});
|
||||
|
||||
test('accepts knownHelpers with built-in and custom names', () => {
|
||||
expect(
|
||||
Handlebars.compile('test', {
|
||||
knownHelpers: { each: true, customHelper: true },
|
||||
})
|
||||
).type.not.toRaiseError();
|
||||
});
|
||||
});
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// Handlebars.precompile
|
||||
// ---------------------------------------------------------------------------
|
||||
describe('Handlebars.precompile', () => {
|
||||
test('returns TemplateSpecification', () => {
|
||||
expect(
|
||||
Handlebars.precompile('{{name}}')
|
||||
).type.toBe<TemplateSpecification>();
|
||||
});
|
||||
|
||||
test('accepts PrecompileOptions', () => {
|
||||
expect(
|
||||
Handlebars.precompile('test', { srcName: 'a.hbs', destName: 'a.js' })
|
||||
).type.not.toRaiseError();
|
||||
});
|
||||
});
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// Handlebars.template
|
||||
// ---------------------------------------------------------------------------
|
||||
describe('Handlebars.template', () => {
|
||||
test('returns HandlebarsTemplateDelegate', () => {
|
||||
const spec = {} as TemplateSpecification;
|
||||
expect(Handlebars.template(spec)).type.toBe<
|
||||
HandlebarsTemplateDelegate<any>
|
||||
>();
|
||||
});
|
||||
});
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// Handlebars.registerHelper / unregisterHelper
|
||||
// ---------------------------------------------------------------------------
|
||||
describe('Handlebars.registerHelper', () => {
|
||||
test('accepts name and function', () => {
|
||||
expect(
|
||||
Handlebars.registerHelper('myHelper', (ctx: any) => 'result')
|
||||
).type.toBe<void>();
|
||||
});
|
||||
|
||||
test('accepts a spec object', () => {
|
||||
expect(
|
||||
Handlebars.registerHelper({ myHelper: () => 'result' })
|
||||
).type.toBe<void>();
|
||||
});
|
||||
});
|
||||
|
||||
describe('Handlebars.unregisterHelper', () => {
|
||||
test('accepts a name', () => {
|
||||
expect(Handlebars.unregisterHelper('myHelper')).type.toBe<void>();
|
||||
});
|
||||
});
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// Handlebars.registerPartial / unregisterPartial
|
||||
// ---------------------------------------------------------------------------
|
||||
describe('Handlebars.registerPartial', () => {
|
||||
test('accepts name and string', () => {
|
||||
expect(
|
||||
Handlebars.registerPartial('myPartial', '<div>{{name}}</div>')
|
||||
).type.toBe<void>();
|
||||
});
|
||||
|
||||
test('accepts spec object', () => {
|
||||
expect(
|
||||
Handlebars.registerPartial({ myPartial: '<div>{{name}}</div>' })
|
||||
).type.toBe<void>();
|
||||
});
|
||||
});
|
||||
|
||||
describe('Handlebars.unregisterPartial', () => {
|
||||
test('accepts a name', () => {
|
||||
expect(Handlebars.unregisterPartial('myPartial')).type.toBe<void>();
|
||||
});
|
||||
});
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// Handlebars.registerDecorator / unregisterDecorator
|
||||
// ---------------------------------------------------------------------------
|
||||
describe('Handlebars.registerDecorator', () => {
|
||||
test('accepts name and function', () => {
|
||||
expect(
|
||||
Handlebars.registerDecorator('myDecorator', () => {})
|
||||
).type.toBe<void>();
|
||||
});
|
||||
});
|
||||
|
||||
describe('Handlebars.unregisterDecorator', () => {
|
||||
test('accepts a name', () => {
|
||||
expect(Handlebars.unregisterDecorator('myDecorator')).type.toBe<void>();
|
||||
});
|
||||
});
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// Handlebars utility functions
|
||||
// ---------------------------------------------------------------------------
|
||||
describe('Handlebars utility functions', () => {
|
||||
test('K returns void', () => {
|
||||
expect(Handlebars.K()).type.toBe<void>();
|
||||
});
|
||||
|
||||
test('createFrame returns any', () => {
|
||||
expect(Handlebars.createFrame({})).type.toBe<any>();
|
||||
});
|
||||
|
||||
test('blockParams returns any[]', () => {
|
||||
expect(Handlebars.blockParams([], [])).type.toBe<any[]>();
|
||||
});
|
||||
|
||||
test('log returns void', () => {
|
||||
expect(Handlebars.log(1, 'message')).type.toBe<void>();
|
||||
});
|
||||
|
||||
test('noConflict returns typeof Handlebars', () => {
|
||||
expect(Handlebars.noConflict()).type.toBe<typeof Handlebars>();
|
||||
});
|
||||
|
||||
test('escapeExpression is exported at top level', () => {
|
||||
expect(Handlebars.escapeExpression('test')).type.toBe<string>();
|
||||
});
|
||||
});
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// Handlebars.HelperOptions
|
||||
// ---------------------------------------------------------------------------
|
||||
describe('Handlebars.HelperOptions', () => {
|
||||
test('fn and inverse are callable TemplateDelegates', () => {
|
||||
const options = {} as Handlebars.HelperOptions;
|
||||
expect(options.fn({})).type.toBe<string>();
|
||||
expect(options.inverse({})).type.toBe<string>();
|
||||
});
|
||||
|
||||
test('hash is a Record', () => {
|
||||
const options = {} as Handlebars.HelperOptions;
|
||||
expect(options.hash).type.toBe<Record<string, any>>();
|
||||
});
|
||||
});
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// Handlebars.SafeString
|
||||
// ---------------------------------------------------------------------------
|
||||
describe('Handlebars.SafeString', () => {
|
||||
test('is constructable with string', () => {
|
||||
expect(
|
||||
new Handlebars.SafeString('<b>bold</b>')
|
||||
).type.toBe<Handlebars.SafeString>();
|
||||
});
|
||||
|
||||
test('toString returns string', () => {
|
||||
const safe = new Handlebars.SafeString('test');
|
||||
expect(safe.toString()).type.toBe<string>();
|
||||
});
|
||||
|
||||
test('toHTML returns string', () => {
|
||||
const safe = new Handlebars.SafeString('test');
|
||||
expect(safe.toHTML()).type.toBe<string>();
|
||||
});
|
||||
});
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// Handlebars.Exception
|
||||
// ---------------------------------------------------------------------------
|
||||
describe('Handlebars.Exception', () => {
|
||||
test('is constructable with message only', () => {
|
||||
expect(new Handlebars.Exception('error')).type.toBe<Handlebars.Exception>();
|
||||
});
|
||||
|
||||
test('is constructable with message and node', () => {
|
||||
expect(
|
||||
new Handlebars.Exception('error', {
|
||||
type: 'MustacheStatement',
|
||||
loc: {
|
||||
source: 'source',
|
||||
start: { line: 1, column: 0 },
|
||||
end: { line: 1, column: 10 },
|
||||
},
|
||||
})
|
||||
).type.not.toRaiseError();
|
||||
});
|
||||
|
||||
test('has all expected fields', () => {
|
||||
const ex = new Handlebars.Exception('msg');
|
||||
expect(ex.message).type.toBe<string>();
|
||||
expect(ex.name).type.toBe<string>();
|
||||
expect(ex.description).type.toBe<string>();
|
||||
expect(ex.fileName).type.toBe<string>();
|
||||
expect(ex.number).type.toBe<number>();
|
||||
expect(ex.lineNumber).type.toBe<any>();
|
||||
expect(ex.endLineNumber).type.toBe<any>();
|
||||
expect(ex.column).type.toBe<any>();
|
||||
expect(ex.endColumn).type.toBe<any>();
|
||||
expect(ex.stack).type.toBe<string | undefined>();
|
||||
});
|
||||
});
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// Handlebars.Utils
|
||||
// ---------------------------------------------------------------------------
|
||||
describe('Handlebars.Utils', () => {
|
||||
test('escapeExpression returns string', () => {
|
||||
expect(Handlebars.Utils.escapeExpression('<script>')).type.toBe<string>();
|
||||
});
|
||||
|
||||
test('createFrame returns any', () => {
|
||||
expect(Handlebars.Utils.createFrame({})).type.toBe<any>();
|
||||
});
|
||||
|
||||
test('blockParams returns any[]', () => {
|
||||
expect(Handlebars.Utils.blockParams([], [])).type.toBe<any[]>();
|
||||
});
|
||||
|
||||
test('isEmpty returns boolean', () => {
|
||||
expect(Handlebars.Utils.isEmpty(null)).type.toBe<boolean>();
|
||||
});
|
||||
|
||||
test('extend returns any', () => {
|
||||
expect(Handlebars.Utils.extend({}, {})).type.toBe<any>();
|
||||
});
|
||||
|
||||
test('toString returns string', () => {
|
||||
expect(Handlebars.Utils.toString(42)).type.toBe<string>();
|
||||
});
|
||||
|
||||
test('isArray returns boolean', () => {
|
||||
expect(Handlebars.Utils.isArray([])).type.toBe<boolean>();
|
||||
});
|
||||
|
||||
test('isFunction returns boolean', () => {
|
||||
expect(Handlebars.Utils.isFunction(() => {})).type.toBe<boolean>();
|
||||
});
|
||||
|
||||
test('isMap returns boolean', () => {
|
||||
expect(Handlebars.Utils.isMap(new Map())).type.toBe<boolean>();
|
||||
});
|
||||
|
||||
test('isSet returns boolean', () => {
|
||||
expect(Handlebars.Utils.isSet(new Set())).type.toBe<boolean>();
|
||||
});
|
||||
});
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// Handlebars namespace constants
|
||||
// ---------------------------------------------------------------------------
|
||||
describe('Handlebars constants', () => {
|
||||
test('VERSION is string', () => {
|
||||
expect(Handlebars.VERSION).type.toBe<string>();
|
||||
});
|
||||
|
||||
test('helpers is a record of HelperDelegate', () => {
|
||||
expect(Handlebars.helpers).type.toBe<{
|
||||
[name: string]: Handlebars.HelperDelegate;
|
||||
}>();
|
||||
});
|
||||
|
||||
test('templates is HandlebarsTemplates', () => {
|
||||
expect(Handlebars.templates).type.toBe<HandlebarsTemplates>();
|
||||
});
|
||||
|
||||
test('partials is a record', () => {
|
||||
expect(Handlebars.partials).type.toBe<{ [name: string]: any }>();
|
||||
});
|
||||
|
||||
test('decorators is a record', () => {
|
||||
expect(Handlebars.decorators).type.toBe<{ [name: string]: Function }>();
|
||||
});
|
||||
|
||||
test('logger is Logger', () => {
|
||||
expect(Handlebars.logger).type.toBe<Logger>();
|
||||
});
|
||||
});
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// Handlebars.create
|
||||
// ---------------------------------------------------------------------------
|
||||
describe('Handlebars.create', () => {
|
||||
test('returns typeof Handlebars', () => {
|
||||
expect(Handlebars.create()).type.toBe<typeof Handlebars>();
|
||||
});
|
||||
});
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// Handlebars.parse / parseWithoutProcessing
|
||||
// ---------------------------------------------------------------------------
|
||||
describe('Handlebars.parse', () => {
|
||||
test('works without options', () => {
|
||||
expect(Handlebars.parse('{{name}}')).type.not.toRaiseError();
|
||||
});
|
||||
|
||||
test('accepts ParseOptions', () => {
|
||||
expect(
|
||||
Handlebars.parse('{{name}}', { srcName: 'test.hbs' })
|
||||
).type.not.toRaiseError();
|
||||
});
|
||||
});
|
||||
|
||||
describe('Handlebars.parseWithoutProcessing', () => {
|
||||
test('returns AST.Program', () => {
|
||||
expect(
|
||||
Handlebars.parseWithoutProcessing('{{name}}')
|
||||
).type.not.toRaiseError();
|
||||
});
|
||||
});
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// RuntimeOptions
|
||||
// ---------------------------------------------------------------------------
|
||||
describe('RuntimeOptions', () => {
|
||||
test('compiled template accepts all RuntimeOptions fields', () => {
|
||||
const template = Handlebars.compile('test');
|
||||
expect(
|
||||
template(
|
||||
{},
|
||||
{
|
||||
partial: true,
|
||||
depths: [{}],
|
||||
helpers: { myHelper: () => '' },
|
||||
partials: { link: '<a>{{name}}</a>' },
|
||||
decorators: { myDec: () => {} },
|
||||
data: { root: {} },
|
||||
blockParams: [[]],
|
||||
allowCallsToHelperMissing: true,
|
||||
allowedProtoMethods: { foo: true },
|
||||
allowedProtoProperties: { bar: false },
|
||||
allowProtoMethodsByDefault: true,
|
||||
allowProtoPropertiesByDefault: false,
|
||||
}
|
||||
)
|
||||
).type.toBe<string>();
|
||||
});
|
||||
|
||||
test('empty options object is valid', () => {
|
||||
const template = Handlebars.compile('test');
|
||||
expect(template({}, {})).type.toBe<string>();
|
||||
});
|
||||
});
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// CompileOptions
|
||||
// ---------------------------------------------------------------------------
|
||||
describe('CompileOptions', () => {
|
||||
test('accepts all fields', () => {
|
||||
expect(
|
||||
Handlebars.compile('test', {
|
||||
data: true,
|
||||
compat: true,
|
||||
knownHelpers: { each: true },
|
||||
knownHelpersOnly: true,
|
||||
noEscape: true,
|
||||
strict: true,
|
||||
assumeObjects: true,
|
||||
preventIndent: true,
|
||||
ignoreStandalone: true,
|
||||
explicitPartialContext: true,
|
||||
})
|
||||
).type.not.toRaiseError();
|
||||
});
|
||||
});
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// HandlebarsTemplateDelegate generic
|
||||
// ---------------------------------------------------------------------------
|
||||
describe('HandlebarsTemplateDelegate generic', () => {
|
||||
test('enforces context type', () => {
|
||||
const template: HandlebarsTemplateDelegate<{ name: string }> =
|
||||
Handlebars.compile<{ name: string }>('{{name}}');
|
||||
expect(template({ name: 'test' })).type.toBe<string>();
|
||||
});
|
||||
});
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// Handlebars.VM.resolvePartial
|
||||
// ---------------------------------------------------------------------------
|
||||
describe('Handlebars.VM.resolvePartial', () => {
|
||||
test('accepts expected parameters and returns HandlebarsTemplateDelegate', () => {
|
||||
const partial = {} as HandlebarsTemplateDelegate | undefined;
|
||||
const context = {};
|
||||
const options = {} as Handlebars.ResolvePartialOptions;
|
||||
expect(Handlebars.VM.resolvePartial(partial, context, options)).type.toBe<
|
||||
HandlebarsTemplateDelegate<any>
|
||||
>();
|
||||
});
|
||||
|
||||
test('ResolvePartialOptions has expected fields', () => {
|
||||
const options = {} as Handlebars.ResolvePartialOptions;
|
||||
expect(options.name).type.toBe<string>();
|
||||
expect(options.helpers).type.toBe<
|
||||
{ [name: string]: Function } | undefined
|
||||
>();
|
||||
expect(options.partials).type.toBe<
|
||||
{ [name: string]: HandlebarsTemplateDelegate } | undefined
|
||||
>();
|
||||
expect(options.decorators).type.toBe<
|
||||
{ [name: string]: Function } | undefined
|
||||
>();
|
||||
expect(options.data).type.toBe<any>();
|
||||
});
|
||||
});
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// ICompiler interface
|
||||
// ---------------------------------------------------------------------------
|
||||
describe('Handlebars.ICompiler', () => {
|
||||
test('has all visitor methods', () => {
|
||||
const compiler = {} as Handlebars.ICompiler;
|
||||
expect(compiler.accept).type.not.toRaiseError();
|
||||
expect(compiler.Program).type.not.toRaiseError();
|
||||
expect(compiler.BlockStatement).type.not.toRaiseError();
|
||||
expect(compiler.PartialStatement).type.not.toRaiseError();
|
||||
expect(compiler.PartialBlockStatement).type.not.toRaiseError();
|
||||
expect(compiler.DecoratorBlock).type.not.toRaiseError();
|
||||
expect(compiler.Decorator).type.not.toRaiseError();
|
||||
expect(compiler.MustacheStatement).type.not.toRaiseError();
|
||||
expect(compiler.ContentStatement).type.not.toRaiseError();
|
||||
expect(compiler.CommentStatement).type.not.toRaiseError();
|
||||
expect(compiler.SubExpression).type.not.toRaiseError();
|
||||
expect(compiler.PathExpression).type.not.toRaiseError();
|
||||
expect(compiler.StringLiteral).type.not.toRaiseError();
|
||||
expect(compiler.NumberLiteral).type.not.toRaiseError();
|
||||
expect(compiler.BooleanLiteral).type.not.toRaiseError();
|
||||
expect(compiler.UndefinedLiteral).type.not.toRaiseError();
|
||||
expect(compiler.NullLiteral).type.not.toRaiseError();
|
||||
expect(compiler.Hash).type.not.toRaiseError();
|
||||
});
|
||||
});
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// Visitor class
|
||||
// ---------------------------------------------------------------------------
|
||||
describe('Handlebars.Visitor', () => {
|
||||
test('implements ICompiler', () => {
|
||||
expect<Handlebars.Visitor>().type.toBeAssignableTo<Handlebars.ICompiler>();
|
||||
});
|
||||
|
||||
test('has acceptKey and acceptArray methods', () => {
|
||||
const visitor = new Handlebars.Visitor();
|
||||
expect(visitor.acceptKey).type.not.toRaiseError();
|
||||
expect(visitor.acceptArray).type.not.toRaiseError();
|
||||
});
|
||||
});
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// Logger interface
|
||||
// ---------------------------------------------------------------------------
|
||||
describe('Logger', () => {
|
||||
test('has log level constants', () => {
|
||||
const logger = {} as Logger;
|
||||
expect(logger.DEBUG).type.toBe<number>();
|
||||
expect(logger.INFO).type.toBe<number>();
|
||||
expect(logger.WARN).type.toBe<number>();
|
||||
expect(logger.ERROR).type.toBe<number>();
|
||||
expect(logger.level).type.toBe<number>();
|
||||
});
|
||||
|
||||
test('has methodMap and log', () => {
|
||||
const logger = {} as Logger;
|
||||
expect(logger.methodMap).type.toBe<{ [level: number]: string }>();
|
||||
expect(logger.log(0, 'msg')).type.toBe<void>();
|
||||
});
|
||||
});
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// Top-level exported types
|
||||
// ---------------------------------------------------------------------------
|
||||
describe('top-level exports', () => {
|
||||
test('HandlebarsTemplatable has template field', () => {
|
||||
const view = {} as HandlebarsTemplatable;
|
||||
expect(view.template).type.toBe<HandlebarsTemplateDelegate>();
|
||||
});
|
||||
|
||||
test('CompilerInfo is a tuple', () => {
|
||||
expect<CompilerInfo>().type.toBe<[number, string]>();
|
||||
});
|
||||
|
||||
test('BuiltinHelperName is a string union', () => {
|
||||
expect<BuiltinHelperName>().type.toBe<
|
||||
| 'helperMissing'
|
||||
| 'blockHelperMissing'
|
||||
| 'each'
|
||||
| 'if'
|
||||
| 'unless'
|
||||
| 'with'
|
||||
| 'log'
|
||||
| 'lookup'
|
||||
>();
|
||||
});
|
||||
|
||||
test('CustomHelperName is string', () => {
|
||||
expect<CustomHelperName>().type.toBe<string>();
|
||||
});
|
||||
|
||||
test('KnownHelpers uses BuiltinHelperName and CustomHelperName', () => {
|
||||
const helpers: KnownHelpers = { each: true, myCustom: true };
|
||||
expect(helpers).type.not.toRaiseError();
|
||||
});
|
||||
});
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// hbs namespace
|
||||
// ---------------------------------------------------------------------------
|
||||
describe('hbs namespace', () => {
|
||||
test('hbs.SafeString aliases Handlebars.SafeString', () => {
|
||||
expect<hbs.SafeString>().type.toBe<Handlebars.SafeString>();
|
||||
});
|
||||
|
||||
test('hbs.Utils aliases typeof Handlebars.Utils', () => {
|
||||
expect<hbs.Utils>().type.toBe<typeof Handlebars.Utils>();
|
||||
});
|
||||
|
||||
test('AST types are accessible', () => {
|
||||
const node = {} as hbs.AST.MustacheStatement;
|
||||
expect(node.type).type.not.toRaiseError();
|
||||
});
|
||||
});
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// Handlebars.AST
|
||||
// ---------------------------------------------------------------------------
|
||||
describe('Handlebars.AST', () => {
|
||||
test('helpers is accessible', () => {
|
||||
expect(Handlebars.AST.helpers).type.not.toRaiseError();
|
||||
});
|
||||
});
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// AST discriminated union
|
||||
// ---------------------------------------------------------------------------
|
||||
describe('AST discriminated union', () => {
|
||||
test('type field narrows AST node types', () => {
|
||||
const node = {} as
|
||||
| hbs.AST.MustacheStatement
|
||||
| hbs.AST.BlockStatement
|
||||
| hbs.AST.PartialStatement
|
||||
| hbs.AST.PartialBlockStatement
|
||||
| hbs.AST.ContentStatement
|
||||
| hbs.AST.CommentStatement
|
||||
| hbs.AST.SubExpression
|
||||
| hbs.AST.PathExpression
|
||||
| hbs.AST.StringLiteral
|
||||
| hbs.AST.BooleanLiteral
|
||||
| hbs.AST.NumberLiteral
|
||||
| hbs.AST.UndefinedLiteral
|
||||
| hbs.AST.NullLiteral
|
||||
| hbs.AST.Hash
|
||||
| hbs.AST.HashPair;
|
||||
|
||||
if (node.type === 'MustacheStatement') {
|
||||
expect(node).type.toBe<hbs.AST.MustacheStatement>();
|
||||
}
|
||||
if (node.type === 'BlockStatement') {
|
||||
expect(node).type.toBe<hbs.AST.BlockStatement>();
|
||||
}
|
||||
if (node.type === 'PartialStatement') {
|
||||
expect(node).type.toBe<hbs.AST.PartialStatement>();
|
||||
}
|
||||
if (node.type === 'PartialBlockStatement') {
|
||||
expect(node).type.toBe<hbs.AST.PartialBlockStatement>();
|
||||
}
|
||||
if (node.type === 'ContentStatement') {
|
||||
expect(node).type.toBe<hbs.AST.ContentStatement>();
|
||||
}
|
||||
if (node.type === 'CommentStatement') {
|
||||
expect(node).type.toBe<hbs.AST.CommentStatement>();
|
||||
}
|
||||
if (node.type === 'SubExpression') {
|
||||
expect(node).type.toBe<hbs.AST.SubExpression>();
|
||||
}
|
||||
if (node.type === 'PathExpression') {
|
||||
expect(node).type.toBe<hbs.AST.PathExpression>();
|
||||
}
|
||||
if (node.type === 'StringLiteral') {
|
||||
expect(node).type.toBe<hbs.AST.StringLiteral>();
|
||||
}
|
||||
if (node.type === 'BooleanLiteral') {
|
||||
expect(node).type.toBe<hbs.AST.BooleanLiteral>();
|
||||
}
|
||||
if (node.type === 'NumberLiteral') {
|
||||
expect(node).type.toBe<hbs.AST.NumberLiteral>();
|
||||
}
|
||||
if (node.type === 'UndefinedLiteral') {
|
||||
expect(node).type.toBe<hbs.AST.UndefinedLiteral>();
|
||||
}
|
||||
if (node.type === 'NullLiteral') {
|
||||
expect(node).type.toBe<hbs.AST.NullLiteral>();
|
||||
}
|
||||
if (node.type === 'Hash') {
|
||||
expect(node).type.toBe<hbs.AST.Hash>();
|
||||
}
|
||||
if (node.type === 'HashPair') {
|
||||
expect(node).type.toBe<hbs.AST.HashPair>();
|
||||
}
|
||||
});
|
||||
});
|
||||
-298
@@ -1,298 +0,0 @@
|
||||
/* These test cases were imported from https://github.com/DefinitelyTyped/DefinitelyTyped
|
||||
* and includes previous contributions from the DefinitelyTyped community.
|
||||
* For full history prior to their migration to handlebars.js, please see:
|
||||
* https://github.com/DefinitelyTyped/DefinitelyTyped/commits/1ce60bdc07f10e0b076778c6c953271c072bc894/types/handlebars/handlebars-tests.ts
|
||||
*/
|
||||
|
||||
import Handlebars from 'handlebars';
|
||||
import { HandlebarsTemplateDelegate, hbs } from 'handlebars';
|
||||
|
||||
const context = {
|
||||
author: { firstName: 'Alan', lastName: 'Johnson' },
|
||||
body: 'I Love Handlebars',
|
||||
comments: [
|
||||
{
|
||||
author: { firstName: 'Yehuda', lastName: 'Katz' },
|
||||
body: 'Me too!',
|
||||
},
|
||||
],
|
||||
};
|
||||
Handlebars.registerHelper('fullName', (person: typeof context.author) => {
|
||||
return person.firstName + ' ' + person.lastName;
|
||||
});
|
||||
|
||||
Handlebars.registerHelper('agree_button', function (this: any) {
|
||||
return new Handlebars.SafeString(
|
||||
'<button>I agree. I ' + this.emotion + ' ' + this.name + '</button>'
|
||||
);
|
||||
});
|
||||
|
||||
const source1 =
|
||||
'<p>Hello, my name is {{name}}. I am from {{hometown}}. I have ' +
|
||||
'{{kids.length}} kids:</p>' +
|
||||
'<ul>{{#kids}}<li>{{name}} is {{age}}</li>{{/kids}}</ul>';
|
||||
const template1 = Handlebars.compile(source1);
|
||||
template1({
|
||||
name: 'Alan',
|
||||
hometown: 'Somewhere, TX',
|
||||
kids: [
|
||||
{ name: 'Jimmy', age: 12 },
|
||||
{ name: 'Sally', age: 4 },
|
||||
],
|
||||
});
|
||||
|
||||
Handlebars.registerHelper('link_to', (context: typeof post) => {
|
||||
return '<a href="' + context.url + '">' + context.body + '</a>';
|
||||
});
|
||||
const post = { url: '/hello-world', body: 'Hello World!' };
|
||||
const context2 = { posts: [post] };
|
||||
const source2 = '<ul>{{#posts}}<li>{{{link_to this}}}</li>{{/posts}}</ul>';
|
||||
const template2: HandlebarsTemplateDelegate<{
|
||||
posts: { url: string; body: string }[];
|
||||
}> = Handlebars.compile(source2);
|
||||
template2(context2);
|
||||
|
||||
Handlebars.registerHelper('link_to', (title: string, context: typeof post) => {
|
||||
return '<a href="/posts' + context.url + '">' + title + '!</a>';
|
||||
});
|
||||
const context3 = { posts: [{ url: '/hello-world', body: 'Hello World!' }] };
|
||||
const source3 =
|
||||
'<ul>{{#posts}}<li>{{{link_to "Post" this}}}</li>{{/posts}}</ul>';
|
||||
const template3 = Handlebars.compile<typeof context3>(source3);
|
||||
template3(context3);
|
||||
|
||||
const source4 =
|
||||
'<ul>{{#people}}<li>{{#link}}{{name}}{{/link}}</li>{{/people}}</ul>';
|
||||
Handlebars.registerHelper('link', function (this: any, context: any) {
|
||||
return '<a href="/people/' + this.id + '">' + context.fn(this) + '</a>';
|
||||
});
|
||||
const template4 = Handlebars.compile<{
|
||||
people: { name: string; id: number }[];
|
||||
}>(source4);
|
||||
const data2 = {
|
||||
people: [
|
||||
{ name: 'Alan', id: 1 },
|
||||
{ name: 'Yehuda', id: 2 },
|
||||
],
|
||||
};
|
||||
template4(data2);
|
||||
|
||||
const source5 = '<ul>{{#people}}<li>{{> link}}</li>{{/people}}</ul>';
|
||||
Handlebars.registerPartial('link', '<a href="/people/{{id}}">{{name}}</a>');
|
||||
const template5 = Handlebars.compile(source5);
|
||||
const data3 = {
|
||||
people: [
|
||||
{ name: 'Alan', id: 1 },
|
||||
{ name: 'Yehuda', id: 2 },
|
||||
],
|
||||
};
|
||||
template5(data3);
|
||||
|
||||
const source6 = '{{#list nav}}<a href="{{url}}">{{title}}</a>{{/list}}';
|
||||
const template6 = Handlebars.compile(source6);
|
||||
Handlebars.registerHelper(
|
||||
'list',
|
||||
(context, options: Handlebars.HelperOptions) => {
|
||||
let ret = '<ul>';
|
||||
for (let i = 0, j = context.length; i < j; i++) {
|
||||
ret = ret + '<li>' + options.fn(context[i]) + '</li>';
|
||||
}
|
||||
return ret + '</ul>';
|
||||
}
|
||||
);
|
||||
template6([{ url: '', title: '' }]);
|
||||
|
||||
const escapedExpression = Handlebars.Utils.escapeExpression(
|
||||
"<script>alert('xss');</script>"
|
||||
);
|
||||
|
||||
Handlebars.helpers !== undefined;
|
||||
|
||||
const parsedTmpl = Handlebars.parse('<p>Hello, my name is {{name}}.</p>', {
|
||||
srcName: '/foo/bar/baz.hbs',
|
||||
ignoreStandalone: true,
|
||||
});
|
||||
|
||||
const parsedTmplWithoutOptions = Handlebars.parse(
|
||||
'<p>Hello, my name is {{name}}.</p>'
|
||||
);
|
||||
|
||||
// Custom partial resolution.
|
||||
const originalResolvePartial = Handlebars.VM.resolvePartial;
|
||||
Handlebars.VM.resolvePartial = <T>(
|
||||
partial: HandlebarsTemplateDelegate<T> | undefined,
|
||||
context: any,
|
||||
options: Handlebars.ResolvePartialOptions
|
||||
): HandlebarsTemplateDelegate<T> => {
|
||||
const name = options.name.replace(/my/, 'your');
|
||||
// transform name.
|
||||
options.name = name;
|
||||
return originalResolvePartial(partial, context, options);
|
||||
};
|
||||
|
||||
// #1544, allow custom helpers in knownHelpers
|
||||
Handlebars.compile('test', {
|
||||
knownHelpers: {
|
||||
each: true,
|
||||
customHelper: true,
|
||||
},
|
||||
});
|
||||
|
||||
Handlebars.compile('test')({}, { allowCallsToHelperMissing: true });
|
||||
|
||||
Handlebars.compile('test')({}, {});
|
||||
|
||||
const allthings = {} as
|
||||
| hbs.AST.MustacheStatement
|
||||
| hbs.AST.BlockStatement
|
||||
| hbs.AST.PartialStatement
|
||||
| hbs.AST.PartialBlockStatement
|
||||
| hbs.AST.ContentStatement
|
||||
| hbs.AST.CommentStatement
|
||||
| hbs.AST.SubExpression
|
||||
| hbs.AST.PathExpression
|
||||
| hbs.AST.StringLiteral
|
||||
| hbs.AST.BooleanLiteral
|
||||
| hbs.AST.NumberLiteral
|
||||
| hbs.AST.UndefinedLiteral
|
||||
| hbs.AST.NullLiteral
|
||||
| hbs.AST.Hash
|
||||
| hbs.AST.HashPair;
|
||||
|
||||
switch (allthings.type) {
|
||||
case 'MustacheStatement':
|
||||
let mustacheStatement: hbs.AST.MustacheStatement;
|
||||
mustacheStatement = allthings;
|
||||
break;
|
||||
case 'BlockStatement':
|
||||
let blockStatement: hbs.AST.BlockStatement;
|
||||
blockStatement = allthings;
|
||||
break;
|
||||
case 'PartialStatement':
|
||||
let partialStatement: hbs.AST.PartialStatement;
|
||||
partialStatement = allthings;
|
||||
break;
|
||||
case 'PartialBlockStatement':
|
||||
let partialBlockStatement: hbs.AST.PartialBlockStatement;
|
||||
partialBlockStatement = allthings;
|
||||
break;
|
||||
case 'ContentStatement':
|
||||
let ContentStatement: hbs.AST.ContentStatement;
|
||||
ContentStatement = allthings;
|
||||
break;
|
||||
case 'CommentStatement':
|
||||
let CommentStatement: hbs.AST.CommentStatement;
|
||||
CommentStatement = allthings;
|
||||
break;
|
||||
case 'SubExpression':
|
||||
let SubExpression: hbs.AST.SubExpression;
|
||||
SubExpression = allthings;
|
||||
break;
|
||||
case 'PathExpression':
|
||||
let PathExpression: hbs.AST.PathExpression;
|
||||
PathExpression = allthings;
|
||||
break;
|
||||
case 'StringLiteral':
|
||||
let StringLiteral: hbs.AST.StringLiteral;
|
||||
StringLiteral = allthings;
|
||||
break;
|
||||
case 'BooleanLiteral':
|
||||
let BooleanLiteral: hbs.AST.BooleanLiteral;
|
||||
BooleanLiteral = allthings;
|
||||
break;
|
||||
case 'NumberLiteral':
|
||||
let NumberLiteral: hbs.AST.NumberLiteral;
|
||||
NumberLiteral = allthings;
|
||||
break;
|
||||
case 'UndefinedLiteral':
|
||||
let UndefinedLiteral: hbs.AST.UndefinedLiteral;
|
||||
UndefinedLiteral = allthings;
|
||||
break;
|
||||
case 'NullLiteral':
|
||||
let NullLiteral: hbs.AST.NullLiteral;
|
||||
NullLiteral = allthings;
|
||||
break;
|
||||
case 'Hash':
|
||||
let Hash: hbs.AST.Hash;
|
||||
Hash = allthings;
|
||||
break;
|
||||
case 'HashPair':
|
||||
let HashPair: hbs.AST.HashPair;
|
||||
HashPair = allthings;
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
function testParseWithoutProcessing() {
|
||||
const parsedTemplate: hbs.AST.Program = Handlebars.parseWithoutProcessing(
|
||||
'<p>Hello, my name is {{name}}.</p>',
|
||||
{
|
||||
srcName: '/foo/bar/baz.hbs',
|
||||
}
|
||||
);
|
||||
|
||||
const parsedTemplateWithoutOptions: hbs.AST.Program =
|
||||
Handlebars.parseWithoutProcessing('<p>Hello, my name is {{name}}.</p>');
|
||||
}
|
||||
|
||||
function testExceptionTypings() {
|
||||
// Test exception constructor with a single argument - message.
|
||||
let exception: Handlebars.Exception = new Handlebars.Exception('message');
|
||||
|
||||
// Fields
|
||||
let message: string = exception.message;
|
||||
let lineNumber: number = exception.lineNumber;
|
||||
let column: number = exception.column;
|
||||
let endLineNumber: number = exception.endLineNumber;
|
||||
let endColumn: number = exception.endColumn;
|
||||
let description = exception.description;
|
||||
let name: string = exception.name;
|
||||
let fileName: string = exception.fileName;
|
||||
let stack: string | undefined = exception.stack;
|
||||
}
|
||||
|
||||
function testExceptionWithNodeTypings() {
|
||||
// Test exception constructor with both arguments.
|
||||
const exception: Handlebars.Exception = new Handlebars.Exception('message', {
|
||||
type: 'MustacheStatement',
|
||||
loc: {
|
||||
source: 'source',
|
||||
start: { line: 1, column: 5 },
|
||||
end: { line: 10, column: 2 },
|
||||
},
|
||||
});
|
||||
|
||||
// Fields
|
||||
let message: string = exception.message;
|
||||
let lineNumber: number = exception.lineNumber;
|
||||
let column: number = exception.column;
|
||||
let endLineNumber: number = exception.endLineNumber;
|
||||
let endColumn: number = exception.endColumn;
|
||||
let description = exception.description;
|
||||
let name: string = exception.name;
|
||||
let fileName: string = exception.fileName;
|
||||
let stack: string | undefined = exception.stack;
|
||||
}
|
||||
|
||||
function testProtoAccessControlControlOptions() {
|
||||
Handlebars.compile('test')(
|
||||
{},
|
||||
{
|
||||
allowedProtoMethods: { allowedMethod: true, forbiddenMethod: false },
|
||||
allowedProtoProperties: {
|
||||
allowedProperty: true,
|
||||
forbiddenProperty: false,
|
||||
},
|
||||
allowProtoMethodsByDefault: true,
|
||||
allowProtoPropertiesByDefault: false,
|
||||
partials: {
|
||||
link: '<a href="/people/{{id}}">{{name}}</a>',
|
||||
},
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
function testHandlebarsVersion() {
|
||||
let version: string = Handlebars.VERSION;
|
||||
}
|
||||
Reference in New Issue
Block a user