tape-six 1.7.4 → 1.7.6
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 +2 -0
- package/TESTING.md +2 -0
- package/bin/tape6-bun.js +23 -99
- package/bin/tape6-deno.js +23 -99
- package/bin/tape6-node.js +18 -111
- package/bin/tape6-seq.js +23 -79
- package/bin/tape6-server.js +6 -0
- package/index.js +2 -2
- package/llms-full.txt +2 -1
- package/package.json +2 -2
- package/src/reporters/TTYReporter.js +2 -2
- package/src/utils/config.js +188 -0
- package/web-app/favicon.ico +0 -0
- package/web-app/favicon.png +0 -0
- package/web-app/index.html +2 -0
package/README.md
CHANGED
|
@@ -420,6 +420,8 @@ Test output can be controlled by flags. See [Supported flags](https://github.com
|
|
|
420
420
|
|
|
421
421
|
The most recent releases:
|
|
422
422
|
|
|
423
|
+
- 1.7.6 _Bug fix: `processArgs` alias canonicalization, dead code removal, doc fix._
|
|
424
|
+
- 1.7.5 _Refactored CLI runners, added `--info` option, renamed `dontCaptureConsole` to `noConsoleCapture`._
|
|
423
425
|
- 1.7.4 _Bug fixes: uncaught exception handling, `StopTest` suppression in parallel runners._
|
|
424
426
|
- 1.7.3 _Bug fixes in reporters, runners, and assertions. Documentation corrections and improvements._
|
|
425
427
|
- 1.7.2 _Minor internal refactoring and fixes._
|
package/TESTING.md
CHANGED
|
@@ -464,6 +464,7 @@ deno run -A tests/test-example.js # Deno
|
|
|
464
464
|
npx tape6 --flags FO # parallel (worker threads)
|
|
465
465
|
npx tape6-seq --flags FO # sequential (in-process, no workers)
|
|
466
466
|
npx tape6 --par 4 --flags FO # limit to 4 workers
|
|
467
|
+
npx tape6 --info --flags FO # show config and exit without running tests
|
|
467
468
|
```
|
|
468
469
|
|
|
469
470
|
**`tape6` vs `tape6-seq`**: The default `tape6` runner spawns worker threads to run test files in parallel — faster, but each file runs in its own isolated context. `tape6-seq` runs all test files sequentially in a single process — slower, but useful for debugging, for tests that share state, or when worker threads are unavailable.
|
|
@@ -514,6 +515,7 @@ Common combinations: `FO` (failures only + stop at first), `FOT` (+ show time).
|
|
|
514
515
|
- `TAPE6_PAR` — number of parallel workers.
|
|
515
516
|
- `TAPE6_TAP` — force TAP output format.
|
|
516
517
|
- `TAPE6_JSONL` — force JSONL output format.
|
|
518
|
+
- `TAPE6_MIN` — force minimal output format.
|
|
517
519
|
|
|
518
520
|
## Configuring test discovery
|
|
519
521
|
|
package/bin/tape6-bun.js
CHANGED
|
@@ -2,24 +2,14 @@
|
|
|
2
2
|
|
|
3
3
|
import {fileURLToPath} from 'node:url';
|
|
4
4
|
|
|
5
|
-
import {
|
|
6
|
-
resolveTests,
|
|
7
|
-
resolvePatterns,
|
|
8
|
-
getReporterFileName,
|
|
9
|
-
getReporterType
|
|
10
|
-
} from '../src/utils/config.js';
|
|
5
|
+
import {getOptions, initFiles, initReporter, showInfo} from '../src/utils/config.js';
|
|
11
6
|
|
|
12
7
|
import {getReporter, setReporter} from '../src/test.js';
|
|
13
8
|
import {selectTimer} from '../src/utils/timer.js';
|
|
14
9
|
|
|
15
10
|
import TestWorker from '../src/runners/bun/TestWorker.js';
|
|
16
11
|
|
|
17
|
-
const
|
|
18
|
-
rootFolder = Bun.cwd;
|
|
19
|
-
|
|
20
|
-
let flags = '',
|
|
21
|
-
parallel = '',
|
|
22
|
-
files = [];
|
|
12
|
+
const rootFolder = Bun.cwd;
|
|
23
13
|
|
|
24
14
|
const showSelf = () => {
|
|
25
15
|
const self = new URL(import.meta.url);
|
|
@@ -31,101 +21,35 @@ const showSelf = () => {
|
|
|
31
21
|
process.exit(0);
|
|
32
22
|
};
|
|
33
23
|
|
|
34
|
-
const config = () => {
|
|
35
|
-
if (Bun.argv.includes('--self')) showSelf();
|
|
36
|
-
|
|
37
|
-
const optionNames = {
|
|
38
|
-
f: 'failureOnly',
|
|
39
|
-
t: 'showTime',
|
|
40
|
-
b: 'showBanner',
|
|
41
|
-
d: 'showData',
|
|
42
|
-
o: 'failOnce',
|
|
43
|
-
n: 'showAssertNumber',
|
|
44
|
-
m: 'monochrome',
|
|
45
|
-
c: 'dontCaptureConsole',
|
|
46
|
-
h: 'hideStreams'
|
|
47
|
-
};
|
|
48
|
-
|
|
49
|
-
let parIsSet = false;
|
|
50
|
-
|
|
51
|
-
for (let i = 2; i < Bun.argv.length; ++i) {
|
|
52
|
-
const arg = Bun.argv[i];
|
|
53
|
-
if (arg == '-f' || arg == '--flags') {
|
|
54
|
-
if (++i < Bun.argv.length) {
|
|
55
|
-
flags += Bun.argv[i];
|
|
56
|
-
}
|
|
57
|
-
continue;
|
|
58
|
-
}
|
|
59
|
-
if (arg == '-p' || arg == '--par') {
|
|
60
|
-
if (++i < Bun.argv.length) {
|
|
61
|
-
parallel = Bun.argv[i];
|
|
62
|
-
parIsSet = true;
|
|
63
|
-
if (!parallel || isNaN(parallel)) {
|
|
64
|
-
parallel = '';
|
|
65
|
-
parIsSet = false;
|
|
66
|
-
}
|
|
67
|
-
}
|
|
68
|
-
continue;
|
|
69
|
-
}
|
|
70
|
-
files.push(arg);
|
|
71
|
-
}
|
|
72
|
-
|
|
73
|
-
flags = (Bun.env.TAPE6_FLAGS || '') + flags;
|
|
74
|
-
for (let i = 0; i < flags.length; ++i) {
|
|
75
|
-
const option = flags[i].toLowerCase(),
|
|
76
|
-
name = optionNames[option];
|
|
77
|
-
if (typeof name == 'string') options[name] = option !== flags[i];
|
|
78
|
-
}
|
|
79
|
-
options.flags = flags;
|
|
80
|
-
|
|
81
|
-
if (!parIsSet) {
|
|
82
|
-
parallel = Bun.env.TAPE6_PAR || parallel;
|
|
83
|
-
}
|
|
84
|
-
if (parallel) {
|
|
85
|
-
parallel = Math.max(0, +parallel);
|
|
86
|
-
if (parallel === Infinity) parallel = 0;
|
|
87
|
-
} else {
|
|
88
|
-
parallel = 0;
|
|
89
|
-
}
|
|
90
|
-
if (!parallel) parallel = globalThis.navigator?.hardwareConcurrency || 1;
|
|
91
|
-
};
|
|
92
|
-
|
|
93
|
-
const init = async () => {
|
|
94
|
-
const currentReporter = getReporter();
|
|
95
|
-
if (!currentReporter) {
|
|
96
|
-
const reporterType = getReporterType(),
|
|
97
|
-
reporterFile = getReporterFileName(reporterType),
|
|
98
|
-
CustomReporter = (await import('../src/reporters/' + reporterFile)).default,
|
|
99
|
-
hasColors = !(
|
|
100
|
-
options.monochrome ||
|
|
101
|
-
Bun.env.NO_COLOR ||
|
|
102
|
-
Bun.env.NODE_DISABLE_COLORS ||
|
|
103
|
-
Bun.env.FORCE_COLOR === '0'
|
|
104
|
-
),
|
|
105
|
-
customOptions = reporterType === 'tap' ? {useJson: true, hasColors} : {...options, hasColors},
|
|
106
|
-
customReporter = new CustomReporter(customOptions);
|
|
107
|
-
setReporter(customReporter);
|
|
108
|
-
}
|
|
109
|
-
|
|
110
|
-
if (files.length) {
|
|
111
|
-
files = await resolvePatterns(rootFolder, files);
|
|
112
|
-
} else {
|
|
113
|
-
files = await resolveTests(rootFolder, 'bun');
|
|
114
|
-
}
|
|
115
|
-
};
|
|
116
|
-
|
|
117
24
|
const main = async () => {
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
25
|
+
const currentOptions = getOptions({
|
|
26
|
+
'--self': showSelf,
|
|
27
|
+
'--info': {isValueRequired: false}
|
|
28
|
+
});
|
|
29
|
+
|
|
30
|
+
const [files] = await Promise.all([
|
|
31
|
+
initFiles(currentOptions.files, rootFolder),
|
|
32
|
+
initReporter(getReporter, setReporter, currentOptions.flags),
|
|
33
|
+
selectTimer()
|
|
34
|
+
]);
|
|
121
35
|
|
|
122
36
|
process.on('uncaughtException', (error, origin) => {
|
|
123
37
|
console.error('UNHANDLED ERROR:', origin, error);
|
|
124
38
|
process.exit(1);
|
|
125
39
|
});
|
|
126
40
|
|
|
41
|
+
if (currentOptions.optionFlags['--info'] === '') {
|
|
42
|
+
showInfo(currentOptions, files);
|
|
43
|
+
process.exit(0);
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
if (!files.length) {
|
|
47
|
+
console.log('No files found.');
|
|
48
|
+
process.exit(1);
|
|
49
|
+
}
|
|
50
|
+
|
|
127
51
|
const reporter = getReporter(),
|
|
128
|
-
worker = new TestWorker(reporter, parallel,
|
|
52
|
+
worker = new TestWorker(reporter, currentOptions.parallel, currentOptions.flags);
|
|
129
53
|
|
|
130
54
|
reporter.report({type: 'test', test: 0});
|
|
131
55
|
|
package/bin/tape6-deno.js
CHANGED
|
@@ -2,24 +2,14 @@
|
|
|
2
2
|
|
|
3
3
|
import {fileURLToPath} from 'node:url';
|
|
4
4
|
|
|
5
|
-
import {
|
|
6
|
-
resolveTests,
|
|
7
|
-
resolvePatterns,
|
|
8
|
-
getReporterFileName,
|
|
9
|
-
getReporterType
|
|
10
|
-
} from '../src/utils/config.js';
|
|
5
|
+
import {getOptions, initFiles, initReporter, showInfo} from '../src/utils/config.js';
|
|
11
6
|
|
|
12
7
|
import {getReporter, setReporter} from '../src/test.js';
|
|
13
8
|
import {selectTimer} from '../src/utils/timer.js';
|
|
14
9
|
|
|
15
10
|
import TestWorker from '../src/runners/deno/TestWorker.js';
|
|
16
11
|
|
|
17
|
-
const
|
|
18
|
-
rootFolder = Deno.cwd();
|
|
19
|
-
|
|
20
|
-
let flags = '',
|
|
21
|
-
parallel = '',
|
|
22
|
-
files = [];
|
|
12
|
+
const rootFolder = Deno.cwd();
|
|
23
13
|
|
|
24
14
|
const showSelf = () => {
|
|
25
15
|
const self = new URL(import.meta.url);
|
|
@@ -31,101 +21,35 @@ const showSelf = () => {
|
|
|
31
21
|
Deno.exit(0);
|
|
32
22
|
};
|
|
33
23
|
|
|
34
|
-
const config = () => {
|
|
35
|
-
if (Deno.args.includes('--self')) showSelf();
|
|
36
|
-
|
|
37
|
-
const optionNames = {
|
|
38
|
-
f: 'failureOnly',
|
|
39
|
-
t: 'showTime',
|
|
40
|
-
b: 'showBanner',
|
|
41
|
-
d: 'showData',
|
|
42
|
-
o: 'failOnce',
|
|
43
|
-
n: 'showAssertNumber',
|
|
44
|
-
m: 'monochrome',
|
|
45
|
-
c: 'dontCaptureConsole',
|
|
46
|
-
h: 'hideStreams'
|
|
47
|
-
};
|
|
48
|
-
|
|
49
|
-
let parIsSet = false;
|
|
50
|
-
|
|
51
|
-
for (let i = 0; i < Deno.args.length; ++i) {
|
|
52
|
-
const arg = Deno.args[i];
|
|
53
|
-
if (arg == '-f' || arg == '--flags') {
|
|
54
|
-
if (++i < Deno.args.length) {
|
|
55
|
-
flags += Deno.args[i];
|
|
56
|
-
}
|
|
57
|
-
continue;
|
|
58
|
-
}
|
|
59
|
-
if (arg == '-p' || arg == '--par') {
|
|
60
|
-
if (++i < Deno.args.length) {
|
|
61
|
-
parallel = Deno.args[i];
|
|
62
|
-
parIsSet = true;
|
|
63
|
-
if (!parallel || isNaN(parallel)) {
|
|
64
|
-
parallel = '';
|
|
65
|
-
parIsSet = false;
|
|
66
|
-
}
|
|
67
|
-
}
|
|
68
|
-
continue;
|
|
69
|
-
}
|
|
70
|
-
files.push(arg);
|
|
71
|
-
}
|
|
72
|
-
|
|
73
|
-
flags = (Deno.env.get('TAPE6_FLAGS') || '') + flags;
|
|
74
|
-
for (let i = 0; i < flags.length; ++i) {
|
|
75
|
-
const option = flags[i].toLowerCase(),
|
|
76
|
-
name = optionNames[option];
|
|
77
|
-
if (typeof name == 'string') options[name] = option !== flags[i];
|
|
78
|
-
}
|
|
79
|
-
options.flags = flags;
|
|
80
|
-
|
|
81
|
-
if (!parIsSet) {
|
|
82
|
-
parallel = Deno.env.get('TAPE6_PAR') || parallel;
|
|
83
|
-
}
|
|
84
|
-
if (parallel) {
|
|
85
|
-
parallel = Math.max(0, +parallel);
|
|
86
|
-
if (parallel === Infinity) parallel = 0;
|
|
87
|
-
} else {
|
|
88
|
-
parallel = 0;
|
|
89
|
-
}
|
|
90
|
-
if (!parallel) parallel = globalThis.navigator?.hardwareConcurrency || 1;
|
|
91
|
-
};
|
|
92
|
-
|
|
93
|
-
const init = async () => {
|
|
94
|
-
const currentReporter = getReporter();
|
|
95
|
-
if (!currentReporter) {
|
|
96
|
-
const reporterType = getReporterType(),
|
|
97
|
-
reporterFile = getReporterFileName(reporterType),
|
|
98
|
-
CustomReporter = (await import('../src/reporters/' + reporterFile)).default,
|
|
99
|
-
hasColors = !(
|
|
100
|
-
options.monochrome ||
|
|
101
|
-
Deno.env.get('NO_COLOR') ||
|
|
102
|
-
Deno.env.get('NODE_DISABLE_COLORS') ||
|
|
103
|
-
Deno.env.get('FORCE_COLOR') === '0'
|
|
104
|
-
),
|
|
105
|
-
customOptions = reporterType === 'tap' ? {useJson: true, hasColors} : {...options, hasColors},
|
|
106
|
-
customReporter = new CustomReporter(customOptions);
|
|
107
|
-
setReporter(customReporter);
|
|
108
|
-
}
|
|
109
|
-
|
|
110
|
-
if (files.length) {
|
|
111
|
-
files = await resolvePatterns(rootFolder, files);
|
|
112
|
-
} else {
|
|
113
|
-
files = await resolveTests(rootFolder, 'deno');
|
|
114
|
-
}
|
|
115
|
-
};
|
|
116
|
-
|
|
117
24
|
const main = async () => {
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
25
|
+
const currentOptions = getOptions({
|
|
26
|
+
'--self': showSelf,
|
|
27
|
+
'--info': {isValueRequired: false}
|
|
28
|
+
});
|
|
29
|
+
|
|
30
|
+
const [files] = await Promise.all([
|
|
31
|
+
initFiles(currentOptions.files, rootFolder),
|
|
32
|
+
initReporter(getReporter, setReporter, currentOptions.flags),
|
|
33
|
+
selectTimer()
|
|
34
|
+
]);
|
|
121
35
|
|
|
122
36
|
addEventListener('error', event => {
|
|
123
37
|
console.log('UNHANDLED ERROR:', event.message);
|
|
124
38
|
event.preventDefault();
|
|
125
39
|
});
|
|
126
40
|
|
|
41
|
+
if (currentOptions.optionFlags['--info'] === '') {
|
|
42
|
+
showInfo(currentOptions, files);
|
|
43
|
+
Deno.exit(0);
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
if (!files.length) {
|
|
47
|
+
console.log('No files found.');
|
|
48
|
+
Deno.exit(1);
|
|
49
|
+
}
|
|
50
|
+
|
|
127
51
|
const reporter = getReporter(),
|
|
128
|
-
worker = new TestWorker(reporter, parallel,
|
|
52
|
+
worker = new TestWorker(reporter, currentOptions.parallel, currentOptions.flags);
|
|
129
53
|
|
|
130
54
|
reporter.report({type: 'test', test: 0});
|
|
131
55
|
|
package/bin/tape6-node.js
CHANGED
|
@@ -1,27 +1,16 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
|
|
3
3
|
import process from 'node:process';
|
|
4
|
-
import os from 'node:os';
|
|
5
4
|
import {fileURLToPath} from 'node:url';
|
|
6
5
|
|
|
7
|
-
import {
|
|
8
|
-
resolveTests,
|
|
9
|
-
resolvePatterns,
|
|
10
|
-
getReporterFileName,
|
|
11
|
-
getReporterType
|
|
12
|
-
} from '../src/utils/config.js';
|
|
6
|
+
import {getOptions, initFiles, initReporter, showInfo} from '../src/utils/config.js';
|
|
13
7
|
|
|
14
8
|
import {getReporter, setReporter} from '../src/test.js';
|
|
15
9
|
import {selectTimer} from '../src/utils/timer.js';
|
|
16
10
|
|
|
17
11
|
import TestWorker from '../src/runners/node/TestWorker.js';
|
|
18
12
|
|
|
19
|
-
const
|
|
20
|
-
rootFolder = process.cwd();
|
|
21
|
-
|
|
22
|
-
let flags = '',
|
|
23
|
-
parallel = '',
|
|
24
|
-
files = [];
|
|
13
|
+
const rootFolder = process.cwd();
|
|
25
14
|
|
|
26
15
|
const showSelf = () => {
|
|
27
16
|
const self = new URL(import.meta.url);
|
|
@@ -33,117 +22,35 @@ const showSelf = () => {
|
|
|
33
22
|
process.exit(0);
|
|
34
23
|
};
|
|
35
24
|
|
|
36
|
-
const config = () => {
|
|
37
|
-
if (process.argv.includes('--self')) showSelf();
|
|
38
|
-
|
|
39
|
-
const optionNames = {
|
|
40
|
-
f: 'failureOnly',
|
|
41
|
-
t: 'showTime',
|
|
42
|
-
b: 'showBanner',
|
|
43
|
-
d: 'showData',
|
|
44
|
-
o: 'failOnce',
|
|
45
|
-
n: 'showAssertNumber',
|
|
46
|
-
m: 'monochrome',
|
|
47
|
-
c: 'dontCaptureConsole',
|
|
48
|
-
h: 'hideStreams'
|
|
49
|
-
};
|
|
50
|
-
|
|
51
|
-
let parIsSet = false;
|
|
52
|
-
|
|
53
|
-
for (let i = 2; i < process.argv.length; ++i) {
|
|
54
|
-
const arg = process.argv[i];
|
|
55
|
-
if (arg == '-f' || arg == '--flags') {
|
|
56
|
-
if (++i < process.argv.length) {
|
|
57
|
-
flags += process.argv[i];
|
|
58
|
-
}
|
|
59
|
-
continue;
|
|
60
|
-
}
|
|
61
|
-
if (arg == '-p' || arg == '--par') {
|
|
62
|
-
if (++i < process.argv.length) {
|
|
63
|
-
parallel = process.argv[i];
|
|
64
|
-
parIsSet = true;
|
|
65
|
-
if (!parallel || isNaN(parallel)) {
|
|
66
|
-
parallel = '';
|
|
67
|
-
parIsSet = false;
|
|
68
|
-
}
|
|
69
|
-
}
|
|
70
|
-
continue;
|
|
71
|
-
}
|
|
72
|
-
files.push(arg);
|
|
73
|
-
}
|
|
74
|
-
|
|
75
|
-
flags = (process.env.TAPE6_FLAGS || '') + flags;
|
|
76
|
-
for (let i = 0; i < flags.length; ++i) {
|
|
77
|
-
const option = flags[i].toLowerCase(),
|
|
78
|
-
name = optionNames[option];
|
|
79
|
-
if (typeof name == 'string') options[name] = option !== flags[i];
|
|
80
|
-
}
|
|
81
|
-
options.flags = flags;
|
|
82
|
-
|
|
83
|
-
if (!parIsSet) {
|
|
84
|
-
parallel = process.env.TAPE6_PAR || parallel;
|
|
85
|
-
}
|
|
86
|
-
if (parallel) {
|
|
87
|
-
parallel = Math.max(0, +parallel);
|
|
88
|
-
if (parallel === Infinity) parallel = 0;
|
|
89
|
-
} else {
|
|
90
|
-
parallel = 0;
|
|
91
|
-
}
|
|
92
|
-
if (!parallel) {
|
|
93
|
-
if (typeof navigator !== 'undefined' && navigator.hardwareConcurrency) {
|
|
94
|
-
parallel = navigator.hardwareConcurrency;
|
|
95
|
-
} else {
|
|
96
|
-
try {
|
|
97
|
-
parallel = os.availableParallelism();
|
|
98
|
-
} catch (e) {
|
|
99
|
-
void e;
|
|
100
|
-
parallel = 1;
|
|
101
|
-
}
|
|
102
|
-
}
|
|
103
|
-
}
|
|
104
|
-
};
|
|
105
|
-
|
|
106
|
-
const init = async () => {
|
|
107
|
-
const currentReporter = getReporter();
|
|
108
|
-
if (!currentReporter) {
|
|
109
|
-
const reporterType = getReporterType(),
|
|
110
|
-
reporterFile = getReporterFileName(reporterType),
|
|
111
|
-
CustomReporter = (await import('../src/reporters/' + reporterFile)).default,
|
|
112
|
-
hasColors = !(
|
|
113
|
-
options.monochrome ||
|
|
114
|
-
process.env.NO_COLOR ||
|
|
115
|
-
process.env.NODE_DISABLE_COLORS ||
|
|
116
|
-
process.env.FORCE_COLOR === '0'
|
|
117
|
-
),
|
|
118
|
-
customOptions = reporterType === 'tap' ? {useJson: true, hasColors} : {...options, hasColors},
|
|
119
|
-
customReporter = new CustomReporter(customOptions);
|
|
120
|
-
setReporter(customReporter);
|
|
121
|
-
}
|
|
122
|
-
|
|
123
|
-
if (files.length) {
|
|
124
|
-
files = await resolvePatterns(rootFolder, files);
|
|
125
|
-
} else {
|
|
126
|
-
files = await resolveTests(rootFolder, 'node');
|
|
127
|
-
}
|
|
128
|
-
};
|
|
129
|
-
|
|
130
25
|
const main = async () => {
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
26
|
+
const currentOptions = getOptions({
|
|
27
|
+
'--self': showSelf,
|
|
28
|
+
'--info': {isValueRequired: false}
|
|
29
|
+
});
|
|
30
|
+
|
|
31
|
+
const [files] = await Promise.all([
|
|
32
|
+
initFiles(currentOptions.files, rootFolder),
|
|
33
|
+
initReporter(getReporter, setReporter, currentOptions.flags),
|
|
34
|
+
selectTimer()
|
|
35
|
+
]);
|
|
134
36
|
|
|
135
37
|
process.on('uncaughtException', (error, origin) => {
|
|
136
38
|
console.error('UNHANDLED ERROR:', origin, error);
|
|
137
39
|
process.exit(1);
|
|
138
40
|
});
|
|
139
41
|
|
|
42
|
+
if (currentOptions.optionFlags['--info'] === '') {
|
|
43
|
+
showInfo(currentOptions, files);
|
|
44
|
+
process.exit(0);
|
|
45
|
+
}
|
|
46
|
+
|
|
140
47
|
if (!files.length) {
|
|
141
48
|
console.log('No files found.');
|
|
142
49
|
process.exit(1);
|
|
143
50
|
}
|
|
144
51
|
|
|
145
52
|
const reporter = getReporter(),
|
|
146
|
-
worker = new TestWorker(reporter, parallel,
|
|
53
|
+
worker = new TestWorker(reporter, currentOptions.parallel, currentOptions.flags);
|
|
147
54
|
|
|
148
55
|
reporter.report({type: 'test', test: 0});
|
|
149
56
|
|
package/bin/tape6-seq.js
CHANGED
|
@@ -3,12 +3,7 @@
|
|
|
3
3
|
import process from 'node:process';
|
|
4
4
|
import {fileURLToPath} from 'node:url';
|
|
5
5
|
|
|
6
|
-
import {
|
|
7
|
-
resolveTests,
|
|
8
|
-
resolvePatterns,
|
|
9
|
-
getReporterFileName,
|
|
10
|
-
getReporterType
|
|
11
|
-
} from '../src/utils/config.js';
|
|
6
|
+
import {getOptions, initFiles, initReporter, showInfo} from '../src/utils/config.js';
|
|
12
7
|
|
|
13
8
|
import {getReporter, setReporter, setConfiguredFlag, testRunner} from '../src/test.js';
|
|
14
9
|
import {selectTimer} from '../src/utils/timer.js';
|
|
@@ -17,11 +12,7 @@ import TestWorker from '../src/runners/seq/TestWorker.js';
|
|
|
17
12
|
|
|
18
13
|
setConfiguredFlag(true);
|
|
19
14
|
|
|
20
|
-
const
|
|
21
|
-
rootFolder = process.cwd();
|
|
22
|
-
|
|
23
|
-
let flags = '',
|
|
24
|
-
files = [];
|
|
15
|
+
const rootFolder = process.cwd();
|
|
25
16
|
|
|
26
17
|
const showSelf = () => {
|
|
27
18
|
const self = new URL(import.meta.url);
|
|
@@ -33,74 +24,17 @@ const showSelf = () => {
|
|
|
33
24
|
process.exit(0);
|
|
34
25
|
};
|
|
35
26
|
|
|
36
|
-
const config = () => {
|
|
37
|
-
if (process.argv.includes('--self')) showSelf();
|
|
38
|
-
|
|
39
|
-
const optionNames = {
|
|
40
|
-
f: 'failureOnly',
|
|
41
|
-
t: 'showTime',
|
|
42
|
-
b: 'showBanner',
|
|
43
|
-
d: 'showData',
|
|
44
|
-
o: 'failOnce',
|
|
45
|
-
n: 'showAssertNumber',
|
|
46
|
-
m: 'monochrome',
|
|
47
|
-
c: 'dontCaptureConsole',
|
|
48
|
-
h: 'hideStreams'
|
|
49
|
-
};
|
|
50
|
-
|
|
51
|
-
for (let i = 2; i < process.argv.length; ++i) {
|
|
52
|
-
const arg = process.argv[i];
|
|
53
|
-
if (arg == '-f' || arg == '--flags') {
|
|
54
|
-
if (++i < process.argv.length) {
|
|
55
|
-
flags += process.argv[i];
|
|
56
|
-
}
|
|
57
|
-
continue;
|
|
58
|
-
}
|
|
59
|
-
if (arg == '-p' || arg == '--par') {
|
|
60
|
-
// skip
|
|
61
|
-
++i;
|
|
62
|
-
continue;
|
|
63
|
-
}
|
|
64
|
-
files.push(arg);
|
|
65
|
-
}
|
|
66
|
-
|
|
67
|
-
flags = (process.env.TAPE6_FLAGS || '') + flags;
|
|
68
|
-
for (let i = 0; i < flags.length; ++i) {
|
|
69
|
-
const option = flags[i].toLowerCase(),
|
|
70
|
-
name = optionNames[option];
|
|
71
|
-
if (typeof name == 'string') options[name] = option !== flags[i];
|
|
72
|
-
}
|
|
73
|
-
options.flags = flags;
|
|
74
|
-
};
|
|
75
|
-
|
|
76
|
-
const init = async () => {
|
|
77
|
-
const currentReporter = getReporter();
|
|
78
|
-
if (!currentReporter) {
|
|
79
|
-
const reporterType = getReporterType(),
|
|
80
|
-
reporterFile = getReporterFileName(reporterType),
|
|
81
|
-
CustomReporter = (await import('../src/reporters/' + reporterFile)).default,
|
|
82
|
-
hasColors = !(
|
|
83
|
-
options.monochrome ||
|
|
84
|
-
process.env.NO_COLOR ||
|
|
85
|
-
process.env.NODE_DISABLE_COLORS ||
|
|
86
|
-
process.env.FORCE_COLOR === '0'
|
|
87
|
-
),
|
|
88
|
-
customOptions = reporterType === 'tap' ? {useJson: true, hasColors} : {...options, hasColors},
|
|
89
|
-
customReporter = new CustomReporter(customOptions);
|
|
90
|
-
setReporter(customReporter);
|
|
91
|
-
}
|
|
92
|
-
|
|
93
|
-
if (files.length) {
|
|
94
|
-
files = await resolvePatterns(rootFolder, files);
|
|
95
|
-
} else {
|
|
96
|
-
files = await resolveTests(rootFolder, 'node');
|
|
97
|
-
}
|
|
98
|
-
};
|
|
99
|
-
|
|
100
27
|
const main = async () => {
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
28
|
+
const currentOptions = getOptions({
|
|
29
|
+
'--self': showSelf,
|
|
30
|
+
'--info': {isValueRequired: false}
|
|
31
|
+
});
|
|
32
|
+
|
|
33
|
+
const [files] = await Promise.all([
|
|
34
|
+
initFiles(currentOptions.files, rootFolder),
|
|
35
|
+
initReporter(getReporter, setReporter, currentOptions.flags),
|
|
36
|
+
selectTimer()
|
|
37
|
+
]);
|
|
104
38
|
|
|
105
39
|
const console = globalThis.console;
|
|
106
40
|
|
|
@@ -109,8 +43,18 @@ const main = async () => {
|
|
|
109
43
|
process.exit(1);
|
|
110
44
|
});
|
|
111
45
|
|
|
46
|
+
if (currentOptions.optionFlags['--info'] === '') {
|
|
47
|
+
showInfo(currentOptions, files);
|
|
48
|
+
process.exit(0);
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
if (!files.length) {
|
|
52
|
+
console.log('No files found.');
|
|
53
|
+
process.exit(1);
|
|
54
|
+
}
|
|
55
|
+
|
|
112
56
|
const reporter = getReporter(),
|
|
113
|
-
worker = new TestWorker(reporter, 1, {...
|
|
57
|
+
worker = new TestWorker(reporter, 1, {...currentOptions.flags, testRunner});
|
|
114
58
|
|
|
115
59
|
reporter.report({type: 'test', test: 0});
|
|
116
60
|
|
package/bin/tape6-server.js
CHANGED
|
@@ -147,6 +147,12 @@ const server = http.createServer(async (req, res) => {
|
|
|
147
147
|
const cfg = await getConfig(rootFolder);
|
|
148
148
|
return sendJson(req, res, cfg.importmap || {imports: {}}, method === 'HEAD');
|
|
149
149
|
}
|
|
150
|
+
if (url.pathname === '/favicon.ico') {
|
|
151
|
+
const faviconFile = path.join(rootFolder, webAppPath, 'favicon.ico');
|
|
152
|
+
const stat = await fsp.stat(faviconFile).catch(() => null);
|
|
153
|
+
if (stat && stat.isFile()) return sendFile(req, res, faviconFile, '.ico', method === 'HEAD');
|
|
154
|
+
return bailOut(req, res);
|
|
155
|
+
}
|
|
150
156
|
if (url.pathname === '/' || url.pathname === '/index' || url.pathname === '/index.html') {
|
|
151
157
|
// redirect to the web app
|
|
152
158
|
url.pathname = webAppPath;
|
package/index.js
CHANGED
|
@@ -38,7 +38,7 @@ const optionNames = {
|
|
|
38
38
|
n: 'showAssertNumber',
|
|
39
39
|
m: 'monochrome',
|
|
40
40
|
j: 'useJsonL',
|
|
41
|
-
c: '
|
|
41
|
+
c: 'noConsoleCapture',
|
|
42
42
|
h: 'hideStreams'
|
|
43
43
|
};
|
|
44
44
|
|
|
@@ -74,7 +74,7 @@ const init = async () => {
|
|
|
74
74
|
|
|
75
75
|
let originalConsole = null,
|
|
76
76
|
setCurrentReporter = null;
|
|
77
|
-
if (!options.
|
|
77
|
+
if (!options.noConsoleCapture && (isNode || isBun || isDeno)) {
|
|
78
78
|
const {captureConsole, setCurrentReporter: setReporter} = await import(
|
|
79
79
|
new URL('./src/utils/capture-console.js', import.meta.url)
|
|
80
80
|
);
|
package/llms-full.txt
CHANGED
|
@@ -286,11 +286,12 @@ Environment-specific subsections: `node`, `deno`, `bun`, `browser`.
|
|
|
286
286
|
Runs test files in parallel using worker threads.
|
|
287
287
|
|
|
288
288
|
```bash
|
|
289
|
-
tape6 [--flags FLAGS] [--par N] [tests...]
|
|
289
|
+
tape6 [--flags FLAGS] [--par N] [--info] [tests...]
|
|
290
290
|
```
|
|
291
291
|
|
|
292
292
|
- `--flags FLAGS` — output control flags (see below).
|
|
293
293
|
- `--par N` — number of parallel workers (default: all CPU cores).
|
|
294
|
+
- `--info` — print current configuration and exit without running tests.
|
|
294
295
|
- No arguments: runs tests from configuration.
|
|
295
296
|
|
|
296
297
|
### tape6-seq
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "tape-six",
|
|
3
|
-
"version": "1.7.
|
|
3
|
+
"version": "1.7.6",
|
|
4
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",
|
|
@@ -104,7 +104,7 @@
|
|
|
104
104
|
},
|
|
105
105
|
"devDependencies": {
|
|
106
106
|
"@types/chai": "^5.2.3",
|
|
107
|
-
"@types/node": "^25.3.
|
|
107
|
+
"@types/node": "^25.3.3",
|
|
108
108
|
"chai": "^6.2.2",
|
|
109
109
|
"playwright": "^1.58.2",
|
|
110
110
|
"puppeteer": "^24.37.5",
|
|
@@ -48,7 +48,7 @@ export class TTYReporter extends Reporter {
|
|
|
48
48
|
showData = true,
|
|
49
49
|
showAssertNumber = false,
|
|
50
50
|
hasColors = true,
|
|
51
|
-
|
|
51
|
+
noConsoleCapture = false,
|
|
52
52
|
hideStreams = false,
|
|
53
53
|
originalConsole
|
|
54
54
|
} = {}) {
|
|
@@ -105,7 +105,7 @@ export class TTYReporter extends Reporter {
|
|
|
105
105
|
|
|
106
106
|
this.overrideLastLine = false;
|
|
107
107
|
|
|
108
|
-
while (
|
|
108
|
+
while (noConsoleCapture && this.output.isTTY) {
|
|
109
109
|
const isCurrentTTY = this.output === process.stdout || this.output === process.stderr;
|
|
110
110
|
if (!isCurrentTTY) break;
|
|
111
111
|
|
package/src/utils/config.js
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import {promises as fsp} from 'node:fs';
|
|
2
2
|
import path from 'node:path';
|
|
3
|
+
import os from 'node:os';
|
|
3
4
|
|
|
4
5
|
import {listing, wildToRe} from './listing.js';
|
|
5
6
|
|
|
@@ -132,3 +133,190 @@ export const getTimeoutValue = () => {
|
|
|
132
133
|
if (isNaN(timeout) || timeout <= 0 || timeout === Infinity) timeout = DEFAULT_START_TIMEOUT;
|
|
133
134
|
return timeout;
|
|
134
135
|
};
|
|
136
|
+
|
|
137
|
+
// parsing options
|
|
138
|
+
|
|
139
|
+
export const flagNames = {
|
|
140
|
+
f: 'failureOnly',
|
|
141
|
+
t: 'showTime',
|
|
142
|
+
b: 'showBanner',
|
|
143
|
+
d: 'showData',
|
|
144
|
+
o: 'failOnce',
|
|
145
|
+
s: 'showStack',
|
|
146
|
+
l: 'showLog',
|
|
147
|
+
n: 'showAssertNumber',
|
|
148
|
+
m: 'monochrome',
|
|
149
|
+
j: 'useJsonL',
|
|
150
|
+
c: 'noConsoleCapture',
|
|
151
|
+
h: 'hideStreams'
|
|
152
|
+
};
|
|
153
|
+
|
|
154
|
+
export const processArgs = argOptions => {
|
|
155
|
+
const result = {files: [], flags: {}};
|
|
156
|
+
let args = [];
|
|
157
|
+
switch (runtime.name) {
|
|
158
|
+
case 'browser':
|
|
159
|
+
return [];
|
|
160
|
+
case 'deno':
|
|
161
|
+
args = Deno.args;
|
|
162
|
+
break;
|
|
163
|
+
case 'bun':
|
|
164
|
+
args = Bun.argv.slice(2);
|
|
165
|
+
break;
|
|
166
|
+
case 'node':
|
|
167
|
+
args = process.argv.slice(2);
|
|
168
|
+
break;
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
const argNames = {};
|
|
172
|
+
for (const argName of Object.keys(argOptions)) {
|
|
173
|
+
let option = argOptions[argName];
|
|
174
|
+
if (typeof option == 'function') {
|
|
175
|
+
option = {fn: option, isValueRequired: true};
|
|
176
|
+
} else {
|
|
177
|
+
option = {...option};
|
|
178
|
+
}
|
|
179
|
+
option.canonicalName = argName;
|
|
180
|
+
argNames[argName] = option;
|
|
181
|
+
if (Array.isArray(option?.aliases)) {
|
|
182
|
+
for (const alias of option.aliases) {
|
|
183
|
+
argNames[alias] = option;
|
|
184
|
+
}
|
|
185
|
+
}
|
|
186
|
+
result.flags[argName] = option?.initialValue ?? null;
|
|
187
|
+
}
|
|
188
|
+
|
|
189
|
+
for (let i = 0; i < args.length; ++i) {
|
|
190
|
+
const arg = args[i],
|
|
191
|
+
[name, ...values] = arg.split('=');
|
|
192
|
+
let opt = argNames[name],
|
|
193
|
+
value = values.join('=');
|
|
194
|
+
|
|
195
|
+
if (!opt) {
|
|
196
|
+
result.files.push(arg);
|
|
197
|
+
continue;
|
|
198
|
+
}
|
|
199
|
+
|
|
200
|
+
if (opt.isValueRequired && !values.length) {
|
|
201
|
+
if (++i < args.length) {
|
|
202
|
+
value = args[i];
|
|
203
|
+
} else {
|
|
204
|
+
value = '';
|
|
205
|
+
}
|
|
206
|
+
}
|
|
207
|
+
|
|
208
|
+
if (typeof opt.fn == 'function') {
|
|
209
|
+
opt.fn(result.flags, name, value);
|
|
210
|
+
} else {
|
|
211
|
+
result.flags[opt.canonicalName] = value;
|
|
212
|
+
}
|
|
213
|
+
}
|
|
214
|
+
|
|
215
|
+
return result;
|
|
216
|
+
};
|
|
217
|
+
|
|
218
|
+
export const getOptions = extraOptions => {
|
|
219
|
+
const args = processArgs({
|
|
220
|
+
'--flags': {
|
|
221
|
+
aliases: ['-f'],
|
|
222
|
+
initialValue: runtime.getEnvVar('TAPE6_FLAGS') || '',
|
|
223
|
+
isValueRequired: true,
|
|
224
|
+
fn: (flags, _, value) => {
|
|
225
|
+
flags['--flags'] += value;
|
|
226
|
+
}
|
|
227
|
+
},
|
|
228
|
+
'--par': {
|
|
229
|
+
aliases: ['-p'],
|
|
230
|
+
initialValue: runtime.getEnvVar('TAPE6_PAR') || '',
|
|
231
|
+
isValueRequired: true
|
|
232
|
+
},
|
|
233
|
+
...extraOptions
|
|
234
|
+
});
|
|
235
|
+
|
|
236
|
+
const flags = args.flags['--flags'],
|
|
237
|
+
options = {flags: {flags}, parallel: 1, files: args.files, optionFlags: args.flags};
|
|
238
|
+
|
|
239
|
+
// set back flags
|
|
240
|
+
if (runtime.name === 'deno') {
|
|
241
|
+
Deno.env.set('TAPE6_FLAGS', flags);
|
|
242
|
+
} else if (runtime.name === 'bun') {
|
|
243
|
+
Bun.env.TAPE6_FLAGS = flags;
|
|
244
|
+
} else {
|
|
245
|
+
process.env.TAPE6_FLAGS = flags;
|
|
246
|
+
}
|
|
247
|
+
|
|
248
|
+
for (let i = 0; i < flags.length; ++i) {
|
|
249
|
+
const flag = flags[i].toLowerCase(),
|
|
250
|
+
name = flagNames[flag];
|
|
251
|
+
if (typeof name == 'string') options.flags[name] = flag !== flags[i];
|
|
252
|
+
}
|
|
253
|
+
|
|
254
|
+
let parallel = args.flags['--par'];
|
|
255
|
+
|
|
256
|
+
if (parallel) {
|
|
257
|
+
parallel = Math.max(0, +parallel);
|
|
258
|
+
if (isNaN(parallel) || parallel === Infinity) parallel = 0;
|
|
259
|
+
} else {
|
|
260
|
+
parallel = 0;
|
|
261
|
+
}
|
|
262
|
+
|
|
263
|
+
if (!parallel) {
|
|
264
|
+
if (typeof navigator !== 'undefined' && navigator.hardwareConcurrency) {
|
|
265
|
+
parallel = navigator.hardwareConcurrency;
|
|
266
|
+
} else {
|
|
267
|
+
try {
|
|
268
|
+
parallel = os.availableParallelism();
|
|
269
|
+
} catch (e) {
|
|
270
|
+
void e;
|
|
271
|
+
parallel = 1;
|
|
272
|
+
}
|
|
273
|
+
}
|
|
274
|
+
}
|
|
275
|
+
options.parallel = parallel;
|
|
276
|
+
|
|
277
|
+
return options;
|
|
278
|
+
};
|
|
279
|
+
|
|
280
|
+
export const initReporter = async (getReporter, setReporter, flags) => {
|
|
281
|
+
const currentReporter = getReporter();
|
|
282
|
+
if (!currentReporter) {
|
|
283
|
+
const reporterType = getReporterType(),
|
|
284
|
+
reporterFile = getReporterFileName(reporterType),
|
|
285
|
+
CustomReporter = (await import('../reporters/' + reporterFile)).default,
|
|
286
|
+
hasColors = !(
|
|
287
|
+
flags.monochrome ||
|
|
288
|
+
runtime.getEnvVar('NO_COLOR') ||
|
|
289
|
+
runtime.getEnvVar('NODE_DISABLE_COLORS') ||
|
|
290
|
+
runtime.getEnvVar('FORCE_COLOR') === '0'
|
|
291
|
+
),
|
|
292
|
+
customOptions = reporterType === 'tap' ? {useJson: true, hasColors} : {...flags, hasColors},
|
|
293
|
+
customReporter = new CustomReporter(customOptions);
|
|
294
|
+
setReporter(customReporter);
|
|
295
|
+
}
|
|
296
|
+
};
|
|
297
|
+
|
|
298
|
+
export const initFiles = (files, rootFolder, type) => {
|
|
299
|
+
if (files.length) return resolvePatterns(rootFolder, files);
|
|
300
|
+
return resolveTests(rootFolder, type || runtime.name);
|
|
301
|
+
};
|
|
302
|
+
|
|
303
|
+
export const showInfo = (options, files) => {
|
|
304
|
+
const reporterType = getReporterType();
|
|
305
|
+
console.log('Runtime: ', runtime.name);
|
|
306
|
+
console.log('Reporter:', reporterType);
|
|
307
|
+
console.log('Parallel:', options.parallel);
|
|
308
|
+
|
|
309
|
+
const names = Object.keys(options.flags),
|
|
310
|
+
width = names.reduce((max, name) => Math.max(max, name.length), 0) + 1;
|
|
311
|
+
console.log('Flags:', options.flags.flags || '');
|
|
312
|
+
for (let i = 0; i < names.length; ++i) {
|
|
313
|
+
const name = names[i];
|
|
314
|
+
if (name === 'flags') continue;
|
|
315
|
+
console.log(' ' + (name + ':').padEnd(width), options.flags[name]);
|
|
316
|
+
}
|
|
317
|
+
|
|
318
|
+
console.log('Files (' + files.length + '):');
|
|
319
|
+
for (const file of files) {
|
|
320
|
+
console.log(' /' + file);
|
|
321
|
+
}
|
|
322
|
+
};
|
|
Binary file
|
|
Binary file
|
package/web-app/index.html
CHANGED
|
@@ -4,6 +4,8 @@
|
|
|
4
4
|
<meta charset="utf-8" />
|
|
5
5
|
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
|
6
6
|
<title>Tape-six test runner</title>
|
|
7
|
+
<link rel="icon" type="image/png" href="./favicon.png" />
|
|
8
|
+
<link rel="icon" type="image/vnd.microsoft.icon" href="./favicon.ico" />
|
|
7
9
|
<link rel="stylesheet" href="./style.css" />
|
|
8
10
|
<link rel="stylesheet" href="./tape6-donut.css" />
|
|
9
11
|
<script type="module" src="./tape6-donut.js"></script>
|