@git.zone/tstest 2.4.2 → 2.5.0

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.
@@ -3,7 +3,7 @@
3
3
  */
4
4
  export const commitinfo = {
5
5
  name: '@git.zone/tstest',
6
- version: '2.4.2',
6
+ version: '2.5.0',
7
7
  description: 'a test utility to run tests that match test/**/*.ts'
8
8
  };
9
9
  //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiMDBfY29tbWl0aW5mb19kYXRhLmpzIiwic291cmNlUm9vdCI6IiIsInNvdXJjZXMiOlsiLi4vdHMvMDBfY29tbWl0aW5mb19kYXRhLnRzIl0sIm5hbWVzIjpbXSwibWFwcGluZ3MiOiJBQUFBOztHQUVHO0FBQ0gsTUFBTSxDQUFDLE1BQU0sVUFBVSxHQUFHO0lBQ3hCLElBQUksRUFBRSxrQkFBa0I7SUFDeEIsT0FBTyxFQUFFLE9BQU87SUFDaEIsV0FBVyxFQUFFLHFEQUFxRDtDQUNuRSxDQUFBIn0=
@@ -6,6 +6,7 @@
6
6
  * - test.chromium.ts
7
7
  * - test.node+chromium.ts
8
8
  * - test.deno+bun.ts
9
+ * - test.all.ts (runs on all runtimes)
9
10
  * - test.chromium.nonci.ts
10
11
  */
11
12
  export type Runtime = 'node' | 'chromium' | 'deno' | 'bun';
@@ -6,11 +6,13 @@
6
6
  * - test.chromium.ts
7
7
  * - test.node+chromium.ts
8
8
  * - test.deno+bun.ts
9
+ * - test.all.ts (runs on all runtimes)
9
10
  * - test.chromium.nonci.ts
10
11
  */
11
12
  const KNOWN_RUNTIMES = new Set(['node', 'chromium', 'deno', 'bun']);
12
13
  const KNOWN_MODIFIERS = new Set(['nonci']);
13
14
  const VALID_EXTENSIONS = new Set(['ts', 'tsx', 'mts', 'cts']);
15
+ const ALL_RUNTIMES = ['node', 'chromium', 'deno', 'bun'];
14
16
  // Legacy mappings for backwards compatibility
15
17
  const LEGACY_RUNTIME_MAP = {
16
18
  browser: ['chromium'],
@@ -67,8 +69,12 @@ export function parseTestFilename(filePath, config = {}) {
67
69
  const runtimeCandidates = token.split('+').map(r => r.trim()).filter(Boolean);
68
70
  const validRuntimes = [];
69
71
  const invalidRuntimes = [];
72
+ let hasAllKeyword = false;
70
73
  for (const candidate of runtimeCandidates) {
71
- if (KNOWN_RUNTIMES.has(candidate)) {
74
+ if (candidate === 'all') {
75
+ hasAllKeyword = true;
76
+ }
77
+ else if (KNOWN_RUNTIMES.has(candidate)) {
72
78
  // Dedupe: only add if not already in list
73
79
  if (!validRuntimes.includes(candidate)) {
74
80
  validRuntimes.push(candidate);
@@ -78,10 +84,16 @@ export function parseTestFilename(filePath, config = {}) {
78
84
  invalidRuntimes.push(candidate);
79
85
  }
80
86
  }
87
+ // If 'all' keyword is present, expand to all runtimes
88
+ if (hasAllKeyword) {
89
+ runtimes = [...ALL_RUNTIMES];
90
+ runtimeTokenIndex = i;
91
+ break;
92
+ }
81
93
  if (invalidRuntimes.length > 0) {
82
94
  if (strictUnknownRuntime) {
83
95
  throw new Error(`Unknown runtime(s) in "${fileName}": ${invalidRuntimes.join(', ')}. ` +
84
- `Valid runtimes: ${Array.from(KNOWN_RUNTIMES).join(', ')}`);
96
+ `Valid runtimes: ${Array.from(KNOWN_RUNTIMES).join(', ')}, all`);
85
97
  }
86
98
  else {
87
99
  console.warn(`⚠️ Warning: Unknown runtime(s) in "${fileName}": ${invalidRuntimes.join(', ')}. ` +
@@ -97,6 +109,12 @@ export function parseTestFilename(filePath, config = {}) {
97
109
  break;
98
110
  }
99
111
  }
112
+ // Check if this is the 'all' keyword (expands to all runtimes)
113
+ if (token === 'all') {
114
+ runtimes = [...ALL_RUNTIMES];
115
+ runtimeTokenIndex = i;
116
+ break;
117
+ }
100
118
  // Check if this is a single runtime token
101
119
  if (KNOWN_RUNTIMES.has(token)) {
102
120
  runtimes = [token];
@@ -156,4 +174,4 @@ export function getLegacyMigrationTarget(fileName) {
156
174
  parts.push(parsed.extension);
157
175
  return parts.join('.');
158
176
  }
159
- //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoidHN0ZXN0LmNsYXNzZXMucnVudGltZS5wYXJzZXIuanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyIuLi90cy90c3Rlc3QuY2xhc3Nlcy5ydW50aW1lLnBhcnNlci50cyJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiQUFBQTs7Ozs7Ozs7O0dBU0c7QUFtQkgsTUFBTSxjQUFjLEdBQWdCLElBQUksR0FBRyxDQUFDLENBQUMsTUFBTSxFQUFFLFVBQVUsRUFBRSxNQUFNLEVBQUUsS0FBSyxDQUFDLENBQUMsQ0FBQztBQUNqRixNQUFNLGVBQWUsR0FBZ0IsSUFBSSxHQUFHLENBQUMsQ0FBQyxPQUFPLENBQUMsQ0FBQyxDQUFDO0FBQ3hELE1BQU0sZ0JBQWdCLEdBQWdCLElBQUksR0FBRyxDQUFDLENBQUMsSUFBSSxFQUFFLEtBQUssRUFBRSxLQUFLLEVBQUUsS0FBSyxDQUFDLENBQUMsQ0FBQztBQUUzRSw4Q0FBOEM7QUFDOUMsTUFBTSxrQkFBa0IsR0FBOEI7SUFDcEQsT0FBTyxFQUFFLENBQUMsVUFBVSxDQUFDO0lBQ3JCLElBQUksRUFBRSxDQUFDLE1BQU0sRUFBRSxVQUFVLENBQUM7Q0FDM0IsQ0FBQztBQUVGOzs7R0FHRztBQUNILE1BQU0sVUFBVSxpQkFBaUIsQ0FDL0IsUUFBZ0IsRUFDaEIsU0FBdUIsRUFBRTtJQUV6QixNQUFNLG9CQUFvQixHQUFHLE1BQU0sQ0FBQyxvQkFBb0IsSUFBSSxJQUFJLENBQUM7SUFDakUsTUFBTSxlQUFlLEdBQUcsTUFBTSxDQUFDLGVBQWUsSUFBSSxDQUFDLE1BQU0sQ0FBQyxDQUFDO0lBRTNELDBDQUEwQztJQUMxQyxNQUFNLFFBQVEsR0FBRyxRQUFRLENBQUMsS0FBSyxDQUFDLEdBQUcsQ0FBQyxDQUFDLEdBQUcsRUFBRSxJQUFJLFFBQVEsQ0FBQztJQUN2RCxNQUFNLFFBQVEsR0FBRyxRQUFRLENBQUM7SUFFMUIseUNBQXlDO0lBQ3pDLE1BQU0sT0FBTyxHQUFHLFFBQVEsQ0FBQyxXQUFXLENBQUMsR0FBRyxDQUFD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
177
+ //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoidHN0ZXN0LmNsYXNzZXMucnVudGltZS5wYXJzZXIuanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyIuLi90cy90c3Rlc3QuY2xhc3Nlcy5ydW50aW1lLnBhcnNlci50cyJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiQUFBQTs7Ozs7Ozs7OztHQVVHO0FBbUJILE1BQU0sY0FBYyxHQUFnQixJQUFJLEdBQUcsQ0FBQyxDQUFDLE1BQU0sRUFBRSxVQUFVLEVBQUUsTUFBTSxFQUFFLEtBQUssQ0FBQyxDQUFDLENBQUM7QUFDakYsTUFBTSxlQUFlLEdBQWdCLElBQUksR0FBRyxDQUFDLENBQUMsT0FBTyxDQUFDLENBQUMsQ0FBQztBQUN4RCxNQUFNLGdCQUFnQixHQUFnQixJQUFJLEdBQUcsQ0FBQyxDQUFDLElBQUksRUFBRSxLQUFLLEVBQUUsS0FBSyxFQUFFLEtBQUssQ0FBQyxDQUFDLENBQUM7QUFDM0UsTUFBTSxZQUFZLEdBQWMsQ0FBQyxNQUFNLEVBQUUsVUFBVSxFQUFFLE1BQU0sRUFBRSxLQUFLLENBQUMsQ0FBQztBQUVwRSw4Q0FBOEM7QUFDOUMsTUFBTSxrQkFBa0IsR0FBOEI7SUFDcEQsT0FBTyxFQUFFLENBQUMsVUFBVSxDQUFDO0lBQ3JCLElBQUksRUFBRSxDQUFDLE1BQU0sRUFBRSxVQUFVLENBQUM7Q0FDM0IsQ0FBQztBQUVGOzs7R0FHRztBQUNILE1BQU0sVUFBVSxpQkFBaUIsQ0FDL0IsUUFBZ0IsRUFDaEIsU0FBdUIsRUFBRTtJQUV6QixNQUFNLG9CQUFvQixHQUFHLE1BQU0sQ0FBQyxvQkFBb0IsSUFBSSxJQUFJLENBQUM7SUFDakUsTUFBTSxlQUFlLEdBQUcsTUFBTSxDQUFDLGVBQWUsSUFBSSxDQUFDLE1BQU0sQ0FBQyxDQUFDO0lBRTNELDBDQUEwQztJQUMxQyxNQUFNLFFBQVEsR0FBRyxRQUFRLENBQUMsS0FBSyxDQUFDLEdBQUcsQ0FBQyxDQUFDLEdBQUcsRUFBRSxJQUFJLFFBQVEsQ0FBQztJQUN2RCxNQUFNLFFBQVEsR0FBRyxR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
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@git.zone/tstest",
3
- "version": "2.4.2",
3
+ "version": "2.5.0",
4
4
  "private": false,
5
5
  "description": "a test utility to run tests that match test/**/*.ts",
6
6
  "exports": {
package/readme.md CHANGED
@@ -1,7 +1,7 @@
1
1
  # @git.zone/tstest
2
- 🧪 **A powerful, modern test runner for TypeScript** - making your test runs beautiful and informative!
2
+ 🧪 **A powerful, modern test runner for TypeScript** - making your test runs beautiful and informative across multiple runtimes!
3
3
 
4
- ## Availabililty and Links
4
+ ## Availability and Links
5
5
  * [npmjs.org (npm package)](https://www.npmjs.com/package/@git.zone/tstest)
6
6
  * [code.foss.global (source)](https://code.foss.global/git.zone/tstest)
7
7
 
@@ -12,10 +12,10 @@
12
12
  ### ✨ Key Features
13
13
 
14
14
  - 🎯 **Smart Test Execution** - Run all tests, single files, or use glob patterns
15
+ - 🚀 **Multi-Runtime Support** - Run tests in Node.js, Deno, Bun, and Chromium
15
16
  - 🎨 **Beautiful Output** - Color-coded results with emojis and clean formatting
16
17
  - 📊 **Multiple Output Modes** - Choose from normal, quiet, verbose, or JSON output
17
18
  - 🔍 **Automatic Discovery** - Finds all your test files automatically
18
- - 🌐 **Cross-Environment** - Supports Node.js and browser testing
19
19
  - 📝 **Detailed Logging** - Optional file logging for debugging
20
20
  - ⚡ **Performance Metrics** - See which tests are slow
21
21
  - 🤖 **CI/CD Ready** - JSON output mode for automation
@@ -26,13 +26,10 @@
26
26
  - ⏳ **Timeout Control** - Set custom timeouts for tests
27
27
  - 🔁 **Retry Logic** - Automatically retry failing tests
28
28
  - 🛠️ **Test Fixtures** - Create reusable test data
29
- - 📦 **Browser-Compatible** - Full browser support with embedded tapbundle
30
29
  - 👀 **Watch Mode** - Automatically re-run tests on file changes
31
30
  - 📊 **Real-time Progress** - Live test execution progress updates
32
31
  - 🎨 **Visual Diffs** - Beautiful side-by-side diffs for failed assertions
33
32
  - 🔄 **Event-based Reporting** - Real-time test lifecycle events
34
- - ⚙️ **Test Configuration** - Flexible test settings with .tstest.json files
35
- - 🚀 **Protocol V2** - Enhanced TAP protocol with Unicode delimiters
36
33
 
37
34
  ## Installation
38
35
 
@@ -42,6 +39,154 @@ npm install --save-dev @git.zone/tstest
42
39
  pnpm add -D @git.zone/tstest
43
40
  ```
44
41
 
42
+ ## Multi-Runtime Architecture
43
+
44
+ tstest supports running your tests across multiple JavaScript runtimes, allowing you to verify cross-platform compatibility easily.
45
+
46
+ ### Supported Runtimes
47
+
48
+ - **Node.js** - Default runtime, uses tsrun for TypeScript execution
49
+ - **Chromium** - Browser environment testing with full DOM support
50
+ - **Deno** - Secure TypeScript/JavaScript runtime with modern features
51
+ - **Bun** - Ultra-fast all-in-one JavaScript runtime
52
+
53
+ ### Test File Naming Convention
54
+
55
+ Name your test files with runtime specifiers to control where they run:
56
+
57
+ | Pattern | Runtimes | Example |
58
+ |---------|----------|---------|
59
+ | `*.ts` | Node.js only (default) | `test.api.ts` |
60
+ | `*.node.ts` | Node.js only | `test.server.node.ts` |
61
+ | `*.chromium.ts` | Chromium browser | `test.dom.chromium.ts` |
62
+ | `*.deno.ts` | Deno runtime | `test.http.deno.ts` |
63
+ | `*.bun.ts` | Bun runtime | `test.fast.bun.ts` |
64
+ | `*.all.ts` | All runtimes (Node, Chromium, Deno, Bun) | `test.universal.all.ts` |
65
+ | `*.node+chromium.ts` | Both Node.js and Chromium | `test.isomorphic.node+chromium.ts` |
66
+ | `*.node+deno.ts` | Both Node.js and Deno | `test.cross.node+deno.ts` |
67
+ | `*.deno+bun.ts` | Both Deno and Bun | `test.modern.deno+bun.ts` |
68
+ | `*.chromium.nonci.ts` | Chromium, skip in CI | `test.visual.chromium.nonci.ts` |
69
+ | `*.all.nonci.ts` | All runtimes, skip in CI | `test.comprehensive.all.nonci.ts` |
70
+
71
+ **Multi-Runtime Examples:**
72
+
73
+ ```typescript
74
+ // test.api.all.ts - runs in all runtimes (Node, Chromium, Deno, Bun)
75
+ import { expect, tap } from '@git.zone/tstest/tapbundle';
76
+
77
+ tap.test('universal HTTP test', async () => {
78
+ const response = await fetch('https://api.example.com/test');
79
+ expect(response.status).toEqual(200);
80
+ });
81
+
82
+ export default tap.start();
83
+ ```
84
+
85
+ ```typescript
86
+ // test.api.node+deno+bun.ts - runs in specific runtimes
87
+ import { expect, tap } from '@git.zone/tstest/tapbundle';
88
+
89
+ tap.test('cross-runtime HTTP test', async () => {
90
+ const response = await fetch('https://api.example.com/test');
91
+ expect(response.status).toEqual(200);
92
+ });
93
+
94
+ export default tap.start();
95
+ ```
96
+
97
+ ### Runtime Execution Order
98
+
99
+ When multiple runtimes are specified, tests execute in this order:
100
+ 1. Node.js
101
+ 2. Bun
102
+ 3. Deno
103
+ 4. Chromium
104
+
105
+ ### Legacy Naming (Deprecated)
106
+
107
+ The following patterns are still supported but deprecated. Use the migration tool to update:
108
+
109
+ | Legacy Pattern | Modern Equivalent | Migration Command |
110
+ |----------------|-------------------|-------------------|
111
+ | `*.browser.ts` | `*.chromium.ts` | `tstest migrate` |
112
+ | `*.both.ts` | `*.node+chromium.ts` | `tstest migrate` |
113
+
114
+ When running legacy files, tstest shows a deprecation warning with the suggested new name.
115
+
116
+ ### Migration Tool
117
+
118
+ Migrate your test files from legacy naming to the new convention:
119
+
120
+ ```bash
121
+ # Dry run - see what would change
122
+ tstest migrate --dry-run
123
+
124
+ # Apply migrations (uses git mv to preserve history)
125
+ tstest migrate --write
126
+ ```
127
+
128
+ **Migration Features:**
129
+ - ✅ Uses `git mv` to preserve file history
130
+ - ✅ Idempotent - safe to run multiple times
131
+ - ✅ Dry-run by default for safety
132
+ - ✅ Colored output showing all changes
133
+ - ✅ Handles modifiers like `.nonci` correctly
134
+
135
+ Example output:
136
+ ```
137
+ ============================================================
138
+ Test File Migration Tool
139
+ ============================================================
140
+
141
+ 🔍 DRY RUN MODE - No files will be modified
142
+
143
+ Found 3 legacy test file(s)
144
+
145
+ Would migrate:
146
+ test.browser.ts
147
+ → test.chromium.ts
148
+
149
+ Would migrate:
150
+ test.both.ts
151
+ → test.node+chromium.ts
152
+
153
+ Would migrate:
154
+ test.auth.browser.nonci.ts
155
+ → test.auth.chromium.nonci.ts
156
+
157
+ ============================================================
158
+ Summary:
159
+ Total legacy files: 3
160
+ Successfully migrated: 3
161
+ Errors: 0
162
+ ============================================================
163
+
164
+ To apply these changes, run:
165
+ tstest migrate --write
166
+ ```
167
+
168
+ ### Runtime-Specific Permissions
169
+
170
+ #### Deno Runtime
171
+
172
+ Tests run with these permissions by default:
173
+ ```bash
174
+ --allow-read
175
+ --allow-env
176
+ --allow-net
177
+ --allow-write
178
+ --allow-sys
179
+ --allow-import # Enables npm packages and Node.js built-ins
180
+ --node-modules-dir # Node.js compatibility mode
181
+ --sloppy-imports # Allows .js imports to resolve to .ts files
182
+ ```
183
+
184
+ Configure custom permissions in your test file or via environment variables.
185
+
186
+ #### Bun Runtime
187
+
188
+ Bun runs with its native TypeScript support and full access to Node.js APIs.
189
+
45
190
  ## Usage
46
191
 
47
192
  ### Basic Test Execution
@@ -92,18 +237,29 @@ tstest "test/unit/*.ts"
92
237
  Pattern: test
93
238
  Found: 4 test file(s)
94
239
 
95
- ▶️ test/test.ts (1/4)
96
- Runtime: node.js
97
- ✅ prepare test (1ms)
98
- Summary: 1/1 PASSED
240
+ ━━━ Part 1: Node.js ━━━
241
+
242
+ ▶️ test/test.node+deno.ts (1/4)
243
+ Runtime: Node.js
244
+ ✅ HTTP request works (12ms)
245
+ ✅ JSON parsing works (3ms)
246
+ Summary: 2/2 PASSED in 1.2s
247
+
248
+ ━━━ Part 2: Deno ━━━
249
+
250
+ ▶️ test/test.node+deno.ts (1/4)
251
+ Runtime: Deno
252
+ ✅ HTTP request works (15ms)
253
+ ✅ JSON parsing works (2ms)
254
+ Summary: 2/2 PASSED in 1.1s
99
255
 
100
256
  📊 Test Summary
101
257
  ┌────────────────────────────────┐
102
258
  │ Total Files: 4 │
103
- │ Total Tests: 4
104
- │ Passed: 4
259
+ │ Total Tests: 8
260
+ │ Passed: 8
105
261
  │ Failed: 0 │
106
- │ Duration: 542ms
262
+ │ Duration: 2.4s
107
263
  └────────────────────────────────┘
108
264
 
109
265
  ALL TESTS PASSED! 🎉
@@ -141,19 +297,7 @@ Perfect for CI/CD pipelines:
141
297
  {"event":"summary","summary":{"totalFiles":4,"totalTests":4,"totalPassed":4,"totalFailed":0,"totalDuration":542}}
142
298
  ```
143
299
 
144
- ## Test File Naming Conventions
145
-
146
- tstest supports different test environments through file naming:
147
-
148
- | Pattern | Environment | Example |
149
- |---------|-------------|---------|
150
- | `*.ts` | Node.js (default) | `test.basic.ts` |
151
- | `*.node.ts` | Node.js only | `test.api.node.ts` |
152
- | `*.chrome.ts` | Chrome browser | `test.dom.chrome.ts` |
153
- | `*.browser.ts` | Browser environment | `test.ui.browser.ts` |
154
- | `*.both.ts` | Both Node.js and browser | `test.isomorphic.both.ts` |
155
-
156
- ### Writing Tests with tapbundle
300
+ ## Writing Tests with tapbundle
157
301
 
158
302
  tstest includes tapbundle, a powerful TAP-based test framework. Import it from the embedded tapbundle:
159
303
 
@@ -165,7 +309,7 @@ tap.test('my awesome test', async () => {
165
309
  expect(result).toEqual('expected value');
166
310
  });
167
311
 
168
- tap.start();
312
+ export default tap.start();
169
313
  ```
170
314
 
171
315
  **Module Exports**
@@ -196,7 +340,7 @@ tap.test('async operations', async (tools) => {
196
340
  });
197
341
 
198
342
  // Start test execution
199
- tap.start();
343
+ export default tap.start();
200
344
  ```
201
345
 
202
346
  ### Test Modifiers and Chaining
@@ -231,20 +375,20 @@ tap.timeout(5000)
231
375
  ```typescript
232
376
  tap.describe('User Management', () => {
233
377
  let testDatabase;
234
-
378
+
235
379
  tap.beforeEach(async () => {
236
380
  testDatabase = await createTestDB();
237
381
  });
238
-
382
+
239
383
  tap.afterEach(async () => {
240
384
  await testDatabase.cleanup();
241
385
  });
242
-
386
+
243
387
  tap.test('should create user', async () => {
244
388
  const user = await testDatabase.createUser({ name: 'John' });
245
389
  expect(user.id).toBeDefined();
246
390
  });
247
-
391
+
248
392
  tap.describe('User Permissions', () => {
249
393
  tap.test('should set admin role', async () => {
250
394
  // Nested describe blocks
@@ -262,37 +406,37 @@ tap.test('using test tools', async (tools) => {
262
406
  // Delay utilities
263
407
  await tools.delayFor(1000); // delay for 1000ms
264
408
  await tools.delayForRandom(100, 500); // random delay between 100-500ms
265
-
409
+
266
410
  // Skip test conditionally
267
411
  tools.skipIf(process.env.CI === 'true', 'Skipping in CI');
268
-
412
+
269
413
  // Skip test unconditionally
270
414
  if (!apiKeyAvailable) {
271
415
  tools.skip('API key not available');
272
416
  }
273
-
417
+
274
418
  // Mark as todo
275
419
  tools.todo('Needs implementation');
276
-
420
+
277
421
  // Retry configuration
278
422
  tools.retry(3); // Set retry count
279
-
423
+
280
424
  // Timeout configuration
281
425
  tools.timeout(10000); // Set timeout to 10s
282
-
426
+
283
427
  // Context sharing between tests
284
428
  tools.context.set('userId', 12345);
285
429
  const userId = tools.context.get('userId');
286
-
430
+
287
431
  // Deferred promises
288
432
  const deferred = tools.defer();
289
433
  setTimeout(() => deferred.resolve('done'), 100);
290
434
  await deferred.promise;
291
-
435
+
292
436
  // Colored console output
293
437
  const coloredString = await tools.coloredString('Success!', 'green');
294
438
  console.log(coloredString);
295
-
439
+
296
440
  // Error handling helper
297
441
  const error = await tools.returnError(async () => {
298
442
  throw new Error('Expected error');
@@ -306,10 +450,10 @@ tap.test('using test tools', async (tools) => {
306
450
  ```typescript
307
451
  tap.test('snapshot test', async (tools) => {
308
452
  const output = generateComplexOutput();
309
-
453
+
310
454
  // Compare with saved snapshot
311
455
  await tools.matchSnapshot(output);
312
-
456
+
313
457
  // Named snapshots for multiple checks in one test
314
458
  await tools.matchSnapshot(output.header, 'header');
315
459
  await tools.matchSnapshot(output.body, 'body');
@@ -339,9 +483,9 @@ tap.defineFixture('testPost', async (data) => ({
339
483
  tap.test('fixture test', async (tools) => {
340
484
  const user = await tools.fixture('testUser', { name: 'John' });
341
485
  const post = await tools.fixture('testPost', { authorId: user.id });
342
-
486
+
343
487
  expect(post.authorId).toEqual(user.id);
344
-
488
+
345
489
  // Factory pattern for multiple instances
346
490
  const users = await tools.factory('testUser').createMany(5);
347
491
  expect(users).toHaveLength(5);
@@ -485,7 +629,7 @@ tap.test('first test', async (tools) => {
485
629
  tap.test('second test', async (tools) => {
486
630
  const sessionId = tools.context.get('sessionId');
487
631
  expect(sessionId).toBeDefined();
488
-
632
+
489
633
  // Cleanup
490
634
  tools.context.delete('sessionId');
491
635
  });
@@ -506,9 +650,9 @@ tap.test('DOM manipulation', async () => {
506
650
  <button id="test-btn">Click Me</button>
507
651
  </div>
508
652
  `);
509
-
653
+
510
654
  expect(element.querySelector('h1').textContent).toEqual('Test Title');
511
-
655
+
512
656
  // Simulate interactions
513
657
  const button = element.querySelector('#test-btn');
514
658
  button.click();
@@ -521,7 +665,7 @@ tap.test('CSS testing', async () => {
521
665
  font-size: 16px;
522
666
  }
523
667
  `;
524
-
668
+
525
669
  // styles is a string that can be injected into the page
526
670
  expect(styles).toInclude('color: red');
527
671
  });
@@ -535,7 +679,7 @@ tap.test('error handling', async (tools) => {
535
679
  const error = await tools.returnError(async () => {
536
680
  await functionThatThrows();
537
681
  });
538
-
682
+
539
683
  expect(error).toBeInstanceOf(Error);
540
684
  expect(error.message).toEqual('Expected error message');
541
685
  });
@@ -612,7 +756,7 @@ When assertions fail, tstest shows beautiful side-by-side diffs:
612
756
  String Diff:
613
757
  - Expected
614
758
  + Received
615
-
759
+
616
760
  - Hello World
617
761
  + Hello Universe
618
762
 
@@ -625,73 +769,6 @@ When assertions fail, tstest shows beautiful side-by-side diffs:
625
769
  }
626
770
  ```
627
771
 
628
- ### Test Configuration (.tstest.json)
629
-
630
- Configure test behavior with `.tstest.json` files:
631
-
632
- ```json
633
- {
634
- "timeout": 30000,
635
- "retries": 2,
636
- "bail": false,
637
- "parallel": true,
638
- "tags": ["unit", "fast"],
639
- "env": {
640
- "NODE_ENV": "test"
641
- }
642
- }
643
- ```
644
-
645
- Configuration files are discovered in:
646
- 1. Test file directory
647
- 2. Parent directories (up to project root)
648
- 3. Project root
649
- 4. Home directory (`~/.tstest.json`)
650
-
651
- Settings cascade and merge, with closer files taking precedence.
652
-
653
- ### Event-based Test Reporting
654
-
655
- tstest emits detailed events during test execution for integration with CI/CD tools:
656
-
657
- ```json
658
- {"event":"suite:started","file":"test/api.test.ts","timestamp":"2025-05-26T10:30:00.000Z"}
659
- {"event":"test:started","name":"api endpoint validation","timestamp":"2025-05-26T10:30:00.100Z"}
660
- {"event":"test:progress","name":"api endpoint validation","message":"Validating response schema"}
661
- {"event":"test:completed","name":"api endpoint validation","passed":true,"duration":145}
662
- {"event":"suite:completed","file":"test/api.test.ts","passed":true,"total":2,"failed":0}
663
- ```
664
-
665
- ### Enhanced TAP Protocol (Protocol V2)
666
-
667
- tstest uses an enhanced TAP protocol with Unicode delimiters for better parsing:
668
-
669
- ```
670
- ⟦TSTEST:EVENT:test:started⟧{"name":"my test","timestamp":"2025-05-26T10:30:00.000Z"}
671
- ok 1 my test
672
- ⟦TSTEST:EVENT:test:completed⟧{"name":"my test","passed":true,"duration":145}
673
- ```
674
-
675
- This prevents conflicts with test output that might contain TAP-like formatting.
676
-
677
- ## Advanced Features
678
-
679
- ### Glob Pattern Support
680
-
681
- Run specific test patterns:
682
- ```bash
683
- # Run all unit tests
684
- tstest "test/unit/**/*.ts"
685
-
686
- # Run all integration tests
687
- tstest "test/integration/*.test.ts"
688
-
689
- # Run multiple patterns
690
- tstest "test/**/*.spec.ts" "test/**/*.test.ts"
691
- ```
692
-
693
- **Important**: Always quote glob patterns to prevent shell expansion. Without quotes, the shell will expand the pattern and only pass the first matching file to tstest.
694
-
695
772
  ### Enhanced Test Logging
696
773
 
697
774
  The `--logfile` option provides intelligent test logging with automatic organization:
@@ -849,6 +926,18 @@ tstest test/api/endpoints.test.ts --verbose --timeout 60
849
926
 
850
927
  ## Changelog
851
928
 
929
+ ### Version 2.4.0
930
+ - 🚀 **Multi-Runtime Architecture** - Support for Deno, Bun, Node.js, and Chromium
931
+ - 🔀 **New Naming Convention** - Flexible `.runtime1+runtime2.ts` pattern
932
+ - 🌐 **Universal Testing** - `.all.ts` pattern runs tests on all supported runtimes
933
+ - 🔄 **Migration Tool** - Easy migration from legacy naming (`.browser.ts`, `.both.ts`)
934
+ - 🦕 **Deno Support** - Full Deno runtime with Node.js compatibility
935
+ - 🐰 **Bun Support** - Ultra-fast Bun runtime integration
936
+ - ⚡ **Dynamic Port Selection** - Random port allocation (30000-40000) prevents conflicts
937
+ - 🏗️ **Runtime Adapter Pattern** - Extensible architecture for adding new runtimes
938
+ - 📝 **Deprecation Warnings** - Helpful migration suggestions for legacy naming
939
+ - ✅ **Comprehensive Tests** - Full test coverage for parser and migration tool
940
+
852
941
  ### Version 1.11.0
853
942
  - 👀 Added Watch Mode with `--watch`/`-w` flag for automatic test re-runs
854
943
  - 📊 Implemented real-time test progress updates with event streaming
@@ -878,7 +967,7 @@ tstest test/api/endpoints.test.ts --verbose --timeout 60
878
967
  - 📝 Improved internal protocol design documentation
879
968
  - 🔧 Added protocol v2 utilities for future improvements
880
969
 
881
- ### Version 1.9.1
970
+ ### Version 1.9.1
882
971
  - 🐛 Fixed log file naming to preserve directory structure
883
972
  - 📁 Log files now prevent collisions: `test__dir__file.log`
884
973
 
@@ -901,7 +990,7 @@ tstest test/api/endpoints.test.ts --verbose --timeout 60
901
990
 
902
991
  ## License and Legal Information
903
992
 
904
- This repository contains open-source code that is licensed under the MIT License. A copy of the MIT License can be found in the [license](license.md) file within this repository.
993
+ This repository contains open-source code that is licensed under the MIT License. A copy of the MIT License can be found in the [license](license) file within this repository.
905
994
 
906
995
  **Please note:** The MIT License does not grant permission to use the trade names, trademarks, service marks, or product names of the project, except as required for reasonable and customary use in describing the origin of the work and reproducing the content of the NOTICE file.
907
996
 
@@ -911,9 +1000,9 @@ This project is owned and maintained by Task Venture Capital GmbH. The names and
911
1000
 
912
1001
  ### Company Information
913
1002
 
914
- Task Venture Capital GmbH
1003
+ Task Venture Capital GmbH
915
1004
  Registered at District court Bremen HRB 35230 HB, Germany
916
1005
 
917
1006
  For any legal inquiries or if you require further information, please contact us via email at hello@task.vc.
918
1007
 
919
- By using this repository, you acknowledge that you have read this section, agree to comply with its terms, and understand that the licensing of the code does not imply endorsement by Task Venture Capital GmbH of any derivative works.
1008
+ By using this repository, you acknowledge that you have read this section, agree to comply with its terms, and understand that the licensing of the code does not imply endorsement by Task Venture Capital GmbH of any derivative works.
@@ -3,6 +3,6 @@
3
3
  */
4
4
  export const commitinfo = {
5
5
  name: '@git.zone/tstest',
6
- version: '2.4.2',
6
+ version: '2.5.0',
7
7
  description: 'a test utility to run tests that match test/**/*.ts'
8
8
  }
@@ -6,6 +6,7 @@
6
6
  * - test.chromium.ts
7
7
  * - test.node+chromium.ts
8
8
  * - test.deno+bun.ts
9
+ * - test.all.ts (runs on all runtimes)
9
10
  * - test.chromium.nonci.ts
10
11
  */
11
12
 
@@ -29,6 +30,7 @@ export interface ParserConfig {
29
30
  const KNOWN_RUNTIMES: Set<string> = new Set(['node', 'chromium', 'deno', 'bun']);
30
31
  const KNOWN_MODIFIERS: Set<string> = new Set(['nonci']);
31
32
  const VALID_EXTENSIONS: Set<string> = new Set(['ts', 'tsx', 'mts', 'cts']);
33
+ const ALL_RUNTIMES: Runtime[] = ['node', 'chromium', 'deno', 'bun'];
32
34
 
33
35
  // Legacy mappings for backwards compatibility
34
36
  const LEGACY_RUNTIME_MAP: Record<string, Runtime[]> = {
@@ -102,9 +104,12 @@ export function parseTestFilename(
102
104
  const runtimeCandidates = token.split('+').map(r => r.trim()).filter(Boolean);
103
105
  const validRuntimes: Runtime[] = [];
104
106
  const invalidRuntimes: string[] = [];
107
+ let hasAllKeyword = false;
105
108
 
106
109
  for (const candidate of runtimeCandidates) {
107
- if (KNOWN_RUNTIMES.has(candidate)) {
110
+ if (candidate === 'all') {
111
+ hasAllKeyword = true;
112
+ } else if (KNOWN_RUNTIMES.has(candidate)) {
108
113
  // Dedupe: only add if not already in list
109
114
  if (!validRuntimes.includes(candidate as Runtime)) {
110
115
  validRuntimes.push(candidate as Runtime);
@@ -114,11 +119,18 @@ export function parseTestFilename(
114
119
  }
115
120
  }
116
121
 
122
+ // If 'all' keyword is present, expand to all runtimes
123
+ if (hasAllKeyword) {
124
+ runtimes = [...ALL_RUNTIMES];
125
+ runtimeTokenIndex = i;
126
+ break;
127
+ }
128
+
117
129
  if (invalidRuntimes.length > 0) {
118
130
  if (strictUnknownRuntime) {
119
131
  throw new Error(
120
132
  `Unknown runtime(s) in "${fileName}": ${invalidRuntimes.join(', ')}. ` +
121
- `Valid runtimes: ${Array.from(KNOWN_RUNTIMES).join(', ')}`
133
+ `Valid runtimes: ${Array.from(KNOWN_RUNTIMES).join(', ')}, all`
122
134
  );
123
135
  } else {
124
136
  console.warn(
@@ -138,6 +150,13 @@ export function parseTestFilename(
138
150
  }
139
151
  }
140
152
 
153
+ // Check if this is the 'all' keyword (expands to all runtimes)
154
+ if (token === 'all') {
155
+ runtimes = [...ALL_RUNTIMES];
156
+ runtimeTokenIndex = i;
157
+ break;
158
+ }
159
+
141
160
  // Check if this is a single runtime token
142
161
  if (KNOWN_RUNTIMES.has(token)) {
143
162
  runtimes = [token as Runtime];