Skip to content

Commit

Permalink
test: implement more real-world testing ability
Browse files Browse the repository at this point in the history
  • Loading branch information
huafu committed Aug 3, 2018
1 parent f2565d3 commit ca697f2
Show file tree
Hide file tree
Showing 21 changed files with 303 additions and 13 deletions.
7 changes: 6 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -45,5 +45,10 @@ jspm_packages
.idea


# Long paths
# tests specific
tests/simple-long-path/long-src-path
# is linked to the temp dir of the os
e2e/__e2e_workdir_link__

# binaries
*.tgz
5 changes: 5 additions & 0 deletions e2e/__cases__/package-template.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
{
"name": "ts-jest-e2e-tmpl",
"version": "0.0.0-test.0",
"private": true
}
3 changes: 3 additions & 0 deletions e2e/__cases__/simple/Hello.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
export class Hello {
constructor(readonly msg: string) {}
}
10 changes: 10 additions & 0 deletions e2e/__cases__/simple/__tests__/Hello.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
declare var jest, describe, it, expect;

import { Hello } from '../Hello';

describe('Hello Class', () => {
it('should create a new Hello', () => {
const hello = new Hello('foo');
expect(hello.msg).toBe('foo');
});
});
10 changes: 10 additions & 0 deletions e2e/__cases__/simple/package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
{
"jest": {
"transform": {
"^.+\\.tsx?$": "ts-jest"
},
"testRegex": "(/__tests__/.*|(\\.|/)(test|spec))\\.(jsx?|tsx?)$",
"moduleFileExtensions": ["ts", "js"],
"testEnvironment": "node"
}
}
5 changes: 5 additions & 0 deletions e2e/__cases__/simple/tsconfig.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
{
"compilerOptions": {
"target": "es5",
}
}
7 changes: 7 additions & 0 deletions e2e/__tests__/e2e-simple.spec.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
import { runE2eTest } from '../../tests/__helpers__/jest-runner';

describe('Simple e2e test', () => {
it('should run the tests with success', () => {
expect('simple').toBeE2eTestWithExitCode(0);
});
});
5 changes: 5 additions & 0 deletions jest.config.e2e.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
const base = require('./jest.config');

module.exports = Object.assign({}, base, {
testRegex: 'e2e/__tests__/.*(?!watch)\\.spec\\.ts$',
});
18 changes: 18 additions & 0 deletions jest.config.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
module.exports = {
transform: {
'^.+\\.tsx?$': '<rootDir>/dist/index.js',
},
testRegex: '(tests|e2e)/__tests__/.+\\.spec\\.ts$',
testPathIgnorePatterns: ['/node_modules/', '/watch.spec.ts$'],
coverageReporters: ['text'],
coverageDirectory: 'test_coverage_dir',
collectCoverageFrom: ['src/**/*.tsx', 'src/**/*.ts'],
moduleFileExtensions: ['ts', 'tsx', 'js', 'jsx', 'json'],
globals: {
'ts-jest': {
tsConfigFile: 'tsconfig.base.json',
},
},
setupTestFrameworkScriptFile:
'<rootDir>/tests/__helpers__/setup-test-framework.ts',
};
5 changes: 5 additions & 0 deletions jest.config.tests.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
const base = require('./jest.config');

module.exports = Object.assign({}, base, {
testRegex: 'tests/__tests__/.*(?!watch)\\.spec\\.ts$',
});
8 changes: 5 additions & 3 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -8,10 +8,12 @@
"build": "tsc -p tsconfig.build.json",
"build:watch": "tsc -p tsconfig.build.json -w",
"test:nolint": "npm run clean-build && jest --clearCache && node scripts/tests.js",
"clean": "rimraf dist/**/* && rimraf tests/*/coverage && rimraf tests/*/debug.txt && rimraf tests/*/node_modules",
"clean": "./scripts/clean.js",
"clean-build": "npm run clean && npm run build",
"pretest": "npm run tslint && npm run clean-build",
"test": "node scripts/tests.js",
"pretest": "npm run tslint",
"test:unit": "./scripts/tests.js",
"test:e2e": "./scripts/e2e.js",
"test": "npm run test:e2e && npm run test:unit",
"tslint": "tslint 'src/**/*.ts'",
"doc": "doctoc .",
"prepublish": "npm run clean-build",
Expand Down
12 changes: 12 additions & 0 deletions scripts/clean.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
#!/usr/bin/env node

const rimraf = require('rimraf');
const Paths = require('./paths');
const { join } = require('path');

rimraf.sync(Paths.distDir);
rimraf.sync(join(Paths.testsRootDir, '*', 'coverage'));
rimraf.sync(join(Paths.testsRootDir, '*', 'debug.txt'));
rimraf.sync(join(Paths.testsRootDir, '*', 'node_modules'));
rimraf.sync(Paths.e2eWorkDir);
rimraf.sync(Paths.e2eWotkDirLink);
74 changes: 74 additions & 0 deletions scripts/e2e.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,74 @@
#!/usr/bin/env node
'use strict';

process.env.NODE_ENV = 'test';
const jest = require('jest');
const { sync: spawnSync } = require('cross-spawn');
const fs = require('fs-extra');
const path = require('path');
const Paths = require('./paths');

function getDirectories(rootDir) {
return fs.readdirSync(rootDir).filter(function(file) {
return fs.statSync(path.join(rootDir, file)).isDirectory();
});
}

function setupE2e() {
// this will trigger the build as well (not using yarn since yarn pack is bugy)
// keep on to so that the build is triggered beforehand (pack => prepublish => clean-build => build)
const res = spawnSync('npm', ['-s', 'pack'], { cwd: Paths.rootDir });
const bundle = path.join(Paths.rootDir, res.stdout.toString().trim());

// ensure directory exists before copying over
fs.mkdirpSync(Paths.e2eWorkTemplateDir);

// create the tempalte package from which node_modules will be originally copied from
fs.copyFileSync(
path.join(Paths.e2eSourceDir, 'package-template.json'),
path.join(Paths.e2eWorkTemplateDir, 'package.json')
);
// link locally so we could find it easily
fs.symlinkSync(Paths.e2eWorkDir, Paths.e2eWotkDirLink);
// TODO: run with specific versions?
spawnSync('npm', ['i', '-D', 'jest', 'typescript', bundle], {
cwd: Paths.e2eWorkTemplateDir,
stdio: 'inherit',
});

// copy into our temp sub-folder
getDirectories(Paths.e2eSourceDir).forEach(directory => {
// copy source and test files
fs.copySync(
path.join(Paths.e2eSourceDir, directory),
path.join(Paths.e2eWorkDir, directory)
);
// create the node_modules dir
const caseNodeModulesDir = path.join(
Paths.e2eWorkDir,
directory,
'node_modules'
);
fs.mkdirpSync(caseNodeModulesDir);
// link each node_modules from tempalte dir (so that we can install some more specific in each package later)
const tmplNodeModulesDir = path.join(
Paths.e2eWorkTemplateDir,
'node_modules'
);
getDirectories(tmplNodeModulesDir).forEach(moduleDir => {
// avoid linking '.bin'
if (moduleDir === '.bin') return;
fs.symlinkSync(
path.join(tmplNodeModulesDir, moduleDir),
path.join(caseNodeModulesDir, moduleDir)
);
});
});
}

setupE2e();

const argv = process.argv.slice(2);
argv.push('--no-cache');
argv.push('--config', path.join(Paths.rootDir, 'jest.config.e2e.js'));
jest.run(argv);
24 changes: 24 additions & 0 deletions scripts/paths.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
const path = require('path');
const os = require('os');

const rootDir = path.resolve(__dirname, '..');
const distDir = path.join(rootDir, 'dist');
const testsRootDir = path.join(rootDir, 'tests');
const e2eRootDir = path.join(rootDir, 'e2e');
const e2eSourceDir = path.join(e2eRootDir, '__cases__');
const e2eTestsDir = path.join(e2eRootDir, '__tests__');
const e2eWorkDir = path.join(os.tmpdir(), '--ts-jest-temp-e2e--');
const e2eWorkTemplateDir = path.join(e2eWorkDir, '__template__');
const e2eWotkDirLink = path.join(e2eRootDir, '__e2e_workdir_link__');

module.exports = {
rootDir,
e2eSourceDir,
e2eRootDir,
e2eWorkDir,
e2eWorkTemplateDir,
e2eWotkDirLink,
distDir,
testsRootDir,
e2eTestsDir,
};
6 changes: 4 additions & 2 deletions scripts/tests.js
100644 → 100755
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
#!/usr/bin/env node
'use strict';

process.env.NODE_ENV = 'test';
Expand All @@ -6,6 +7,7 @@ process.env.PUBLIC_URL = '';
const jest = require('jest');
const fs = require('fs-extra');
const path = require('path');
const Paths = require('./paths');

function getDirectories(rootDir) {
return fs.readdirSync(rootDir).filter(function(file) {
Expand All @@ -24,6 +26,7 @@ function isTestCaseFolder(basename) {
}

function createIntegrationMock() {
// copy files for simple integration tests
const testsRoot = 'tests';
const testCaseFolders = getDirectories(testsRoot).filter(isTestCaseFolder);

Expand Down Expand Up @@ -58,6 +61,5 @@ createIntegrationMock();

const argv = process.argv.slice(2);
argv.push('--no-cache');
argv.push('--testPathPattern', '^(?!(.*watch.spec.ts$)).*');

argv.push('--config', path.join(Paths.rootDir, 'jest.config.tests.js'));
jest.run(argv);
8 changes: 8 additions & 0 deletions tests/@types/jest-shim.d.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
import 'jest';

declare namespace jest {
interface Matchers<R> {
toBeE2eTestWithExitCode(expectedExitCode: number): R;
toBeTestCaseWithExitCode(expectedExitCode: number): R;
}
}
61 changes: 61 additions & 0 deletions tests/__helpers__/jest-runner.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
// from: https://github.com/facebook/jest/blob/master/integration_tests/runJest.js

import { sync as spawnSync } from 'cross-spawn';
import * as path from 'path';
import * as Paths from '../../scripts/paths';

function runJestTestIn(
dir: string,
args: string[] = [],
env = {},
): JestRunResult {
if (!path.isAbsolute(dir)) {
throw new Error(`You must give an absolute path to runJestTestIn().`);
}

const JEST_BIN = path.join(dir, 'node_modules', 'jest', 'bin', 'jest.js');

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

// Call to string on byte arrays and strip ansi color codes for more accurate string comparison.
const stdout = result.stdout ? stripAnsiColors(result.stdout.toString()) : '';
const stderr = result.stderr ? stripAnsiColors(result.stderr.toString()) : '';
const output = result.output
? stripAnsiColors(result.output.join('\n\n'))
: '';

return { status: result.status, stderr, stdout, output };
}

// from https://stackoverflow.com/questions/25245716/remove-all-ansi-colors-styles-from-strings
function stripAnsiColors(stringToStrip: string): string {
return stringToStrip.replace(
/[\u001b\u009b][[()#;?]*(?:[0-9]{1,4}(?:;[0-9]{0,4})*)?[0-9A-ORZcf-nqry=><]/g,
'',
);
}

export interface JestRunResult {
status: number;
stdout: string;
stderr: string;
output: string;
}

export function runTestCase(
name: string,
{ env = {}, args = [] } = {},
): JestRunResult {
return runJestTestIn(path.join(Paths.testsRootDir, name), args, env);
}

export function runE2eTest(
name: string,
{ env = {}, args = [] } = {},
): JestRunResult {
return runJestTestIn(path.join(Paths.e2eWorkDir, name), args, env);
}
4 changes: 2 additions & 2 deletions tests/__helpers__/mock-jest-config.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { TsJestConfig } from '../../dist/types';
import { TsJestConfig } from '../../src/types';

const { resolve } = require.requireActual('path');

Expand All @@ -17,7 +17,7 @@ export default function mockJestConfig(
// resolves the path since jest would give a resolved path
const rootDir = resolve(__dirname, '..', testCaseFolder);
// create base jest config object
let options: any = { rootDir, cwd: rootDir };
const options: any = { rootDir, cwd: rootDir };
// adds TS Jest options if any given
if (tsJest != null) {
options.globals = { 'ts-jest': tsJest };
Expand Down
8 changes: 4 additions & 4 deletions tests/__helpers__/runJest.ts
Original file line number Diff line number Diff line change
Expand Up @@ -34,11 +34,11 @@ export default function runJest(dir: string, args: string[], env = {}): Result {
});

// Call to string on byte arrays and strip ansi color codes for more accurate string comparison.
result.stdout = result.stdout && stripAnsiColors(result.stdout.toString());
result.stderr = result.stderr && stripAnsiColors(result.stderr.toString());
result.output = result.output && stripAnsiColors(result.output.toString());
const stdout = result.stdout && stripAnsiColors(result.stdout.toString());
const stderr = result.stderr && stripAnsiColors(result.stderr.toString());
const output = result.output && stripAnsiColors(result.output.toString());

return result;
return { status: result.status, stdout, stderr, output };
}

// from https://stackoverflow.com/questions/25245716/remove-all-ansi-colors-styles-from-strings
Expand Down
30 changes: 30 additions & 0 deletions tests/__helpers__/setup-test-framework.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
import { runE2eTest, runTestCase } from './jest-runner';

function expectTest(runner: any, received, expectedStatus) {
const { status, output } = runner(received);
const pass: boolean = expectedStatus === status;
if (pass) {
return {
message: () =>
`expected e2e test ${received} to not exit with status ${expectedStatus}` +
`\n\nOutput:\n ${this.utils.printReceived(output)}`,
pass: true,
};
} else {
return {
message: () =>
`expected e2e test ${received} to exit with status ${expectedStatus}` +
`\n\nOutput:\n ${this.utils.printReceived(output)}`,
pass: false,
};
}
}

expect.extend({
toBeE2eTestWithExitCode(received: string, expectedStatus: number) {
return expectTest.call(this, runE2eTest, received, expectedStatus);
},
toBeTestCaseWithExitCode(received: string, expectedStatus: number) {
return expectTest.call(this, runTestCase, received, expectedStatus);
},
});
6 changes: 5 additions & 1 deletion tsconfig.base.json
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,11 @@
"allowSyntheticDefaultImports": false,
"skipLibCheck": true,
"lib": ["es2015"],
"types": ["jest"]
"types": ["jest"],
"typeRoots": [
"node_modules/@types",
"tests/@types"
]
},
"exclude": ["**/node_modules/**/*"]
}

0 comments on commit ca697f2

Please sign in to comment.