Skip to content

Commit dfa9c0f

Browse files
committed
feat(test): jest serializers
1 parent cc04021 commit dfa9c0f

15 files changed

Lines changed: 6195 additions & 26 deletions

File tree

e2e/README.md

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -40,9 +40,10 @@ The returned value is an object with those properties and methods:
4040
- `stdout`, _string_: the data written to stdout during the run
4141
- `stderr`, _string_: the data written to stderr during the run
4242
- `output`, _string_: the data written to stdout and stderr during the run
43-
- `outputForSnapshot`, _string_: same as `output`, expect it's sanitized for jest snapshot (time values are replaced with static values, ...)
4443

45-
**Note**: _You can optionally pass the expected status code as the first argument of `run()`. In the case it's not the correct one, it'll write in the console the actual `output` so that you can debug the test case._
44+
**Note 1**: _The value returned by `run()` is snapshot friendly (ie.: you can `expect(x.run()).toMatchSnapshot()`, it'll remove any changin values such as time values)_
45+
46+
**Note 2**: _You can optionally pass the expected status code as the first argument of `run()`. In the case it's not the correct one, it'll write in the console the actual `output` so that you can debug the test case._
4647

4748
Bare simple example of using it in your tests:
4849
```ts
Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
{
2+
"name": "ts-jest-debug",
3+
"version": "1.0.0",
4+
"main": "index.js",
5+
"license": "MIT",
6+
"scripts": {
7+
"test": "jest --config ./test/jest.config.json"
8+
}
9+
}
Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
export function echo(shout: string) {
2+
console.log('WITHIN SOURCE');
3+
if (process.env.__FORCE_FAIL) {
4+
throw new Error('WITHIN SOURCE');
5+
}
6+
return shout;
7+
}
Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
import { echo } from '../src/echo';
2+
3+
describe('echo', () => {
4+
it('echoes', () => {
5+
console.log('WITHIN TEST');
6+
expect(echo('repeat')).toEqual('repeat');
7+
});
8+
});
Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
{
2+
"moduleFileExtensions": ["js", "json", "ts"],
3+
"roots": ["../src", "."],
4+
"testRegex": ".spec.ts$",
5+
"transform": {
6+
"^.+\\.(t|j)s$": "ts-jest"
7+
},
8+
"coverageDirectory": "./coverage",
9+
"testEnvironment": "node"
10+
}
Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
{
2+
"extends": "../tsconfig.json",
3+
"compilerOptions": {
4+
"lib": [
5+
"es2015",
6+
"dom"
7+
]
8+
},
9+
"include": [
10+
"../src/**/*",
11+
"**/*"
12+
]
13+
}
Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
{
2+
"compilerOptions": {
3+
"module": "commonjs",
4+
"declaration": true,
5+
"strict": true,
6+
"sourceMap": true,
7+
"outDir": "./dist",
8+
"emitDecoratorMetadata": true,
9+
"experimentalDecorators": true,
10+
"target": "es6",
11+
"lib": [
12+
"es2015",
13+
"es2017",
14+
"dom"
15+
]
16+
},
17+
"include": [
18+
"src/**/*"
19+
]
20+
}

e2e/__helpers__/test-case.ts

Lines changed: 75 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
// tslint:disable-file:no-shadowed-variable
12
import { sync as spawnSync } from 'cross-spawn';
23
import { join } from 'path';
34
import * as Paths from '../../scripts/paths';
@@ -44,15 +45,12 @@ class TestCaseRunDescriptor {
4445
return this._options.template;
4546
}
4647

47-
run(logOutputUnlessStatusIs?: number): TestRunResult {
48+
run(logUnlessStatus?: number): TestRunResult {
4849
const result = run(this.name, {
4950
...this._options,
5051
template: this.templateName,
5152
});
52-
if (
53-
logOutputUnlessStatusIs != null &&
54-
logOutputUnlessStatusIs !== result.status
55-
) {
53+
if (logUnlessStatus != null && logUnlessStatus !== result.status) {
5654
console.log(
5755
`Output of test run in "${this.name}" using template "${
5856
this.templateName
@@ -62,29 +60,93 @@ class TestCaseRunDescriptor {
6260
}
6361
return result;
6462
}
63+
64+
runWithTemplates<T extends string>(
65+
logUnlessStatus: number,
66+
...templates: T[]
67+
): TestRunResultsMap<T>;
68+
runWithTemplates<T extends string>(...templates: T[]): TestRunResultsMap<T>;
69+
runWithTemplates<T extends string>(
70+
logUnlessStatus: number | T,
71+
...templates: T[]
72+
): TestRunResultsMap<T> {
73+
if (typeof logUnlessStatus !== 'number') {
74+
templates.unshift(logUnlessStatus);
75+
logUnlessStatus = undefined;
76+
}
77+
if (templates.length < 1) {
78+
throw new RangeError(
79+
`There must be at least one template to run the test case with.`,
80+
);
81+
}
82+
if (!templates.every((t, i) => templates.indexOf(t, i + 1) === -1)) {
83+
throw new Error(
84+
`Each template must be unique. Given ${templates.join(', ')}`,
85+
);
86+
}
87+
return templates.reduce(
88+
(map, template) => {
89+
const desc = new TestCaseRunDescriptor(this.name, {
90+
...this._options,
91+
template,
92+
});
93+
map[template as string] = desc.run(logUnlessStatus as number);
94+
return map;
95+
},
96+
{} as TestRunResultsMap<T>,
97+
);
98+
}
6599
}
66100

101+
export const TestRunResultFlag = Symbol.for('[ts-jest-test-run-result]');
102+
67103
export interface RunTestOptions {
68104
template?: string;
69105
env?: {};
70106
args?: string[];
71107
}
72108

73109
export interface TestRunResult {
110+
[TestRunResultFlag]: true;
74111
status: number;
75112
stdout: string;
76113
stderr: string;
77114
output: string;
78-
outputForSnapshot: string;
79115
}
80116

117+
// tslint:disable-next-line:interface-over-type-literal
118+
export type TestRunResultsMap<T extends string = string> = {
119+
[key in T]: TestRunResult
120+
};
121+
81122
export default function configureTestCase(
82123
name: string,
83124
options: RunTestOptions = {},
84125
): TestCaseRunDescriptor {
85126
return new TestCaseRunDescriptor(name, options);
86127
}
87128

129+
export function sanitizeOutput(output: string): string {
130+
return (
131+
output
132+
.trim()
133+
// removes total and estimated times
134+
.replace(
135+
/^(\s*Time\s*:\s*)[\d.]+m?s(?:(,\s*estimated\s+)[\d.]+m?s)?(\s*)$/gm,
136+
(_, start, estimatedPrefix, end) => {
137+
return `${start}XXs${
138+
estimatedPrefix ? `${estimatedPrefix}YYs` : ''
139+
}${end}`;
140+
},
141+
)
142+
// removes each test time values
143+
.replace(
144+
/^(\s*(?:|)\s+.+\s+\()[\d.]+m?s(\)\s*)$/gm,
145+
(_, start, end) => `${start}XXms${end}`,
146+
)
147+
);
148+
}
149+
88150
export function run(
89151
name: string,
90152
{ args = [], env = {}, template }: RunTestOptions = {},
@@ -115,24 +177,14 @@ export function run(
115177
const output = result.output
116178
? stripAnsiColors(result.output.join('\n\n'))
117179
: '';
118-
const outputForSnapshot = output
119-
.trim()
120-
// removes total and estimated time(s)
121-
.replace(
122-
/^(\s*Time\s*:\s*)[\d.]+m?s(?:(,\s*estimated\s+)[\d.]+m?s)?(\s*)$/gm,
123-
(_, start, estimatedPrefix, end) => {
124-
return `${start}XXs${
125-
estimatedPrefix ? `${estimatedPrefix}YYs` : ''
126-
}${end}`;
127-
},
128-
)
129-
// removes each test time(s)
130-
.replace(
131-
/^(\s*(?:|)\s+.+\s+\()[\d.]+m?s(\)\s*)$/gm,
132-
(_, start, end) => `${start}XXms${end}`,
133-
);
134180

135-
return { status: result.status, stderr, stdout, output, outputForSnapshot };
181+
return {
182+
[TestRunResultFlag]: true,
183+
status: result.status,
184+
stderr,
185+
stdout,
186+
output,
187+
};
136188
}
137189

138190
// from https://stackoverflow.com/questions/25245716/remove-all-ansi-colors-styles-from-strings
Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
import {
2+
TestRunResultFlag,
3+
TestRunResult,
4+
sanitizeOutput,
5+
} from '../__helpers__/test-case';
6+
7+
export const test = (val: any) => val && val[TestRunResultFlag];
8+
export const print = (val: TestRunResult, serialize: any, indent: any) => {
9+
const out = [
10+
`===[ STDOUT ]${'='.repeat(67)}`,
11+
sanitizeOutput(val.stdout),
12+
`===[ STDERR ]${'='.repeat(67)}`,
13+
sanitizeOutput(val.stderr),
14+
'='.repeat(80),
15+
]
16+
.map(l => indent(l))
17+
.join('\n');
18+
return `jest exit code: ${val.status}\n${out}`;
19+
};

0 commit comments

Comments
 (0)