Skip to content

Commit

Permalink
(chore) Extract common sample logic into a class (#868)
Browse files Browse the repository at this point in the history
svelte2tsx
  • Loading branch information
pushkine authored Mar 10, 2021
1 parent 60e976e commit 8f10ccb
Show file tree
Hide file tree
Showing 3 changed files with 139 additions and 56 deletions.
185 changes: 133 additions & 52 deletions packages/svelte2tsx/test/helpers.ts
Original file line number Diff line number Diff line change
@@ -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<typeof htmlx2jsx | typeof svelte2tsx>;

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
Expand Down
9 changes: 5 additions & 4 deletions packages/svelte2tsx/test/svelte2tsx/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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'
Expand Down
1 change: 1 addition & 0 deletions packages/svelte2tsx/test/tsconfig.json
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
{
"compilerOptions": {
"types": ["@types/node", "@types/mocha"],
"target": "esnext",
"module": "commonjs",
"moduleResolution": "node",
Expand Down

0 comments on commit 8f10ccb

Please sign in to comment.