ava 3.12.1 → 3.13.0
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/lib/api.js +8 -1
- package/lib/assert.js +11 -3
- package/lib/cli.js +5 -2
- package/lib/fork.js +84 -20
- package/lib/ipc-flow-control.js +39 -0
- package/lib/load-config.js +8 -2
- package/lib/plugin-support/shared-worker-loader.js +252 -0
- package/lib/plugin-support/shared-workers.js +138 -0
- package/lib/reporters/default.js +33 -2
- package/lib/run-status.js +5 -0
- package/lib/runner.js +3 -0
- package/lib/test.js +2 -1
- package/lib/worker/ipc.js +174 -29
- package/lib/worker/plugin.js +121 -0
- package/lib/worker/subprocess.js +24 -1
- package/package.json +16 -14
- package/plugin.d.ts +81 -0
- package/plugin.js +9 -0
package/lib/reporters/default.js
CHANGED
|
@@ -195,6 +195,7 @@ class Reporter {
|
|
|
195
195
|
this.internalErrors = [];
|
|
196
196
|
this.knownFailures = [];
|
|
197
197
|
this.lineNumberErrors = [];
|
|
198
|
+
this.sharedWorkerErrors = [];
|
|
198
199
|
this.uncaughtExceptions = [];
|
|
199
200
|
this.unhandledRejections = [];
|
|
200
201
|
this.unsavedSnapshots = [];
|
|
@@ -340,6 +341,19 @@ class Reporter {
|
|
|
340
341
|
break;
|
|
341
342
|
}
|
|
342
343
|
|
|
344
|
+
case 'shared-worker-error': {
|
|
345
|
+
this.sharedWorkerErrors.push(event);
|
|
346
|
+
|
|
347
|
+
if (this.verbose) {
|
|
348
|
+
this.lineWriter.ensureEmptyLine();
|
|
349
|
+
this.lineWriter.writeLine(colors.error(`${figures.cross} Error in shared worker`));
|
|
350
|
+
this.lineWriter.writeLine();
|
|
351
|
+
this.writeErr(event);
|
|
352
|
+
}
|
|
353
|
+
|
|
354
|
+
break;
|
|
355
|
+
}
|
|
356
|
+
|
|
343
357
|
case 'snapshot-error':
|
|
344
358
|
this.unsavedSnapshots.push(event);
|
|
345
359
|
break;
|
|
@@ -710,7 +724,7 @@ class Reporter {
|
|
|
710
724
|
}
|
|
711
725
|
|
|
712
726
|
if (this.failures.length > 0) {
|
|
713
|
-
const writeTrailingLines = this.internalErrors.length > 0 || this.uncaughtExceptions.length > 0 || this.unhandledRejections.length > 0;
|
|
727
|
+
const writeTrailingLines = this.internalErrors.length > 0 || this.sharedWorkerErrors.length > 0 || this.uncaughtExceptions.length > 0 || this.unhandledRejections.length > 0;
|
|
714
728
|
|
|
715
729
|
const lastFailure = this.failures[this.failures.length - 1];
|
|
716
730
|
for (const event of this.failures) {
|
|
@@ -734,7 +748,7 @@ class Reporter {
|
|
|
734
748
|
|
|
735
749
|
if (!this.verbose) {
|
|
736
750
|
if (this.internalErrors.length > 0) {
|
|
737
|
-
const writeTrailingLines = this.uncaughtExceptions.length > 0 || this.unhandledRejections.length > 0;
|
|
751
|
+
const writeTrailingLines = this.sharedWorkerErrors.length > 0 || this.uncaughtExceptions.length > 0 || this.unhandledRejections.length > 0;
|
|
738
752
|
|
|
739
753
|
const last = this.internalErrors[this.internalErrors.length - 1];
|
|
740
754
|
for (const event of this.internalErrors) {
|
|
@@ -756,6 +770,23 @@ class Reporter {
|
|
|
756
770
|
}
|
|
757
771
|
}
|
|
758
772
|
|
|
773
|
+
if (this.sharedWorkerErrors.length > 0) {
|
|
774
|
+
const writeTrailingLines = this.uncaughtExceptions.length > 0 || this.unhandledRejections.length > 0;
|
|
775
|
+
|
|
776
|
+
const last = this.sharedWorkerErrors[this.sharedWorkerErrors.length - 1];
|
|
777
|
+
for (const evt of this.sharedWorkerErrors) {
|
|
778
|
+
this.lineWriter.writeLine(colors.error(`${figures.cross} Error in shared worker`));
|
|
779
|
+
this.lineWriter.writeLine();
|
|
780
|
+
this.writeErr(evt.err);
|
|
781
|
+
if (evt !== last || writeTrailingLines) {
|
|
782
|
+
this.lineWriter.writeLine();
|
|
783
|
+
this.lineWriter.writeLine();
|
|
784
|
+
}
|
|
785
|
+
|
|
786
|
+
wroteSomething = true;
|
|
787
|
+
}
|
|
788
|
+
}
|
|
789
|
+
|
|
759
790
|
if (this.uncaughtExceptions.length > 0) {
|
|
760
791
|
const writeTrailingLines = this.unhandledRejections.length > 0;
|
|
761
792
|
|
package/lib/run-status.js
CHANGED
|
@@ -27,6 +27,7 @@ class RunStatus extends Emittery {
|
|
|
27
27
|
passedKnownFailingTests: 0,
|
|
28
28
|
passedTests: 0,
|
|
29
29
|
selectedTests: 0,
|
|
30
|
+
sharedWorkerErrors: 0,
|
|
30
31
|
skippedTests: 0,
|
|
31
32
|
timeouts: 0,
|
|
32
33
|
todoTests: 0,
|
|
@@ -93,6 +94,9 @@ class RunStatus extends Emittery {
|
|
|
93
94
|
this.addPendingTest(event);
|
|
94
95
|
}
|
|
95
96
|
|
|
97
|
+
break;
|
|
98
|
+
case 'shared-worker-error':
|
|
99
|
+
stats.sharedWorkerErrors++;
|
|
96
100
|
break;
|
|
97
101
|
case 'test-failed':
|
|
98
102
|
stats.failedTests++;
|
|
@@ -164,6 +168,7 @@ class RunStatus extends Emittery {
|
|
|
164
168
|
this.stats.failedHooks > 0 ||
|
|
165
169
|
this.stats.failedTests > 0 ||
|
|
166
170
|
this.stats.failedWorkers > 0 ||
|
|
171
|
+
this.stats.sharedWorkerErrors > 0 ||
|
|
167
172
|
this.stats.timeouts > 0 ||
|
|
168
173
|
this.stats.uncaughtExceptions > 0 ||
|
|
169
174
|
this.stats.unhandledRejections > 0
|
package/lib/runner.js
CHANGED
|
@@ -42,6 +42,7 @@ class Runner extends Emittery {
|
|
|
42
42
|
serial: [],
|
|
43
43
|
todo: []
|
|
44
44
|
};
|
|
45
|
+
this.waitForReady = [];
|
|
45
46
|
|
|
46
47
|
const uniqueTestTitles = new Set();
|
|
47
48
|
this.registerUniqueTitle = title => {
|
|
@@ -444,6 +445,8 @@ class Runner extends Emittery {
|
|
|
444
445
|
});
|
|
445
446
|
}
|
|
446
447
|
|
|
448
|
+
await Promise.all(this.waitForReady);
|
|
449
|
+
|
|
447
450
|
if (concurrentTests.length === 0 && serialTests.length === 0) {
|
|
448
451
|
this.emit('finish');
|
|
449
452
|
// Don't run any hooks if there are no tests to run.
|
package/lib/test.js
CHANGED
|
@@ -187,7 +187,8 @@ class ExecutionContext extends assert.Assertions {
|
|
|
187
187
|
}
|
|
188
188
|
|
|
189
189
|
get passed() {
|
|
190
|
-
|
|
190
|
+
const test = testMap.get(this);
|
|
191
|
+
return test.isHook ? test.testPassed : !test.assertError;
|
|
191
192
|
}
|
|
192
193
|
|
|
193
194
|
_throwsArgStart(assertion, file, line) {
|
package/lib/worker/ipc.js
CHANGED
|
@@ -1,50 +1,42 @@
|
|
|
1
1
|
'use strict';
|
|
2
|
-
const
|
|
2
|
+
const events = require('events');
|
|
3
|
+
const pEvent = require('p-event');
|
|
4
|
+
const {controlFlow} = require('../ipc-flow-control');
|
|
5
|
+
const {get: getOptions} = require('./options');
|
|
3
6
|
|
|
4
|
-
const
|
|
5
|
-
process.on('message', message => {
|
|
6
|
-
if (!message.ava) {
|
|
7
|
-
return;
|
|
8
|
-
}
|
|
9
|
-
|
|
10
|
-
switch (message.ava.type) {
|
|
11
|
-
case 'options':
|
|
12
|
-
emitter.emit('options', message.ava.options);
|
|
13
|
-
break;
|
|
14
|
-
case 'peer-failed':
|
|
15
|
-
emitter.emit('peerFailed');
|
|
16
|
-
break;
|
|
17
|
-
case 'pong':
|
|
18
|
-
emitter.emit('pong');
|
|
19
|
-
break;
|
|
20
|
-
default:
|
|
21
|
-
break;
|
|
22
|
-
}
|
|
23
|
-
});
|
|
7
|
+
const selectAvaMessage = type => message => message.ava && message.ava.type === type;
|
|
24
8
|
|
|
25
|
-
exports.options =
|
|
26
|
-
exports.peerFailed =
|
|
9
|
+
exports.options = pEvent(process, 'message', selectAvaMessage('options')).then(message => message.ava.options);
|
|
10
|
+
exports.peerFailed = pEvent(process, 'message', selectAvaMessage('peer-failed'));
|
|
27
11
|
|
|
12
|
+
const bufferedSend = controlFlow(process);
|
|
28
13
|
function send(evt) {
|
|
29
|
-
|
|
30
|
-
process.send({ava: evt});
|
|
31
|
-
}
|
|
14
|
+
bufferedSend({ava: evt});
|
|
32
15
|
}
|
|
33
16
|
|
|
34
17
|
exports.send = send;
|
|
35
18
|
|
|
19
|
+
let refs = 1;
|
|
20
|
+
function ref() {
|
|
21
|
+
if (++refs === 1) {
|
|
22
|
+
process.channel.ref();
|
|
23
|
+
}
|
|
24
|
+
}
|
|
25
|
+
|
|
36
26
|
function unref() {
|
|
37
|
-
|
|
27
|
+
if (refs > 0 && --refs === 0) {
|
|
28
|
+
process.channel.unref();
|
|
29
|
+
}
|
|
38
30
|
}
|
|
39
31
|
|
|
40
32
|
exports.unref = unref;
|
|
41
33
|
|
|
42
34
|
let pendingPings = Promise.resolve();
|
|
43
35
|
async function flush() {
|
|
44
|
-
|
|
36
|
+
ref();
|
|
45
37
|
const promise = pendingPings.then(async () => { // eslint-disable-line promise/prefer-await-to-then
|
|
46
38
|
send({type: 'ping'});
|
|
47
|
-
await
|
|
39
|
+
await pEvent(process, 'message', selectAvaMessage('pong'));
|
|
48
40
|
if (promise === pendingPings) {
|
|
49
41
|
unref();
|
|
50
42
|
}
|
|
@@ -54,3 +46,156 @@ async function flush() {
|
|
|
54
46
|
}
|
|
55
47
|
|
|
56
48
|
exports.flush = flush;
|
|
49
|
+
|
|
50
|
+
let channelCounter = 0;
|
|
51
|
+
let messageCounter = 0;
|
|
52
|
+
|
|
53
|
+
const channelEmitters = new Map();
|
|
54
|
+
function createChannelEmitter(channelId) {
|
|
55
|
+
if (channelEmitters.size === 0) {
|
|
56
|
+
process.on('message', message => {
|
|
57
|
+
if (!message.ava) {
|
|
58
|
+
return;
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
const {channelId, type, ...payload} = message.ava;
|
|
62
|
+
if (
|
|
63
|
+
type === 'shared-worker-error' ||
|
|
64
|
+
type === 'shared-worker-message' ||
|
|
65
|
+
type === 'shared-worker-ready'
|
|
66
|
+
) {
|
|
67
|
+
const emitter = channelEmitters.get(channelId);
|
|
68
|
+
if (emitter !== undefined) {
|
|
69
|
+
emitter.emit(type, payload);
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
});
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
const emitter = new events.EventEmitter();
|
|
76
|
+
channelEmitters.set(channelId, emitter);
|
|
77
|
+
return [emitter, () => channelEmitters.delete(channelId)];
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
function registerSharedWorker(filename, initialData) {
|
|
81
|
+
const channelId = `${getOptions().forkId}/channel/${++channelCounter}`;
|
|
82
|
+
const [channelEmitter, unsubscribe] = createChannelEmitter(channelId);
|
|
83
|
+
|
|
84
|
+
let forcedUnref = false;
|
|
85
|
+
let refs = 0;
|
|
86
|
+
const forceUnref = () => {
|
|
87
|
+
if (forcedUnref) {
|
|
88
|
+
return;
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
forcedUnref = true;
|
|
92
|
+
if (refs > 0) {
|
|
93
|
+
unref();
|
|
94
|
+
}
|
|
95
|
+
};
|
|
96
|
+
|
|
97
|
+
const refChannel = () => {
|
|
98
|
+
if (!forcedUnref && ++refs === 1) {
|
|
99
|
+
ref();
|
|
100
|
+
}
|
|
101
|
+
};
|
|
102
|
+
|
|
103
|
+
const unrefChannel = () => {
|
|
104
|
+
if (!forcedUnref && refs > 0 && --refs === 0) {
|
|
105
|
+
unref();
|
|
106
|
+
}
|
|
107
|
+
};
|
|
108
|
+
|
|
109
|
+
send({
|
|
110
|
+
type: 'shared-worker-connect',
|
|
111
|
+
channelId,
|
|
112
|
+
filename,
|
|
113
|
+
initialData
|
|
114
|
+
});
|
|
115
|
+
|
|
116
|
+
let currentlyAvailable = false;
|
|
117
|
+
let error = null;
|
|
118
|
+
|
|
119
|
+
refChannel();
|
|
120
|
+
const ready = pEvent(channelEmitter, 'shared-worker-ready').then(() => { // eslint-disable-line promise/prefer-await-to-then
|
|
121
|
+
currentlyAvailable = error === null;
|
|
122
|
+
}).finally(unrefChannel);
|
|
123
|
+
|
|
124
|
+
const messageEmitters = new Set();
|
|
125
|
+
const handleMessage = message => {
|
|
126
|
+
// Wait for a turn of the event loop, to allow new subscriptions to be set
|
|
127
|
+
// up in response to the previous message.
|
|
128
|
+
setImmediate(() => {
|
|
129
|
+
for (const emitter of messageEmitters) {
|
|
130
|
+
emitter.emit('message', message);
|
|
131
|
+
}
|
|
132
|
+
});
|
|
133
|
+
};
|
|
134
|
+
|
|
135
|
+
channelEmitter.on('shared-worker-message', handleMessage);
|
|
136
|
+
|
|
137
|
+
pEvent(channelEmitter, 'shared-worker-error').then(() => { // eslint-disable-line promise/prefer-await-to-then
|
|
138
|
+
unsubscribe();
|
|
139
|
+
forceUnref();
|
|
140
|
+
|
|
141
|
+
error = new Error('The shared worker is no longer available');
|
|
142
|
+
currentlyAvailable = false;
|
|
143
|
+
for (const emitter of messageEmitters) {
|
|
144
|
+
emitter.emit('error', error);
|
|
145
|
+
}
|
|
146
|
+
});
|
|
147
|
+
|
|
148
|
+
return {
|
|
149
|
+
forceUnref,
|
|
150
|
+
ready,
|
|
151
|
+
channel: {
|
|
152
|
+
available: ready,
|
|
153
|
+
|
|
154
|
+
get currentlyAvailable() {
|
|
155
|
+
return currentlyAvailable;
|
|
156
|
+
},
|
|
157
|
+
|
|
158
|
+
async * receive() {
|
|
159
|
+
if (error !== null) {
|
|
160
|
+
throw error;
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
const emitter = new events.EventEmitter();
|
|
164
|
+
messageEmitters.add(emitter);
|
|
165
|
+
try {
|
|
166
|
+
refChannel();
|
|
167
|
+
for await (const [message] of events.on(emitter, 'message')) {
|
|
168
|
+
yield message;
|
|
169
|
+
}
|
|
170
|
+
} finally {
|
|
171
|
+
unrefChannel();
|
|
172
|
+
messageEmitters.delete(emitter);
|
|
173
|
+
}
|
|
174
|
+
},
|
|
175
|
+
|
|
176
|
+
post(serializedData, replyTo) {
|
|
177
|
+
if (error !== null) {
|
|
178
|
+
throw error;
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
if (!currentlyAvailable) {
|
|
182
|
+
throw new Error('Shared worker is not yet available');
|
|
183
|
+
}
|
|
184
|
+
|
|
185
|
+
const messageId = `${channelId}/message/${++messageCounter}`;
|
|
186
|
+
send({
|
|
187
|
+
type: 'shared-worker-message',
|
|
188
|
+
channelId,
|
|
189
|
+
messageId,
|
|
190
|
+
replyTo,
|
|
191
|
+
serializedData
|
|
192
|
+
});
|
|
193
|
+
|
|
194
|
+
return messageId;
|
|
195
|
+
}
|
|
196
|
+
}
|
|
197
|
+
};
|
|
198
|
+
}
|
|
199
|
+
|
|
200
|
+
exports.registerSharedWorker = registerSharedWorker;
|
|
201
|
+
|
|
@@ -0,0 +1,121 @@
|
|
|
1
|
+
const v8 = require('v8');
|
|
2
|
+
const pkg = require('../../package.json');
|
|
3
|
+
const subprocess = require('./subprocess');
|
|
4
|
+
const options = require('./options');
|
|
5
|
+
|
|
6
|
+
const workers = new Map();
|
|
7
|
+
const workerTeardownFns = new WeakMap();
|
|
8
|
+
|
|
9
|
+
function createSharedWorker(filename, initialData, teardown) {
|
|
10
|
+
const channel = subprocess.registerSharedWorker(filename, initialData, teardown);
|
|
11
|
+
|
|
12
|
+
class ReceivedMessage {
|
|
13
|
+
constructor(id, serializedData) {
|
|
14
|
+
this.id = id;
|
|
15
|
+
this.data = v8.deserialize(new Uint8Array(serializedData));
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
reply(data) {
|
|
19
|
+
return publishMessage(data, this.id);
|
|
20
|
+
}
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
// Ensure that, no matter how often it's received, we have a stable message
|
|
24
|
+
// object.
|
|
25
|
+
const messageCache = new WeakMap();
|
|
26
|
+
async function * receiveMessages(replyTo) {
|
|
27
|
+
for await (const evt of channel.receive()) {
|
|
28
|
+
if (replyTo === undefined && evt.replyTo !== undefined) {
|
|
29
|
+
continue;
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
if (replyTo !== undefined && evt.replyTo !== replyTo) {
|
|
33
|
+
continue;
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
let message = messageCache.get(evt);
|
|
37
|
+
if (message === undefined) {
|
|
38
|
+
message = new ReceivedMessage(evt.messageId, evt.serializedData);
|
|
39
|
+
messageCache.set(evt, message);
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
yield message;
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
function publishMessage(data, replyTo) {
|
|
47
|
+
const id = channel.post([...v8.serialize(data)], replyTo);
|
|
48
|
+
|
|
49
|
+
return {
|
|
50
|
+
id,
|
|
51
|
+
async * replies() {
|
|
52
|
+
yield * receiveMessages(id);
|
|
53
|
+
}
|
|
54
|
+
};
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
return {
|
|
58
|
+
available: channel.available,
|
|
59
|
+
protocol: 'experimental',
|
|
60
|
+
|
|
61
|
+
get currentlyAvailable() {
|
|
62
|
+
return channel.currentlyAvailable;
|
|
63
|
+
},
|
|
64
|
+
|
|
65
|
+
publish(data) {
|
|
66
|
+
return publishMessage(data);
|
|
67
|
+
},
|
|
68
|
+
|
|
69
|
+
async * subscribe() {
|
|
70
|
+
yield * receiveMessages();
|
|
71
|
+
}
|
|
72
|
+
};
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
const supportsSharedWorkers = process.versions.node >= '12.17.0';
|
|
76
|
+
|
|
77
|
+
function registerSharedWorker({
|
|
78
|
+
filename,
|
|
79
|
+
initialData,
|
|
80
|
+
supportedProtocols,
|
|
81
|
+
teardown
|
|
82
|
+
}) {
|
|
83
|
+
if (!options.get().experiments.sharedWorkers) {
|
|
84
|
+
throw new Error('Shared workers are experimental. Opt in to them in your AVA configuration');
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
if (!supportsSharedWorkers) {
|
|
88
|
+
throw new Error('Shared workers require Node.js 12.17 or newer');
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
if (!supportedProtocols.includes('experimental')) {
|
|
92
|
+
throw new Error(`This version of AVA (${pkg.version}) does not support any of the desired shared worker protocols: ${supportedProtocols.join()}`);
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
let worker = workers.get(filename);
|
|
96
|
+
if (worker === undefined) {
|
|
97
|
+
worker = createSharedWorker(filename, initialData, async () => {
|
|
98
|
+
// Run possibly asynchronous teardown functions serially, in reverse
|
|
99
|
+
// order. Any error will crash the worker.
|
|
100
|
+
const teardownFns = workerTeardownFns.get(worker);
|
|
101
|
+
if (teardownFns !== undefined) {
|
|
102
|
+
for await (const fn of [...teardownFns].reverse()) {
|
|
103
|
+
await fn();
|
|
104
|
+
}
|
|
105
|
+
}
|
|
106
|
+
});
|
|
107
|
+
workers.set(filename, worker);
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
if (teardown !== undefined) {
|
|
111
|
+
if (workerTeardownFns.has(worker)) {
|
|
112
|
+
workerTeardownFns.get(worker).push(teardown);
|
|
113
|
+
} else {
|
|
114
|
+
workerTeardownFns.set(worker, [teardown]);
|
|
115
|
+
}
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
return worker;
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
exports.registerSharedWorker = registerSharedWorker;
|
package/lib/worker/subprocess.js
CHANGED
|
@@ -32,6 +32,8 @@ ipc.options.then(async options => {
|
|
|
32
32
|
const dependencyTracking = require('./dependency-tracker');
|
|
33
33
|
const lineNumberSelection = require('./line-numbers');
|
|
34
34
|
|
|
35
|
+
const sharedWorkerTeardowns = [];
|
|
36
|
+
|
|
35
37
|
async function exit(code) {
|
|
36
38
|
if (!process.exitCode) {
|
|
37
39
|
process.exitCode = code;
|
|
@@ -89,7 +91,7 @@ ipc.options.then(async options => {
|
|
|
89
91
|
exit(1);
|
|
90
92
|
});
|
|
91
93
|
|
|
92
|
-
runner.on('finish', () => {
|
|
94
|
+
runner.on('finish', async () => {
|
|
93
95
|
try {
|
|
94
96
|
const {cannotSave, touchedFiles} = runner.saveSnapshotState();
|
|
95
97
|
if (cannotSave) {
|
|
@@ -103,6 +105,14 @@ ipc.options.then(async options => {
|
|
|
103
105
|
return;
|
|
104
106
|
}
|
|
105
107
|
|
|
108
|
+
try {
|
|
109
|
+
await Promise.all(sharedWorkerTeardowns.map(fn => fn()));
|
|
110
|
+
} catch (error) {
|
|
111
|
+
ipc.send({type: 'uncaught-exception', err: serializeError('Shared worker teardown error', false, error, runner.file)});
|
|
112
|
+
exit(1);
|
|
113
|
+
return;
|
|
114
|
+
}
|
|
115
|
+
|
|
106
116
|
nowAndTimers.setImmediate(() => {
|
|
107
117
|
currentlyUnhandled()
|
|
108
118
|
.filter(rejection => !attributedRejections.has(rejection.promise))
|
|
@@ -129,6 +139,19 @@ ipc.options.then(async options => {
|
|
|
129
139
|
return runner;
|
|
130
140
|
};
|
|
131
141
|
|
|
142
|
+
exports.registerSharedWorker = (filename, initialData, teardown) => {
|
|
143
|
+
const {channel, forceUnref, ready} = ipc.registerSharedWorker(filename, initialData);
|
|
144
|
+
runner.waitForReady.push(ready);
|
|
145
|
+
sharedWorkerTeardowns.push(async () => {
|
|
146
|
+
try {
|
|
147
|
+
await teardown();
|
|
148
|
+
} finally {
|
|
149
|
+
forceUnref();
|
|
150
|
+
}
|
|
151
|
+
});
|
|
152
|
+
return channel;
|
|
153
|
+
};
|
|
154
|
+
|
|
132
155
|
// Store value to prevent required modules from modifying it.
|
|
133
156
|
const testPath = options.file;
|
|
134
157
|
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "ava",
|
|
3
|
-
"version": "3.
|
|
3
|
+
"version": "3.13.0",
|
|
4
4
|
"description": "Node.js test runner that lets you develop with confidence.",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"repository": "avajs/ava",
|
|
@@ -16,7 +16,8 @@
|
|
|
16
16
|
"lib",
|
|
17
17
|
"*.js",
|
|
18
18
|
"!*.config.js",
|
|
19
|
-
"index.d.ts"
|
|
19
|
+
"index.d.ts",
|
|
20
|
+
"*.d.ts"
|
|
20
21
|
],
|
|
21
22
|
"keywords": [
|
|
22
23
|
"🦄",
|
|
@@ -75,8 +76,8 @@
|
|
|
75
76
|
"concordance": "^5.0.1",
|
|
76
77
|
"convert-source-map": "^1.7.0",
|
|
77
78
|
"currently-unhandled": "^0.4.1",
|
|
78
|
-
"debug": "^4.
|
|
79
|
-
"del": "^
|
|
79
|
+
"debug": "^4.2.0",
|
|
80
|
+
"del": "^6.0.0",
|
|
80
81
|
"emittery": "^0.7.1",
|
|
81
82
|
"equal-length": "^1.0.0",
|
|
82
83
|
"figures": "^3.2.0",
|
|
@@ -85,19 +86,20 @@
|
|
|
85
86
|
"import-local": "^3.0.2",
|
|
86
87
|
"indent-string": "^4.0.0",
|
|
87
88
|
"is-error": "^2.2.2",
|
|
88
|
-
"is-plain-object": "^
|
|
89
|
+
"is-plain-object": "^5.0.0",
|
|
89
90
|
"is-promise": "^4.0.0",
|
|
90
91
|
"lodash": "^4.17.20",
|
|
91
92
|
"matcher": "^3.0.0",
|
|
92
93
|
"md5-hex": "^3.0.1",
|
|
93
|
-
"mem": "^6.1.
|
|
94
|
+
"mem": "^6.1.1",
|
|
94
95
|
"ms": "^2.1.2",
|
|
95
|
-
"ora": "^5.
|
|
96
|
+
"ora": "^5.1.0",
|
|
97
|
+
"p-event": "^4.2.0",
|
|
96
98
|
"p-map": "^4.0.0",
|
|
97
99
|
"picomatch": "^2.2.2",
|
|
98
100
|
"pkg-conf": "^3.1.0",
|
|
99
101
|
"plur": "^4.0.0",
|
|
100
|
-
"pretty-ms": "^7.0.
|
|
102
|
+
"pretty-ms": "^7.0.1",
|
|
101
103
|
"read-pkg": "^5.2.0",
|
|
102
104
|
"resolve-cwd": "^3.0.0",
|
|
103
105
|
"slash": "^3.0.0",
|
|
@@ -109,7 +111,7 @@
|
|
|
109
111
|
"trim-off-newlines": "^1.0.1",
|
|
110
112
|
"update-notifier": "^4.1.1",
|
|
111
113
|
"write-file-atomic": "^3.0.3",
|
|
112
|
-
"yargs": "^
|
|
114
|
+
"yargs": "^16.0.3"
|
|
113
115
|
},
|
|
114
116
|
"devDependencies": {
|
|
115
117
|
"@ava/babel": "^1.0.1",
|
|
@@ -117,12 +119,12 @@
|
|
|
117
119
|
"@babel/plugin-proposal-do-expressions": "^7.10.4",
|
|
118
120
|
"@sinonjs/fake-timers": "^6.0.1",
|
|
119
121
|
"ansi-escapes": "^4.3.1",
|
|
120
|
-
"c8": "^7.3.
|
|
122
|
+
"c8": "^7.3.1",
|
|
121
123
|
"delay": "^4.4.0",
|
|
122
124
|
"esm": "^3.2.25",
|
|
123
125
|
"execa": "^4.0.3",
|
|
124
126
|
"get-stream": "^6.0.0",
|
|
125
|
-
"
|
|
127
|
+
"it-first": "^1.0.4",
|
|
126
128
|
"proxyquire": "^2.1.3",
|
|
127
129
|
"react": "^16.13.1",
|
|
128
130
|
"react-test-renderer": "^16.13.1",
|
|
@@ -131,11 +133,11 @@
|
|
|
131
133
|
"source-map-fixtures": "^2.1.0",
|
|
132
134
|
"tap": "^14.10.8",
|
|
133
135
|
"temp-write": "^4.0.0",
|
|
134
|
-
"tempy": "^0.
|
|
136
|
+
"tempy": "^0.7.1",
|
|
135
137
|
"touch": "^3.1.0",
|
|
136
138
|
"tsd": "^0.13.1",
|
|
137
|
-
"typescript": "^
|
|
138
|
-
"xo": "^0.33.
|
|
139
|
+
"typescript": "^4.0.3",
|
|
140
|
+
"xo": "^0.33.1",
|
|
139
141
|
"zen-observable": "^0.8.15"
|
|
140
142
|
}
|
|
141
143
|
}
|
package/plugin.d.ts
ADDED
|
@@ -0,0 +1,81 @@
|
|
|
1
|
+
export namespace SharedWorker {
|
|
2
|
+
export type ProtocolIdentifier = 'experimental';
|
|
3
|
+
|
|
4
|
+
/* eslint-disable @typescript-eslint/method-signature-style */
|
|
5
|
+
export type FactoryOptions = {
|
|
6
|
+
negotiateProtocol <Data = unknown>(supported: readonly ['experimental']): Experimental.Protocol<Data>;
|
|
7
|
+
// Add overloads for additional protocols.
|
|
8
|
+
};
|
|
9
|
+
/* eslint-enable @typescript-eslint/method-signature-style */
|
|
10
|
+
|
|
11
|
+
export type Factory = (options: FactoryOptions) => void;
|
|
12
|
+
|
|
13
|
+
export namespace Experimental {
|
|
14
|
+
export type Protocol<Data = unknown> = {
|
|
15
|
+
readonly initialData: Data;
|
|
16
|
+
readonly protocol: 'experimental';
|
|
17
|
+
broadcast: (data: Data) => BroadcastMessage<Data>;
|
|
18
|
+
ready: () => Protocol<Data>;
|
|
19
|
+
subscribe: () => AsyncIterableIterator<ReceivedMessage<Data>>;
|
|
20
|
+
testWorkers: () => AsyncIterableIterator<TestWorker<Data>>;
|
|
21
|
+
};
|
|
22
|
+
|
|
23
|
+
export type BroadcastMessage<Data = unknown> = {
|
|
24
|
+
readonly id: string;
|
|
25
|
+
replies: () => AsyncIterableIterator<ReceivedMessage<Data>>;
|
|
26
|
+
};
|
|
27
|
+
|
|
28
|
+
export type PublishedMessage<Data = unknown> = {
|
|
29
|
+
readonly id: string;
|
|
30
|
+
replies: () => AsyncIterableIterator<ReceivedMessage<Data>>;
|
|
31
|
+
};
|
|
32
|
+
|
|
33
|
+
export type ReceivedMessage<Data = unknown> = {
|
|
34
|
+
readonly data: Data;
|
|
35
|
+
readonly id: string;
|
|
36
|
+
readonly testWorker: TestWorker;
|
|
37
|
+
reply: (data: Data) => PublishedMessage<Data>;
|
|
38
|
+
};
|
|
39
|
+
|
|
40
|
+
export type TestWorker<Data = unknown> = {
|
|
41
|
+
readonly id: string;
|
|
42
|
+
readonly file: string;
|
|
43
|
+
publish: (data: Data) => PublishedMessage<Data>;
|
|
44
|
+
subscribe: () => AsyncIterableIterator<ReceivedMessage<Data>>;
|
|
45
|
+
teardown: <TeardownFn extends () => void> (fn: TeardownFn) => TeardownFn;
|
|
46
|
+
};
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
export namespace Plugin {
|
|
50
|
+
export type RegistrationOptions<Identifier extends ProtocolIdentifier, Data = unknown> = {
|
|
51
|
+
readonly filename: string;
|
|
52
|
+
readonly initialData?: Data;
|
|
53
|
+
readonly supportedProtocols: readonly Identifier[];
|
|
54
|
+
readonly teardown?: () => void;
|
|
55
|
+
};
|
|
56
|
+
|
|
57
|
+
export namespace Experimental {
|
|
58
|
+
export type Protocol<Data = unknown> = {
|
|
59
|
+
readonly available: Promise<void>;
|
|
60
|
+
readonly currentlyAvailable: boolean;
|
|
61
|
+
readonly protocol: 'experimental';
|
|
62
|
+
publish: (data: Data) => PublishedMessage<Data>;
|
|
63
|
+
subscribe: () => AsyncIterableIterator<ReceivedMessage<Data>>;
|
|
64
|
+
};
|
|
65
|
+
|
|
66
|
+
export type PublishedMessage<Data = unknown> = {
|
|
67
|
+
readonly id: string;
|
|
68
|
+
replies: () => AsyncIterableIterator<ReceivedMessage<Data>>;
|
|
69
|
+
};
|
|
70
|
+
|
|
71
|
+
export type ReceivedMessage<Data = unknown> = {
|
|
72
|
+
readonly data: Data;
|
|
73
|
+
readonly id: string;
|
|
74
|
+
reply: (data: Data) => PublishedMessage<Data>;
|
|
75
|
+
};
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
export function registerSharedWorker<Data = unknown>(options: SharedWorker.Plugin.RegistrationOptions<'experimental', Data>): SharedWorker.Plugin.Experimental.Protocol<Data>;
|
|
81
|
+
// Add overloads for additional protocols.
|