@git.zone/tstest 3.6.7 → 4.1.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.hints.md DELETED
@@ -1,510 +0,0 @@
1
- # Architecture Overview
2
-
3
- ## Project Structure
4
-
5
- This project integrates tstest with tapbundle through a modular architecture:
6
-
7
- 1. **tstest** (`/ts/`) - The test runner that discovers and executes test files
8
- 2. **tapbundle** (`/ts_tapbundle/`) - The TAP testing framework for writing tests
9
- 3. **tapbundle_serverside** (`/ts_tapbundle_serverside/`) - Server-side testing utilities (network port finding, runCommand, env vars, HTTPS certs, MongoDB, S3, test assets)
10
-
11
- ## Node Runtime CJS JSON Compatibility
12
-
13
- Node 24 and newer exposed a `tsx` synchronous-loader regression in versions
14
- 4.22.0 and 4.22.1: JSON reached through CommonJS `require()` was transformed
15
- into JavaScript in memory, then passed to Node's CommonJS JSON parser. The
16
- resulting error misleadingly showed generated JavaScript such as `var ...` as
17
- invalid JSON.
18
-
19
- The upstream fix shipped in `tsx` 4.22.2. `tstest` requires
20
- `@git.zone/tsrun` 2.0.5 or newer, whose `tsx` dependency starts at a fixed
21
- version. The Node runtime regression test covers the ESM hook to CommonJS
22
- `require()` to JSON path and verifies that the fixture bytes remain unchanged.
23
-
24
- This loader failure is not evidence that `node_modules/**/db.json` was
25
- overwritten on disk. Any observed file hash or content change is a separate
26
- filesystem-writer issue and must be traced independently.
27
-
28
- ## How Components Work Together
29
-
30
- ### Test Execution Flow
31
-
32
- 1. **CLI Entry Point** (`cli.js` � `cli.ts.js` � `cli.child.ts`)
33
- - The CLI uses tsx to run TypeScript files directly
34
- - Accepts glob patterns to find test files
35
- - Supports options like `--verbose`, `--quiet`, `--web`
36
-
37
- 2. **Test Discovery**
38
- - tstest scans for test files matching the provided pattern
39
- - Defaults to `test/**/*.ts` when no pattern is specified
40
- - Supports both file and directory modes
41
-
42
- 3. **Test Runner**
43
- - Each test file imports `tap` and `expect` from tapbundle
44
- - Tests are written using `tap.test()` with async functions
45
- - Browser tests are compiled with esbuild and run in Chromium via Puppeteer
46
-
47
- ### Key Integration Points
48
-
49
- 1. **Import Structure**
50
- - Test files import from local tapbundle: `import { tap, expect } from '../../ts_tapbundle/index.js'`
51
- - Server-side tests also import from tapbundle_serverside for Node.js-only utilities: `import { tapNodeTools } from '../../ts_tapbundle_serverside/index.js'`
52
-
53
- 2. **WebHelpers**
54
- - Browser tests can use webhelpers for DOM manipulation
55
- - `webhelpers.html` - Template literal for creating HTML strings
56
- - `webhelpers.fixture` - Creates DOM elements from HTML strings
57
- - Automatically detects browser environment and only enables in browser context
58
-
59
- 3. **Build System**
60
- - Uses `tsbuild tsfolders` to compile TypeScript (invoked by `pnpm build`)
61
- - Maintains separate output directories: `/dist_ts/`, `/dist_ts_tapbundle/`, `/dist_ts_tapbundle_serverside/`, `/dist_ts_tapbundle_protocol/`
62
- - Compilation order is resolved automatically based on dependencies in tspublish.json files
63
- - Protocol imports use compiled dist directories:
64
- ```typescript
65
- // In ts/tstest.classes.tap.parser.ts
66
- import { ProtocolParser } from '../dist_ts_tapbundle_protocol/index.js';
67
-
68
- // In ts_tapbundle/tapbundle.classes.tap.ts
69
- import { ProtocolEmitter } from '../dist_ts_tapbundle_protocol/index.js';
70
- ```
71
-
72
- ### Test Scripts
73
-
74
- The package.json defines several test scripts:
75
- - `test` - Builds and runs all tests (tapbundle and tstest)
76
- - `test:tapbundle` - Runs tapbundle framework tests
77
- - `test:tstest` - Runs tstest's own tests
78
- - Both support `:verbose` variants for detailed output
79
-
80
- ### Environment Detection
81
-
82
- The framework automatically detects the runtime environment:
83
- - Node.js tests run directly via tsx
84
- - Browser tests are compiled and served via a local server
85
- - WebHelpers are only enabled in browser environment
86
-
87
- This architecture allows for seamless testing across both Node.js and browser environments while maintaining a clean separation of concerns.
88
-
89
- ## Logging System
90
-
91
- ### Log File Naming (Fixed in v1.9.1)
92
-
93
- When using the `--logfile` flag, tstest creates log files in `.nogit/testlogs/`. The log file naming was updated to preserve directory structure and prevent collisions:
94
-
95
- - **Old behavior**: `test/tapbundle/test.ts` → `.nogit/testlogs/test.log`
96
- - **New behavior**: `test/tapbundle/test.ts` → `.nogit/testlogs/test__tapbundle__test.log`
97
-
98
- This fix ensures that test files with the same basename in different directories don't overwrite each other's logs. The implementation:
99
- 1. Takes the relative path from the current working directory
100
- 2. Replaces path separators (`/`) with double underscores (`__`)
101
- 3. Removes the `.ts` extension
102
- 4. Creates a flat filename that preserves the directory structure
103
-
104
- ### Test Timing Display (Fixed in v1.9.2)
105
-
106
- Fixed an issue where test timing was displayed incorrectly with duplicate values like:
107
- - Before: `✅ test name # time=133ms (0ms)`
108
- - After: `✅ test name (133ms)`
109
-
110
- The issue was in the TAP parser regex which was greedily capturing the entire line including the TAP timing comment. Changed the regex from `(.*)` to `(.*?)` to make it non-greedy, properly separating the test name from the timing metadata.
111
-
112
- ## Protocol Limitations and Improvements
113
-
114
- ### Current TAP Protocol Issues
115
- The current implementation uses standard TAP format with metadata in comments:
116
- ```
117
- ok 1 - test name # time=123ms
118
- ```
119
-
120
- This has several limitations:
121
- 1. **Delimiter Conflict**: Test descriptions containing `#` can break parsing
122
- 2. **Regex Fragility**: Complex regex patterns that are hard to maintain
123
- 3. **Limited Metadata**: Difficult to add rich error information or custom data
124
-
125
- ### Planned Protocol V2
126
- A new internal protocol is being designed that will:
127
- - Use Unicode delimiters `⟦TSTEST:⟧` that won't conflict with test content
128
- - Support structured JSON metadata
129
- - Allow rich error reporting with stack traces and diffs
130
- - Completely replace v1 protocol (no backwards compatibility)
131
-
132
- ### ts_tapbundle_protocol Directory
133
- The protocol v2 implementation is contained in a separate `ts_tapbundle_protocol` directory:
134
- - **Isomorphic Code**: All protocol code works in both browser and Node.js environments
135
- - **No Platform Dependencies**: No Node.js-specific imports, ensuring true cross-platform compatibility
136
- - **Clean Separation**: Protocol logic is isolated from platform-specific code in tstest and tapbundle
137
- - **Shared Implementation**: Both tstest (parser) and tapbundle (emitter) use the same protocol classes
138
- - **Build Process**:
139
- - Compiled by `pnpm build` via tsbuild to `dist_ts_tapbundle_protocol/`
140
- - Build order managed through tspublish.json files
141
- - Other modules import from the compiled dist directory, not source
142
-
143
- This architectural decision ensures the protocol can be used in any JavaScript environment without modification and maintains proper build dependencies.
144
-
145
- See `readme.protocol.md` for the full specification and `ts_tapbundle_protocol/` for the implementation.
146
-
147
- ## Protocol V2 Implementation Status
148
-
149
- The Protocol V2 has been implemented to fix issues with TAP protocol parsing when test descriptions contain special characters like `#`, `###SNAPSHOT###`, or protocol markers like `⟦TSTEST:ERROR⟧`.
150
-
151
- ### Implementation Details:
152
-
153
- 1. **Protocol Components**:
154
- - `ProtocolEmitter` - Generates protocol v2 messages (used by tapbundle)
155
- - `ProtocolParser` - Parses protocol v2 messages (used by tstest)
156
- - Uses Unicode markers `⟦TSTEST:` and `⟧` to avoid conflicts with test content
157
-
158
- 2. **Current Status**:
159
- - ✅ Basic protocol emission and parsing works
160
- - ✅ Handles test descriptions with special characters correctly
161
- - ✅ Supports metadata for timing, tags, errors
162
- - ⚠️ Protocol messages sometimes appear in console output (parsing not catching all cases)
163
-
164
- 3. **Key Findings**:
165
- - `tap.skip.test()` doesn't create actual test objects, just logs and increments counter
166
- - `tap.todo()` method is not implemented (no `addTodo` method in Tap class)
167
- - Protocol parser's `isBlockStart` was fixed to only match exact block markers, not partial matches in test descriptions
168
-
169
- 4. **Import Paths**:
170
- - tstest imports from: `import { ProtocolParser } from '../dist_ts_tapbundle_protocol/index.js';`
171
- - tapbundle imports from: `import { ProtocolEmitter } from '../dist_ts_tapbundle_protocol/index.js';`
172
-
173
- ## Test Configuration System (Phase 2)
174
-
175
- The Test Configuration System has been implemented to provide global settings and lifecycle hooks for tests.
176
-
177
- ### Key Features:
178
-
179
- 1. **00init.ts Discovery**:
180
- - Automatically detects `00init.ts` files in the same directory as test files
181
- - Creates a temporary loader file that imports both `00init.ts` and the test file
182
- - Loader files are cleaned up automatically after test execution
183
-
184
- 2. **Settings Inheritance**:
185
- - Global settings from `00init.ts` → File-level settings → Test-level settings
186
- - Settings include: timeout, retries, retryDelay, bail, concurrency
187
- - Lifecycle hooks: beforeAll, afterAll, beforeEach, afterEach
188
-
189
- 3. **Implementation Details**:
190
- - `SettingsManager` class handles settings inheritance and merging
191
- - `tap.settings()` API allows configuration at any level
192
- - Lifecycle hooks are integrated into test execution flow
193
-
194
- ### Important Development Notes:
195
-
196
- 1. **Local Development**: When developing tstest itself, use `node cli.js` instead of globally installed `tstest` to test changes
197
-
198
- 2. **Console Output Buffering**: Console output from tests is buffered and only displayed for failing tests. TAP-compliant comments (lines starting with `#`) are always shown.
199
-
200
- 3. **TypeScript Warnings**: Fixed async/await warnings in `movePreviousLogFiles()` by using sync versions of file operations
201
-
202
- ## Enhanced Communication Features (Phase 3)
203
-
204
- The Enhanced Communication system has been implemented to provide rich, real-time feedback during test execution.
205
-
206
- ### Key Features:
207
-
208
- 1. **Event-Based Test Lifecycle Reporting**:
209
- - `test:queued` - Test is ready to run
210
- - `test:started` - Test execution begins
211
- - `test:completed` - Test finishes (with pass/fail status)
212
- - `suite:started` - Test suite/describe block begins
213
- - `suite:completed` - Test suite/describe block ends
214
- - `hook:started` - Lifecycle hook (beforeEach/afterEach) begins
215
- - `hook:completed` - Lifecycle hook finishes
216
- - `assertion:failed` - Assertion failure with detailed information
217
-
218
- 2. **Visual Diff Output for Assertion Failures**:
219
- - **String Diffs**: Character-by-character comparison with colored output
220
- - **Object/Array Diffs**: Deep property comparison showing added/removed/changed properties
221
- - **Primitive Diffs**: Clear display of expected vs actual values
222
- - **Colorized Output**: Green for expected, red for actual, yellow for differences
223
- - **Smart Formatting**: Multi-line strings and complex objects are formatted for readability
224
-
225
- 3. **Real-Time Test Progress API**:
226
- - Tests emit progress events as they execute
227
- - tstest parser processes events and updates display in real-time
228
- - Structured event format carries rich metadata (timing, errors, diffs)
229
- - Seamless integration with existing TAP protocol via Protocol V2
230
-
231
- ### Implementation Details:
232
- - Events are transmitted via Protocol V2's `EVENT` block type
233
- - Event data is JSON-encoded within protocol markers
234
- - Parser handles events asynchronously for real-time updates
235
- - Visual diffs are generated using custom diff algorithms for each data type
236
-
237
- ## Watch Mode (Phase 4)
238
-
239
- tstest now supports watch mode for automatic test re-runs on file changes.
240
-
241
- ### Usage
242
- ```bash
243
- tstest test/**/*.ts --watch
244
- tstest test/specific.ts -w
245
- ```
246
-
247
- ### Features
248
- - **Automatic Re-runs**: Tests re-run when any watched file changes
249
- - **Debouncing**: Multiple rapid changes are batched (300ms delay)
250
- - **Clear Output**: Console is cleared before each run for clean results
251
- - **Status Updates**: Shows which files triggered the re-run
252
- - **Graceful Exit**: Press Ctrl+C to stop watching
253
-
254
- ### Options
255
- - `--watch` or `-w`: Enable watch mode
256
- - `--watch-ignore`: Comma-separated patterns to ignore (e.g., `--watch-ignore node_modules,dist`)
257
-
258
- ### Implementation Details
259
- - Uses `@push.rocks/smartchok` for cross-platform file watching
260
- - Watches the entire project directory from where tests are run
261
- - Ignores changes matching the ignore patterns
262
- - Shows "Waiting for file changes..." between runs
263
-
264
- ## Phase 1 API Improvements (v3.1.0)
265
-
266
- ### New Features Implemented
267
-
268
- #### 1. tap.postTask() - Global Teardown (COMPLETED)
269
-
270
- Added symmetric teardown method to complement `tap.preTask()`:
271
-
272
- **Implementation:**
273
- - Created `PostTask` class in `ts_tapbundle/tapbundle.classes.posttask.ts`
274
- - Mirrors PreTask structure with description and function
275
- - Integrated into Tap class execution flow
276
- - Runs after all tests complete but before global `afterAll` hook
277
-
278
- **Usage:**
279
- ```typescript
280
- tap.postTask('cleanup database', async () => {
281
- await cleanupDatabase();
282
- });
283
- ```
284
-
285
- **Execution Order:**
286
- 1. preTask hooks
287
- 2. Global beforeAll
288
- 3. Tests (with suite hooks)
289
- 4. **postTask hooks** ← NEW
290
- 5. Global afterAll
291
-
292
- #### 2. Suite-Level beforeAll/afterAll (COMPLETED)
293
-
294
- Added once-per-suite lifecycle hooks:
295
-
296
- **Implementation:**
297
- - Extended `ITestSuite` interface with `beforeAll` and `afterAll` properties
298
- - Added `tap.beforeAll()` and `tap.afterAll()` methods
299
- - Integrated into `_runSuite()` execution flow
300
- - Properly handles nested suites
301
-
302
- **Usage:**
303
- ```typescript
304
- tap.describe('Database Tests', () => {
305
- tap.beforeAll(async () => {
306
- await initializeDatabaseConnection(); // Runs once
307
- });
308
-
309
- tap.test('test 1', async () => {});
310
- tap.test('test 2', async () => {});
311
-
312
- tap.afterAll(async () => {
313
- await closeDatabaseConnection(); // Runs once
314
- });
315
- });
316
- ```
317
-
318
- **Execution Order per Suite:**
319
- 1. Suite beforeAll ← NEW
320
- 2. Suite beforeEach
321
- 3. Test
322
- 4. Suite afterEach
323
- 5. (Repeat 2-4 for each test)
324
- 6. Child suites (recursive)
325
- 7. Suite afterAll ← NEW
326
-
327
- #### 3. tap.parallel() Fluent Entry Point (COMPLETED)
328
-
329
- Added fluent API for parallel test creation:
330
-
331
- **Implementation:**
332
- - Updated `TestBuilder` class with `_parallel` flag
333
- - Builder constructor accepts optional parallel parameter
334
- - Added `tap.parallel()` method returning configured builder
335
- - Fixed `testParallel()` to return TapTest<T> (was void)
336
-
337
- **Usage:**
338
- ```typescript
339
- // Simple parallel test
340
- tap.parallel().test('fetch data', async () => {});
341
-
342
- // With full configuration
343
- tap
344
- .parallel()
345
- .tags('api', 'integration')
346
- .retry(2)
347
- .timeout(5000)
348
- .test('configured parallel test', async () => {});
349
- ```
350
-
351
- **Benefits:**
352
- - Consistent with other fluent builders (tags, priority, etc.)
353
- - More discoverable than separate `testParallel()` method
354
- - Allows chaining parallel with other configurations
355
- - `testParallel()` kept for backward compatibility
356
-
357
- ### Documentation Updates
358
-
359
- **tapbundle/readme.md:**
360
- - Added suite-level beforeAll/afterAll documentation
361
- - Documented postTask with execution order notes
362
- - Added parallel() fluent API examples
363
- - Expanded TapTools documentation with all methods
364
- - Added "Additional Tap Methods" section for fail(), getSettings(), etc.
365
- - Documented all previously undocumented methods
366
-
367
- ### Tests
368
-
369
- **test/tapbundle/test.new-lifecycle.ts:**
370
- - Tests postTask execution order
371
- - Verifies suite-level beforeAll/afterAll
372
- - Tests nested suite lifecycle
373
- - Validates parallel() fluent API
374
- - Confirms all execution order requirements
375
-
376
- **Test Results:** All 9 tests passing ✅
377
-
378
- ### Breaking Changes
379
-
380
- None - all changes are additive and backward compatible.
381
-
382
- ### Migration Guide
383
-
384
- No migration needed. New features are opt-in:
385
- - Continue using existing patterns
386
- - Adopt new features incrementally
387
- - `testParallel()` still works (recommended: switch to `parallel().test()`)
388
-
389
- ## Fixed Issues
390
-
391
- ### tap.skip.test(), tap.todo(), and tap.only.test() (Fixed)
392
-
393
- Previously reported issues with these methods have been resolved:
394
-
395
- 1. **tap.skip.test()** - Now properly creates test objects that are counted in test results
396
- - Tests marked with `skip.test()` appear in the test count
397
- - Shows as passed with skip directive in TAP output
398
- - `markAsSkipped()` method added to handle pre-test skip marking
399
-
400
- 2. **tap.todo.test()** - Fully implemented with test object creation
401
- - Supports both `tap.todo.test('description')` and `tap.todo.test('description', testFunc)`
402
- - Todo tests are counted and marked with todo directive
403
- - Both regular and parallel todo tests supported
404
-
405
- 3. **tap.only.test()** - Works correctly for focused testing
406
- - When `.only` tests exist, only those tests run
407
- - Other tests are not executed but still counted
408
- - Both regular and parallel only tests supported
409
-
410
- These fixes ensure accurate test counts and proper TAP-compliant output for all test states.
411
-
412
- ## Test Timing Implementation
413
-
414
- ### Timing Architecture
415
-
416
- Test timing is captured using `@push.rocks/smarttime`'s `HrtMeasurement` class, which provides high-resolution timing:
417
-
418
- 1. **Timing Capture**:
419
- - Each `TapTest` instance has its own `HrtMeasurement`
420
- - Timer starts immediately before test function execution
421
- - Timer stops after test completes (or fails/times out)
422
- - Millisecond precision is used for reporting
423
-
424
- 2. **Protocol Integration**:
425
- - Timing is embedded in TAP output using Protocol V2 markers
426
- - Inline format for simple timing: `ok 1 - test name ⟦TSTEST:time:123⟧`
427
- - Block format for complex metadata: `⟦TSTEST:META:{"time":456,"file":"test.ts"}⟧`
428
-
429
- 3. **Performance Metrics Calculation**:
430
- - Average is calculated from sum of individual test times, not total runtime
431
- - Slowest test detection prefers tests with >0ms duration
432
- - Failed tests still contribute their execution time to metrics
433
-
434
- ### Edge Cases and Considerations
435
-
436
- 1. **Sub-millisecond Tests**:
437
- - Very fast tests may report 0ms due to millisecond rounding
438
- - Performance metrics handle this by showing "All tests completed in <1ms" when appropriate
439
-
440
- 2. **Special Test States**:
441
- - **Skipped tests**: Report 0ms (not executed)
442
- - **Todo tests**: Report 0ms (not executed)
443
- - **Failed tests**: Report actual execution time before failure
444
- - **Timeout tests**: Report time until timeout occurred
445
-
446
- 3. **Parallel Test Timing**:
447
- - Each parallel test tracks its own execution time independently
448
- - Parallel tests may have overlapping execution periods
449
- - Total suite time reflects wall-clock time, not sum of test times
450
-
451
- 4. **Hook Timing**:
452
- - `beforeEach`/`afterEach` hooks are not included in individual test times
453
- - Only the actual test function execution is measured
454
-
455
- 5. **Retry Timing**:
456
- - When tests retry, only the final attempt's duration is reported
457
- - Each retry attempt emits separate `test:started` events
458
-
459
- ### Parser Fix for Timing Metadata
460
-
461
- The protocol parser was fixed to correctly handle inline timing metadata:
462
- - Changed condition from `!simpleMatch[1].includes(':')` to check for simple key:value pairs
463
- - Excludes prefixed formats (META:, SKIP:, TODO:, EVENT:) while parsing simple formats like `time:250`
464
-
465
- This ensures timing metadata is correctly extracted and displayed in test results.
466
-
467
- ## Streaming Console Output (Fixed)
468
-
469
- ### Problem
470
- When tests use `process.stdout.write()` for streaming output (without newlines), each write was appearing on a separate line. This happened because:
471
- 1. Child process stdout data events arrive as separate chunks
472
- 2. `TapParser._processLog()` split on `\n` and processed each segment
473
- 3. `testConsoleOutput()` used `console.log()` which added a newline to each call
474
-
475
- ### Solution
476
- The streaming behavior is now preserved by:
477
- 1. **Line buffering for TAP parsing**: Only buffer content that looks like TAP protocol messages
478
- 2. **True streaming for console output**: Use `process.stdout.write()` instead of `console.log()` for partial lines
479
- 3. **Intelligent detection**: `_looksLikeTapStart()` checks if content could be a TAP protocol message
480
-
481
- ### Implementation Details
482
-
483
- **TapParser changes:**
484
- - Added `lineBuffer` property to buffer incomplete TAP protocol lines
485
- - Rewrote `_processLog()` to handle streaming correctly:
486
- - Complete lines (with newline) are processed through protocol parser
487
- - Incomplete lines that look like TAP are buffered
488
- - Incomplete lines that don't look like TAP are streamed immediately
489
- - Added `_looksLikeTapStart()` helper to detect TAP protocol patterns
490
- - Added `_handleConsoleOutput()` to handle console output with proper streaming
491
- - Buffer is flushed on process exit
492
-
493
- **TsTestLogger changes:**
494
- - Added `testConsoleOutputStreaming()` method that uses `process.stdout.write()` in verbose mode
495
- - Added `logToTestFileRaw()` for writing to log files without adding newlines
496
- - In non-verbose mode, streaming content is appended to the last buffered entry
497
-
498
- **TapTestResult changes:**
499
- - Added `addLogLineRaw()` method that doesn't append newlines
500
-
501
- ### Usage
502
- Tests can now use streaming output naturally:
503
- ```typescript
504
- process.stdout.write("Loading");
505
- process.stdout.write(".");
506
- process.stdout.write(".");
507
- process.stdout.write(".\n");
508
- ```
509
-
510
- This will correctly display as `Loading...` on a single line in verbose mode.