@git.zone/tstest 1.9.1 → 1.9.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.plan.md CHANGED
@@ -2,6 +2,81 @@
2
2
 
3
3
  !! FIRST: Reread /home/philkunz/.claude/CLAUDE.md to ensure following all guidelines !!
4
4
 
5
+ ## Improved Internal Protocol (NEW - Critical)
6
+
7
+ ### Current Issues
8
+ - TAP protocol uses `#` for metadata which conflicts with test descriptions containing `#`
9
+ - Fragile regex parsing that breaks with special characters
10
+ - Limited extensibility for new metadata types
11
+
12
+ ### Proposed Solution: Protocol V2
13
+ - Use Unicode delimiters `⟦TSTEST:META:{}⟧` that won't appear in test names
14
+ - Structured JSON metadata format
15
+ - Separate protocol blocks for complex data (errors, snapshots)
16
+ - Backwards compatible with gradual migration
17
+
18
+ ### Implementation
19
+ - Phase 1: Add protocol v2 parser alongside v1
20
+ - Phase 2: Generate v2 by default with --legacy flag for v1
21
+ - Phase 3: Full migration to v2 in next major version
22
+
23
+ See `readme.protocol.md` for detailed specification.
24
+
25
+ ## Test Configuration System (NEW)
26
+
27
+ ### Global Test Configuration via 00init.ts
28
+ - **Discovery**: Check for `test/00init.ts` before running tests
29
+ - **Execution**: Import and execute before any test files if found
30
+ - **Purpose**: Define project-wide default test settings
31
+
32
+ ### tap.settings() API
33
+ ```typescript
34
+ interface TapSettings {
35
+ // Timing
36
+ timeout?: number; // Default timeout for all tests (ms)
37
+ slowThreshold?: number; // Mark tests as slow if they exceed this (ms)
38
+
39
+ // Execution Control
40
+ bail?: boolean; // Stop on first test failure
41
+ retries?: number; // Number of retries for failed tests
42
+ retryDelay?: number; // Delay between retries (ms)
43
+
44
+ // Output Control
45
+ suppressConsole?: boolean; // Suppress console output in passing tests
46
+ verboseErrors?: boolean; // Show full stack traces
47
+ showTestDuration?: boolean; // Show duration for each test
48
+
49
+ // Parallel Execution
50
+ maxConcurrency?: number; // Max parallel tests (for .para files)
51
+ isolateTests?: boolean; // Run each test in fresh context
52
+
53
+ // Lifecycle Hooks
54
+ beforeAll?: () => Promise<void> | void;
55
+ afterAll?: () => Promise<void> | void;
56
+ beforeEach?: (testName: string) => Promise<void> | void;
57
+ afterEach?: (testName: string, passed: boolean) => Promise<void> | void;
58
+
59
+ // Environment
60
+ env?: Record<string, string>; // Additional environment variables
61
+
62
+ // Features
63
+ enableSnapshots?: boolean; // Enable snapshot testing
64
+ snapshotDirectory?: string; // Custom snapshot directory
65
+ updateSnapshots?: boolean; // Update snapshots instead of comparing
66
+ }
67
+ ```
68
+
69
+ ### Settings Inheritance
70
+ - Global (00init.ts) → File level → Test level
71
+ - More specific settings override less specific ones
72
+ - Arrays/objects are merged, primitives are replaced
73
+
74
+ ### Implementation Phases
75
+ 1. **Core Infrastructure**: Settings storage and merge logic
76
+ 2. **Discovery**: 00init.ts loading mechanism
77
+ 3. **Application**: Apply settings to test execution
78
+ 4. **Advanced**: Parallel execution and snapshot configuration
79
+
5
80
  ## 1. Enhanced Communication Between tapbundle and tstest
6
81
 
7
82
  ### 1.1 Real-time Test Progress API
@@ -18,45 +93,9 @@
18
93
 
19
94
  ## 2. Enhanced toolsArg Functionality
20
95
 
21
- ### 2.1 Test Flow Control
22
- ```typescript
23
- tap.test('conditional test', async (toolsArg) => {
24
- const result = await someOperation();
25
-
26
- // Skip the rest of the test
27
- if (!result) {
28
- return toolsArg.skip('Precondition not met');
29
- }
30
-
31
- // Conditional skipping
32
- await toolsArg.skipIf(condition, 'Reason for skipping');
33
-
34
- // Mark test as todo
35
- await toolsArg.todo('Not implemented yet');
36
- });
37
- ```
38
-
39
- ### 2.2 Test Metadata and Configuration ✅
40
- ```typescript
41
- // Fluent syntax ✅
42
- tap.tags('slow', 'integration')
43
- .priority('high')
44
- .timeout(5000)
45
- .retry(3)
46
- .test('configurable test', async (toolsArg) => {
47
- // Test implementation
48
- });
49
- ```
50
-
51
- ### 2.3 Test Data and Context Sharing ✅
96
+ ### 2.3 Test Data and Context Sharing (Partial)
52
97
  ```typescript
53
98
  tap.test('data-driven test', async (toolsArg) => {
54
- // Access shared context ✅
55
- const sharedData = toolsArg.context.get('sharedData');
56
-
57
- // Set data for other tests ✅
58
- toolsArg.context.set('resultData', computedValue);
59
-
60
99
  // Parameterized test data (not yet implemented)
61
100
  const testData = toolsArg.data<TestInput>();
62
101
  expect(processData(testData)).toEqual(expected);
@@ -65,32 +104,7 @@ tap.test('data-driven test', async (toolsArg) => {
65
104
 
66
105
  ## 3. Nested Tests and Test Suites
67
106
 
68
- ### 3.1 Test Grouping with describe()
69
- ```typescript
70
- tap.describe('User Authentication', () => {
71
- tap.beforeEach(async (toolsArg) => {
72
- // Setup for each test in this suite
73
- await toolsArg.context.set('db', await createTestDatabase());
74
- });
75
-
76
- tap.afterEach(async (toolsArg) => {
77
- // Cleanup after each test
78
- await toolsArg.context.get('db').cleanup();
79
- });
80
-
81
- tap.test('should login with valid credentials', async (toolsArg) => {
82
- // Test implementation
83
- });
84
-
85
- tap.describe('Password Reset', () => {
86
- tap.test('should send reset email', async (toolsArg) => {
87
- // Nested test
88
- });
89
- });
90
- });
91
- ```
92
-
93
- ### 3.2 Hierarchical Test Organization
107
+ ### 3.2 Hierarchical Test Organization (Not yet implemented)
94
108
  - Support for multiple levels of nesting
95
109
  - Inherited context and configuration from parent suites
96
110
  - Aggregated reporting for test suites
@@ -98,15 +112,7 @@ tap.describe('User Authentication', () => {
98
112
 
99
113
  ## 4. Advanced Test Features
100
114
 
101
- ### 4.1 Snapshot Testing
102
- ```typescript
103
- tap.test('component render', async (toolsArg) => {
104
- const output = renderComponent(props);
105
-
106
- // Compare with stored snapshot
107
- await toolsArg.matchSnapshot(output, 'component-output');
108
- });
109
- ```
115
+ ### 4.1 Snapshot Testing ✅ (Basic implementation complete)
110
116
 
111
117
  ### 4.2 Performance Benchmarking
112
118
  ```typescript
@@ -124,30 +130,9 @@ tap.test('performance test', async (toolsArg) => {
124
130
  });
125
131
  ```
126
132
 
127
- ### 4.3 Test Fixtures and Factories ✅
128
- ```typescript
129
- tap.test('with fixtures', async (toolsArg) => {
130
- // Create test fixtures
131
- const user = await toolsArg.fixture('user', { name: 'Test User' });
132
- const post = await toolsArg.fixture('post', { author: user });
133
-
134
- // Use factory functions
135
- const users = await toolsArg.factory('user').createMany(5);
136
- });
137
- ```
138
133
 
139
134
  ## 5. Test Execution Improvements
140
135
 
141
- ### 5.1 Parallel Test Execution ✅
142
- - Run independent tests concurrently ✅
143
- - Configurable concurrency limits (via file naming convention)
144
- - Resource pooling for shared resources
145
- - Proper isolation between parallel tests ✅
146
-
147
- Implementation:
148
- - Tests with `para__<groupNumber>` in filename run in parallel
149
- - Different groups run sequentially
150
- - Tests without `para__` run serially
151
136
 
152
137
  ### 5.2 Watch Mode
153
138
  - Automatically re-run tests on file changes
@@ -155,11 +140,8 @@ Implementation:
155
140
  - Fast feedback loop for development
156
141
  - Integration with IDE/editor plugins
157
142
 
158
- ### 5.3 Advanced Test Filtering (partially)
143
+ ### 5.3 Advanced Test Filtering (Partial)
159
144
  ```typescript
160
- // Run tests by tags ✅
161
- tstest --tags "unit,fast"
162
-
163
145
  // Exclude tests by pattern (not yet implemented)
164
146
  tstest --exclude "**/slow/**"
165
147
 
@@ -198,50 +180,36 @@ tstest --changed
198
180
  - Links to documentation
199
181
  - Code examples in error output
200
182
 
201
- ### 7.2 Interactive Mode (Needs Detailed Specification)
202
- - REPL for exploring test failures
203
- - Need to define: How to enter interactive mode? When tests fail?
204
- - What commands/features should be available in the REPL?
205
- - Debugging integration
206
- - Node.js inspector protocol integration?
207
- - Breakpoint support?
208
- - Step-through test execution
209
- - Pause between tests?
210
- - Step into/over/out functionality?
211
- - Interactive test data manipulation
212
- - Modify test inputs on the fly?
213
- - Inspect intermediate values?
214
-
215
- ### 7.3 ~~VS Code Extension~~ (Scratched)
216
- - ~~Test explorer integration~~
217
- - ~~Inline test results~~
218
- - ~~CodeLens for running individual tests~~
219
- - ~~Debugging support~~
220
-
221
183
  ## Implementation Phases
222
184
 
223
- ### Phase 1: Core Enhancements (Priority: High)
224
- 1. Implement enhanced toolsArg methods (skip, skipIf, timeout, retry) ✅
225
- 2. Add basic test grouping with describe() ✅
226
- 3. Improve error reporting between tapbundle and tstest
185
+ ### Phase 1: Improved Internal Protocol (Priority: Critical) (NEW)
186
+ 1. Implement Protocol V2 parser in tstest
187
+ 2. Add protocol version negotiation
188
+ 3. Update tapbundle to generate V2 format with feature flag
189
+ 4. Test with real-world test suites containing special characters
190
+
191
+ ### Phase 2: Test Configuration System (Priority: High)
192
+ 1. Implement tap.settings() API with TypeScript interfaces
193
+ 2. Add 00init.ts discovery and loading mechanism
194
+ 3. Implement settings inheritance and merge logic
195
+ 4. Apply settings to test execution (timeouts, retries, etc.)
227
196
 
228
- ### Phase 2: Advanced Features (Priority: Medium)
229
- 1. Implement nested test suites (basic describe support)
230
- 2. Add snapshot testing
231
- 3. Create test fixture system
232
- 4. Implement parallel test execution ✅
197
+ ### Phase 3: Enhanced Communication (Priority: High)
198
+ 1. Build on Protocol V2 for richer communication
199
+ 2. Implement real-time test progress API
200
+ 3. Add structured error reporting with diffs and traces
233
201
 
234
- ### Phase 3: Developer Experience (Priority: Medium)
202
+ ### Phase 4: Developer Experience (Priority: Medium)
235
203
  1. Add watch mode
236
204
  2. Implement custom reporters
237
- 3. ~~Create VS Code extension~~ (Scratched)
238
- 4. Add interactive debugging (Needs detailed spec first)
205
+ 3. Complete advanced test filtering options
206
+ 4. Add performance benchmarking API
239
207
 
240
- ### Phase 4: Analytics and Performance (Priority: Low)
208
+ ### Phase 5: Analytics and Performance (Priority: Low)
241
209
  1. Build test analytics dashboard
242
- 2. Add performance benchmarking
243
- 3. Implement coverage integration
244
- 4. Create trend analysis tools
210
+ 2. Implement coverage integration
211
+ 3. Create trend analysis tools
212
+ 4. Add test impact analysis
245
213
 
246
214
  ## Technical Considerations
247
215
 
@@ -0,0 +1,287 @@
1
+ # Improved Internal Protocol Design
2
+
3
+ ## Current Issues with TAP Protocol
4
+
5
+ 1. **Delimiter Conflict**: Using `#` for metadata conflicts with test descriptions containing `#`
6
+ 2. **Ambiguous Parsing**: No clear boundary between test name and metadata
7
+ 3. **Limited Extensibility**: Adding new metadata requires regex changes
8
+ 4. **Mixed Concerns**: Protocol data mixed with human-readable output
9
+
10
+ ## Proposed Internal Protocol v2
11
+
12
+ ### Design Principles
13
+
14
+ 1. **Clear Separation**: Protocol data must be unambiguously separated from user content
15
+ 2. **Extensibility**: Easy to add new metadata without breaking parsers
16
+ 3. **Backwards Compatible**: Can coexist with standard TAP for gradual migration
17
+ 4. **Machine Readable**: Structured format for reliable parsing
18
+ 5. **Human Friendly**: Still readable in raw form
19
+
20
+ ### Protocol Options
21
+
22
+ #### Option 1: Special Delimiters
23
+ ```
24
+ ok 1 - test description ::TSTEST:: {"time":123,"retry":0}
25
+ not ok 2 - another test ::TSTEST:: {"time":45,"error":"timeout"}
26
+ ok 3 - skipped test ::TSTEST:: {"time":0,"skip":"not ready"}
27
+ ```
28
+
29
+ **Pros**:
30
+ - Simple to implement
31
+ - Backwards compatible with TAP parsers (they ignore the suffix)
32
+ - Easy to parse with split()
33
+
34
+ **Cons**:
35
+ - Still could conflict if test name contains `::TSTEST::`
36
+ - Not standard TAP
37
+
38
+ #### Option 2: Separate Metadata Lines
39
+ ```
40
+ ok 1 - test description
41
+ ::METADATA:: {"test":1,"time":123,"retry":0}
42
+ not ok 2 - another test
43
+ ::METADATA:: {"test":2,"time":45,"error":"timeout"}
44
+ ```
45
+
46
+ **Pros**:
47
+ - Complete separation of concerns
48
+ - No chance of conflicts
49
+ - Can include arbitrary metadata
50
+
51
+ **Cons**:
52
+ - Requires correlation between lines
53
+ - More complex parsing
54
+
55
+ #### Option 3: YAML Blocks (TAP 13 Compatible)
56
+ ```
57
+ ok 1 - test description
58
+ ---
59
+ time: 123
60
+ retry: 0
61
+ ...
62
+ not ok 2 - another test
63
+ ---
64
+ time: 45
65
+ error: timeout
66
+ stack: |
67
+ Error: timeout
68
+ at Test.run (test.js:10:5)
69
+ ...
70
+ ```
71
+
72
+ **Pros**:
73
+ - Standard TAP 13 feature
74
+ - Structured data format
75
+ - Human readable
76
+ - Extensible
77
+
78
+ **Cons**:
79
+ - More verbose
80
+ - YAML parsing overhead
81
+
82
+ #### Option 4: Binary Protocol Markers (Recommended)
83
+ ```
84
+ ok 1 - test description
85
+ ␛[TSTEST:eyJ0aW1lIjoxMjMsInJldHJ5IjowfQ==]␛
86
+ not ok 2 - another test
87
+ ␛[TSTEST:eyJ0aW1lIjo0NSwiZXJyb3IiOiJ0aW1lb3V0In0=]␛
88
+ ```
89
+
90
+ Using ASCII escape character (␛ = \x1B) with base64 encoded JSON.
91
+
92
+ **Pros**:
93
+ - Zero chance of accidental conflicts
94
+ - Compact
95
+ - Fast to parse
96
+ - Invisible in most terminals
97
+
98
+ **Cons**:
99
+ - Not human readable in raw form
100
+ - Requires base64 encoding/decoding
101
+
102
+ ### Recommended Implementation: Hybrid Approach
103
+
104
+ Use multiple strategies based on context:
105
+
106
+ 1. **For timing and basic metadata**: Use structured delimiters
107
+ ```
108
+ ok 1 - test name ⟦time:123,retry:0⟧
109
+ ```
110
+
111
+ 2. **For complex data (errors, snapshots)**: Use separate protocol lines
112
+ ```
113
+ ok 1 - test failed
114
+ ⟦TSTEST:ERROR⟧
115
+ {"message":"Assertion failed","stack":"...","diff":"..."}
116
+ ⟦/TSTEST:ERROR⟧
117
+ ```
118
+
119
+ 3. **For human-readable output**: Keep standard TAP comments
120
+ ```
121
+ # Test suite: User Authentication
122
+ ok 1 - should login
123
+ ```
124
+
125
+ ### Implementation Plan
126
+
127
+ #### Phase 1: Parser Enhancement
128
+ 1. Add new protocol parser alongside existing TAP parser
129
+ 2. Support both old and new formats during transition
130
+ 3. Add protocol version negotiation
131
+
132
+ #### Phase 2: Metadata Structure
133
+ ```typescript
134
+ interface TestMetadata {
135
+ // Timing
136
+ time: number; // milliseconds
137
+ startTime?: number; // Unix timestamp
138
+ endTime?: number; // Unix timestamp
139
+
140
+ // Status
141
+ skip?: string; // skip reason
142
+ todo?: string; // todo reason
143
+ retry?: number; // retry attempt
144
+ maxRetries?: number; // max retries allowed
145
+
146
+ // Error details
147
+ error?: {
148
+ message: string;
149
+ stack?: string;
150
+ diff?: string;
151
+ actual?: any;
152
+ expected?: any;
153
+ };
154
+
155
+ // Test context
156
+ file?: string; // source file
157
+ line?: number; // line number
158
+ column?: number; // column number
159
+
160
+ // Custom data
161
+ tags?: string[]; // test tags
162
+ custom?: Record<string, any>;
163
+ }
164
+ ```
165
+
166
+ #### Phase 3: Protocol Messages
167
+
168
+ ##### Success Message
169
+ ```
170
+ ok 1 - user authentication works
171
+ ⟦TSTEST:META:{"time":123,"tags":["auth","unit"]}⟧
172
+ ```
173
+
174
+ ##### Failure Message
175
+ ```
176
+ not ok 2 - login fails with invalid password
177
+ ⟦TSTEST:META:{"time":45,"retry":1,"maxRetries":3}⟧
178
+ ⟦TSTEST:ERROR⟧
179
+ {
180
+ "message": "Expected 401 but got 500",
181
+ "stack": "Error: Expected 401 but got 500\n at Test.run (auth.test.ts:25:10)",
182
+ "actual": 500,
183
+ "expected": 401
184
+ }
185
+ ⟦/TSTEST:ERROR⟧
186
+ ```
187
+
188
+ ##### Skip Message
189
+ ```
190
+ ok 3 - database integration test ⟦TSTEST:SKIP:No database connection⟧
191
+ ```
192
+
193
+ ##### Snapshot Communication
194
+ ```
195
+ ⟦TSTEST:SNAPSHOT:user-profile⟧
196
+ {
197
+ "name": "John Doe",
198
+ "email": "john@example.com",
199
+ "roles": ["user", "admin"]
200
+ }
201
+ ⟦/TSTEST:SNAPSHOT⟧
202
+ ```
203
+
204
+ ### Migration Strategy
205
+
206
+ 1. **Version Detection**: First line indicates protocol version
207
+ ```
208
+ ⟦TSTEST:PROTOCOL:2.0⟧
209
+ TAP version 13
210
+ ```
211
+
212
+ 2. **Gradual Rollout**:
213
+ - v1.10: Add protocol v2 parser, keep v1 generator
214
+ - v1.11: Generate v2 by default, v1 with --legacy flag
215
+ - v2.0: Remove v1 support
216
+
217
+ 3. **Feature Flags**:
218
+ ```typescript
219
+ tap.settings({
220
+ protocol: 'v2', // or 'v1', 'auto'
221
+ protocolFeatures: {
222
+ structuredErrors: true,
223
+ enhancedTiming: true,
224
+ binaryMarkers: false
225
+ }
226
+ });
227
+ ```
228
+
229
+ ### Benefits of New Protocol
230
+
231
+ 1. **Reliability**: No more regex fragility or description conflicts
232
+ 2. **Performance**: Faster parsing with clear boundaries
233
+ 3. **Extensibility**: Easy to add new metadata fields
234
+ 4. **Debugging**: Rich error information with stack traces and diffs
235
+ 5. **Integration**: Better IDE and CI/CD tool integration
236
+ 6. **Forward Compatible**: Room for future enhancements
237
+
238
+ ### Example Parser Implementation
239
+
240
+ ```typescript
241
+ class ProtocolV2Parser {
242
+ private readonly MARKER_START = '⟦TSTEST:';
243
+ private readonly MARKER_END = '⟧';
244
+
245
+ parseMetadata(line: string): TestMetadata | null {
246
+ const start = line.lastIndexOf(this.MARKER_START);
247
+ if (start === -1) return null;
248
+
249
+ const end = line.indexOf(this.MARKER_END, start);
250
+ if (end === -1) return null;
251
+
252
+ const content = line.substring(start + this.MARKER_START.length, end);
253
+ const [type, data] = content.split(':', 2);
254
+
255
+ switch (type) {
256
+ case 'META':
257
+ return JSON.parse(data);
258
+ case 'SKIP':
259
+ return { skip: data };
260
+ case 'TODO':
261
+ return { todo: data };
262
+ default:
263
+ return null;
264
+ }
265
+ }
266
+
267
+ parseTestLine(line: string): ParsedTest {
268
+ // First extract any metadata
269
+ const metadata = this.parseMetadata(line);
270
+
271
+ // Then parse the TAP part (without metadata)
272
+ const cleanLine = this.removeMetadata(line);
273
+ const tapResult = this.parseTAP(cleanLine);
274
+
275
+ return { ...tapResult, metadata };
276
+ }
277
+ }
278
+ ```
279
+
280
+ ### Next Steps
281
+
282
+ 1. Implement proof of concept with basic metadata support
283
+ 2. Test with real-world test suites for edge cases
284
+ 3. Benchmark parsing performance
285
+ 4. Get feedback from users
286
+ 5. Finalize protocol specification
287
+ 6. Implement in both tapbundle and tstest
@@ -3,6 +3,6 @@
3
3
  */
4
4
  export const commitinfo = {
5
5
  name: '@git.zone/tstest',
6
- version: '1.9.1',
6
+ version: '1.9.3',
7
7
  description: 'a test utility to run tests that match test/**/*.ts'
8
8
  }
@@ -16,7 +16,7 @@ export class TapParser {
16
16
  expectedTests: number;
17
17
  receivedTests: number;
18
18
 
19
- testStatusRegex = /(ok|not\sok)\s([0-9]+)\s-\s(.*)(\s#\s(.*))?$/;
19
+ testStatusRegex = /(ok|not\sok)\s([0-9]+)\s-\s(.*?)(\s#\s(.*))?$/;
20
20
  activeTapTestResult: TapTestResult;
21
21
  collectingErrorDetails: boolean = false;
22
22
  currentTestError: string[] = [];
@@ -77,7 +77,7 @@ export class TapParser {
77
77
  return false;
78
78
  })();
79
79
 
80
- const testSubject = regexResult[3];
80
+ const testSubject = regexResult[3].trim();
81
81
  const testMetadata = regexResult[5]; // This will be either "time=XXXms" or "SKIP reason" or "TODO reason"
82
82
 
83
83
  let testDuration = 0;
@@ -153,8 +153,16 @@ export class TsTestLogger {
153
153
 
154
154
  // Only set up test log file if --logfile option is specified
155
155
  if (this.options.logFile) {
156
- const baseFilename = path.basename(filename, '.ts');
157
- this.currentTestLogFile = path.join('.nogit', 'testlogs', `${baseFilename}.log`);
156
+ // Create a safe filename that preserves directory structure
157
+ // Convert relative path to a flat filename by replacing separators with __
158
+ const relativeFilename = path.relative(process.cwd(), filename);
159
+ const safeFilename = relativeFilename
160
+ .replace(/\\/g, '/') // Normalize Windows paths
161
+ .replace(/\//g, '__') // Replace path separators with double underscores
162
+ .replace(/\.ts$/, '') // Remove .ts extension
163
+ .replace(/^\.\.__|^\.__|^__/, ''); // Clean up leading separators from relative paths
164
+
165
+ this.currentTestLogFile = path.join('.nogit', 'testlogs', `${safeFilename}.log`);
158
166
 
159
167
  // Ensure the directory exists
160
168
  const logDir = path.dirname(this.currentTestLogFile);