diff --git a/packages/svelte2tsx/test/helpers.ts b/packages/svelte2tsx/test/helpers.ts index a39b37dde..15f0c0bf9 100644 --- a/packages/svelte2tsx/test/helpers.ts +++ b/packages/svelte2tsx/test/helpers.ts @@ -1,90 +1,171 @@ import fs from 'fs'; import assert from 'assert'; +import { TestFunction } from 'mocha'; +import svelte2tsx from './build/index'; +import { htmlx2jsx } from './build/htmlxtojsx'; -export function benchmark(fn) { +export function benchmark(fn: () => void) { return -Date.now() + (fn(), Date.now()); } -export function readFileSync(path) { +export function readFileSync(path: string) { return fs.existsSync(path) ? fs.readFileSync(path, 'utf-8').replace(/\r\n/g, '\n').replace(/\s+$/, '') : null; } -function check_dir(path, { allowed = [], required = allowed }) { - const unchecked = new Set(required); - const unknown = []; - loop: for (const fileName of fs.readdirSync(path)) { - for (const name of unchecked) { - if ('*' === name[0] ? fileName.endsWith(name.slice(1)) : name === fileName) { - unchecked.delete(name); - continue loop; +class Sample { + readonly folder: string[]; + readonly directory: string; + private skipped = false; + + constructor(dir: string, readonly name: string) { + this.directory = `${dir}/samples/${name}`; + this.folder = fs.readdirSync(this.directory); + } + + check_dir({ required = [], allowed = required }: { allowed?: string[]; required?: string[] }) { + const unchecked = new Set(required); + const unknown = []; + + loop: for (const fileName of this.folder) { + for (const name of unchecked) { + if ('*' === name[0] ? fileName.endsWith(name.slice(1)) : name === fileName) { + unchecked.delete(name); + continue loop; + } } + for (const name of allowed) { + if ('*' === name[0] ? fileName.endsWith(name.slice(1)) : name === fileName) { + continue loop; + } + } + unknown.push(fileName); } - for (const name of allowed) { - if ('*' === name[0] ? fileName.endsWith(name.slice(1)) : name === fileName) { - continue loop; + + if (unknown.length) { + const errors = + unknown.map((name) => `Unexpected file "${name}"`).join('\n') + + `\nat ${this.directory}`; + if (process.env.CI) { + throw new Error('\n' + errors); + } else { + after(() => { + console.log(errors); + }); } } - unknown.push(fileName); + + if (unchecked.size) { + throw new Error( + `Expected file${unchecked.size === 1 ? '' : 's'} ${[...unchecked] + .map((str) => `"${str}"`) + .join(', ')} in "${this.directory}"` + ); + } } - if (unknown.length !== 0) { - after(() => { - for (const name of unknown) { - const msg = `Unexpected file ${path.split('/').slice(-1)}/${name}`; - if (process.env.CI) { - throw new Error(msg); - } else { - console.info(msg); - } + + it(fn: () => void) { + let _it = it; + + if (this.name.startsWith('.')) { + _it = it.skip as TestFunction; + } else if (this.name.endsWith('.solo')) { + _it = it.only as TestFunction; + } + + const sample = this; + + _it(this.name, function () { + try { + fn(); + if (sample.skipped) this.skip(); + } catch (err) { + if (sample.skipped) this.skip(); + throw err; } }); } - if (unchecked.size !== 0) { - throw new Error( - `Expected file(s) ${[...unchecked].map((str) => `"${str}"`).join(', ')} in ${path}` - ); + + has(file: string) { + return this.folder.includes(file); + } + + get(file: string) { + return readFileSync(`${this.directory}/${file}`); + } + + generate(fileName: string, content: string) { + const path = `${this.directory}/${fileName}`; + if (process.env.CI) { + throw new Error(`Forgot to generate expected sample result at "${path}"`); + } + after(() => { + fs.writeFileSync(path, content); + console.info(`(generated) ${this.name}/${fileName}`); + }); + this.skipped = true; + } + + eval(fileName: string, ...args: any[]) { + const fn = require(`${this.directory}/${fileName}`); + fn(...args); } } -export function test_samples(dir, transform, tsx) { - for (const testName of fs.readdirSync(`${dir}/samples`)) { - const path = `${dir}/samples/${testName}`; - const expected_path = `${path}/expected.${tsx}`; - const has_expected = fs.existsSync(expected_path); - const solo = testName.endsWith('.solo'); - const skip = testName.startsWith('.'); - check_dir(path, { +type TransformSampleFn = ( + input: string, + config: { + fileName: string; + sampleName: string; + emitOnTemplateError: boolean; + } +) => ReturnType; + +export function test_samples(dir: string, transform: TransformSampleFn, tsx: 'jsx' | 'tsx') { + for (const sample of each_sample(dir)) { + const svelteFile = sample.folder.find((f) => f.endsWith('.svelte')); + + sample.check_dir({ required: ['*.svelte'], allowed: ['expected.js', `expected.${tsx}`, 'test.js'] }); - (skip ? it.skip : solo ? it.only : it)(testName, function () { - const testJsPath = `${path}/test.js`; - if (fs.existsSync(testJsPath)) { - const test = require(testJsPath); - test(); + + const shouldGenerateExpected = !sample.has(`expected.${tsx}`); + + sample.it(function () { + if (sample.has('test.js')) { + sample.eval('test.js'); return; } + const input = sample.get(svelteFile); + const config = { + fileName: svelteFile, + sampleName: sample.name, + emitOnTemplateError: false + }; - const { filename, content } = get_input_content(path); - const output = transform(content, testName, filename); - if (!has_expected) { - after(() => { - fs.writeFileSync(expected_path, output.code); - console.info(`Generated ${testName}/expected.${tsx}`); - }); - this.skip(); + const output = transform(input, config); + + if (shouldGenerateExpected) { + sample.generate(`expected.${tsx}`, output.code); } else { - assert.strictEqual(output.code, readFileSync(expected_path)); + assert.strictEqual(output.code, sample.get(`expected.${tsx}`)); } - if (fs.existsSync(`${path}/expected.js`)) { - const run = require(`${path}/expected.js`); - run(output); + + if (sample.has('expected.js')) { + sample.eval('expected.js', output); } }); } } +export function* each_sample(dir: string) { + for (const name of fs.readdirSync(`${dir}/samples`)) { + yield new Sample(dir, name); + } +} + /** * * @param {string} dirPath diff --git a/packages/svelte2tsx/test/svelte2tsx/index.ts b/packages/svelte2tsx/test/svelte2tsx/index.ts index fcbc59788..b0677dd41 100644 --- a/packages/svelte2tsx/test/svelte2tsx/index.ts +++ b/packages/svelte2tsx/test/svelte2tsx/index.ts @@ -4,11 +4,12 @@ import { test_samples } from '../helpers'; describe('svelte2tsx', () => { test_samples( __dirname, - (input, testName, filename) => { + (input, { sampleName, fileName, emitOnTemplateError }) => { return svelte2tsx(input, { - strictMode: testName.includes('strictMode'), - isTsFile: testName.startsWith('ts-'), - filename + strictMode: sampleName.includes('strictMode'), + isTsFile: sampleName.startsWith('ts-'), + filename: fileName, + emitOnTemplateError }); }, 'tsx' diff --git a/packages/svelte2tsx/test/tsconfig.json b/packages/svelte2tsx/test/tsconfig.json index 5358bf42a..77caa0e1d 100644 --- a/packages/svelte2tsx/test/tsconfig.json +++ b/packages/svelte2tsx/test/tsconfig.json @@ -1,5 +1,6 @@ { "compilerOptions": { + "types": ["@types/node", "@types/mocha"], "target": "esnext", "module": "commonjs", "moduleResolution": "node",