nova-terminal-assistant 0.1.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.
Potentially problematic release.
This version of nova-terminal-assistant might be problematic. Click here for more details.
- package/README.md +358 -0
- package/bin/nova +38 -0
- package/bin/nova.js +12 -0
- package/package.json +67 -0
- package/src/cli/commands/SmartCompletion.ts +458 -0
- package/src/cli/index.ts +5 -0
- package/src/cli/startup/IFlowRepl.ts +212 -0
- package/src/cli/startup/InkBasedRepl.ts +1056 -0
- package/src/cli/startup/InteractiveRepl.ts +2833 -0
- package/src/cli/startup/NovaApp.ts +1861 -0
- package/src/cli/startup/index.ts +4 -0
- package/src/cli/startup/parseArgs.ts +293 -0
- package/src/cli/test-modules.ts +27 -0
- package/src/cli/ui/IFlowDropdown.ts +425 -0
- package/src/cli/ui/ModernReplUI.ts +276 -0
- package/src/cli/ui/SimpleSelector2.ts +215 -0
- package/src/cli/ui/components/ConfirmDialog.ts +176 -0
- package/src/cli/ui/components/ErrorPanel.ts +364 -0
- package/src/cli/ui/components/InkAppRunner.tsx +67 -0
- package/src/cli/ui/components/InkComponents.tsx +613 -0
- package/src/cli/ui/components/NovaInkApp.tsx +312 -0
- package/src/cli/ui/components/ProgressBar.ts +177 -0
- package/src/cli/ui/components/ProgressIndicator.ts +298 -0
- package/src/cli/ui/components/QuickActions.ts +396 -0
- package/src/cli/ui/components/SimpleErrorPanel.ts +231 -0
- package/src/cli/ui/components/StatusBar.ts +194 -0
- package/src/cli/ui/components/ThinkingBlockRenderer.ts +401 -0
- package/src/cli/ui/components/index.ts +27 -0
- package/src/cli/ui/ink-prototype.tsx +347 -0
- package/src/cli/utils/CliUI.ts +336 -0
- package/src/cli/utils/CompletionHelper.ts +388 -0
- package/src/cli/utils/EnhancedCompleter.test.ts +226 -0
- package/src/cli/utils/EnhancedCompleter.ts +513 -0
- package/src/cli/utils/ErrorEnhancer.ts +429 -0
- package/src/cli/utils/OutputFormatter.ts +193 -0
- package/src/cli/utils/index.ts +9 -0
- package/src/core/agents/AgentOrchestrator.ts +515 -0
- package/src/core/agents/index.ts +17 -0
- package/src/core/audit/AuditLogger.ts +509 -0
- package/src/core/audit/index.ts +11 -0
- package/src/core/auth/AuthManager.d.ts.map +1 -0
- package/src/core/auth/AuthManager.ts +138 -0
- package/src/core/auth/index.d.ts.map +1 -0
- package/src/core/auth/index.ts +2 -0
- package/src/core/config/ConfigManager.d.ts.map +1 -0
- package/src/core/config/ConfigManager.test.ts +183 -0
- package/src/core/config/ConfigManager.ts +1219 -0
- package/src/core/config/index.d.ts.map +1 -0
- package/src/core/config/index.ts +1 -0
- package/src/core/context/ContextBuilder.d.ts.map +1 -0
- package/src/core/context/ContextBuilder.ts +171 -0
- package/src/core/context/ContextCompressor.d.ts.map +1 -0
- package/src/core/context/ContextCompressor.ts +642 -0
- package/src/core/context/LayeredMemoryManager.ts +657 -0
- package/src/core/context/MemoryDiscovery.d.ts.map +1 -0
- package/src/core/context/MemoryDiscovery.ts +175 -0
- package/src/core/context/defaultSystemPrompt.d.ts.map +1 -0
- package/src/core/context/defaultSystemPrompt.ts +35 -0
- package/src/core/context/index.d.ts.map +1 -0
- package/src/core/context/index.ts +22 -0
- package/src/core/extensions/SkillGenerator.ts +421 -0
- package/src/core/extensions/SkillInstaller.d.ts.map +1 -0
- package/src/core/extensions/SkillInstaller.ts +257 -0
- package/src/core/extensions/SkillRegistry.d.ts.map +1 -0
- package/src/core/extensions/SkillRegistry.ts +361 -0
- package/src/core/extensions/SkillValidator.ts +525 -0
- package/src/core/extensions/index.ts +15 -0
- package/src/core/index.d.ts.map +1 -0
- package/src/core/index.ts +42 -0
- package/src/core/mcp/McpManager.d.ts.map +1 -0
- package/src/core/mcp/McpManager.ts +632 -0
- package/src/core/mcp/index.d.ts.map +1 -0
- package/src/core/mcp/index.ts +2 -0
- package/src/core/model/ModelClient.d.ts.map +1 -0
- package/src/core/model/ModelClient.ts +217 -0
- package/src/core/model/ModelConnectionTester.ts +363 -0
- package/src/core/model/ModelValidator.ts +348 -0
- package/src/core/model/index.d.ts.map +1 -0
- package/src/core/model/index.ts +6 -0
- package/src/core/model/providers/AnthropicProvider.d.ts.map +1 -0
- package/src/core/model/providers/AnthropicProvider.ts +279 -0
- package/src/core/model/providers/CodingPlanProvider.d.ts.map +1 -0
- package/src/core/model/providers/CodingPlanProvider.ts +210 -0
- package/src/core/model/providers/OllamaCloudProvider.d.ts.map +1 -0
- package/src/core/model/providers/OllamaCloudProvider.ts +405 -0
- package/src/core/model/providers/OllamaManager.d.ts.map +1 -0
- package/src/core/model/providers/OllamaManager.ts +201 -0
- package/src/core/model/providers/OllamaProvider.d.ts.map +1 -0
- package/src/core/model/providers/OllamaProvider.ts +73 -0
- package/src/core/model/providers/OpenAICompatibleProvider.d.ts.map +1 -0
- package/src/core/model/providers/OpenAICompatibleProvider.ts +327 -0
- package/src/core/model/providers/OpenAIProvider.d.ts.map +1 -0
- package/src/core/model/providers/OpenAIProvider.ts +29 -0
- package/src/core/model/providers/index.d.ts.map +1 -0
- package/src/core/model/providers/index.ts +12 -0
- package/src/core/model/types.d.ts.map +1 -0
- package/src/core/model/types.ts +77 -0
- package/src/core/security/ApprovalManager.d.ts.map +1 -0
- package/src/core/security/ApprovalManager.ts +174 -0
- package/src/core/security/FileFilter.d.ts.map +1 -0
- package/src/core/security/FileFilter.ts +141 -0
- package/src/core/security/HookExecutor.d.ts.map +1 -0
- package/src/core/security/HookExecutor.ts +178 -0
- package/src/core/security/SandboxExecutor.ts +447 -0
- package/src/core/security/index.d.ts.map +1 -0
- package/src/core/security/index.ts +8 -0
- package/src/core/session/AgentLoop.d.ts.map +1 -0
- package/src/core/session/AgentLoop.ts +501 -0
- package/src/core/session/SessionManager.d.ts.map +1 -0
- package/src/core/session/SessionManager.test.ts +183 -0
- package/src/core/session/SessionManager.ts +460 -0
- package/src/core/session/index.d.ts.map +1 -0
- package/src/core/session/index.ts +3 -0
- package/src/core/telemetry/Telemetry.d.ts.map +1 -0
- package/src/core/telemetry/Telemetry.ts +90 -0
- package/src/core/telemetry/TelemetryService.ts +531 -0
- package/src/core/telemetry/index.d.ts.map +1 -0
- package/src/core/telemetry/index.ts +12 -0
- package/src/core/testing/AutoFixer.ts +385 -0
- package/src/core/testing/ErrorAnalyzer.ts +499 -0
- package/src/core/testing/TestRunner.ts +265 -0
- package/src/core/testing/agent-cli-tests.ts +538 -0
- package/src/core/testing/index.ts +11 -0
- package/src/core/tools/ToolRegistry.d.ts.map +1 -0
- package/src/core/tools/ToolRegistry.test.ts +206 -0
- package/src/core/tools/ToolRegistry.ts +260 -0
- package/src/core/tools/impl/EditFileTool.d.ts.map +1 -0
- package/src/core/tools/impl/EditFileTool.ts +97 -0
- package/src/core/tools/impl/ListDirectoryTool.d.ts.map +1 -0
- package/src/core/tools/impl/ListDirectoryTool.ts +142 -0
- package/src/core/tools/impl/MemoryTool.d.ts.map +1 -0
- package/src/core/tools/impl/MemoryTool.ts +102 -0
- package/src/core/tools/impl/ReadFileTool.d.ts.map +1 -0
- package/src/core/tools/impl/ReadFileTool.ts +58 -0
- package/src/core/tools/impl/SearchContentTool.d.ts.map +1 -0
- package/src/core/tools/impl/SearchContentTool.ts +94 -0
- package/src/core/tools/impl/SearchFileTool.d.ts.map +1 -0
- package/src/core/tools/impl/SearchFileTool.ts +61 -0
- package/src/core/tools/impl/ShellTool.d.ts.map +1 -0
- package/src/core/tools/impl/ShellTool.ts +118 -0
- package/src/core/tools/impl/TaskTool.d.ts.map +1 -0
- package/src/core/tools/impl/TaskTool.ts +207 -0
- package/src/core/tools/impl/TodoTool.d.ts.map +1 -0
- package/src/core/tools/impl/TodoTool.ts +122 -0
- package/src/core/tools/impl/WebFetchTool.d.ts.map +1 -0
- package/src/core/tools/impl/WebFetchTool.ts +103 -0
- package/src/core/tools/impl/WebSearchTool.d.ts.map +1 -0
- package/src/core/tools/impl/WebSearchTool.ts +89 -0
- package/src/core/tools/impl/WriteFileTool.d.ts.map +1 -0
- package/src/core/tools/impl/WriteFileTool.ts +49 -0
- package/src/core/tools/impl/index.d.ts.map +1 -0
- package/src/core/tools/impl/index.ts +16 -0
- package/src/core/tools/index.d.ts.map +1 -0
- package/src/core/tools/index.ts +7 -0
- package/src/core/tools/schemas/execution.d.ts.map +1 -0
- package/src/core/tools/schemas/execution.ts +42 -0
- package/src/core/tools/schemas/file.d.ts.map +1 -0
- package/src/core/tools/schemas/file.ts +119 -0
- package/src/core/tools/schemas/index.d.ts.map +1 -0
- package/src/core/tools/schemas/index.ts +11 -0
- package/src/core/tools/schemas/memory.d.ts.map +1 -0
- package/src/core/tools/schemas/memory.ts +52 -0
- package/src/core/tools/schemas/orchestration.d.ts.map +1 -0
- package/src/core/tools/schemas/orchestration.ts +44 -0
- package/src/core/tools/schemas/search.d.ts.map +1 -0
- package/src/core/tools/schemas/search.ts +112 -0
- package/src/core/tools/schemas/todo.d.ts.map +1 -0
- package/src/core/tools/schemas/todo.ts +32 -0
- package/src/core/tools/schemas/web.d.ts.map +1 -0
- package/src/core/tools/schemas/web.ts +86 -0
- package/src/core/types/config.d.ts.map +1 -0
- package/src/core/types/config.ts +200 -0
- package/src/core/types/errors.d.ts.map +1 -0
- package/src/core/types/errors.ts +204 -0
- package/src/core/types/index.d.ts.map +1 -0
- package/src/core/types/index.ts +8 -0
- package/src/core/types/session.d.ts.map +1 -0
- package/src/core/types/session.ts +216 -0
- package/src/core/types/tools.d.ts.map +1 -0
- package/src/core/types/tools.ts +157 -0
- package/src/core/utils/CheckpointManager.d.ts.map +1 -0
- package/src/core/utils/CheckpointManager.ts +327 -0
- package/src/core/utils/Logger.d.ts.map +1 -0
- package/src/core/utils/Logger.ts +98 -0
- package/src/core/utils/RetryManager.ts +471 -0
- package/src/core/utils/TokenCounter.d.ts.map +1 -0
- package/src/core/utils/TokenCounter.ts +414 -0
- package/src/core/utils/VectorMemoryStore.ts +440 -0
- package/src/core/utils/helpers.d.ts.map +1 -0
- package/src/core/utils/helpers.ts +89 -0
- package/src/core/utils/index.d.ts.map +1 -0
- package/src/core/utils/index.ts +19 -0
|
@@ -0,0 +1,265 @@
|
|
|
1
|
+
// ============================================================================
|
|
2
|
+
// TestRunner - Execute test commands and parse results
|
|
3
|
+
// Reference: Aider-style automatic test execution
|
|
4
|
+
// ============================================================================
|
|
5
|
+
|
|
6
|
+
import { exec } from 'node:child_process';
|
|
7
|
+
import { promisify } from 'node:util';
|
|
8
|
+
|
|
9
|
+
const execAsync = promisify(exec);
|
|
10
|
+
|
|
11
|
+
export interface FailedTest {
|
|
12
|
+
/** Test file or test name */
|
|
13
|
+
file: string;
|
|
14
|
+
/** Error message */
|
|
15
|
+
error: string;
|
|
16
|
+
/** Line number if available */
|
|
17
|
+
line?: number;
|
|
18
|
+
/** Test framework that reported the failure */
|
|
19
|
+
framework?: string;
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
export interface TestResult {
|
|
23
|
+
/** Whether all tests passed */
|
|
24
|
+
success: boolean;
|
|
25
|
+
/** Raw stdout output */
|
|
26
|
+
output: string;
|
|
27
|
+
/** Raw stderr output */
|
|
28
|
+
errors: string;
|
|
29
|
+
/** List of failed tests */
|
|
30
|
+
failedTests: FailedTest[];
|
|
31
|
+
/** Total number of tests run (if parseable) */
|
|
32
|
+
totalTests?: number;
|
|
33
|
+
/** Number of passed tests (if parseable) */
|
|
34
|
+
passedTests?: number;
|
|
35
|
+
/** Duration in ms */
|
|
36
|
+
duration: number;
|
|
37
|
+
/** Exit code */
|
|
38
|
+
exitCode: number;
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
export interface TestRunnerOptions {
|
|
42
|
+
/** Working directory for the test command */
|
|
43
|
+
cwd?: string;
|
|
44
|
+
/** Timeout in ms (default: 60000) */
|
|
45
|
+
timeout?: number;
|
|
46
|
+
/** Environment variables */
|
|
47
|
+
env?: Record<string, string>;
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
// --- Test result parsing patterns ---
|
|
51
|
+
|
|
52
|
+
const TEST_PATTERNS: Array<{
|
|
53
|
+
framework: string;
|
|
54
|
+
failPattern: RegExp;
|
|
55
|
+
totalCountPattern?: RegExp;
|
|
56
|
+
passCountPattern?: RegExp;
|
|
57
|
+
errorLinePattern?: RegExp;
|
|
58
|
+
}> = [
|
|
59
|
+
{
|
|
60
|
+
framework: 'jest',
|
|
61
|
+
failPattern: /FAIL\s+([^\s]+)/g,
|
|
62
|
+
totalCountPattern: /Tests:\s+(\d+)\s+failed/,
|
|
63
|
+
passCountPattern: /Tests:\s+(\d+)\s+passed,\s+(\d+)\s+total/,
|
|
64
|
+
errorLinePattern: /at\s+([^\n]+):(\d+):\d+/,
|
|
65
|
+
},
|
|
66
|
+
{
|
|
67
|
+
framework: 'vitest',
|
|
68
|
+
failPattern: /×\s+([^\s]+)\s*\([^\)]*\)/g,
|
|
69
|
+
totalCountPattern: /Tests\s+(\d+)\s+failed/,
|
|
70
|
+
passCountPattern: /Tests\s+(\d+)\s+passed\s+\|/,
|
|
71
|
+
errorLinePattern: /at\s+([^\n]+):(\d+):\d+/,
|
|
72
|
+
},
|
|
73
|
+
{
|
|
74
|
+
framework: 'mocha',
|
|
75
|
+
failPattern: /\s+\d+\)\s+([^\n:]+):/g,
|
|
76
|
+
totalCountPattern: /(\d+)\s+passing/,
|
|
77
|
+
errorLinePattern: /at\s+([^\n]+):(\d+):\d+/,
|
|
78
|
+
},
|
|
79
|
+
{
|
|
80
|
+
framework: 'pytest',
|
|
81
|
+
failPattern: /FAILED\s+([^\s-]+)/g,
|
|
82
|
+
totalCountPattern: /(\d+)\s+failed/,
|
|
83
|
+
passCountPattern: /(\d+)\s+passed/,
|
|
84
|
+
errorLinePattern: /File\s+"([^"]+)",\s+line\s+(\d+)/,
|
|
85
|
+
},
|
|
86
|
+
{
|
|
87
|
+
framework: 'go-test',
|
|
88
|
+
failPattern: /---\s+FAIL:\s+([^\s]+)/g,
|
|
89
|
+
totalCountPattern: /FAIL\s+\d+/,
|
|
90
|
+
errorLinePattern: /([^/\\]+\.go):(\d+)/,
|
|
91
|
+
},
|
|
92
|
+
{
|
|
93
|
+
framework: 'cargo-test',
|
|
94
|
+
failPattern: /test\s+\.\.\.\s+FAILED/g,
|
|
95
|
+
errorLinePattern: /----\s+([^\n]+)\s+stdout/,
|
|
96
|
+
},
|
|
97
|
+
{
|
|
98
|
+
framework: 'generic',
|
|
99
|
+
failPattern: /(?:FAIL|FAILED|ERROR|error)\s*[:\s]+(.+)/gi,
|
|
100
|
+
errorLinePattern: /at\s+([^\n]+):(\d+):\d+/,
|
|
101
|
+
},
|
|
102
|
+
];
|
|
103
|
+
|
|
104
|
+
// --- TestRunner ---
|
|
105
|
+
|
|
106
|
+
export class TestRunner {
|
|
107
|
+
private options: TestRunnerOptions;
|
|
108
|
+
|
|
109
|
+
constructor(options?: TestRunnerOptions) {
|
|
110
|
+
this.options = {
|
|
111
|
+
cwd: process.cwd(),
|
|
112
|
+
timeout: 60000,
|
|
113
|
+
...options,
|
|
114
|
+
};
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
/**
|
|
118
|
+
* Run a test command and parse the results.
|
|
119
|
+
*/
|
|
120
|
+
async run(testCommand: string): Promise<TestResult> {
|
|
121
|
+
const startTime = Date.now();
|
|
122
|
+
|
|
123
|
+
try {
|
|
124
|
+
const { stdout, stderr } = await execAsync(testCommand, {
|
|
125
|
+
cwd: this.options.cwd,
|
|
126
|
+
timeout: this.options.timeout,
|
|
127
|
+
env: { ...process.env, ...this.options.env },
|
|
128
|
+
maxBuffer: 1024 * 1024, // 1MB
|
|
129
|
+
});
|
|
130
|
+
|
|
131
|
+
const duration = Date.now() - startTime;
|
|
132
|
+
const combinedOutput = `${stdout}\n${stderr}`;
|
|
133
|
+
const failedTests = this.parseFailedTests(combinedOutput);
|
|
134
|
+
const { totalTests, passedTests } = this.parseTestCounts(combinedOutput);
|
|
135
|
+
|
|
136
|
+
return {
|
|
137
|
+
success: failedTests.length === 0,
|
|
138
|
+
output: stdout,
|
|
139
|
+
errors: stderr,
|
|
140
|
+
failedTests,
|
|
141
|
+
totalTests,
|
|
142
|
+
passedTests,
|
|
143
|
+
duration,
|
|
144
|
+
exitCode: 0,
|
|
145
|
+
};
|
|
146
|
+
} catch (err: any) {
|
|
147
|
+
const duration = Date.now() - startTime;
|
|
148
|
+
const output = err.stdout || '';
|
|
149
|
+
const stderr = err.stderr || '';
|
|
150
|
+
const combinedOutput = `${output}\n${stderr}`;
|
|
151
|
+
const failedTests = this.parseFailedTests(combinedOutput);
|
|
152
|
+
const { totalTests, passedTests } = this.parseTestCounts(combinedOutput);
|
|
153
|
+
|
|
154
|
+
return {
|
|
155
|
+
success: false,
|
|
156
|
+
output,
|
|
157
|
+
errors: stderr || err.message,
|
|
158
|
+
failedTests,
|
|
159
|
+
totalTests,
|
|
160
|
+
passedTests,
|
|
161
|
+
duration,
|
|
162
|
+
exitCode: err.code || 1,
|
|
163
|
+
};
|
|
164
|
+
}
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
/**
|
|
168
|
+
* Auto-detect the test command for the current project.
|
|
169
|
+
*/
|
|
170
|
+
async detectTestCommand(): Promise<string | null> {
|
|
171
|
+
const fs = await import('node:fs/promises');
|
|
172
|
+
|
|
173
|
+
// Check for test scripts in package.json
|
|
174
|
+
try {
|
|
175
|
+
const pkg = JSON.parse(await fs.readFile('package.json', 'utf-8'));
|
|
176
|
+
if (pkg.scripts?.test) return pkg.scripts.test;
|
|
177
|
+
if (pkg.scripts?.['test:unit']) return pkg.scripts['test:unit'];
|
|
178
|
+
if (pkg.scripts?.['test:ci']) return pkg.scripts['test:ci'];
|
|
179
|
+
} catch {
|
|
180
|
+
// No package.json
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
// Check for test files
|
|
184
|
+
const testIndicators = [
|
|
185
|
+
{ file: 'jest.config.js', cmd: 'npx jest' },
|
|
186
|
+
{ file: 'jest.config.ts', cmd: 'npx jest' },
|
|
187
|
+
{ file: 'vitest.config.ts', cmd: 'npx vitest run' },
|
|
188
|
+
{ file: 'vitest.config.js', cmd: 'npx vitest run' },
|
|
189
|
+
{ file: '.mocharc.yml', cmd: 'npx mocha' },
|
|
190
|
+
{ file: 'pytest.ini', cmd: 'python -m pytest' },
|
|
191
|
+
{ file: 'Cargo.toml', cmd: 'cargo test' },
|
|
192
|
+
];
|
|
193
|
+
|
|
194
|
+
for (const indicator of testIndicators) {
|
|
195
|
+
try {
|
|
196
|
+
await fs.access(indicator.file);
|
|
197
|
+
return indicator.cmd;
|
|
198
|
+
} catch {
|
|
199
|
+
// File doesn't exist
|
|
200
|
+
}
|
|
201
|
+
}
|
|
202
|
+
|
|
203
|
+
return null;
|
|
204
|
+
}
|
|
205
|
+
|
|
206
|
+
/**
|
|
207
|
+
* Parse failed tests from test output.
|
|
208
|
+
*/
|
|
209
|
+
private parseFailedTests(output: string): FailedTest[] {
|
|
210
|
+
const failures: FailedTest[] = [];
|
|
211
|
+
|
|
212
|
+
for (const pattern of TEST_PATTERNS) {
|
|
213
|
+
const failRegex = new RegExp(pattern.failPattern.source, pattern.failPattern.flags);
|
|
214
|
+
const matches = output.matchAll(failRegex);
|
|
215
|
+
|
|
216
|
+
for (const match of matches) {
|
|
217
|
+
const testName = match[1]?.trim() || '';
|
|
218
|
+
if (!testName || testName.length < 2) continue;
|
|
219
|
+
// Skip internal framework messages
|
|
220
|
+
if (testName.startsWith('PASS') || testName.startsWith('FAIL')) continue;
|
|
221
|
+
|
|
222
|
+
let line: number | undefined;
|
|
223
|
+
const errorLineRegex = new RegExp(pattern.errorLinePattern.source, 'g');
|
|
224
|
+
const lineMatch = errorLineRegex.exec(output);
|
|
225
|
+
if (lineMatch) {
|
|
226
|
+
line = parseInt(lineMatch[2], 10) || undefined;
|
|
227
|
+
}
|
|
228
|
+
|
|
229
|
+
failures.push({
|
|
230
|
+
file: testName,
|
|
231
|
+
error: '',
|
|
232
|
+
line,
|
|
233
|
+
framework: pattern.framework,
|
|
234
|
+
});
|
|
235
|
+
}
|
|
236
|
+
}
|
|
237
|
+
|
|
238
|
+
// Deduplicate by file name
|
|
239
|
+
const seen = new Set<string>();
|
|
240
|
+
return failures.filter((f) => {
|
|
241
|
+
if (seen.has(f.file)) return false;
|
|
242
|
+
seen.add(f.file);
|
|
243
|
+
return true;
|
|
244
|
+
});
|
|
245
|
+
}
|
|
246
|
+
|
|
247
|
+
/**
|
|
248
|
+
* Parse total and passed test counts from output.
|
|
249
|
+
*/
|
|
250
|
+
private parseTestCounts(output: string): { totalTests?: number; passedTests?: number } {
|
|
251
|
+
for (const pattern of TEST_PATTERNS) {
|
|
252
|
+
if (pattern.passCountPattern) {
|
|
253
|
+
const match = output.match(pattern.passCountPattern);
|
|
254
|
+
if (match) {
|
|
255
|
+
return {
|
|
256
|
+
totalTests: parseInt(match[2], 10) || undefined,
|
|
257
|
+
passedTests: parseInt(match[1], 10) || undefined,
|
|
258
|
+
};
|
|
259
|
+
}
|
|
260
|
+
}
|
|
261
|
+
}
|
|
262
|
+
|
|
263
|
+
return {};
|
|
264
|
+
}
|
|
265
|
+
}
|