5
* LICENSE file in the root directory of this source tree.
6
*/
7
8
-import chalk from "chalk";
8
import {
9
FILTER_FILENAME,
11
- TestFixture,
10
+ TestResult,
11
+ UpdateSnapshotKind,
12
getFixtures,
13
+ getUpdatedSnapshot,
14
+ isExpectError,
15
readTestFilter,
16
+ report,
17
+ update,
18
} from "fixture-test-utils";
19
import { Worker } from "jest-worker";
20
import process from "process";
22
import yargs from "yargs";
23
import { hideBin } from "yargs/helpers";
24
import SproutTodoFilter from "./SproutTodoFilter";
25
+import { EvaluatorResult } from "./runner-evaluator";
26
+import type { SproutFixtureResult } from "./runner-worker";
27
import * as RunnerWorker from "./runner-worker";
28
29
const WORKER_PATH = require.resolve("./runner-worker");
44
});
45
46
type RunnerOptions = {
41
- filter: boolean;
47
sync: boolean;
43
- verbose: boolean;
48
+ mode: "update" | "filter" | undefined;
49
};
50
51
const opts: RunnerOptions = yargs
55
"Run compiler in main thread (instead of using worker threads)."
56
)
57
.default("sync", false)
53
- .boolean("filter")
54
- .describe(
55
- "filter",
56
- `Evaluate fixtures in filter mode ("${FILTER_FILENAME}")\n`
57
- )
58
- .default("filter", false)
59
- .boolean("verbose")
60
- .describe("verbose", "Print all fixture outputs and logs.")
61
- .default("verbose", false)
58
+ .option("mode", {
59
+ type: "string",
60
+ desc:
61
+ "Sprout tester modes:\n" +
62
+ " [default] - test all test fixtures\n" +
63
+ ` filter - test filtered fixtures ("${FILTER_FILENAME}")\n` +
64
+ " update - update all test fixtures)\n",
65
+ choices: ["update", "filter", undefined],
66
+ default: undefined,
67
+ })
68
.help("help")
69
.strict()
70
.parseSync(hideBin(process.argv));
75
}
76
return a.every((val, idx) => val === b[idx]);
77
}
78
+function stringify(result: EvaluatorResult): string {
79
+ return `(kind: ${result.kind}) ${result.value}${
80
+ result.logs.length > 0 ? `\nlogs: [${result.logs.toString()}]` : ""
81
+ }`;
82
+}
83
73
-function reportResults(
74
- results: Array<[string, RunnerWorker.TestResult]>,
75
- verbose: boolean
76
-): boolean {
77
- const failures: Array<[string, RunnerWorker.TestResult]> = [];
78
-
79
- for (const [fixtureName, result] of results) {
80
- if (result.unexpectedError !== null) {
81
- console.log(
82
- chalk.red.inverse.bold(" FAIL ") + " " + chalk.dim(fixtureName)
83
- );
84
- failures.push([fixtureName, result]);
85
- continue;
86
- }
87
- const { forgetResult, nonForgetResult } = result;
88
- if (
89
- forgetResult.kind === "UnexpectedError" ||
90
- nonForgetResult.kind === "UnexpectedError" ||
91
- forgetResult.kind !== nonForgetResult.kind ||
92
- forgetResult.value !== nonForgetResult.value ||
93
- !logsEqual(forgetResult.logs, nonForgetResult.logs)
94
- ) {
95
- console.log(
96
- chalk.red.inverse.bold(" FAIL ") + " " + chalk.dim(fixtureName)
97
- );
98
- failures.push([fixtureName, result]);
99
- } else {
100
- console.log(
101
- chalk.green.inverse.bold(" PASS ") + " " + chalk.dim(fixtureName)
102
- );
103
- if (verbose) {
104
- console.log(
105
- ` ${forgetResult.kind} ${forgetResult.value} ${
106
- forgetResult.logs.length > 0
107
- ? JSON.stringify(forgetResult.logs, undefined, 2)
108
- : ""
109
- }`
110
- );
111
- }
112
- }
84
+function transformResult(result: SproutFixtureResult): TestResult {
85
+ function makeError(description: string, value: string) {
86
+ return {
87
+ outputPath: result.snapshotPath,
88
+ actual: null,
89
+ expected: null,
90
+ unexpectedError: `${description}\n${value}`,
91
+ };
92
}
114
-
115
- if (failures.length !== 0) {
116
- console.log("\n" + chalk.red.bold("Failures:") + "\n");
117
-
118
- for (const [fixtureName, result] of failures) {
119
- console.log(chalk.red.bold("FAIL:") + " " + fixtureName);
120
-
121
- if (result.unexpectedError !== null) {
122
- console.log(
123
- chalk.red("Unexpected error when building fixture:") +
124
- ` ${result.unexpectedError}`
125
- );
126
- continue;
127
- }
128
- const { forgetResult, nonForgetResult } = result;
129
- if (forgetResult.kind === "UnexpectedError") {
130
- console.log(
131
- chalk.red(
132
- "Unexpected error when evaluating Forget-transformed fixture:"
133
- ) + ` ${forgetResult.value}`
134
- );
135
- }
136
- if (nonForgetResult.kind === "UnexpectedError") {
137
- console.log(
138
- chalk.red("Unexpected error when evaluating original fixture:") +
139
- ` ${nonForgetResult.value}`
140
- );
141
- }
142
- const hasUnexpectedError =
143
- forgetResult.kind === "UnexpectedError" ||
144
- nonForgetResult.kind === "UnexpectedError";
145
- if (
146
- !hasUnexpectedError &&
147
- (forgetResult.kind !== nonForgetResult.kind ||
148
- forgetResult.value !== nonForgetResult.value ||
149
- !logsEqual(forgetResult.logs, nonForgetResult.logs))
150
- ) {
151
- console.log(
152
- chalk.red("Difference in forget and non-forget results.") +
153
- `\nExpected result: ${JSON.stringify(
154
- nonForgetResult,
155
- undefined,
156
- 2
157
- )}\nFound: ${JSON.stringify(forgetResult, undefined, 2)}`
158
- );
159
- }
160
- }
93
+ if (result.unexpectedError !== null) {
94
+ return makeError("UnexpectedError in runner", result.unexpectedError);
95
+ }
96
+ const { forgetResult, nonForgetResult } = result;
97
+ if (forgetResult.kind === "UnexpectedError") {
98
+ return makeError("UnexpectedError in Forget runner", forgetResult.value);
99
+ } else if (nonForgetResult.kind === "UnexpectedError") {
100
+ return makeError(
101
+ "UnexpectedError in non-forget runner",
102
+ nonForgetResult.value
103
+ );
104
+ } else if (
105
+ forgetResult.kind !== nonForgetResult.kind ||
106
+ forgetResult.value !== nonForgetResult.value ||
107
+ !logsEqual(forgetResult.logs, nonForgetResult.logs)
108
+ ) {
109
+ return makeError(
110
+ "Found differences in evaluator results",
111
+ `Non-forget (expected):
112
+${stringify(nonForgetResult)}
113
+Forget:
114
+${stringify(forgetResult)}
115
+`
116
+ );
117
+ } else {
118
+ return {
119
+ outputPath: result.snapshotPath,
120
+ expected: result.snapshot,
121
+ actual: getUpdatedSnapshot(
122
+ result.snapshot,
123
+ stringify(forgetResult),
124
+ UpdateSnapshotKind.Sprout
125
+ ),
126
+ unexpectedError: null,
127
+ };
128
}
162
-
163
- console.log(
164
- `${results.length} Tests, ${results.length - failures.length} Passed, ${
165
- failures.length
166
- } Failed`
167
- );
168
- return failures.length === 0;
129
}
130
131
/**
138
worker.getStderr().pipe(process.stderr);
139
worker.getStdout().pipe(process.stdout);
140
181
- const testFilter = opts.filter ? await readTestFilter() : null;
182
- let allFixtures: Map<string, TestFixture> = getFixtures(testFilter);
141
+ const testFilter = opts.mode === "filter" ? await readTestFilter() : null;
142
+ const allFixtures = await getFixtures(testFilter);
143
184
- allFixtures = new Map(
185
- Array.from(allFixtures.entries()).filter(
186
- ([filename, _]) => !SproutTodoFilter.has(filename)
187
- )
144
+ const validFixtures = new Map(
145
+ Array.from(allFixtures.entries()).filter(([fixtureName, fixture]) => {
146
+ return !SproutTodoFilter.has(fixtureName) && !isExpectError(fixture);
147
+ })
148
);
189
-
190
- const validFixtures = new Map();
191
- for (const [name, fixture] of allFixtures) {
192
- if (
193
- fixture.basename.startsWith("error.") ||
194
- fixture.basename.startsWith("todo.error.")
195
- ) {
196
- // skip
197
- continue;
198
- }
199
- validFixtures.set(name, fixture);
200
- }
201
-
202
- const results: Array<[string, RunnerWorker.TestResult]> = [];
149
+ const sproutResults: Array<[string, SproutFixtureResult]> = [];
150
if (!opts.sync) {
204
- const work: Array<Promise<[string, RunnerWorker.TestResult]>> = [];
151
+ const work: Array<Promise<[string, SproutFixtureResult]>> = [];
152
for (const [fixtureName, fixture] of validFixtures) {
153
work.push(worker.run(fixture).then((result) => [fixtureName, result]));
154
}
208
- results.push(...(await Promise.all(work)));
155
+ sproutResults.push(...(await Promise.all(work)));
156
} else {
157
for (const [fixtureName, fixture] of validFixtures) {
211
- const result: [string, RunnerWorker.TestResult] = await RunnerWorker.run(
158
+ const result: [string, SproutFixtureResult] = await RunnerWorker.run(
159
fixture
160
).then((result) => [fixtureName, result]);
214
- results.push(result);
161
+ sproutResults.push(result);
162
}
163
}
164
218
- const isSuccess = reportResults(results, opts.verbose);
165
+ const results = new Map(
166
+ sproutResults.map(([fixtureName, result]) => [
167
+ fixtureName,
168
+ transformResult(result),
169
+ ])
170
+ );
171
+ let isSuccess;
172
+ if (opts.mode === "update") {
173
+ isSuccess = true;
174
+ update(results);
175
+ } else {
176
+ isSuccess = report(results);
177
+ }
178
/**
179
* This is important, as we're using jsdom (which seems to be attaching some
180
* tasks that require force exiting. If we do not await workers terminating,