UNPKG

estructura_automation

Version:

Paquete de estructura de automation

429 lines (373 loc) 14.4 kB
/** * ======================================================================================== * HTML GENERATOR - Generación de Documentación Web Interactiva * ---------------------------------------------------------------------------------------- * Responsabilidades: * - Generar HTML principal con estructura completa * - Crear vistas dinámicas para navegación jerárquica * - Generar breadcrumbs y navegación * - Implementar sistema de búsqueda inteligente * - Embeber JavaScript para interactividad * ======================================================================================== */ /** * Genera breadcrumbs dinámicos con navegación completa hacia atrás * @param {Array} pathArray - Array con la ruta * @returns {string} HTML de breadcrumbs */ const generateBreadcrumbs = (pathArray) => { let breadcrumbs = `<a href="http://127.0.0.1:5501/report/matrizPrueba.html">Aplicaciones</a>`; // Hacer clickeables todos los niveles anteriores (excepto el último) pathArray.forEach((pathItem, index) => { breadcrumbs += `<span class="separator">/</span>`; if (index < pathArray.length - 1) { // Elementos intermedios - clickeables para navegar hacia atrás const nodeIdParts = pathArray.slice(0, index + 1).map(part => part.toLowerCase().replace(/\s+/g, '-') ); const nodeId = nodeIdParts.join('_'); breadcrumbs += `<a href="#" onclick="navigateToNode('${nodeId}'); return false;">${pathItem}</a>`; } else { // Último elemento - no clickeable (es la página actual) breadcrumbs += `<span>${pathItem}</span>`; } }); return breadcrumbs; }; /** * Genera vistas dinámicas para cualquier nodo (carpeta o conjunto de test cases) * @param {Object} node - Nodo actual * @param {Array} pathArray - Ruta completa hasta este nodo * @returns {string} HTML generado */ const generateNodeViews = (node, pathArray) => { let html = ''; // 1. VISTA DE NAVEGACIÓN (si tiene subcarpetas o test cases) if (node.hasSubfolders || node.hasFeatures) { const viewId = `${node.id}-view`; const fullPath = [...pathArray, node.name]; // Solo la vista raíz (sin pathArray) se muestra inicialmente const isRootView = pathArray.length === 0; const hiddenClass = isRootView ? '' : 'hidden'; html += ` <div id="${viewId}" class="node-view ${hiddenClass}"> <div class="breadcrumbs"> ${generateBreadcrumbs(fullPath)} </div> <div class="grid-container">`; // MOSTRAR SUBCARPETAS PRIMERO (ordenadas) Object.values(node.children || {}).forEach(child => { const childTestCount = child.testCases.length; const childSubfolderCount = Object.keys(child.children || {}).length; html += ` <div class="card" onclick="navigateToNode('${child.id}')"> <div class="card-header">${child.name}</div> <div class="card-content">`; if (childSubfolderCount > 0) { html += ` <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 19a2 2 0 0 1-2 2H4a2 2 0 0 1-2-2V5a2 2 0 0 1 2-2l5 0 2 3h9a2 2 0 0 1 2 2z"></path> </svg> ${childSubfolderCount} carpeta${childSubfolderCount !== 1 ? 's' : ''} </div>`; } if (childTestCount > 0) { html += ` <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> ${childTestCount} test${childTestCount !== 1 ? 's' : ''} </div>`; } html += ` </div> </div>`; }); // MOSTRAR TEST CASES DESPUÉS (si los hay en este nivel) node.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> ${testCase.isManual ? ` <div class="manual-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="M14.7 6.3a1 1 0 0 0 0 1.4l1.6 1.6a1 1 0 0 0 1.4 0l3.77-3.77a6 6 0 0 1-7.94 7.94l-6.91 6.91a2.12 2.12 0 0 1-3-3l6.91-6.91a6 6 0 0 1 7.94-7.94l-3.76 3.76z"></path> </svg> Manual </div>` : ''} <div class="test-id-badge">ID: ${testCase.id}</div> </div> </div>`; }); html += ` </div> </div>`; } // 2. VISTAS DE PASOS para cada test case node.testCases.forEach(testCase => { const fullPath = [...pathArray, node.name]; html += ` <div id="${testCase.id}-steps-view" class="steps-view hidden"> <div class="breadcrumbs"> ${generateBreadcrumbs([...fullPath, testCase.name])} </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>`; testCase.steps.forEach(step => { html += `<div class="step-item">${step.text}</div>`; }); html += ` </div> </div>`; }); // 3. RECURSIÓN para todos los hijos Object.values(node.children || {}).forEach(child => { html += generateNodeViews(child, [...pathArray, node.name]); }); return html; }; /** * Genera el JavaScript embebido para la funcionalidad del frontend * @returns {string} Código JavaScript completo */ const generateEmbeddedJavaScript = () => { return ` <script> // Sistema de navegación dinámico universal const navigateToNode = (nodeId) => { hideAllViews(); const targetView = document.getElementById(nodeId + '-view'); if (targetView) { targetView.classList.remove('hidden'); } else { console.error('Vista no encontrada:', nodeId + '-view'); } } const showSteps = (testCaseId) => { hideAllViews(); const targetView = document.getElementById(testCaseId + '-steps-view'); if (targetView) { targetView.classList.remove('hidden'); } } const hideAllViews = () => { const allViews = document.querySelectorAll('.node-view, .steps-view'); allViews.forEach(view => view.classList.add('hidden')); // Limpiar vistas de búsqueda const searchView = document.getElementById('search-results-view'); if (searchView) { searchView.remove(); } } // Funcionalidad de búsqueda mejorada document.addEventListener('DOMContentLoaded', function() { const searchInput = document.getElementById('searchInput'); const searchButton = document.getElementById('searchButton'); const searchTestCases = () => { const searchTerm = searchInput.value.toLowerCase().trim(); if (!searchTerm) { hideAllViews(); const firstView = document.querySelector('.node-view:not(.hidden)') || document.querySelector('.node-view'); if (firstView) { firstView.classList.remove('hidden'); } return; } const allTestCards = document.querySelectorAll('.test-case-card'); let exactMatch = null; let foundCards = []; allTestCards.forEach((card) => { const cardName = card.getAttribute('data-name'); const cardId = card.getAttribute('data-id'); if (!cardName || !cardId) return; const cardNameLower = cardName.toLowerCase(); const cardIdLower = cardId.toLowerCase(); if (cardIdLower === searchTerm) { exactMatch = card; return; } if (cardNameLower.includes(searchTerm) || cardIdLower.includes(searchTerm)) { foundCards.push(card); } }); if (exactMatch) { showSearchResults([exactMatch], searchTerm, true); } else if (foundCards.length > 0) { showSearchResults(foundCards, searchTerm, false); } else { alert('No se encontró: ' + searchTerm); } } const showSearchResults = (matchingCards, searchTerm, isExactMatch = false) => { // Limpiar vista anterior const oldView = document.getElementById('search-results-view'); if (oldView) oldView.remove(); // Crear nueva vista const searchResultsView = document.createElement('div'); searchResultsView.id = 'search-results-view'; searchResultsView.className = 'node-view'; const resultType = isExactMatch ? '🎯 ID Exacto' : '🔍 Resultados'; const breadcrumbsHTML = '<div class="breadcrumbs">' + '<a href="http://127.0.0.1:5501/report/matrizPrueba.html">Aplicaciones</a>' + '<span class="separator">/</span>' + '<span>' + resultType + ': "' + searchTerm + '" (' + matchingCards.length + ')</span>' + '</div>'; let cardsHTML = ''; matchingCards.forEach(card => { cardsHTML += card.outerHTML; }); const gridHTML = '<div class="grid-container">' + cardsHTML + '</div>'; searchResultsView.innerHTML = breadcrumbsHTML + gridHTML; // Ocultar todo y mostrar resultados hideAllViews(); document.getElementById('content').appendChild(searchResultsView); searchResultsView.style.display = 'block'; // Resaltar la tarjeta const newCards = searchResultsView.querySelectorAll('.test-case-card'); newCards.forEach(card => { card.classList.add('highlight'); }); } searchButton.addEventListener('click', function(e) { e.preventDefault(); searchTestCases(); }); searchInput.addEventListener('keypress', (e) => { if (e.key === 'Enter') { e.preventDefault(); searchTestCases(); } }); // La vista raíz ya está visible por defecto }); </script>`; }; /** * Genera el HTML principal completo * @param {Object} app - Datos de la aplicación * @param {Object} options - Opciones de configuración * @returns {string} HTML completo */ const generateHTML = (app, options = {}) => { const { title = 'Casos de Prueba - Paguetodo', logoPath = 'styles/logo_paguetodo.png', cssPath = 'styles/styles.css' } = options; const html = `<!DOCTYPE html> <html lang="es"> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <title>${title}</title> <link href="https://fonts.googleapis.com/css2?family=Inter:wght@400;500;600;700&display=swap" rel="stylesheet"> <link href="${cssPath}" rel="stylesheet"> </head> <body> <div class="logo-container"> <img src="${logoPath}" alt="PagueTodo Logo" class="company-logo"> </div> <div class="container"> <h1>${title}</h1> <div class="fecha">Fecha de generación: ${new Date().toLocaleDateString()}</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"> ${generateNodeViews(app, [])} </div> </div> ${generateEmbeddedJavaScript()} </body> </html>`; return html; }; /** * Genera un reporte de estadísticas en HTML * @param {Object} metadata - Metadata de tests * @returns {string} HTML del reporte */ const generateStatsReport = (metadata) => { const { statistics } = metadata; return ` <div class="stats-report"> <h2>📊 Estadísticas de Tests</h2> <div class="stats-grid"> <div class="stat-card"> <h3>Tests Totales</h3> <span class="stat-number">${metadata.totalTests}</span> </div> <div class="stat-card"> <h3>Tests Manuales</h3> <span class="stat-number">${metadata.manualTests}</span> <span class="stat-percentage">${metadata.manualPercentage}%</span> </div> <div class="stat-card"> <h3>Tests Automatizables</h3> <span class="stat-number">${metadata.automatableTests}</span> <span class="stat-percentage">${metadata.automatablePercentage}%</span> </div> <div class="stat-card"> <h3>Pasos Totales</h3> <span class="stat-number">${statistics.totalSteps}</span> </div> <div class="stat-card"> <h3>Promedio Pasos/Test</h3> <span class="stat-number">${statistics.averageStepsPerTest}</span> </div> <div class="stat-card"> <h3>Tags Únicos</h3> <span class="stat-number">${statistics.tagStatistics.totalUniqueTags}</span> </div> </div> </div>`; }; /** * Genera una vista de debugging para desarrollo * @param {Object} hierarchicalData - Datos jerárquicos * @param {Object} metadata - Metadata * @returns {string} HTML de debugging */ const generateDebugView = (hierarchicalData, metadata) => { return ` <div class="debug-view" style="display: none;" id="debug-view"> <h2>🔧 Vista de Debugging</h2> <details> <summary>Estructura Jerárquica</summary> <pre>${JSON.stringify(hierarchicalData, null, 2)}</pre> </details> <details> <summary>Metadata</summary> <pre>${JSON.stringify(metadata, null, 2)}</pre> </details> <button onclick="document.getElementById('debug-view').style.display='none'">Cerrar</button> </div> <button onclick="document.getElementById('debug-view').style.display='block'" style="position: fixed; top: 10px; right: 10px; z-index: 1000;"> 🔧 Debug </button>`; }; module.exports = { generateBreadcrumbs, generateNodeViews, generateHTML, generateEmbeddedJavaScript, generateStatsReport, generateDebugView };