taist 0.1.0 → 0.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.md +224 -0
- package/instrument.js +33 -4
- package/lib/vitest-reporter.js +309 -0
- package/package.json +24 -5
- package/types/index.d.ts +41 -0
- package/types/instrument.d.ts +225 -0
- package/types/taist.d.ts +288 -0
- package/types/testing.d.ts +98 -0
- package/types/toon-formatter.d.ts +148 -0
- package/types/trace-collector.d.ts +128 -0
- package/types/vitest-reporter.d.ts +101 -0
package/README.md
CHANGED
|
@@ -13,6 +13,7 @@ Version: 0.1.0 | January 2025 | [Technical Specification](./SPEC.md)
|
|
|
13
13
|
5. [Test Integration](#test-integration)
|
|
14
14
|
6. [Configuration Reference](#configuration-reference)
|
|
15
15
|
7. [Usage Examples](#usage-examples)
|
|
16
|
+
8. [TypeScript Support](#typescript-support)
|
|
16
17
|
|
|
17
18
|
---
|
|
18
19
|
|
|
@@ -236,6 +237,63 @@ const tracedFn = tracer.wrapMethod(myFunction, 'myFunction');
|
|
|
236
237
|
- Custom trace collection logic
|
|
237
238
|
- Maximum flexibility needed
|
|
238
239
|
|
|
240
|
+
### Side-Effect Import vs Direct Function Calls
|
|
241
|
+
|
|
242
|
+
The `import 'taist/instrument'` side-effect import is **optional**. It provides convenience features but is not required for basic instrumentation.
|
|
243
|
+
|
|
244
|
+
#### What the Side-Effect Import Does
|
|
245
|
+
|
|
246
|
+
When you add `import 'taist/instrument'` to your entry point, it:
|
|
247
|
+
|
|
248
|
+
1. **Creates a global ServiceTracer** - Initialized from environment variables
|
|
249
|
+
2. **Sets up signal handlers** - Outputs trace summary on SIGINT/SIGTERM
|
|
250
|
+
3. **Configures periodic output** - Writes trace summaries at intervals
|
|
251
|
+
4. **Exports a pre-configured `tracer`** - Ready for immediate use
|
|
252
|
+
|
|
253
|
+
#### When to Use the Side-Effect Import
|
|
254
|
+
|
|
255
|
+
**Use `import 'taist/instrument'`** when:
|
|
256
|
+
- You want automatic trace output on process shutdown
|
|
257
|
+
- You're instrumenting at application startup
|
|
258
|
+
- You want environment-variable-based configuration
|
|
259
|
+
- You need the global tracer instance
|
|
260
|
+
|
|
261
|
+
```javascript
|
|
262
|
+
// Entry point - loads global tracer and signal handlers
|
|
263
|
+
import 'taist/instrument';
|
|
264
|
+
import { instrumentExpress, instrumentService } from 'taist/instrument';
|
|
265
|
+
```
|
|
266
|
+
|
|
267
|
+
#### When to Skip the Side-Effect Import
|
|
268
|
+
|
|
269
|
+
**Skip the side-effect import** when:
|
|
270
|
+
- You're instrumenting post-startup (e.g., in tests)
|
|
271
|
+
- You want more control over tracer lifecycle
|
|
272
|
+
- You don't want signal handlers registered
|
|
273
|
+
- You're creating multiple independent tracers
|
|
274
|
+
|
|
275
|
+
```javascript
|
|
276
|
+
// Direct function imports - no side effects
|
|
277
|
+
import { instrumentExpress, instrumentService } from 'taist/instrument';
|
|
278
|
+
|
|
279
|
+
// Or use the programmatic API for full control
|
|
280
|
+
import { ServiceTracer } from 'taist';
|
|
281
|
+
const tracer = new ServiceTracer({ enabled: true });
|
|
282
|
+
```
|
|
283
|
+
|
|
284
|
+
#### Post-Startup Instrumentation
|
|
285
|
+
|
|
286
|
+
The instrumentation functions (`instrumentExpress`, `instrumentService`, etc.) work independently of the side-effect import. You can instrument services after your application has started:
|
|
287
|
+
|
|
288
|
+
```javascript
|
|
289
|
+
// No side-effect import needed
|
|
290
|
+
import { instrumentService } from 'taist/instrument';
|
|
291
|
+
|
|
292
|
+
// Instrument a service created dynamically
|
|
293
|
+
const service = new DynamicService();
|
|
294
|
+
const traced = instrumentService(service, 'DynamicService');
|
|
295
|
+
```
|
|
296
|
+
|
|
239
297
|
---
|
|
240
298
|
|
|
241
299
|
## Test Integration
|
|
@@ -310,6 +368,139 @@ session.printTraces({
|
|
|
310
368
|
});
|
|
311
369
|
```
|
|
312
370
|
|
|
371
|
+
### Vitest Reporter Plugin
|
|
372
|
+
|
|
373
|
+
The taist Vitest reporter replaces Vitest's default output with TOON format and adds execution tracing. When you run `vitest`, you get:
|
|
374
|
+
- Token-optimized test results (90% fewer tokens than default output)
|
|
375
|
+
- Automatic execution traces from instrumented code
|
|
376
|
+
- Call hierarchy with timing, arguments, and return values
|
|
377
|
+
|
|
378
|
+
#### Step 1: Configure vitest.config.js
|
|
379
|
+
|
|
380
|
+
```javascript
|
|
381
|
+
// vitest.config.js
|
|
382
|
+
import { defineConfig } from 'vitest/config';
|
|
383
|
+
|
|
384
|
+
export default defineConfig({
|
|
385
|
+
test: {
|
|
386
|
+
reporters: ['taist/vitest-reporter']
|
|
387
|
+
}
|
|
388
|
+
});
|
|
389
|
+
```
|
|
390
|
+
|
|
391
|
+
#### Step 2: Instrument Your Code
|
|
392
|
+
|
|
393
|
+
In your test file or setup file, instrument the services you want to trace:
|
|
394
|
+
|
|
395
|
+
```javascript
|
|
396
|
+
// test/my-service.test.js
|
|
397
|
+
import { describe, it, expect } from 'vitest';
|
|
398
|
+
import { instrumentService } from 'taist/instrument';
|
|
399
|
+
import { UserService } from '../src/user-service.js';
|
|
400
|
+
|
|
401
|
+
// Wrap the service with instrumentation
|
|
402
|
+
const userService = instrumentService(new UserService(), 'UserService');
|
|
403
|
+
|
|
404
|
+
describe('UserService', () => {
|
|
405
|
+
it('should create a user', async () => {
|
|
406
|
+
// Calls to userService methods are now traced
|
|
407
|
+
const user = await userService.create({ name: 'Alice' });
|
|
408
|
+
expect(user.id).toBeDefined();
|
|
409
|
+
});
|
|
410
|
+
});
|
|
411
|
+
```
|
|
412
|
+
|
|
413
|
+
#### Step 3: Run Tests
|
|
414
|
+
|
|
415
|
+
```bash
|
|
416
|
+
vitest run
|
|
417
|
+
```
|
|
418
|
+
|
|
419
|
+
#### Example Output
|
|
420
|
+
|
|
421
|
+
```
|
|
422
|
+
===TESTS: 5/5===
|
|
423
|
+
|
|
424
|
+
============================================================
|
|
425
|
+
TRACE OUTPUT
|
|
426
|
+
============================================================
|
|
427
|
+
Traces: 12 | Requests: 5
|
|
428
|
+
|
|
429
|
+
--- UserService.create ---
|
|
430
|
+
fn:UserService.create depth:0 45ms
|
|
431
|
+
fn:UserService.validate depth:1 5ms
|
|
432
|
+
fn:UserService.hashPassword depth:1 30ms
|
|
433
|
+
fn:UserService.save depth:1 10ms
|
|
434
|
+
|
|
435
|
+
--- UserService.getById ---
|
|
436
|
+
fn:UserService.getById depth:0 8ms
|
|
437
|
+
fn:Cache.get depth:1 2ms
|
|
438
|
+
```
|
|
439
|
+
|
|
440
|
+
#### Reporter Options
|
|
441
|
+
|
|
442
|
+
```javascript
|
|
443
|
+
export default defineConfig({
|
|
444
|
+
test: {
|
|
445
|
+
reporters: [['taist/vitest-reporter', {
|
|
446
|
+
format: 'toon', // Output format: 'toon' | 'json' | 'compact'
|
|
447
|
+
traceEnabled: true, // Start trace collector (default: true)
|
|
448
|
+
traceDepth: 3, // Max depth to trace (default: 3)
|
|
449
|
+
showTrace: true, // Include trace output (default: true)
|
|
450
|
+
maxTraceGroups: 10, // Max trace groups to show (default: 10)
|
|
451
|
+
silent: false, // Suppress all output (default: false)
|
|
452
|
+
outputFile: null // Write to file instead of stdout
|
|
453
|
+
}]]
|
|
454
|
+
}
|
|
455
|
+
});
|
|
456
|
+
```
|
|
457
|
+
|
|
458
|
+
#### How Trace Collection Works
|
|
459
|
+
|
|
460
|
+
1. **Reporter starts** → Creates a TraceCollector (Unix socket server)
|
|
461
|
+
2. **Environment set** → Sets `TAIST_ENABLED=true` and `TAIST_COLLECTOR_SOCKET=/tmp/taist-collector-xxx.sock`
|
|
462
|
+
3. **Tests run** → Instrumented code sends traces to the collector via the socket
|
|
463
|
+
4. **Tests finish** → Reporter collects traces and outputs them with test results
|
|
464
|
+
|
|
465
|
+
**Note:** Traces are collected from code instrumented with `instrumentService()` or `instrumentExpress()`. Code that isn't instrumented won't appear in the trace output.
|
|
466
|
+
|
|
467
|
+
#### Using with Test Setup Files
|
|
468
|
+
|
|
469
|
+
For larger projects, instrument services in a setup file:
|
|
470
|
+
|
|
471
|
+
```javascript
|
|
472
|
+
// test/setup.js
|
|
473
|
+
import { instrumentService } from 'taist/instrument';
|
|
474
|
+
import { UserService } from '../src/services/user-service.js';
|
|
475
|
+
import { OrderService } from '../src/services/order-service.js';
|
|
476
|
+
|
|
477
|
+
// Export instrumented services for use in tests
|
|
478
|
+
export const userService = instrumentService(new UserService(), 'UserService');
|
|
479
|
+
export const orderService = instrumentService(new OrderService(), 'OrderService');
|
|
480
|
+
```
|
|
481
|
+
|
|
482
|
+
```javascript
|
|
483
|
+
// vitest.config.js
|
|
484
|
+
export default defineConfig({
|
|
485
|
+
test: {
|
|
486
|
+
reporters: ['taist/vitest-reporter'],
|
|
487
|
+
setupFiles: ['./test/setup.js']
|
|
488
|
+
}
|
|
489
|
+
});
|
|
490
|
+
```
|
|
491
|
+
|
|
492
|
+
```javascript
|
|
493
|
+
// test/user.test.js
|
|
494
|
+
import { userService } from './setup.js';
|
|
495
|
+
|
|
496
|
+
describe('UserService', () => {
|
|
497
|
+
it('should get user by id', async () => {
|
|
498
|
+
const user = await userService.getById(123);
|
|
499
|
+
expect(user).toBeDefined();
|
|
500
|
+
});
|
|
501
|
+
});
|
|
502
|
+
```
|
|
503
|
+
|
|
313
504
|
### Example Output
|
|
314
505
|
|
|
315
506
|
When tests complete, you'll see the execution tree grouped by HTTP request:
|
|
@@ -461,6 +652,39 @@ npm install --save-dev taist
|
|
|
461
652
|
|
|
462
653
|
---
|
|
463
654
|
|
|
655
|
+
## TypeScript Support
|
|
656
|
+
|
|
657
|
+
Taist includes TypeScript type definitions out of the box. No additional `@types` packages needed.
|
|
658
|
+
|
|
659
|
+
```typescript
|
|
660
|
+
import { Taist, TestResults } from 'taist';
|
|
661
|
+
import { instrumentExpress, instrumentService } from 'taist/instrument';
|
|
662
|
+
import { TraceSession, TraceCollector } from 'taist/testing';
|
|
663
|
+
|
|
664
|
+
// Full type safety
|
|
665
|
+
const taist = new Taist({ format: 'toon', depth: 3 });
|
|
666
|
+
const results: TestResults = await taist.run();
|
|
667
|
+
|
|
668
|
+
// Typed instrumentation
|
|
669
|
+
import express from 'express';
|
|
670
|
+
const app = express();
|
|
671
|
+
instrumentExpress(app);
|
|
672
|
+
|
|
673
|
+
class MyService {
|
|
674
|
+
getData(): string { return 'data'; }
|
|
675
|
+
}
|
|
676
|
+
const service = instrumentService(new MyService(), 'MyService');
|
|
677
|
+
```
|
|
678
|
+
|
|
679
|
+
Types are available for all exports:
|
|
680
|
+
- `taist` - Main API (Taist class, TestResults, etc.)
|
|
681
|
+
- `taist/instrument` - Instrumentation functions
|
|
682
|
+
- `taist/testing` - Test utilities (TraceSession, TraceCollector)
|
|
683
|
+
- `taist/vitest-reporter` - Vitest reporter
|
|
684
|
+
- `taist/types` - All types re-exported
|
|
685
|
+
|
|
686
|
+
---
|
|
687
|
+
|
|
464
688
|
## License
|
|
465
689
|
|
|
466
690
|
MIT License - Open source and free for commercial use
|
package/instrument.js
CHANGED
|
@@ -1,11 +1,40 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Taist Auto-Instrumentation Module
|
|
3
3
|
*
|
|
4
|
-
*
|
|
5
|
-
* import 'taist/instrument';
|
|
4
|
+
* This module can be used in two ways:
|
|
6
5
|
*
|
|
7
|
-
*
|
|
8
|
-
*
|
|
6
|
+
* 1. SIDE-EFFECT IMPORT (optional):
|
|
7
|
+
* Add `import 'taist/instrument';` at the top of your entry point.
|
|
8
|
+
* This initializes a global tracer, sets up signal handlers for graceful
|
|
9
|
+
* shutdown, and configures periodic trace output based on environment variables.
|
|
10
|
+
*
|
|
11
|
+
* @example
|
|
12
|
+
* // Entry point - loads global tracer and signal handlers
|
|
13
|
+
* import 'taist/instrument';
|
|
14
|
+
* import { instrumentExpress } from 'taist/instrument';
|
|
15
|
+
*
|
|
16
|
+
* 2. DIRECT FUNCTION IMPORTS (no side effects):
|
|
17
|
+
* Import only the functions you need:
|
|
18
|
+
* `import { instrumentExpress, instrumentService } from 'taist/instrument';`
|
|
19
|
+
* This is useful for post-startup instrumentation or when you want more
|
|
20
|
+
* control over the tracer lifecycle.
|
|
21
|
+
*
|
|
22
|
+
* @example
|
|
23
|
+
* // Direct imports for post-startup instrumentation
|
|
24
|
+
* import { instrumentService } from 'taist/instrument';
|
|
25
|
+
* const traced = instrumentService(myService, 'MyService');
|
|
26
|
+
*
|
|
27
|
+
* Environment Variables (used by side-effect import):
|
|
28
|
+
* - TAIST_ENABLED: Enable/disable tracing (default: true)
|
|
29
|
+
* - TAIST_DEPTH: Trace depth level (default: 3)
|
|
30
|
+
* - TAIST_FORMAT: Output format: toon, json, compact (default: toon)
|
|
31
|
+
* - TAIST_OUTPUT_FILE: Write traces to file instead of stdout
|
|
32
|
+
* - TAIST_OUTPUT_INTERVAL: Output interval in ms (default: 30000)
|
|
33
|
+
* - TAIST_INCLUDE: Only trace modules matching patterns (comma-separated)
|
|
34
|
+
* - TAIST_EXCLUDE: Skip modules matching patterns
|
|
35
|
+
* - TAIST_SLOW_THRESHOLD: Slow operation threshold in ms (default: 100)
|
|
36
|
+
*
|
|
37
|
+
* @module taist/instrument
|
|
9
38
|
*/
|
|
10
39
|
|
|
11
40
|
import { ServiceTracer, autoInstrument } from './lib/service-tracer.js';
|
|
@@ -0,0 +1,309 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Vitest TOON Reporter
|
|
3
|
+
*
|
|
4
|
+
* A native Vitest reporter that outputs test results in TOON format
|
|
5
|
+
* with integrated trace collection for execution visibility.
|
|
6
|
+
*
|
|
7
|
+
* The reporter automatically:
|
|
8
|
+
* - Starts a TraceCollector to receive traces from instrumented code
|
|
9
|
+
* - Sets environment variables so instrumented code can connect
|
|
10
|
+
* - Collects and outputs execution traces alongside test results
|
|
11
|
+
*
|
|
12
|
+
* Usage in vitest.config.js:
|
|
13
|
+
* reporters: ['taist/vitest-reporter']
|
|
14
|
+
*
|
|
15
|
+
* With options:
|
|
16
|
+
* reporters: [['taist/vitest-reporter', { format: 'toon', traceDepth: 3 }]]
|
|
17
|
+
*
|
|
18
|
+
* Your test setup should instrument code that will be tested:
|
|
19
|
+
* import { instrumentService } from 'taist/instrument';
|
|
20
|
+
* const service = instrumentService(new MyService(), 'MyService');
|
|
21
|
+
*/
|
|
22
|
+
|
|
23
|
+
import { ToonFormatter } from './toon-formatter.js';
|
|
24
|
+
import { TraceCollector } from './trace-collector.js';
|
|
25
|
+
import fs from 'fs';
|
|
26
|
+
|
|
27
|
+
/**
|
|
28
|
+
* @typedef {Object} TaistReporterOptions
|
|
29
|
+
* @property {'toon' | 'json' | 'compact'} [format='toon'] - Output format
|
|
30
|
+
* @property {boolean} [traceEnabled=true] - Enable execution tracing
|
|
31
|
+
* @property {number} [traceDepth=3] - Trace depth level
|
|
32
|
+
* @property {boolean} [showTrace=true] - Include traces in output
|
|
33
|
+
* @property {boolean} [silent=false] - Suppress output
|
|
34
|
+
* @property {string | null} [outputFile=null] - Write to file instead of stdout
|
|
35
|
+
* @property {number} [maxTraceGroups=10] - Max request groups to show in trace output
|
|
36
|
+
*/
|
|
37
|
+
|
|
38
|
+
export class TaistReporter {
|
|
39
|
+
/**
|
|
40
|
+
* @param {TaistReporterOptions} options
|
|
41
|
+
*/
|
|
42
|
+
constructor(options = {}) {
|
|
43
|
+
this.options = {
|
|
44
|
+
format: options.format || 'toon',
|
|
45
|
+
traceEnabled: options.traceEnabled !== false,
|
|
46
|
+
traceDepth: options.traceDepth || 3,
|
|
47
|
+
showTrace: options.showTrace !== false,
|
|
48
|
+
silent: options.silent || false,
|
|
49
|
+
outputFile: options.outputFile || null,
|
|
50
|
+
maxTraceGroups: options.maxTraceGroups || 10,
|
|
51
|
+
...options
|
|
52
|
+
};
|
|
53
|
+
|
|
54
|
+
this.formatter = new ToonFormatter(this.options);
|
|
55
|
+
this.vitest = null;
|
|
56
|
+
this.startTime = 0;
|
|
57
|
+
this.collector = null;
|
|
58
|
+
this.collectorReady = null;
|
|
59
|
+
this.taskResults = new Map(); // Map task id to result
|
|
60
|
+
this.results = {
|
|
61
|
+
stats: { total: 0, passed: 0, failed: 0, skipped: 0 },
|
|
62
|
+
failures: [],
|
|
63
|
+
duration: 0,
|
|
64
|
+
trace: []
|
|
65
|
+
};
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
/**
|
|
69
|
+
* Called when Vitest is initialized
|
|
70
|
+
* @param {import('vitest/node').Vitest} vitest
|
|
71
|
+
*/
|
|
72
|
+
onInit(vitest) {
|
|
73
|
+
this.vitest = vitest;
|
|
74
|
+
this.startTime = performance.now();
|
|
75
|
+
|
|
76
|
+
// Start trace collector if tracing is enabled
|
|
77
|
+
if (this.options.traceEnabled) {
|
|
78
|
+
this.collector = new TraceCollector({
|
|
79
|
+
maxTraces: 10000
|
|
80
|
+
});
|
|
81
|
+
|
|
82
|
+
// Start collector and store the promise
|
|
83
|
+
this.collectorReady = this.collector.start().then(() => {
|
|
84
|
+
// Set environment variables so instrumented code can connect
|
|
85
|
+
process.env.TAIST_ENABLED = 'true';
|
|
86
|
+
process.env.TAIST_DEPTH = String(this.options.traceDepth);
|
|
87
|
+
process.env.TAIST_COLLECTOR_SOCKET = this.collector.getSocketPath();
|
|
88
|
+
}).catch(err => {
|
|
89
|
+
console.error('[taist] Failed to start trace collector:', err.message);
|
|
90
|
+
this.collector = null;
|
|
91
|
+
});
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
/**
|
|
96
|
+
* Called when task results are updated (Vitest 2.x)
|
|
97
|
+
* @param {Array} packs - Array of [taskId, result, meta]
|
|
98
|
+
*/
|
|
99
|
+
onTaskUpdate(packs) {
|
|
100
|
+
if (!packs) return;
|
|
101
|
+
|
|
102
|
+
for (const pack of packs) {
|
|
103
|
+
const [taskId, result] = pack;
|
|
104
|
+
if (result) {
|
|
105
|
+
this.taskResults.set(taskId, result);
|
|
106
|
+
}
|
|
107
|
+
}
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
/**
|
|
111
|
+
* Called when test run finishes (Vitest 2.x)
|
|
112
|
+
* @param {Array} files - Test files with results
|
|
113
|
+
* @param {Array} errors - Unhandled errors
|
|
114
|
+
*/
|
|
115
|
+
async onFinished(files, errors) {
|
|
116
|
+
this.results.duration = performance.now() - this.startTime;
|
|
117
|
+
|
|
118
|
+
// Reset stats
|
|
119
|
+
this.results.stats = { total: 0, passed: 0, failed: 0, skipped: 0 };
|
|
120
|
+
this.results.failures = [];
|
|
121
|
+
|
|
122
|
+
// Process all test files
|
|
123
|
+
if (files) {
|
|
124
|
+
for (const file of files) {
|
|
125
|
+
this._processFile(file);
|
|
126
|
+
}
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
// Add unhandled errors as failures
|
|
130
|
+
if (errors) {
|
|
131
|
+
for (const error of errors) {
|
|
132
|
+
this.results.failures.push({
|
|
133
|
+
test: 'Unhandled Error',
|
|
134
|
+
error: error.message || String(error),
|
|
135
|
+
stack: error.stack
|
|
136
|
+
});
|
|
137
|
+
this.results.stats.total++;
|
|
138
|
+
this.results.stats.failed++;
|
|
139
|
+
}
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
// Collect traces if enabled
|
|
143
|
+
if (this.options.traceEnabled && this.collector) {
|
|
144
|
+
// Wait for collector to be ready
|
|
145
|
+
if (this.collectorReady) {
|
|
146
|
+
await this.collectorReady;
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
// Give a small delay for any final traces to arrive
|
|
150
|
+
await new Promise(resolve => setTimeout(resolve, 100));
|
|
151
|
+
|
|
152
|
+
// Get collected traces
|
|
153
|
+
if (this.options.showTrace) {
|
|
154
|
+
this.results.trace = this.collector.getTraces();
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
// Stop the collector
|
|
158
|
+
await this.collector.stop();
|
|
159
|
+
this.collector = null;
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
// Output results
|
|
163
|
+
this._outputResults();
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
/**
|
|
167
|
+
* Process a test file and its tasks recursively
|
|
168
|
+
* @private
|
|
169
|
+
*/
|
|
170
|
+
_processFile(file) {
|
|
171
|
+
if (file.tasks) {
|
|
172
|
+
for (const task of file.tasks) {
|
|
173
|
+
this._processTask(task, file);
|
|
174
|
+
}
|
|
175
|
+
}
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
/**
|
|
179
|
+
* Process a task (test or suite) recursively
|
|
180
|
+
* @private
|
|
181
|
+
*/
|
|
182
|
+
_processTask(task, file) {
|
|
183
|
+
if (task.type === 'test') {
|
|
184
|
+
this.results.stats.total++;
|
|
185
|
+
|
|
186
|
+
const state = task.result?.state;
|
|
187
|
+
if (state === 'pass') {
|
|
188
|
+
this.results.stats.passed++;
|
|
189
|
+
} else if (state === 'fail') {
|
|
190
|
+
this.results.stats.failed++;
|
|
191
|
+
this.results.failures.push(this._formatFailure(task, file));
|
|
192
|
+
} else if (state === 'skip') {
|
|
193
|
+
this.results.stats.skipped++;
|
|
194
|
+
}
|
|
195
|
+
} else if (task.type === 'suite' && task.tasks) {
|
|
196
|
+
// Process nested tasks
|
|
197
|
+
for (const subtask of task.tasks) {
|
|
198
|
+
this._processTask(subtask, file);
|
|
199
|
+
}
|
|
200
|
+
}
|
|
201
|
+
}
|
|
202
|
+
|
|
203
|
+
/**
|
|
204
|
+
* Format a test failure for TOON output
|
|
205
|
+
* @private
|
|
206
|
+
*/
|
|
207
|
+
_formatFailure(task, file) {
|
|
208
|
+
const failure = {
|
|
209
|
+
test: this._getTestName(task),
|
|
210
|
+
location: this._getLocation(task, file)
|
|
211
|
+
};
|
|
212
|
+
|
|
213
|
+
const error = task.result?.errors?.[0];
|
|
214
|
+
if (error) {
|
|
215
|
+
failure.error = error.message || String(error);
|
|
216
|
+
failure.stack = error.stack;
|
|
217
|
+
|
|
218
|
+
// Extract diff if available
|
|
219
|
+
if (error.expected !== undefined || error.actual !== undefined) {
|
|
220
|
+
failure.diff = {
|
|
221
|
+
expected: error.expected,
|
|
222
|
+
actual: error.actual
|
|
223
|
+
};
|
|
224
|
+
}
|
|
225
|
+
}
|
|
226
|
+
|
|
227
|
+
return failure;
|
|
228
|
+
}
|
|
229
|
+
|
|
230
|
+
/**
|
|
231
|
+
* Get full test name including suite hierarchy
|
|
232
|
+
* @private
|
|
233
|
+
*/
|
|
234
|
+
_getTestName(task) {
|
|
235
|
+
const names = [];
|
|
236
|
+
let current = task;
|
|
237
|
+
|
|
238
|
+
while (current) {
|
|
239
|
+
if (current.name && current.type !== 'file') {
|
|
240
|
+
names.unshift(current.name);
|
|
241
|
+
}
|
|
242
|
+
current = current.suite;
|
|
243
|
+
}
|
|
244
|
+
|
|
245
|
+
return names.join(' > ') || task.name;
|
|
246
|
+
}
|
|
247
|
+
|
|
248
|
+
/**
|
|
249
|
+
* Get test location
|
|
250
|
+
* @private
|
|
251
|
+
*/
|
|
252
|
+
_getLocation(task, file) {
|
|
253
|
+
if (task.location) {
|
|
254
|
+
return {
|
|
255
|
+
file: file?.filepath || file?.name || '',
|
|
256
|
+
line: task.location.line,
|
|
257
|
+
column: task.location.column
|
|
258
|
+
};
|
|
259
|
+
}
|
|
260
|
+
return file?.filepath || file?.name || '';
|
|
261
|
+
}
|
|
262
|
+
|
|
263
|
+
/**
|
|
264
|
+
* Output formatted results
|
|
265
|
+
* @private
|
|
266
|
+
*/
|
|
267
|
+
_outputResults() {
|
|
268
|
+
if (this.options.silent) {
|
|
269
|
+
return;
|
|
270
|
+
}
|
|
271
|
+
|
|
272
|
+
// Format test results
|
|
273
|
+
let output = this.formatter.format(this.results);
|
|
274
|
+
|
|
275
|
+
// Add trace tree if we have traces
|
|
276
|
+
if (this.options.showTrace && this.results.trace && this.results.trace.length > 0) {
|
|
277
|
+
output += '\n\n';
|
|
278
|
+
output += this.formatter.formatTraceTree(this.results.trace, {
|
|
279
|
+
maxGroups: this.options.maxTraceGroups,
|
|
280
|
+
showHeader: true
|
|
281
|
+
});
|
|
282
|
+
}
|
|
283
|
+
|
|
284
|
+
if (this.options.outputFile) {
|
|
285
|
+
fs.writeFileSync(this.options.outputFile, output);
|
|
286
|
+
} else {
|
|
287
|
+
console.log(output);
|
|
288
|
+
}
|
|
289
|
+
}
|
|
290
|
+
|
|
291
|
+
/**
|
|
292
|
+
* Get the collected results (for programmatic access)
|
|
293
|
+
* @returns {Object} Test results
|
|
294
|
+
*/
|
|
295
|
+
getResults() {
|
|
296
|
+
return this.results;
|
|
297
|
+
}
|
|
298
|
+
|
|
299
|
+
/**
|
|
300
|
+
* Get the trace collector socket path (for manual instrumentation setup)
|
|
301
|
+
* @returns {string|null} Socket path or null if collector not running
|
|
302
|
+
*/
|
|
303
|
+
getSocketPath() {
|
|
304
|
+
return this.collector?.getSocketPath() || null;
|
|
305
|
+
}
|
|
306
|
+
}
|
|
307
|
+
|
|
308
|
+
// Default export for Vitest reporter configuration
|
|
309
|
+
export default TaistReporter;
|
package/package.json
CHANGED
|
@@ -1,14 +1,16 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "taist",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.1",
|
|
4
4
|
"description": "Token-Optimized Testing Framework for AI-Assisted Development",
|
|
5
5
|
"main": "index.js",
|
|
6
6
|
"type": "module",
|
|
7
7
|
"bin": {
|
|
8
8
|
"taist": "./taist.js"
|
|
9
9
|
},
|
|
10
|
+
"types": "./types/index.d.ts",
|
|
10
11
|
"files": [
|
|
11
12
|
"lib/",
|
|
13
|
+
"types/",
|
|
12
14
|
"index.js",
|
|
13
15
|
"taist.js",
|
|
14
16
|
"instrument.js",
|
|
@@ -18,11 +20,28 @@
|
|
|
18
20
|
".taistrc.json"
|
|
19
21
|
],
|
|
20
22
|
"exports": {
|
|
21
|
-
".":
|
|
22
|
-
|
|
23
|
-
|
|
23
|
+
".": {
|
|
24
|
+
"types": "./types/index.d.ts",
|
|
25
|
+
"default": "./index.js"
|
|
26
|
+
},
|
|
27
|
+
"./instrument": {
|
|
28
|
+
"types": "./types/instrument.d.ts",
|
|
29
|
+
"default": "./instrument.js"
|
|
30
|
+
},
|
|
31
|
+
"./testing": {
|
|
32
|
+
"types": "./types/testing.d.ts",
|
|
33
|
+
"default": "./testing.js"
|
|
34
|
+
},
|
|
35
|
+
"./vitest-reporter": {
|
|
36
|
+
"types": "./types/vitest-reporter.d.ts",
|
|
37
|
+
"default": "./lib/vitest-reporter.js"
|
|
38
|
+
},
|
|
39
|
+
"./types": "./types/index.d.ts",
|
|
24
40
|
"./module-patcher": "./lib/module-patcher.js",
|
|
25
|
-
"./trace-collector":
|
|
41
|
+
"./trace-collector": {
|
|
42
|
+
"types": "./types/trace-collector.d.ts",
|
|
43
|
+
"default": "./lib/trace-collector.js"
|
|
44
|
+
},
|
|
26
45
|
"./trace-reporter": "./lib/trace-reporter.js",
|
|
27
46
|
"./trace-context": "./lib/trace-context.js",
|
|
28
47
|
"./instrument-all": "./lib/instrument-all.js",
|
package/types/index.d.ts
ADDED
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Taist - Token-Optimized Testing Framework
|
|
3
|
+
* Main type definitions
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
// Main entry point exports
|
|
7
|
+
export {
|
|
8
|
+
Taist,
|
|
9
|
+
TaistOptions,
|
|
10
|
+
RunConfig,
|
|
11
|
+
WatchConfig,
|
|
12
|
+
TestResults,
|
|
13
|
+
TestStats,
|
|
14
|
+
TestFailure,
|
|
15
|
+
TraceEntry,
|
|
16
|
+
CoverageInfo,
|
|
17
|
+
LocationInfo,
|
|
18
|
+
DiffInfo
|
|
19
|
+
} from './taist';
|
|
20
|
+
|
|
21
|
+
export { VitestRunner, VitestRunnerOptions } from './vitest-runner';
|
|
22
|
+
export { OutputFormatter, OutputFormatterOptions } from './output-formatter';
|
|
23
|
+
export { WatchHandler, WatchHandlerOptions } from './watch-handler';
|
|
24
|
+
export { ExecutionTracer, ExecutionTracerOptions } from './execution-tracer';
|
|
25
|
+
export {
|
|
26
|
+
ToonFormatter,
|
|
27
|
+
ToonFormatterOptions,
|
|
28
|
+
FormatTraceTreeOptions
|
|
29
|
+
} from './toon-formatter';
|
|
30
|
+
|
|
31
|
+
// Re-export instrument types
|
|
32
|
+
export * from './instrument';
|
|
33
|
+
|
|
34
|
+
// Re-export testing types
|
|
35
|
+
export * from './testing';
|
|
36
|
+
|
|
37
|
+
// Re-export trace-collector types
|
|
38
|
+
export * from './trace-collector';
|
|
39
|
+
|
|
40
|
+
// Re-export vitest-reporter types
|
|
41
|
+
export { TaistReporter, TaistReporterOptions } from './vitest-reporter';
|