ava 4.0.0-alpha.1 → 4.0.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/entrypoints/cli.mjs +4 -0
- package/{eslint-plugin-helper.js → entrypoints/eslint-plugin-helper.cjs} +20 -7
- package/entrypoints/main.cjs +2 -0
- package/entrypoints/main.mjs +1 -0
- package/entrypoints/plugin.cjs +2 -0
- package/entrypoints/plugin.mjs +4 -0
- package/index.d.ts +6 -709
- package/lib/api.js +95 -46
- package/lib/assert.js +122 -173
- package/lib/chalk.js +9 -14
- package/lib/cli.js +105 -97
- package/lib/code-excerpt.js +12 -17
- package/lib/concordance-options.js +30 -31
- package/lib/context-ref.js +3 -6
- package/lib/create-chain.js +32 -4
- package/lib/environment-variables.js +1 -4
- package/lib/eslint-plugin-helper-worker.js +16 -26
- package/lib/extensions.js +2 -2
- package/lib/fork.js +42 -83
- package/lib/glob-helpers.cjs +140 -0
- package/lib/globs.js +136 -163
- package/lib/{ipc-flow-control.js → ipc-flow-control.cjs} +1 -0
- package/lib/is-ci.js +4 -2
- package/lib/like-selector.js +7 -13
- package/lib/line-numbers.js +10 -17
- package/lib/load-config.js +62 -56
- package/lib/module-types.js +3 -3
- package/lib/node-arguments.js +4 -5
- package/lib/{now-and-timers.js → now-and-timers.cjs} +0 -0
- package/lib/parse-test-args.js +22 -11
- package/lib/pkg.cjs +2 -0
- package/lib/plugin-support/shared-worker-loader.js +45 -48
- package/lib/plugin-support/shared-workers.js +24 -43
- package/lib/provider-manager.js +20 -14
- package/lib/reporters/beautify-stack.js +6 -11
- package/lib/reporters/colors.js +40 -15
- package/lib/reporters/default.js +115 -350
- package/lib/reporters/format-serialized-error.js +7 -18
- package/lib/reporters/improper-usage-messages.js +8 -9
- package/lib/reporters/prefix-title.js +17 -15
- package/lib/reporters/tap.js +15 -16
- package/lib/run-status.js +25 -23
- package/lib/runner.js +138 -127
- package/lib/scheduler.js +42 -36
- package/lib/serialize-error.js +34 -34
- package/lib/snapshot-manager.js +83 -76
- package/lib/test.js +114 -195
- package/lib/watcher.js +65 -40
- package/lib/worker/base.js +48 -99
- package/lib/worker/channel.cjs +290 -0
- package/lib/worker/dependency-tracker.js +22 -22
- package/lib/worker/guard-environment.cjs +19 -0
- package/lib/worker/line-numbers.js +57 -19
- package/lib/worker/main.cjs +12 -0
- package/lib/worker/{options.js → options.cjs} +0 -0
- package/lib/worker/{plugin.js → plugin.cjs} +31 -16
- package/lib/worker/state.cjs +5 -0
- package/lib/worker/{utils.js → utils.cjs} +1 -1
- package/package.json +60 -68
- package/plugin.d.ts +51 -53
- package/readme.md +5 -12
- package/types/assertions.d.ts +327 -0
- package/types/subscribable.ts +6 -0
- package/types/test-fn.d.ts +231 -0
- package/types/try-fn.d.ts +58 -0
- package/cli.js +0 -11
- package/index.js +0 -8
- package/lib/worker/channel.js +0 -218
- package/lib/worker/main.js +0 -20
- package/plugin.js +0 -9
package/lib/runner.js
CHANGED
|
@@ -1,14 +1,19 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
1
|
+
import process from 'node:process';
|
|
2
|
+
import {pathToFileURL} from 'node:url';
|
|
3
|
+
|
|
4
|
+
import Emittery from 'emittery';
|
|
5
|
+
import {matcher} from 'matcher';
|
|
6
|
+
|
|
7
|
+
import ContextRef from './context-ref.js';
|
|
8
|
+
import createChain from './create-chain.js';
|
|
9
|
+
import parseTestArgs from './parse-test-args.js';
|
|
10
|
+
import serializeError from './serialize-error.js';
|
|
11
|
+
import {load as loadSnapshots, determineSnapshotDir} from './snapshot-manager.js';
|
|
12
|
+
import Runnable from './test.js';
|
|
13
|
+
import {waitForReady} from './worker/state.cjs';
|
|
14
|
+
|
|
15
|
+
const makeFileURL = file => file.startsWith('file://') ? file : pathToFileURL(file).toString();
|
|
16
|
+
export default class Runner extends Emittery {
|
|
12
17
|
constructor(options = {}) {
|
|
13
18
|
super();
|
|
14
19
|
|
|
@@ -18,7 +23,6 @@ class Runner extends Emittery {
|
|
|
18
23
|
this.file = options.file;
|
|
19
24
|
this.checkSelectedByLineNumbers = options.checkSelectedByLineNumbers;
|
|
20
25
|
this.match = options.match || [];
|
|
21
|
-
this.powerAssert = undefined; // Assigned later.
|
|
22
26
|
this.projectDir = options.projectDir;
|
|
23
27
|
this.recordNewSnapshots = options.recordNewSnapshots === true;
|
|
24
28
|
this.runOnlyExclusive = options.runOnlyExclusive === true;
|
|
@@ -30,16 +34,6 @@ class Runner extends Emittery {
|
|
|
30
34
|
this.boundCompareTestSnapshot = this.compareTestSnapshot.bind(this);
|
|
31
35
|
this.boundSkipSnapshot = this.skipSnapshot.bind(this);
|
|
32
36
|
this.interrupted = false;
|
|
33
|
-
this.snapshots = snapshotManager.load({
|
|
34
|
-
file: this.file,
|
|
35
|
-
fixedLocation: this.snapshotDir,
|
|
36
|
-
projectDir: this.projectDir,
|
|
37
|
-
recordNewSnapshots: this.recordNewSnapshots,
|
|
38
|
-
updating: this.updateSnapshots
|
|
39
|
-
});
|
|
40
|
-
if (this.snapshots.snapPath !== undefined) {
|
|
41
|
-
this.emit('dependency', this.snapshots.snapPath);
|
|
42
|
-
}
|
|
43
37
|
|
|
44
38
|
this.nextTaskIndex = 0;
|
|
45
39
|
this.tasks = {
|
|
@@ -51,9 +45,9 @@ class Runner extends Emittery {
|
|
|
51
45
|
beforeEach: [],
|
|
52
46
|
concurrent: [],
|
|
53
47
|
serial: [],
|
|
54
|
-
todo: []
|
|
48
|
+
todo: [],
|
|
55
49
|
};
|
|
56
|
-
this.waitForReady =
|
|
50
|
+
this.waitForReady = waitForReady;
|
|
57
51
|
|
|
58
52
|
const uniqueTestTitles = new Set();
|
|
59
53
|
this.registerUniqueTitle = title => {
|
|
@@ -65,14 +59,21 @@ class Runner extends Emittery {
|
|
|
65
59
|
return true;
|
|
66
60
|
};
|
|
67
61
|
|
|
62
|
+
this.notifyTimeoutUpdate = timeoutMs => {
|
|
63
|
+
this.emit('stateChange', {
|
|
64
|
+
type: 'test-timeout-configured',
|
|
65
|
+
period: timeoutMs,
|
|
66
|
+
});
|
|
67
|
+
};
|
|
68
|
+
|
|
68
69
|
let hasStarted = false;
|
|
69
70
|
let scheduledStart = false;
|
|
70
71
|
const meta = Object.freeze({
|
|
71
|
-
file: options.file,
|
|
72
|
+
file: makeFileURL(options.file),
|
|
72
73
|
get snapshotDirectory() {
|
|
73
74
|
const {file, snapshotDir: fixedLocation, projectDir} = options;
|
|
74
|
-
return
|
|
75
|
-
}
|
|
75
|
+
return makeFileURL(determineSnapshotDir({file, fixedLocation, projectDir}));
|
|
76
|
+
},
|
|
76
77
|
});
|
|
77
78
|
this.chain = createChain((metadata, testArgs) => { // eslint-disable-line complexity
|
|
78
79
|
if (hasStarted) {
|
|
@@ -89,94 +90,95 @@ class Runner extends Emittery {
|
|
|
89
90
|
|
|
90
91
|
metadata.taskIndex = this.nextTaskIndex++;
|
|
91
92
|
|
|
92
|
-
const {args,
|
|
93
|
+
const {args, implementation, title} = parseTestArgs(testArgs);
|
|
93
94
|
|
|
94
95
|
if (this.checkSelectedByLineNumbers) {
|
|
95
96
|
metadata.selected = this.checkSelectedByLineNumbers();
|
|
96
97
|
}
|
|
97
98
|
|
|
98
99
|
if (metadata.todo) {
|
|
99
|
-
if (
|
|
100
|
+
if (implementation) {
|
|
100
101
|
throw new TypeError('`todo` tests are not allowed to have an implementation. Use `test.skip()` for tests with an implementation.');
|
|
101
102
|
}
|
|
102
103
|
|
|
103
|
-
if (!
|
|
104
|
+
if (!title.raw) { // Either undefined or a string.
|
|
104
105
|
throw new TypeError('`todo` tests require a title');
|
|
105
106
|
}
|
|
106
107
|
|
|
107
|
-
if (!this.registerUniqueTitle(
|
|
108
|
-
throw new Error(`Duplicate test title: ${
|
|
108
|
+
if (!this.registerUniqueTitle(title.value)) {
|
|
109
|
+
throw new Error(`Duplicate test title: ${title.value}`);
|
|
109
110
|
}
|
|
110
111
|
|
|
111
112
|
// --match selects TODO tests.
|
|
112
|
-
if (this.match.length > 0 && matcher(
|
|
113
|
+
if (this.match.length > 0 && matcher(title.value, this.match).length === 1) {
|
|
113
114
|
metadata.exclusive = true;
|
|
114
115
|
this.runOnlyExclusive = true;
|
|
115
116
|
}
|
|
116
117
|
|
|
117
|
-
this.tasks.todo.push({title:
|
|
118
|
+
this.tasks.todo.push({title: title.value, metadata});
|
|
118
119
|
this.emit('stateChange', {
|
|
119
120
|
type: 'declared-test',
|
|
120
|
-
title:
|
|
121
|
+
title: title.value,
|
|
121
122
|
knownFailing: false,
|
|
122
|
-
todo: true
|
|
123
|
+
todo: true,
|
|
123
124
|
});
|
|
124
125
|
} else {
|
|
125
|
-
if (
|
|
126
|
+
if (!implementation) {
|
|
126
127
|
throw new TypeError('Expected an implementation. Use `test.todo()` for tests without an implementation.');
|
|
127
128
|
}
|
|
128
129
|
|
|
129
|
-
|
|
130
|
-
|
|
130
|
+
if (Array.isArray(implementation)) {
|
|
131
|
+
throw new TypeError('AVA 4 no longer supports multiple implementations.');
|
|
132
|
+
}
|
|
131
133
|
|
|
132
|
-
|
|
133
|
-
|
|
134
|
+
if (title.isSet && !title.isValid) {
|
|
135
|
+
throw new TypeError('Test & hook titles must be strings');
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
let fallbackTitle = title.value;
|
|
139
|
+
if (title.isEmpty) {
|
|
140
|
+
if (metadata.type === 'test') {
|
|
141
|
+
throw new TypeError('Tests must have a title');
|
|
142
|
+
} else if (metadata.always) {
|
|
143
|
+
fallbackTitle = `${metadata.type}.always hook`;
|
|
144
|
+
} else {
|
|
145
|
+
fallbackTitle = `${metadata.type} hook`;
|
|
134
146
|
}
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
if (metadata.type === 'test' && !this.registerUniqueTitle(title.value)) {
|
|
150
|
+
throw new Error(`Duplicate test title: ${title.value}`);
|
|
151
|
+
}
|
|
135
152
|
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
153
|
+
const task = {
|
|
154
|
+
title: title.value || fallbackTitle,
|
|
155
|
+
implementation,
|
|
156
|
+
args,
|
|
157
|
+
metadata: {...metadata},
|
|
158
|
+
};
|
|
159
|
+
|
|
160
|
+
if (metadata.type === 'test') {
|
|
161
|
+
if (this.match.length > 0) {
|
|
162
|
+
// --match overrides .only()
|
|
163
|
+
task.metadata.exclusive = matcher(title.value, this.match).length === 1;
|
|
144
164
|
}
|
|
145
165
|
|
|
146
|
-
if (metadata.
|
|
147
|
-
|
|
166
|
+
if (task.metadata.exclusive) {
|
|
167
|
+
this.runOnlyExclusive = true;
|
|
148
168
|
}
|
|
149
169
|
|
|
150
|
-
|
|
151
|
-
title,
|
|
152
|
-
implementation,
|
|
153
|
-
args,
|
|
154
|
-
metadata: {...metadata}
|
|
155
|
-
};
|
|
170
|
+
this.tasks[metadata.serial ? 'serial' : 'concurrent'].push(task);
|
|
156
171
|
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
this.tasks[metadata.serial ? 'serial' : 'concurrent'].push(task);
|
|
168
|
-
|
|
169
|
-
this.snapshots.touch(title, metadata.taskIndex);
|
|
170
|
-
|
|
171
|
-
this.emit('stateChange', {
|
|
172
|
-
type: 'declared-test',
|
|
173
|
-
title,
|
|
174
|
-
knownFailing: metadata.failing,
|
|
175
|
-
todo: false
|
|
176
|
-
});
|
|
177
|
-
} else if (!metadata.skipped) {
|
|
178
|
-
this.tasks[metadata.type + (metadata.always ? 'Always' : '')].push(task);
|
|
179
|
-
}
|
|
172
|
+
this.snapshots.touch(title.value, metadata.taskIndex);
|
|
173
|
+
|
|
174
|
+
this.emit('stateChange', {
|
|
175
|
+
type: 'declared-test',
|
|
176
|
+
title: title.value,
|
|
177
|
+
knownFailing: metadata.failing,
|
|
178
|
+
todo: false,
|
|
179
|
+
});
|
|
180
|
+
} else if (!metadata.skipped) {
|
|
181
|
+
this.tasks[metadata.type + (metadata.always ? 'Always' : '')].push(task);
|
|
180
182
|
}
|
|
181
183
|
}
|
|
182
184
|
}, {
|
|
@@ -187,10 +189,33 @@ class Runner extends Emittery {
|
|
|
187
189
|
failing: false,
|
|
188
190
|
callback: false,
|
|
189
191
|
inline: false, // Set for attempt metadata created by `t.try()`
|
|
190
|
-
always: false
|
|
192
|
+
always: false,
|
|
191
193
|
}, meta);
|
|
192
194
|
}
|
|
193
195
|
|
|
196
|
+
get snapshots() {
|
|
197
|
+
if (this._snapshots) {
|
|
198
|
+
return this._snapshots;
|
|
199
|
+
}
|
|
200
|
+
|
|
201
|
+
// Lazy load not when the runner is instantiated but when snapshots are
|
|
202
|
+
// needed. This should be after the test file has been loaded and source
|
|
203
|
+
// maps are available.
|
|
204
|
+
const snapshots = loadSnapshots({
|
|
205
|
+
file: this.file,
|
|
206
|
+
fixedLocation: this.snapshotDir,
|
|
207
|
+
projectDir: this.projectDir,
|
|
208
|
+
recordNewSnapshots: this.recordNewSnapshots,
|
|
209
|
+
updating: this.updateSnapshots,
|
|
210
|
+
});
|
|
211
|
+
if (snapshots.snapPath !== undefined) {
|
|
212
|
+
this.emit('dependency', snapshots.snapPath);
|
|
213
|
+
}
|
|
214
|
+
|
|
215
|
+
this._snapshots = snapshots;
|
|
216
|
+
return snapshots;
|
|
217
|
+
}
|
|
218
|
+
|
|
194
219
|
compareTestSnapshot(options) {
|
|
195
220
|
return this.snapshots.compare(options);
|
|
196
221
|
}
|
|
@@ -199,8 +224,8 @@ class Runner extends Emittery {
|
|
|
199
224
|
return this.snapshots.skipSnapshot(options);
|
|
200
225
|
}
|
|
201
226
|
|
|
202
|
-
saveSnapshotState() {
|
|
203
|
-
return {touchedFiles: this.snapshots.save()};
|
|
227
|
+
async saveSnapshotState() {
|
|
228
|
+
return {touchedFiles: await this.snapshots.save()};
|
|
204
229
|
}
|
|
205
230
|
|
|
206
231
|
onRun(runnable) {
|
|
@@ -211,16 +236,6 @@ class Runner extends Emittery {
|
|
|
211
236
|
this.activeRunnables.delete(runnable);
|
|
212
237
|
}
|
|
213
238
|
|
|
214
|
-
attributeLeakedError(error) {
|
|
215
|
-
for (const runnable of this.activeRunnables) {
|
|
216
|
-
if (runnable.attributeLeakedError(error)) {
|
|
217
|
-
return true;
|
|
218
|
-
}
|
|
219
|
-
}
|
|
220
|
-
|
|
221
|
-
return false;
|
|
222
|
-
}
|
|
223
|
-
|
|
224
239
|
beforeExitHandler() {
|
|
225
240
|
for (const runnable of this.activeRunnables) {
|
|
226
241
|
runnable.finishDueToInactivity();
|
|
@@ -242,23 +257,23 @@ class Runner extends Emittery {
|
|
|
242
257
|
let waitForSerial = Promise.resolve();
|
|
243
258
|
await runnables.reduce((previous, runnable) => { // eslint-disable-line unicorn/no-array-reduce
|
|
244
259
|
if (runnable.metadata.serial || this.serial) {
|
|
245
|
-
waitForSerial = previous.then(() =>
|
|
260
|
+
waitForSerial = previous.then(() =>
|
|
246
261
|
// Serial runnables run as long as there was no previous failure, unless
|
|
247
262
|
// the runnable should always be run.
|
|
248
|
-
|
|
249
|
-
|
|
263
|
+
(allPassed || runnable.metadata.always) && runAndStoreResult(runnable),
|
|
264
|
+
);
|
|
250
265
|
return waitForSerial;
|
|
251
266
|
}
|
|
252
267
|
|
|
253
268
|
return Promise.all([
|
|
254
269
|
previous,
|
|
255
|
-
waitForSerial.then(() =>
|
|
270
|
+
waitForSerial.then(() =>
|
|
256
271
|
// Concurrent runnables are kicked off after the previous serial
|
|
257
272
|
// runnables have completed, as long as there was no previous failure
|
|
258
273
|
// (or if the runnable should always be run). One concurrent runnable's
|
|
259
274
|
// failure does not prevent the next runnable from running.
|
|
260
|
-
|
|
261
|
-
|
|
275
|
+
(allPassed || runnable.metadata.always) && runAndStoreResult(runnable),
|
|
276
|
+
),
|
|
262
277
|
]);
|
|
263
278
|
}, waitForSerial);
|
|
264
279
|
|
|
@@ -279,17 +294,17 @@ class Runner extends Emittery {
|
|
|
279
294
|
contextRef,
|
|
280
295
|
experiments: this.experiments,
|
|
281
296
|
failWithoutAssertions: false,
|
|
282
|
-
fn: task.args.length === 0
|
|
283
|
-
task.implementation
|
|
284
|
-
t => Reflect.apply(task.implementation, null, [t, ...task.args]),
|
|
297
|
+
fn: task.args.length === 0
|
|
298
|
+
? task.implementation
|
|
299
|
+
: t => Reflect.apply(task.implementation, null, [t, ...task.args]),
|
|
285
300
|
compareTestSnapshot: this.boundCompareTestSnapshot,
|
|
286
301
|
skipSnapshot: this.boundSkipSnapshot,
|
|
287
302
|
updateSnapshots: this.updateSnapshots,
|
|
288
303
|
metadata: task.metadata,
|
|
289
|
-
powerAssert: this.powerAssert,
|
|
290
304
|
title: `${task.title}${titleSuffix || ''}`,
|
|
291
305
|
isHook: true,
|
|
292
|
-
testPassed
|
|
306
|
+
testPassed,
|
|
307
|
+
notifyTimeoutUpdate: this.notifyTimeoutUpdate,
|
|
293
308
|
}));
|
|
294
309
|
const outcome = await this.runMultiple(hooks, this.serial);
|
|
295
310
|
for (const result of outcome.storedResults) {
|
|
@@ -298,7 +313,7 @@ class Runner extends Emittery {
|
|
|
298
313
|
type: 'hook-finished',
|
|
299
314
|
title: result.title,
|
|
300
315
|
duration: result.duration,
|
|
301
|
-
logs: result.logs
|
|
316
|
+
logs: result.logs,
|
|
302
317
|
});
|
|
303
318
|
} else {
|
|
304
319
|
this.emit('stateChange', {
|
|
@@ -306,7 +321,7 @@ class Runner extends Emittery {
|
|
|
306
321
|
title: result.title,
|
|
307
322
|
err: serializeError('Hook failure', true, result.error),
|
|
308
323
|
duration: result.duration,
|
|
309
|
-
logs: result.logs
|
|
324
|
+
logs: result.logs,
|
|
310
325
|
});
|
|
311
326
|
}
|
|
312
327
|
}
|
|
@@ -320,8 +335,8 @@ class Runner extends Emittery {
|
|
|
320
335
|
this.tasks.beforeEach,
|
|
321
336
|
contextRef,
|
|
322
337
|
{
|
|
323
|
-
titleSuffix: hookSuffix
|
|
324
|
-
}
|
|
338
|
+
titleSuffix: hookSuffix,
|
|
339
|
+
},
|
|
325
340
|
);
|
|
326
341
|
|
|
327
342
|
let testOk = false;
|
|
@@ -331,16 +346,16 @@ class Runner extends Emittery {
|
|
|
331
346
|
contextRef,
|
|
332
347
|
experiments: this.experiments,
|
|
333
348
|
failWithoutAssertions: this.failWithoutAssertions,
|
|
334
|
-
fn: task.args.length === 0
|
|
335
|
-
task.implementation
|
|
336
|
-
t => Reflect.apply(task.implementation, null, [t, ...task.args]),
|
|
349
|
+
fn: task.args.length === 0
|
|
350
|
+
? task.implementation
|
|
351
|
+
: t => Reflect.apply(task.implementation, null, [t, ...task.args]),
|
|
337
352
|
compareTestSnapshot: this.boundCompareTestSnapshot,
|
|
338
353
|
skipSnapshot: this.boundSkipSnapshot,
|
|
339
354
|
updateSnapshots: this.updateSnapshots,
|
|
340
355
|
metadata: task.metadata,
|
|
341
|
-
powerAssert: this.powerAssert,
|
|
342
356
|
title: task.title,
|
|
343
|
-
registerUniqueTitle: this.registerUniqueTitle
|
|
357
|
+
registerUniqueTitle: this.registerUniqueTitle,
|
|
358
|
+
notifyTimeoutUpdate: this.notifyTimeoutUpdate,
|
|
344
359
|
});
|
|
345
360
|
|
|
346
361
|
const result = await this.runSingle(test);
|
|
@@ -352,7 +367,7 @@ class Runner extends Emittery {
|
|
|
352
367
|
title: result.title,
|
|
353
368
|
duration: result.duration,
|
|
354
369
|
knownFailing: result.metadata.failing,
|
|
355
|
-
logs: result.logs
|
|
370
|
+
logs: result.logs,
|
|
356
371
|
});
|
|
357
372
|
|
|
358
373
|
hooksOk = await this.runHooks(
|
|
@@ -360,7 +375,7 @@ class Runner extends Emittery {
|
|
|
360
375
|
contextRef,
|
|
361
376
|
{
|
|
362
377
|
titleSuffix: hookSuffix,
|
|
363
|
-
testPassed: testOk
|
|
378
|
+
testPassed: testOk,
|
|
364
379
|
});
|
|
365
380
|
} else {
|
|
366
381
|
this.emit('stateChange', {
|
|
@@ -369,7 +384,7 @@ class Runner extends Emittery {
|
|
|
369
384
|
err: serializeError('Test failure', true, result.error, this.file),
|
|
370
385
|
duration: result.duration,
|
|
371
386
|
knownFailing: result.metadata.failing,
|
|
372
|
-
logs: result.logs
|
|
387
|
+
logs: result.logs,
|
|
373
388
|
});
|
|
374
389
|
// Don't run `afterEach` hooks if the test failed.
|
|
375
390
|
}
|
|
@@ -380,7 +395,7 @@ class Runner extends Emittery {
|
|
|
380
395
|
contextRef,
|
|
381
396
|
{
|
|
382
397
|
titleSuffix: hookSuffix,
|
|
383
|
-
testPassed: testOk
|
|
398
|
+
testPassed: testOk,
|
|
384
399
|
});
|
|
385
400
|
return alwaysOk && hooksOk && testOk;
|
|
386
401
|
}
|
|
@@ -404,7 +419,7 @@ class Runner extends Emittery {
|
|
|
404
419
|
title: task.title,
|
|
405
420
|
knownFailing: task.metadata.failing,
|
|
406
421
|
skip: task.metadata.skipped,
|
|
407
|
-
todo: false
|
|
422
|
+
todo: false,
|
|
408
423
|
});
|
|
409
424
|
|
|
410
425
|
if (task.metadata.skipped) {
|
|
@@ -430,7 +445,7 @@ class Runner extends Emittery {
|
|
|
430
445
|
title: task.title,
|
|
431
446
|
knownFailing: task.metadata.failing,
|
|
432
447
|
skip: task.metadata.skipped,
|
|
433
|
-
todo: false
|
|
448
|
+
todo: false,
|
|
434
449
|
});
|
|
435
450
|
|
|
436
451
|
if (task.metadata.skipped) {
|
|
@@ -456,7 +471,7 @@ class Runner extends Emittery {
|
|
|
456
471
|
title: task.title,
|
|
457
472
|
knownFailing: false,
|
|
458
473
|
skip: false,
|
|
459
|
-
todo: true
|
|
474
|
+
todo: true,
|
|
460
475
|
});
|
|
461
476
|
}
|
|
462
477
|
|
|
@@ -472,7 +487,7 @@ class Runner extends Emittery {
|
|
|
472
487
|
|
|
473
488
|
// Note that the hooks and tests always begin running asynchronously.
|
|
474
489
|
const beforePromise = this.runHooks(this.tasks.before, contextRef);
|
|
475
|
-
const serialPromise = beforePromise.then(beforeHooksOk => {
|
|
490
|
+
const serialPromise = beforePromise.then(beforeHooksOk => {
|
|
476
491
|
// Don't run tests if a `before` hook failed.
|
|
477
492
|
if (!beforeHooksOk) {
|
|
478
493
|
return false;
|
|
@@ -494,7 +509,7 @@ class Runner extends Emittery {
|
|
|
494
509
|
return this.runTest(task, contextRef.copy());
|
|
495
510
|
}, true);
|
|
496
511
|
});
|
|
497
|
-
const concurrentPromise = Promise.all([beforePromise, serialPromise]).then(async ([beforeHooksOk, serialOk]) => {
|
|
512
|
+
const concurrentPromise = Promise.all([beforePromise, serialPromise]).then(async ([beforeHooksOk, serialOk]) => {
|
|
498
513
|
// Don't run tests if a `before` hook failed, or if `failFast` is enabled
|
|
499
514
|
// and a previous serial test failed.
|
|
500
515
|
if (!beforeHooksOk || (!serialOk && this.failFast)) {
|
|
@@ -508,9 +523,7 @@ class Runner extends Emittery {
|
|
|
508
523
|
|
|
509
524
|
// If a concurrent test fails, even if `failFast` is enabled it won't
|
|
510
525
|
// stop other concurrent tests from running.
|
|
511
|
-
const allOkays = await Promise.all(concurrentTests.map(task =>
|
|
512
|
-
return this.runTest(task, contextRef.copy());
|
|
513
|
-
}));
|
|
526
|
+
const allOkays = await Promise.all(concurrentTests.map(task => this.runTest(task, contextRef.copy())));
|
|
514
527
|
return allOkays.every(ok => ok);
|
|
515
528
|
});
|
|
516
529
|
|
|
@@ -537,5 +550,3 @@ class Runner extends Emittery {
|
|
|
537
550
|
this.interrupted = true;
|
|
538
551
|
}
|
|
539
552
|
}
|
|
540
|
-
|
|
541
|
-
module.exports = Runner;
|
package/lib/scheduler.js
CHANGED
|
@@ -1,47 +1,53 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
const writeFileAtomic = require('write-file-atomic');
|
|
4
|
-
const isCi = require('./is-ci');
|
|
1
|
+
import fs from 'node:fs';
|
|
2
|
+
import path from 'node:path';
|
|
5
3
|
|
|
6
|
-
|
|
4
|
+
import writeFileAtomic from 'write-file-atomic';
|
|
7
5
|
|
|
8
|
-
|
|
9
|
-
if (isCi || !cacheDir) {
|
|
10
|
-
return;
|
|
11
|
-
}
|
|
6
|
+
import isCi from './is-ci.js';
|
|
12
7
|
|
|
13
|
-
|
|
14
|
-
writeFileAtomic.sync(path.join(cacheDir, FILENAME), JSON.stringify(runStatus.getFailedTestFiles()));
|
|
15
|
-
} catch {}
|
|
16
|
-
};
|
|
8
|
+
const FILENAME = 'failing-tests.json';
|
|
17
9
|
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
}
|
|
23
|
-
|
|
24
|
-
const filePath = path.join(cacheDir, FILENAME);
|
|
25
|
-
let failedTestFiles;
|
|
26
|
-
try {
|
|
27
|
-
failedTestFiles = JSON.parse(fs.readFileSync(filePath));
|
|
28
|
-
} catch {
|
|
29
|
-
return selectedFiles;
|
|
30
|
-
}
|
|
31
|
-
|
|
32
|
-
return [...selectedFiles].sort((f, s) => {
|
|
33
|
-
if (failedTestFiles.includes(f) && failedTestFiles.includes(s)) {
|
|
34
|
-
return 0;
|
|
10
|
+
const scheduler = {
|
|
11
|
+
storeFailedTestFiles(runStatus, cacheDir) {
|
|
12
|
+
if (isCi || !cacheDir) {
|
|
13
|
+
return;
|
|
35
14
|
}
|
|
36
15
|
|
|
37
|
-
|
|
38
|
-
|
|
16
|
+
try {
|
|
17
|
+
writeFileAtomic.sync(path.join(cacheDir, FILENAME), JSON.stringify(runStatus.getFailedTestFiles()));
|
|
18
|
+
} catch {}
|
|
19
|
+
},
|
|
20
|
+
|
|
21
|
+
// Order test-files, so that files with failing tests come first
|
|
22
|
+
failingTestsFirst(selectedFiles, cacheDir, cacheEnabled) {
|
|
23
|
+
if (isCi || cacheEnabled === false) {
|
|
24
|
+
return selectedFiles;
|
|
39
25
|
}
|
|
40
26
|
|
|
41
|
-
|
|
42
|
-
|
|
27
|
+
const filePath = path.join(cacheDir, FILENAME);
|
|
28
|
+
let failedTestFiles;
|
|
29
|
+
try {
|
|
30
|
+
failedTestFiles = JSON.parse(fs.readFileSync(filePath));
|
|
31
|
+
} catch {
|
|
32
|
+
return selectedFiles;
|
|
43
33
|
}
|
|
44
34
|
|
|
45
|
-
return
|
|
46
|
-
|
|
35
|
+
return [...selectedFiles].sort((f, s) => {
|
|
36
|
+
if (failedTestFiles.includes(f) && failedTestFiles.includes(s)) {
|
|
37
|
+
return 0;
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
if (failedTestFiles.includes(f)) {
|
|
41
|
+
return -1;
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
if (failedTestFiles.includes(s)) {
|
|
45
|
+
return 1;
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
return 0;
|
|
49
|
+
});
|
|
50
|
+
},
|
|
47
51
|
};
|
|
52
|
+
|
|
53
|
+
export default scheduler;
|