vibecodingmachine-core 2026.2.20-438 → 2026.2.26-1739
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 +240 -0
- package/package.json +10 -2
- package/src/agents/Agent.js +300 -0
- package/src/agents/AgentAdditionService.js +311 -0
- package/src/agents/AgentCheckService.js +690 -0
- package/src/agents/AgentInstallationService.js +140 -0
- package/src/agents/AgentSetupService.js +467 -0
- package/src/agents/AgentStatus.js +183 -0
- package/src/agents/AgentVerificationService.js +634 -0
- package/src/agents/ConfigurationSchemaValidator.js +543 -0
- package/src/agents/EnvironmentConfigurationManager.js +602 -0
- package/src/agents/InstallationErrorHandler.js +372 -0
- package/src/agents/InstallationLog.js +363 -0
- package/src/agents/InstallationMethod.js +510 -0
- package/src/agents/InstallationOrchestrator.js +352 -0
- package/src/agents/InstallationProgressReporter.js +372 -0
- package/src/agents/InstallationRetryManager.js +322 -0
- package/src/agents/InstallationType.js +254 -0
- package/src/agents/OperationTypes.js +310 -0
- package/src/agents/PerformanceMetricsCollector.js +493 -0
- package/src/agents/SecurityValidationService.js +534 -0
- package/src/agents/VerificationTest.js +354 -0
- package/src/agents/VerificationType.js +226 -0
- package/src/agents/WindowsPermissionHandler.js +518 -0
- package/src/agents/config/AgentConfigManager.js +393 -0
- package/src/agents/config/AgentDefaultsRegistry.js +373 -0
- package/src/agents/config/ConfigValidator.js +281 -0
- package/src/agents/discovery/AgentDiscoveryService.js +707 -0
- package/src/agents/logging/AgentLogger.js +511 -0
- package/src/agents/status/AgentStatusManager.js +481 -0
- package/src/agents/storage/FileManager.js +454 -0
- package/src/agents/verification/AgentCommunicationTester.js +474 -0
- package/src/agents/verification/BaseVerifier.js +430 -0
- package/src/agents/verification/CommandVerifier.js +480 -0
- package/src/agents/verification/FileOperationVerifier.js +453 -0
- package/src/agents/verification/ResultAnalyzer.js +707 -0
- package/src/agents/verification/TestRequirementManager.js +495 -0
- package/src/agents/verification/VerificationRunner.js +433 -0
- package/src/agents/windows/BaseWindowsInstaller.js +441 -0
- package/src/agents/windows/ChocolateyInstaller.js +509 -0
- package/src/agents/windows/DirectInstaller.js +443 -0
- package/src/agents/windows/InstallerFactory.js +391 -0
- package/src/agents/windows/NpmInstaller.js +505 -0
- package/src/agents/windows/PowerShellInstaller.js +458 -0
- package/src/agents/windows/WinGetInstaller.js +390 -0
- package/src/analysis/analysis-reporter.js +132 -0
- package/src/analysis/boundary-detector.js +712 -0
- package/src/analysis/categorizer.js +340 -0
- package/src/analysis/codebase-scanner.js +384 -0
- package/src/analysis/line-counter.js +513 -0
- package/src/analysis/priority-calculator.js +679 -0
- package/src/analysis/report/analysis-report.js +250 -0
- package/src/analysis/report/package-analyzer.js +278 -0
- package/src/analysis/report/recommendation-generator.js +382 -0
- package/src/analysis/report/statistics-generator.js +515 -0
- package/src/analysis/reports/analysis-report-model.js +101 -0
- package/src/analysis/reports/recommendation-generator.js +283 -0
- package/src/analysis/reports/report-generators.js +191 -0
- package/src/analysis/reports/statistics-calculator.js +231 -0
- package/src/analysis/reports/trend-analyzer.js +219 -0
- package/src/analysis/strategy-generator.js +814 -0
- package/src/auto-mode/AutoModeBusinessLogic.js +836 -0
- package/src/config/refactoring-config.js +307 -0
- package/src/health-tracking/json-storage.js +38 -2
- package/src/ide-integration/applescript-manager-core.js +233 -0
- package/src/ide-integration/applescript-manager.cjs +357 -28
- package/src/ide-integration/applescript-manager.js +89 -3599
- package/src/ide-integration/cdp-manager.js +306 -0
- package/src/ide-integration/claude-code-cli-manager.cjs +1 -1
- package/src/ide-integration/continuation-handler.js +337 -0
- package/src/ide-integration/ide-status-checker.js +292 -0
- package/src/ide-integration/macos-ide-manager.js +627 -0
- package/src/ide-integration/macos-text-sender.js +528 -0
- package/src/ide-integration/response-reader.js +548 -0
- package/src/ide-integration/windows-automation-manager.js +121 -0
- package/src/ide-integration/windows-ide-manager.js +373 -0
- package/src/index.cjs +25 -3
- package/src/index.js +15 -1
- package/src/llm/direct-llm-manager.cjs +90 -2
- package/src/models/compliance-report.js +538 -0
- package/src/models/file-analysis.js +681 -0
- package/src/models/refactoring-plan.js +770 -0
- package/src/monitoring/alert-system.js +834 -0
- package/src/monitoring/compliance-progress-tracker.js +437 -0
- package/src/monitoring/continuous-scan-notifications.js +661 -0
- package/src/monitoring/continuous-scanner.js +279 -0
- package/src/monitoring/file-monitor/file-analyzer.js +262 -0
- package/src/monitoring/file-monitor/file-monitor.js +237 -0
- package/src/monitoring/file-monitor/watcher.js +194 -0
- package/src/monitoring/file-monitor.js +17 -0
- package/src/monitoring/notification-manager.js +437 -0
- package/src/monitoring/scanner-core.js +368 -0
- package/src/monitoring/scanner-events.js +214 -0
- package/src/monitoring/violation-notification-system.js +515 -0
- package/src/refactoring/boundaries/cohesion-analyzer.js +316 -0
- package/src/refactoring/boundaries/extraction-result.js +285 -0
- package/src/refactoring/boundaries/extraction-strategies.js +392 -0
- package/src/refactoring/boundaries/module-boundary.js +209 -0
- package/src/refactoring/boundary/boundary-detector.js +741 -0
- package/src/refactoring/boundary/boundary-types.js +405 -0
- package/src/refactoring/boundary/extraction-strategies.js +554 -0
- package/src/refactoring/boundary-extraction-result.js +77 -0
- package/src/refactoring/boundary-extraction-strategies.js +330 -0
- package/src/refactoring/boundary-extractor.js +384 -0
- package/src/refactoring/boundary-types.js +46 -0
- package/src/refactoring/circular/circular-dependency.js +88 -0
- package/src/refactoring/circular/cycle-detection.js +147 -0
- package/src/refactoring/circular/dependency-node.js +82 -0
- package/src/refactoring/circular/dependency-result.js +107 -0
- package/src/refactoring/circular/dependency-types.js +58 -0
- package/src/refactoring/circular/graph-builder.js +213 -0
- package/src/refactoring/circular/resolution-strategy.js +72 -0
- package/src/refactoring/circular/strategy-generator.js +229 -0
- package/src/refactoring/circular-dependency-resolver-original.js +809 -0
- package/src/refactoring/circular-dependency-resolver.js +200 -0
- package/src/refactoring/code-mover.js +761 -0
- package/src/refactoring/file-splitter.js +696 -0
- package/src/refactoring/functionality-validator.js +816 -0
- package/src/refactoring/import-manager.js +774 -0
- package/src/refactoring/module-boundary.js +107 -0
- package/src/refactoring/refactoring-executor.js +672 -0
- package/src/refactoring/refactoring-rollback.js +614 -0
- package/src/refactoring/test-validator.js +631 -0
- package/src/requirement-management/default-requirement-manager.js +321 -0
- package/src/requirement-management/requirement-file-parser.js +159 -0
- package/src/requirement-management/requirement-sequencer.js +221 -0
- package/src/rui/commands/AgentCommandParser.js +600 -0
- package/src/rui/commands/AgentCommands.js +487 -0
- package/src/rui/commands/AgentResponseFormatter.js +832 -0
- package/src/scripts/verify-full-compliance.js +269 -0
- package/src/sync/sync-engine-core.js +1 -0
- package/src/sync/sync-engine-remote-handlers.js +135 -0
- package/src/task-generation/automated-task-generator.js +351 -0
- package/src/task-generation/prioritizer.js +287 -0
- package/src/task-generation/task-list-updater.js +215 -0
- package/src/task-generation/task-management-integration.js +480 -0
- package/src/task-generation/task-manager-integration.js +270 -0
- package/src/task-generation/violation-task-generator.js +474 -0
- package/src/task-management/continuous-scan-integration.js +342 -0
- package/src/timeout-management/index.js +12 -3
- package/src/timeout-management/response-time-tracker.js +167 -0
- package/src/timeout-management/timeout-calculator.js +159 -0
- package/src/timeout-management/timeout-config-manager.js +172 -0
- package/src/utils/ast-analyzer.js +417 -0
- package/src/utils/current-requirement-manager.js +276 -0
- package/src/utils/current-requirement-operations.js +472 -0
- package/src/utils/dependency-mapper.js +456 -0
- package/src/utils/download-with-progress.js +4 -2
- package/src/utils/electron-update-checker.js +4 -1
- package/src/utils/file-size-analyzer.js +272 -0
- package/src/utils/import-updater.js +280 -0
- package/src/utils/refactoring-tools.js +512 -0
- package/src/utils/report-generator.js +569 -0
- package/src/utils/reports/report-analysis.js +218 -0
- package/src/utils/reports/report-types.js +55 -0
- package/src/utils/reports/summary-generators.js +102 -0
- package/src/utils/requirement-file-management.js +157 -0
- package/src/utils/requirement-helpers/requirement-file-ops.js +392 -0
- package/src/utils/requirement-helpers/requirement-mover.js +414 -0
- package/src/utils/requirement-helpers/requirement-parser.js +326 -0
- package/src/utils/requirement-helpers/requirement-status.js +320 -0
- package/src/utils/requirement-helpers-new.js +55 -0
- package/src/utils/requirement-helpers-refactored.js +367 -0
- package/src/utils/requirement-helpers.js +291 -1191
- package/src/utils/requirement-movement-operations.js +450 -0
- package/src/utils/requirement-movement.js +312 -0
- package/src/utils/requirement-parsing-helpers.js +56 -0
- package/src/utils/requirement-statistics.js +200 -0
- package/src/utils/requirement-text-utils.js +58 -0
- package/src/utils/rollback/rollback-handlers.js +125 -0
- package/src/utils/rollback/rollback-operation.js +63 -0
- package/src/utils/rollback/rollback-recorder.js +166 -0
- package/src/utils/rollback/rollback-state-manager.js +175 -0
- package/src/utils/rollback/rollback-types.js +33 -0
- package/src/utils/rollback/rollback-utils.js +110 -0
- package/src/utils/rollback-manager-original.js +569 -0
- package/src/utils/rollback-manager.js +202 -0
- package/src/utils/smoke-test-cli.js +362 -0
- package/src/utils/smoke-test-gui.js +351 -0
- package/src/utils/smoke-test-orchestrator.js +321 -0
- package/src/utils/smoke-test-runner.js +60 -0
- package/src/utils/smoke-test-web.js +347 -0
- package/src/utils/specification-helpers.js +39 -13
- package/src/utils/specification-migration.js +97 -0
- package/src/utils/test-runner.js +579 -0
- package/src/utils/validation-framework.js +518 -0
- package/src/validation/compliance-analyzer.js +197 -0
- package/src/validation/compliance-report-generator.js +343 -0
- package/src/validation/compliance-reporter.js +711 -0
- package/src/validation/compliance-rules.js +127 -0
- package/src/validation/constitution-validator-new.js +196 -0
- package/src/validation/constitution-validator.js +17 -0
- package/src/validation/file-validators.js +170 -0
- package/src/validation/line-limit/file-analyzer.js +201 -0
- package/src/validation/line-limit/line-limit-validator.js +208 -0
- package/src/validation/line-limit/validation-result.js +144 -0
- package/src/validation/line-limit-core.js +225 -0
- package/src/validation/line-limit-reporter.js +134 -0
- package/src/validation/line-limit-result.js +125 -0
- package/src/validation/line-limit-validator.js +41 -0
- package/src/validation/metrics-calculator.js +660 -0
- package/src/sync/sync-engine-backup.js +0 -559
|
@@ -0,0 +1,579 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Test Runner Integration
|
|
3
|
+
*
|
|
4
|
+
* Integrates with existing test infrastructure to validate refactoring operations.
|
|
5
|
+
* Provides test execution, result analysis, and regression detection.
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
const { execSync, spawn } = require('child_process');
|
|
9
|
+
const fs = require('fs');
|
|
10
|
+
const path = require('path');
|
|
11
|
+
|
|
12
|
+
/**
|
|
13
|
+
* Test result types
|
|
14
|
+
*/
|
|
15
|
+
const TEST_RESULT_TYPES = {
|
|
16
|
+
PASS: 'pass',
|
|
17
|
+
FAIL: 'fail',
|
|
18
|
+
SKIP: 'skip',
|
|
19
|
+
ERROR: 'error'
|
|
20
|
+
};
|
|
21
|
+
|
|
22
|
+
/**
|
|
23
|
+
* Test runner class
|
|
24
|
+
*/
|
|
25
|
+
class TestRunner {
|
|
26
|
+
constructor(options = {}) {
|
|
27
|
+
this.options = {
|
|
28
|
+
testCommand: 'npm test',
|
|
29
|
+
testDir: 'tests',
|
|
30
|
+
timeout: 300000, // 5 minutes
|
|
31
|
+
retries: 0,
|
|
32
|
+
verbose: false,
|
|
33
|
+
...options
|
|
34
|
+
};
|
|
35
|
+
|
|
36
|
+
this.baselineResults = null;
|
|
37
|
+
this.currentResults = null;
|
|
38
|
+
this.regressionDetected = false;
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
/**
|
|
42
|
+
* Run baseline tests before refactoring
|
|
43
|
+
*/
|
|
44
|
+
async runBaseline() {
|
|
45
|
+
console.log('Running baseline tests...');
|
|
46
|
+
|
|
47
|
+
try {
|
|
48
|
+
const results = await this.executeTests();
|
|
49
|
+
this.baselineResults = results;
|
|
50
|
+
|
|
51
|
+
console.log(`Baseline completed: ${results.passed}/${results.total} passed`);
|
|
52
|
+
return results;
|
|
53
|
+
} catch (error) {
|
|
54
|
+
throw new Error(`Baseline test execution failed: ${error.message}`);
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
/**
|
|
59
|
+
* Run tests after refactoring
|
|
60
|
+
*/
|
|
61
|
+
async runValidation() {
|
|
62
|
+
if (!this.baselineResults) {
|
|
63
|
+
throw new Error('Baseline results not available. Run baseline first.');
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
console.log('Running validation tests...');
|
|
67
|
+
|
|
68
|
+
try {
|
|
69
|
+
const results = await this.executeTests();
|
|
70
|
+
this.currentResults = results;
|
|
71
|
+
|
|
72
|
+
// Compare with baseline
|
|
73
|
+
const comparison = this.compareResults(this.baselineResults, results);
|
|
74
|
+
this.regressionDetected = comparison.hasRegressions;
|
|
75
|
+
|
|
76
|
+
console.log(`Validation completed: ${results.passed}/${results.total} passed`);
|
|
77
|
+
|
|
78
|
+
if (this.regressionDetected) {
|
|
79
|
+
console.log(`⚠️ Regressions detected: ${comparison.regressions.length}`);
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
return {
|
|
83
|
+
results,
|
|
84
|
+
comparison,
|
|
85
|
+
hasRegressions: this.regressionDetected
|
|
86
|
+
};
|
|
87
|
+
} catch (error) {
|
|
88
|
+
throw new Error(`Validation test execution failed: ${error.message}`);
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
/**
|
|
93
|
+
* Execute tests and collect results
|
|
94
|
+
*/
|
|
95
|
+
async executeTests() {
|
|
96
|
+
return new Promise((resolve, reject) => {
|
|
97
|
+
const startTime = Date.now();
|
|
98
|
+
let output = '';
|
|
99
|
+
let errorOutput = '';
|
|
100
|
+
|
|
101
|
+
try {
|
|
102
|
+
// Use npm test with JSON reporter if available
|
|
103
|
+
const testProcess = spawn('npm', ['test', '--', '--json', '--verbose'], {
|
|
104
|
+
stdio: ['pipe', 'pipe', 'pipe'],
|
|
105
|
+
cwd: process.cwd()
|
|
106
|
+
});
|
|
107
|
+
|
|
108
|
+
testProcess.stdout.on('data', (data) => {
|
|
109
|
+
output += data.toString();
|
|
110
|
+
if (this.options.verbose) {
|
|
111
|
+
process.stdout.write(data);
|
|
112
|
+
}
|
|
113
|
+
});
|
|
114
|
+
|
|
115
|
+
testProcess.stderr.on('data', (data) => {
|
|
116
|
+
errorOutput += data.toString();
|
|
117
|
+
if (this.options.verbose) {
|
|
118
|
+
process.stderr.write(data);
|
|
119
|
+
}
|
|
120
|
+
});
|
|
121
|
+
|
|
122
|
+
testProcess.on('close', (code) => {
|
|
123
|
+
const duration = Date.now() - startTime;
|
|
124
|
+
|
|
125
|
+
try {
|
|
126
|
+
const results = this.parseTestOutput(output, errorOutput, code, duration);
|
|
127
|
+
resolve(results);
|
|
128
|
+
} catch (parseError) {
|
|
129
|
+
// Fallback to basic parsing
|
|
130
|
+
const fallbackResults = this.parseBasicOutput(output, errorOutput, code, duration);
|
|
131
|
+
resolve(fallbackResults);
|
|
132
|
+
}
|
|
133
|
+
});
|
|
134
|
+
|
|
135
|
+
testProcess.on('error', (error) => {
|
|
136
|
+
reject(new Error(`Test process failed: ${error.message}`));
|
|
137
|
+
});
|
|
138
|
+
|
|
139
|
+
// Set timeout
|
|
140
|
+
if (this.options.timeout) {
|
|
141
|
+
setTimeout(() => {
|
|
142
|
+
testProcess.kill('SIGKILL');
|
|
143
|
+
reject(new Error(`Test execution timed out after ${this.options.timeout}ms`));
|
|
144
|
+
}, this.options.timeout);
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
} catch (error) {
|
|
148
|
+
reject(new Error(`Failed to start test process: ${error.message}`));
|
|
149
|
+
}
|
|
150
|
+
});
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
/**
|
|
154
|
+
* Parse test output (Jest JSON format)
|
|
155
|
+
*/
|
|
156
|
+
parseTestOutput(stdout, stderr, exitCode, duration) {
|
|
157
|
+
try {
|
|
158
|
+
// Try to parse JSON output
|
|
159
|
+
const jsonMatch = stdout.match(/\{[\s\S]*\}/);
|
|
160
|
+
if (jsonMatch) {
|
|
161
|
+
const jsonData = JSON.parse(jsonMatch[0]);
|
|
162
|
+
return this.parseJestResults(jsonData, duration);
|
|
163
|
+
}
|
|
164
|
+
} catch (error) {
|
|
165
|
+
// JSON parsing failed, fall back to basic parsing
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
return this.parseBasicOutput(stdout, stderr, exitCode, duration);
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
/**
|
|
172
|
+
* Parse Jest results
|
|
173
|
+
*/
|
|
174
|
+
parseJestResults(jsonData, duration) {
|
|
175
|
+
const results = {
|
|
176
|
+
total: 0,
|
|
177
|
+
passed: 0,
|
|
178
|
+
failed: 0,
|
|
179
|
+
skipped: 0,
|
|
180
|
+
errors: 0,
|
|
181
|
+
duration,
|
|
182
|
+
suites: [],
|
|
183
|
+
tests: [],
|
|
184
|
+
coverage: null
|
|
185
|
+
};
|
|
186
|
+
|
|
187
|
+
if (jsonData.numTotalTests) {
|
|
188
|
+
results.total = jsonData.numTotalTests;
|
|
189
|
+
results.passed = jsonData.numPassedTests || 0;
|
|
190
|
+
results.failed = jsonData.numFailedTests || 0;
|
|
191
|
+
results.skipped = jsonData.numPendingTests || 0;
|
|
192
|
+
}
|
|
193
|
+
|
|
194
|
+
if (jsonData.testResults) {
|
|
195
|
+
for (const testSuite of jsonData.testResults) {
|
|
196
|
+
const suite = {
|
|
197
|
+
name: testSuite.name,
|
|
198
|
+
file: testSuite.testFilePath,
|
|
199
|
+
passed: 0,
|
|
200
|
+
failed: 0,
|
|
201
|
+
skipped: 0,
|
|
202
|
+
duration: testSuite.perfStats?.end || 0,
|
|
203
|
+
tests: []
|
|
204
|
+
};
|
|
205
|
+
|
|
206
|
+
if (testSuite.assertionResults) {
|
|
207
|
+
for (const test of testSuite.assertionResults) {
|
|
208
|
+
const testResult = {
|
|
209
|
+
title: test.title,
|
|
210
|
+
fullName: test.fullName,
|
|
211
|
+
status: test.status,
|
|
212
|
+
duration: test.duration || 0,
|
|
213
|
+
failureMessages: test.failureMessages || []
|
|
214
|
+
};
|
|
215
|
+
|
|
216
|
+
suite.tests.push(testResult);
|
|
217
|
+
results.tests.push(testResult);
|
|
218
|
+
|
|
219
|
+
switch (test.status) {
|
|
220
|
+
case 'passed':
|
|
221
|
+
suite.passed++;
|
|
222
|
+
break;
|
|
223
|
+
case 'failed':
|
|
224
|
+
suite.failed++;
|
|
225
|
+
break;
|
|
226
|
+
case 'pending':
|
|
227
|
+
suite.skipped++;
|
|
228
|
+
break;
|
|
229
|
+
}
|
|
230
|
+
}
|
|
231
|
+
}
|
|
232
|
+
|
|
233
|
+
results.suites.push(suite);
|
|
234
|
+
}
|
|
235
|
+
}
|
|
236
|
+
|
|
237
|
+
if (jsonData.coverageMap) {
|
|
238
|
+
results.coverage = this.parseCoverageData(jsonData.coverageMap);
|
|
239
|
+
}
|
|
240
|
+
|
|
241
|
+
return results;
|
|
242
|
+
}
|
|
243
|
+
|
|
244
|
+
/**
|
|
245
|
+
* Parse basic test output
|
|
246
|
+
*/
|
|
247
|
+
parseBasicOutput(stdout, stderr, exitCode, duration) {
|
|
248
|
+
const results = {
|
|
249
|
+
total: 0,
|
|
250
|
+
passed: 0,
|
|
251
|
+
failed: 0,
|
|
252
|
+
skipped: 0,
|
|
253
|
+
errors: 0,
|
|
254
|
+
duration,
|
|
255
|
+
suites: [],
|
|
256
|
+
tests: [],
|
|
257
|
+
coverage: null
|
|
258
|
+
};
|
|
259
|
+
|
|
260
|
+
// Parse Jest-style output
|
|
261
|
+
const testMatch = stdout.match(/(\d+)\s+tests?\s+(\d+)\s+failed/);
|
|
262
|
+
if (testMatch) {
|
|
263
|
+
results.total = parseInt(testMatch[1]);
|
|
264
|
+
results.failed = parseInt(testMatch[2]);
|
|
265
|
+
results.passed = results.total - results.failed;
|
|
266
|
+
}
|
|
267
|
+
|
|
268
|
+
// Parse pass/fail summary
|
|
269
|
+
const passMatch = stdout.match(/(\d+)\s+passing/);
|
|
270
|
+
if (passMatch) {
|
|
271
|
+
results.passed = parseInt(passMatch[1]);
|
|
272
|
+
}
|
|
273
|
+
|
|
274
|
+
const failMatch = stdout.match(/(\d+)\s+failing/);
|
|
275
|
+
if (failMatch) {
|
|
276
|
+
results.failed = parseInt(failMatch[1]);
|
|
277
|
+
}
|
|
278
|
+
|
|
279
|
+
const skipMatch = stdout.match(/(\d+)\s+skipping/);
|
|
280
|
+
if (skipMatch) {
|
|
281
|
+
results.skipped = parseInt(skipMatch[1]);
|
|
282
|
+
}
|
|
283
|
+
|
|
284
|
+
// Extract individual test results
|
|
285
|
+
const testLines = stdout.split('\n');
|
|
286
|
+
for (const line of testLines) {
|
|
287
|
+
const testMatch = line.match(/^(✓|✗|○)\s+(.+)$/);
|
|
288
|
+
if (testMatch) {
|
|
289
|
+
const status = testMatch[1] === '✓' ? 'passed' :
|
|
290
|
+
testMatch[1] === '✗' ? 'failed' : 'skipped';
|
|
291
|
+
const title = testMatch[2];
|
|
292
|
+
|
|
293
|
+
results.tests.push({
|
|
294
|
+
title,
|
|
295
|
+
status,
|
|
296
|
+
duration: 0,
|
|
297
|
+
failureMessages: status === 'failed' ? [line] : []
|
|
298
|
+
});
|
|
299
|
+
}
|
|
300
|
+
}
|
|
301
|
+
|
|
302
|
+
return results;
|
|
303
|
+
}
|
|
304
|
+
|
|
305
|
+
/**
|
|
306
|
+
* Parse coverage data
|
|
307
|
+
*/
|
|
308
|
+
parseCoverageData(coverageMap) {
|
|
309
|
+
const coverage = {
|
|
310
|
+
lines: { total: 0, covered: 0, percentage: 0 },
|
|
311
|
+
functions: { total: 0, covered: 0, percentage: 0 },
|
|
312
|
+
branches: { total: 0, covered: 0, percentage: 0 },
|
|
313
|
+
statements: { total: 0, covered: 0, percentage: 0 }
|
|
314
|
+
};
|
|
315
|
+
|
|
316
|
+
for (const [filePath, fileCoverage] of Object.entries(coverageMap)) {
|
|
317
|
+
// Parse line coverage
|
|
318
|
+
if (fileCoverage.l) {
|
|
319
|
+
const lines = Object.values(fileCoverage.l);
|
|
320
|
+
coverage.lines.total += lines.length;
|
|
321
|
+
coverage.lines.covered += lines.filter(count => count > 0).length;
|
|
322
|
+
}
|
|
323
|
+
|
|
324
|
+
// Parse function coverage
|
|
325
|
+
if (fileCoverage.f) {
|
|
326
|
+
const functions = Object.values(fileCoverage.f);
|
|
327
|
+
coverage.functions.total += functions.length;
|
|
328
|
+
coverage.functions.covered += functions.filter(count => count > 0).length;
|
|
329
|
+
}
|
|
330
|
+
|
|
331
|
+
// Parse branch coverage
|
|
332
|
+
if (fileCoverage.b) {
|
|
333
|
+
for (const branches of Object.values(fileCoverage.b)) {
|
|
334
|
+
coverage.branches.total += branches.length;
|
|
335
|
+
coverage.branches.covered += branches.filter(count => count > 0).length;
|
|
336
|
+
}
|
|
337
|
+
}
|
|
338
|
+
|
|
339
|
+
// Parse statement coverage
|
|
340
|
+
if (fileCoverage.s) {
|
|
341
|
+
const statements = Object.values(fileCoverage.s);
|
|
342
|
+
coverage.statements.total += statements.length;
|
|
343
|
+
coverage.statements.covered += statements.filter(count => count > 0).length;
|
|
344
|
+
}
|
|
345
|
+
}
|
|
346
|
+
|
|
347
|
+
// Calculate percentages
|
|
348
|
+
for (const metric of Object.values(coverage)) {
|
|
349
|
+
metric.percentage = metric.total > 0 ?
|
|
350
|
+
Math.round((metric.covered / metric.total) * 100) : 0;
|
|
351
|
+
}
|
|
352
|
+
|
|
353
|
+
return coverage;
|
|
354
|
+
}
|
|
355
|
+
|
|
356
|
+
/**
|
|
357
|
+
* Compare baseline and current results
|
|
358
|
+
*/
|
|
359
|
+
compareResults(baseline, current) {
|
|
360
|
+
const comparison = {
|
|
361
|
+
hasRegressions: false,
|
|
362
|
+
regressions: [],
|
|
363
|
+
improvements: [],
|
|
364
|
+
unchanged: [],
|
|
365
|
+
summary: {
|
|
366
|
+
baseline: {
|
|
367
|
+
total: baseline.total,
|
|
368
|
+
passed: baseline.passed,
|
|
369
|
+
failed: baseline.failed,
|
|
370
|
+
duration: baseline.duration
|
|
371
|
+
},
|
|
372
|
+
current: {
|
|
373
|
+
total: current.total,
|
|
374
|
+
passed: current.passed,
|
|
375
|
+
failed: current.failed,
|
|
376
|
+
duration: current.duration
|
|
377
|
+
}
|
|
378
|
+
}
|
|
379
|
+
};
|
|
380
|
+
|
|
381
|
+
// Check for test count changes
|
|
382
|
+
if (current.total < baseline.total) {
|
|
383
|
+
comparison.regressions.push({
|
|
384
|
+
type: 'missing_tests',
|
|
385
|
+
message: `${baseline.total - current.total} tests are missing`,
|
|
386
|
+
severity: 'high'
|
|
387
|
+
});
|
|
388
|
+
comparison.hasRegressions = true;
|
|
389
|
+
}
|
|
390
|
+
|
|
391
|
+
if (current.total > baseline.total) {
|
|
392
|
+
comparison.improvements.push({
|
|
393
|
+
type: 'new_tests',
|
|
394
|
+
message: `${current.total - baseline.total} new tests added`,
|
|
395
|
+
severity: 'info'
|
|
396
|
+
});
|
|
397
|
+
}
|
|
398
|
+
|
|
399
|
+
// Check for test failures
|
|
400
|
+
if (current.failed > baseline.failed) {
|
|
401
|
+
comparison.regressions.push({
|
|
402
|
+
type: 'new_failures',
|
|
403
|
+
message: `${current.failed - baseline.failed} new test failures`,
|
|
404
|
+
severity: 'high'
|
|
405
|
+
});
|
|
406
|
+
comparison.hasRegressions = true;
|
|
407
|
+
}
|
|
408
|
+
|
|
409
|
+
if (current.failed < baseline.failed) {
|
|
410
|
+
comparison.improvements.push({
|
|
411
|
+
type: 'fixed_failures',
|
|
412
|
+
message: `${baseline.failed - current.failed} test failures fixed`,
|
|
413
|
+
severity: 'good'
|
|
414
|
+
});
|
|
415
|
+
}
|
|
416
|
+
|
|
417
|
+
// Check for performance regression
|
|
418
|
+
const performanceIncrease = current.duration - baseline.duration;
|
|
419
|
+
const performancePercent = (performanceIncrease / baseline.duration) * 100;
|
|
420
|
+
|
|
421
|
+
if (performanceIncrease > 5000 && performancePercent > 10) { // 5s and 10%
|
|
422
|
+
comparison.regressions.push({
|
|
423
|
+
type: 'performance',
|
|
424
|
+
message: `Test execution increased by ${Math.round(performancePercent)}% (${Math.round(performanceIncrease/1000)}s)`,
|
|
425
|
+
severity: 'medium'
|
|
426
|
+
});
|
|
427
|
+
comparison.hasRegressions = true;
|
|
428
|
+
}
|
|
429
|
+
|
|
430
|
+
// Compare individual test results
|
|
431
|
+
const baselineTests = new Map();
|
|
432
|
+
for (const test of baseline.tests) {
|
|
433
|
+
baselineTests.set(test.fullName, test);
|
|
434
|
+
}
|
|
435
|
+
|
|
436
|
+
for (const test of current.tests) {
|
|
437
|
+
const baselineTest = baselineTests.get(test.fullName);
|
|
438
|
+
|
|
439
|
+
if (!baselineTest) {
|
|
440
|
+
comparison.improvements.push({
|
|
441
|
+
type: 'new_test',
|
|
442
|
+
message: `New test: ${test.title}`,
|
|
443
|
+
severity: 'info'
|
|
444
|
+
});
|
|
445
|
+
} else if (baselineTest.status === 'passed' && test.status === 'failed') {
|
|
446
|
+
comparison.regressions.push({
|
|
447
|
+
type: 'test_regression',
|
|
448
|
+
message: `Test now failing: ${test.title}`,
|
|
449
|
+
severity: 'high',
|
|
450
|
+
test: test
|
|
451
|
+
});
|
|
452
|
+
comparison.hasRegressions = true;
|
|
453
|
+
} else if (baselineTest.status === 'failed' && test.status === 'passed') {
|
|
454
|
+
comparison.improvements.push({
|
|
455
|
+
type: 'test_fixed',
|
|
456
|
+
message: `Test now passing: ${test.title}`,
|
|
457
|
+
severity: 'good'
|
|
458
|
+
});
|
|
459
|
+
}
|
|
460
|
+
}
|
|
461
|
+
|
|
462
|
+
return comparison;
|
|
463
|
+
}
|
|
464
|
+
|
|
465
|
+
/**
|
|
466
|
+
* Run specific test files
|
|
467
|
+
*/
|
|
468
|
+
async runSpecificTests(testFiles) {
|
|
469
|
+
console.log(`Running ${testFiles.length} specific tests...`);
|
|
470
|
+
|
|
471
|
+
const testArgs = ['test', '--', ...testFiles];
|
|
472
|
+
|
|
473
|
+
return new Promise((resolve, reject) => {
|
|
474
|
+
const startTime = Date.now();
|
|
475
|
+
let output = '';
|
|
476
|
+
let errorOutput = '';
|
|
477
|
+
|
|
478
|
+
const testProcess = spawn('npm', testArgs, {
|
|
479
|
+
stdio: ['pipe', 'pipe', 'pipe'],
|
|
480
|
+
cwd: process.cwd()
|
|
481
|
+
});
|
|
482
|
+
|
|
483
|
+
testProcess.stdout.on('data', (data) => {
|
|
484
|
+
output += data.toString();
|
|
485
|
+
if (this.options.verbose) {
|
|
486
|
+
process.stdout.write(data);
|
|
487
|
+
}
|
|
488
|
+
});
|
|
489
|
+
|
|
490
|
+
testProcess.stderr.on('data', (data) => {
|
|
491
|
+
errorOutput += data.toString();
|
|
492
|
+
if (this.options.verbose) {
|
|
493
|
+
process.stderr.write(data);
|
|
494
|
+
}
|
|
495
|
+
});
|
|
496
|
+
|
|
497
|
+
testProcess.on('close', (code) => {
|
|
498
|
+
const duration = Date.now() - startTime;
|
|
499
|
+
const results = this.parseBasicOutput(output, errorOutput, code, duration);
|
|
500
|
+
resolve(results);
|
|
501
|
+
});
|
|
502
|
+
|
|
503
|
+
testProcess.on('error', (error) => {
|
|
504
|
+
reject(new Error(`Test process failed: ${error.message}`));
|
|
505
|
+
});
|
|
506
|
+
});
|
|
507
|
+
}
|
|
508
|
+
|
|
509
|
+
/**
|
|
510
|
+
* Check if tests are available
|
|
511
|
+
*/
|
|
512
|
+
checkTestAvailability() {
|
|
513
|
+
const testPaths = [
|
|
514
|
+
'package.json',
|
|
515
|
+
'tests',
|
|
516
|
+
'__tests__',
|
|
517
|
+
'test',
|
|
518
|
+
'spec'
|
|
519
|
+
];
|
|
520
|
+
|
|
521
|
+
for (const testPath of testPaths) {
|
|
522
|
+
if (fs.existsSync(testPath)) {
|
|
523
|
+
return true;
|
|
524
|
+
}
|
|
525
|
+
}
|
|
526
|
+
|
|
527
|
+
return false;
|
|
528
|
+
}
|
|
529
|
+
|
|
530
|
+
/**
|
|
531
|
+
* Get test configuration
|
|
532
|
+
*/
|
|
533
|
+
getTestConfig() {
|
|
534
|
+
try {
|
|
535
|
+
const packageJson = JSON.parse(fs.readFileSync('package.json', 'utf8'));
|
|
536
|
+
return {
|
|
537
|
+
hasTestScript: !!packageJson.scripts?.test,
|
|
538
|
+
testScript: packageJson.scripts?.test,
|
|
539
|
+
jestConfig: packageJson.jest,
|
|
540
|
+
dependencies: {
|
|
541
|
+
jest: packageJson.dependencies?.jest || packageJson.devDependencies?.jest,
|
|
542
|
+
mocha: packageJson.dependencies?.mocha || packageJson.devDependencies?.mocha,
|
|
543
|
+
jasmine: packageJson.dependencies?.jasmine || packageJson.devDependencies?.jasmine
|
|
544
|
+
}
|
|
545
|
+
};
|
|
546
|
+
} catch (error) {
|
|
547
|
+
return { hasTestScript: false };
|
|
548
|
+
}
|
|
549
|
+
}
|
|
550
|
+
|
|
551
|
+
/**
|
|
552
|
+
* Generate test report
|
|
553
|
+
*/
|
|
554
|
+
generateReport() {
|
|
555
|
+
if (!this.currentResults) {
|
|
556
|
+
throw new Error('No test results available. Run tests first.');
|
|
557
|
+
}
|
|
558
|
+
|
|
559
|
+
const report = {
|
|
560
|
+
timestamp: new Date().toISOString(),
|
|
561
|
+
baseline: this.baselineResults,
|
|
562
|
+
current: this.currentResults,
|
|
563
|
+
comparison: this.compareResults(this.baselineResults, this.currentResults),
|
|
564
|
+
config: this.getTestConfig(),
|
|
565
|
+
summary: {
|
|
566
|
+
hasRegressions: this.regressionDetected,
|
|
567
|
+
testCountChanged: this.baselineResults.total !== this.currentResults.total,
|
|
568
|
+
performanceChanged: Math.abs(this.currentResults.duration - this.baselineResults.duration) > 1000
|
|
569
|
+
}
|
|
570
|
+
};
|
|
571
|
+
|
|
572
|
+
return report;
|
|
573
|
+
}
|
|
574
|
+
}
|
|
575
|
+
|
|
576
|
+
module.exports = {
|
|
577
|
+
TestRunner,
|
|
578
|
+
TEST_RESULT_TYPES
|
|
579
|
+
};
|