tape-six 1.7.0 → 1.7.2
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/README.md +23 -0
- package/bin/tape6-bun.js +7 -8
- package/bin/tape6-deno.js +7 -8
- package/bin/tape6-node.js +25 -9
- package/bin/tape6-seq.js +7 -8
- package/index.d.ts +1 -1
- package/index.js +9 -0
- package/llms-full.txt +384 -0
- package/llms.txt +243 -0
- package/package.json +24 -8
- package/src/reporters/MinReporter.js +37 -0
- package/src/reporters/TTYReporter.js +4 -4
- package/src/runners/bun/worker.js +33 -5
- package/src/runners/deno/worker.js +33 -5
- package/src/runners/node/worker.js +34 -5
- package/src/runners/seq/TestWorker.js +68 -24
- package/src/test.js +6 -4
- package/src/utils/config.js +42 -4
- package/src/utils/listing.js +12 -20
- package/src/utils/fileSets.js +0 -114
package/llms.txt
ADDED
|
@@ -0,0 +1,243 @@
|
|
|
1
|
+
# tape-six
|
|
2
|
+
|
|
3
|
+
> TAP-based unit test library for modern JavaScript and TypeScript. Works in Node, Deno, Bun, and browsers.
|
|
4
|
+
|
|
5
|
+
## Install
|
|
6
|
+
|
|
7
|
+
npm i -D tape-six
|
|
8
|
+
|
|
9
|
+
## Quick start
|
|
10
|
+
|
|
11
|
+
```js
|
|
12
|
+
import test from 'tape-six';
|
|
13
|
+
|
|
14
|
+
test('my test', async t => {
|
|
15
|
+
t.ok(true, 'truthy value');
|
|
16
|
+
t.equal(1 + 1, 2, 'addition works');
|
|
17
|
+
t.deepEqual({a: 1}, {a: 1}, 'objects are equal');
|
|
18
|
+
});
|
|
19
|
+
```
|
|
20
|
+
|
|
21
|
+
Run: `node my-test.js` or `npx tape6 --flags FO`
|
|
22
|
+
|
|
23
|
+
## API
|
|
24
|
+
|
|
25
|
+
### test(name, options, testFn)
|
|
26
|
+
|
|
27
|
+
Registers a test. All three arguments are optional and can be in any order (recognized by type).
|
|
28
|
+
|
|
29
|
+
- `name` (string) — test name. Defaults to function name or '(anonymous)'.
|
|
30
|
+
- `options` (object) — see TestOptions below.
|
|
31
|
+
- `testFn` (function) — `async (t: Tester) => void`. Can be sync or async.
|
|
32
|
+
|
|
33
|
+
Returns a promise that resolves when the test finishes. Usually no need to await.
|
|
34
|
+
|
|
35
|
+
Aliases: `suite`, `describe`, `it` — all are the same function.
|
|
36
|
+
|
|
37
|
+
```js
|
|
38
|
+
import {test, describe, it, suite} from 'tape-six';
|
|
39
|
+
```
|
|
40
|
+
|
|
41
|
+
#### test.skip(name, options, testFn)
|
|
42
|
+
|
|
43
|
+
Registers a test that will be skipped (not executed).
|
|
44
|
+
|
|
45
|
+
#### test.todo(name, options, testFn)
|
|
46
|
+
|
|
47
|
+
Registers a test marked as work-in-progress. Failures are reported but not counted.
|
|
48
|
+
|
|
49
|
+
#### test.asPromise(name, options, testPromiseFn)
|
|
50
|
+
|
|
51
|
+
Registers a test using callback-style completion: `(t, resolve, reject) => void`.
|
|
52
|
+
|
|
53
|
+
### TestOptions
|
|
54
|
+
|
|
55
|
+
- `name` (string) — test name.
|
|
56
|
+
- `testFn` (function) — test function.
|
|
57
|
+
- `skip` (boolean) — skip this test.
|
|
58
|
+
- `todo` (boolean) — mark as TODO.
|
|
59
|
+
- `timeout` (number) — timeout in ms. Test is marked failed if exceeded.
|
|
60
|
+
- `beforeAll` / `before` (function) — run before all embedded tests.
|
|
61
|
+
- `afterAll` / `after` (function) — run after all embedded tests.
|
|
62
|
+
- `beforeEach` (function) — run before each embedded test.
|
|
63
|
+
- `afterEach` (function) — run after each embedded test.
|
|
64
|
+
|
|
65
|
+
### Tester (the `t` object)
|
|
66
|
+
|
|
67
|
+
The object passed to test functions. Provides assertions and test control.
|
|
68
|
+
|
|
69
|
+
#### Properties
|
|
70
|
+
|
|
71
|
+
- `t.signal` — AbortSignal, aborted when test times out. Use for cancellation.
|
|
72
|
+
- `t.any` (or `t._`) — symbol for matching any value in deepEqual/match.
|
|
73
|
+
|
|
74
|
+
#### Assertions (all `msg` arguments are optional)
|
|
75
|
+
|
|
76
|
+
- `t.pass(msg)` — unconditional pass.
|
|
77
|
+
- `t.fail(msg)` — unconditional fail.
|
|
78
|
+
- `t.ok(value, msg)` — assert truthy. Aliases: `true`, `assert`.
|
|
79
|
+
- `t.notOk(value, msg)` — assert falsy. Aliases: `false`, `notok`.
|
|
80
|
+
- `t.error(err, msg)` — assert err is falsy. Aliases: `ifError`, `ifErr`, `iferror`.
|
|
81
|
+
- `t.strictEqual(a, b, msg)` — assert `a === b`. Aliases: `is`, `equal`, `equals`, `isEqual`, `strictEquals`.
|
|
82
|
+
- `t.notStrictEqual(a, b, msg)` — assert `a !== b`. Aliases: `not`, `notEqual`, `notEquals`, `isNotEqual`, `doesNotEqual`, `isUnequal`, `notStrictEquals`, `isNot`.
|
|
83
|
+
- `t.looseEqual(a, b, msg)` — assert `a == b`. Alias: `looseEquals`.
|
|
84
|
+
- `t.notLooseEqual(a, b, msg)` — assert `a != b`. Alias: `notLooseEquals`.
|
|
85
|
+
- `t.deepEqual(a, b, msg)` — recursive strict deep equality. Aliases: `same`, `deepEquals`, `isEquivalent`.
|
|
86
|
+
- `t.notDeepEqual(a, b, msg)` — assert not deeply equal. Aliases: `notSame`, `notDeepEquals`, `notEquivalent`, `notDeeply`, `isNotDeepEqual`, `isNotEquivalent`.
|
|
87
|
+
- `t.deepLooseEqual(a, b, msg)` — recursive loose deep equality.
|
|
88
|
+
- `t.notDeepLooseEqual(a, b, msg)` — assert not deeply loosely equal.
|
|
89
|
+
- `t.throws(fn, msg)` — assert fn() throws.
|
|
90
|
+
- `t.doesNotThrow(fn, msg)` — assert fn() does not throw.
|
|
91
|
+
- `t.matchString(string, regexp, msg)` — assert string matches regexp.
|
|
92
|
+
- `t.doesNotMatchString(string, regexp, msg)` — assert string does not match regexp.
|
|
93
|
+
- `t.match(a, b, msg)` — assert a matches pattern b (structural).
|
|
94
|
+
- `t.doesNotMatch(a, b, msg)` — assert a does not match pattern b.
|
|
95
|
+
- `t.rejects(promise, msg)` — assert promise rejects. **Async: must await.** Alias: `doesNotResolve`.
|
|
96
|
+
- `t.resolves(promise, msg)` — assert promise resolves. **Async: must await.** Alias: `doesNotReject`.
|
|
97
|
+
|
|
98
|
+
#### Embedded tests (all async, should be awaited)
|
|
99
|
+
|
|
100
|
+
- `await t.test(name, options, testFn)` — nested test.
|
|
101
|
+
- `await t.skip(name, options, testFn)` — nested skipped test.
|
|
102
|
+
- `await t.todo(name, options, testFn)` — nested TODO test.
|
|
103
|
+
- `await t.asPromise(name, options, testPromiseFn)` — nested callback-style test.
|
|
104
|
+
|
|
105
|
+
#### Utilities
|
|
106
|
+
|
|
107
|
+
- `t.plan(n)` — declare expected assertion count (rarely needed).
|
|
108
|
+
- `t.comment(msg)` — emit a comment.
|
|
109
|
+
- `t.skipTest(msg)` — skip current test with a message.
|
|
110
|
+
- `t.bailOut(msg)` — abort entire test suite.
|
|
111
|
+
|
|
112
|
+
### Hooks
|
|
113
|
+
|
|
114
|
+
Hooks can be registered as standalone functions or via test options or Tester methods.
|
|
115
|
+
|
|
116
|
+
```js
|
|
117
|
+
import {test, beforeAll, afterAll, beforeEach, afterEach} from 'tape-six';
|
|
118
|
+
// `before` is an alias for `beforeAll`, `after` is an alias for `afterAll`.
|
|
119
|
+
|
|
120
|
+
beforeAll(() => { /* runs once before all tests */ });
|
|
121
|
+
afterAll(() => { /* runs once after all tests */ });
|
|
122
|
+
beforeEach(() => { /* runs before each test */ });
|
|
123
|
+
afterEach(() => { /* runs after each test */ });
|
|
124
|
+
|
|
125
|
+
// Or inside a test:
|
|
126
|
+
test('suite', async t => {
|
|
127
|
+
t.beforeAll(() => { /* before embedded tests */ });
|
|
128
|
+
t.afterAll(() => { /* after embedded tests */ });
|
|
129
|
+
t.beforeEach(() => { /* before each embedded test */ });
|
|
130
|
+
t.afterEach(() => { /* after each embedded test */ });
|
|
131
|
+
|
|
132
|
+
await t.test('inner', t => { t.pass(); });
|
|
133
|
+
});
|
|
134
|
+
|
|
135
|
+
// Or via test.beforeAll(), test.afterAll(), etc:
|
|
136
|
+
test.beforeAll(() => { /* ... */ });
|
|
137
|
+
```
|
|
138
|
+
|
|
139
|
+
## Common patterns
|
|
140
|
+
|
|
141
|
+
### Basic test file
|
|
142
|
+
|
|
143
|
+
```js
|
|
144
|
+
import test from 'tape-six';
|
|
145
|
+
|
|
146
|
+
test('arithmetic', t => {
|
|
147
|
+
t.equal(2 + 2, 4, 'addition');
|
|
148
|
+
t.ok(10 > 5, 'comparison');
|
|
149
|
+
});
|
|
150
|
+
|
|
151
|
+
test('async operation', async t => {
|
|
152
|
+
const result = await fetchData();
|
|
153
|
+
t.ok(result, 'got result');
|
|
154
|
+
t.equal(result.status, 200, 'status is 200');
|
|
155
|
+
});
|
|
156
|
+
```
|
|
157
|
+
|
|
158
|
+
### Testing exceptions
|
|
159
|
+
|
|
160
|
+
```js
|
|
161
|
+
test('errors', async t => {
|
|
162
|
+
t.throws(() => { throw new Error('boom'); }, 'should throw');
|
|
163
|
+
t.doesNotThrow(() => 42, 'should not throw');
|
|
164
|
+
await t.rejects(Promise.reject(new Error('fail')), 'should reject');
|
|
165
|
+
await t.resolves(Promise.resolve(42), 'should resolve');
|
|
166
|
+
});
|
|
167
|
+
```
|
|
168
|
+
|
|
169
|
+
### Nested tests with setup/teardown
|
|
170
|
+
|
|
171
|
+
```js
|
|
172
|
+
test('database tests', async t => {
|
|
173
|
+
let db;
|
|
174
|
+
t.beforeAll(async () => { db = await connect(); });
|
|
175
|
+
t.afterAll(async () => { await db.close(); });
|
|
176
|
+
|
|
177
|
+
await t.test('insert', async t => {
|
|
178
|
+
const result = await db.insert({name: 'Alice'});
|
|
179
|
+
t.ok(result.id, 'got an id');
|
|
180
|
+
});
|
|
181
|
+
|
|
182
|
+
await t.test('query', async t => {
|
|
183
|
+
const rows = await db.query('SELECT * FROM users');
|
|
184
|
+
t.ok(rows.length > 0, 'has rows');
|
|
185
|
+
});
|
|
186
|
+
});
|
|
187
|
+
```
|
|
188
|
+
|
|
189
|
+
### Using any for partial matching
|
|
190
|
+
|
|
191
|
+
```js
|
|
192
|
+
test('partial match', t => {
|
|
193
|
+
const result = {id: 123, name: 'Alice', timestamp: Date.now()};
|
|
194
|
+
t.deepEqual(result, {id: 123, name: 'Alice', timestamp: t.any});
|
|
195
|
+
});
|
|
196
|
+
```
|
|
197
|
+
|
|
198
|
+
### Using describe/it style
|
|
199
|
+
|
|
200
|
+
```js
|
|
201
|
+
import {describe, it} from 'tape-six';
|
|
202
|
+
|
|
203
|
+
describe('my module', () => {
|
|
204
|
+
it('should work', t => {
|
|
205
|
+
t.ok(true);
|
|
206
|
+
});
|
|
207
|
+
});
|
|
208
|
+
```
|
|
209
|
+
|
|
210
|
+
### Skip and TODO
|
|
211
|
+
|
|
212
|
+
```js
|
|
213
|
+
test.skip('not ready yet', t => { t.fail(); });
|
|
214
|
+
test.todo('work in progress', t => { t.equal(1, 2); }); // failure not counted
|
|
215
|
+
```
|
|
216
|
+
|
|
217
|
+
## Running tests
|
|
218
|
+
|
|
219
|
+
```bash
|
|
220
|
+
node test-file.js # run single file directly
|
|
221
|
+
npx tape6 --flags FO # run all configured tests, show failures only + fail once
|
|
222
|
+
npx tape6-seq # run sequentially (no worker threads)
|
|
223
|
+
npx tape6-server --trace # start browser test server
|
|
224
|
+
```
|
|
225
|
+
|
|
226
|
+
## Configuration (package.json)
|
|
227
|
+
|
|
228
|
+
```json
|
|
229
|
+
{
|
|
230
|
+
"scripts": {
|
|
231
|
+
"test": "tape6 --flags FO"
|
|
232
|
+
},
|
|
233
|
+
"tape6": {
|
|
234
|
+
"tests": ["/tests/test-*.*js"]
|
|
235
|
+
}
|
|
236
|
+
}
|
|
237
|
+
```
|
|
238
|
+
|
|
239
|
+
## Links
|
|
240
|
+
|
|
241
|
+
- Docs: https://github.com/uhop/tape-six/wiki
|
|
242
|
+
- npm: https://www.npmjs.com/package/tape-six
|
|
243
|
+
- Full LLM reference: https://github.com/uhop/tape-six/blob/master/llms-full.txt
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "tape-six",
|
|
3
|
-
"version": "1.7.
|
|
4
|
-
"description": "
|
|
3
|
+
"version": "1.7.2",
|
|
4
|
+
"description": "TAP-based unit test library for Node, Deno, Bun, and browsers. ES modules, TypeScript, zero dependencies.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "index.js",
|
|
7
7
|
"module": "index.js",
|
|
@@ -13,6 +13,7 @@
|
|
|
13
13
|
},
|
|
14
14
|
"bin": {
|
|
15
15
|
"tape6": "bin/tape6.js",
|
|
16
|
+
"tape6-node": "bin/tape6-node.js",
|
|
16
17
|
"tape6-bun": "bin/tape6-bun.js",
|
|
17
18
|
"tape6-deno": "bin/tape6-deno.js",
|
|
18
19
|
"tape6-seq": "bin/tape6-seq.js",
|
|
@@ -30,8 +31,8 @@
|
|
|
30
31
|
"test:bun": "bun run ./bin/tape6-bun.js --flags FO",
|
|
31
32
|
"test:deno": "deno run -A ./bin/tape6-deno.js --flags FO",
|
|
32
33
|
"test:seq": "node ./bin/tape6-seq.js --flags FO",
|
|
33
|
-
"test:bun
|
|
34
|
-
"test:deno
|
|
34
|
+
"test:seq:bun": "bun run ./bin/tape6-seq.js --flags FO",
|
|
35
|
+
"test:seq:deno": "deno run -A ./bin/tape6-seq.js --flags FO",
|
|
35
36
|
"test:puppeteer": "node tests/puppeteer-chrome.js",
|
|
36
37
|
"test:playwright": "node tests/playwright-chrome.js",
|
|
37
38
|
"ts-check": "tsc --noEmit",
|
|
@@ -49,7 +50,18 @@
|
|
|
49
50
|
"test",
|
|
50
51
|
"harness",
|
|
51
52
|
"assert",
|
|
52
|
-
"browser"
|
|
53
|
+
"browser",
|
|
54
|
+
"unit-test",
|
|
55
|
+
"testing",
|
|
56
|
+
"test-runner",
|
|
57
|
+
"esm",
|
|
58
|
+
"es-modules",
|
|
59
|
+
"typescript",
|
|
60
|
+
"deno",
|
|
61
|
+
"bun",
|
|
62
|
+
"nodejs",
|
|
63
|
+
"tap-protocol",
|
|
64
|
+
"cross-runtime"
|
|
53
65
|
],
|
|
54
66
|
"author": "Eugene Lazutkin <eugene.lazutkin@gmail.com> (https://www.lazutkin.com/)",
|
|
55
67
|
"funding": "https://github.com/sponsors/uhop",
|
|
@@ -58,11 +70,15 @@
|
|
|
58
70
|
"url": "https://github.com/uhop/tape-six/issues"
|
|
59
71
|
},
|
|
60
72
|
"homepage": "https://github.com/uhop/tape-six#readme",
|
|
73
|
+
"llms": "https://raw.githubusercontent.com/uhop/tape-six/master/llms.txt",
|
|
74
|
+
"llmsFull": "https://raw.githubusercontent.com/uhop/tape-six/master/llms-full.txt",
|
|
61
75
|
"files": [
|
|
62
76
|
"index.*",
|
|
63
77
|
"bin",
|
|
64
78
|
"web-app",
|
|
65
|
-
"src"
|
|
79
|
+
"src",
|
|
80
|
+
"llms.txt",
|
|
81
|
+
"llms-full.txt"
|
|
66
82
|
],
|
|
67
83
|
"tape6": {
|
|
68
84
|
"tests": [
|
|
@@ -85,10 +101,10 @@
|
|
|
85
101
|
},
|
|
86
102
|
"devDependencies": {
|
|
87
103
|
"@types/chai": "^5.2.3",
|
|
88
|
-
"@types/node": "^25.
|
|
104
|
+
"@types/node": "^25.3.0",
|
|
89
105
|
"chai": "^6.2.2",
|
|
90
106
|
"playwright": "^1.58.2",
|
|
91
|
-
"puppeteer": "^24.37.
|
|
107
|
+
"puppeteer": "^24.37.5",
|
|
92
108
|
"typescript": "^5.9.3"
|
|
93
109
|
}
|
|
94
110
|
}
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
import Reporter from './Reporter.js';
|
|
2
|
+
|
|
3
|
+
const getEnvVar = name => {
|
|
4
|
+
if (typeof Deno == 'object' && Deno?.version) {
|
|
5
|
+
return Deno.env.get(name);
|
|
6
|
+
} else if (typeof Bun == 'object' && Bun?.version) {
|
|
7
|
+
return Bun.env[name];
|
|
8
|
+
} else if (typeof process == 'object' && process?.versions?.node) {
|
|
9
|
+
return process.env[name];
|
|
10
|
+
}
|
|
11
|
+
return undefined;
|
|
12
|
+
};
|
|
13
|
+
|
|
14
|
+
export class MinReporter extends Reporter {
|
|
15
|
+
constructor({failOnce = false, originalConsole} = {}) {
|
|
16
|
+
super({failOnce});
|
|
17
|
+
this.console = originalConsole || console;
|
|
18
|
+
}
|
|
19
|
+
report(event) {
|
|
20
|
+
event = this.state?.preprocess(event) || event;
|
|
21
|
+
const handler = Reporter.EVENT_MAP[event.type];
|
|
22
|
+
typeof handler == 'string' && this[handler]?.(event);
|
|
23
|
+
this.console.log(
|
|
24
|
+
'Test:',
|
|
25
|
+
event.test,
|
|
26
|
+
'Type:',
|
|
27
|
+
event.type,
|
|
28
|
+
'Name:',
|
|
29
|
+
event.name,
|
|
30
|
+
'Fail:',
|
|
31
|
+
event.fail
|
|
32
|
+
);
|
|
33
|
+
this.state?.postprocess(event);
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
export default MinReporter;
|
|
@@ -58,7 +58,7 @@ export class TTYReporter extends Reporter {
|
|
|
58
58
|
this.hasColors =
|
|
59
59
|
hasColors &&
|
|
60
60
|
this.output.isTTY &&
|
|
61
|
-
(typeof this.output.hasColors == 'function' ? this.output.hasColors(
|
|
61
|
+
(typeof this.output.hasColors == 'function' ? this.output.hasColors(16) : true);
|
|
62
62
|
this.renumberAsserts = renumberAsserts;
|
|
63
63
|
this.failureOnly = failureOnly;
|
|
64
64
|
this.showBanner = showBanner;
|
|
@@ -221,7 +221,7 @@ export class TTYReporter extends Reporter {
|
|
|
221
221
|
paint = method === 'error' || method === 'assert' ? 'stderrPaint' : 'stdoutPaint',
|
|
222
222
|
prefix = this[paint](consoleDict[method] + ':') + ' ';
|
|
223
223
|
for (const line of lines) {
|
|
224
|
-
this.out(prefix + line);
|
|
224
|
+
this.out(prefix + line, this.failureOnly);
|
|
225
225
|
}
|
|
226
226
|
}
|
|
227
227
|
}
|
|
@@ -231,7 +231,7 @@ export class TTYReporter extends Reporter {
|
|
|
231
231
|
const lines = event.name.split(/\r?\n/),
|
|
232
232
|
prefix = this.stdoutPaint('stdout:') + ' ';
|
|
233
233
|
for (const line of lines) {
|
|
234
|
-
this.out(prefix + line);
|
|
234
|
+
this.out(prefix + line, this.failureOnly);
|
|
235
235
|
}
|
|
236
236
|
}
|
|
237
237
|
break;
|
|
@@ -240,7 +240,7 @@ export class TTYReporter extends Reporter {
|
|
|
240
240
|
const lines = event.name.split(/\r?\n/),
|
|
241
241
|
prefix = this.stderrPaint('stderr:') + ' ';
|
|
242
242
|
for (const line of lines) {
|
|
243
|
-
this.out(prefix + line);
|
|
243
|
+
this.out(prefix + line, this.failureOnly);
|
|
244
244
|
}
|
|
245
245
|
}
|
|
246
246
|
break;
|
|
@@ -1,8 +1,36 @@
|
|
|
1
|
-
const
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
1
|
+
const DEFAULT_START_TIMEOUT = 5_000;
|
|
2
|
+
|
|
3
|
+
const getTimeout = () => {
|
|
4
|
+
const timeoutValue = Bun.env.TAPE6_WORKER_START_TIMEOUT;
|
|
5
|
+
if (!timeoutValue) return DEFAULT_START_TIMEOUT;
|
|
6
|
+
let timeout = Number(timeoutValue);
|
|
7
|
+
if (isNaN(timeout) || timeout <= 0 || timeout === Infinity) timeout = DEFAULT_START_TIMEOUT;
|
|
8
|
+
return timeout;
|
|
9
|
+
};
|
|
10
|
+
|
|
11
|
+
const reportToParent = fileName => {
|
|
12
|
+
const timeout = getTimeout();
|
|
13
|
+
let timeoutBeforeStartId = setTimeout(() => {
|
|
14
|
+
postMessage({type: 'test', test: 0, name: 'FILE: /' + fileName});
|
|
15
|
+
postMessage({
|
|
16
|
+
name: `No tests found in ${timeout}ms`,
|
|
17
|
+
test: 0,
|
|
18
|
+
marker: new Error(),
|
|
19
|
+
operator: 'error',
|
|
20
|
+
fail: true
|
|
21
|
+
});
|
|
22
|
+
postMessage({type: 'end', test: 0, name: 'FILE: /' + fileName, fail: true});
|
|
23
|
+
}, timeout);
|
|
24
|
+
return msg => {
|
|
25
|
+
if (timeoutBeforeStartId) {
|
|
26
|
+
clearTimeout(timeoutBeforeStartId);
|
|
27
|
+
timeoutBeforeStartId = null;
|
|
28
|
+
}
|
|
29
|
+
if ((msg.type === 'test' || msg.type === 'end') && !msg.test && !msg.name) {
|
|
30
|
+
msg.name = 'FILE: /' + fileName;
|
|
31
|
+
}
|
|
32
|
+
postMessage(msg);
|
|
33
|
+
};
|
|
6
34
|
};
|
|
7
35
|
|
|
8
36
|
addEventListener('message', async event => {
|
|
@@ -1,8 +1,36 @@
|
|
|
1
|
-
const
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
1
|
+
const DEFAULT_START_TIMEOUT = 5_000;
|
|
2
|
+
|
|
3
|
+
const getTimeout = () => {
|
|
4
|
+
const timeoutValue = Deno.env.get('TAPE6_WORKER_START TIMEOUT');
|
|
5
|
+
if (!timeoutValue) return DEFAULT_START_TIMEOUT;
|
|
6
|
+
let timeout = Number(timeoutValue);
|
|
7
|
+
if (isNaN(timeout) || timeout <= 0 || timeout === Infinity) timeout = DEFAULT_START_TIMEOUT;
|
|
8
|
+
return timeout;
|
|
9
|
+
};
|
|
10
|
+
|
|
11
|
+
const reportToParent = fileName => {
|
|
12
|
+
const timeout = getTimeout();
|
|
13
|
+
let timeoutBeforeStartId = setTimeout(() => {
|
|
14
|
+
postMessage({type: 'test', test: 0, name: 'FILE: /' + fileName});
|
|
15
|
+
postMessage({
|
|
16
|
+
name: `No tests found in ${timeout}ms`,
|
|
17
|
+
test: 0,
|
|
18
|
+
marker: new Error(),
|
|
19
|
+
operator: 'error',
|
|
20
|
+
fail: true
|
|
21
|
+
});
|
|
22
|
+
postMessage({type: 'end', test: 0, name: 'FILE: /' + fileName, fail: true});
|
|
23
|
+
}, timeout);
|
|
24
|
+
return msg => {
|
|
25
|
+
if (timeoutBeforeStartId) {
|
|
26
|
+
clearTimeout(timeoutBeforeStartId);
|
|
27
|
+
timeoutBeforeStartId = null;
|
|
28
|
+
}
|
|
29
|
+
if ((msg.type === 'test' || msg.type === 'end') && !msg.test && !msg.name) {
|
|
30
|
+
msg.name = 'FILE: /' + fileName;
|
|
31
|
+
}
|
|
32
|
+
postMessage(msg);
|
|
33
|
+
};
|
|
6
34
|
};
|
|
7
35
|
|
|
8
36
|
addEventListener('message', async event => {
|
|
@@ -1,10 +1,39 @@
|
|
|
1
|
+
import process from 'node:process';
|
|
1
2
|
import {parentPort} from 'node:worker_threads';
|
|
2
3
|
|
|
3
|
-
const
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
4
|
+
const DEFAULT_START_TIMEOUT = 5_000;
|
|
5
|
+
|
|
6
|
+
const getTimeout = () => {
|
|
7
|
+
const timeoutValue = process.env.TAPE6_WORKER_START_TIMEOUT;
|
|
8
|
+
if (!timeoutValue) return DEFAULT_START_TIMEOUT;
|
|
9
|
+
let timeout = Number(timeoutValue);
|
|
10
|
+
if (isNaN(timeout) || timeout <= 0 || timeout === Infinity) timeout = DEFAULT_START_TIMEOUT;
|
|
11
|
+
return timeout;
|
|
12
|
+
};
|
|
13
|
+
|
|
14
|
+
const reportToParent = fileName => {
|
|
15
|
+
const timeout = getTimeout();
|
|
16
|
+
let timeoutBeforeStartId = setTimeout(() => {
|
|
17
|
+
parentPort.postMessage({type: 'test', test: 0, name: 'FILE: /' + fileName});
|
|
18
|
+
parentPort.postMessage({
|
|
19
|
+
name: `No tests found in ${timeout}ms`,
|
|
20
|
+
test: 0,
|
|
21
|
+
marker: new Error(),
|
|
22
|
+
operator: 'error',
|
|
23
|
+
fail: true
|
|
24
|
+
});
|
|
25
|
+
parentPort.postMessage({type: 'end', test: 0, name: 'FILE: /' + fileName, fail: true});
|
|
26
|
+
}, timeout);
|
|
27
|
+
return msg => {
|
|
28
|
+
if (timeoutBeforeStartId) {
|
|
29
|
+
clearTimeout(timeoutBeforeStartId);
|
|
30
|
+
timeoutBeforeStartId = null;
|
|
31
|
+
}
|
|
32
|
+
if ((msg.type === 'test' || msg.type === 'end') && !msg.test && !msg.name) {
|
|
33
|
+
msg.name = 'FILE: /' + fileName;
|
|
34
|
+
}
|
|
35
|
+
parentPort.postMessage(msg);
|
|
36
|
+
};
|
|
8
37
|
};
|
|
9
38
|
|
|
10
39
|
parentPort.on('message', async msg => {
|
|
@@ -4,6 +4,7 @@ import {pathToFileURL} from 'node:url';
|
|
|
4
4
|
|
|
5
5
|
import {isStopTest} from '../../State.js';
|
|
6
6
|
import EventServer from '../../utils/EventServer.js';
|
|
7
|
+
import {getTimeoutValue} from '../../utils/config.js';
|
|
7
8
|
import {getOriginalConsole, setCurrentReporter} from '../../utils/capture-console.js';
|
|
8
9
|
import {
|
|
9
10
|
clearBeforeAll,
|
|
@@ -11,7 +12,8 @@ import {
|
|
|
11
12
|
clearBeforeEach,
|
|
12
13
|
clearAfterEach,
|
|
13
14
|
setReporter,
|
|
14
|
-
registerNotifyCallback
|
|
15
|
+
registerNotifyCallback,
|
|
16
|
+
unregisterNotifyCallback
|
|
15
17
|
} from '../../test.js';
|
|
16
18
|
import BypassReporter from './BypassReporter.js';
|
|
17
19
|
|
|
@@ -23,10 +25,16 @@ export default class TestWorker extends EventServer {
|
|
|
23
25
|
this.testRunner = options.testRunner;
|
|
24
26
|
this.counter = 0;
|
|
25
27
|
this.originalConsole = getOriginalConsole() || globalThis.console;
|
|
28
|
+
this.timeout = getTimeoutValue();
|
|
29
|
+
this.timeoutId = null;
|
|
26
30
|
}
|
|
27
31
|
makeTask(fileName) {
|
|
28
32
|
const id = String(++this.counter),
|
|
29
33
|
reporter = new BypassReporter(this.reporter, event => {
|
|
34
|
+
if (this.timeoutId) {
|
|
35
|
+
clearTimeout(this.timeoutId);
|
|
36
|
+
this.timeoutId = null;
|
|
37
|
+
}
|
|
30
38
|
try {
|
|
31
39
|
this.report(id, event);
|
|
32
40
|
} catch (error) {
|
|
@@ -39,33 +47,22 @@ export default class TestWorker extends EventServer {
|
|
|
39
47
|
setReporter(reporter);
|
|
40
48
|
setCurrentReporter(this.reporter);
|
|
41
49
|
process.env.TAPE6_TEST_FILE_NAME = fileName;
|
|
42
|
-
|
|
50
|
+
const url = new URL(fileName, baseName);
|
|
51
|
+
import(url)
|
|
43
52
|
.then(async () => {
|
|
44
53
|
const testRunner = await this.testRunner;
|
|
45
54
|
registerNotifyCallback(testRunner);
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
type: 'comment',
|
|
50
|
-
name: 'fail to load: ' + (error.message || 'Worker error'),
|
|
51
|
-
test: 0
|
|
52
|
-
});
|
|
53
|
-
try {
|
|
54
|
-
this.report(id, {
|
|
55
|
-
name: String(error),
|
|
56
|
-
test: 0,
|
|
57
|
-
marker: new Error(),
|
|
58
|
-
operator: 'error',
|
|
59
|
-
fail: true,
|
|
60
|
-
data: {
|
|
61
|
-
actual: error
|
|
62
|
-
}
|
|
63
|
-
});
|
|
64
|
-
} catch (error) {
|
|
65
|
-
if (!isStopTest(error)) throw error;
|
|
55
|
+
if (this.timeoutId) {
|
|
56
|
+
clearTimeout(this.timeoutId);
|
|
57
|
+
this.timeoutId = null;
|
|
66
58
|
}
|
|
67
|
-
this.
|
|
68
|
-
|
|
59
|
+
this.timeoutId = setTimeout(() => {
|
|
60
|
+
this.timeoutId = null;
|
|
61
|
+
unregisterNotifyCallback(testRunner);
|
|
62
|
+
this.#reportTimeout(id, fileName);
|
|
63
|
+
}, this.timeout);
|
|
64
|
+
})
|
|
65
|
+
.catch(error => this.#reportError(id, error));
|
|
69
66
|
return id;
|
|
70
67
|
}
|
|
71
68
|
destroyTask() {
|
|
@@ -76,4 +73,51 @@ export default class TestWorker extends EventServer {
|
|
|
76
73
|
clearBeforeEach();
|
|
77
74
|
clearAfterEach();
|
|
78
75
|
}
|
|
76
|
+
#reportError(id, error) {
|
|
77
|
+
this.report(id, {
|
|
78
|
+
type: 'comment',
|
|
79
|
+
name: 'fail to load: ' + (error.message || 'Worker error'),
|
|
80
|
+
test: 0
|
|
81
|
+
});
|
|
82
|
+
try {
|
|
83
|
+
this.report(id, {
|
|
84
|
+
name: String(error),
|
|
85
|
+
test: 0,
|
|
86
|
+
marker: new Error(),
|
|
87
|
+
operator: 'error',
|
|
88
|
+
fail: true,
|
|
89
|
+
data: {
|
|
90
|
+
actual: error
|
|
91
|
+
}
|
|
92
|
+
});
|
|
93
|
+
} catch (error) {
|
|
94
|
+
if (!isStopTest(error)) throw error;
|
|
95
|
+
}
|
|
96
|
+
this.close(id);
|
|
97
|
+
}
|
|
98
|
+
#reportTimeout(id, fileName) {
|
|
99
|
+
this.report(id, {
|
|
100
|
+
type: 'test',
|
|
101
|
+
test: 0,
|
|
102
|
+
name: 'FILE: /' + fileName
|
|
103
|
+
});
|
|
104
|
+
try {
|
|
105
|
+
this.report(id, {
|
|
106
|
+
name: `No tests found in ${this.timeout}ms`,
|
|
107
|
+
test: 0,
|
|
108
|
+
marker: new Error(),
|
|
109
|
+
operator: 'error',
|
|
110
|
+
fail: true
|
|
111
|
+
});
|
|
112
|
+
} catch (error) {
|
|
113
|
+
if (!isStopTest(error)) throw error;
|
|
114
|
+
}
|
|
115
|
+
this.report(id, {
|
|
116
|
+
type: 'end',
|
|
117
|
+
test: 0,
|
|
118
|
+
name: 'FILE: /' + fileName,
|
|
119
|
+
fail: true
|
|
120
|
+
});
|
|
121
|
+
this.close(id);
|
|
122
|
+
}
|
|
79
123
|
}
|
package/src/test.js
CHANGED
|
@@ -12,7 +12,7 @@ let tests = [],
|
|
|
12
12
|
reporter = null,
|
|
13
13
|
testCounter = 0,
|
|
14
14
|
isConfigured = false,
|
|
15
|
-
notifyCallback =
|
|
15
|
+
notifyCallback = new Set();
|
|
16
16
|
|
|
17
17
|
export const getConfiguredFlag = () => isConfigured;
|
|
18
18
|
export const setConfiguredFlag = value => (isConfigured = !!value);
|
|
@@ -26,10 +26,12 @@ export const registerNotifyCallback = callback => {
|
|
|
26
26
|
if (tests.length) {
|
|
27
27
|
defer(callback);
|
|
28
28
|
} else {
|
|
29
|
-
notifyCallback.
|
|
29
|
+
notifyCallback.add(callback);
|
|
30
30
|
}
|
|
31
31
|
};
|
|
32
32
|
|
|
33
|
+
export const unregisterNotifyCallback = callback => notifyCallback.delete(callback);
|
|
34
|
+
|
|
33
35
|
const testers = [];
|
|
34
36
|
|
|
35
37
|
export const getTesters = () => testers;
|
|
@@ -86,9 +88,9 @@ export const test = async (name, options, testFn) => {
|
|
|
86
88
|
isTimerSet = true;
|
|
87
89
|
}
|
|
88
90
|
const deferred = makeDeferred();
|
|
89
|
-
if (tests.push({options, deferred}) === 1 && notifyCallback.
|
|
91
|
+
if (tests.push({options, deferred}) === 1 && notifyCallback.size) {
|
|
90
92
|
for (const callback of notifyCallback) defer(callback);
|
|
91
|
-
notifyCallback =
|
|
93
|
+
notifyCallback = new Set();
|
|
92
94
|
}
|
|
93
95
|
|
|
94
96
|
return deferred.promise;
|