Files
festipod/scripts/parse-test-results.ts
T
Sylvain Duchesne 901fd659df NextGraph integration (WIP), broker banner, and feature-based architecture
- Add NextGraph data layer with @ng-org/orm, SHEX shapes (Event, UserProfile,
  Participation), session management, and FestipodDataContext with dual-mode
  operation (connected via NextGraph or local seed data)
- Add BrokerBanner and NgStatus components showing connection status
- Refactor to feature-based architecture: organize code by business domain
  (event, user, home, auth, workshop, meeting, notification) instead of
  technical layer. Modules only import from shared/, never from each other
- Collocate BDD features and step definitions with their modules: event-specific
  steps in event/steps/, user steps in user/steps/, shared generic steps remain
  in shared/steps/
- Set up multi-layer BDD structure (frontend/backend/e2e steps per module)
- Add project documentation (AGENTS.md, .project/knowledge/)

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-11 12:19:45 +01:00

154 lines
4.6 KiB
TypeScript

import type { FeatureTestStatus, ScenarioTestResult } from '../src/shared/types/gherkin';
interface CucumberScenario {
id: string;
name: string;
steps: Array<{
result: {
status: 'passed' | 'failed' | 'skipped' | 'pending' | 'undefined';
duration?: number;
error_message?: string;
};
}>;
}
interface CucumberFeature {
id: string;
uri: string;
name: string;
elements: CucumberScenario[];
}
export async function parseTestResults(): Promise<Map<string, FeatureTestStatus>> {
const reportPath = 'reports/cucumber-report.json';
const file = Bun.file(reportPath);
if (!await file.exists()) {
console.log('No test report found at', reportPath);
return new Map();
}
const content = await file.text();
const features: CucumberFeature[] = JSON.parse(content);
const results = new Map<string, FeatureTestStatus>();
for (const feature of features) {
// Extract feature ID from URI (e.g., features/user/us-9-visualiser-photo.feature -> us-9)
const match = feature.uri.match(/us-(\d+)/i);
const featureId = match ? `us-${match[1]}` : feature.id;
const scenarios = feature.elements.filter(el => el.name); // Filter out Background
let passed = 0;
let failed = 0;
let skipped = 0;
const scenarioResults: ScenarioTestResult[] = [];
for (const scenario of scenarios) {
const { status: scenarioStatus, errorMessage } = getScenarioStatusAndError(scenario);
if (scenarioStatus === 'passed') passed++;
else if (scenarioStatus === 'failed') failed++;
else skipped++;
scenarioResults.push({
name: scenario.name,
status: scenarioStatus,
errorMessage,
});
}
results.set(featureId, {
featureId,
totalScenarios: scenarios.length,
passed,
failed,
skipped,
lastRun: new Date(),
scenarios: scenarioResults,
});
}
return results;
}
function getScenarioStatusAndError(scenario: CucumberScenario): { status: 'passed' | 'failed' | 'skipped'; errorMessage?: string } {
for (const step of scenario.steps) {
if (step.result.status === 'failed') {
return { status: 'failed', errorMessage: step.result.error_message };
}
if (step.result.status === 'skipped' || step.result.status === 'pending' || step.result.status === 'undefined') {
return { status: 'skipped' };
}
}
return { status: 'passed' };
}
// Generate TypeScript file with test results
async function generateTestResultsFile() {
const results = await parseTestResults();
const resultsArray = Array.from(results.entries()).map(([id, status]) => ({
...status,
lastRun: status.lastRun?.toISOString(),
}));
const output = `// Auto-generated by scripts/parse-test-results.ts
// Do not edit manually - run "bun run test:results" to regenerate
import type { FeatureTestStatus, ScenarioTestResult } from '../types/gherkin';
interface RawFeatureTestStatus {
featureId: string;
totalScenarios: number;
passed: number;
failed: number;
skipped: number;
lastRun?: string;
scenarios?: ScenarioTestResult[];
}
const rawResults: RawFeatureTestStatus[] = ${JSON.stringify(resultsArray, null, 2)};
export const testResults: Map<string, FeatureTestStatus> = new Map(
rawResults.map(r => [r.featureId, { ...r, lastRun: r.lastRun ? new Date(r.lastRun) : undefined }])
);
export function getTestStatus(featureId: string): FeatureTestStatus | undefined {
return testResults.get(featureId);
}
export function getScenarioResults(featureId: string): ScenarioTestResult[] {
return testResults.get(featureId)?.scenarios ?? [];
}
export function getAllTestResults(): FeatureTestStatus[] {
return Array.from(testResults.values());
}
export function getTestSummary() {
const results = getAllTestResults();
const firstResult = results[0];
return {
totalFeatures: results.length,
totalScenarios: results.reduce((acc, r) => acc + r.totalScenarios, 0),
passed: results.reduce((acc, r) => acc + r.passed, 0),
failed: results.reduce((acc, r) => acc + r.failed, 0),
skipped: results.reduce((acc, r) => acc + r.skipped, 0),
lastRun: firstResult?.lastRun,
};
}
`;
await Bun.write('src/shared/data/testResults.ts', output);
console.log(`Generated test results for ${results.size} features`);
// Print summary
let totalPassed = 0, totalFailed = 0, totalSkipped = 0;
results.forEach(r => {
totalPassed += r.passed;
totalFailed += r.failed;
totalSkipped += r.skipped;
});
console.log(`Summary: ${totalPassed} passed, ${totalFailed} failed, ${totalSkipped} skipped`);
}
generateTestResultsFile().catch(console.error);