tape-six 0.9.2 → 0.9.4
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 +3 -1
- package/bin/tape6-bun.js +128 -0
- package/bin/tape6-deno.js +131 -0
- package/bin/tape6-server.js +10 -8
- package/bin/tape6.js +16 -24
- package/index.js +5 -5
- package/package.json +5 -3
- package/src/TTYReporter.js +56 -17
- package/src/bun/TestWorker.js +63 -0
- package/src/bun/worker.js +21 -0
- package/src/deno/TestWorker.js +82 -0
- package/src/node/TestWorker.js +19 -9
- package/src/node/config.js +3 -3
- package/src/node/listing.js +2 -2
- package/src/test.js +2 -2
- package/src/utils/box.js +1 -1
- package/src/utils/defer.js +51 -26
- package/src/utils/formatters.js +2 -2
- package/src/utils/timer.js +1 -1
- package/webApp/TestWorker.js +3 -1
- package/webApp/index.html +15 -6
- package/webApp/index.js +2 -2
package/README.md
CHANGED
|
@@ -30,8 +30,10 @@ If you are familiar with other TAP-based libraries you'll feel right at home.
|
|
|
30
30
|
|
|
31
31
|
## Release notes
|
|
32
32
|
|
|
33
|
-
The
|
|
33
|
+
The most recent releases:
|
|
34
34
|
|
|
35
|
+
* 0.9.4 *Updated deps. Added test runners for Bun and Deno.*
|
|
36
|
+
* 0.9.3 *Made TTY reporter work with non-TTY streams.*
|
|
35
37
|
* 0.9.2 *Fixed Windows runner.*
|
|
36
38
|
* 0.9.1 *More updates related to renaming `tape6` ⇒ `tape-six`.*
|
|
37
39
|
* 0.9.0 *Initial release.*
|
package/bin/tape6-bun.js
ADDED
|
@@ -0,0 +1,128 @@
|
|
|
1
|
+
#!/usr/bin/env bun
|
|
2
|
+
|
|
3
|
+
import {resolveTests, resolvePatterns} from '../src/node/config.js';
|
|
4
|
+
|
|
5
|
+
import {getReporter, setReporter} from '../src/test.js';
|
|
6
|
+
import State from '../src/State.js';
|
|
7
|
+
import TapReporter from '../src/TapReporter.js';
|
|
8
|
+
import {selectTimer} from '../src/utils/timer.js';
|
|
9
|
+
|
|
10
|
+
import TestWorker from '../src/bun/TestWorker.js';
|
|
11
|
+
|
|
12
|
+
const options = {},
|
|
13
|
+
rootFolder = process.cwd();
|
|
14
|
+
|
|
15
|
+
let flags = '',
|
|
16
|
+
parallel = '',
|
|
17
|
+
files = [];
|
|
18
|
+
|
|
19
|
+
const config = () => {
|
|
20
|
+
const optionNames = {
|
|
21
|
+
f: 'failureOnly',
|
|
22
|
+
t: 'showTime',
|
|
23
|
+
b: 'showBanner',
|
|
24
|
+
d: 'showData',
|
|
25
|
+
o: 'failOnce',
|
|
26
|
+
n: 'showAssertNumber'
|
|
27
|
+
};
|
|
28
|
+
|
|
29
|
+
let flagIsSet = false,
|
|
30
|
+
parIsSet = false;
|
|
31
|
+
|
|
32
|
+
for (let i = 2; i < Bun.argv.length; ++i) {
|
|
33
|
+
const arg = Bun.argv[i];
|
|
34
|
+
if (arg == '-f' || arg == '--flags') {
|
|
35
|
+
if (++i < Bun.argv.length) {
|
|
36
|
+
flags = Bun.argv[i];
|
|
37
|
+
flagIsSet = true;
|
|
38
|
+
}
|
|
39
|
+
continue;
|
|
40
|
+
}
|
|
41
|
+
if (arg == '-p' || arg == '--par') {
|
|
42
|
+
if (++i < Bun.argv.length) {
|
|
43
|
+
parallel = Bun.argv[i];
|
|
44
|
+
parIsSet = true;
|
|
45
|
+
if (!parallel || isNaN(parallel)) {
|
|
46
|
+
parallel = '';
|
|
47
|
+
parIsSet = false;
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
continue;
|
|
51
|
+
}
|
|
52
|
+
files.push(arg);
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
if (!flagIsSet) {
|
|
56
|
+
flags = process.env.TAPE6_FLAGS || flags;
|
|
57
|
+
}
|
|
58
|
+
for (let i = 0; i < flags.length; ++i) {
|
|
59
|
+
const option = flags[i].toLowerCase(),
|
|
60
|
+
name = optionNames[option];
|
|
61
|
+
if (typeof name == 'string') options[name] = option !== flags[i];
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
if (!parIsSet) {
|
|
65
|
+
parallel = process.env.TAPE6_PAR || parallel;
|
|
66
|
+
}
|
|
67
|
+
if (parallel) {
|
|
68
|
+
parallel = Math.max(0, +parallel);
|
|
69
|
+
if (parallel === Infinity) parallel = 0;
|
|
70
|
+
} else {
|
|
71
|
+
parallel = 0;
|
|
72
|
+
}
|
|
73
|
+
if (!parallel) parallel = navigator.hardwareConcurrency;
|
|
74
|
+
};
|
|
75
|
+
|
|
76
|
+
const init = async () => {
|
|
77
|
+
let reporter = getReporter();
|
|
78
|
+
if (!reporter) {
|
|
79
|
+
if (!process.env.TAPE6_TAP) {
|
|
80
|
+
const TTYReporter = (await import('../src/TTYReporter.js')).default,
|
|
81
|
+
ttyReporter = new TTYReporter(options);
|
|
82
|
+
ttyReporter.testCounter = -2;
|
|
83
|
+
ttyReporter.technicalDepth = 1;
|
|
84
|
+
reporter = ttyReporter.report.bind(ttyReporter);
|
|
85
|
+
}
|
|
86
|
+
if (!reporter) {
|
|
87
|
+
const tapReporter = new TapReporter({useJson: true});
|
|
88
|
+
reporter = tapReporter.report.bind(tapReporter);
|
|
89
|
+
}
|
|
90
|
+
setReporter(reporter);
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
if (files.length) {
|
|
94
|
+
files = await resolvePatterns(rootFolder, files);
|
|
95
|
+
} else {
|
|
96
|
+
files = await resolveTests(rootFolder, 'deno');
|
|
97
|
+
}
|
|
98
|
+
};
|
|
99
|
+
|
|
100
|
+
const main = async () => {
|
|
101
|
+
config();
|
|
102
|
+
await init();
|
|
103
|
+
await selectTimer();
|
|
104
|
+
|
|
105
|
+
process.on('uncaughtException', error => console.error('UNHANDLED ERROR:', error));
|
|
106
|
+
|
|
107
|
+
const rootState = new State(null, {callback: getReporter(), failOnce: options.failOnce}),
|
|
108
|
+
worker = new TestWorker(event => rootState.emit(event), parallel, options);
|
|
109
|
+
|
|
110
|
+
rootState.emit({type: 'test', test: 0, time: rootState.timer.now()});
|
|
111
|
+
|
|
112
|
+
await new Promise(resolve => {
|
|
113
|
+
worker.done = () => resolve();
|
|
114
|
+
worker.execute(files);
|
|
115
|
+
});
|
|
116
|
+
|
|
117
|
+
rootState.emit({
|
|
118
|
+
type: 'end',
|
|
119
|
+
test: 0,
|
|
120
|
+
time: rootState.timer.now(),
|
|
121
|
+
fail: rootState.failed > 0,
|
|
122
|
+
data: rootState
|
|
123
|
+
});
|
|
124
|
+
|
|
125
|
+
process.exit(rootState.failed > 0 ? 1 : 0);
|
|
126
|
+
};
|
|
127
|
+
|
|
128
|
+
main().catch(error => console.error('ERROR:', error));
|
|
@@ -0,0 +1,131 @@
|
|
|
1
|
+
#!/usr/bin/env -S deno run --allow-read --allow-env --allow-hrtime --ext=js
|
|
2
|
+
|
|
3
|
+
import {resolveTests, resolvePatterns} from '../src/node/config.js';
|
|
4
|
+
|
|
5
|
+
import {getReporter, setReporter} from '../src/test.js';
|
|
6
|
+
import State from '../src/State.js';
|
|
7
|
+
import TapReporter from '../src/TapReporter.js';
|
|
8
|
+
import {selectTimer} from '../src/utils/timer.js';
|
|
9
|
+
|
|
10
|
+
import TestWorker from '../src/deno/TestWorker.js';
|
|
11
|
+
|
|
12
|
+
const options = {},
|
|
13
|
+
rootFolder = Deno.cwd();
|
|
14
|
+
|
|
15
|
+
let flags = '',
|
|
16
|
+
parallel = '',
|
|
17
|
+
files = [];
|
|
18
|
+
|
|
19
|
+
const config = () => {
|
|
20
|
+
const optionNames = {
|
|
21
|
+
f: 'failureOnly',
|
|
22
|
+
t: 'showTime',
|
|
23
|
+
b: 'showBanner',
|
|
24
|
+
d: 'showData',
|
|
25
|
+
o: 'failOnce',
|
|
26
|
+
n: 'showAssertNumber'
|
|
27
|
+
};
|
|
28
|
+
|
|
29
|
+
let flagIsSet = false,
|
|
30
|
+
parIsSet = false;
|
|
31
|
+
|
|
32
|
+
for (let i = 0; i < Deno.args.length; ++i) {
|
|
33
|
+
const arg = Deno.args[i];
|
|
34
|
+
if (arg == '-f' || arg == '--flags') {
|
|
35
|
+
if (++i < Deno.args.length) {
|
|
36
|
+
flags = Deno.args[i];
|
|
37
|
+
flagIsSet = true;
|
|
38
|
+
}
|
|
39
|
+
continue;
|
|
40
|
+
}
|
|
41
|
+
if (arg == '-p' || arg == '--par') {
|
|
42
|
+
if (++i < Deno.args.length) {
|
|
43
|
+
parallel = Deno.args[i];
|
|
44
|
+
parIsSet = true;
|
|
45
|
+
if (!parallel || isNaN(parallel)) {
|
|
46
|
+
parallel = '';
|
|
47
|
+
parIsSet = false;
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
continue;
|
|
51
|
+
}
|
|
52
|
+
files.push(arg);
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
if (!flagIsSet) {
|
|
56
|
+
flags = Deno.env.get('TAPE6_FLAGS') || flags;
|
|
57
|
+
}
|
|
58
|
+
for (let i = 0; i < flags.length; ++i) {
|
|
59
|
+
const option = flags[i].toLowerCase(),
|
|
60
|
+
name = optionNames[option];
|
|
61
|
+
if (typeof name == 'string') options[name] = option !== flags[i];
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
if (!parIsSet) {
|
|
65
|
+
parallel = Deno.env.get('TAPE6_PAR') || parallel;
|
|
66
|
+
}
|
|
67
|
+
if (parallel) {
|
|
68
|
+
parallel = Math.max(0, +parallel);
|
|
69
|
+
if (parallel === Infinity) parallel = 0;
|
|
70
|
+
} else {
|
|
71
|
+
parallel = 0;
|
|
72
|
+
}
|
|
73
|
+
if (!parallel) parallel = navigator.hardwareConcurrency;
|
|
74
|
+
};
|
|
75
|
+
|
|
76
|
+
const init = async () => {
|
|
77
|
+
let reporter = getReporter();
|
|
78
|
+
if (!reporter) {
|
|
79
|
+
if (!Deno.env.get('TAPE6_TAP')) {
|
|
80
|
+
const TTYReporter = (await import('../src/TTYReporter.js')).default,
|
|
81
|
+
ttyReporter = new TTYReporter(options);
|
|
82
|
+
ttyReporter.testCounter = -2;
|
|
83
|
+
ttyReporter.technicalDepth = 1;
|
|
84
|
+
reporter = ttyReporter.report.bind(ttyReporter);
|
|
85
|
+
}
|
|
86
|
+
if (!reporter) {
|
|
87
|
+
const tapReporter = new TapReporter({useJson: true});
|
|
88
|
+
reporter = tapReporter.report.bind(tapReporter);
|
|
89
|
+
}
|
|
90
|
+
setReporter(reporter);
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
if (files.length) {
|
|
94
|
+
files = await resolvePatterns(rootFolder, files);
|
|
95
|
+
} else {
|
|
96
|
+
files = await resolveTests(rootFolder, 'deno');
|
|
97
|
+
}
|
|
98
|
+
};
|
|
99
|
+
|
|
100
|
+
const main = async () => {
|
|
101
|
+
config();
|
|
102
|
+
await init();
|
|
103
|
+
await selectTimer();
|
|
104
|
+
|
|
105
|
+
addEventListener('error', event => {
|
|
106
|
+
console.log('UNHANDLED ERROR:', event.message);
|
|
107
|
+
event.preventDefault();
|
|
108
|
+
});
|
|
109
|
+
|
|
110
|
+
const rootState = new State(null, {callback: getReporter(), failOnce: options.failOnce}),
|
|
111
|
+
worker = new TestWorker(event => rootState.emit(event), parallel, options);
|
|
112
|
+
|
|
113
|
+
rootState.emit({type: 'test', test: 0, time: rootState.timer.now()});
|
|
114
|
+
|
|
115
|
+
await new Promise(resolve => {
|
|
116
|
+
worker.done = () => resolve();
|
|
117
|
+
worker.execute(files);
|
|
118
|
+
});
|
|
119
|
+
|
|
120
|
+
rootState.emit({
|
|
121
|
+
type: 'end',
|
|
122
|
+
test: 0,
|
|
123
|
+
time: rootState.timer.now(),
|
|
124
|
+
fail: rootState.failed > 0,
|
|
125
|
+
data: rootState
|
|
126
|
+
});
|
|
127
|
+
|
|
128
|
+
Deno.exit(rootState.failed > 0 ? 1 : 0);
|
|
129
|
+
};
|
|
130
|
+
|
|
131
|
+
main().catch(error => console.error('ERROR:', error));
|
package/bin/tape6-server.js
CHANGED
|
@@ -1,8 +1,9 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
|
|
3
|
-
import http from 'http';
|
|
4
|
-
import fs from 'fs';
|
|
5
|
-
import path from 'path';
|
|
3
|
+
import http from 'node:http';
|
|
4
|
+
import fs from 'node:fs';
|
|
5
|
+
import path from 'node:path';
|
|
6
|
+
import process from 'node:process';
|
|
6
7
|
|
|
7
8
|
import {resolveTests, resolvePatterns} from '../src/node/config.js';
|
|
8
9
|
|
|
@@ -34,8 +35,9 @@ const mimeTable = {
|
|
|
34
35
|
defaultMime = 'application/octet-stream',
|
|
35
36
|
rootFolder = process.env.SERVER_ROOT || process.cwd(),
|
|
36
37
|
traceCalls = process.argv.includes('--trace'),
|
|
37
|
-
|
|
38
|
-
|
|
38
|
+
hasColors =
|
|
39
|
+
process.stdout.isTTY &&
|
|
40
|
+
(typeof process.stdout.hasColors == 'function' ? process.stdout.hasColors() : true);
|
|
39
41
|
|
|
40
42
|
let webAppPath = process.env.WEBAPP_PATH;
|
|
41
43
|
if (!webAppPath) {
|
|
@@ -72,7 +74,7 @@ const sendFile = (req, res, fileName, ext, justHeaders) => {
|
|
|
72
74
|
if (!ext) {
|
|
73
75
|
ext = path.extname(fileName).toLowerCase();
|
|
74
76
|
}
|
|
75
|
-
let mime = ext && mimeTable[ext.
|
|
77
|
+
let mime = ext && mimeTable[ext.substring(1)];
|
|
76
78
|
if (!mime || typeof mime != 'string') {
|
|
77
79
|
mime = defaultMime;
|
|
78
80
|
}
|
|
@@ -160,8 +162,8 @@ const server = http.createServer(async (req, res) => {
|
|
|
160
162
|
bailOut(req, res);
|
|
161
163
|
});
|
|
162
164
|
|
|
163
|
-
server.on('clientError', (
|
|
164
|
-
if (
|
|
165
|
+
server.on('clientError', (error, socket) => {
|
|
166
|
+
if (error.code === 'ECONNRESET' || !socket.writable) return;
|
|
165
167
|
socket.end('HTTP/1.1 400 Bad Request\r\n\r\n');
|
|
166
168
|
});
|
|
167
169
|
|
package/bin/tape6.js
CHANGED
|
@@ -1,19 +1,13 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
|
|
3
|
-
import cluster from 'cluster';
|
|
4
|
-
import os from 'os';
|
|
5
|
-
import path from 'path';
|
|
3
|
+
import cluster from 'node:cluster';
|
|
4
|
+
import os from 'node:os';
|
|
5
|
+
import path from 'node:path';
|
|
6
|
+
import process from 'node:process';
|
|
6
7
|
|
|
7
8
|
import {resolveTests, resolvePatterns} from '../src/node/config.js';
|
|
8
9
|
|
|
9
|
-
import {
|
|
10
|
-
getTests,
|
|
11
|
-
clearTests,
|
|
12
|
-
getReporter,
|
|
13
|
-
setReporter,
|
|
14
|
-
runTests,
|
|
15
|
-
setConfiguredFlag
|
|
16
|
-
} from '../src/test.js';
|
|
10
|
+
import {getReporter, setReporter} from '../src/test.js';
|
|
17
11
|
import State from '../src/State.js';
|
|
18
12
|
import TapReporter from '../src/TapReporter.js';
|
|
19
13
|
import TestWorker from '../src/node/TestWorker.js';
|
|
@@ -27,7 +21,7 @@ let flags = '',
|
|
|
27
21
|
parallel = '',
|
|
28
22
|
files = [];
|
|
29
23
|
|
|
30
|
-
const
|
|
24
|
+
const mainConfiguration = () => {
|
|
31
25
|
const optionNames = {
|
|
32
26
|
f: 'failureOnly',
|
|
33
27
|
t: 'showTime',
|
|
@@ -83,10 +77,10 @@ const masterConfiguration = () => {
|
|
|
83
77
|
if (!parallel) parallel = os.cpus().length;
|
|
84
78
|
};
|
|
85
79
|
|
|
86
|
-
const
|
|
80
|
+
const mainInitialization = async () => {
|
|
87
81
|
let reporter = getReporter();
|
|
88
82
|
if (!reporter) {
|
|
89
|
-
if (
|
|
83
|
+
if (!process.env.TAPE6_TAP) {
|
|
90
84
|
const TTYReporter = (await import('../src/TTYReporter.js')).default,
|
|
91
85
|
ttyReporter = new TTYReporter(options);
|
|
92
86
|
ttyReporter.testCounter = -2;
|
|
@@ -107,11 +101,13 @@ const masterInitialization = async () => {
|
|
|
107
101
|
}
|
|
108
102
|
};
|
|
109
103
|
|
|
110
|
-
const
|
|
111
|
-
|
|
112
|
-
await
|
|
104
|
+
const mainProcess = async () => {
|
|
105
|
+
mainConfiguration();
|
|
106
|
+
await mainInitialization();
|
|
113
107
|
await selectTimer();
|
|
114
108
|
|
|
109
|
+
process.on('uncaughtException', error => console.error('UNHANDLED ERROR:', error));
|
|
110
|
+
|
|
115
111
|
const rootState = new State(null, {callback: getReporter(), failOnce: options.failOnce}),
|
|
116
112
|
worker = new TestWorker(event => rootState.emit(event), parallel, options);
|
|
117
113
|
|
|
@@ -191,7 +187,6 @@ const workerProcess = async () => {
|
|
|
191
187
|
await import(name);
|
|
192
188
|
} catch (error) {
|
|
193
189
|
reject(error);
|
|
194
|
-
return;
|
|
195
190
|
}
|
|
196
191
|
});
|
|
197
192
|
process.send({started: true});
|
|
@@ -204,11 +199,8 @@ const workerProcess = async () => {
|
|
|
204
199
|
}
|
|
205
200
|
};
|
|
206
201
|
|
|
207
|
-
const main =
|
|
208
|
-
if (cluster.
|
|
209
|
-
|
|
210
|
-
} else if (cluster.isWorker) {
|
|
211
|
-
await workerProcess();
|
|
212
|
-
}
|
|
202
|
+
const main = () => {
|
|
203
|
+
if (cluster.isWorker) return workerProcess();
|
|
204
|
+
return mainProcess();
|
|
213
205
|
};
|
|
214
206
|
main().catch(error => console.error('ERROR:', error));
|
package/index.js
CHANGED
|
@@ -18,7 +18,7 @@ defer(async () => {
|
|
|
18
18
|
if (typeof window.__tape6_flags == 'string') {
|
|
19
19
|
flags = window.__tape6_flags;
|
|
20
20
|
} else if (window.location.search) {
|
|
21
|
-
flags = (new URLSearchParams(window.location.search.
|
|
21
|
+
flags = (new URLSearchParams(window.location.search.substring(1))).get('flags') || '';
|
|
22
22
|
}
|
|
23
23
|
} else if (isNode) {
|
|
24
24
|
flags = process.env.TAPE6_FLAGS || '';
|
|
@@ -33,14 +33,14 @@ defer(async () => {
|
|
|
33
33
|
let reporter = getReporter();
|
|
34
34
|
if (!reporter) {
|
|
35
35
|
if (isBrowser) {
|
|
36
|
-
const id = window.__tape6_id || (new URLSearchParams(window.location.search.
|
|
36
|
+
const id = window.__tape6_id || (new URLSearchParams(window.location.search.substring(1))).get('id');
|
|
37
37
|
if (typeof window.__tape6_reporter == 'function') {
|
|
38
38
|
reporter = event => window.__tape6_reporter(id, event);
|
|
39
39
|
} else if (window.parent && typeof window.parent.__tape6_reporter == 'function') {
|
|
40
40
|
reporter = event => window.parent.__tape6_reporter(id, event);
|
|
41
41
|
}
|
|
42
42
|
} else if (isNode) {
|
|
43
|
-
if (
|
|
43
|
+
if (!process.env.TAPE6_TAP) {
|
|
44
44
|
const TTYReporter = (await import('./src/TTYReporter.js')).default,
|
|
45
45
|
ttyReporter = new TTYReporter(options);
|
|
46
46
|
reporter = ttyReporter.report.bind(ttyReporter);
|
|
@@ -66,8 +66,8 @@ defer(async () => {
|
|
|
66
66
|
|
|
67
67
|
if (isNode) {
|
|
68
68
|
!process.env.TAPE6_WORKER && process.exit(rootState.failed > 0 ? 1 : 0);
|
|
69
|
-
} else if (typeof
|
|
70
|
-
|
|
69
|
+
} else if (typeof __tape6_reportResults == 'function') {
|
|
70
|
+
__tape6_reportResults(rootState.failed > 0 ? 'failure' : 'success');
|
|
71
71
|
}
|
|
72
72
|
});
|
|
73
73
|
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "tape-six",
|
|
3
|
-
"version": "0.9.
|
|
3
|
+
"version": "0.9.4",
|
|
4
4
|
"description": "TAP for the modern JavaScript (ES6).",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "index.js",
|
|
@@ -10,6 +10,8 @@
|
|
|
10
10
|
},
|
|
11
11
|
"bin": {
|
|
12
12
|
"tape6": "bin/tape6.js",
|
|
13
|
+
"tape6-bun": "bin/tape6-bun.js",
|
|
14
|
+
"tape6-deno": "bin/tape6-deno.js",
|
|
13
15
|
"tape6-server": "bin/tape6-server.js"
|
|
14
16
|
},
|
|
15
17
|
"scripts": {
|
|
@@ -18,7 +20,7 @@
|
|
|
18
20
|
"copyDeep6": "node scripts/copyFolder.js --src ./vendors/deep6/src --dst ./src/deep6 --clear",
|
|
19
21
|
"build": "npm run copyDeep6",
|
|
20
22
|
"prepublishOnly": "npm run build",
|
|
21
|
-
"test": "node ./bin/tape6.js"
|
|
23
|
+
"test": "node ./bin/tape6.js --flags FO"
|
|
22
24
|
},
|
|
23
25
|
"github": "http://github.com/uhop/tape-six",
|
|
24
26
|
"repository": {
|
|
@@ -50,6 +52,6 @@
|
|
|
50
52
|
]
|
|
51
53
|
},
|
|
52
54
|
"devDependencies": {
|
|
53
|
-
"puppeteer": "^
|
|
55
|
+
"puppeteer": "^22.8.1"
|
|
54
56
|
}
|
|
55
57
|
}
|
package/src/TTYReporter.js
CHANGED
|
@@ -1,4 +1,12 @@
|
|
|
1
|
-
import
|
|
1
|
+
import process from 'node:process';
|
|
2
|
+
import {
|
|
3
|
+
stringRep,
|
|
4
|
+
normalizeBox,
|
|
5
|
+
padBox,
|
|
6
|
+
padBoxLeft,
|
|
7
|
+
drawBox,
|
|
8
|
+
stackHorizontally
|
|
9
|
+
} from './utils/box.js';
|
|
2
10
|
import {formatNumber, formatTime} from './utils/formatters.js';
|
|
3
11
|
|
|
4
12
|
// colors
|
|
@@ -19,15 +27,15 @@ class TTYReporter {
|
|
|
19
27
|
output = process.stdout,
|
|
20
28
|
renumberAsserts = false,
|
|
21
29
|
failureOnly = false,
|
|
22
|
-
showBanner =
|
|
30
|
+
showBanner = output.isTTY,
|
|
23
31
|
showTime = true,
|
|
24
|
-
showData =
|
|
32
|
+
showData = true,
|
|
25
33
|
showAssertNumber = false
|
|
26
34
|
} = {}) {
|
|
27
|
-
if (!output || !output.isTTY) throw Error('Module TTYReporter works only with TTY output streams.');
|
|
28
|
-
|
|
29
35
|
this.output = output;
|
|
30
|
-
this.hasColors =
|
|
36
|
+
this.hasColors =
|
|
37
|
+
this.output.isTTY &&
|
|
38
|
+
(typeof this.output.hasColors == 'function' ? this.output.hasColors(256) : true);
|
|
31
39
|
this.renumberAsserts = renumberAsserts;
|
|
32
40
|
this.failureOnly = failureOnly;
|
|
33
41
|
this.showBanner = showBanner;
|
|
@@ -35,7 +43,13 @@ class TTYReporter {
|
|
|
35
43
|
this.showData = showData;
|
|
36
44
|
this.showAssertNumber = showAssertNumber;
|
|
37
45
|
|
|
38
|
-
this.depth =
|
|
46
|
+
this.depth =
|
|
47
|
+
this.assertCounter =
|
|
48
|
+
this.failedAsserts =
|
|
49
|
+
this.successfulAsserts =
|
|
50
|
+
this.skippedAsserts =
|
|
51
|
+
this.todoAsserts =
|
|
52
|
+
0;
|
|
39
53
|
this.testCounter = -1;
|
|
40
54
|
this.technicalDepth = 0;
|
|
41
55
|
|
|
@@ -58,7 +72,7 @@ class TTYReporter {
|
|
|
58
72
|
this.failure = this.paint(failureStyle, reset);
|
|
59
73
|
this.skipped = this.paint(skippedStyle, reset);
|
|
60
74
|
|
|
61
|
-
this.out('');
|
|
75
|
+
this.output.isTTY && this.out('');
|
|
62
76
|
}
|
|
63
77
|
paint(prefix, suffix = '\x1B[39m') {
|
|
64
78
|
return this.hasColors ? text => join(prefix, text, suffix) : text => text;
|
|
@@ -78,12 +92,16 @@ class TTYReporter {
|
|
|
78
92
|
return this;
|
|
79
93
|
}
|
|
80
94
|
report(event) {
|
|
81
|
-
this.output.
|
|
82
|
-
|
|
95
|
+
if (this.output.isTTY) {
|
|
96
|
+
this.output.moveCursor(0, -1);
|
|
97
|
+
this.output.clearLine(0);
|
|
98
|
+
}
|
|
83
99
|
let text;
|
|
84
100
|
switch (event.type) {
|
|
85
101
|
case 'test':
|
|
86
|
-
this.depth > this.technicalDepth &&
|
|
102
|
+
this.depth > this.technicalDepth &&
|
|
103
|
+
!this.failureOnly &&
|
|
104
|
+
this.out('\u25CB ' + (event.name || 'anonymous test'));
|
|
87
105
|
++this.depth;
|
|
88
106
|
++this.testCounter;
|
|
89
107
|
this.testStack.push({name: event.name, lines: this.lines, fail: false});
|
|
@@ -108,7 +126,7 @@ class TTYReporter {
|
|
|
108
126
|
total = state.asserts - state.skipped,
|
|
109
127
|
success = total - state.failed;
|
|
110
128
|
|
|
111
|
-
if (!this.showBanner) {
|
|
129
|
+
if (!this.showBanner || !this.output.isTTY) {
|
|
112
130
|
this.out(
|
|
113
131
|
this.blackBg(
|
|
114
132
|
' ' +
|
|
@@ -139,7 +157,14 @@ class TTYReporter {
|
|
|
139
157
|
box1 = drawBox(box1);
|
|
140
158
|
box1 = padBox(box1, 0, 3);
|
|
141
159
|
box1 = normalizeBox(
|
|
142
|
-
[
|
|
160
|
+
[
|
|
161
|
+
...box1,
|
|
162
|
+
'',
|
|
163
|
+
'Passed: ' +
|
|
164
|
+
(event.fail
|
|
165
|
+
? formatNumber((total > 0 ? success / total : 1) * 100, 1) + '%'
|
|
166
|
+
: '100%')
|
|
167
|
+
],
|
|
143
168
|
' ',
|
|
144
169
|
'center'
|
|
145
170
|
);
|
|
@@ -161,7 +186,18 @@ class TTYReporter {
|
|
|
161
186
|
'left'
|
|
162
187
|
);
|
|
163
188
|
box2 = padBoxLeft(box2, 1);
|
|
164
|
-
box2 = stackHorizontally(
|
|
189
|
+
box2 = stackHorizontally(
|
|
190
|
+
normalizeBox([
|
|
191
|
+
'tests:',
|
|
192
|
+
'asserts:',
|
|
193
|
+
' passed:',
|
|
194
|
+
' failed:',
|
|
195
|
+
' skipped:',
|
|
196
|
+
' todo:',
|
|
197
|
+
'time:'
|
|
198
|
+
]),
|
|
199
|
+
box2
|
|
200
|
+
);
|
|
165
201
|
|
|
166
202
|
box2[0] = this.brightWhite(box2[0]);
|
|
167
203
|
// box2[1] = this.brightYellow(box2[1]);
|
|
@@ -205,7 +241,7 @@ class TTYReporter {
|
|
|
205
241
|
event.skip && ++this.skippedAsserts;
|
|
206
242
|
event.todo && ++this.todoAsserts;
|
|
207
243
|
if (!isFailed && this.failureOnly) break;
|
|
208
|
-
text =
|
|
244
|
+
text = event.fail ? '✗' : '✓';
|
|
209
245
|
if (this.showAssertNumber) {
|
|
210
246
|
text += ' ' + (this.renumberAsserts ? ++this.assertCounter : event.id);
|
|
211
247
|
}
|
|
@@ -239,14 +275,17 @@ class TTYReporter {
|
|
|
239
275
|
if (event.hasOwnProperty('actual')) {
|
|
240
276
|
this.out(this.lowWhite(' actual: ') + this.formatValue(event.actual));
|
|
241
277
|
}
|
|
242
|
-
const stack =
|
|
278
|
+
const stack =
|
|
279
|
+
event.actual && event.actual.type === 'Error' && typeof event.actual.stack == 'string'
|
|
280
|
+
? event.actual.stack
|
|
281
|
+
: event.marker.stack;
|
|
243
282
|
if (typeof stack == 'string') {
|
|
244
283
|
this.out(this.lowWhite(' stack: |-'));
|
|
245
284
|
stack.split('\n').forEach(line => this.out(this.lowWhite(' ' + line)));
|
|
246
285
|
}
|
|
247
286
|
break;
|
|
248
287
|
}
|
|
249
|
-
this.showScore();
|
|
288
|
+
this.output.isTTY && this.showScore();
|
|
250
289
|
}
|
|
251
290
|
showScore() {
|
|
252
291
|
this.out(
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
import {StopTest} from '../State.js';
|
|
2
|
+
import EventServer from '../utils/EventServer.js';
|
|
3
|
+
|
|
4
|
+
const utilName = new URL('../test.js', import.meta.url),
|
|
5
|
+
baseName = new URL('../../', import.meta.url);
|
|
6
|
+
|
|
7
|
+
export default class TestWorker extends EventServer {
|
|
8
|
+
constructor(reporter, numberOfTasks = navigator.hardwareConcurrency, options) {
|
|
9
|
+
super(reporter, numberOfTasks, options);
|
|
10
|
+
this.counter = 0;
|
|
11
|
+
this.idToWorker = {};
|
|
12
|
+
}
|
|
13
|
+
makeTask(fileName) {
|
|
14
|
+
const testName = new URL(fileName, baseName),
|
|
15
|
+
id = String(++this.counter),
|
|
16
|
+
worker = new Worker(new URL('./worker.js', import.meta.url), {
|
|
17
|
+
type: 'module'
|
|
18
|
+
});
|
|
19
|
+
this.idToWorker[id] = worker;
|
|
20
|
+
worker.addEventListener('message', event => {
|
|
21
|
+
const msg = event.data;
|
|
22
|
+
try {
|
|
23
|
+
this.report(id, msg);
|
|
24
|
+
} catch (error) {
|
|
25
|
+
if (error instanceof StopTest) {
|
|
26
|
+
console.error('# immediate StopTest:', error.message || 'StopTest is activated');
|
|
27
|
+
worker.terminate();
|
|
28
|
+
process.exit(1);
|
|
29
|
+
}
|
|
30
|
+
throw error;
|
|
31
|
+
}
|
|
32
|
+
if (msg.type === 'end' && msg.test === 0) {
|
|
33
|
+
this.close(id);
|
|
34
|
+
return;
|
|
35
|
+
}
|
|
36
|
+
});
|
|
37
|
+
worker.addEventListener('error', error => {
|
|
38
|
+
this.report(id, {
|
|
39
|
+
type: 'comment',
|
|
40
|
+
name: 'fail to load: ' + (error.message || 'Worker error`'),
|
|
41
|
+
test: 0
|
|
42
|
+
});
|
|
43
|
+
this.close(id);
|
|
44
|
+
});
|
|
45
|
+
worker.addEventListener('messageerror', error => {
|
|
46
|
+
this.report(id, {
|
|
47
|
+
type: 'comment',
|
|
48
|
+
name: 'fail to load: ' + (error.message || 'Worker error`'),
|
|
49
|
+
test: 0
|
|
50
|
+
});
|
|
51
|
+
this.close(id);
|
|
52
|
+
});
|
|
53
|
+
worker.postMessage({testName: testName.href, utilName: utilName.href});
|
|
54
|
+
return id;
|
|
55
|
+
}
|
|
56
|
+
destroyTask(id) {
|
|
57
|
+
const worker = this.idToWorker[id];
|
|
58
|
+
if (worker) {
|
|
59
|
+
worker.terminate();
|
|
60
|
+
delete this.idToWorker[id];
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
}
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
const sanitizeMsg = msg => {
|
|
2
|
+
if (msg.type !== 'end') return msg;
|
|
3
|
+
const state = {};
|
|
4
|
+
for (const [key, value] of Object.entries(msg.data)) {
|
|
5
|
+
if (typeof value != 'number') continue;
|
|
6
|
+
state[key] = value;
|
|
7
|
+
}
|
|
8
|
+
return {...msg, data: state};
|
|
9
|
+
};
|
|
10
|
+
|
|
11
|
+
addEventListener('message', async event => {
|
|
12
|
+
const msg = event.data;
|
|
13
|
+
try {
|
|
14
|
+
const {setReporter} = await import(msg.utilName);
|
|
15
|
+
setReporter(msg => postMessage(sanitizeMsg(msg)));
|
|
16
|
+
await import(msg.testName);
|
|
17
|
+
} catch (error) {
|
|
18
|
+
postMessage({type: 'comment', name: 'fail to load: ' + error.message, test: 0});
|
|
19
|
+
close();
|
|
20
|
+
}
|
|
21
|
+
});
|
|
@@ -0,0 +1,82 @@
|
|
|
1
|
+
import {StopTest} from '../State.js';
|
|
2
|
+
import EventServer from '../utils/EventServer.js';
|
|
3
|
+
|
|
4
|
+
const utilName = new URL('../test.js', import.meta.url),
|
|
5
|
+
baseName = new URL('../../', import.meta.url);
|
|
6
|
+
|
|
7
|
+
export default class TestWorker extends EventServer {
|
|
8
|
+
constructor(reporter, numberOfTasks = navigator.hardwareConcurrency, options) {
|
|
9
|
+
super(reporter, numberOfTasks, options);
|
|
10
|
+
this.counter = 0;
|
|
11
|
+
this.idToWorker = {};
|
|
12
|
+
}
|
|
13
|
+
makeTask(fileName) {
|
|
14
|
+
const testName = new URL(fileName, baseName),
|
|
15
|
+
id = String(++this.counter),
|
|
16
|
+
worker = new Worker(
|
|
17
|
+
new URL(
|
|
18
|
+
'data:text/javascript;charset=utf-8,' +
|
|
19
|
+
encodeURIComponent(`
|
|
20
|
+
import {setReporter} from ${JSON.stringify(utilName.href)};
|
|
21
|
+
|
|
22
|
+
const sanitizeMsg = msg => {
|
|
23
|
+
if (msg.type !== 'end') return msg;
|
|
24
|
+
const state = {};
|
|
25
|
+
for (const [key, value] of Object.entries(msg.data)) {
|
|
26
|
+
if (typeof value != 'number') continue;
|
|
27
|
+
state[key] = value;
|
|
28
|
+
}
|
|
29
|
+
return {...msg, data: state};
|
|
30
|
+
};
|
|
31
|
+
|
|
32
|
+
setReporter(msg => postMessage(sanitizeMsg(msg)));
|
|
33
|
+
|
|
34
|
+
try {
|
|
35
|
+
await import(${JSON.stringify(testName.href)});
|
|
36
|
+
} catch (error) {
|
|
37
|
+
postMessage({type: 'comment', name: 'fail to load: ' + error.message, test: 0});
|
|
38
|
+
close();
|
|
39
|
+
}
|
|
40
|
+
`)
|
|
41
|
+
),
|
|
42
|
+
{
|
|
43
|
+
type: 'module'
|
|
44
|
+
// deno: {permissions: 'inherit'}
|
|
45
|
+
}
|
|
46
|
+
);
|
|
47
|
+
this.idToWorker[id] = worker;
|
|
48
|
+
worker.addEventListener('message', event => {
|
|
49
|
+
const msg = event.data;
|
|
50
|
+
try {
|
|
51
|
+
this.report(id, msg);
|
|
52
|
+
} catch (error) {
|
|
53
|
+
if (error instanceof StopTest) {
|
|
54
|
+
console.error('# immediate StopTest:', error.message || 'StopTest is activated');
|
|
55
|
+
worker.terminate();
|
|
56
|
+
Deno.exit(1);
|
|
57
|
+
}
|
|
58
|
+
throw error;
|
|
59
|
+
}
|
|
60
|
+
if (msg.type === 'end' && msg.test === 0) {
|
|
61
|
+
this.close(id);
|
|
62
|
+
return;
|
|
63
|
+
}
|
|
64
|
+
});
|
|
65
|
+
worker.addEventListener('error', error => {
|
|
66
|
+
this.report(id, {
|
|
67
|
+
type: 'comment',
|
|
68
|
+
name: 'fail to load: ' + (error.message || 'Worker error`'),
|
|
69
|
+
test: 0
|
|
70
|
+
});
|
|
71
|
+
this.close(id);
|
|
72
|
+
});
|
|
73
|
+
return id;
|
|
74
|
+
}
|
|
75
|
+
destroyTask(id) {
|
|
76
|
+
const worker = this.idToWorker[id];
|
|
77
|
+
if (worker) {
|
|
78
|
+
worker.terminate();
|
|
79
|
+
delete this.idToWorker[id];
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
}
|
package/src/node/TestWorker.js
CHANGED
|
@@ -1,6 +1,8 @@
|
|
|
1
|
-
import cluster from 'cluster';
|
|
2
|
-
import os from 'os';
|
|
1
|
+
import cluster from 'node:cluster';
|
|
2
|
+
import os from 'node:os';
|
|
3
|
+
import process from 'node:process';
|
|
3
4
|
|
|
5
|
+
import {StopTest} from '../State.js';
|
|
4
6
|
import EventServer from '../utils/EventServer.js';
|
|
5
7
|
|
|
6
8
|
export default class TestWorker extends EventServer {
|
|
@@ -13,14 +15,22 @@ export default class TestWorker extends EventServer {
|
|
|
13
15
|
worker.on('message', msg => {
|
|
14
16
|
if (msg.started) {
|
|
15
17
|
worker.send({id, fileName, options: this.options});
|
|
16
|
-
|
|
17
|
-
let done = false;
|
|
18
|
-
msg.events.forEach(event => {
|
|
19
|
-
this.report(id, event);
|
|
20
|
-
if (event.type === 'end' && event.test === 0) done = true;
|
|
21
|
-
});
|
|
22
|
-
worker.send(done? {done: true} : {received: true});
|
|
18
|
+
return;
|
|
23
19
|
}
|
|
20
|
+
let done = false;
|
|
21
|
+
msg.events.forEach(event => {
|
|
22
|
+
try {
|
|
23
|
+
this.report(id, event);
|
|
24
|
+
} catch (error) {
|
|
25
|
+
if (error instanceof StopTest) {
|
|
26
|
+
console.error('# immediate StopTest:', error.message || 'StopTest is activated');
|
|
27
|
+
process.exit(1);
|
|
28
|
+
}
|
|
29
|
+
throw error;
|
|
30
|
+
}
|
|
31
|
+
if (event.type === 'end' && event.test === 0) done = true;
|
|
32
|
+
});
|
|
33
|
+
worker.send(done ? {done: true} : {received: true});
|
|
24
34
|
});
|
|
25
35
|
worker.on('exit', (code, signal) => {
|
|
26
36
|
let errorMsg = '';
|
package/src/node/config.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import {promises as fsp} from 'fs';
|
|
2
|
-
import path from 'path';
|
|
1
|
+
import {promises as fsp} from 'node:fs';
|
|
2
|
+
import path from 'node:path';
|
|
3
3
|
|
|
4
4
|
import {listing, wildToRe} from './listing.js';
|
|
5
5
|
import {union, exclude} from '../utils/fileSets.js';
|
|
@@ -8,7 +8,7 @@ export const resolvePatterns = async (rootFolder, patterns) => {
|
|
|
8
8
|
let result = [];
|
|
9
9
|
for (const item of patterns) {
|
|
10
10
|
if (item.length && item[0] == '!') {
|
|
11
|
-
result = exclude(result, wildToRe(rootFolder, item.
|
|
11
|
+
result = exclude(result, wildToRe(rootFolder, item.substring(1)));
|
|
12
12
|
} else {
|
|
13
13
|
result = union(result, await listing(rootFolder, item));
|
|
14
14
|
}
|
package/src/node/listing.js
CHANGED
package/src/test.js
CHANGED
|
@@ -164,9 +164,9 @@ Tester.prototype.todo = async function todo(name, options, testFn) {
|
|
|
164
164
|
options = processArgs(name, options, testFn);
|
|
165
165
|
if (this.state.skip) {
|
|
166
166
|
this.comment('SKIP test: ' + options.name);
|
|
167
|
-
|
|
168
|
-
await runTests(this.state, [{options: {...options, todo: true}}]);
|
|
167
|
+
return;
|
|
169
168
|
}
|
|
169
|
+
await runTests(this.state, [{options: {...options, todo: true}}]);
|
|
170
170
|
};
|
|
171
171
|
|
|
172
172
|
Tester.prototype.asPromise = async function asPromise(name, options, testFn) {
|
package/src/utils/box.js
CHANGED
|
@@ -24,7 +24,7 @@ export const normalizeBox = (strings, symbol = ' ', align = 'right') => {
|
|
|
24
24
|
return padding + s;
|
|
25
25
|
case 'center':
|
|
26
26
|
const half = padding.length >> 1;
|
|
27
|
-
return padding.
|
|
27
|
+
return padding.substring(0, half) + s + padding.substring(half);
|
|
28
28
|
}
|
|
29
29
|
return s + padding;
|
|
30
30
|
});
|
package/src/utils/defer.js
CHANGED
|
@@ -1,32 +1,57 @@
|
|
|
1
|
-
let deferImplementation;
|
|
1
|
+
let deferImplementation = globalThis.setTimeout;
|
|
2
2
|
|
|
3
3
|
// initialize the variable
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
4
|
+
do {
|
|
5
|
+
// The selection below doesn't work
|
|
6
|
+
// if (typeof queueMicrotask == 'function') {
|
|
7
|
+
// deferImplementation = globalThis.queueMicrotask;
|
|
8
|
+
// console.log('\nSelected: queueMicrotask()\n');
|
|
9
|
+
// break;
|
|
10
|
+
// }
|
|
11
|
+
|
|
12
|
+
// The selection below doesn't work in Bun
|
|
13
|
+
// if (typeof process == 'object' && typeof process?.nextTick == 'function') {
|
|
14
|
+
// deferImplementation = process.nextTick;
|
|
15
|
+
// break;
|
|
16
|
+
// }
|
|
17
|
+
|
|
18
|
+
if (typeof setImmediate == 'function') {
|
|
19
|
+
deferImplementation = setImmediate;
|
|
20
|
+
break;
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
if (typeof window != 'object') break;
|
|
24
|
+
// below are browser-only implementations
|
|
25
|
+
|
|
26
|
+
if (typeof window.requestIdleCallback == 'function') {
|
|
27
|
+
deferImplementation = window.requestIdleCallback;
|
|
28
|
+
break;
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
if (typeof window.postMessage == 'function' && typeof window.addEventListener == 'function') {
|
|
32
|
+
let buffer = [];
|
|
33
|
+
window.addEventListener(
|
|
34
|
+
'message',
|
|
35
|
+
evt => {
|
|
36
|
+
const src = evt.source;
|
|
37
|
+
if ((src === window || src === null) && evt.data === 'tape6-process-tick') {
|
|
38
|
+
evt.stopPropagation();
|
|
39
|
+
if (buffer.length) {
|
|
40
|
+
const tasks = buffer.slice(0);
|
|
41
|
+
buffer = [];
|
|
42
|
+
tasks.forEach(fn => fn());
|
|
43
|
+
}
|
|
18
44
|
}
|
|
19
|
-
}
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
}
|
|
28
|
-
|
|
29
|
-
}
|
|
45
|
+
},
|
|
46
|
+
true
|
|
47
|
+
);
|
|
48
|
+
deferImplementation = fn => {
|
|
49
|
+
buffer.push(fn);
|
|
50
|
+
window.postMessage('tape6-process-tick', '*');
|
|
51
|
+
};
|
|
52
|
+
break;
|
|
53
|
+
}
|
|
54
|
+
} while (false);
|
|
30
55
|
|
|
31
56
|
const defer = fn => deferImplementation(fn);
|
|
32
57
|
|
package/src/utils/formatters.js
CHANGED
|
@@ -4,8 +4,8 @@ export const formatNumber = (n, precision = 0) => {
|
|
|
4
4
|
if (!f && i.length <= 3) return n < 0 ? '-' + s : s;
|
|
5
5
|
const parts = [];
|
|
6
6
|
let start = i.length % 3;
|
|
7
|
-
start && parts.push(i.
|
|
8
|
-
for (; start < i.length; start += 3) parts.push(i.
|
|
7
|
+
start && parts.push(i.substring(0, start));
|
|
8
|
+
for (; start < i.length; start += 3) parts.push(i.substring(start, start + 3));
|
|
9
9
|
let result = parts.join(',');
|
|
10
10
|
f && !/^0*$/.test(f) && (result += '.' + f.replace(/0+$/, ''));
|
|
11
11
|
return n < 0 ? '-' + result : result;
|
package/src/utils/timer.js
CHANGED
|
@@ -13,7 +13,7 @@ export const selectTimer = async () => {
|
|
|
13
13
|
if (typeof process == 'object' && typeof process.exit == 'function') {
|
|
14
14
|
// Node
|
|
15
15
|
try {
|
|
16
|
-
const {performance} = await import('perf_hooks');
|
|
16
|
+
const {performance} = await import('node:perf_hooks');
|
|
17
17
|
setTimer(performance);
|
|
18
18
|
return;
|
|
19
19
|
} catch (error) {
|
package/webApp/TestWorker.js
CHANGED
|
@@ -27,8 +27,10 @@ export default class TestWorker extends EventServer {
|
|
|
27
27
|
iframe.contentWindow.document.open();
|
|
28
28
|
iframe.contentWindow.document.write(`
|
|
29
29
|
<!doctype html>
|
|
30
|
-
<html>
|
|
30
|
+
<html lang="en">
|
|
31
31
|
<head>
|
|
32
|
+
<meta charset="utf-8" />
|
|
33
|
+
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
|
32
34
|
<title>Test IFRAME</title>
|
|
33
35
|
<script type="module">
|
|
34
36
|
window.__tape6_id = ${JSON.stringify(id)};
|
package/webApp/index.html
CHANGED
|
@@ -1,8 +1,9 @@
|
|
|
1
1
|
<!DOCTYPE html>
|
|
2
|
-
<html>
|
|
2
|
+
<html lang="en">
|
|
3
3
|
<head>
|
|
4
|
-
<
|
|
4
|
+
<meta charset="utf-8" />
|
|
5
5
|
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
|
6
|
+
<title>Tape-six test runner</title>
|
|
6
7
|
<link rel="stylesheet" href="./style.css" />
|
|
7
8
|
<link rel="stylesheet" href="./tape6-donut.css" />
|
|
8
9
|
<script type="module" src="./tape6-donut.js"></script>
|
|
@@ -57,10 +58,18 @@
|
|
|
57
58
|
</div>
|
|
58
59
|
|
|
59
60
|
<form class="tile tools">
|
|
60
|
-
Show:
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
61
|
+
Show: <label class="tape6-toggle"
|
|
62
|
+
><input type="checkbox" name="failed-only" value="0" /><span class="toggle"></span
|
|
63
|
+
> failed only</label
|
|
64
|
+
>
|
|
65
|
+
<label class="tape6-toggle"
|
|
66
|
+
><input type="checkbox" name="show-data" value="0" /><span class="toggle"></span
|
|
67
|
+
> data</label
|
|
68
|
+
>
|
|
69
|
+
<label class="tape6-toggle"
|
|
70
|
+
><input type="checkbox" name="show-stack" value="0" /><span class="toggle"></span
|
|
71
|
+
> stack</label
|
|
72
|
+
>
|
|
64
73
|
</form>
|
|
65
74
|
|
|
66
75
|
<div class="tile report collapse-passed"></div>
|
package/webApp/index.js
CHANGED
|
@@ -17,7 +17,7 @@ let flags = '',
|
|
|
17
17
|
patterns = [];
|
|
18
18
|
|
|
19
19
|
if (window.location.search) {
|
|
20
|
-
const searchParams = new URLSearchParams(window.location.search.
|
|
20
|
+
const searchParams = new URLSearchParams(window.location.search.substring(1));
|
|
21
21
|
flags = searchParams.get('flags') || '';
|
|
22
22
|
parallel = searchParams.get('par') || '';
|
|
23
23
|
if (parallel && !isNaN(parallel)) {
|
|
@@ -108,7 +108,7 @@ window.addEventListener('DOMContentLoaded', () => {
|
|
|
108
108
|
});
|
|
109
109
|
|
|
110
110
|
if (typeof window.__tape6_reportResults == 'function') {
|
|
111
|
-
window.__tape6_reportResults(rootState.failed > 0 ? 'failure' : 'success');
|
|
111
|
+
await window.__tape6_reportResults(rootState.failed > 0 ? 'failure' : 'success');
|
|
112
112
|
}
|
|
113
113
|
});
|
|
114
114
|
});
|