tape-six 1.6.0 → 1.7.1

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
@@ -6,9 +6,9 @@
6
6
  `tape-six` is a [TAP](https://en.wikipedia.org/wiki/Test_Anything_Protocol)-based library for unit tests.
7
7
  It is written in the modern JavaScript for the modern JavaScript and works in [Node](https://nodejs.org/), [Deno](https://deno.land/), [Bun](https://bun.sh/) and browsers.
8
8
 
9
- It runs ES modules (`import`-based code) natively and supports CommonJS modules transparently using built-in [ESM](https://nodejs.org/api/esm.html).
9
+ It runs ES modules (`import`-based code) natively and supports CommonJS modules transparently using the built-in [ESM](https://nodejs.org/api/esm.html).
10
10
 
11
- It can run TypeScript code with modern versions of Node, Bun and Deno without transpilation. Obviously TS bindings are included.
11
+ It can run TypeScript code with modern versions of Node, Bun and Deno **without transpilation**. Obviously TS bindings are included.
12
12
 
13
13
  Individual test files can be executed directly with `node`, `deno`, `bun` without a need for a special test runner utility. It facilitates debugging and improves testing.
14
14
 
@@ -33,7 +33,7 @@ with existing unit test libraries:
33
33
  - Tests should work with ES modules natively.
34
34
  - What if I want to debug some CommonJS code with Node? No problem, it just works.
35
35
  - Tests should work with TypeScript natively.
36
- - It just workss: modern runtimes (Node, Deno, Bun) support running TypeScript natively without transpilation by ignoring type information and running the code directly.
36
+ - It just works: modern runtimes (Node, Deno, Bun) support running TypeScript natively without transpilation by ignoring type information and running the code directly.
37
37
  - The [DX](https://en.wikipedia.org/wiki/User_experience#Developer_experience) in browsers are usually abysmal.
38
38
  - Both console-based debugging and a UI to navigate results are properly supported.
39
39
  - Integration with browser automation tools is supported for automated testing.
@@ -99,11 +99,29 @@ And:
99
99
 
100
100
  <img width="240" height="329" alt="image" src="https://github.com/user-attachments/assets/f3d8ac65-9e6a-499d-837f-0271146da1de" />
101
101
 
102
+ ## Project structure
103
+
104
+ ```
105
+ tape-six/
106
+ ├── index.js # Main entry point, exports test, hooks, aliases
107
+ ├── index.d.ts # TypeScript definitions for the public API
108
+ ├── package.json # Package config; "tape6" section configures test discovery
109
+ ├── bin/ # CLI utilities: tape6, tape6-server, tape6-node, tape6-bun, tape6-deno, tape6-seq
110
+ ├── src/ # Source code (test engine, reporters, runners, utilities)
111
+ ├── web-app/ # Browser test UI application
112
+ ├── tests/ # Test files
113
+ ├── ts-tests/ # TypeScript test files
114
+ ├── wiki/ # GitHub wiki documentation (submodule)
115
+ └── vendors/ # Git submodules (deep6)
116
+ ```
117
+
102
118
  ## Docs
103
119
 
104
120
  The documentation can be found in the [wiki](https://github.com/uhop/tape-six/wiki).
105
121
  See how it can be used in [tests/](https://github.com/uhop/tape-six/tree/master/tests).
106
122
 
123
+ For AI assistants: see [llms.txt](https://github.com/uhop/tape-six/blob/master/llms.txt) and [llms-full.txt](https://github.com/uhop/tape-six/blob/master/llms-full.txt) for LLM-optimized documentation.
124
+
107
125
  The whole API is based on two objects: `test` and `Tester`.
108
126
 
109
127
  ### `test`
@@ -247,6 +265,8 @@ The following methods are available (all `msg` arguments are optional):
247
265
  - `notStrictEquals()` &mdash; an alias of `notStrictEqual()`.
248
266
  - `doesNotEqual()` &mdash; an alias of `notStrictEqual()`.
249
267
  - `isUnequal()` &mdash; an alias of `notStrictEqual()`.
268
+ - `isNotEqual()` &mdash; an alias of `notStrictEqual()`.
269
+ - `isNot()` &mdash; an alias of `notStrictEqual()`.
250
270
  - `looseEqual(a, b, msg)` &mdash; asserts that `a` and `b` are loosely equal.
251
271
  - Loose equality is defined as `a == b`.
252
272
  - `looseEquals()` &mdash; an alias of `looseEqual()`.
@@ -290,6 +310,7 @@ The following methods are available (all `msg` arguments are optional):
290
310
  - Miscellaneous:
291
311
  - `any` &mdash; returns the `any` object. It can be used in deep equivalency asserts to match any value.
292
312
  See [deep6's any](https://github.com/uhop/deep6/wiki/any) for details.
313
+ - `_` &mdash; an alias of `any`.
293
314
  - `plan(n)` &mdash; sets the number of tests in the test suite. Rarely used.
294
315
  - `comment(msg)` &mdash; sends a comment to the test harness. Rarely used.
295
316
  - `skipTest(...args, msg)` &mdash; skips the current test yet sends a message to the test harness.
@@ -316,6 +337,10 @@ test('Sample test', async t => {
316
337
  });
317
338
  ```
318
339
 
340
+ ### Before/after hooks
341
+
342
+ `tape-six` supports scope-based before/after hooks: `beforeAll`, `afterAll`, `beforeEach`, `afterEach`, which can be used to set-up and tear-down a proper environment for tests. Read all about it in [before and after hooks](https://github.com/uhop/tape-six/wiki/Before-and-after-hooks).
343
+
319
344
  ### Running tests
320
345
 
321
346
  It is super easy to run tests:
@@ -328,7 +353,7 @@ It is super easy to run tests:
328
353
  3. Or you can run them in a browser!
329
354
  4. Profit!
330
355
 
331
- If you have a lot of tests, you can organize them using multiple files and directories.
356
+ If you have a lot of tests, you can organize them using multiple files and directories (see configuration below).
332
357
  `tape-six` provides multiple test runners that can run them in different environments.
333
358
 
334
359
  Tests can run in parallel using multiple threads to speed up the whole process.
@@ -341,6 +366,12 @@ tape6 --par 1 # run one test at a time
341
366
 
342
367
  If you want to run tests in separate processes, check out [tape-six-proc](https://www.npmjs.com/package/tape-six-proc). Why do you want to do that? When tests have to modify globals or use single-threaded binary extensions.
343
368
 
369
+ If you want to run tests sequentially without expenses of threads and processes, you can use the `tape6-seq` command, which uses the same options and configuration as `tape6`:
370
+
371
+ ```bash
372
+ tape6-seq
373
+ ```
374
+
344
375
  ### Configuring test runners
345
376
 
346
377
  TLDR version &mdash; add to your `package.json`:
@@ -380,29 +411,10 @@ Test output can be controlled by flags. See [Supported flags](https://github.com
380
411
 
381
412
  The most recent releases:
382
413
 
414
+ - 1.7.1 _Added AI support, added timeout to start test runners, fixed some bugs in the sequential test runner._
415
+ - 1.7.0 _New features: after/before hooks for tests, aliases for `suite()`, `describe()`, `it()`, `tape6-seq` &mdash; an in-process sequential test runner. Improvements: stricter monochrome detection, refactoring, bugfixes, updated dev dependencies and the documentation._
383
416
  - 1.6.0 _New features: support for `AssertionError` and 3rd-party assertion libraries based on it like `node:assert` and `chai`, support for `console.assert()`, support for `signal` to cancel asynchronous operations, tests wait for embedded tests, improved reporting of errors, updated dev dependencies._
384
417
  - 1.5.1 _Better support for stopping parallel tests, better support for "failed to load" errors._
385
418
  - 1.5.0 _Internal refactoring (moved state to reporters), added type identification of values in the DOM and TTY reporters, multiple minor fixes._
386
- - 1.4.5 _Internal: added flags support for custom test runners._
387
- - 1.4.4 _Refreshed the lock file._
388
- - 1.4.3 _Updated dev dependencies + a minor bugfix._
389
- - 1.4.2 _Improved documentation._
390
- - 1.4.1 _Added browser automation support._
391
- - 1.4.0 _Added a high-level helper `OK()` for evaluating simple expressions._
392
- - 1.3.5 _Minor improvements, better docs._
393
- - 1.3.4 _Minor bugfixes and improvements._
394
- - 1.3.3 _Added a way to hide console/streams output, better support for file tests, better TTY formatting._
395
- - 1.3.2 _Internal refactoring (capture console calls), updated dependencies._
396
- - 1.3.1 _Bugfix for web browser using JSONL reporter._
397
- - 1.3.0 _Bugfixes, updated dependencies, new feature: proxied console calls._
398
- - 1.2.0 _Updated dependencies + added an optional prefix for JSON lines._
399
- - 1.1.2 _Updated dependencies._
400
- - 1.1.1 _Technical re-release with the missing `index.d.ts` file._
401
- - 1.1.0 _Added TypeScript support._
402
- - 1.0.4 _Bugfix for platform-specific tests, old platforms, minor updates to accommodate Deno 2, updated dev deps._
403
- - 1.0.3 _Minor update to accommodate changes in Bun and updated dev deps._
404
- - 1.0.2 _Bugfix for Deno using the JSONL reporter._
405
- - 1.0.1 _Technical release: added more links._
406
- - 1.0.0 _The first official release._
407
419
 
408
420
  For more info consult full [release notes](https://github.com/uhop/tape-six/wiki/Release-notes).
package/bin/tape6-bun.js CHANGED
@@ -5,13 +5,14 @@ import {fileURLToPath} from 'node:url';
5
5
  import {
6
6
  resolveTests,
7
7
  resolvePatterns,
8
- getReporter as getReporterType
8
+ getReporterFileName,
9
+ getReporterType
9
10
  } from '../src/utils/config.js';
10
11
 
11
12
  import {getReporter, setReporter} from '../src/test.js';
12
13
  import {selectTimer} from '../src/utils/timer.js';
13
14
 
14
- import TestWorker from '../src/bun/TestWorker.js';
15
+ import TestWorker from '../src/runners/bun/TestWorker.js';
15
16
 
16
17
  const options = {},
17
18
  rootFolder = Bun.cwd;
@@ -89,20 +90,19 @@ const config = () => {
89
90
  if (!parallel) parallel = globalThis.navigator?.hardwareConcurrency || 1;
90
91
  };
91
92
 
92
- const reporters = {
93
- jsonl: 'JSONLReporter.js',
94
- tap: 'TapReporter.js',
95
- tty: 'TTYReporter.js'
96
- };
97
-
98
93
  const init = async () => {
99
94
  const currentReporter = getReporter();
100
95
  if (!currentReporter) {
101
96
  const reporterType = getReporterType(),
102
- reporterFile = reporters[reporterType] || reporters.tty,
97
+ reporterFile = getReporterFileName(reporterType),
103
98
  CustomReporter = (await import('../src/reporters/' + reporterFile)).default,
104
- customOptions =
105
- reporterType === 'tap' ? {useJson: true, hasColors: !options.monochrome} : options,
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
106
  customReporter = new CustomReporter(customOptions);
107
107
  setReporter(customReporter);
108
108
  }
package/bin/tape6-deno.js CHANGED
@@ -5,13 +5,14 @@ import {fileURLToPath} from 'node:url';
5
5
  import {
6
6
  resolveTests,
7
7
  resolvePatterns,
8
- getReporter as getReporterType
8
+ getReporterFileName,
9
+ getReporterType
9
10
  } from '../src/utils/config.js';
10
11
 
11
12
  import {getReporter, setReporter} from '../src/test.js';
12
13
  import {selectTimer} from '../src/utils/timer.js';
13
14
 
14
- import TestWorker from '../src/deno/TestWorker.js';
15
+ import TestWorker from '../src/runners/deno/TestWorker.js';
15
16
 
16
17
  const options = {},
17
18
  rootFolder = Deno.cwd();
@@ -89,20 +90,19 @@ const config = () => {
89
90
  if (!parallel) parallel = globalThis.navigator?.hardwareConcurrency || 1;
90
91
  };
91
92
 
92
- const reporters = {
93
- jsonl: 'JSONLReporter.js',
94
- tap: 'TapReporter.js',
95
- tty: 'TTYReporter.js'
96
- };
97
-
98
93
  const init = async () => {
99
94
  const currentReporter = getReporter();
100
95
  if (!currentReporter) {
101
96
  const reporterType = getReporterType(),
102
- reporterFile = reporters[reporterType] || reporters.tty,
97
+ reporterFile = getReporterFileName(reporterType),
103
98
  CustomReporter = (await import('../src/reporters/' + reporterFile)).default,
104
- customOptions =
105
- reporterType === 'tap' ? {useJson: true, hasColors: !options.monochrome} : options,
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
106
  customReporter = new CustomReporter(customOptions);
107
107
  setReporter(customReporter);
108
108
  }
package/bin/tape6-node.js CHANGED
@@ -6,13 +6,14 @@ import {fileURLToPath} from 'node:url';
6
6
  import {
7
7
  resolveTests,
8
8
  resolvePatterns,
9
- getReporter as getReporterType
9
+ getReporterFileName,
10
+ getReporterType
10
11
  } from '../src/utils/config.js';
11
12
 
12
13
  import {getReporter, setReporter} from '../src/test.js';
13
14
  import {selectTimer} from '../src/utils/timer.js';
14
15
 
15
- import TestWorker from '../src/node/TestWorker.js';
16
+ import TestWorker from '../src/runners/node/TestWorker.js';
16
17
 
17
18
  const options = {},
18
19
  rootFolder = process.cwd();
@@ -90,20 +91,19 @@ const config = () => {
90
91
  if (!parallel) parallel = globalThis.navigator?.hardwareConcurrency || 1;
91
92
  };
92
93
 
93
- const reporters = {
94
- jsonl: 'JSONLReporter.js',
95
- tap: 'TapReporter.js',
96
- tty: 'TTYReporter.js'
97
- };
98
-
99
94
  const init = async () => {
100
95
  const currentReporter = getReporter();
101
96
  if (!currentReporter) {
102
97
  const reporterType = getReporterType(),
103
- reporterFile = reporters[reporterType] || reporters.tty,
98
+ reporterFile = getReporterFileName(reporterType),
104
99
  CustomReporter = (await import('../src/reporters/' + reporterFile)).default,
105
- customOptions =
106
- reporterType === 'tap' ? {useJson: true, hasColors: !options.monochrome} : options,
100
+ hasColors = !(
101
+ options.monochrome ||
102
+ process.env.NO_COLOR ||
103
+ process.env.NODE_DISABLE_COLORS ||
104
+ process.env.FORCE_COLOR === '0'
105
+ ),
106
+ customOptions = reporterType === 'tap' ? {useJson: true, hasColors} : {...options, hasColors},
107
107
  customReporter = new CustomReporter(customOptions);
108
108
  setReporter(customReporter);
109
109
  }
@@ -124,6 +124,11 @@ const main = async () => {
124
124
  console.error('UNHANDLED ERROR:', origin, error)
125
125
  );
126
126
 
127
+ if (!files.length) {
128
+ console.log('No files found.');
129
+ process.exit(1);
130
+ }
131
+
127
132
  const reporter = getReporter(),
128
133
  worker = new TestWorker(reporter, parallel, options);
129
134
 
@@ -7,6 +7,7 @@ const runtimeFiles = {
7
7
  node: 'tape6-node.js',
8
8
  deno: 'tape6-deno.js',
9
9
  bun: 'tape6-bun.js',
10
+ seq: 'tape6-seq.js',
10
11
  server: 'tape6-server.js',
11
12
  runner: 'tape6-runner.js',
12
13
  main: 'tape6.js'
@@ -0,0 +1,132 @@
1
+ #!/usr/bin/env node
2
+
3
+ import process from 'node:process';
4
+ import {fileURLToPath} from 'node:url';
5
+
6
+ import {
7
+ resolveTests,
8
+ resolvePatterns,
9
+ getReporterFileName,
10
+ getReporterType
11
+ } from '../src/utils/config.js';
12
+
13
+ import {getReporter, setReporter, setConfiguredFlag, testRunner} from '../src/test.js';
14
+ import {selectTimer} from '../src/utils/timer.js';
15
+
16
+ import TestWorker from '../src/runners/seq/TestWorker.js';
17
+
18
+ setConfiguredFlag(true);
19
+
20
+ const options = {},
21
+ rootFolder = process.cwd();
22
+
23
+ let flags = '',
24
+ files = [];
25
+
26
+ const showSelf = () => {
27
+ const self = new URL(import.meta.url);
28
+ if (self.protocol === 'file:') {
29
+ console.log(fileURLToPath(self));
30
+ } else {
31
+ console.log(self);
32
+ }
33
+ process.exit(0);
34
+ };
35
+
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
+ const main = async () => {
101
+ config();
102
+ await init();
103
+ await selectTimer();
104
+
105
+ const console = globalThis.console;
106
+
107
+ process.on('uncaughtException', (error, origin) =>
108
+ console.error('UNHANDLED ERROR:', origin, error)
109
+ );
110
+
111
+ const reporter = getReporter(),
112
+ worker = new TestWorker(reporter, 1, {...options, testRunner});
113
+
114
+ reporter.report({type: 'test', test: 0});
115
+
116
+ await new Promise(resolve => {
117
+ worker.done = () => resolve();
118
+ worker.execute(files);
119
+ });
120
+
121
+ const hasFailed = reporter.state && reporter.state.failed > 0;
122
+
123
+ reporter.report({
124
+ type: 'end',
125
+ test: 0,
126
+ fail: hasFailed
127
+ });
128
+
129
+ process.exit(hasFailed ? 1 : 0);
130
+ };
131
+
132
+ main().catch(error => console.error('ERROR:', error));