@hyperfixi/testing-framework 2.0.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/assertions-BImuwZsB.d.mts +458 -0
- package/dist/assertions-BImuwZsB.d.ts +458 -0
- package/dist/assertions.d.mts +1 -0
- package/dist/assertions.d.ts +1 -0
- package/dist/assertions.js +625 -0
- package/dist/assertions.js.map +1 -0
- package/dist/assertions.mjs +621 -0
- package/dist/assertions.mjs.map +1 -0
- package/dist/index.d.mts +246 -0
- package/dist/index.d.ts +246 -0
- package/dist/index.js +1465 -0
- package/dist/index.js.map +1 -0
- package/dist/index.mjs +1435 -0
- package/dist/index.mjs.map +1 -0
- package/package.json +121 -0
- package/src/assertions.test.ts +490 -0
- package/src/assertions.ts +779 -0
- package/src/index.test.ts +490 -0
- package/src/index.ts +601 -0
- package/src/jsdom.d.ts +7 -0
- package/src/multilingual/README.md +295 -0
- package/src/multilingual/bundle-builder.ts +345 -0
- package/src/multilingual/cli.ts +224 -0
- package/src/multilingual/index.ts +68 -0
- package/src/multilingual/orchestrator.ts +275 -0
- package/src/multilingual/pattern-loader.ts +176 -0
- package/src/multilingual/reporters/console-reporter.ts +294 -0
- package/src/multilingual/reporters/json-reporter.ts +99 -0
- package/src/multilingual/reporters/regression-reporter.ts +263 -0
- package/src/multilingual/tools/analyze-failures.ts +326 -0
- package/src/multilingual/types.ts +268 -0
- package/src/multilingual/validators/parse-diagnostics.ts +337 -0
- package/src/multilingual/validators/parse-validator.ts +193 -0
- package/src/multilingual/validators/size-validator.ts +187 -0
- package/src/runner.test.ts +813 -0
- package/src/runner.ts +639 -0
- package/src/types.ts +505 -0
|
@@ -0,0 +1,263 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Regression Reporter - Compares current results against baseline
|
|
3
|
+
*/
|
|
4
|
+
|
|
5
|
+
import { readFileSync, existsSync, writeFileSync } from 'node:fs';
|
|
6
|
+
import type {
|
|
7
|
+
Reporter,
|
|
8
|
+
TestConfig,
|
|
9
|
+
LanguageCode,
|
|
10
|
+
LanguageResults,
|
|
11
|
+
TestResults,
|
|
12
|
+
Baseline,
|
|
13
|
+
RegressionResult,
|
|
14
|
+
} from '../types';
|
|
15
|
+
|
|
16
|
+
/**
|
|
17
|
+
* Regression Reporter
|
|
18
|
+
*
|
|
19
|
+
* Compares test results against a baseline and reports regressions.
|
|
20
|
+
*/
|
|
21
|
+
export class RegressionReporter implements Reporter {
|
|
22
|
+
private baselinePath: string;
|
|
23
|
+
private baseline: Baseline | null = null;
|
|
24
|
+
private currentResults: TestResults | null = null;
|
|
25
|
+
private regressionResults: RegressionResult[] = [];
|
|
26
|
+
|
|
27
|
+
constructor(baselinePath: string = './test-results/baseline.json') {
|
|
28
|
+
this.baselinePath = baselinePath;
|
|
29
|
+
this.loadBaseline();
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
/**
|
|
33
|
+
* Load baseline from file
|
|
34
|
+
*/
|
|
35
|
+
private loadBaseline(): void {
|
|
36
|
+
if (!existsSync(this.baselinePath)) {
|
|
37
|
+
console.warn(`Warning: Baseline file not found at ${this.baselinePath}`);
|
|
38
|
+
return;
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
try {
|
|
42
|
+
const data = readFileSync(this.baselinePath, 'utf-8');
|
|
43
|
+
this.baseline = JSON.parse(data);
|
|
44
|
+
} catch (error) {
|
|
45
|
+
console.error(
|
|
46
|
+
`Error loading baseline: ${error instanceof Error ? error.message : String(error)}`
|
|
47
|
+
);
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
/**
|
|
52
|
+
* Report test start
|
|
53
|
+
*/
|
|
54
|
+
reportStart(_config: TestConfig): void {
|
|
55
|
+
// No-op
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
/**
|
|
59
|
+
* Report language start
|
|
60
|
+
*/
|
|
61
|
+
reportLanguageStart(_language: LanguageCode, _bundle: string): void {
|
|
62
|
+
// No-op
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
/**
|
|
66
|
+
* Report language complete
|
|
67
|
+
*/
|
|
68
|
+
reportLanguageComplete(_results: LanguageResults): void {
|
|
69
|
+
// No-op
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
/**
|
|
73
|
+
* Report complete test run - compare to baseline
|
|
74
|
+
*/
|
|
75
|
+
reportComplete(results: TestResults): void {
|
|
76
|
+
this.currentResults = results;
|
|
77
|
+
|
|
78
|
+
if (!this.baseline) {
|
|
79
|
+
console.log('No baseline available - skipping regression analysis');
|
|
80
|
+
return;
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
this.regressionResults = this.compareToBaseline(results);
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
/**
|
|
87
|
+
* Get regression results
|
|
88
|
+
*/
|
|
89
|
+
getRegressionResults(): RegressionResult[] {
|
|
90
|
+
return this.regressionResults;
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
/**
|
|
94
|
+
* Report error
|
|
95
|
+
*/
|
|
96
|
+
reportError(_error: Error): void {
|
|
97
|
+
// No-op
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
/**
|
|
101
|
+
* Compare current results to baseline
|
|
102
|
+
*/
|
|
103
|
+
private compareToBaseline(results: TestResults): RegressionResult[] {
|
|
104
|
+
if (!this.baseline) return [];
|
|
105
|
+
|
|
106
|
+
const regressions: RegressionResult[] = [];
|
|
107
|
+
|
|
108
|
+
for (const langResult of results.languageResults) {
|
|
109
|
+
const baselineLang = this.baseline.languages[langResult.language];
|
|
110
|
+
if (!baselineLang) continue;
|
|
111
|
+
|
|
112
|
+
const parseRateDelta = (langResult.parseRate - baselineLang.parseRate) * 100;
|
|
113
|
+
const avgConfidenceDelta = langResult.avgConfidence - baselineLang.avgConfidence;
|
|
114
|
+
const bundleSizeDelta = this.getBundleSizeDelta(langResult, baselineLang);
|
|
115
|
+
|
|
116
|
+
// Identify new failures and successes
|
|
117
|
+
const newFailures = this.findNewFailures(langResult, baselineLang);
|
|
118
|
+
const newSuccesses = this.findNewSuccesses(langResult, baselineLang);
|
|
119
|
+
|
|
120
|
+
// Determine status
|
|
121
|
+
let status: 'improved' | 'regressed' | 'unchanged' = 'unchanged';
|
|
122
|
+
if (parseRateDelta < -5 || newFailures.length > 0) {
|
|
123
|
+
status = 'regressed';
|
|
124
|
+
} else if (parseRateDelta > 5 || newSuccesses.length > 0) {
|
|
125
|
+
status = 'improved';
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
regressions.push({
|
|
129
|
+
language: langResult.language,
|
|
130
|
+
parseRateDelta,
|
|
131
|
+
avgConfidenceDelta,
|
|
132
|
+
bundleSizeDelta: bundleSizeDelta !== undefined ? bundleSizeDelta : undefined,
|
|
133
|
+
newFailures,
|
|
134
|
+
newSuccesses,
|
|
135
|
+
status,
|
|
136
|
+
});
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
return regressions;
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
/**
|
|
143
|
+
* Get bundle size delta
|
|
144
|
+
*/
|
|
145
|
+
private getBundleSizeDelta(
|
|
146
|
+
current: LanguageResults,
|
|
147
|
+
baseline: { bundleSize: number | undefined }
|
|
148
|
+
): number | undefined {
|
|
149
|
+
if (!current.bundleSize || !baseline.bundleSize) return undefined;
|
|
150
|
+
return ((current.bundleSize - baseline.bundleSize) / baseline.bundleSize) * 100;
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
/**
|
|
154
|
+
* Find patterns that failed in current but passed in baseline
|
|
155
|
+
*/
|
|
156
|
+
private findNewFailures(
|
|
157
|
+
current: LanguageResults,
|
|
158
|
+
baseline: {
|
|
159
|
+
parseSuccess: number;
|
|
160
|
+
patterns: Record<string, { success: boolean; confidence: number | undefined }> | undefined;
|
|
161
|
+
}
|
|
162
|
+
): string[] {
|
|
163
|
+
// If no pattern-level baseline, return empty array
|
|
164
|
+
if (!baseline.patterns) return [];
|
|
165
|
+
|
|
166
|
+
const newFailures: string[] = [];
|
|
167
|
+
|
|
168
|
+
for (const result of current.parseResults) {
|
|
169
|
+
const baselinePattern = baseline.patterns[result.pattern.codeExampleId];
|
|
170
|
+
if (baselinePattern && baselinePattern.success && !result.success) {
|
|
171
|
+
newFailures.push(result.pattern.codeExampleId);
|
|
172
|
+
}
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
return newFailures.slice(0, 10); // Limit to 10 for reporting
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
/**
|
|
179
|
+
* Find patterns that passed in current but failed in baseline
|
|
180
|
+
*/
|
|
181
|
+
private findNewSuccesses(
|
|
182
|
+
current: LanguageResults,
|
|
183
|
+
baseline: {
|
|
184
|
+
parseSuccess: number;
|
|
185
|
+
patterns: Record<string, { success: boolean; confidence: number | undefined }> | undefined;
|
|
186
|
+
}
|
|
187
|
+
): string[] {
|
|
188
|
+
// If no pattern-level baseline, return empty array
|
|
189
|
+
if (!baseline.patterns) return [];
|
|
190
|
+
|
|
191
|
+
const newSuccesses: string[] = [];
|
|
192
|
+
|
|
193
|
+
for (const result of current.parseResults) {
|
|
194
|
+
const baselinePattern = baseline.patterns[result.pattern.codeExampleId];
|
|
195
|
+
if (baselinePattern && !baselinePattern.success && result.success) {
|
|
196
|
+
newSuccesses.push(result.pattern.codeExampleId);
|
|
197
|
+
}
|
|
198
|
+
}
|
|
199
|
+
|
|
200
|
+
return newSuccesses.slice(0, 10); // Limit to 10 for reporting
|
|
201
|
+
}
|
|
202
|
+
|
|
203
|
+
/**
|
|
204
|
+
* Save current results as new baseline
|
|
205
|
+
*/
|
|
206
|
+
saveAsBaseline(results: TestResults): void {
|
|
207
|
+
const baseline: Baseline = {
|
|
208
|
+
timestamp: results.timestamp,
|
|
209
|
+
commit: results.commit || 'unknown',
|
|
210
|
+
languages: {},
|
|
211
|
+
bundles: {} as Record<
|
|
212
|
+
string,
|
|
213
|
+
{ size: number; languages: LanguageCode[]; gzipSize: number | undefined }
|
|
214
|
+
>,
|
|
215
|
+
};
|
|
216
|
+
|
|
217
|
+
// Convert bundles to proper format
|
|
218
|
+
for (const [name, info] of Object.entries(results.bundles)) {
|
|
219
|
+
baseline.bundles[name] = {
|
|
220
|
+
size: info.size,
|
|
221
|
+
languages: info.languages,
|
|
222
|
+
gzipSize: info.gzipSize ?? undefined,
|
|
223
|
+
};
|
|
224
|
+
}
|
|
225
|
+
|
|
226
|
+
for (const langResult of results.languageResults) {
|
|
227
|
+
// Build pattern-level tracking
|
|
228
|
+
const patterns: Record<string, { success: boolean; confidence: number | undefined }> = {};
|
|
229
|
+
for (const result of langResult.parseResults) {
|
|
230
|
+
patterns[result.pattern.codeExampleId] = {
|
|
231
|
+
success: result.success,
|
|
232
|
+
confidence: result.confidence,
|
|
233
|
+
};
|
|
234
|
+
}
|
|
235
|
+
|
|
236
|
+
baseline.languages[langResult.language] = {
|
|
237
|
+
parseSuccess: langResult.parseSuccess,
|
|
238
|
+
parseFailure: langResult.parseFailure,
|
|
239
|
+
parseRate: langResult.parseRate,
|
|
240
|
+
avgConfidence: langResult.avgConfidence,
|
|
241
|
+
bundleSize: langResult.bundleSize ?? undefined,
|
|
242
|
+
patterns,
|
|
243
|
+
};
|
|
244
|
+
}
|
|
245
|
+
|
|
246
|
+
writeFileSync(this.baselinePath, JSON.stringify(baseline, null, 2));
|
|
247
|
+
console.log(`Baseline saved to ${this.baselinePath}`);
|
|
248
|
+
}
|
|
249
|
+
|
|
250
|
+
/**
|
|
251
|
+
* Check if baseline exists
|
|
252
|
+
*/
|
|
253
|
+
hasBaseline(): boolean {
|
|
254
|
+
return this.baseline !== null;
|
|
255
|
+
}
|
|
256
|
+
}
|
|
257
|
+
|
|
258
|
+
/**
|
|
259
|
+
* Create a regression reporter
|
|
260
|
+
*/
|
|
261
|
+
export function createRegressionReporter(baselinePath?: string): RegressionReporter {
|
|
262
|
+
return new RegressionReporter(baselinePath);
|
|
263
|
+
}
|
|
@@ -0,0 +1,326 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
/**
|
|
3
|
+
* Parse Failure Analysis Tool
|
|
4
|
+
*
|
|
5
|
+
* Analyzes parse failures to identify patterns and suggest fixes
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
import { readFileSync, writeFileSync, existsSync } from 'node:fs';
|
|
9
|
+
import { join } from 'node:path';
|
|
10
|
+
import { FailureAnalyzer } from '../validators/parse-diagnostics';
|
|
11
|
+
import type { TestResults, ParseResult, LanguageCode } from '../types';
|
|
12
|
+
|
|
13
|
+
interface AnalysisOptions {
|
|
14
|
+
language: LanguageCode | undefined;
|
|
15
|
+
verbose: boolean;
|
|
16
|
+
outputFile: string | undefined;
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
/**
|
|
20
|
+
* Parse command-line arguments
|
|
21
|
+
*/
|
|
22
|
+
function parseArgs(): AnalysisOptions {
|
|
23
|
+
const args = process.argv.slice(2);
|
|
24
|
+
const options: AnalysisOptions = {
|
|
25
|
+
language: undefined,
|
|
26
|
+
verbose: false,
|
|
27
|
+
outputFile: undefined,
|
|
28
|
+
};
|
|
29
|
+
|
|
30
|
+
for (let i = 0; i < args.length; i++) {
|
|
31
|
+
switch (args[i]) {
|
|
32
|
+
case '--language':
|
|
33
|
+
case '-l': {
|
|
34
|
+
const lang = args[++i];
|
|
35
|
+
if (lang) options.language = lang as LanguageCode;
|
|
36
|
+
break;
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
case '--verbose':
|
|
40
|
+
case '-v':
|
|
41
|
+
options.verbose = true;
|
|
42
|
+
break;
|
|
43
|
+
|
|
44
|
+
case '--output':
|
|
45
|
+
case '-o': {
|
|
46
|
+
const file = args[++i];
|
|
47
|
+
if (file) options.outputFile = file;
|
|
48
|
+
break;
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
case '--help':
|
|
52
|
+
case '-h':
|
|
53
|
+
showHelp();
|
|
54
|
+
process.exit(0);
|
|
55
|
+
break;
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
return options;
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
/**
|
|
63
|
+
* Show help message
|
|
64
|
+
*/
|
|
65
|
+
function showHelp(): void {
|
|
66
|
+
console.log(`
|
|
67
|
+
Parse Failure Analysis Tool
|
|
68
|
+
|
|
69
|
+
Analyzes test results to identify failure patterns and suggest improvements.
|
|
70
|
+
|
|
71
|
+
USAGE:
|
|
72
|
+
npm run analyze-failures [OPTIONS]
|
|
73
|
+
|
|
74
|
+
OPTIONS:
|
|
75
|
+
-l, --language <code> Analyze specific language only
|
|
76
|
+
-v, --verbose Show detailed output
|
|
77
|
+
-o, --output <file> Write report to file
|
|
78
|
+
-h, --help Show this help message
|
|
79
|
+
|
|
80
|
+
EXAMPLES:
|
|
81
|
+
# Analyze all failures
|
|
82
|
+
npm run analyze-failures
|
|
83
|
+
|
|
84
|
+
# Analyze Japanese failures with verbose output
|
|
85
|
+
npm run analyze-failures -- --language ja --verbose
|
|
86
|
+
|
|
87
|
+
# Generate report file
|
|
88
|
+
npm run analyze-failures -- --output failure-analysis.md
|
|
89
|
+
`);
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
/**
|
|
93
|
+
* Load test results
|
|
94
|
+
*/
|
|
95
|
+
function loadResults(): TestResults | undefined {
|
|
96
|
+
const resultsPath = join(process.cwd(), 'test-results', 'results.json');
|
|
97
|
+
|
|
98
|
+
if (!existsSync(resultsPath)) {
|
|
99
|
+
console.error('Error: No test results found at:', resultsPath);
|
|
100
|
+
console.error('Run tests first: npm run test:multilingual -- --full');
|
|
101
|
+
return undefined;
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
const data = readFileSync(resultsPath, 'utf-8');
|
|
105
|
+
return JSON.parse(data);
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
/**
|
|
109
|
+
* Extract all parse results from test results
|
|
110
|
+
*/
|
|
111
|
+
function extractParseResults(
|
|
112
|
+
results: TestResults,
|
|
113
|
+
language: LanguageCode | undefined
|
|
114
|
+
): ParseResult[] {
|
|
115
|
+
let allResults: ParseResult[] = [];
|
|
116
|
+
|
|
117
|
+
for (const langResult of results.languageResults) {
|
|
118
|
+
if (language && langResult.language !== language) continue;
|
|
119
|
+
allResults = allResults.concat(langResult.parseResults);
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
return allResults;
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
/**
|
|
126
|
+
* Generate detailed failure breakdown
|
|
127
|
+
*/
|
|
128
|
+
function generateDetailedBreakdown(analyzer: FailureAnalyzer, results: ParseResult[]): string {
|
|
129
|
+
const failures = results.filter(r => !r.success);
|
|
130
|
+
const byLanguage = analyzer.analyzeByLanguage(results);
|
|
131
|
+
|
|
132
|
+
let output = '\n';
|
|
133
|
+
output += '═══════════════════════════════════════════════════════════════\n';
|
|
134
|
+
output += ' DETAILED FAILURE BREAKDOWN\n';
|
|
135
|
+
output += '═══════════════════════════════════════════════════════════════\n\n';
|
|
136
|
+
|
|
137
|
+
const sortedLangs = Object.entries(byLanguage)
|
|
138
|
+
.sort(([, a], [, b]) => b.failures - a.failures)
|
|
139
|
+
.filter(([, data]) => data.failures > 0);
|
|
140
|
+
|
|
141
|
+
for (const [lang, data] of sortedLangs) {
|
|
142
|
+
const failRate = ((data.failures / data.totalPatterns) * 100).toFixed(1);
|
|
143
|
+
const passRate = (100 - parseFloat(failRate)).toFixed(1);
|
|
144
|
+
|
|
145
|
+
output += `\n${lang.toUpperCase()}\n`;
|
|
146
|
+
output += '─'.repeat(60) + '\n';
|
|
147
|
+
output += ` Status: ${data.failures}/${data.totalPatterns} failures (${failRate}% fail, ${passRate}% pass)\n\n`;
|
|
148
|
+
|
|
149
|
+
output += ' Common Error Types:\n';
|
|
150
|
+
const errorCounts: Map<string, number> = new Map();
|
|
151
|
+
const langFailures = failures.filter(f => f.pattern.language === lang);
|
|
152
|
+
|
|
153
|
+
for (const failure of langFailures) {
|
|
154
|
+
const err = failure.error || 'Unknown';
|
|
155
|
+
errorCounts.set(err, (errorCounts.get(err) || 0) + 1);
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
const sortedErrors = Array.from(errorCounts.entries())
|
|
159
|
+
.sort(([, a], [, b]) => b - a)
|
|
160
|
+
.slice(0, 5);
|
|
161
|
+
|
|
162
|
+
for (const [error, count] of sortedErrors) {
|
|
163
|
+
const pct = ((count / data.failures) * 100).toFixed(1);
|
|
164
|
+
output += ` - ${error}: ${count} (${pct}%)\n`;
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
output += '\n Problematic Patterns (first 5):\n';
|
|
168
|
+
data.problematicPatterns.slice(0, 5).forEach((pattern: string, i: number) => {
|
|
169
|
+
output += ` ${i + 1}. "${pattern}"\n`;
|
|
170
|
+
});
|
|
171
|
+
|
|
172
|
+
output += '\n';
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
return output;
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
/**
|
|
179
|
+
* Generate recommendations
|
|
180
|
+
*/
|
|
181
|
+
function generateRecommendations(analyzer: FailureAnalyzer, results: ParseResult[]): string {
|
|
182
|
+
const failures = results.filter(r => !r.success);
|
|
183
|
+
const patterns = analyzer.identifyFailurePatterns(failures);
|
|
184
|
+
const byLanguage = analyzer.analyzeByLanguage(results);
|
|
185
|
+
|
|
186
|
+
let output = '\n';
|
|
187
|
+
output += '═══════════════════════════════════════════════════════════════\n';
|
|
188
|
+
output += ' RECOMMENDATIONS\n';
|
|
189
|
+
output += '═══════════════════════════════════════════════════════════════\n\n';
|
|
190
|
+
|
|
191
|
+
// Language-specific recommendations
|
|
192
|
+
const sovLanguages: LanguageCode[] = ['ja', 'ko', 'tr'];
|
|
193
|
+
const vsoLanguages: LanguageCode[] = ['ar'];
|
|
194
|
+
const sovFailures = sovLanguages.filter(
|
|
195
|
+
lang => byLanguage[lang] && byLanguage[lang].failures > byLanguage[lang].totalPatterns * 0.3
|
|
196
|
+
);
|
|
197
|
+
const vsoFailures = vsoLanguages.filter(
|
|
198
|
+
lang => byLanguage[lang] && byLanguage[lang].failures > byLanguage[lang].totalPatterns * 0.3
|
|
199
|
+
);
|
|
200
|
+
|
|
201
|
+
if (sovFailures.length > 0) {
|
|
202
|
+
output +=
|
|
203
|
+
'## SOV Language Issues (' + sovFailures.map(l => l.toUpperCase()).join(', ') + ')\n\n';
|
|
204
|
+
output += ' Priority Actions:\n';
|
|
205
|
+
output += ' 1. Improve particle detection and positioning\n';
|
|
206
|
+
output += ' 2. Enhance verb conjugation recognition\n';
|
|
207
|
+
output += ' 3. Add support for complex clause patterns\n\n';
|
|
208
|
+
output += ' Implementation Files:\n';
|
|
209
|
+
output += ' - packages/semantic/src/tokenizers/ja.ts (Japanese)\n';
|
|
210
|
+
output += ' - packages/semantic/src/tokenizers/ko.ts (Korean)\n';
|
|
211
|
+
output += ' - packages/semantic/src/tokenizers/tr.ts (Turkish)\n';
|
|
212
|
+
output += ' - packages/semantic/src/parser/semantic-parser.ts\n\n';
|
|
213
|
+
}
|
|
214
|
+
|
|
215
|
+
if (vsoFailures.length > 0) {
|
|
216
|
+
output +=
|
|
217
|
+
'## VSO Language Issues (' + vsoFailures.map(l => l.toUpperCase()).join(', ') + ')\n\n';
|
|
218
|
+
output += ' Priority Actions:\n';
|
|
219
|
+
output += ' 1. Implement verb-first pattern matching\n';
|
|
220
|
+
output += ' 2. Enhance preposition handling\n';
|
|
221
|
+
output += ' 3. Improve right-to-left text processing\n\n';
|
|
222
|
+
output += ' Implementation Files:\n';
|
|
223
|
+
output += ' - packages/semantic/src/tokenizers/ar.ts\n';
|
|
224
|
+
output += ' - packages/semantic/src/parser/semantic-parser.ts\n\n';
|
|
225
|
+
}
|
|
226
|
+
|
|
227
|
+
// Pattern-specific recommendations
|
|
228
|
+
output += '## Common Failure Patterns\n\n';
|
|
229
|
+
|
|
230
|
+
if (patterns.multiClause > failures.length * 0.2) {
|
|
231
|
+
output += ` ⚠️ Multi-clause patterns: ${patterns.multiClause} failures\n`;
|
|
232
|
+
output += ' → Improve conjunction handling (それから/then/ثم/그러면)\n';
|
|
233
|
+
output += ' → Better clause separation logic\n\n';
|
|
234
|
+
}
|
|
235
|
+
|
|
236
|
+
if (patterns.styleProperties > failures.length * 0.15) {
|
|
237
|
+
output += ` ⚠️ Style property access: ${patterns.styleProperties} failures\n`;
|
|
238
|
+
output += ' → Add *property syntax support across all languages\n';
|
|
239
|
+
output += ' → Validate CSS property name recognition\n\n';
|
|
240
|
+
}
|
|
241
|
+
|
|
242
|
+
if (patterns.eventModifiers > failures.length * 0.15) {
|
|
243
|
+
output += ` ⚠️ Event modifiers: ${patterns.eventModifiers} failures\n`;
|
|
244
|
+
output += ' → Improve bracket syntax parsing\n';
|
|
245
|
+
output += ' → Add debounced/throttled keyword support\n\n';
|
|
246
|
+
}
|
|
247
|
+
|
|
248
|
+
if (patterns.conditionals > failures.length * 0.15) {
|
|
249
|
+
output += ` ⚠️ Conditionals: ${patterns.conditionals} failures\n`;
|
|
250
|
+
output += ' → Enhance if/else block parsing\n';
|
|
251
|
+
output += ' → Add match/conditional keyword recognition\n\n';
|
|
252
|
+
}
|
|
253
|
+
|
|
254
|
+
if (patterns.windowEvents > failures.length * 0.1) {
|
|
255
|
+
output += ` ⚠️ Window events: ${patterns.windowEvents} failures\n`;
|
|
256
|
+
output += ' → Add window/document scope handling\n';
|
|
257
|
+
output += ' → Validate event target resolution\n\n';
|
|
258
|
+
}
|
|
259
|
+
|
|
260
|
+
if (patterns.complexSelectors > failures.length * 0.1) {
|
|
261
|
+
output += ` ⚠️ Complex selectors: ${patterns.complexSelectors} failures\n`;
|
|
262
|
+
output += ' → Improve closest/first/parent selector parsing\n';
|
|
263
|
+
output += ' → Add HTML literal syntax support (<div.class/>)\n\n';
|
|
264
|
+
}
|
|
265
|
+
|
|
266
|
+
return output;
|
|
267
|
+
}
|
|
268
|
+
|
|
269
|
+
/**
|
|
270
|
+
* Main analysis function
|
|
271
|
+
*/
|
|
272
|
+
async function main(): Promise<void> {
|
|
273
|
+
const options = parseArgs();
|
|
274
|
+
|
|
275
|
+
console.log('Loading test results...');
|
|
276
|
+
const results = loadResults();
|
|
277
|
+
if (!results) {
|
|
278
|
+
process.exit(1);
|
|
279
|
+
}
|
|
280
|
+
|
|
281
|
+
console.log('Analyzing failures...\n');
|
|
282
|
+
const parseResults = extractParseResults(results, options.language);
|
|
283
|
+
const analyzer = new FailureAnalyzer();
|
|
284
|
+
|
|
285
|
+
// Generate basic report
|
|
286
|
+
const report = analyzer.generateReport(parseResults);
|
|
287
|
+
|
|
288
|
+
// Generate detailed breakdown
|
|
289
|
+
const breakdown = generateDetailedBreakdown(analyzer, parseResults);
|
|
290
|
+
|
|
291
|
+
// Generate recommendations
|
|
292
|
+
const recommendations = generateRecommendations(analyzer, parseResults);
|
|
293
|
+
|
|
294
|
+
// Combine all sections
|
|
295
|
+
const fullReport = report + breakdown + recommendations;
|
|
296
|
+
|
|
297
|
+
// Output
|
|
298
|
+
if (options.outputFile) {
|
|
299
|
+
writeFileSync(options.outputFile, fullReport);
|
|
300
|
+
console.log(`Report written to: ${options.outputFile}`);
|
|
301
|
+
} else {
|
|
302
|
+
console.log(fullReport);
|
|
303
|
+
}
|
|
304
|
+
|
|
305
|
+
// Summary
|
|
306
|
+
const failures = parseResults.filter(r => !r.success);
|
|
307
|
+
const failRate = ((failures.length / parseResults.length) * 100).toFixed(1);
|
|
308
|
+
|
|
309
|
+
console.log('\n═══════════════════════════════════════════════════════════════');
|
|
310
|
+
console.log(' ANALYSIS COMPLETE');
|
|
311
|
+
console.log('═══════════════════════════════════════════════════════════════\n');
|
|
312
|
+
console.log(` Analyzed: ${parseResults.length} patterns`);
|
|
313
|
+
console.log(` Failures: ${failures.length} (${failRate}%)`);
|
|
314
|
+
console.log(` Languages: ${options.language || 'all'}`);
|
|
315
|
+
console.log();
|
|
316
|
+
}
|
|
317
|
+
|
|
318
|
+
// Run if called directly
|
|
319
|
+
if (import.meta.url === `file://${process.argv[1]}`) {
|
|
320
|
+
main().catch(err => {
|
|
321
|
+
console.error('Error:', err.message);
|
|
322
|
+
process.exit(1);
|
|
323
|
+
});
|
|
324
|
+
}
|
|
325
|
+
|
|
326
|
+
export { main, parseArgs, showHelp };
|