Skip to content

Commit

Permalink
feat(tests): more test tools + adds test related to debuggin issues
Browse files Browse the repository at this point in the history
  • Loading branch information
huafu committed Aug 12, 2018
1 parent f170285 commit 8dcafca
Show file tree
Hide file tree
Showing 10 changed files with 375 additions and 75 deletions.
27 changes: 27 additions & 0 deletions e2e/__helpers__/__hooks-source__.js.hbs
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
// this is a template!

const fs = require('fs');
const path = require('path');
const root = __dirname;
const writeProcessIoTo = {{writeProcessIoTo}};

exports.afterProcess = function (args, result) {
// const source = args[0];
const filePath = args[1];
const relPath = path.relative(root, filePath);
if (writeProcessIoTo && filePath.startsWith(`${root}${path.sep}`)) {
const dest = `${path.join(writeProcessIoTo, relPath)}.json`;
const segments = relPath.split(path.sep);
segments.pop();
const madeSegments = [];
segments.forEach(segment => {
madeSegments.push(segment);
const p = join(writeProcessIoTo, madeSegments.join(sep));
if (!fs.existsSync(p)) fs.mkdirSync(p);
});
fs.writeFileSync(dest, JSON.stringify({
in: args,
out: typeof result === 'object' ? result.code : result,
}), 'utf8');
}
}
184 changes: 159 additions & 25 deletions e2e/__helpers__/test-case.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
// tslint:disable-file:no-shadowed-variable
import { sync as spawnSync } from 'cross-spawn';
import { join } from 'path';
import { join, relative, sep } from 'path';
import * as Paths from '../../scripts/paths';
import * as fs from 'fs-extra';

Expand Down Expand Up @@ -133,6 +133,8 @@ export interface RunTestOptions {
template?: string;
env?: {};
args?: string[];
inject?: (() => any) | string;
writeIo?: boolean;
}

export interface TestRunResult {
Expand All @@ -141,6 +143,12 @@ export interface TestRunResult {
stdout: string;
stderr: string;
output: string;
ioDataFor(relPath: string): TestFileIoData;
}
interface TestFileIoData {
in: [string, jest.Path, jest.ProjectConfig, jest.TransformOptions?];
// out: string | jest.TransformedSource;
out: string;
}

// tslint:disable-next-line:interface-over-type-literal
Expand Down Expand Up @@ -194,27 +202,73 @@ export function templateNameForPath(path: string): string {
return 'default';
}

export function run(
name: string,
{ args = [], env = {}, template }: RunTestOptions = {},
): TestRunResult {
const dir = prepareTest(
export function run(name: string, options: RunTestOptions = {}): TestRunResult {
const { args = [], env = {}, template, inject, writeIo } = options;
const { workdir: dir, sourceDir, hooksFile, ioDir } = prepareTest(
name,
template || templateNameForPath(join(Paths.e2eSourceDir, name)),
options,
);
const pkg = require(join(dir, 'package.json'));

const prefix =
pkg.scripts && pkg.scripts.test
? ['npm', '-s', 'run', 'test']
: [join(dir, 'node_modules', '.bin', 'jest')];
args = [...prefix, ...args];
const cmd = args.shift();
let cmdArgs: string[] = [];
if (inject) {
cmdArgs.push('--testPathPattern="/__eval\\\\.ts$"');
} // '--testRegex=""'
if (process.argv.find(v => ['--updateSnapshot', '-u'].includes(v))) {
cmdArgs.push('-u');
}
cmdArgs.push(...args);
if (!inject && pkg.scripts && pkg.scripts.test) {
if (cmdArgs.length) {
cmdArgs.unshift('--');
}
cmdArgs = ['npm', '-s', 'run', 'test', ...cmdArgs];
} else {
cmdArgs.unshift(join(dir, 'node_modules', '.bin', 'jest'));
}

const cmd = cmdArgs.shift();

// Add both process.env which is the standard and custom env variables
const mergedEnv: any = {
...process.env,
...env,
};
if (inject) {
const injected =
typeof inject === 'function'
? `(${inject.toString()}).apply(this);`
: inject;
mergedEnv.__TS_JEST_EVAL = injected;
}
if (writeIo) {
mergedEnv.__TS_JEST_HOOKS = hooksFile;
}

const result = spawnSync(cmd, args, {
const result = spawnSync(cmd, cmdArgs, {
cwd: dir,
// Add both process.env which is the standard and custom env variables
env: { ...process.env, ...env },
env: mergedEnv,
});

// we need to copy each snapshot which does NOT exists in the source dir
fs.readdirSync(dir).forEach(item => {
if (
item === 'node_modules' ||
!fs.statSync(join(dir, item)).isDirectory()
) {
return;
}
const srcDir = join(sourceDir, item);
const wrkDir = join(dir, item);
fs.copySync(wrkDir, srcDir, {
overwrite: false,
filter: from => {
return relative(sourceDir, from)
.split(sep)
.includes('__snapshots__');
},
});
});

// Call to string on byte arrays and strip ansi color codes for more accurate string comparison.
Expand All @@ -224,13 +278,19 @@ export function run(
? stripAnsiColors(result.output.join('\n\n'))
: '';

return {
const res = {
[TestRunResultFlag]: true,
status: result.status,
stderr,
stdout,
output,
};
if (writeIo) {
Object.defineProperty(res, 'ioDataFor', {
value: (relPath: string) => require(`${ioDir}/${relPath}.json`),
});
}
return res as any;
}

// from https://stackoverflow.com/questions/25245716/remove-all-ansi-colors-styles-from-strings
Expand All @@ -241,18 +301,29 @@ function stripAnsiColors(stringToStrip: string): string {
);
}

function prepareTest(name: string, template: string): string {
interface PreparedTest {
workdir: string;
templateDir: string;
sourceDir: string;
ioDir: string;
hooksFile: string;
}
function prepareTest(
name: string,
template: string,
options: RunTestOptions = {},
): PreparedTest {
const sourceDir = join(Paths.e2eSourceDir, name);
// working directory is in the temp directory, different for each template name
const caseDir = join(Paths.e2eWorkDir, template, name);
const caseWorkdir = join(Paths.e2eWorkDir, template, name);
const templateDir = join(Paths.e2eWorkTemplatesDir, template);

// recreate the directory
fs.removeSync(caseDir);
fs.mkdirpSync(caseDir);
fs.removeSync(caseWorkdir);
fs.mkdirpSync(caseWorkdir);

const tmplModulesDir = join(templateDir, 'node_modules');
const caseModulesDir = join(caseDir, 'node_modules');
const caseModulesDir = join(caseWorkdir, 'node_modules');

// link the node_modules dir if the template has one
if (fs.existsSync(tmplModulesDir)) {
Expand All @@ -264,19 +335,82 @@ function prepareTest(name: string, template: string): string {
if (TEMPLATE_EXCLUDED_ITEMS.includes(item)) {
return;
}
fs.copySync(join(templateDir, item), join(caseDir, item));
fs.copySync(join(templateDir, item), join(caseWorkdir, item));
});

// copy source and test files
fs.copySync(sourceDir, caseDir);
const snapshotDirs: Record<string, 0> = Object.create(null);
fs.copySync(sourceDir, caseWorkdir, {
filter: src => {
const relPath = relative(sourceDir, src);
const segments = relPath.split(sep);
if (segments.includes('__snapshots__')) {
// link snapshots
while (segments[segments.length - 1] !== '__snapshots__') {
segments.pop();
}
snapshotDirs[segments.join(sep)] = 0;
return false;
} else {
return true;
}
},
});
// create symbolic links for the existing snapshots
Object.keys(snapshotDirs).forEach(dir => {
fs.ensureSymlinkSync(join(sourceDir, dir), join(caseWorkdir, dir));
});

// create the special files
fs.outputFileSync(join(caseWorkdir, '__eval.ts'), EVAL_SOURCE, 'utf8');
let ioDir!: string;
if (options.writeIo) {
ioDir = join(caseWorkdir, '__io');
fs.mkdirpSync(ioDir);
}
const hooksFile = join(caseWorkdir, '__hooks.js');
fs.outputFileSync(
hooksFile,
hooksSourceWith({
writeProcessIoTo: ioDir || false,
}),
'utf8',
);

// create a package.json if it does not exists, and/or enforce the package name
const pkgFile = join(caseDir, 'package.json');
const pkgFile = join(caseWorkdir, 'package.json');
const pkg: any = fs.existsSync(pkgFile) ? fs.readJsonSync(pkgFile) : {};
pkg.name = name;
pkg.private = true;
pkg.version = `0.0.0-mock0`;
fs.outputJsonSync(pkgFile, pkg, { spaces: 2 });

return caseDir;
return { workdir: caseWorkdir, templateDir, sourceDir, hooksFile, ioDir };
}

const EVAL_SOURCE = `
describe.skip('__eval', () => {
test.skip('__test', () => {
expect(true).toBe(true);
});
it.skip('__test', () => {
expect(true).toBe(true);
});
});
eval(process.__TS_JEST_EVAL);
`;

// tslint:disable-next-line:variable-name
let __hooksSource: string;
function hooksSourceWith(vars: Record<string, any>): string {
if (!__hooksSource) {
__hooksSource = fs.readFileSync(
join(__dirname, '__hooks-source__.js.hbs'),
'utf8',
);
}
return __hooksSource.replace(/\{\{([^\}]+)\}\}/g, (_, key) =>
JSON.stringify(vars[key]),
);
}
Loading

0 comments on commit 8dcafca

Please sign in to comment.