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 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 options = {},
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
- config();
119
- await init();
120
- await selectTimer();
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, options);
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 options = {},
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
- config();
119
- await init();
120
- await selectTimer();
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, options);
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 options = {},
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
- config();
132
- await init();
133
- await selectTimer();
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, options);
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 options = {},
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
- config();
102
- await init();
103
- await selectTimer();
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, {...options, testRunner});
57
+ worker = new TestWorker(reporter, 1, {...currentOptions.flags, testRunner});
114
58
 
115
59
  reporter.report({type: 'test', test: 0});
116
60
 
@@ -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: 'dontCaptureConsole',
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.dontCaptureConsole && (isNode || isBun || isDeno)) {
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.4",
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.1",
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
- dontCaptureConsole = false,
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 (dontCaptureConsole && this.output.isTTY) {
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
 
@@ -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
@@ -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>