agentic-qe 2.6.5 → 2.6.6
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/CHANGELOG.md +52 -0
- package/README.md +1 -1
- package/dist/agents/CoverageAnalyzerAgent.d.ts +31 -0
- package/dist/agents/CoverageAnalyzerAgent.d.ts.map +1 -1
- package/dist/agents/CoverageAnalyzerAgent.js +159 -0
- package/dist/agents/CoverageAnalyzerAgent.js.map +1 -1
- package/dist/agents/FleetCommanderAgent.d.ts +36 -0
- package/dist/agents/FleetCommanderAgent.d.ts.map +1 -1
- package/dist/agents/FleetCommanderAgent.js +226 -0
- package/dist/agents/FleetCommanderAgent.js.map +1 -1
- package/dist/cli/commands/kg/mincut.d.ts +50 -0
- package/dist/cli/commands/kg/mincut.d.ts.map +1 -0
- package/dist/cli/commands/kg/mincut.js +372 -0
- package/dist/cli/commands/kg/mincut.js.map +1 -0
- package/dist/cli/index.js +91 -0
- package/dist/cli/index.js.map +1 -1
- package/dist/cli/init/claude-config.js +2 -2
- package/dist/code-intelligence/analysis/mincut/CircularDependencyDetector.d.ts +148 -0
- package/dist/code-intelligence/analysis/mincut/CircularDependencyDetector.d.ts.map +1 -0
- package/dist/code-intelligence/analysis/mincut/CircularDependencyDetector.js +393 -0
- package/dist/code-intelligence/analysis/mincut/CircularDependencyDetector.js.map +1 -0
- package/dist/code-intelligence/analysis/mincut/GraphAdapter.d.ts +169 -0
- package/dist/code-intelligence/analysis/mincut/GraphAdapter.d.ts.map +1 -0
- package/dist/code-intelligence/analysis/mincut/GraphAdapter.js +335 -0
- package/dist/code-intelligence/analysis/mincut/GraphAdapter.js.map +1 -0
- package/dist/code-intelligence/analysis/mincut/JsMinCut.d.ts +55 -0
- package/dist/code-intelligence/analysis/mincut/JsMinCut.d.ts.map +1 -0
- package/dist/code-intelligence/analysis/mincut/JsMinCut.js +265 -0
- package/dist/code-intelligence/analysis/mincut/JsMinCut.js.map +1 -0
- package/dist/code-intelligence/analysis/mincut/MinCutAnalyzer.d.ts +92 -0
- package/dist/code-intelligence/analysis/mincut/MinCutAnalyzer.d.ts.map +1 -0
- package/dist/code-intelligence/analysis/mincut/MinCutAnalyzer.js +200 -0
- package/dist/code-intelligence/analysis/mincut/MinCutAnalyzer.js.map +1 -0
- package/dist/code-intelligence/analysis/mincut/ModuleCouplingAnalyzer.d.ts +157 -0
- package/dist/code-intelligence/analysis/mincut/ModuleCouplingAnalyzer.d.ts.map +1 -0
- package/dist/code-intelligence/analysis/mincut/ModuleCouplingAnalyzer.js +434 -0
- package/dist/code-intelligence/analysis/mincut/ModuleCouplingAnalyzer.js.map +1 -0
- package/dist/code-intelligence/analysis/mincut/index.d.ts +12 -0
- package/dist/code-intelligence/analysis/mincut/index.d.ts.map +1 -0
- package/dist/code-intelligence/analysis/mincut/index.js +20 -0
- package/dist/code-intelligence/analysis/mincut/index.js.map +1 -0
- package/dist/code-intelligence/analysis/mincut/types.d.ts +145 -0
- package/dist/code-intelligence/analysis/mincut/types.d.ts.map +1 -0
- package/dist/code-intelligence/analysis/mincut/types.js +16 -0
- package/dist/code-intelligence/analysis/mincut/types.js.map +1 -0
- package/dist/code-intelligence/chunking/ASTChunker.d.ts +1 -1
- package/dist/code-intelligence/chunking/ASTChunker.d.ts.map +1 -1
- package/dist/code-intelligence/chunking/ASTChunker.js +4 -4
- package/dist/code-intelligence/chunking/ASTChunker.js.map +1 -1
- package/dist/code-intelligence/graph/GraphBuilder.d.ts +120 -0
- package/dist/code-intelligence/graph/GraphBuilder.d.ts.map +1 -1
- package/dist/code-intelligence/graph/GraphBuilder.js +517 -0
- package/dist/code-intelligence/graph/GraphBuilder.js.map +1 -1
- package/dist/code-intelligence/orchestrator/CodeIntelligenceOrchestrator.d.ts.map +1 -1
- package/dist/code-intelligence/orchestrator/CodeIntelligenceOrchestrator.js +3 -3
- package/dist/code-intelligence/orchestrator/CodeIntelligenceOrchestrator.js.map +1 -1
- package/dist/code-intelligence/parser/{TreeSitterParser.d.ts → WebTreeSitterParser.d.ts} +45 -10
- package/dist/code-intelligence/parser/WebTreeSitterParser.d.ts.map +1 -0
- package/dist/code-intelligence/parser/{TreeSitterParser.js → WebTreeSitterParser.js} +147 -54
- package/dist/code-intelligence/parser/WebTreeSitterParser.js.map +1 -0
- package/dist/code-intelligence/parser/extractors/BaseExtractor.d.ts +12 -10
- package/dist/code-intelligence/parser/extractors/BaseExtractor.d.ts.map +1 -1
- package/dist/code-intelligence/parser/extractors/BaseExtractor.js +7 -3
- package/dist/code-intelligence/parser/extractors/BaseExtractor.js.map +1 -1
- package/dist/code-intelligence/parser/extractors/GoExtractor.d.ts +7 -5
- package/dist/code-intelligence/parser/extractors/GoExtractor.d.ts.map +1 -1
- package/dist/code-intelligence/parser/extractors/GoExtractor.js +2 -2
- package/dist/code-intelligence/parser/extractors/GoExtractor.js.map +1 -1
- package/dist/code-intelligence/parser/extractors/JavaScriptExtractor.d.ts +7 -5
- package/dist/code-intelligence/parser/extractors/JavaScriptExtractor.d.ts.map +1 -1
- package/dist/code-intelligence/parser/extractors/JavaScriptExtractor.js +2 -2
- package/dist/code-intelligence/parser/extractors/JavaScriptExtractor.js.map +1 -1
- package/dist/code-intelligence/parser/extractors/PythonExtractor.d.ts +7 -5
- package/dist/code-intelligence/parser/extractors/PythonExtractor.d.ts.map +1 -1
- package/dist/code-intelligence/parser/extractors/PythonExtractor.js +2 -2
- package/dist/code-intelligence/parser/extractors/PythonExtractor.js.map +1 -1
- package/dist/code-intelligence/parser/extractors/RustExtractor.d.ts +7 -5
- package/dist/code-intelligence/parser/extractors/RustExtractor.d.ts.map +1 -1
- package/dist/code-intelligence/parser/extractors/RustExtractor.js +2 -2
- package/dist/code-intelligence/parser/extractors/RustExtractor.js.map +1 -1
- package/dist/code-intelligence/parser/extractors/TypeScriptExtractor.d.ts +7 -5
- package/dist/code-intelligence/parser/extractors/TypeScriptExtractor.d.ts.map +1 -1
- package/dist/code-intelligence/parser/extractors/TypeScriptExtractor.js +2 -2
- package/dist/code-intelligence/parser/extractors/TypeScriptExtractor.js.map +1 -1
- package/dist/code-intelligence/parser/index.d.ts +7 -1
- package/dist/code-intelligence/parser/index.d.ts.map +1 -1
- package/dist/code-intelligence/parser/index.js +11 -3
- package/dist/code-intelligence/parser/index.js.map +1 -1
- package/dist/code-intelligence/service/CodeIntelligenceService.d.ts.map +1 -1
- package/dist/code-intelligence/service/CodeIntelligenceService.js +7 -5
- package/dist/code-intelligence/service/CodeIntelligenceService.js.map +1 -1
- package/dist/core/memory/HNSWVectorMemory.js +1 -1
- package/dist/coverage/CriticalPathDetector.d.ts +240 -0
- package/dist/coverage/CriticalPathDetector.d.ts.map +1 -0
- package/dist/coverage/CriticalPathDetector.js +388 -0
- package/dist/coverage/CriticalPathDetector.js.map +1 -0
- package/dist/coverage/index.d.ts +13 -0
- package/dist/coverage/index.d.ts.map +1 -0
- package/dist/coverage/index.js +16 -0
- package/dist/coverage/index.js.map +1 -0
- package/dist/fleet/topology/SPOFMonitor.d.ts +181 -0
- package/dist/fleet/topology/SPOFMonitor.d.ts.map +1 -0
- package/dist/fleet/topology/SPOFMonitor.js +286 -0
- package/dist/fleet/topology/SPOFMonitor.js.map +1 -0
- package/dist/fleet/topology/TopologyMinCutAnalyzer.d.ts +87 -0
- package/dist/fleet/topology/TopologyMinCutAnalyzer.d.ts.map +1 -0
- package/dist/fleet/topology/TopologyMinCutAnalyzer.js +472 -0
- package/dist/fleet/topology/TopologyMinCutAnalyzer.js.map +1 -0
- package/dist/fleet/topology/index.d.ts +13 -0
- package/dist/fleet/topology/index.d.ts.map +1 -0
- package/dist/fleet/topology/index.js +20 -0
- package/dist/fleet/topology/index.js.map +1 -0
- package/dist/fleet/topology/types.d.ts +139 -0
- package/dist/fleet/topology/types.d.ts.map +1 -0
- package/dist/fleet/topology/types.js +19 -0
- package/dist/fleet/topology/types.js.map +1 -0
- package/dist/mcp/handlers/test/test-execute-parallel.d.ts +34 -3
- package/dist/mcp/handlers/test/test-execute-parallel.d.ts.map +1 -1
- package/dist/mcp/handlers/test/test-execute-parallel.js +120 -5
- package/dist/mcp/handlers/test/test-execute-parallel.js.map +1 -1
- package/dist/mcp/server-instructions.d.ts +2 -2
- package/dist/mcp/server-instructions.d.ts.map +1 -1
- package/dist/mcp/server-instructions.js +2 -2
- package/dist/test/partition/MinCutPartitioner.d.ts +97 -0
- package/dist/test/partition/MinCutPartitioner.d.ts.map +1 -0
- package/dist/test/partition/MinCutPartitioner.js +459 -0
- package/dist/test/partition/MinCutPartitioner.js.map +1 -0
- package/dist/test/partition/RealTestExecutor.d.ts +86 -0
- package/dist/test/partition/RealTestExecutor.d.ts.map +1 -0
- package/dist/test/partition/RealTestExecutor.js +279 -0
- package/dist/test/partition/RealTestExecutor.js.map +1 -0
- package/dist/test/partition/TestDependencyAnalyzer.d.ts +75 -0
- package/dist/test/partition/TestDependencyAnalyzer.d.ts.map +1 -0
- package/dist/test/partition/TestDependencyAnalyzer.js +297 -0
- package/dist/test/partition/TestDependencyAnalyzer.js.map +1 -0
- package/dist/test/partition/index.d.ts +10 -0
- package/dist/test/partition/index.d.ts.map +1 -0
- package/dist/test/partition/index.js +26 -0
- package/dist/test/partition/index.js.map +1 -0
- package/dist/test/partition/types.d.ts +120 -0
- package/dist/test/partition/types.d.ts.map +1 -0
- package/dist/test/partition/types.js +21 -0
- package/dist/test/partition/types.js.map +1 -0
- package/package.json +4 -7
- package/dist/code-intelligence/parser/TreeSitterParser.d.ts.map +0 -1
- package/dist/code-intelligence/parser/TreeSitterParser.js.map +0 -1
|
@@ -0,0 +1,279 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* Real Test Executor
|
|
4
|
+
*
|
|
5
|
+
* Executes tests using actual Jest/Vitest via child_process.spawn.
|
|
6
|
+
* This replaces the fake "setTimeout + random pass/fail" mock
|
|
7
|
+
* with real test execution.
|
|
8
|
+
*/
|
|
9
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
10
|
+
if (k2 === undefined) k2 = k;
|
|
11
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
12
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
13
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
14
|
+
}
|
|
15
|
+
Object.defineProperty(o, k2, desc);
|
|
16
|
+
}) : (function(o, m, k, k2) {
|
|
17
|
+
if (k2 === undefined) k2 = k;
|
|
18
|
+
o[k2] = m[k];
|
|
19
|
+
}));
|
|
20
|
+
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
21
|
+
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
22
|
+
}) : function(o, v) {
|
|
23
|
+
o["default"] = v;
|
|
24
|
+
});
|
|
25
|
+
var __importStar = (this && this.__importStar) || (function () {
|
|
26
|
+
var ownKeys = function(o) {
|
|
27
|
+
ownKeys = Object.getOwnPropertyNames || function (o) {
|
|
28
|
+
var ar = [];
|
|
29
|
+
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
|
|
30
|
+
return ar;
|
|
31
|
+
};
|
|
32
|
+
return ownKeys(o);
|
|
33
|
+
};
|
|
34
|
+
return function (mod) {
|
|
35
|
+
if (mod && mod.__esModule) return mod;
|
|
36
|
+
var result = {};
|
|
37
|
+
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
|
|
38
|
+
__setModuleDefault(result, mod);
|
|
39
|
+
return result;
|
|
40
|
+
};
|
|
41
|
+
})();
|
|
42
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
43
|
+
exports.RealTestExecutor = void 0;
|
|
44
|
+
exports.aggregateBatchResults = aggregateBatchResults;
|
|
45
|
+
const child_process_1 = require("child_process");
|
|
46
|
+
const path = __importStar(require("path"));
|
|
47
|
+
const Logger_js_1 = require("../../utils/Logger.js");
|
|
48
|
+
const logger = Logger_js_1.Logger.getInstance();
|
|
49
|
+
const DEFAULT_CONFIG = {
|
|
50
|
+
command: 'npx',
|
|
51
|
+
runnerArgs: ['jest', '--json', '--testLocationInResults'],
|
|
52
|
+
timeout: 30000,
|
|
53
|
+
cwd: process.cwd(),
|
|
54
|
+
collectCoverage: false,
|
|
55
|
+
};
|
|
56
|
+
/**
|
|
57
|
+
* Executes real tests using Jest via child_process
|
|
58
|
+
*/
|
|
59
|
+
class RealTestExecutor {
|
|
60
|
+
constructor(config = {}) {
|
|
61
|
+
this.runningProcesses = new Map();
|
|
62
|
+
this.config = { ...DEFAULT_CONFIG, ...config };
|
|
63
|
+
}
|
|
64
|
+
/**
|
|
65
|
+
* Execute a single test file
|
|
66
|
+
*/
|
|
67
|
+
async executeTest(testFile, workerIndex = 0) {
|
|
68
|
+
const startTime = performance.now();
|
|
69
|
+
const absolutePath = path.resolve(testFile);
|
|
70
|
+
return new Promise((resolve) => {
|
|
71
|
+
const args = [
|
|
72
|
+
...this.config.runnerArgs,
|
|
73
|
+
absolutePath,
|
|
74
|
+
'--forceExit',
|
|
75
|
+
'--runInBand',
|
|
76
|
+
'--no-coverage',
|
|
77
|
+
this.config.collectCoverage ? '--coverage' : '',
|
|
78
|
+
].filter(Boolean);
|
|
79
|
+
let stdout = '';
|
|
80
|
+
let stderr = '';
|
|
81
|
+
let timedOut = false;
|
|
82
|
+
const proc = (0, child_process_1.spawn)(this.config.command, args, {
|
|
83
|
+
cwd: this.config.cwd,
|
|
84
|
+
env: {
|
|
85
|
+
...process.env,
|
|
86
|
+
...this.config.env,
|
|
87
|
+
NODE_OPTIONS: '--max-old-space-size=512',
|
|
88
|
+
FORCE_COLOR: '0', // Disable colors for JSON parsing
|
|
89
|
+
},
|
|
90
|
+
stdio: ['pipe', 'pipe', 'pipe'],
|
|
91
|
+
});
|
|
92
|
+
this.runningProcesses.set(testFile, proc);
|
|
93
|
+
const timeoutHandle = setTimeout(() => {
|
|
94
|
+
timedOut = true;
|
|
95
|
+
proc.kill('SIGTERM');
|
|
96
|
+
setTimeout(() => proc.kill('SIGKILL'), 1000);
|
|
97
|
+
}, this.config.timeout);
|
|
98
|
+
proc.stdout?.on('data', (data) => {
|
|
99
|
+
stdout += data.toString();
|
|
100
|
+
});
|
|
101
|
+
proc.stderr?.on('data', (data) => {
|
|
102
|
+
stderr += data.toString();
|
|
103
|
+
});
|
|
104
|
+
proc.on('close', (code) => {
|
|
105
|
+
clearTimeout(timeoutHandle);
|
|
106
|
+
this.runningProcesses.delete(testFile);
|
|
107
|
+
const duration = performance.now() - startTime;
|
|
108
|
+
if (timedOut) {
|
|
109
|
+
resolve({
|
|
110
|
+
testFile,
|
|
111
|
+
passed: false,
|
|
112
|
+
failed: 1,
|
|
113
|
+
total: 1,
|
|
114
|
+
duration,
|
|
115
|
+
error: `Test timed out after ${this.config.timeout}ms`,
|
|
116
|
+
workerIndex,
|
|
117
|
+
});
|
|
118
|
+
return;
|
|
119
|
+
}
|
|
120
|
+
// Try to parse Jest JSON output
|
|
121
|
+
const result = this.parseJestOutput(stdout, testFile, workerIndex, duration);
|
|
122
|
+
if (result) {
|
|
123
|
+
resolve(result);
|
|
124
|
+
return;
|
|
125
|
+
}
|
|
126
|
+
// Fallback: use exit code
|
|
127
|
+
resolve({
|
|
128
|
+
testFile,
|
|
129
|
+
passed: code === 0,
|
|
130
|
+
failed: code === 0 ? 0 : 1,
|
|
131
|
+
total: 1,
|
|
132
|
+
duration,
|
|
133
|
+
error: code !== 0 ? stderr || 'Test failed' : undefined,
|
|
134
|
+
output: stdout,
|
|
135
|
+
workerIndex,
|
|
136
|
+
});
|
|
137
|
+
});
|
|
138
|
+
proc.on('error', (error) => {
|
|
139
|
+
clearTimeout(timeoutHandle);
|
|
140
|
+
this.runningProcesses.delete(testFile);
|
|
141
|
+
resolve({
|
|
142
|
+
testFile,
|
|
143
|
+
passed: false,
|
|
144
|
+
failed: 1,
|
|
145
|
+
total: 1,
|
|
146
|
+
duration: performance.now() - startTime,
|
|
147
|
+
error: error.message,
|
|
148
|
+
workerIndex,
|
|
149
|
+
});
|
|
150
|
+
});
|
|
151
|
+
});
|
|
152
|
+
}
|
|
153
|
+
/**
|
|
154
|
+
* Execute a batch of test files sequentially on a single worker
|
|
155
|
+
*/
|
|
156
|
+
async executeBatch(testFiles, workerIndex = 0) {
|
|
157
|
+
const startTime = performance.now();
|
|
158
|
+
const results = [];
|
|
159
|
+
let passed = 0;
|
|
160
|
+
let failed = 0;
|
|
161
|
+
for (const testFile of testFiles) {
|
|
162
|
+
const result = await this.executeTest(testFile, workerIndex);
|
|
163
|
+
results.push(result);
|
|
164
|
+
if (result.passed) {
|
|
165
|
+
passed++;
|
|
166
|
+
}
|
|
167
|
+
else {
|
|
168
|
+
failed++;
|
|
169
|
+
}
|
|
170
|
+
logger.debug(`Worker ${workerIndex}: ${testFile} - ${result.passed ? 'PASS' : 'FAIL'} (${result.duration.toFixed(0)}ms)`);
|
|
171
|
+
}
|
|
172
|
+
return {
|
|
173
|
+
results,
|
|
174
|
+
totalDuration: performance.now() - startTime,
|
|
175
|
+
passed,
|
|
176
|
+
failed,
|
|
177
|
+
};
|
|
178
|
+
}
|
|
179
|
+
/**
|
|
180
|
+
* Execute multiple batches in parallel (one per worker)
|
|
181
|
+
*/
|
|
182
|
+
async executeParallel(batches, onProgress) {
|
|
183
|
+
const batchPromises = batches.map((batch, workerIndex) => this.executeBatchWithProgress(batch, workerIndex, onProgress));
|
|
184
|
+
return Promise.all(batchPromises);
|
|
185
|
+
}
|
|
186
|
+
/**
|
|
187
|
+
* Execute batch with progress callback
|
|
188
|
+
*/
|
|
189
|
+
async executeBatchWithProgress(testFiles, workerIndex, onProgress) {
|
|
190
|
+
const startTime = performance.now();
|
|
191
|
+
const results = [];
|
|
192
|
+
let passed = 0;
|
|
193
|
+
let failed = 0;
|
|
194
|
+
for (const testFile of testFiles) {
|
|
195
|
+
const result = await this.executeTest(testFile, workerIndex);
|
|
196
|
+
results.push(result);
|
|
197
|
+
if (result.passed) {
|
|
198
|
+
passed++;
|
|
199
|
+
}
|
|
200
|
+
else {
|
|
201
|
+
failed++;
|
|
202
|
+
}
|
|
203
|
+
if (onProgress) {
|
|
204
|
+
onProgress(workerIndex, result);
|
|
205
|
+
}
|
|
206
|
+
}
|
|
207
|
+
return {
|
|
208
|
+
results,
|
|
209
|
+
totalDuration: performance.now() - startTime,
|
|
210
|
+
passed,
|
|
211
|
+
failed,
|
|
212
|
+
};
|
|
213
|
+
}
|
|
214
|
+
/**
|
|
215
|
+
* Parse Jest JSON output to extract test results
|
|
216
|
+
*/
|
|
217
|
+
parseJestOutput(output, testFile, workerIndex, duration) {
|
|
218
|
+
try {
|
|
219
|
+
// Jest outputs JSON on a single line, find it
|
|
220
|
+
const lines = output.split('\n');
|
|
221
|
+
for (const line of lines) {
|
|
222
|
+
if (line.startsWith('{') && line.includes('"numTotalTests"')) {
|
|
223
|
+
const json = JSON.parse(line);
|
|
224
|
+
return {
|
|
225
|
+
testFile,
|
|
226
|
+
passed: json.numFailedTests === 0,
|
|
227
|
+
failed: json.numFailedTests || 0,
|
|
228
|
+
total: json.numTotalTests || 1,
|
|
229
|
+
duration: json.testResults?.[0]?.endTime
|
|
230
|
+
? json.testResults[0].endTime - json.testResults[0].startTime
|
|
231
|
+
: duration,
|
|
232
|
+
workerIndex,
|
|
233
|
+
};
|
|
234
|
+
}
|
|
235
|
+
}
|
|
236
|
+
}
|
|
237
|
+
catch {
|
|
238
|
+
// JSON parsing failed, fall through
|
|
239
|
+
}
|
|
240
|
+
return null;
|
|
241
|
+
}
|
|
242
|
+
/**
|
|
243
|
+
* Kill all running test processes
|
|
244
|
+
*/
|
|
245
|
+
killAll() {
|
|
246
|
+
for (const [testFile, proc] of this.runningProcesses) {
|
|
247
|
+
logger.warn(`Killing running test: ${testFile}`);
|
|
248
|
+
proc.kill('SIGTERM');
|
|
249
|
+
}
|
|
250
|
+
this.runningProcesses.clear();
|
|
251
|
+
}
|
|
252
|
+
/**
|
|
253
|
+
* Get number of currently running tests
|
|
254
|
+
*/
|
|
255
|
+
getRunningCount() {
|
|
256
|
+
return this.runningProcesses.size;
|
|
257
|
+
}
|
|
258
|
+
}
|
|
259
|
+
exports.RealTestExecutor = RealTestExecutor;
|
|
260
|
+
/**
|
|
261
|
+
* Aggregate results from multiple batch executions
|
|
262
|
+
*/
|
|
263
|
+
function aggregateBatchResults(batches) {
|
|
264
|
+
const allResults = batches.flatMap(b => b.results);
|
|
265
|
+
const totalDuration = allResults.reduce((sum, r) => sum + r.duration, 0);
|
|
266
|
+
const maxWorkerDuration = Math.max(...batches.map(b => b.totalDuration));
|
|
267
|
+
const passed = batches.reduce((sum, b) => sum + b.passed, 0);
|
|
268
|
+
const failed = batches.reduce((sum, b) => sum + b.failed, 0);
|
|
269
|
+
return {
|
|
270
|
+
allResults,
|
|
271
|
+
totalDuration,
|
|
272
|
+
maxWorkerDuration,
|
|
273
|
+
passed,
|
|
274
|
+
failed,
|
|
275
|
+
total: passed + failed,
|
|
276
|
+
parallelSpeedup: maxWorkerDuration > 0 ? totalDuration / maxWorkerDuration : 1,
|
|
277
|
+
};
|
|
278
|
+
}
|
|
279
|
+
//# sourceMappingURL=RealTestExecutor.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"RealTestExecutor.js","sourceRoot":"","sources":["../../../src/test/partition/RealTestExecutor.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Test Dependency Analyzer
|
|
3
|
+
*
|
|
4
|
+
* Uses ts-morph to parse actual TypeScript/JavaScript imports
|
|
5
|
+
* and build a real dependency graph for test partitioning.
|
|
6
|
+
*
|
|
7
|
+
* This replaces the fake "random 30% chance" dependency inference
|
|
8
|
+
* with actual AST-based import analysis.
|
|
9
|
+
*/
|
|
10
|
+
import { TestFile } from './types.js';
|
|
11
|
+
export interface DependencyAnalysisResult {
|
|
12
|
+
/** Map of test file path -> list of dependencies */
|
|
13
|
+
dependencies: Map<string, string[]>;
|
|
14
|
+
/** Map of test file path -> list of files that depend on it */
|
|
15
|
+
dependents: Map<string, string[]>;
|
|
16
|
+
/** Shared fixtures/helpers detected */
|
|
17
|
+
sharedFixtures: string[];
|
|
18
|
+
/** Time taken to analyze in ms */
|
|
19
|
+
analysisTimeMs: number;
|
|
20
|
+
}
|
|
21
|
+
export interface TestFileWithHistory {
|
|
22
|
+
path: string;
|
|
23
|
+
/** Average duration from historical data (ms) */
|
|
24
|
+
avgDuration?: number;
|
|
25
|
+
/** Flakiness rate from historical data (0-1) */
|
|
26
|
+
flakinessRate?: number;
|
|
27
|
+
/** Last N execution times */
|
|
28
|
+
recentDurations?: number[];
|
|
29
|
+
}
|
|
30
|
+
/**
|
|
31
|
+
* Analyzes test file dependencies using AST parsing
|
|
32
|
+
*/
|
|
33
|
+
export declare class TestDependencyAnalyzer {
|
|
34
|
+
private project;
|
|
35
|
+
private testDirectory;
|
|
36
|
+
private cachedAnalysis;
|
|
37
|
+
constructor(testDirectory?: string);
|
|
38
|
+
/**
|
|
39
|
+
* Analyze dependencies for all test files in the directory
|
|
40
|
+
*/
|
|
41
|
+
analyzeTestDependencies(testFiles: string[]): Promise<DependencyAnalysisResult>;
|
|
42
|
+
/**
|
|
43
|
+
* Extract all imports from a source file, resolving to relative paths
|
|
44
|
+
*/
|
|
45
|
+
private extractImports;
|
|
46
|
+
/**
|
|
47
|
+
* Resolve an import path to a normalized file path
|
|
48
|
+
*/
|
|
49
|
+
private resolveImportPath;
|
|
50
|
+
/**
|
|
51
|
+
* Check if a path looks like a shared fixture/helper
|
|
52
|
+
*/
|
|
53
|
+
private isSharedFixture;
|
|
54
|
+
/**
|
|
55
|
+
* Convert analysis result to TestFile array for partitioner
|
|
56
|
+
*/
|
|
57
|
+
toTestFiles(testPaths: string[], analysis: DependencyAnalysisResult, history?: Map<string, TestFileWithHistory>): TestFile[];
|
|
58
|
+
/**
|
|
59
|
+
* Estimate test duration based on file size and complexity
|
|
60
|
+
*/
|
|
61
|
+
private estimateDuration;
|
|
62
|
+
/**
|
|
63
|
+
* Infer priority from file path
|
|
64
|
+
*/
|
|
65
|
+
private inferPriority;
|
|
66
|
+
/**
|
|
67
|
+
* Infer tags from file path
|
|
68
|
+
*/
|
|
69
|
+
private inferTags;
|
|
70
|
+
/**
|
|
71
|
+
* Clear cached analysis
|
|
72
|
+
*/
|
|
73
|
+
clearCache(): void;
|
|
74
|
+
}
|
|
75
|
+
//# sourceMappingURL=TestDependencyAnalyzer.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"TestDependencyAnalyzer.d.ts","sourceRoot":"","sources":["../../../src/test/partition/TestDependencyAnalyzer.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAKH,OAAO,EAAE,QAAQ,EAAE,MAAM,YAAY,CAAC;AAKtC,MAAM,WAAW,wBAAwB;IACvC,oDAAoD;IACpD,YAAY,EAAE,GAAG,CAAC,MAAM,EAAE,MAAM,EAAE,CAAC,CAAC;IACpC,+DAA+D;IAC/D,UAAU,EAAE,GAAG,CAAC,MAAM,EAAE,MAAM,EAAE,CAAC,CAAC;IAClC,uCAAuC;IACvC,cAAc,EAAE,MAAM,EAAE,CAAC;IACzB,kCAAkC;IAClC,cAAc,EAAE,MAAM,CAAC;CACxB;AAED,MAAM,WAAW,mBAAmB;IAClC,IAAI,EAAE,MAAM,CAAC;IACb,iDAAiD;IACjD,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,gDAAgD;IAChD,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB,6BAA6B;IAC7B,eAAe,CAAC,EAAE,MAAM,EAAE,CAAC;CAC5B;AAED;;GAEG;AACH,qBAAa,sBAAsB;IACjC,OAAO,CAAC,OAAO,CAAU;IACzB,OAAO,CAAC,aAAa,CAAS;IAC9B,OAAO,CAAC,cAAc,CAAyC;gBAEnD,aAAa,GAAE,MAAgB;IAQ3C;;OAEG;IACU,uBAAuB,CAAC,SAAS,EAAE,MAAM,EAAE,GAAG,OAAO,CAAC,wBAAwB,CAAC;IA2E5F;;OAEG;IACH,OAAO,CAAC,cAAc;IAyCtB;;OAEG;IACH,OAAO,CAAC,iBAAiB;IAyBzB;;OAEG;IACH,OAAO,CAAC,eAAe;IAcvB;;OAEG;IACI,WAAW,CAChB,SAAS,EAAE,MAAM,EAAE,EACnB,QAAQ,EAAE,wBAAwB,EAClC,OAAO,CAAC,EAAE,GAAG,CAAC,MAAM,EAAE,mBAAmB,CAAC,GACzC,QAAQ,EAAE;IAgBb;;OAEG;IACH,OAAO,CAAC,gBAAgB;IAmBxB;;OAEG;IACH,OAAO,CAAC,aAAa;IAerB;;OAEG;IACH,OAAO,CAAC,SAAS;IAqBjB;;OAEG;IACI,UAAU,IAAI,IAAI;CAO1B"}
|
|
@@ -0,0 +1,297 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* Test Dependency Analyzer
|
|
4
|
+
*
|
|
5
|
+
* Uses ts-morph to parse actual TypeScript/JavaScript imports
|
|
6
|
+
* and build a real dependency graph for test partitioning.
|
|
7
|
+
*
|
|
8
|
+
* This replaces the fake "random 30% chance" dependency inference
|
|
9
|
+
* with actual AST-based import analysis.
|
|
10
|
+
*/
|
|
11
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
12
|
+
if (k2 === undefined) k2 = k;
|
|
13
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
14
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
15
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
16
|
+
}
|
|
17
|
+
Object.defineProperty(o, k2, desc);
|
|
18
|
+
}) : (function(o, m, k, k2) {
|
|
19
|
+
if (k2 === undefined) k2 = k;
|
|
20
|
+
o[k2] = m[k];
|
|
21
|
+
}));
|
|
22
|
+
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
23
|
+
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
24
|
+
}) : function(o, v) {
|
|
25
|
+
o["default"] = v;
|
|
26
|
+
});
|
|
27
|
+
var __importStar = (this && this.__importStar) || (function () {
|
|
28
|
+
var ownKeys = function(o) {
|
|
29
|
+
ownKeys = Object.getOwnPropertyNames || function (o) {
|
|
30
|
+
var ar = [];
|
|
31
|
+
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
|
|
32
|
+
return ar;
|
|
33
|
+
};
|
|
34
|
+
return ownKeys(o);
|
|
35
|
+
};
|
|
36
|
+
return function (mod) {
|
|
37
|
+
if (mod && mod.__esModule) return mod;
|
|
38
|
+
var result = {};
|
|
39
|
+
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
|
|
40
|
+
__setModuleDefault(result, mod);
|
|
41
|
+
return result;
|
|
42
|
+
};
|
|
43
|
+
})();
|
|
44
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
45
|
+
exports.TestDependencyAnalyzer = void 0;
|
|
46
|
+
const ts_morph_1 = require("ts-morph");
|
|
47
|
+
const path = __importStar(require("path"));
|
|
48
|
+
const fs = __importStar(require("fs"));
|
|
49
|
+
const Logger_js_1 = require("../../utils/Logger.js");
|
|
50
|
+
const logger = Logger_js_1.Logger.getInstance();
|
|
51
|
+
/**
|
|
52
|
+
* Analyzes test file dependencies using AST parsing
|
|
53
|
+
*/
|
|
54
|
+
class TestDependencyAnalyzer {
|
|
55
|
+
constructor(testDirectory = 'tests') {
|
|
56
|
+
this.cachedAnalysis = null;
|
|
57
|
+
this.testDirectory = testDirectory;
|
|
58
|
+
this.project = new ts_morph_1.Project({
|
|
59
|
+
skipAddingFilesFromTsConfig: true,
|
|
60
|
+
skipFileDependencyResolution: false,
|
|
61
|
+
});
|
|
62
|
+
}
|
|
63
|
+
/**
|
|
64
|
+
* Analyze dependencies for all test files in the directory
|
|
65
|
+
*/
|
|
66
|
+
async analyzeTestDependencies(testFiles) {
|
|
67
|
+
const startTime = performance.now();
|
|
68
|
+
const dependencies = new Map();
|
|
69
|
+
const dependents = new Map();
|
|
70
|
+
const sharedFixtures = new Set();
|
|
71
|
+
// Add all test files to the project
|
|
72
|
+
const sourceFiles = new Map();
|
|
73
|
+
for (const testPath of testFiles) {
|
|
74
|
+
try {
|
|
75
|
+
const absolutePath = path.resolve(testPath);
|
|
76
|
+
if (fs.existsSync(absolutePath)) {
|
|
77
|
+
const sourceFile = this.project.addSourceFileAtPath(absolutePath);
|
|
78
|
+
sourceFiles.set(testPath, sourceFile);
|
|
79
|
+
dependencies.set(testPath, []);
|
|
80
|
+
dependents.set(testPath, []);
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
catch (error) {
|
|
84
|
+
logger.warn(`Failed to parse test file: ${testPath}`, { error });
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
// Analyze imports for each test file
|
|
88
|
+
const testFileSet = new Set(testFiles);
|
|
89
|
+
for (const [testPath, sourceFile] of sourceFiles) {
|
|
90
|
+
const imports = this.extractImports(sourceFile, testPath);
|
|
91
|
+
for (const importPath of imports) {
|
|
92
|
+
// Check if this import is another test file
|
|
93
|
+
if (testFileSet.has(importPath)) {
|
|
94
|
+
// Direct test-to-test dependency
|
|
95
|
+
dependencies.get(testPath).push(importPath);
|
|
96
|
+
dependents.get(importPath).push(testPath);
|
|
97
|
+
}
|
|
98
|
+
else if (this.isSharedFixture(importPath)) {
|
|
99
|
+
// Shared fixture/helper
|
|
100
|
+
sharedFixtures.add(importPath);
|
|
101
|
+
// Tests sharing the same fixture are implicitly related
|
|
102
|
+
// Find other tests that import the same fixture
|
|
103
|
+
for (const [otherPath, otherSource] of sourceFiles) {
|
|
104
|
+
if (otherPath !== testPath) {
|
|
105
|
+
const otherImports = this.extractImports(otherSource, otherPath);
|
|
106
|
+
if (otherImports.includes(importPath)) {
|
|
107
|
+
// Both tests depend on same fixture - weak coupling
|
|
108
|
+
if (!dependencies.get(testPath).includes(otherPath)) {
|
|
109
|
+
dependencies.get(testPath).push(otherPath);
|
|
110
|
+
}
|
|
111
|
+
}
|
|
112
|
+
}
|
|
113
|
+
}
|
|
114
|
+
}
|
|
115
|
+
}
|
|
116
|
+
}
|
|
117
|
+
const result = {
|
|
118
|
+
dependencies,
|
|
119
|
+
dependents,
|
|
120
|
+
sharedFixtures: Array.from(sharedFixtures),
|
|
121
|
+
analysisTimeMs: performance.now() - startTime,
|
|
122
|
+
};
|
|
123
|
+
this.cachedAnalysis = result;
|
|
124
|
+
logger.info('Test dependency analysis complete', {
|
|
125
|
+
testCount: testFiles.length,
|
|
126
|
+
totalDependencies: Array.from(dependencies.values()).reduce((sum, deps) => sum + deps.length, 0),
|
|
127
|
+
sharedFixtures: sharedFixtures.size,
|
|
128
|
+
analysisTimeMs: result.analysisTimeMs.toFixed(2),
|
|
129
|
+
});
|
|
130
|
+
return result;
|
|
131
|
+
}
|
|
132
|
+
/**
|
|
133
|
+
* Extract all imports from a source file, resolving to relative paths
|
|
134
|
+
*/
|
|
135
|
+
extractImports(sourceFile, testPath) {
|
|
136
|
+
const imports = [];
|
|
137
|
+
const testDir = path.dirname(testPath);
|
|
138
|
+
for (const importDecl of sourceFile.getImportDeclarations()) {
|
|
139
|
+
const moduleSpecifier = importDecl.getModuleSpecifierValue();
|
|
140
|
+
// Skip node_modules imports
|
|
141
|
+
if (!moduleSpecifier.startsWith('.') && !moduleSpecifier.startsWith('/')) {
|
|
142
|
+
continue;
|
|
143
|
+
}
|
|
144
|
+
// Resolve relative import to absolute path
|
|
145
|
+
let resolvedPath = this.resolveImportPath(moduleSpecifier, testDir);
|
|
146
|
+
if (resolvedPath) {
|
|
147
|
+
imports.push(resolvedPath);
|
|
148
|
+
}
|
|
149
|
+
}
|
|
150
|
+
// Also check for dynamic imports
|
|
151
|
+
sourceFile.forEachDescendant((node) => {
|
|
152
|
+
if (ts_morph_1.Node.isCallExpression(node)) {
|
|
153
|
+
const expression = node.getExpression();
|
|
154
|
+
if (ts_morph_1.Node.isIdentifier(expression) && expression.getText() === 'require') {
|
|
155
|
+
const args = node.getArguments();
|
|
156
|
+
if (args.length > 0 && ts_morph_1.Node.isStringLiteral(args[0])) {
|
|
157
|
+
const moduleSpecifier = args[0].getLiteralValue();
|
|
158
|
+
if (moduleSpecifier.startsWith('.') || moduleSpecifier.startsWith('/')) {
|
|
159
|
+
const resolvedPath = this.resolveImportPath(moduleSpecifier, testDir);
|
|
160
|
+
if (resolvedPath) {
|
|
161
|
+
imports.push(resolvedPath);
|
|
162
|
+
}
|
|
163
|
+
}
|
|
164
|
+
}
|
|
165
|
+
}
|
|
166
|
+
}
|
|
167
|
+
});
|
|
168
|
+
return imports;
|
|
169
|
+
}
|
|
170
|
+
/**
|
|
171
|
+
* Resolve an import path to a normalized file path
|
|
172
|
+
*/
|
|
173
|
+
resolveImportPath(moduleSpecifier, fromDir) {
|
|
174
|
+
// Handle .js extension in imports (ESM style)
|
|
175
|
+
let cleanSpecifier = moduleSpecifier.replace(/\.js$/, '');
|
|
176
|
+
// Try different extensions
|
|
177
|
+
const extensions = ['.ts', '.tsx', '.js', '.jsx', ''];
|
|
178
|
+
const basePath = path.resolve(fromDir, cleanSpecifier);
|
|
179
|
+
for (const ext of extensions) {
|
|
180
|
+
const fullPath = basePath + ext;
|
|
181
|
+
if (fs.existsSync(fullPath)) {
|
|
182
|
+
// Return path relative to project root
|
|
183
|
+
return path.relative(process.cwd(), fullPath);
|
|
184
|
+
}
|
|
185
|
+
// Also try index files
|
|
186
|
+
const indexPath = path.join(basePath, `index${ext}`);
|
|
187
|
+
if (fs.existsSync(indexPath)) {
|
|
188
|
+
return path.relative(process.cwd(), indexPath);
|
|
189
|
+
}
|
|
190
|
+
}
|
|
191
|
+
return null;
|
|
192
|
+
}
|
|
193
|
+
/**
|
|
194
|
+
* Check if a path looks like a shared fixture/helper
|
|
195
|
+
*/
|
|
196
|
+
isSharedFixture(importPath) {
|
|
197
|
+
const lowerPath = importPath.toLowerCase();
|
|
198
|
+
return (lowerPath.includes('fixture') ||
|
|
199
|
+
lowerPath.includes('helper') ||
|
|
200
|
+
lowerPath.includes('mock') ||
|
|
201
|
+
lowerPath.includes('stub') ||
|
|
202
|
+
lowerPath.includes('factory') ||
|
|
203
|
+
lowerPath.includes('setup') ||
|
|
204
|
+
lowerPath.includes('utils') ||
|
|
205
|
+
lowerPath.includes('__mocks__'));
|
|
206
|
+
}
|
|
207
|
+
/**
|
|
208
|
+
* Convert analysis result to TestFile array for partitioner
|
|
209
|
+
*/
|
|
210
|
+
toTestFiles(testPaths, analysis, history) {
|
|
211
|
+
return testPaths.map(testPath => {
|
|
212
|
+
const historyData = history?.get(testPath);
|
|
213
|
+
return {
|
|
214
|
+
path: testPath,
|
|
215
|
+
estimatedDuration: historyData?.avgDuration ?? this.estimateDuration(testPath),
|
|
216
|
+
dependencies: analysis.dependencies.get(testPath) ?? [],
|
|
217
|
+
dependents: analysis.dependents.get(testPath) ?? [],
|
|
218
|
+
flakinessScore: historyData?.flakinessRate ?? 0,
|
|
219
|
+
priority: this.inferPriority(testPath),
|
|
220
|
+
tags: this.inferTags(testPath),
|
|
221
|
+
};
|
|
222
|
+
});
|
|
223
|
+
}
|
|
224
|
+
/**
|
|
225
|
+
* Estimate test duration based on file size and complexity
|
|
226
|
+
*/
|
|
227
|
+
estimateDuration(testPath) {
|
|
228
|
+
try {
|
|
229
|
+
const absolutePath = path.resolve(testPath);
|
|
230
|
+
if (!fs.existsSync(absolutePath)) {
|
|
231
|
+
return 100; // Default 100ms
|
|
232
|
+
}
|
|
233
|
+
const content = fs.readFileSync(absolutePath, 'utf-8');
|
|
234
|
+
const lines = content.split('\n').length;
|
|
235
|
+
const testCount = (content.match(/\b(it|test)\s*\(/g) || []).length;
|
|
236
|
+
const describeCount = (content.match(/\bdescribe\s*\(/g) || []).length;
|
|
237
|
+
// Estimate: 50ms base + 20ms per test + 5ms per 100 lines
|
|
238
|
+
return 50 + (testCount * 20) + (lines / 100 * 5);
|
|
239
|
+
}
|
|
240
|
+
catch {
|
|
241
|
+
return 100;
|
|
242
|
+
}
|
|
243
|
+
}
|
|
244
|
+
/**
|
|
245
|
+
* Infer priority from file path
|
|
246
|
+
*/
|
|
247
|
+
inferPriority(testPath) {
|
|
248
|
+
const lowerPath = testPath.toLowerCase();
|
|
249
|
+
if (lowerPath.includes('critical') || lowerPath.includes('smoke')) {
|
|
250
|
+
return 'critical';
|
|
251
|
+
}
|
|
252
|
+
if (lowerPath.includes('integration') || lowerPath.includes('e2e')) {
|
|
253
|
+
return 'high';
|
|
254
|
+
}
|
|
255
|
+
if (lowerPath.includes('unit')) {
|
|
256
|
+
return 'medium';
|
|
257
|
+
}
|
|
258
|
+
return 'medium';
|
|
259
|
+
}
|
|
260
|
+
/**
|
|
261
|
+
* Infer tags from file path
|
|
262
|
+
*/
|
|
263
|
+
inferTags(testPath) {
|
|
264
|
+
const tags = [];
|
|
265
|
+
const pathParts = testPath.toLowerCase().split(/[/\\]/);
|
|
266
|
+
// Extract directory names as tags
|
|
267
|
+
for (const part of pathParts) {
|
|
268
|
+
if (part === 'tests' || part === 'test' || part === '__tests__')
|
|
269
|
+
continue;
|
|
270
|
+
if (part.endsWith('.ts') || part.endsWith('.js'))
|
|
271
|
+
continue;
|
|
272
|
+
if (part.length > 0 && part.length < 30) {
|
|
273
|
+
tags.push(part);
|
|
274
|
+
}
|
|
275
|
+
}
|
|
276
|
+
// Add test type tags
|
|
277
|
+
if (testPath.includes('unit'))
|
|
278
|
+
tags.push('unit');
|
|
279
|
+
if (testPath.includes('integration'))
|
|
280
|
+
tags.push('integration');
|
|
281
|
+
if (testPath.includes('e2e'))
|
|
282
|
+
tags.push('e2e');
|
|
283
|
+
return [...new Set(tags)]; // Dedupe
|
|
284
|
+
}
|
|
285
|
+
/**
|
|
286
|
+
* Clear cached analysis
|
|
287
|
+
*/
|
|
288
|
+
clearCache() {
|
|
289
|
+
this.cachedAnalysis = null;
|
|
290
|
+
this.project = new ts_morph_1.Project({
|
|
291
|
+
skipAddingFilesFromTsConfig: true,
|
|
292
|
+
skipFileDependencyResolution: false,
|
|
293
|
+
});
|
|
294
|
+
}
|
|
295
|
+
}
|
|
296
|
+
exports.TestDependencyAnalyzer = TestDependencyAnalyzer;
|
|
297
|
+
//# sourceMappingURL=TestDependencyAnalyzer.js.map
|