#!/usr/bin/env node /** * Gherkin Test Report Generator * * Generates Gherkin format test cases from Jest test results * and updates Linear TC issues with Gherkin descriptions */ const fs = require('fs'); const path = require('path'); // Test case mapping to functional areas const FUNCTIONAL_AREAS = { 'contact': 'Kapcsolat Űrlap', 'navigation': 'Navigáció Rendszer', 'homepage': 'Kezdőlap Funkcionalitás', 'responsive': 'Responsive Design', 'performance': 'Teljesítmény', 'security': 'Biztonság', 'accessibility': 'Accessibility (A11y)', 'api': 'API Endpoints' }; // Gherkin templates for different test types const GHERKIN_TEMPLATES = { 'validation': { feature: 'Validáció', user: 'weboldal látogató', want: 'érvényes adatokat küldeni', value: 'sikeresen kapcsolatot felvenni' }, 'navigation': { feature: 'Navigáció', user: 'weboldal látogató', want: 'könnyen navigálni az oldalak között', value: 'gyorsan megtalálni a kívánt információt' }, 'performance': { feature: 'Teljesítmény', user: 'weboldal látogató', want: 'gyorsan betöltődő weboldalt', value: 'ne várjak a tartalom megjelenésére' }, 'security': { feature: 'Biztonság', user: 'weboldal rendszergazdája', want: 'megvédeni a rendszert támadásoktól', value: 'biztonságos működést biztosítani' } }; /** * Extract test case ID from test title */ function extractTestCaseId(title) { const match = title.match(/^(TC-\d+):/); return match ? match[1] : null; } /** * Determine functional area from test file path and title */ function determineFunctionalArea(testFilePath, title) { const filePath = testFilePath.toLowerCase(); if (filePath.includes('contact') || title.toLowerCase().includes('contact')) { return 'contact'; } if (filePath.includes('header') || title.toLowerCase().includes('navigation')) { return 'navigation'; } if (filePath.includes('page') || title.toLowerCase().includes('homepage')) { return 'homepage'; } if (title.toLowerCase().includes('responsive') || title.toLowerCase().includes('mobile')) { return 'responsive'; } if (title.toLowerCase().includes('performance') || title.toLowerCase().includes('lighthouse')) { return 'performance'; } if (title.toLowerCase().includes('security') || title.toLowerCase().includes('spam')) { return 'security'; } if (title.toLowerCase().includes('accessibility') || title.toLowerCase().includes('a11y')) { return 'accessibility'; } if (filePath.includes('api') || title.toLowerCase().includes('api')) { return 'api'; } return 'general'; } /** * Generate Gherkin scenario from test case */ function generateGherkinScenario(test, functionalArea) { const template = GHERKIN_TEMPLATES[functionalArea] || GHERKIN_TEMPLATES['validation']; // Extract test steps from title and description const title = test.title.replace(/^(TC-\d+):\s*/, ''); const steps = parseTestSteps(title, test); return `Feature: ${template.feature} As a ${template.user} I want to ${template.want} So that ${template.value} Background: Given a weboldal betöltött állapotban van Scenario: ${title} ${steps.map(step => ` ${step}`).join('\n')} # Test Execution Details # Status: ${test.status.toUpperCase()} # Duration: ${test.duration}ms # Last Run: ${new Date().toISOString()} # File: ${test.file}`; } /** * Parse test steps from title and generate Gherkin steps */ function parseTestSteps(title, test) { const steps = []; const titleLower = title.toLowerCase(); // Common patterns for Given/When/Then if (titleLower.includes('should') || titleLower.includes('validates')) { steps.push('Given a felhasználó a weboldalon van'); steps.push('When a megfelelő műveletet végzi'); steps.push('Then a várt eredmény következik be'); } if (titleLower.includes('email') && titleLower.includes('validation')) { steps.push('Given a felhasználó a kapcsolat űrlapon van'); steps.push('When érvénytelen email címet ad meg'); steps.push('Then hibaüzenet jelenik meg'); steps.push('And az űrlap nem kerül elküldésre'); } if (titleLower.includes('rate limiting')) { steps.push('Given a felhasználó elérte a rate limitet'); steps.push('When új kérést próbál küldeni'); steps.push('Then 429 Too Many Requests választ kap'); steps.push('And a kérés nem kerül feldolgozásra'); } if (titleLower.includes('responsive') || titleLower.includes('mobile')) { steps.push('Given a felhasználó mobil eszközön van'); steps.push('When megnyitja a weboldalt'); steps.push('Then a hamburger menü látható'); steps.push('And a layout mobilra optimalizált'); } if (titleLower.includes('performance') || titleLower.includes('lighthouse')) { steps.push('Given a Lighthouse audit futtatásra kerül'); steps.push('When a teljesítmény mérés befejeződik'); steps.push('Then a Performance score ≥ 90'); steps.push('And a betöltési idő < 3 másodperc'); } // Default steps if no pattern matches if (steps.length === 0) { steps.push('Given a felhasználó a weboldalon van'); steps.push('When a megfelelő műveletet végzi'); steps.push('Then a várt eredmény következik be'); } return steps; } /** * Generate functional area analysis */ function generateFunctionalAnalysis(testResults) { const analysis = {}; // Handle different test result formats const testSuites = testResults.testResults || []; testSuites.forEach(suite => { const tests = suite.assertionResults || suite.testResults || []; tests.forEach(test => { const tcId = extractTestCaseId(test.title); if (!tcId) return; const functionalArea = determineFunctionalArea(suite.name || suite.testFilePath, test.title); const areaName = FUNCTIONAL_AREAS[functionalArea] || 'Általános'; if (!analysis[areaName]) { analysis[areaName] = { total: 0, passed: 0, failed: 0, skipped: 0, tests: [] }; } analysis[areaName].total++; analysis[areaName][test.status]++; analysis[areaName].tests.push({ id: tcId, title: test.title, status: test.status, duration: test.duration }); }); }); return analysis; } /** * Generate coverage dashboard */ function generateCoverageDashboard(analysis) { let dashboard = `# Test Coverage Dashboard - ${new Date().toISOString().split('T')[0]} ## 📊 Összefoglaló `; let totalTests = 0; let totalPassed = 0; let totalFailed = 0; let totalSkipped = 0; Object.values(analysis).forEach(area => { totalTests += area.total; totalPassed += area.passed; totalFailed += area.failed; totalSkipped += area.skipped; }); const successRate = totalTests > 0 ? Math.round((totalPassed / totalTests) * 100) : 0; dashboard += `- **Összes teszt**: ${totalTests} - **Sikeres**: ${totalPassed} (${successRate}%) - **Sikertelen**: ${totalFailed} (${Math.round((totalFailed / totalTests) * 100)}%) - **Kihagyott**: ${totalSkipped} (${Math.round((totalSkipped / totalTests) * 100)}%) ## 🎯 Területenkénti Elemzés `; Object.entries(analysis).forEach(([areaName, data]) => { const successRate = data.total > 0 ? Math.round((data.passed / data.total) * 100) : 0; let status = '✅ Kiváló'; if (successRate < 80) status = '🔴 Kritikus'; else if (successRate < 90) status = '⚠️ Figyelendő'; dashboard += `### ${areaName} - **Tesztesetek**: ${data.total} - **Sikeres**: ${data.passed} (${successRate}%) - **Sikertelen**: ${data.failed} (${Math.round((data.failed / data.total) * 100)}%) - **Státusz**: ${status} - **Lemaradás**: ${data.failed > 0 ? 'Van' : 'Nincs'} `; }); // Critical issues const criticalIssues = Object.entries(analysis) .filter(([_, data]) => data.failed > 0) .map(([areaName, data]) => `${areaName}: ${data.failed} sikertelen teszt`); if (criticalIssues.length > 0) { dashboard += `## 🚨 Kritikus Lemaradások ${criticalIssues.map(issue => `1. **${issue}**`).join('\n')} `; } dashboard += `## 📈 Javaslatok 1. Sikertelen tesztek javítása 2. Hiányzó tesztesetek implementálása 3. Performance optimalizálás 4. Monitoring beállítása --- *Generálva: ${new Date().toISOString()}* `; return dashboard; } /** * Main execution */ async function main() { console.log('🥒 Generating Gherkin test reports...'); try { // Read test results const resultsPath = process.argv[2] || 'proto/test-results.json'; const testResults = JSON.parse(fs.readFileSync(resultsPath, 'utf8')); // Generate functional analysis const analysis = generateFunctionalAnalysis(testResults); // Generate Gherkin scenarios for each test case const gherkinScenarios = {}; const testSuites = testResults.testResults || []; testSuites.forEach(suite => { const tests = suite.assertionResults || suite.testResults || []; tests.forEach(test => { const tcId = extractTestCaseId(test.title); if (!tcId) return; const functionalArea = determineFunctionalArea(suite.name || suite.testFilePath, test.title); const gherkin = generateGherkinScenario(test, functionalArea); gherkinScenarios[tcId] = { gherkin, functionalArea, test: { ...test, file: suite.name || suite.testFilePath } }; }); }); // Generate coverage dashboard const dashboard = generateCoverageDashboard(analysis); // Save reports fs.writeFileSync('gherkin-scenarios.json', JSON.stringify(gherkinScenarios, null, 2)); fs.writeFileSync('test-coverage-dashboard.md', dashboard); console.log('✅ Gherkin reports generated successfully!'); console.log(`📊 Functional areas analyzed: ${Object.keys(analysis).length}`); console.log(`🥒 Gherkin scenarios generated: ${Object.keys(gherkinScenarios).length}`); console.log(`📈 Coverage dashboard: test-coverage-dashboard.md`); // Display summary console.log('\n📋 Summary:'); Object.entries(analysis).forEach(([areaName, data]) => { const successRate = Math.round((data.passed / data.total) * 100); console.log(` ${areaName}: ${data.passed}/${data.total} (${successRate}%)`); }); } catch (error) { console.error('❌ Error generating Gherkin reports:', error.message); process.exit(1); } } if (require.main === module) { main(); } module.exports = { extractTestCaseId, determineFunctionalArea, generateGherkinScenario, generateFunctionalAnalysis, generateCoverageDashboard };