@git.zone/tstest 1.0.96 → 1.2.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.
- package/dist_ts/00_commitinfo_data.js +2 -2
- package/dist_ts/index.d.ts +5 -0
- package/dist_ts/index.js +61 -4
- package/dist_ts/tstest.classes.tap.combinator.d.ts +3 -0
- package/dist_ts/tstest.classes.tap.combinator.js +14 -40
- package/dist_ts/tstest.classes.tap.parser.d.ts +3 -1
- package/dist_ts/tstest.classes.tap.parser.js +42 -18
- package/dist_ts/tstest.classes.tap.testresult.d.ts +1 -1
- package/dist_ts/tstest.classes.testdirectory.d.ts +9 -7
- package/dist_ts/tstest.classes.testdirectory.js +47 -8
- package/dist_ts/tstest.classes.tstest.d.ts +8 -3
- package/dist_ts/tstest.classes.tstest.js +35 -34
- package/dist_ts/tstest.logging.d.ts +48 -0
- package/dist_ts/tstest.logging.js +228 -0
- package/package.json +12 -12
- package/readme.plan.md +199 -0
- package/ts/00_commitinfo_data.ts +1 -1
- package/ts/index.ts +65 -3
- package/ts/tstest.classes.tap.combinator.ts +19 -41
- package/ts/tstest.classes.tap.parser.ts +44 -47
- package/ts/tstest.classes.testdirectory.ts +59 -14
- package/ts/tstest.classes.tstest.ts +42 -36
- package/ts/tstest.logging.ts +285 -0
package/readme.plan.md
ADDED
|
@@ -0,0 +1,199 @@
|
|
|
1
|
+
# Plan for improving logging and output in tstest
|
|
2
|
+
|
|
3
|
+
!! FIRST: Reread /home/philkunz/.claude/CLAUDE.md to ensure following all guidelines !!
|
|
4
|
+
|
|
5
|
+
## Goal - ✅ MOSTLY COMPLETED
|
|
6
|
+
- ✅ Make test output cleaner and more visually appealing
|
|
7
|
+
- ✅ Add structured logging capabilities
|
|
8
|
+
- ✅ Support different verbosity levels
|
|
9
|
+
- ✅ Improve CI/CD compatibility
|
|
10
|
+
- ✅ Add progress indicators and timing summaries
|
|
11
|
+
|
|
12
|
+
## Current State - UPDATED
|
|
13
|
+
- ✅ Clean, modern visual design with Unicode characters
|
|
14
|
+
- ✅ Structured output format (JSON support)
|
|
15
|
+
- ✅ Multiple verbosity levels (quiet, normal, verbose)
|
|
16
|
+
- ✅ Real-time output with cleaner formatting
|
|
17
|
+
- ✅ Better error aggregation and display
|
|
18
|
+
- ✅ TAP protocol support integrated with new logger
|
|
19
|
+
|
|
20
|
+
## Completed Improvements
|
|
21
|
+
|
|
22
|
+
### 1. ✅ Created new TsTestLogger class
|
|
23
|
+
- ✅ Centralized logging with consistent formatting
|
|
24
|
+
- ✅ Support for different output modes (normal, quiet, verbose)
|
|
25
|
+
- ✅ Better visual hierarchy with modern Unicode characters
|
|
26
|
+
- ✅ Progress indicators for multiple test files
|
|
27
|
+
- ✅ Structured error collection and display
|
|
28
|
+
|
|
29
|
+
### 2. ✅ Updated visual design
|
|
30
|
+
- ✅ Replaced heavy separators with cleaner alternatives
|
|
31
|
+
- ✅ Used better emoji and Unicode characters
|
|
32
|
+
- ✅ Added indentation for hierarchical display
|
|
33
|
+
- ✅ Grouped related information visually
|
|
34
|
+
- ✅ Added color coding consistently
|
|
35
|
+
|
|
36
|
+
### 3. ✅ Added command-line options
|
|
37
|
+
- ✅ `--quiet` for minimal CI-friendly output
|
|
38
|
+
- ✅ `--verbose` for detailed debugging information
|
|
39
|
+
- ✅ `--no-color` for environments without color support
|
|
40
|
+
- ✅ `--json` for structured JSON output
|
|
41
|
+
- ⏳ `--log-file <path>` for persistent logging (TODO)
|
|
42
|
+
|
|
43
|
+
### 4. ✅ Improved progress feedback
|
|
44
|
+
- ⏳ Show progress bar for multiple files (TODO)
|
|
45
|
+
- ✅ Display current file being executed
|
|
46
|
+
- ✅ Show real-time test counts
|
|
47
|
+
- ⏳ Add ETA for long test suites (TODO)
|
|
48
|
+
|
|
49
|
+
### 5. ✅ Better error and summary display
|
|
50
|
+
- ✅ Collect all errors and display at end
|
|
51
|
+
- ✅ Show timing metrics and performance summary (in verbose mode)
|
|
52
|
+
- ✅ Highlight slowest tests (in verbose mode)
|
|
53
|
+
- ✅ Add test failure context
|
|
54
|
+
|
|
55
|
+
### 6. ✅ Browser console integration
|
|
56
|
+
- ✅ Clearly separate browser logs from test output
|
|
57
|
+
- ⏳ Add browser log filtering options (TODO)
|
|
58
|
+
- ✅ Format browser errors specially
|
|
59
|
+
|
|
60
|
+
## Implementation Steps - COMPLETED
|
|
61
|
+
|
|
62
|
+
### Phase 1: ✅ Core Logger Implementation
|
|
63
|
+
1. ✅ Created `tstest.logging.ts` with TsTestLogger class
|
|
64
|
+
2. ✅ Added LogOptions interface for configuration
|
|
65
|
+
3. ✅ Implemented basic formatting methods
|
|
66
|
+
4. ✅ Added progress and summary methods
|
|
67
|
+
|
|
68
|
+
### Phase 2: ✅ Integration
|
|
69
|
+
1. ✅ Updated CLI to accept new command-line options
|
|
70
|
+
2. ✅ Modified TsTest class to use new logger
|
|
71
|
+
3. ✅ Updated TapParser to use structured logging
|
|
72
|
+
4. ✅ Updated TapCombinator for better summaries
|
|
73
|
+
|
|
74
|
+
### Phase 3: ✅ Visual Improvements
|
|
75
|
+
1. ✅ Replaced all existing separators
|
|
76
|
+
2. ✅ Updated color scheme
|
|
77
|
+
3. ✅ Added emoji and Unicode characters
|
|
78
|
+
4. ✅ Implemented hierarchical output
|
|
79
|
+
|
|
80
|
+
### Phase 4: ✅ Advanced Features
|
|
81
|
+
1. ✅ Add JSON output format
|
|
82
|
+
2. ⏳ Implement file-based logging (TODO)
|
|
83
|
+
3. ✅ Add performance metrics collection
|
|
84
|
+
4. ✅ Create error aggregation system
|
|
85
|
+
|
|
86
|
+
### Phase 5: ✅ Browser Integration
|
|
87
|
+
1. ✅ Update browser console forwarding
|
|
88
|
+
2. ✅ Add browser log formatting
|
|
89
|
+
3. ✅ Implement browser-specific indicators
|
|
90
|
+
|
|
91
|
+
## Files modified
|
|
92
|
+
- ✅ `ts/tstest.logging.ts` - New logger implementation (created)
|
|
93
|
+
- ✅ `ts/index.ts` - Added CLI options parsing
|
|
94
|
+
- ✅ `ts/tstest.classes.tstest.ts` - Integrated new logger
|
|
95
|
+
- ✅ `ts/tstest.classes.tap.parser.ts` - Updated output formatting
|
|
96
|
+
- ✅ `ts/tstest.classes.tap.combinator.ts` - Improved summary display
|
|
97
|
+
- ❌ `ts/tstest.logprefixes.ts` - Still in use, can be deprecated later
|
|
98
|
+
- ❌ `package.json` - No new dependencies needed
|
|
99
|
+
|
|
100
|
+
## Success Criteria - ACHIEVED
|
|
101
|
+
- ✅ Cleaner, more readable test output
|
|
102
|
+
- ✅ Configurable verbosity levels
|
|
103
|
+
- ✅ Better CI/CD integration
|
|
104
|
+
- ✅ Improved error visibility
|
|
105
|
+
- ✅ Performance metrics available
|
|
106
|
+
- ✅ Consistent visual design
|
|
107
|
+
- ✅ Maintained backward compatibility
|
|
108
|
+
|
|
109
|
+
## Example Output Comparison
|
|
110
|
+
|
|
111
|
+
### Current Output
|
|
112
|
+
```
|
|
113
|
+
☰☰☰☰☰☰☰☰☰☰☰☰☰☰☰☰☰☰☰☰☰☰☰☰☰☰☰☰☰☰☰☰☰☰☰☰☰☰☰☰
|
|
114
|
+
**TSTEST** FOUND 4 TESTFILE(S):
|
|
115
|
+
**TSTEST** test/test.ts
|
|
116
|
+
------------------------------------------------
|
|
117
|
+
=> Running test/test.ts in node.js runtime.
|
|
118
|
+
= = = = = = = = = = = = = = = = = = = = = = = =
|
|
119
|
+
```
|
|
120
|
+
|
|
121
|
+
### Actual Output (IMPLEMENTED)
|
|
122
|
+
```
|
|
123
|
+
🔍 Test Discovery
|
|
124
|
+
Mode: directory
|
|
125
|
+
Pattern: test
|
|
126
|
+
Found: 4 test file(s)
|
|
127
|
+
|
|
128
|
+
▶️ test/test.ts (1/4)
|
|
129
|
+
Runtime: node.js
|
|
130
|
+
✅ prepare test (0ms)
|
|
131
|
+
Summary: 1/1 PASSED
|
|
132
|
+
|
|
133
|
+
▶️ test/test.single.ts (2/4)
|
|
134
|
+
Runtime: node.js
|
|
135
|
+
✅ single file test execution (1ms)
|
|
136
|
+
Summary: 1/1 PASSED
|
|
137
|
+
|
|
138
|
+
📊 Test Summary
|
|
139
|
+
┌────────────────────────────────┐
|
|
140
|
+
│ Total Files: 4 │
|
|
141
|
+
│ Total Tests: 4 │
|
|
142
|
+
│ Passed: 4 │
|
|
143
|
+
│ Failed: 0 │
|
|
144
|
+
│ Duration: 5739ms │
|
|
145
|
+
└────────────────────────────────┘
|
|
146
|
+
|
|
147
|
+
ALL TESTS PASSED! 🎉
|
|
148
|
+
```
|
|
149
|
+
|
|
150
|
+
### Additional Features Implemented
|
|
151
|
+
|
|
152
|
+
1. **Quiet Mode**:
|
|
153
|
+
```
|
|
154
|
+
Found 1 tests
|
|
155
|
+
✅ single file test execution
|
|
156
|
+
|
|
157
|
+
Summary: 1/1 | 1210ms | PASSED
|
|
158
|
+
```
|
|
159
|
+
|
|
160
|
+
2. **JSON Mode**:
|
|
161
|
+
```json
|
|
162
|
+
{"event":"discovery","count":1,"pattern":"test/test.single.ts","executionMode":"file"}
|
|
163
|
+
{"event":"fileStart","filename":"test/test.single.ts","runtime":"node.js","index":1,"total":1}
|
|
164
|
+
{"event":"testResult","testName":"single file test execution","passed":true,"duration":0}
|
|
165
|
+
{"event":"summary","summary":{"totalFiles":1,"totalTests":1,"totalPassed":1,"totalFailed":0,"totalDuration":1223,"fileResults":[...]}}
|
|
166
|
+
```
|
|
167
|
+
|
|
168
|
+
3. **Error Display**:
|
|
169
|
+
```
|
|
170
|
+
❌ Failed Tests:
|
|
171
|
+
|
|
172
|
+
test/test.fail.ts
|
|
173
|
+
❌ This test should fail
|
|
174
|
+
|
|
175
|
+
SOME TESTS FAILED! ❌
|
|
176
|
+
```
|
|
177
|
+
|
|
178
|
+
## Summary of Implementation
|
|
179
|
+
|
|
180
|
+
The logging improvement plan has been successfully implemented with the following achievements:
|
|
181
|
+
|
|
182
|
+
1. **Created a new centralized TsTestLogger class** that handles all output formatting
|
|
183
|
+
2. **Added multiple output modes**: quiet, normal, verbose, and JSON
|
|
184
|
+
3. **Improved visual design** with modern Unicode characters and emojis
|
|
185
|
+
4. **Added CLI argument parsing** for all new options
|
|
186
|
+
5. **Integrated the logger throughout the codebase** (TsTest, TapParser, TapCombinator)
|
|
187
|
+
6. **Better error aggregation and display** with failed tests shown at the end
|
|
188
|
+
7. **Performance metrics** available in verbose mode
|
|
189
|
+
8. **Clean, hierarchical output** that's much more readable
|
|
190
|
+
|
|
191
|
+
### Remaining TODOs
|
|
192
|
+
|
|
193
|
+
Only a few minor features remain unimplemented:
|
|
194
|
+
- File-based logging (--log-file option)
|
|
195
|
+
- Progress bar visualization
|
|
196
|
+
- ETA for long test suites
|
|
197
|
+
- Browser log filtering options
|
|
198
|
+
|
|
199
|
+
The core logging improvements are complete and provide a much better user experience!
|
package/ts/00_commitinfo_data.ts
CHANGED
package/ts/index.ts
CHANGED
|
@@ -1,10 +1,72 @@
|
|
|
1
1
|
import { TsTest } from './tstest.classes.tstest.js';
|
|
2
|
+
import type { LogOptions } from './tstest.logging.js';
|
|
3
|
+
|
|
4
|
+
export enum TestExecutionMode {
|
|
5
|
+
DIRECTORY = 'directory',
|
|
6
|
+
FILE = 'file',
|
|
7
|
+
GLOB = 'glob'
|
|
8
|
+
}
|
|
2
9
|
|
|
3
10
|
export const runCli = async () => {
|
|
4
|
-
|
|
5
|
-
|
|
11
|
+
// Parse command line arguments
|
|
12
|
+
const args = process.argv.slice(2);
|
|
13
|
+
const logOptions: LogOptions = {};
|
|
14
|
+
let testPath: string | null = null;
|
|
15
|
+
|
|
16
|
+
// Parse options
|
|
17
|
+
for (let i = 0; i < args.length; i++) {
|
|
18
|
+
const arg = args[i];
|
|
19
|
+
|
|
20
|
+
switch (arg) {
|
|
21
|
+
case '--quiet':
|
|
22
|
+
case '-q':
|
|
23
|
+
logOptions.quiet = true;
|
|
24
|
+
break;
|
|
25
|
+
case '--verbose':
|
|
26
|
+
case '-v':
|
|
27
|
+
logOptions.verbose = true;
|
|
28
|
+
break;
|
|
29
|
+
case '--no-color':
|
|
30
|
+
logOptions.noColor = true;
|
|
31
|
+
break;
|
|
32
|
+
case '--json':
|
|
33
|
+
logOptions.json = true;
|
|
34
|
+
break;
|
|
35
|
+
case '--log-file':
|
|
36
|
+
if (i + 1 < args.length) {
|
|
37
|
+
logOptions.logFile = args[++i];
|
|
38
|
+
}
|
|
39
|
+
break;
|
|
40
|
+
default:
|
|
41
|
+
if (!arg.startsWith('-')) {
|
|
42
|
+
testPath = arg;
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
if (!testPath) {
|
|
48
|
+
console.error('You must specify a test directory/file/pattern as argument. Please try again.');
|
|
49
|
+
console.error('\nUsage: tstest <path> [options]');
|
|
50
|
+
console.error('\nOptions:');
|
|
51
|
+
console.error(' --quiet, -q Minimal output');
|
|
52
|
+
console.error(' --verbose, -v Verbose output');
|
|
53
|
+
console.error(' --no-color Disable colored output');
|
|
54
|
+
console.error(' --json Output results as JSON');
|
|
55
|
+
console.error(' --log-file Write logs to file');
|
|
6
56
|
process.exit(1);
|
|
7
57
|
}
|
|
8
|
-
|
|
58
|
+
|
|
59
|
+
let executionMode: TestExecutionMode;
|
|
60
|
+
|
|
61
|
+
// Detect execution mode based on the argument
|
|
62
|
+
if (testPath.includes('*') || testPath.includes('?') || testPath.includes('[') || testPath.includes('{')) {
|
|
63
|
+
executionMode = TestExecutionMode.GLOB;
|
|
64
|
+
} else if (testPath.endsWith('.ts')) {
|
|
65
|
+
executionMode = TestExecutionMode.FILE;
|
|
66
|
+
} else {
|
|
67
|
+
executionMode = TestExecutionMode.DIRECTORY;
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
const tsTestInstance = new TsTest(process.cwd(), testPath, executionMode, logOptions);
|
|
9
71
|
await tsTestInstance.run();
|
|
10
72
|
};
|
|
@@ -6,59 +6,37 @@ import { coloredString as cs } from '@push.rocks/consolecolor';
|
|
|
6
6
|
|
|
7
7
|
import { TapParser } from './tstest.classes.tap.parser.js';
|
|
8
8
|
import * as logPrefixes from './tstest.logprefixes.js';
|
|
9
|
+
import { TsTestLogger } from './tstest.logging.js';
|
|
9
10
|
|
|
10
11
|
export class TapCombinator {
|
|
11
12
|
tapParserStore: TapParser[] = [];
|
|
13
|
+
private logger: TsTestLogger;
|
|
14
|
+
|
|
15
|
+
constructor(logger: TsTestLogger) {
|
|
16
|
+
this.logger = logger;
|
|
17
|
+
}
|
|
18
|
+
|
|
12
19
|
addTapParser(tapParserArg: TapParser) {
|
|
13
20
|
this.tapParserStore.push(tapParserArg);
|
|
14
21
|
}
|
|
15
22
|
|
|
16
23
|
evaluate() {
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
let failGlobal = false;
|
|
24
|
+
// Call the logger's summary method
|
|
25
|
+
this.logger.summary();
|
|
26
|
+
|
|
27
|
+
// Check for failures
|
|
28
|
+
let failGlobal = false;
|
|
22
29
|
for (const tapParser of this.tapParserStore) {
|
|
23
|
-
if (!tapParser.expectedTests
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
logPrefixes.TsTestPrefix +
|
|
27
|
-
cs(` ${tapParser.fileName} ${plugins.figures.cross}`, 'red') +
|
|
28
|
-
` ${plugins.figures.pointer} ` +
|
|
29
|
-
`does not specify tests!`;
|
|
30
|
-
console.log(overviewString);
|
|
31
|
-
} else if (tapParser.expectedTests !== tapParser.receivedTests) {
|
|
32
|
-
failGlobal = true;
|
|
33
|
-
let overviewString =
|
|
34
|
-
logPrefixes.TsTestPrefix +
|
|
35
|
-
cs(` ${tapParser.fileName} ${plugins.figures.cross}`, 'red') +
|
|
36
|
-
` ${plugins.figures.pointer} ` +
|
|
37
|
-
tapParser.getTestOverviewAsString() +
|
|
38
|
-
`did not execute all specified tests!`;
|
|
39
|
-
console.log(overviewString);
|
|
40
|
-
} else if (tapParser.getErrorTests().length === 0) {
|
|
41
|
-
let overviewString =
|
|
42
|
-
logPrefixes.TsTestPrefix +
|
|
43
|
-
cs(` ${tapParser.fileName} ${plugins.figures.tick}`, 'green') +
|
|
44
|
-
` ${plugins.figures.pointer} ` +
|
|
45
|
-
tapParser.getTestOverviewAsString();
|
|
46
|
-
console.log(overviewString);
|
|
47
|
-
} else {
|
|
30
|
+
if (!tapParser.expectedTests ||
|
|
31
|
+
tapParser.expectedTests !== tapParser.receivedTests ||
|
|
32
|
+
tapParser.getErrorTests().length > 0) {
|
|
48
33
|
failGlobal = true;
|
|
49
|
-
|
|
50
|
-
logPrefixes.TsTestPrefix +
|
|
51
|
-
cs(` ${tapParser.fileName} ${plugins.figures.cross}`, 'red') +
|
|
52
|
-
` ${plugins.figures.pointer} ` +
|
|
53
|
-
tapParser.getTestOverviewAsString();
|
|
54
|
-
console.log(overviewString);
|
|
34
|
+
break;
|
|
55
35
|
}
|
|
56
36
|
}
|
|
57
|
-
|
|
58
|
-
if
|
|
59
|
-
|
|
60
|
-
} else {
|
|
61
|
-
console.log(cs('FINAL RESULT: FAIL!', 'red'));
|
|
37
|
+
|
|
38
|
+
// Exit with error code if tests failed
|
|
39
|
+
if (failGlobal) {
|
|
62
40
|
process.exit(1);
|
|
63
41
|
}
|
|
64
42
|
}
|
|
@@ -7,6 +7,7 @@ import { coloredString as cs } from '@push.rocks/consolecolor';
|
|
|
7
7
|
import * as plugins from './tstest.plugins.js';
|
|
8
8
|
import { TapTestResult } from './tstest.classes.tap.testresult.js';
|
|
9
9
|
import * as logPrefixes from './tstest.logprefixes.js';
|
|
10
|
+
import { TsTestLogger } from './tstest.logging.js';
|
|
10
11
|
|
|
11
12
|
export class TapParser {
|
|
12
13
|
testStore: TapTestResult[] = [];
|
|
@@ -19,11 +20,15 @@ export class TapParser {
|
|
|
19
20
|
activeTapTestResult: TapTestResult;
|
|
20
21
|
|
|
21
22
|
pretaskRegex = /^::__PRETASK:(.*)$/;
|
|
23
|
+
|
|
24
|
+
private logger: TsTestLogger;
|
|
22
25
|
|
|
23
26
|
/**
|
|
24
27
|
* the constructor for TapParser
|
|
25
28
|
*/
|
|
26
|
-
constructor(public fileName: string) {
|
|
29
|
+
constructor(public fileName: string, logger?: TsTestLogger) {
|
|
30
|
+
this.logger = logger;
|
|
31
|
+
}
|
|
27
32
|
|
|
28
33
|
private _getNewTapTestResult() {
|
|
29
34
|
this.activeTapTestResult = new TapTestResult(this.testStore.length + 1);
|
|
@@ -45,9 +50,9 @@ export class TapParser {
|
|
|
45
50
|
logLineIsTapProtocol = true;
|
|
46
51
|
const regexResult = this.expectedTestsRegex.exec(logLine);
|
|
47
52
|
this.expectedTests = parseInt(regexResult[2]);
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
53
|
+
if (this.logger) {
|
|
54
|
+
this.logger.tapOutput(`Expecting ${this.expectedTests} tests!`);
|
|
55
|
+
}
|
|
51
56
|
|
|
52
57
|
// initiating first TapResult
|
|
53
58
|
this._getNewTapTestResult();
|
|
@@ -55,7 +60,9 @@ export class TapParser {
|
|
|
55
60
|
logLineIsTapProtocol = true;
|
|
56
61
|
const pretaskContentMatch = this.pretaskRegex.exec(logLine);
|
|
57
62
|
if (pretaskContentMatch && pretaskContentMatch[1]) {
|
|
58
|
-
|
|
63
|
+
if (this.logger) {
|
|
64
|
+
this.logger.tapOutput(`Pretask -> ${pretaskContentMatch[1]}: Success.`);
|
|
65
|
+
}
|
|
59
66
|
}
|
|
60
67
|
} else if (this.testStatusRegex.test(logLine)) {
|
|
61
68
|
logLineIsTapProtocol = true;
|
|
@@ -73,26 +80,20 @@ export class TapParser {
|
|
|
73
80
|
|
|
74
81
|
// test for protocol error
|
|
75
82
|
if (testId !== this.activeTapTestResult.id) {
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
83
|
+
if (this.logger) {
|
|
84
|
+
this.logger.error('Something is strange! Test Ids are not equal!');
|
|
85
|
+
}
|
|
79
86
|
}
|
|
80
87
|
this.activeTapTestResult.setTestResult(testOk);
|
|
81
88
|
|
|
82
89
|
if (testOk) {
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
cs(testSubject, 'blue') +
|
|
87
|
-
` | ${cs(`${testDuration} ms`, 'orange')}`
|
|
88
|
-
);
|
|
90
|
+
if (this.logger) {
|
|
91
|
+
this.logger.testResult(testSubject, true, testDuration);
|
|
92
|
+
}
|
|
89
93
|
} else {
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
cs(testSubject, 'blue') +
|
|
94
|
-
` | ${cs(`${testDuration} ms`, 'orange')}`
|
|
95
|
-
);
|
|
94
|
+
if (this.logger) {
|
|
95
|
+
this.logger.testResult(testSubject, false, testDuration);
|
|
96
|
+
}
|
|
96
97
|
}
|
|
97
98
|
}
|
|
98
99
|
|
|
@@ -100,7 +101,9 @@ export class TapParser {
|
|
|
100
101
|
if (this.activeTapTestResult) {
|
|
101
102
|
this.activeTapTestResult.addLogLine(logLine);
|
|
102
103
|
}
|
|
103
|
-
|
|
104
|
+
if (this.logger) {
|
|
105
|
+
this.logger.tapOutput(logLine);
|
|
106
|
+
}
|
|
104
107
|
}
|
|
105
108
|
|
|
106
109
|
if (this.activeTapTestResult && this.activeTapTestResult.testSettled) {
|
|
@@ -172,38 +175,32 @@ export class TapParser {
|
|
|
172
175
|
|
|
173
176
|
// check wether all tests ran
|
|
174
177
|
if (this.expectedTests === this.receivedTests) {
|
|
175
|
-
|
|
176
|
-
`${
|
|
177
|
-
|
|
178
|
-
'green'
|
|
179
|
-
)}`
|
|
180
|
-
);
|
|
178
|
+
if (this.logger) {
|
|
179
|
+
this.logger.tapOutput(`${this.receivedTests} out of ${this.expectedTests} Tests completed!`);
|
|
180
|
+
}
|
|
181
181
|
} else {
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
'red'
|
|
186
|
-
)}`
|
|
187
|
-
);
|
|
182
|
+
if (this.logger) {
|
|
183
|
+
this.logger.error(`Only ${this.receivedTests} out of ${this.expectedTests} completed!`);
|
|
184
|
+
}
|
|
188
185
|
}
|
|
189
186
|
if (!this.expectedTests) {
|
|
190
|
-
|
|
187
|
+
if (this.logger) {
|
|
188
|
+
this.logger.error('No tests were defined. Therefore the testfile failed!');
|
|
189
|
+
}
|
|
191
190
|
} else if (this.expectedTests !== this.receivedTests) {
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
'red'
|
|
196
|
-
)
|
|
197
|
-
);
|
|
191
|
+
if (this.logger) {
|
|
192
|
+
this.logger.error('The amount of received tests and expectedTests is unequal! Therefore the testfile failed');
|
|
193
|
+
}
|
|
198
194
|
} else if (this.getErrorTests().length === 0) {
|
|
199
|
-
|
|
195
|
+
if (this.logger) {
|
|
196
|
+
this.logger.tapOutput('All tests are successfull!!!');
|
|
197
|
+
this.logger.testFileEnd(this.receivedTests, 0, 0);
|
|
198
|
+
}
|
|
200
199
|
} else {
|
|
201
|
-
|
|
202
|
-
`${
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
)}`
|
|
206
|
-
);
|
|
200
|
+
if (this.logger) {
|
|
201
|
+
this.logger.tapOutput(`${this.getErrorTests().length} tests threw an error!!!`, true);
|
|
202
|
+
this.logger.testFileEnd(this.receivedTests - this.getErrorTests().length, this.getErrorTests().length, 0);
|
|
203
|
+
}
|
|
207
204
|
}
|
|
208
205
|
}
|
|
209
206
|
}
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import * as plugins from './tstest.plugins.js';
|
|
2
2
|
import * as paths from './tstest.paths.js';
|
|
3
3
|
import { SmartFile } from '@push.rocks/smartfile';
|
|
4
|
+
import { TestExecutionMode } from './index.js';
|
|
4
5
|
|
|
5
6
|
// tap related stuff
|
|
6
7
|
import { TapCombinator } from './tstest.classes.tap.combinator.js';
|
|
@@ -14,14 +15,14 @@ export class TestDirectory {
|
|
|
14
15
|
cwd: string;
|
|
15
16
|
|
|
16
17
|
/**
|
|
17
|
-
* the
|
|
18
|
+
* the test path or pattern
|
|
18
19
|
*/
|
|
19
|
-
|
|
20
|
+
testPath: string;
|
|
20
21
|
|
|
21
22
|
/**
|
|
22
|
-
* the
|
|
23
|
+
* the execution mode
|
|
23
24
|
*/
|
|
24
|
-
|
|
25
|
+
executionMode: TestExecutionMode;
|
|
25
26
|
|
|
26
27
|
/**
|
|
27
28
|
* an array of Smartfiles
|
|
@@ -30,27 +31,71 @@ export class TestDirectory {
|
|
|
30
31
|
|
|
31
32
|
/**
|
|
32
33
|
* the constructor for TestDirectory
|
|
33
|
-
*
|
|
34
|
-
* @param
|
|
34
|
+
* @param cwdArg - the current working directory
|
|
35
|
+
* @param testPathArg - the test path/pattern
|
|
36
|
+
* @param executionModeArg - the execution mode
|
|
35
37
|
*/
|
|
36
|
-
constructor(cwdArg: string,
|
|
38
|
+
constructor(cwdArg: string, testPathArg: string, executionModeArg: TestExecutionMode) {
|
|
37
39
|
this.cwd = cwdArg;
|
|
38
|
-
this.
|
|
40
|
+
this.testPath = testPathArg;
|
|
41
|
+
this.executionMode = executionModeArg;
|
|
39
42
|
}
|
|
40
43
|
|
|
41
44
|
private async _init() {
|
|
42
|
-
this.
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
45
|
+
switch (this.executionMode) {
|
|
46
|
+
case TestExecutionMode.FILE:
|
|
47
|
+
// Single file mode
|
|
48
|
+
const filePath = plugins.path.isAbsolute(this.testPath)
|
|
49
|
+
? this.testPath
|
|
50
|
+
: plugins.path.join(this.cwd, this.testPath);
|
|
51
|
+
|
|
52
|
+
if (await plugins.smartfile.fs.fileExists(filePath)) {
|
|
53
|
+
this.testfileArray = [await plugins.smartfile.SmartFile.fromFilePath(filePath)];
|
|
54
|
+
} else {
|
|
55
|
+
throw new Error(`Test file not found: ${filePath}`);
|
|
56
|
+
}
|
|
57
|
+
break;
|
|
58
|
+
|
|
59
|
+
case TestExecutionMode.GLOB:
|
|
60
|
+
// Glob pattern mode - use listFileTree which supports glob patterns
|
|
61
|
+
const globPattern = this.testPath;
|
|
62
|
+
const matchedFiles = await plugins.smartfile.fs.listFileTree(this.cwd, globPattern);
|
|
63
|
+
|
|
64
|
+
this.testfileArray = await Promise.all(
|
|
65
|
+
matchedFiles.map(async (filePath) => {
|
|
66
|
+
const absolutePath = plugins.path.isAbsolute(filePath)
|
|
67
|
+
? filePath
|
|
68
|
+
: plugins.path.join(this.cwd, filePath);
|
|
69
|
+
return await plugins.smartfile.SmartFile.fromFilePath(absolutePath);
|
|
70
|
+
})
|
|
71
|
+
);
|
|
72
|
+
break;
|
|
73
|
+
|
|
74
|
+
case TestExecutionMode.DIRECTORY:
|
|
75
|
+
// Directory mode - now recursive with ** pattern
|
|
76
|
+
const dirPath = plugins.path.join(this.cwd, this.testPath);
|
|
77
|
+
const testPattern = '**/test*.ts';
|
|
78
|
+
|
|
79
|
+
const testFiles = await plugins.smartfile.fs.listFileTree(dirPath, testPattern);
|
|
80
|
+
|
|
81
|
+
this.testfileArray = await Promise.all(
|
|
82
|
+
testFiles.map(async (filePath) => {
|
|
83
|
+
const absolutePath = plugins.path.isAbsolute(filePath)
|
|
84
|
+
? filePath
|
|
85
|
+
: plugins.path.join(dirPath, filePath);
|
|
86
|
+
return await plugins.smartfile.SmartFile.fromFilePath(absolutePath);
|
|
87
|
+
})
|
|
88
|
+
);
|
|
89
|
+
break;
|
|
90
|
+
}
|
|
46
91
|
}
|
|
47
92
|
|
|
48
93
|
async getTestFilePathArray() {
|
|
49
94
|
await this._init();
|
|
50
95
|
const testFilePaths: string[] = [];
|
|
51
96
|
for (const testFile of this.testfileArray) {
|
|
52
|
-
|
|
53
|
-
testFilePaths.push(
|
|
97
|
+
// Use the path directly from the SmartFile
|
|
98
|
+
testFilePaths.push(testFile.path);
|
|
54
99
|
}
|
|
55
100
|
return testFilePaths;
|
|
56
101
|
}
|