tape-six 1.7.2 → 1.7.3
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 +18 -8
- package/TESTING.md +756 -0
- package/index.d.ts +28 -21
- package/llms-full.txt +28 -8
- package/llms.txt +22 -7
- package/package.json +7 -4
- package/src/Tester.js +2 -2
- package/src/reporters/MinReporter.js +0 -11
- package/src/reporters/Reporter.js +1 -1
- package/src/runners/deno/worker.js +1 -1
- package/src/runners/seq/BypassReporter.js +2 -2
- package/src/test.js +1 -0
- package/workflows/write-tests.md +33 -0
package/README.md
CHANGED
|
@@ -4,7 +4,7 @@
|
|
|
4
4
|
[npm-url]: https://npmjs.org/package/tape-six
|
|
5
5
|
|
|
6
6
|
`tape-six` is a [TAP](https://en.wikipedia.org/wiki/Test_Anything_Protocol)-based library for unit tests.
|
|
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.
|
|
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. **Zero runtime dependencies.**
|
|
8
8
|
|
|
9
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
|
|
|
@@ -37,7 +37,7 @@ with existing unit test libraries:
|
|
|
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.
|
|
40
|
-
- Examples for
|
|
40
|
+
- Examples for [Playwright](https://playwright.dev/) and [Puppeteer](https://pptr.dev/) are provided.
|
|
41
41
|
|
|
42
42
|
## How it looks
|
|
43
43
|
|
|
@@ -137,6 +137,8 @@ import test from 'tape-six';
|
|
|
137
137
|
// const {default: test} = require('tape-six');
|
|
138
138
|
```
|
|
139
139
|
|
|
140
|
+
To help port tests from other frameworks, `test()` is aliased as `suite()`, `describe()` and `it()`. When called inside a test body, `test()` and its aliases automatically delegate to the current tester's `t.test()` method. The same applies to `test.skip()`, `test.todo()`, and `test.asPromise()`. Using `t.test()` directly is still preferred because it makes the delegation explicit.
|
|
141
|
+
|
|
140
142
|
This function registers a test suite. Available options:
|
|
141
143
|
|
|
142
144
|
- `async test(name, options, testFn)` — registers a test suite to be executed asynchronously.
|
|
@@ -162,11 +164,15 @@ The arguments mentioned above are:
|
|
|
162
164
|
- `name` — the optional name of the test suite. If not provided, it will be set to the name of the test function or `'(anonymous)'`.
|
|
163
165
|
- Can be overridden by the `name` argument.
|
|
164
166
|
- `timeout` — the optional timeout in milliseconds. It is used for asynchronous tests.
|
|
165
|
-
- If the timeout is exceeded, the
|
|
166
|
-
- **Important:** JavaScript does not provide a generic way to cancel asynchronous operations.
|
|
167
|
-
When the timeout is exceeded, `tape6` will stop waiting for the test to finish,
|
|
168
|
-
but it will continue running in the background.
|
|
167
|
+
- If the timeout is exceeded, `tape6` will use the tester's `signal` to indicate cancellation and stop waiting for the test to finish.
|
|
169
168
|
- The default: no timeout.
|
|
169
|
+
- Hooks:
|
|
170
|
+
- `beforeAll` — a function to be executed before all tests in the suite.
|
|
171
|
+
- `afterAll` — a function to be executed after all tests in the suite.
|
|
172
|
+
- `beforeEach` — a function to be executed before each test in the suite.
|
|
173
|
+
- `afterEach` — a function to be executed after each test in the suite.
|
|
174
|
+
- `before` — an alias for `beforeAll`.
|
|
175
|
+
- `after` — an alias for `afterAll`.
|
|
170
176
|
- `testFn` — the optional test function to be executed (see below).
|
|
171
177
|
- Can be overridden by the `testFn` argument.
|
|
172
178
|
- `testPromiseFn` — the optional callback-based test function to be executed.
|
|
@@ -307,8 +313,11 @@ The following methods are available (all `msg` arguments are optional):
|
|
|
307
313
|
- `skip(name, options, testFn)` — skips a test suite asynchronously. See `test.skip()` above.
|
|
308
314
|
- `todo(name, options, testFn)` — runs a provisional test suite asynchronously. See `test.todo()` above.
|
|
309
315
|
- `asPromise(name, options, testPromiseFn)` — runs a test suite asynchronously. See `test.asPromise()` above.
|
|
316
|
+
- Note: top-level `test()` and its aliases auto-delegate to `t.test()` when called inside a test body. Using `t.test()` directly is preferred.
|
|
317
|
+
- Properties:
|
|
318
|
+
- `signal` — an [AbortSignal](https://developer.mozilla.org/en-US/docs/Web/API/AbortSignal) that fires when the test is aborted (e.g. on timeout or bail-out). Use it to cancel pending async operations.
|
|
310
319
|
- Miscellaneous:
|
|
311
|
-
- `any` —
|
|
320
|
+
- `any` — a symbol that can be used in deep equivalency asserts to match any value.
|
|
312
321
|
See [deep6's any](https://github.com/uhop/deep6/wiki/any) for details.
|
|
313
322
|
- `_` — an alias of `any`.
|
|
314
323
|
- `plan(n)` — sets the number of tests in the test suite. Rarely used.
|
|
@@ -339,7 +348,7 @@ test('Sample test', async t => {
|
|
|
339
348
|
|
|
340
349
|
### Before/after hooks
|
|
341
350
|
|
|
342
|
-
`tape-six` supports scope-based before/after hooks: `beforeAll`, `afterAll`, `beforeEach`, `afterEach
|
|
351
|
+
`tape-six` supports scope-based before/after hooks: `beforeAll`, `afterAll`, `beforeEach`, `afterEach` (with `before`/`after` as aliases for `beforeAll`/`afterAll`), which can be used to set-up and tear-down a proper environment for tests. Like `test()` and its aliases, the top-level hook functions auto-delegate to the current tester when called inside a test body. Using `t.beforeAll()`, etc. is preferred. Read all about it in [before and after hooks](https://github.com/uhop/tape-six/wiki/Before-and-after-hooks).
|
|
343
352
|
|
|
344
353
|
### Running tests
|
|
345
354
|
|
|
@@ -411,6 +420,7 @@ Test output can be controlled by flags. See [Supported flags](https://github.com
|
|
|
411
420
|
|
|
412
421
|
The most recent releases:
|
|
413
422
|
|
|
423
|
+
- 1.7.3 _Bug fixes in reporters, runners, and assertions. Documentation corrections and improvements._
|
|
414
424
|
- 1.7.2 _Minor internal refactoring and fixes._
|
|
415
425
|
- 1.7.1 _Added AI support, added timeout to start test runners, fixed some bugs in the sequential test runner._
|
|
416
426
|
- 1.7.0 _New features: after/before hooks for tests, aliases for `suite()`, `describe()`, `it()`, `tape6-seq` — an in-process sequential test runner. Improvements: stricter monochrome detection, refactoring, bugfixes, updated dev dependencies and the documentation._
|
package/TESTING.md
ADDED
|
@@ -0,0 +1,756 @@
|
|
|
1
|
+
# Testing with tape-six
|
|
2
|
+
|
|
3
|
+
> This guide is for AI agents and developers working on projects that use `tape-six` for testing.
|
|
4
|
+
> It covers how to write tests, run them, and configure test discovery.
|
|
5
|
+
|
|
6
|
+
`tape-six` supports ES modules and CommonJS. TypeScript is supported natively — no transpilation needed (Node 22+, Deno, Bun all run `.ts` files directly).
|
|
7
|
+
|
|
8
|
+
## Install
|
|
9
|
+
|
|
10
|
+
```bash
|
|
11
|
+
npm i -D tape-six
|
|
12
|
+
```
|
|
13
|
+
|
|
14
|
+
## Quick start
|
|
15
|
+
|
|
16
|
+
Create a test file (e.g., `tests/test-example.js`):
|
|
17
|
+
|
|
18
|
+
```js
|
|
19
|
+
import test from 'tape-six';
|
|
20
|
+
|
|
21
|
+
test('arithmetic', t => {
|
|
22
|
+
t.equal(2 + 2, 4, 'addition');
|
|
23
|
+
t.ok(10 > 5, 'comparison');
|
|
24
|
+
t.deepEqual({a: 1}, {a: 1}, 'deep equality');
|
|
25
|
+
});
|
|
26
|
+
```
|
|
27
|
+
|
|
28
|
+
Run it directly:
|
|
29
|
+
|
|
30
|
+
```bash
|
|
31
|
+
node tests/test-example.js
|
|
32
|
+
```
|
|
33
|
+
|
|
34
|
+
Run all configured tests:
|
|
35
|
+
|
|
36
|
+
```bash
|
|
37
|
+
npx tape6 --flags FO
|
|
38
|
+
```
|
|
39
|
+
|
|
40
|
+
## Writing tests
|
|
41
|
+
|
|
42
|
+
### Importing
|
|
43
|
+
|
|
44
|
+
ES modules (`.js`, `.mjs`, `.ts`, `.mts`):
|
|
45
|
+
|
|
46
|
+
```js
|
|
47
|
+
import test from 'tape-six';
|
|
48
|
+
// or named imports:
|
|
49
|
+
import {test, describe, it, beforeAll, afterAll, beforeEach, afterEach} from 'tape-six';
|
|
50
|
+
```
|
|
51
|
+
|
|
52
|
+
CommonJS (`.cjs`, `.cts`):
|
|
53
|
+
|
|
54
|
+
```js
|
|
55
|
+
const {test} = require('tape-six');
|
|
56
|
+
```
|
|
57
|
+
|
|
58
|
+
### Registering tests
|
|
59
|
+
|
|
60
|
+
`test(name, options, testFn)` — all arguments are optional, recognized by type.
|
|
61
|
+
|
|
62
|
+
```js
|
|
63
|
+
test('name', t => {
|
|
64
|
+
t.pass('unconditional pass');
|
|
65
|
+
});
|
|
66
|
+
|
|
67
|
+
test('async test', async t => {
|
|
68
|
+
const result = await fetchData();
|
|
69
|
+
t.equal(result.status, 200, 'status OK');
|
|
70
|
+
});
|
|
71
|
+
```
|
|
72
|
+
|
|
73
|
+
Aliases: `suite()`, `describe()`, `it()` — all identical to `test()`.
|
|
74
|
+
|
|
75
|
+
When called inside a test body, top-level functions (`test`, `it`, `describe`, and all hooks) automatically delegate to the current tester. Using `t.test()`, `t.before()`, etc. is still preferred because it makes the delegation explicit. The top-level form is convenient when porting tests from frameworks like Mocha or Jest:
|
|
76
|
+
|
|
77
|
+
```js
|
|
78
|
+
import {describe, it, before, beforeEach} from 'tape-six';
|
|
79
|
+
|
|
80
|
+
describe('module', () => {
|
|
81
|
+
before(() => {
|
|
82
|
+
/* setup — same as t.before() */
|
|
83
|
+
});
|
|
84
|
+
beforeEach(() => {
|
|
85
|
+
/* per-test setup */
|
|
86
|
+
});
|
|
87
|
+
|
|
88
|
+
it('works', t => {
|
|
89
|
+
t.ok(true);
|
|
90
|
+
});
|
|
91
|
+
|
|
92
|
+
it('also works', t => {
|
|
93
|
+
t.equal(1 + 1, 2);
|
|
94
|
+
});
|
|
95
|
+
});
|
|
96
|
+
```
|
|
97
|
+
|
|
98
|
+
### Skip and TODO
|
|
99
|
+
|
|
100
|
+
```js
|
|
101
|
+
test.skip('not ready', t => {
|
|
102
|
+
t.fail();
|
|
103
|
+
}); // skipped entirely
|
|
104
|
+
test.todo('in progress', t => {
|
|
105
|
+
t.equal(1, 2);
|
|
106
|
+
}); // runs, failures not counted
|
|
107
|
+
```
|
|
108
|
+
|
|
109
|
+
### Assertions
|
|
110
|
+
|
|
111
|
+
All `msg` arguments are optional. If omitted, a generic message is used.
|
|
112
|
+
|
|
113
|
+
| Method | Checks | Common aliases |
|
|
114
|
+
| ------------------------------------ | ------------------------------- | -------------------------------- |
|
|
115
|
+
| `t.pass(msg)` | Unconditional pass | |
|
|
116
|
+
| `t.fail(msg)` | Unconditional fail | |
|
|
117
|
+
| `t.ok(val, msg)` | `val` is truthy | `true`, `assert` |
|
|
118
|
+
| `t.notOk(val, msg)` | `val` is falsy | `false`, `notok` |
|
|
119
|
+
| `t.error(err, msg)` | `err` is falsy | `ifError`, `ifErr` |
|
|
120
|
+
| `t.equal(a, b, msg)` | `a === b` | `is`, `strictEqual`, `isEqual` |
|
|
121
|
+
| `t.notEqual(a, b, msg)` | `a !== b` | `not`, `notStrictEqual`, `isNot` |
|
|
122
|
+
| `t.deepEqual(a, b, msg)` | Deep strict equality | `same`, `isEquivalent` |
|
|
123
|
+
| `t.notDeepEqual(a, b, msg)` | Not deeply equal | `notSame`, `notEquivalent` |
|
|
124
|
+
| `t.looseEqual(a, b, msg)` | `a == b` | |
|
|
125
|
+
| `t.notLooseEqual(a, b, msg)` | `a != b` | |
|
|
126
|
+
| `t.deepLooseEqual(a, b, msg)` | Deep loose equality | |
|
|
127
|
+
| `t.notDeepLooseEqual(a, b, msg)` | Not deeply loosely equal | |
|
|
128
|
+
| `t.throws(fn, msg)` | `fn()` throws | |
|
|
129
|
+
| `t.doesNotThrow(fn, msg)` | `fn()` does not throw | |
|
|
130
|
+
| `t.matchString(str, re, msg)` | `str` matches `re` | |
|
|
131
|
+
| `t.doesNotMatchString(str, re, msg)` | `str` doesn't match `re` | |
|
|
132
|
+
| `t.match(a, b, msg)` | Structural pattern match | |
|
|
133
|
+
| `t.doesNotMatch(a, b, msg)` | No structural match | |
|
|
134
|
+
| `t.rejects(promise, msg)` | Promise rejects (**await it**) | `doesNotResolve` |
|
|
135
|
+
| `t.resolves(promise, msg)` | Promise resolves (**await it**) | `doesNotReject` |
|
|
136
|
+
|
|
137
|
+
### Async assertions
|
|
138
|
+
|
|
139
|
+
`rejects` and `resolves` are async — always `await` them:
|
|
140
|
+
|
|
141
|
+
```js
|
|
142
|
+
test('async asserts', async t => {
|
|
143
|
+
await t.rejects(Promise.reject(new Error('fail')), 'should reject');
|
|
144
|
+
await t.resolves(Promise.resolve(42), 'should resolve');
|
|
145
|
+
});
|
|
146
|
+
```
|
|
147
|
+
|
|
148
|
+
### Nested (embedded) tests
|
|
149
|
+
|
|
150
|
+
Tests can be nested. The preferred way is `t.test()` because it makes the delegation explicit. Top-level `test()`/`it()` are equivalent inside a test body — they auto-delegate to the current tester:
|
|
151
|
+
|
|
152
|
+
```js
|
|
153
|
+
import {test, it} from 'tape-six';
|
|
154
|
+
|
|
155
|
+
test('suite', async t => {
|
|
156
|
+
// these two forms are equivalent:
|
|
157
|
+
await t.test('using t.test', t => {
|
|
158
|
+
t.pass();
|
|
159
|
+
});
|
|
160
|
+
|
|
161
|
+
await it('using top-level it()', t => {
|
|
162
|
+
t.ok(true);
|
|
163
|
+
});
|
|
164
|
+
});
|
|
165
|
+
```
|
|
166
|
+
|
|
167
|
+
Embedded tests must be `await`ed to preserve execution order.
|
|
168
|
+
|
|
169
|
+
### Hooks (setup/teardown)
|
|
170
|
+
|
|
171
|
+
Hooks are scoped — they only affect tests at their registration level.
|
|
172
|
+
|
|
173
|
+
`before` is an alias for `beforeAll`, `after` is an alias for `afterAll`. These aliases work everywhere: as named exports, on `test.before`/`test.after`, on `t.before`/`t.after`, and in options objects.
|
|
174
|
+
|
|
175
|
+
**Top-level hooks** (affect all top-level tests in the file):
|
|
176
|
+
|
|
177
|
+
```js
|
|
178
|
+
import {test, beforeAll, afterAll, beforeEach, afterEach} from 'tape-six';
|
|
179
|
+
// or with aliases:
|
|
180
|
+
import {test, before, after, beforeEach, afterEach} from 'tape-six';
|
|
181
|
+
|
|
182
|
+
beforeAll(() => {
|
|
183
|
+
/* once before first test */
|
|
184
|
+
});
|
|
185
|
+
afterAll(() => {
|
|
186
|
+
/* once after last test */
|
|
187
|
+
});
|
|
188
|
+
beforeEach(() => {
|
|
189
|
+
/* before each test */
|
|
190
|
+
});
|
|
191
|
+
afterEach(() => {
|
|
192
|
+
/* after each test */
|
|
193
|
+
});
|
|
194
|
+
```
|
|
195
|
+
|
|
196
|
+
**Nested hooks** (affect embedded tests within a suite):
|
|
197
|
+
|
|
198
|
+
The preferred way is `t.before()`/`t.after()` because it makes the scope explicit. Top-level `before()`/`after()` are equivalent inside a test body — they auto-delegate to the current tester:
|
|
199
|
+
|
|
200
|
+
```js
|
|
201
|
+
import {test, before, after, beforeEach} from 'tape-six';
|
|
202
|
+
|
|
203
|
+
test('database tests', async t => {
|
|
204
|
+
let db;
|
|
205
|
+
// these use top-level functions — they auto-delegate to t:
|
|
206
|
+
before(async () => {
|
|
207
|
+
db = await connect();
|
|
208
|
+
});
|
|
209
|
+
after(async () => {
|
|
210
|
+
await db.close();
|
|
211
|
+
});
|
|
212
|
+
beforeEach(() => {
|
|
213
|
+
/* reset state */
|
|
214
|
+
});
|
|
215
|
+
|
|
216
|
+
// equivalent using t. methods:
|
|
217
|
+
// t.before(async () => { db = await connect(); });
|
|
218
|
+
// t.after(async () => { await db.close(); });
|
|
219
|
+
// t.beforeEach(() => { /* reset state */ });
|
|
220
|
+
|
|
221
|
+
await t.test('insert', async t => {
|
|
222
|
+
const result = await db.insert({name: 'Alice'});
|
|
223
|
+
t.ok(result.id, 'got an id');
|
|
224
|
+
});
|
|
225
|
+
|
|
226
|
+
await t.test('query', async t => {
|
|
227
|
+
const rows = await db.query('SELECT * FROM users');
|
|
228
|
+
t.ok(rows.length > 0, 'has rows');
|
|
229
|
+
});
|
|
230
|
+
});
|
|
231
|
+
```
|
|
232
|
+
|
|
233
|
+
**Hooks via options** (reusable across tests):
|
|
234
|
+
|
|
235
|
+
```js
|
|
236
|
+
const dbOpts = {
|
|
237
|
+
beforeEach: () => resetFixtures(),
|
|
238
|
+
afterEach: () => cleanupDb()
|
|
239
|
+
};
|
|
240
|
+
|
|
241
|
+
test('suite A', dbOpts, async t => {
|
|
242
|
+
/* ... */
|
|
243
|
+
});
|
|
244
|
+
test('suite B', dbOpts, async t => {
|
|
245
|
+
/* ... */
|
|
246
|
+
});
|
|
247
|
+
```
|
|
248
|
+
|
|
249
|
+
## Migrating from other test frameworks
|
|
250
|
+
|
|
251
|
+
`tape-six` supports `describe`/`it` and `before`/`after` aliases. When called inside a test body, all top-level functions automatically delegate to the current tester. This means migration from Mocha, Jest, or `node:test` is nearly mechanical — change the import and swap assertions.
|
|
252
|
+
|
|
253
|
+
### Mocha / Jest → tape-six
|
|
254
|
+
|
|
255
|
+
```js
|
|
256
|
+
// Mocha / Jest
|
|
257
|
+
describe('module', () => {
|
|
258
|
+
before(() => {
|
|
259
|
+
/* setup */
|
|
260
|
+
});
|
|
261
|
+
after(() => {
|
|
262
|
+
/* teardown */
|
|
263
|
+
});
|
|
264
|
+
beforeEach(() => {
|
|
265
|
+
/* per-test setup */
|
|
266
|
+
});
|
|
267
|
+
|
|
268
|
+
it('works', () => {
|
|
269
|
+
expect(1).toBe(1);
|
|
270
|
+
});
|
|
271
|
+
});
|
|
272
|
+
```
|
|
273
|
+
|
|
274
|
+
```js
|
|
275
|
+
// tape-six — just change the import and swap assertions
|
|
276
|
+
import {describe, it, before, after, beforeEach} from 'tape-six';
|
|
277
|
+
|
|
278
|
+
describe('module', () => {
|
|
279
|
+
before(() => {
|
|
280
|
+
/* setup */
|
|
281
|
+
});
|
|
282
|
+
after(() => {
|
|
283
|
+
/* teardown */
|
|
284
|
+
});
|
|
285
|
+
beforeEach(() => {
|
|
286
|
+
/* per-test setup */
|
|
287
|
+
});
|
|
288
|
+
|
|
289
|
+
it('works', t => {
|
|
290
|
+
t.equal(1, 1);
|
|
291
|
+
});
|
|
292
|
+
});
|
|
293
|
+
```
|
|
294
|
+
|
|
295
|
+
Key differences from Mocha/Jest:
|
|
296
|
+
|
|
297
|
+
- **Assertions** use `t.equal`, `t.deepEqual`, etc. instead of `expect`.
|
|
298
|
+
- The test function receives a `t` argument for assertions.
|
|
299
|
+
- No magic globals — everything is imported explicitly.
|
|
300
|
+
- `it()` inside `describe()` is auto-delegated, no need to use `t.test()`.
|
|
301
|
+
- `before()`/`after()` inside `describe()` are auto-delegated, no need to use `t.before()`/`t.after()`.
|
|
302
|
+
|
|
303
|
+
### Chai → tape-six
|
|
304
|
+
|
|
305
|
+
Chai is commonly used with Mocha and other test frameworks. Its `expect`/`should`/`assert` styles all throw `AssertionError`, which `tape-six` catches automatically. You can use Chai assertions directly inside `tape-six` tests, or replace them with `t.*` equivalents:
|
|
306
|
+
|
|
307
|
+
```js
|
|
308
|
+
// Chai expect style
|
|
309
|
+
import {expect} from 'chai';
|
|
310
|
+
|
|
311
|
+
describe('module', () => {
|
|
312
|
+
it('works', () => {
|
|
313
|
+
expect(1 + 1).to.equal(2);
|
|
314
|
+
expect([1, 2]).to.deep.equal([1, 2]);
|
|
315
|
+
expect(true).to.be.ok;
|
|
316
|
+
expect(() => badFn()).to.throw();
|
|
317
|
+
});
|
|
318
|
+
});
|
|
319
|
+
```
|
|
320
|
+
|
|
321
|
+
```js
|
|
322
|
+
// tape-six — Chai assertions work as-is, or replace with t.*
|
|
323
|
+
import {describe, it} from 'tape-six';
|
|
324
|
+
|
|
325
|
+
describe('module', () => {
|
|
326
|
+
it('works', t => {
|
|
327
|
+
t.equal(1 + 1, 2);
|
|
328
|
+
t.deepEqual([1, 2], [1, 2]);
|
|
329
|
+
t.ok(true);
|
|
330
|
+
t.throws(() => badFn());
|
|
331
|
+
});
|
|
332
|
+
});
|
|
333
|
+
```
|
|
334
|
+
|
|
335
|
+
You can also keep Chai alongside `tape-six` — failures are reported correctly either way:
|
|
336
|
+
|
|
337
|
+
```js
|
|
338
|
+
import {describe, it} from 'tape-six';
|
|
339
|
+
import {expect} from 'chai';
|
|
340
|
+
|
|
341
|
+
describe('mixed assertions', () => {
|
|
342
|
+
it('uses both', t => {
|
|
343
|
+
expect(1).to.be.lessThan(2); // Chai — caught automatically
|
|
344
|
+
t.equal(1 + 1, 2); // tape-six native
|
|
345
|
+
});
|
|
346
|
+
});
|
|
347
|
+
```
|
|
348
|
+
|
|
349
|
+
### node:test → tape-six
|
|
350
|
+
|
|
351
|
+
```js
|
|
352
|
+
// node:test
|
|
353
|
+
import {describe, it, before, after} from 'node:test';
|
|
354
|
+
import assert from 'node:assert/strict';
|
|
355
|
+
|
|
356
|
+
describe('module', () => {
|
|
357
|
+
before(() => {
|
|
358
|
+
/* setup */
|
|
359
|
+
});
|
|
360
|
+
it('works', () => {
|
|
361
|
+
assert.equal(1, 1);
|
|
362
|
+
});
|
|
363
|
+
});
|
|
364
|
+
```
|
|
365
|
+
|
|
366
|
+
```js
|
|
367
|
+
// tape-six — change import, optionally swap assert for t.*
|
|
368
|
+
import {describe, it, before} from 'tape-six';
|
|
369
|
+
|
|
370
|
+
describe('module', () => {
|
|
371
|
+
before(() => {
|
|
372
|
+
/* setup */
|
|
373
|
+
});
|
|
374
|
+
it('works', t => {
|
|
375
|
+
t.equal(1, 1);
|
|
376
|
+
});
|
|
377
|
+
});
|
|
378
|
+
```
|
|
379
|
+
|
|
380
|
+
Note: you can also keep using `node:assert` inside tape-six tests — `AssertionError` is caught automatically (see [3rd-party assertion libraries](#3rd-party-assertion-libraries)).
|
|
381
|
+
|
|
382
|
+
### Quick reference
|
|
383
|
+
|
|
384
|
+
| Mocha / Jest / node:test | tape-six equivalent |
|
|
385
|
+
| ----------------------------------- | ------------------------------------------------- |
|
|
386
|
+
| `describe(name, fn)` | `describe(name, fn)` — same |
|
|
387
|
+
| `it(name, fn)` | `it(name, t => { ... })` — same, but receives `t` |
|
|
388
|
+
| `before(fn)` | `before(fn)` — same (alias for `beforeAll`) |
|
|
389
|
+
| `after(fn)` | `after(fn)` — same (alias for `afterAll`) |
|
|
390
|
+
| `beforeEach(fn)` | `beforeEach(fn)` — same |
|
|
391
|
+
| `afterEach(fn)` | `afterEach(fn)` — same |
|
|
392
|
+
| `expect(a).toBe(b)` | `t.equal(a, b)` |
|
|
393
|
+
| `expect(a).toEqual(b)` | `t.deepEqual(a, b)` |
|
|
394
|
+
| `expect(a).toBeTruthy()` | `t.ok(a)` |
|
|
395
|
+
| `expect(fn).toThrow()` | `t.throws(fn)` |
|
|
396
|
+
| `assert.equal(a, b)` | `t.equal(a, b)` or keep `assert.equal` |
|
|
397
|
+
| `assert.deepEqual(a, b)` | `t.deepEqual(a, b)` or keep `assert.deepEqual` |
|
|
398
|
+
| `expect(a).to.equal(b)` (Chai) | `t.equal(a, b)` or keep `expect` |
|
|
399
|
+
| `expect(a).to.deep.equal(b)` (Chai) | `t.deepEqual(a, b)` or keep `expect` |
|
|
400
|
+
| `expect(a).to.be.ok` (Chai) | `t.ok(a)` or keep `expect` |
|
|
401
|
+
| `expect(fn).to.throw()` (Chai) | `t.throws(fn)` or keep `expect` |
|
|
402
|
+
|
|
403
|
+
### Wildcard matching with `t.any`
|
|
404
|
+
|
|
405
|
+
Use `t.any` (or `t._`) in deep equality checks to match any value:
|
|
406
|
+
|
|
407
|
+
```js
|
|
408
|
+
test('partial match', t => {
|
|
409
|
+
const result = {id: 123, name: 'Alice', createdAt: new Date()};
|
|
410
|
+
t.deepEqual(result, {id: 123, name: 'Alice', createdAt: t.any});
|
|
411
|
+
});
|
|
412
|
+
```
|
|
413
|
+
|
|
414
|
+
### Testing exceptions
|
|
415
|
+
|
|
416
|
+
```js
|
|
417
|
+
test('errors', async t => {
|
|
418
|
+
t.throws(() => {
|
|
419
|
+
throw new Error('boom');
|
|
420
|
+
}, 'should throw');
|
|
421
|
+
t.doesNotThrow(() => 42, 'should not throw');
|
|
422
|
+
await t.rejects(Promise.reject(new Error('fail')), 'should reject');
|
|
423
|
+
await t.resolves(Promise.resolve(42), 'should resolve');
|
|
424
|
+
});
|
|
425
|
+
```
|
|
426
|
+
|
|
427
|
+
### 3rd-party assertion libraries
|
|
428
|
+
|
|
429
|
+
`tape-six` catches `AssertionError` automatically. You can use `chai` or `node:assert`:
|
|
430
|
+
|
|
431
|
+
```js
|
|
432
|
+
import test from 'tape-six';
|
|
433
|
+
import {expect} from 'chai';
|
|
434
|
+
|
|
435
|
+
test('with chai', t => {
|
|
436
|
+
expect(1).to.be.lessThan(2);
|
|
437
|
+
expect([1, 2]).to.deep.equal([1, 2]);
|
|
438
|
+
});
|
|
439
|
+
```
|
|
440
|
+
|
|
441
|
+
```js
|
|
442
|
+
import test from 'tape-six';
|
|
443
|
+
import assert from 'node:assert/strict';
|
|
444
|
+
|
|
445
|
+
test('with node:assert', t => {
|
|
446
|
+
assert.equal(1 + 1, 2);
|
|
447
|
+
assert.deepEqual({a: 1}, {a: 1});
|
|
448
|
+
});
|
|
449
|
+
```
|
|
450
|
+
|
|
451
|
+
## Running tests
|
|
452
|
+
|
|
453
|
+
### Single file
|
|
454
|
+
|
|
455
|
+
```bash
|
|
456
|
+
node tests/test-example.js # Node.js
|
|
457
|
+
bun run tests/test-example.js # Bun
|
|
458
|
+
deno run -A tests/test-example.js # Deno
|
|
459
|
+
```
|
|
460
|
+
|
|
461
|
+
### All configured tests
|
|
462
|
+
|
|
463
|
+
```bash
|
|
464
|
+
npx tape6 --flags FO # parallel (worker threads)
|
|
465
|
+
npx tape6-seq --flags FO # sequential (in-process, no workers)
|
|
466
|
+
npx tape6 --par 4 --flags FO # limit to 4 workers
|
|
467
|
+
```
|
|
468
|
+
|
|
469
|
+
**`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.
|
|
470
|
+
|
|
471
|
+
### Selected test files
|
|
472
|
+
|
|
473
|
+
```bash
|
|
474
|
+
npx tape6 --flags FO tests/test-foo.js tests/test-bar.js
|
|
475
|
+
npx tape6-seq --flags FO tests/test-foo.js tests/test-bar.js
|
|
476
|
+
```
|
|
477
|
+
|
|
478
|
+
### Typical package.json scripts
|
|
479
|
+
|
|
480
|
+
```json
|
|
481
|
+
{
|
|
482
|
+
"scripts": {
|
|
483
|
+
"test": "tape6 --flags FO",
|
|
484
|
+
"test:bun": "tape6-bun --flags FO",
|
|
485
|
+
"test:deno": "tape6-deno --flags FO",
|
|
486
|
+
"test:seq": "tape6-seq --flags FO",
|
|
487
|
+
"test:seq:bun": "bun run `tape6-seq --self` --flags FO",
|
|
488
|
+
"test:seq:deno": "deno run -A `tape6-seq --self` --flags FO"
|
|
489
|
+
}
|
|
490
|
+
}
|
|
491
|
+
```
|
|
492
|
+
|
|
493
|
+
### Flags
|
|
494
|
+
|
|
495
|
+
Flags control test output. Uppercase = enabled, lowercase = disabled.
|
|
496
|
+
|
|
497
|
+
| Flag | Meaning |
|
|
498
|
+
| ---- | -------------------------------------- |
|
|
499
|
+
| `F` | **F**ailures only — hide passing tests |
|
|
500
|
+
| `O` | Fail **o**nce — stop at first failure |
|
|
501
|
+
| `T` | Show **t**ime for each test |
|
|
502
|
+
| `D` | Show **d**ata of failed tests |
|
|
503
|
+
| `B` | Show **b**anner with summary |
|
|
504
|
+
| `N` | Show assert **n**umber |
|
|
505
|
+
| `M` | **M**onochrome — no colors |
|
|
506
|
+
| `C` | Don't **c**apture console output |
|
|
507
|
+
| `H` | **H**ide streams and console output |
|
|
508
|
+
|
|
509
|
+
Common combinations: `FO` (failures only + stop at first), `FOT` (+ show time).
|
|
510
|
+
|
|
511
|
+
### Environment variables
|
|
512
|
+
|
|
513
|
+
- `TAPE6_FLAGS` — flags string (alternative to `--flags`).
|
|
514
|
+
- `TAPE6_PAR` — number of parallel workers.
|
|
515
|
+
- `TAPE6_TAP` — force TAP output format.
|
|
516
|
+
- `TAPE6_JSONL` — force JSONL output format.
|
|
517
|
+
|
|
518
|
+
## Configuring test discovery
|
|
519
|
+
|
|
520
|
+
Add to `package.json`:
|
|
521
|
+
|
|
522
|
+
```json
|
|
523
|
+
{
|
|
524
|
+
"tape6": {
|
|
525
|
+
"tests": ["/tests/test-*.*js"],
|
|
526
|
+
"importmap": {
|
|
527
|
+
"imports": {
|
|
528
|
+
"tape-six": "/node_modules/tape-six/index.js",
|
|
529
|
+
"tape-six/": "/node_modules/tape-six/src/",
|
|
530
|
+
"my-package": "/src/index.js",
|
|
531
|
+
"my-package/": "/src/"
|
|
532
|
+
}
|
|
533
|
+
}
|
|
534
|
+
}
|
|
535
|
+
}
|
|
536
|
+
```
|
|
537
|
+
|
|
538
|
+
- `tests` — glob patterns for test files (relative to project root with leading `/`). Common for all environments.
|
|
539
|
+
- `cli` — additional patterns for CLI-only environments (Node, Bun, Deno). Typically used for `.cjs` files.
|
|
540
|
+
- `node`, `deno`, `bun`, `browser` — additional patterns specific to a given environment. These are **not overrides** — they are added to `tests` (and `cli` for non-browser).
|
|
541
|
+
- `importmap` — import map for browser testing (standard [import map](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/script/type/importmap) format).
|
|
542
|
+
|
|
543
|
+
Example with environment-specific tests:
|
|
544
|
+
|
|
545
|
+
```json
|
|
546
|
+
{
|
|
547
|
+
"tape6": {
|
|
548
|
+
"node": ["/tests/node/test-*.js"],
|
|
549
|
+
"deno": ["/tests/deno/test-*.js"],
|
|
550
|
+
"browser": ["/tests/web/test-*.html"],
|
|
551
|
+
"tests": ["/tests/test-*.*js"],
|
|
552
|
+
"importmap": {
|
|
553
|
+
"imports": {
|
|
554
|
+
"tape-six": "/node_modules/tape-six/index.js",
|
|
555
|
+
"tape-six/": "/node_modules/tape-six/src/"
|
|
556
|
+
}
|
|
557
|
+
}
|
|
558
|
+
}
|
|
559
|
+
}
|
|
560
|
+
```
|
|
561
|
+
|
|
562
|
+
In this example, running `tape6` on Node will execute tests matching `/tests/node/test-*.js` + `/tests/test-*.*js`. Running in a browser will execute `/tests/web/test-*.html` + `/tests/test-*.*js`.
|
|
563
|
+
|
|
564
|
+
## Browser testing
|
|
565
|
+
|
|
566
|
+
Browser tests use `tape6-server`, a static file server bundled with `tape-six` that provides a web UI for running tests.
|
|
567
|
+
|
|
568
|
+
### Setup
|
|
569
|
+
|
|
570
|
+
1. Configure `importmap` in `package.json` so the browser can resolve bare imports:
|
|
571
|
+
|
|
572
|
+
```json
|
|
573
|
+
{
|
|
574
|
+
"tape6": {
|
|
575
|
+
"tests": ["/tests/test-*.*js"],
|
|
576
|
+
"browser": ["/tests/web/test-*.html"],
|
|
577
|
+
"importmap": {
|
|
578
|
+
"imports": {
|
|
579
|
+
"tape-six": "/node_modules/tape-six/index.js",
|
|
580
|
+
"tape-six/": "/node_modules/tape-six/src/",
|
|
581
|
+
"my-package": "/src/index.js",
|
|
582
|
+
"my-package/": "/src/"
|
|
583
|
+
}
|
|
584
|
+
}
|
|
585
|
+
}
|
|
586
|
+
}
|
|
587
|
+
```
|
|
588
|
+
|
|
589
|
+
2. Start the server:
|
|
590
|
+
|
|
591
|
+
```bash
|
|
592
|
+
npx tape6-server --trace
|
|
593
|
+
```
|
|
594
|
+
|
|
595
|
+
3. Open `http://localhost:3000` in a browser to see the web UI and run all configured tests.
|
|
596
|
+
|
|
597
|
+
### Running all configured browser tests
|
|
598
|
+
|
|
599
|
+
Navigate to:
|
|
600
|
+
|
|
601
|
+
```
|
|
602
|
+
http://localhost:3000/
|
|
603
|
+
```
|
|
604
|
+
|
|
605
|
+
The web app fetches the configured test list from the server and runs them.
|
|
606
|
+
|
|
607
|
+
### Running specific test files by name
|
|
608
|
+
|
|
609
|
+
Use the `?q=` query parameter (supports multiple values):
|
|
610
|
+
|
|
611
|
+
```
|
|
612
|
+
http://localhost:3000/?q=/tests/test-foo.js&q=/tests/test-bar.js
|
|
613
|
+
```
|
|
614
|
+
|
|
615
|
+
These are glob patterns resolved by the server, so wildcards work:
|
|
616
|
+
|
|
617
|
+
```
|
|
618
|
+
http://localhost:3000/?q=/tests/test-sample.*js
|
|
619
|
+
```
|
|
620
|
+
|
|
621
|
+
### Running a single test file (HTML shim)
|
|
622
|
+
|
|
623
|
+
Create an HTML file that loads test scripts directly with an inline import map:
|
|
624
|
+
|
|
625
|
+
```html
|
|
626
|
+
<!DOCTYPE html>
|
|
627
|
+
<html lang="en">
|
|
628
|
+
<head>
|
|
629
|
+
<meta charset="utf-8" />
|
|
630
|
+
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
|
631
|
+
<title>My tests</title>
|
|
632
|
+
<script type="importmap">
|
|
633
|
+
{
|
|
634
|
+
"imports": {
|
|
635
|
+
"tape-six": "/node_modules/tape-six/index.js",
|
|
636
|
+
"tape-six/": "/node_modules/tape-six/src/"
|
|
637
|
+
}
|
|
638
|
+
}
|
|
639
|
+
</script>
|
|
640
|
+
<script type="module" src="../test-sample.js"></script>
|
|
641
|
+
</head>
|
|
642
|
+
<body>
|
|
643
|
+
<h1>My tests</h1>
|
|
644
|
+
<p>See the console.</p>
|
|
645
|
+
</body>
|
|
646
|
+
</html>
|
|
647
|
+
```
|
|
648
|
+
|
|
649
|
+
Navigate to the HTML file directly (e.g., `http://localhost:3000/tests/web/test-simple.html`). Results appear in the browser console. This approach does not use the web UI.
|
|
650
|
+
|
|
651
|
+
### Flags and parallel execution in the browser
|
|
652
|
+
|
|
653
|
+
Append query parameters:
|
|
654
|
+
|
|
655
|
+
```
|
|
656
|
+
http://localhost:3000/?flags=FO&par=3
|
|
657
|
+
```
|
|
658
|
+
|
|
659
|
+
### Browser automation
|
|
660
|
+
|
|
661
|
+
Use Puppeteer or Playwright to run browser tests from the command line:
|
|
662
|
+
|
|
663
|
+
```js
|
|
664
|
+
import puppeteer from 'puppeteer';
|
|
665
|
+
const browser = await puppeteer.launch({headless: true});
|
|
666
|
+
const page = await browser.newPage();
|
|
667
|
+
page.on('console', msg => console.log(msg.text()));
|
|
668
|
+
await page.exposeFunction('__tape6_reportResults', async text => {
|
|
669
|
+
await browser.close();
|
|
670
|
+
process.exit(text === 'success' ? 0 : 1);
|
|
671
|
+
});
|
|
672
|
+
await page.goto('http://localhost:3000/?flags=M');
|
|
673
|
+
```
|
|
674
|
+
|
|
675
|
+
### Browser limitations
|
|
676
|
+
|
|
677
|
+
- Browsers cannot run TypeScript files directly — only `.js` and `.mjs`.
|
|
678
|
+
- Browsers cannot run CommonJS (`.cjs`) files.
|
|
679
|
+
- Browsers can run HTML shim files in addition to JS files.
|
|
680
|
+
|
|
681
|
+
## Test file conventions
|
|
682
|
+
|
|
683
|
+
- **Naming**: `test-*.js`, `test-*.mjs`, `test-*.cjs`, `test-*.ts`, `test-*.mts`, `test-*.cts`.
|
|
684
|
+
- **Location**: typically `tests/` directory.
|
|
685
|
+
- **Self-contained**: each test file should be directly executable with `node`.
|
|
686
|
+
- **One concern per file**: group related tests in a single file, use embedded tests for sub-grouping.
|
|
687
|
+
|
|
688
|
+
## Patterns for AI agents writing tests
|
|
689
|
+
|
|
690
|
+
### Testing a new function
|
|
691
|
+
|
|
692
|
+
```js
|
|
693
|
+
import test from 'tape-six';
|
|
694
|
+
import {myFunction} from 'my-package/my-module.js';
|
|
695
|
+
|
|
696
|
+
test('myFunction', async t => {
|
|
697
|
+
await t.test('returns correct result for basic input', t => {
|
|
698
|
+
t.deepEqual(myFunction(1, 2), {sum: 3, product: 2});
|
|
699
|
+
});
|
|
700
|
+
|
|
701
|
+
await t.test('handles edge cases', t => {
|
|
702
|
+
t.deepEqual(myFunction(0, 0), {sum: 0, product: 0});
|
|
703
|
+
t.throws(() => myFunction(null), 'throws on null input');
|
|
704
|
+
});
|
|
705
|
+
|
|
706
|
+
await t.test('async variant', async t => {
|
|
707
|
+
const result = await myFunction.async(1, 2);
|
|
708
|
+
t.equal(result.sum, 3);
|
|
709
|
+
});
|
|
710
|
+
});
|
|
711
|
+
```
|
|
712
|
+
|
|
713
|
+
### Testing a class
|
|
714
|
+
|
|
715
|
+
```js
|
|
716
|
+
import test from 'tape-six';
|
|
717
|
+
import {MyClass} from 'my-package/my-class.js';
|
|
718
|
+
|
|
719
|
+
test('MyClass', async t => {
|
|
720
|
+
let instance;
|
|
721
|
+
t.beforeEach(() => {
|
|
722
|
+
instance = new MyClass();
|
|
723
|
+
});
|
|
724
|
+
|
|
725
|
+
await t.test('constructor', t => {
|
|
726
|
+
t.ok(instance, 'creates instance');
|
|
727
|
+
t.equal(instance.size, 0, 'starts empty');
|
|
728
|
+
});
|
|
729
|
+
|
|
730
|
+
await t.test('add', t => {
|
|
731
|
+
instance.add('item');
|
|
732
|
+
t.equal(instance.size, 1, 'size increases');
|
|
733
|
+
});
|
|
734
|
+
|
|
735
|
+
await t.test('remove', t => {
|
|
736
|
+
instance.add('item');
|
|
737
|
+
instance.remove('item');
|
|
738
|
+
t.equal(instance.size, 0, 'size decreases');
|
|
739
|
+
});
|
|
740
|
+
});
|
|
741
|
+
```
|
|
742
|
+
|
|
743
|
+
### Verifying after writing tests
|
|
744
|
+
|
|
745
|
+
```bash
|
|
746
|
+
node tests/test-<name>.js # run your new test file directly
|
|
747
|
+
npm test # run full suite to check for regressions
|
|
748
|
+
```
|
|
749
|
+
|
|
750
|
+
## Links
|
|
751
|
+
|
|
752
|
+
- Full API reference: https://github.com/uhop/tape-six/wiki/Tester
|
|
753
|
+
- Hooks documentation: https://github.com/uhop/tape-six/wiki/Before-and-after-hooks
|
|
754
|
+
- Configuration: https://github.com/uhop/tape-six/wiki/Set-up-tests
|
|
755
|
+
- Supported flags: https://github.com/uhop/tape-six/wiki/Supported-flags
|
|
756
|
+
- npm: https://www.npmjs.com/package/tape-six
|
package/index.d.ts
CHANGED
|
@@ -66,15 +66,15 @@ export declare interface Tester {
|
|
|
66
66
|
/**
|
|
67
67
|
* A symbol that can be used to match any value.
|
|
68
68
|
*/
|
|
69
|
-
any:
|
|
69
|
+
any: symbol;
|
|
70
70
|
|
|
71
71
|
/**
|
|
72
72
|
* A symbol that can be used to match any value. An alias of `any`.
|
|
73
73
|
*/
|
|
74
|
-
_:
|
|
74
|
+
_: symbol;
|
|
75
75
|
|
|
76
76
|
/**
|
|
77
|
-
* A signal that can be used to abort asynchronous operations. It is triggered when the test
|
|
77
|
+
* A signal that can be used to abort asynchronous operations. It is triggered when the test ends.
|
|
78
78
|
*/
|
|
79
79
|
signal: AbortSignal;
|
|
80
80
|
|
|
@@ -287,7 +287,7 @@ export declare interface Tester {
|
|
|
287
287
|
): Promise<void>;
|
|
288
288
|
|
|
289
289
|
/**
|
|
290
|
-
* Runs
|
|
290
|
+
* Runs a callback-based test wrapped in a promise.
|
|
291
291
|
* @param name - The name of the test
|
|
292
292
|
* @param fn - The test function
|
|
293
293
|
* @param options - The test options
|
|
@@ -299,7 +299,7 @@ export declare interface Tester {
|
|
|
299
299
|
): Promise<void>;
|
|
300
300
|
|
|
301
301
|
/**
|
|
302
|
-
* Runs
|
|
302
|
+
* Runs a callback-based test wrapped in a promise.
|
|
303
303
|
* @param name - The name of the test
|
|
304
304
|
* @param options - The test options
|
|
305
305
|
* @param fn - The test function
|
|
@@ -311,7 +311,7 @@ export declare interface Tester {
|
|
|
311
311
|
): Promise<void>;
|
|
312
312
|
|
|
313
313
|
/**
|
|
314
|
-
* Runs
|
|
314
|
+
* Runs a callback-based test wrapped in a promise.
|
|
315
315
|
* @param fn - The test function
|
|
316
316
|
* @param options - The test options
|
|
317
317
|
* @param name - The name of the test
|
|
@@ -323,7 +323,7 @@ export declare interface Tester {
|
|
|
323
323
|
): Promise<void>;
|
|
324
324
|
|
|
325
325
|
/**
|
|
326
|
-
* Runs
|
|
326
|
+
* Runs a callback-based test wrapped in a promise.
|
|
327
327
|
* @param fn - The test function
|
|
328
328
|
* @param name - The name of the test
|
|
329
329
|
* @param options - The test options
|
|
@@ -335,7 +335,7 @@ export declare interface Tester {
|
|
|
335
335
|
): Promise<void>;
|
|
336
336
|
|
|
337
337
|
/**
|
|
338
|
-
* Runs
|
|
338
|
+
* Runs a callback-based test wrapped in a promise.
|
|
339
339
|
* @param options - The test options
|
|
340
340
|
* @param fn - The test function
|
|
341
341
|
* @param name - The name of the test
|
|
@@ -347,7 +347,7 @@ export declare interface Tester {
|
|
|
347
347
|
): Promise<void>;
|
|
348
348
|
|
|
349
349
|
/**
|
|
350
|
-
* Runs
|
|
350
|
+
* Runs a callback-based test wrapped in a promise.
|
|
351
351
|
* @param options - The test options
|
|
352
352
|
* @param name - The name of the test
|
|
353
353
|
* @param fn - The test function
|
|
@@ -425,8 +425,8 @@ export declare interface Tester {
|
|
|
425
425
|
notOk(value: unknown, message?: string): void;
|
|
426
426
|
|
|
427
427
|
/**
|
|
428
|
-
* Asserts that `error` is an
|
|
429
|
-
* @param error - The
|
|
428
|
+
* Asserts that `error` is falsy. If the value is truthy (e.g. an Error object), the assertion fails.
|
|
429
|
+
* @param error - The value to test
|
|
430
430
|
* @param message - Optional message to display if the assertion fails
|
|
431
431
|
*/
|
|
432
432
|
error(error: Error | null | unknown, message?: string): void;
|
|
@@ -600,22 +600,22 @@ export declare interface Tester {
|
|
|
600
600
|
notok(value: unknown, message?: string): void;
|
|
601
601
|
|
|
602
602
|
/**
|
|
603
|
-
* Asserts that `error` is an
|
|
604
|
-
* @param error - The
|
|
603
|
+
* Asserts that `error` is falsy. If the value is truthy (e.g. an Error object), the assertion fails. Alias of `error`.
|
|
604
|
+
* @param error - The value to test
|
|
605
605
|
* @param message - Optional message to display if the assertion fails
|
|
606
606
|
*/
|
|
607
607
|
ifError(error: Error | null | unknown, message?: string): void;
|
|
608
608
|
|
|
609
609
|
/**
|
|
610
|
-
* Asserts that `error` is an
|
|
611
|
-
* @param error - The
|
|
610
|
+
* Asserts that `error` is falsy. If the value is truthy (e.g. an Error object), the assertion fails. Alias of `error`.
|
|
611
|
+
* @param error - The value to test
|
|
612
612
|
* @param message - Optional message to display if the assertion fails
|
|
613
613
|
*/
|
|
614
614
|
ifErr(error: Error | null | unknown, message?: string): void;
|
|
615
615
|
|
|
616
616
|
/**
|
|
617
|
-
* Asserts that `error` is an
|
|
618
|
-
* @param error - The
|
|
617
|
+
* Asserts that `error` is falsy. If the value is truthy (e.g. an Error object), the assertion fails. Alias of `error`.
|
|
618
|
+
* @param error - The value to test
|
|
619
619
|
* @param message - Optional message to display if the assertion fails
|
|
620
620
|
*/
|
|
621
621
|
iferror(error: Error | null | unknown, message?: string): void;
|
|
@@ -1122,57 +1122,64 @@ export declare interface Test {
|
|
|
1122
1122
|
}
|
|
1123
1123
|
|
|
1124
1124
|
/**
|
|
1125
|
-
* The main function
|
|
1125
|
+
* The main function used to define tests. When called inside a test body, it delegates to the current tester.
|
|
1126
|
+
* Many other functions are defined as properties of this function.
|
|
1126
1127
|
*/
|
|
1127
1128
|
export declare const test: Test;
|
|
1128
1129
|
|
|
1129
1130
|
/**
|
|
1130
|
-
* An alias for `test`.
|
|
1131
|
+
* An alias for `test`. When called inside a test body, it delegates to the current tester.
|
|
1131
1132
|
*/
|
|
1132
1133
|
export declare const suite = test;
|
|
1133
1134
|
|
|
1134
1135
|
/**
|
|
1135
|
-
* An alias for `test`.
|
|
1136
|
+
* An alias for `test`. When called inside a test body, it delegates to the current tester.
|
|
1136
1137
|
*/
|
|
1137
1138
|
export declare const describe = test;
|
|
1138
1139
|
|
|
1139
1140
|
/**
|
|
1140
|
-
* An alias for `test`.
|
|
1141
|
+
* An alias for `test`. When called inside a test body, it delegates to the current tester.
|
|
1141
1142
|
*/
|
|
1142
1143
|
export declare const it = test;
|
|
1143
1144
|
|
|
1144
1145
|
/**
|
|
1145
1146
|
* Registers a function that will be called before all 1st-level embedded tests in the current scope.
|
|
1147
|
+
* When called inside a test body, delegates to the current tester.
|
|
1146
1148
|
* @param fn a hook function
|
|
1147
1149
|
*/
|
|
1148
1150
|
export declare const beforeAll: typeof test.beforeAll;
|
|
1149
1151
|
|
|
1150
1152
|
/**
|
|
1151
1153
|
* Registers a function that will be called after all 1st-level embedded tests in the current scope.
|
|
1154
|
+
* When called inside a test body, delegates to the current tester.
|
|
1152
1155
|
* @param fn a hook function
|
|
1153
1156
|
*/
|
|
1154
1157
|
export declare const afterAll: typeof test.afterAll;
|
|
1155
1158
|
|
|
1156
1159
|
/**
|
|
1157
1160
|
* Registers a function that will be called before each 1st-level embedded test in the current scope.
|
|
1161
|
+
* When called inside a test body, delegates to the current tester.
|
|
1158
1162
|
* @param fn a hook function
|
|
1159
1163
|
*/
|
|
1160
1164
|
export declare const beforeEach: typeof test.beforeEach;
|
|
1161
1165
|
|
|
1162
1166
|
/**
|
|
1163
1167
|
* Registers a function that will be called after each 1st-level embedded test in the current scope.
|
|
1168
|
+
* When called inside a test body, delegates to the current tester.
|
|
1164
1169
|
* @param fn a hook function
|
|
1165
1170
|
*/
|
|
1166
1171
|
export declare const afterEach: typeof test.afterEach;
|
|
1167
1172
|
|
|
1168
1173
|
/**
|
|
1169
1174
|
* Registers a function that will be called before all 1st-level embedded tests in the current scope. An alias for `beforeAll()`.
|
|
1175
|
+
* When called inside a test body, delegates to the current tester.
|
|
1170
1176
|
* @param fn a hook function
|
|
1171
1177
|
*/
|
|
1172
1178
|
export declare const before: typeof test.before;
|
|
1173
1179
|
|
|
1174
1180
|
/**
|
|
1175
1181
|
* Registers a function that will be called after all 1st-level embedded tests in the current scope. An alias for `afterAll()`.
|
|
1182
|
+
* When called inside a test body, delegates to the current tester.
|
|
1176
1183
|
* @param fn a hook function
|
|
1177
1184
|
*/
|
|
1178
1185
|
export declare const after: typeof test.after;
|
package/llms-full.txt
CHANGED
|
@@ -9,6 +9,7 @@
|
|
|
9
9
|
- Browser testing with built-in web UI and automation support (Puppeteer, Playwright)
|
|
10
10
|
- Before/after hooks: `beforeAll`, `afterAll`, `beforeEach`, `afterEach`
|
|
11
11
|
- `test()` is aliased as `suite()`, `describe()`, and `it()` for easy migration
|
|
12
|
+
- When called inside a test body, top-level functions auto-delegate to the current tester
|
|
12
13
|
- Compatible with `AssertionError`-based libraries like `node:assert` and `chai`
|
|
13
14
|
|
|
14
15
|
## Quick start
|
|
@@ -128,6 +129,20 @@ test('top', async t => {
|
|
|
128
129
|
|
|
129
130
|
Always `await` embedded tests to preserve execution order.
|
|
130
131
|
|
|
132
|
+
Top-level `test()`/`it()`/`describe()` auto-delegate when called inside a test body, so these are equivalent:
|
|
133
|
+
|
|
134
|
+
```js
|
|
135
|
+
import {describe, it, before, beforeEach} from 'tape-six';
|
|
136
|
+
|
|
137
|
+
describe('module', () => {
|
|
138
|
+
before(() => { /* setup */ });
|
|
139
|
+
beforeEach(() => { /* per-test setup */ });
|
|
140
|
+
|
|
141
|
+
it('works', t => { t.ok(true); });
|
|
142
|
+
it('also works', t => { t.equal(1, 1); });
|
|
143
|
+
});
|
|
144
|
+
```
|
|
145
|
+
|
|
131
146
|
## Tester API
|
|
132
147
|
|
|
133
148
|
The `Tester` object is passed to test functions. All `msg` arguments are optional.
|
|
@@ -163,17 +178,17 @@ The `Tester` object is passed to test functions. All `msg` arguments are optiona
|
|
|
163
178
|
|
|
164
179
|
### Embedded test methods
|
|
165
180
|
|
|
166
|
-
- `test(name, options, testFn)` — nested test suite (async, await it).
|
|
181
|
+
- `test(name, options, testFn)` — nested test suite (async, await it). Top-level `test()`/`it()` also works.
|
|
167
182
|
- `skip(name, options, testFn)` — skip nested suite.
|
|
168
183
|
- `todo(name, options, testFn)` — TODO nested suite.
|
|
169
184
|
- `asPromise(name, options, testPromiseFn)` — callback-style nested suite.
|
|
170
185
|
|
|
171
186
|
### Hooks
|
|
172
187
|
|
|
173
|
-
- `beforeAll(fn)` / `before(fn)` — run before first nested test.
|
|
174
|
-
- `afterAll(fn)` / `after(fn)` — run after last nested test.
|
|
175
|
-
- `beforeEach(fn)` — run before each nested test.
|
|
176
|
-
- `afterEach(fn)` — run after each nested test.
|
|
188
|
+
- `beforeAll(fn)` / `before(fn)` — run before first nested test. Top-level `beforeAll()`/`before()` also works.
|
|
189
|
+
- `afterAll(fn)` / `after(fn)` — run after last nested test. Top-level `afterAll()`/`after()` also works.
|
|
190
|
+
- `beforeEach(fn)` — run before each nested test. Top-level `beforeEach()` also works.
|
|
191
|
+
- `afterEach(fn)` — run after each nested test. Top-level `afterEach()` also works.
|
|
177
192
|
|
|
178
193
|
### Miscellaneous
|
|
179
194
|
|
|
@@ -211,12 +226,16 @@ beforeEach(() => { /* runs before each top-level test */ });
|
|
|
211
226
|
afterEach(() => { /* runs after each top-level test */ });
|
|
212
227
|
```
|
|
213
228
|
|
|
214
|
-
Nested hooks (
|
|
229
|
+
Nested hooks (top-level functions auto-delegate to current tester):
|
|
215
230
|
|
|
216
231
|
```js
|
|
232
|
+
import {test, beforeEach, afterEach} from 'tape-six';
|
|
233
|
+
|
|
217
234
|
test('suite', async t => {
|
|
218
|
-
|
|
219
|
-
|
|
235
|
+
beforeEach(() => { /* before each nested test */ });
|
|
236
|
+
afterEach(() => { /* after each nested test */ });
|
|
237
|
+
|
|
238
|
+
// equivalent: t.beforeEach(), t.afterEach()
|
|
220
239
|
|
|
221
240
|
await t.test('test 1', t => t.pass());
|
|
222
241
|
await t.test('test 2', t => t.pass());
|
|
@@ -323,6 +342,7 @@ Browser: `http://localhost:3000/?flags=FO`
|
|
|
323
342
|
- `TAPE6_PAR` — number of parallel workers.
|
|
324
343
|
- `TAPE6_TAP` — force TAP reporter (any non-empty value).
|
|
325
344
|
- `TAPE6_JSONL` — force JSONL reporter (any non-empty value).
|
|
345
|
+
- `TAPE6_MIN` — force minimal reporter (any non-empty value).
|
|
326
346
|
- `TAPE6_TEST_FILE_NAME` — set by runners to identify the current test file.
|
|
327
347
|
|
|
328
348
|
## Browser testing
|
package/llms.txt
CHANGED
|
@@ -33,6 +33,7 @@ Registers a test. All three arguments are optional and can be in any order (reco
|
|
|
33
33
|
Returns a promise that resolves when the test finishes. Usually no need to await.
|
|
34
34
|
|
|
35
35
|
Aliases: `suite`, `describe`, `it` — all are the same function.
|
|
36
|
+
When called inside a test body, these (and all hook functions) automatically delegate to the current tester — no need to use `t.test()` or `t.before()` explicitly.
|
|
36
37
|
|
|
37
38
|
```js
|
|
38
39
|
import {test, describe, it, suite} from 'tape-six';
|
|
@@ -97,7 +98,7 @@ The object passed to test functions. Provides assertions and test control.
|
|
|
97
98
|
|
|
98
99
|
#### Embedded tests (all async, should be awaited)
|
|
99
100
|
|
|
100
|
-
- `await t.test(name, options, testFn)` — nested test.
|
|
101
|
+
- `await t.test(name, options, testFn)` — nested test. Top-level `test()`/`it()` also works (auto-delegates).
|
|
101
102
|
- `await t.skip(name, options, testFn)` — nested skipped test.
|
|
102
103
|
- `await t.todo(name, options, testFn)` — nested TODO test.
|
|
103
104
|
- `await t.asPromise(name, options, testPromiseFn)` — nested callback-style test.
|
|
@@ -122,12 +123,14 @@ afterAll(() => { /* runs once after all tests */ });
|
|
|
122
123
|
beforeEach(() => { /* runs before each test */ });
|
|
123
124
|
afterEach(() => { /* runs after each test */ });
|
|
124
125
|
|
|
125
|
-
//
|
|
126
|
+
// Inside a test — top-level functions auto-delegate to current tester:
|
|
126
127
|
test('suite', async t => {
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
128
|
+
beforeAll(() => { /* before embedded tests */ });
|
|
129
|
+
afterAll(() => { /* after embedded tests */ });
|
|
130
|
+
beforeEach(() => { /* before each embedded test */ });
|
|
131
|
+
afterEach(() => { /* after each embedded test */ });
|
|
132
|
+
|
|
133
|
+
// equivalent: t.beforeAll(), t.afterAll(), t.beforeEach(), t.afterEach()
|
|
131
134
|
|
|
132
135
|
await t.test('inner', t => { t.pass(); });
|
|
133
136
|
});
|
|
@@ -198,9 +201,12 @@ test('partial match', t => {
|
|
|
198
201
|
### Using describe/it style
|
|
199
202
|
|
|
200
203
|
```js
|
|
201
|
-
import {describe, it} from 'tape-six';
|
|
204
|
+
import {describe, it, before, beforeEach} from 'tape-six';
|
|
202
205
|
|
|
203
206
|
describe('my module', () => {
|
|
207
|
+
before(() => { /* setup — auto-delegates to current tester */ });
|
|
208
|
+
beforeEach(() => { /* per-test setup */ });
|
|
209
|
+
|
|
204
210
|
it('should work', t => {
|
|
205
211
|
t.ok(true);
|
|
206
212
|
});
|
|
@@ -223,6 +229,15 @@ npx tape6-seq # run sequentially (no worker threads)
|
|
|
223
229
|
npx tape6-server --trace # start browser test server
|
|
224
230
|
```
|
|
225
231
|
|
|
232
|
+
## Environment variables
|
|
233
|
+
|
|
234
|
+
- `TAPE6_FLAGS` — flags string.
|
|
235
|
+
- `TAPE6_PAR` — number of parallel workers.
|
|
236
|
+
- `TAPE6_TAP` — force TAP reporter (any non-empty value).
|
|
237
|
+
- `TAPE6_JSONL` — force JSONL reporter (any non-empty value).
|
|
238
|
+
- `TAPE6_MIN` — force minimal reporter (any non-empty value).
|
|
239
|
+
- `TAPE6_TEST_FILE_NAME` — set by runners to identify the current test file.
|
|
240
|
+
|
|
226
241
|
## Configuration (package.json)
|
|
227
242
|
|
|
228
243
|
```json
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "tape-six",
|
|
3
|
-
"version": "1.7.
|
|
3
|
+
"version": "1.7.3",
|
|
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",
|
|
@@ -61,7 +61,8 @@
|
|
|
61
61
|
"bun",
|
|
62
62
|
"nodejs",
|
|
63
63
|
"tap-protocol",
|
|
64
|
-
"cross-runtime"
|
|
64
|
+
"cross-runtime",
|
|
65
|
+
"zero-dependency"
|
|
65
66
|
],
|
|
66
67
|
"author": "Eugene Lazutkin <eugene.lazutkin@gmail.com> (https://www.lazutkin.com/)",
|
|
67
68
|
"funding": "https://github.com/sponsors/uhop",
|
|
@@ -78,7 +79,9 @@
|
|
|
78
79
|
"web-app",
|
|
79
80
|
"src",
|
|
80
81
|
"llms.txt",
|
|
81
|
-
"llms-full.txt"
|
|
82
|
+
"llms-full.txt",
|
|
83
|
+
"TESTING.md",
|
|
84
|
+
"workflows"
|
|
82
85
|
],
|
|
83
86
|
"tape6": {
|
|
84
87
|
"tests": [
|
|
@@ -101,7 +104,7 @@
|
|
|
101
104
|
},
|
|
102
105
|
"devDependencies": {
|
|
103
106
|
"@types/chai": "^5.2.3",
|
|
104
|
-
"@types/node": "^25.3.
|
|
107
|
+
"@types/node": "^25.3.1",
|
|
105
108
|
"chai": "^6.2.2",
|
|
106
109
|
"playwright": "^1.58.2",
|
|
107
110
|
"puppeteer": "^24.37.5",
|
package/src/Tester.js
CHANGED
|
@@ -236,7 +236,7 @@ export class Tester {
|
|
|
236
236
|
|
|
237
237
|
deepLooseEqual(a, b, msg) {
|
|
238
238
|
this.reporter.report({
|
|
239
|
-
name: msg || 'should be loosely equal',
|
|
239
|
+
name: msg || 'should be deeply loosely equal',
|
|
240
240
|
test: this.testNumber,
|
|
241
241
|
marker: new Error(),
|
|
242
242
|
time: this.timer.now(),
|
|
@@ -251,7 +251,7 @@ export class Tester {
|
|
|
251
251
|
|
|
252
252
|
notDeepLooseEqual(a, b, msg) {
|
|
253
253
|
this.reporter.report({
|
|
254
|
-
name: msg || 'should not be loosely equal',
|
|
254
|
+
name: msg || 'should not be deeply loosely equal',
|
|
255
255
|
test: this.testNumber,
|
|
256
256
|
marker: new Error(),
|
|
257
257
|
time: this.timer.now(),
|
|
@@ -1,16 +1,5 @@
|
|
|
1
1
|
import Reporter from './Reporter.js';
|
|
2
2
|
|
|
3
|
-
const getEnvVar = name => {
|
|
4
|
-
if (typeof Deno == 'object' && Deno?.version) {
|
|
5
|
-
return Deno.env.get(name);
|
|
6
|
-
} else if (typeof Bun == 'object' && Bun?.version) {
|
|
7
|
-
return Bun.env[name];
|
|
8
|
-
} else if (typeof process == 'object' && process?.versions?.node) {
|
|
9
|
-
return process.env[name];
|
|
10
|
-
}
|
|
11
|
-
return undefined;
|
|
12
|
-
};
|
|
13
|
-
|
|
14
3
|
export class MinReporter extends Reporter {
|
|
15
4
|
constructor({failOnce = false, originalConsole} = {}) {
|
|
16
5
|
super({failOnce});
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
const DEFAULT_START_TIMEOUT = 5_000;
|
|
2
2
|
|
|
3
3
|
const getTimeout = () => {
|
|
4
|
-
const timeoutValue = Deno.env.get('
|
|
4
|
+
const timeoutValue = Deno.env.get('TAPE6_WORKER_START_TIMEOUT');
|
|
5
5
|
if (!timeoutValue) return DEFAULT_START_TIMEOUT;
|
|
6
6
|
let timeout = Number(timeoutValue);
|
|
7
7
|
if (isNaN(timeout) || timeout <= 0 || timeout === Infinity) timeout = DEFAULT_START_TIMEOUT;
|
package/src/test.js
CHANGED
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
---
|
|
2
|
+
description: Write or update tape-six tests for a module or feature
|
|
3
|
+
---
|
|
4
|
+
|
|
5
|
+
# Write Tests
|
|
6
|
+
|
|
7
|
+
Write or update tests using the tape-six testing library.
|
|
8
|
+
|
|
9
|
+
## Notes
|
|
10
|
+
|
|
11
|
+
- `tape-six` supports ES modules (`.js`, `.mjs`, `.ts`, `.mts`) and CommonJS (`.cjs`, `.cts`).
|
|
12
|
+
- TypeScript is supported natively — no transpilation needed (Node 22+, Deno, Bun run `.ts` files directly).
|
|
13
|
+
- The default `tape6` runner uses worker threads for parallel execution. `tape6-seq` runs sequentially in-process — useful for debugging or when tests share state.
|
|
14
|
+
|
|
15
|
+
## Steps
|
|
16
|
+
|
|
17
|
+
1. Read the testing guide at `node_modules/tape-six/TESTING.md` for API reference and patterns.
|
|
18
|
+
2. Identify the module or feature to test. Read its source code to understand the public API.
|
|
19
|
+
3. Create or update the test file in `tests/test-<name>.js` (or `.ts` for TypeScript projects):
|
|
20
|
+
- Import `test` from `tape-six` (ESM: `import test from 'tape-six'`; CJS: `const {test} = require('tape-six')`).
|
|
21
|
+
- Import the module under test using the project's package name.
|
|
22
|
+
- Write one top-level `test()` per logical group.
|
|
23
|
+
- Use embedded `await t.test()` for sub-cases.
|
|
24
|
+
- Use `t.beforeEach`/`t.afterEach` for shared setup/teardown.
|
|
25
|
+
- Cover: normal operation, edge cases, error conditions.
|
|
26
|
+
- Use `t.equal` for primitives, `t.deepEqual` for objects/arrays, `t.throws` for errors, `await t.rejects` for async errors.
|
|
27
|
+
- All `msg` arguments are optional but recommended for clarity.
|
|
28
|
+
// turbo
|
|
29
|
+
4. Run the new test file directly to verify: `node tests/test-<name>.js`
|
|
30
|
+
// turbo
|
|
31
|
+
5. Run the full test suite to check for regressions: `npm test`
|
|
32
|
+
- If debugging, use `npm run test:seq` (runs sequentially, easier to trace issues).
|
|
33
|
+
6. Report results and any failures.
|