UNPKG

estructura_automation

Version:

Paquete de estructura de automation de paguetodo

616 lines (517 loc) 21.4 kB
/* eslint-disable @typescript-eslint/no-require-imports */ /* eslint-disable no-console */ /** * ======================================================================================== * GENERADOR DE CASOS DE PRUEBA A PARTIR DE ARCHIVOS GHERKIN * ---------------------------------------------------------------------------------------- * Este script genera un documento HTML con estructura de tarjetas y breadcrumbs * siguiendo la jerarquía: App → Módulo → Caso de Prueba → Pasos * ======================================================================================== */ // Importaciones necesarias const fs = require('fs'); const path = require('path'); // Configuración: Directorio base donde buscar apps y módulos const FEATURES_DIR = path.resolve(path.join(__dirname, '../../documentation')); const OUTPUT_FILE = path.resolve(path.join(__dirname, '../index.html')); // Verificar que el directorio existe if (fs.existsSync(FEATURES_DIR)) { fs.readdirSync(FEATURES_DIR); } else { console.error('El directorio documentation NO existe'); process.exit(1); } /** * Analiza el contenido de un archivo .feature y extrae su estructura * @param {string} content - Contenido del archivo .feature * @returns {Object} Objeto estructurado con escenarios y sus pasos */ const parseFeatureFile = (content) => { const lines = content.split('\n'); const feature = { scenarios: [] }; let currentScenario = null; let inScenario = false; lines.forEach(line => { const trimmedLine = line.trim(); if (trimmedLine.startsWith('Scenario:') || trimmedLine.startsWith('Scenario Outline:')) { inScenario = true; if (currentScenario) { feature.scenarios.push(currentScenario); } currentScenario = { name: trimmedLine.substring(trimmedLine.indexOf(':') + 1).trim(), steps: [] }; } else if (inScenario && ( trimmedLine.startsWith('Given ') || trimmedLine.startsWith('When ') || trimmedLine.startsWith('Then ') || trimmedLine.startsWith('And ') || trimmedLine.startsWith('But ') )) { // Eliminar las palabras reservadas de Gherkin completamente let stepText = trimmedLine; // Eliminar palabras reservadas al inicio de la línea stepText = stepText.replace(/^Given /, ''); stepText = stepText.replace(/^When /, ''); stepText = stepText.replace(/^Then /, ''); stepText = stepText.replace(/^And /, ''); stepText = stepText.replace(/^But /, ''); currentScenario.steps.push(stepText); } }); if (currentScenario) { feature.scenarios.push(currentScenario); } return feature; }; /** * Recorre recursivamente el directorio de features y construye la estructura jerárquica * @returns {Object} Estructura de datos organizada jerárquicamente */ const buildHierarchicalData = () => { const hierarchicalData = { apps: {} }; // Verificar que el directorio existe if (!fs.existsSync(FEATURES_DIR)) { console.error(`El directorio de features no existe: ${FEATURES_DIR}`); return hierarchicalData; } // Leer el directorio principal para encontrar las apps const appDirs = fs.readdirSync(FEATURES_DIR, { withFileTypes: true }) .filter(entry => entry.isDirectory()) .map(dir => dir.name); // Procesar cada app appDirs.forEach((appName) => { const appId = appName.toLowerCase().replace(/\s+/g, '-'); hierarchicalData.apps[appId] = { id: appId, name: formatName(appName), modules: {} }; const appPath = path.join(FEATURES_DIR, appName); try { // Leer módulos (directorios dentro de la app) const moduleDirs = fs.readdirSync(appPath, { withFileTypes: true }) .filter(entry => entry.isDirectory()) .map(dir => dir.name); // Procesar cada módulo moduleDirs.forEach((moduleName) => { const moduleId = `${appId}_${moduleName.toLowerCase().replace(/\s+/g, '-')}`; const modulePath = path.join(appPath, moduleName); hierarchicalData.apps[appId].modules[moduleId] = { id: moduleId, name: formatName(moduleName), testCases: [], subModules: {} }; // Buscar archivos .feature en este módulo const featureFiles = fs.readdirSync(modulePath) .filter(file => file.endsWith('.feature')); // Procesar los archivos .feature en este módulo si existen featureFiles.forEach((featureFile, fileIndex) => { const featureContent = fs.readFileSync(path.join(modulePath, featureFile), 'utf8'); const feature = parseFeatureFile(featureContent); // Agregar cada escenario como un caso de prueba feature.scenarios.forEach((scenario, scenarioIndex) => { const testCaseId = `${moduleId}_tc_${fileIndex+1}_${scenarioIndex+1}`; const testCase = { id: testCaseId, name: scenario.name, steps: scenario.steps.map((step, stepIndex) => ({ id: `${testCaseId}_step_${stepIndex+1}`, text: step })) }; hierarchicalData.apps[appId].modules[moduleId].testCases.push(testCase); }); }); // Buscar subdirectorios (para estructura de 4 niveles) try { const subDirs = fs.readdirSync(modulePath, { withFileTypes: true }) .filter(entry => entry.isDirectory()) .map(dir => dir.name); // Procesar cada subdirectorio como submódulo subDirs.forEach((subDirName) => { const subDirPath = path.join(modulePath, subDirName); const subModuleId = `${moduleId}_${subDirName.toLowerCase().replace(/\s+/g, '-')}`; // Crear submódulo hierarchicalData.apps[appId].modules[moduleId].subModules[subModuleId] = { id: subModuleId, name: formatName(subDirName), testCases: [] }; // Buscar archivos .feature en este subdirectorio const subFeatureFiles = fs.readdirSync(subDirPath) .filter(file => file.endsWith('.feature')); // Procesar los archivos .feature en este subdirectorio subFeatureFiles.forEach((featureFile, fileIndex) => { const featureContent = fs.readFileSync(path.join(subDirPath, featureFile), 'utf8'); const feature = parseFeatureFile(featureContent); // Agregar cada escenario como un caso de prueba del submódulo feature.scenarios.forEach((scenario, scenarioIndex) => { const testCaseId = `${subModuleId}_tc_${fileIndex+1}_${scenarioIndex+1}`; const testCase = { id: testCaseId, name: scenario.name, steps: scenario.steps.map((step, stepIndex) => ({ id: `${testCaseId}_step_${stepIndex+1}`, text: step })) }; hierarchicalData.apps[appId].modules[moduleId].subModules[subModuleId].testCases.push(testCase); }); }); }); } catch (subDirError) { // Si hay error al leer subdirectorios, simplemente seguimos con el siguiente módulo console.error(`Error al procesar subdirectorios en ${modulePath}: ${subDirError.message}`); } }); } catch (error) { console.error(`Error al procesar app ${appName}: ${error.message}`); } }); return hierarchicalData; }; /** * Formatea un nombre para visualización * @param {string} name - Nombre a formatear * @returns {string} Nombre formateado */ const formatName = (name) => { return name.charAt(0).toUpperCase() + name.slice(1).replace(/_/g, ' '); }; /** * Genera el HTML final con todas las aplicaciones, módulos y casos de prueba */ const generateHTML = (hierarchicalData) => { const fecha = new Date().toLocaleDateString('es-ES'); // Verificar que existan aplicaciones const appIds = Object.keys(hierarchicalData.apps); if (appIds.length === 0) { console.error('No se encontraron carpetas dentro de la carpeta documentation'); process.exit(1); } // Usar la primera aplicación disponible const appId = appIds[0]; const app = hierarchicalData.apps[appId]; let html = ` <!DOCTYPE html> <html lang="es"> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <title>Casos de Prueba - Paguetodo</title> <link href="https://fonts.googleapis.com/css2?family=Inter:wght@400;500;600;700&display=swap" rel="stylesheet"> <link href="styles/styles.css" rel="stylesheet"> </head> <body> <div class="logo-container"> <img src="styles/logo_paguetodo.png" alt="PagueTodo Logo" class="company-logo"> </div> <div class="container"> <h1>Casos de Prueba - Paguetodo</h1> <div class="fecha">Fecha de generación: ${fecha}</div> <div class="search-box"> <input type="text" id="searchInput" placeholder="Buscar por nombre o ID de caso..."> <button id="searchButton">Buscar</button> </div> <div id="content"> <!-- Vistas de módulos para la aplicación principal --> <div id="${appId}-modules-view" class="modules-view"> <div class="breadcrumbs"> <a href="http://127.0.0.1:5501/report/matrizPrueba.html">Aplicaciones</a> <span class="separator">/</span> <span>${app.name}</span> </div> <div class="grid-container">`; // Generar tarjetas para cada módulo Object.values(app.modules).forEach(module => { const totalTestCases = module.testCases.length; const totalSubModules = Object.keys(module.subModules || {}).length; // Si hay submódulos, el enlace muestra los submódulos, si no hay, muestra los casos de prueba directamente const onClickAction = totalSubModules > 0 ? `showSubModules('${appId}', '${module.id}')` : `showTestCases('${appId}', '${module.id}', 'module')`; html += ` <div class="card" onclick="${onClickAction}"> <div class="card-header">${module.name}</div> <div class="card-content"> <div class="count-badge"> <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"> <path d="M22 11.08V12a10 10 0 1 1-5.93-9.14"></path> <polyline points="22 4 12 14.01 9 11.01"></polyline> </svg> ${totalTestCases > 0 ? `${totalTestCases} caso${totalTestCases !== 1 ? 's' : ''} de prueba` : ''} ${totalTestCases > 0 && totalSubModules > 0 ? ' y ' : ''} ${totalSubModules > 0 ? `${totalSubModules} submódulo${totalSubModules !== 1 ? 's' : ''}` : ''} </div> </div> </div>`; }); html += ` </div> </div>`; // Generar vistas de submódulos para cada módulo Object.values(app.modules).forEach(module => { if (Object.keys(module.subModules || {}).length > 0) { html += ` <div id="${module.id}-submodules-view" class="submodules-view hidden"> <div class="breadcrumbs"> <a href="http://127.0.0.1:5501/report/matrizPrueba.html">Aplicaciones</a> <span class="separator">/</span> <a href="#" onclick="showModules('${appId}'); return false;">${app.name}</a> <span class="separator">/</span> <span>${module.name}</span> </div> <div class="grid-container">`; // Generar tarjetas para cada submódulo Object.values(module.subModules).forEach(subModule => { const totalTestCases = subModule.testCases.length; html += ` <div class="card" onclick="showTestCases('${appId}', '${subModule.id}', 'submodule')"> <div class="card-header">${subModule.name}</div> <div class="card-content"> <div class="count-badge"> <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"> <path d="M22 11.08V12a10 10 0 1 1-5.93-9.14"></path> <polyline points="22 4 12 14.01 9 11.01"></polyline> </svg> ${totalTestCases} caso${totalTestCases !== 1 ? 's' : ''} de prueba </div> </div> </div>`; }); html += ` </div> </div>`; } // Generar vistas de casos de prueba para cada módulo html += ` <div id="${module.id}-testcases-view" class="testcases-view hidden"> <div class="breadcrumbs"> <a href="http://127.0.0.1:5501/report/matrizPrueba.html">Aplicaciones</a> <span class="separator">/</span> <a href="#" onclick="showModules('${appId}'); return false;">${app.name}</a> <span class="separator">/</span> <span>${module.name}</span> </div> <div class="grid-container">`; // Generar tarjetas para cada caso de prueba del módulo module.testCases.forEach(testCase => { const totalSteps = testCase.steps.length; html += ` <div class="card test-case-card" data-id="${testCase.id}" data-name="${testCase.name}" onclick="showSteps('${testCase.id}')"> <div class="card-header">${testCase.name}</div> <div class="card-content"> <div class="count-badge"> <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"> <polyline points="9 11 12 14 22 4"></polyline> <path d="M21 12v7a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2V5a2 2 0 0 1 2-2h11"></path> </svg> ${totalSteps} paso${totalSteps !== 1 ? 's' : ''} </div> <div class="test-id-badge">ID: ${testCase.id}</div> </div> </div>`; }); html += ` </div> </div>`; // Generar vistas de casos de prueba para cada submódulo Object.values(module.subModules || {}).forEach(subModule => { html += ` <div id="${subModule.id}-testcases-view" class="testcases-view hidden"> <div class="breadcrumbs"> <a href="http://127.0.0.1:5501/report/matrizPrueba.html">Aplicaciones</a> <span class="separator">/</span> <a href="#" onclick="showModules('${appId}'); return false;">${app.name}</a> <span class="separator">/</span> <a href="#" onclick="showSubModules('${appId}', '${module.id}'); return false;">${module.name}</a> <span class="separator">/</span> <span>${subModule.name}</span> </div> <div class="grid-container">`; // Generar tarjetas para cada caso de prueba del submódulo subModule.testCases.forEach(testCase => { const totalSteps = testCase.steps.length; html += ` <div class="card test-case-card" data-id="${testCase.id}" data-name="${testCase.name}" onclick="showSteps('${testCase.id}')"> <div class="card-header">${testCase.name}</div> <div class="card-content"> <div class="count-badge"> <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"> <polyline points="9 11 12 14 22 4"></polyline> <path d="M21 12v7a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2V5a2 2 0 0 1 2-2h11"></path> </svg> ${totalSteps} paso${totalSteps !== 1 ? 's' : ''} </div> <div class="test-id-badge">ID: ${testCase.id}</div> </div> </div>`; }); html += ` </div> </div>`; }); // Generar vistas de pasos para cada caso de prueba del módulo module.testCases.forEach(testCase => { html += ` <div id="${testCase.id}-steps-view" class="steps-view hidden"> <div class="breadcrumbs"> <a href="http://127.0.0.1:5501/report/matrizPrueba.html">Aplicaciones</a> <span class="separator">/</span> <a href="#" onclick="showModules('${appId}'); return false;">${app.name}</a> <span class="separator">/</span> <a href="#" onclick="showTestCases('${appId}', '${module.id}', 'module'); return false;">${module.name}</a> <span class="separator">/</span> <span>${testCase.name}</span> </div> <div class="test-steps-container"> <div class="test-steps-header"> <span>${testCase.name}</span> <span class="test-case-id">ID: ${testCase.id}</span> </div>`; // Generar elementos para cada paso testCase.steps.forEach(step => { html += ` <div class="step-item">${step.text}</div>`; }); html += ` </div> </div>`; }); // Generar vistas de pasos para cada caso de prueba de los submódulos Object.values(module.subModules || {}).forEach(subModule => { subModule.testCases.forEach(testCase => { html += ` <div id="${testCase.id}-steps-view" class="steps-view hidden"> <div class="breadcrumbs"> <a href="http://127.0.0.1:5501/report/matrizPrueba.html">Aplicaciones</a> <span class="separator">/</span> <a href="#" onclick="showModules('${appId}'); return false;">${app.name}</a> <span class="separator">/</span> <a href="#" onclick="showSubModules('${appId}', '${module.id}'); return false;">${module.name}</a> <span class="separator">/</span> <a href="#" onclick="showTestCases('${appId}', '${subModule.id}', 'submodule'); return false;">${subModule.name}</a> <span class="separator">/</span> <span>${testCase.name}</span> </div> <div class="test-steps-container"> <div class="test-steps-header"> <span>${testCase.name}</span> <span class="test-case-id">ID: ${testCase.id}</span> </div>`; // Generar elementos para cada paso testCase.steps.forEach(step => { html += ` <div class="step-item">${step.text}</div>`; }); html += ` </div> </div>`; }); }); }); html += ` </div> </div> <script> // Funciones de navegación const showModules = (appId) => { // Ocultar todas las vistas hideAllViews(); // Mostrar la vista de módulos para esta aplicación document.getElementById(appId + '-modules-view').classList.remove('hidden'); } const showSubModules = (appId, moduleId) => { // Ocultar todas las vistas hideAllViews(); // Mostrar la vista de submódulos para este módulo document.getElementById(moduleId + '-submodules-view').classList.remove('hidden'); } const showTestCases = (appId, entityId, type) => { // Ocultar todas las vistas hideAllViews(); // Mostrar la vista de casos de prueba para este módulo o submódulo document.getElementById(entityId + '-testcases-view').classList.remove('hidden'); } const showSteps = (testCaseId) => { // Ocultar todas las vistas hideAllViews(); // Mostrar la vista de pasos para este caso de prueba document.getElementById(testCaseId + '-steps-view').classList.remove('hidden'); } const hideAllViews = () => { // Ocultar vistas de módulos const moduleViews = document.querySelectorAll('.modules-view'); moduleViews.forEach(view => view.classList.add('hidden')); // Ocultar vistas de submódulos const subModuleViews = document.querySelectorAll('.submodules-view'); subModuleViews.forEach(view => view.classList.add('hidden')); // Ocultar vistas de casos de prueba const testCaseViews = document.querySelectorAll('.testcases-view'); testCaseViews.forEach(view => view.classList.add('hidden')); // Ocultar vistas de pasos const stepViews = document.querySelectorAll('.steps-view'); stepViews.forEach(view => view.classList.add('hidden')); } // Funcionalidad de búsqueda document.addEventListener('DOMContentLoaded', function() { const searchInput = document.getElementById('searchInput'); const searchButton = document.getElementById('searchButton'); const searchTestCases = () => { const searchTerm = searchInput.value.toLowerCase(); if (!searchTerm) return; const testCards = document.querySelectorAll('.test-case-card'); let found = false; testCards.forEach(card => { card.classList.remove('highlight'); const cardName = card.getAttribute('data-name').toLowerCase(); const cardId = card.getAttribute('data-id').toLowerCase(); if (cardName.includes(searchTerm) || cardId.includes(searchTerm)) { // Mostrar la vista de casos de prueba y resaltar la tarjeta showSteps(cardId); // Resaltar la tarjeta encontrada card.classList.add('highlight'); card.scrollIntoView({ behavior: 'smooth', block: 'center' }); found = true; } }); if (!found) { alert('No se encontraron casos de prueba que coincidan con la búsqueda.'); } } searchButton.addEventListener('click', searchTestCases); searchInput.addEventListener('keypress', (e) => { if (e.key === 'Enter') { searchTestCases(); } }); // Mostrar la vista de módulos al cargar la página showModules('` + appId + `'); }); </script> </body> </html>`; return html; }; /** * Función principal que ejecuta el generador */ const main = () => { try { const hierarchicalData = buildHierarchicalData(); const html = generateHTML(hierarchicalData); fs.writeFileSync(OUTPUT_FILE, html); } catch (error) { console.error(`Error en la generación: ${error.message}`); console.error(error.stack); } }; // Ejecutar el script main();