estructura_automation
Version:
Paquete de estructura de automation de paguetodo
616 lines (517 loc) • 21.4 kB
JavaScript
/* 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 = `
<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();