estructura_automation
Version:
Paquete de estructura de automation
249 lines (214 loc) • 7.04 kB
JavaScript
/**
* ========================================================================================
* METADATA BUILDER - Generación de Metadata de Tests
* ----------------------------------------------------------------------------------------
* Responsabilidades:
* - Generar metadata JSON para sincronización con tests automatizados
* - Calcular estadísticas de tests (manuales vs automáticos)
* - Crear mapas de rutas y identificadores
* - Estructurar datos para sistemas externos
* ========================================================================================
*/
/**
* Genera metadata JSON para sincronización con tests automatizados
* @param {Object} hierarchicalData - Estructura de datos jerárquica
* @returns {Object} Metadata de tests
*/
const generateTestsMetadata = (hierarchicalData) => {
const tests = [];
/**
* Procesa recursivamente un nodo para extraer todos los tests
* @param {Object} node - Nodo actual
* @param {Array} pathArray - Array con la ruta hasta este nodo
*/
const processNode = (node, pathArray = []) => {
// Procesar test cases del nodo actual
node.testCases.forEach(testCase => {
tests.push({
id: testCase.id,
name: testCase.name,
path: pathArray.join('/'),
automatable: !testCase.isManual,
isManual: testCase.isManual,
tags: testCase.tags,
featureFile: testCase.featureFile,
stepCount: testCase.steps.length,
steps: testCase.steps.map(step => ({
id: step.id,
text: step.text
}))
});
});
// Procesar recursivamente todos los hijos
Object.values(node.children || {}).forEach(child => {
processNode(child, [...pathArray, child.name]);
});
};
// Procesar todas las aplicaciones
Object.values(hierarchicalData.apps).forEach(app => {
processNode(app, [app.name]);
});
// Calcular estadísticas
const manualTests = tests.filter(t => t.isManual);
const automatableTests = tests.filter(t => !t.isManual);
return {
generatedAt: new Date().toISOString(),
totalTests: tests.length,
manualTests: manualTests.length,
automatableTests: automatableTests.length,
manualPercentage: Math.round((manualTests.length / tests.length) * 100),
automatablePercentage: Math.round((automatableTests.length / tests.length) * 100),
tests: tests,
// Estadísticas adicionales
statistics: {
totalSteps: tests.reduce((sum, test) => sum + test.stepCount, 0),
averageStepsPerTest: Math.round(tests.reduce((sum, test) => sum + test.stepCount, 0) / tests.length),
testsByApp: getTestsByApp(hierarchicalData),
tagStatistics: getTagStatistics(tests)
}
};
};
/**
* Calcula estadísticas de tests por aplicación
* @param {Object} hierarchicalData - Datos jerárquicos
* @returns {Object} Estadísticas por app
*/
const getTestsByApp = (hierarchicalData) => {
const appStats = {};
Object.entries(hierarchicalData.apps).forEach(([appId, app]) => {
const appTests = [];
const collectTests = (node) => {
appTests.push(...node.testCases);
Object.values(node.children || {}).forEach(child => {
collectTests(child);
});
};
collectTests(app);
appStats[appId] = {
name: app.name,
totalTests: appTests.length,
manualTests: appTests.filter(t => t.isManual).length,
automatableTests: appTests.filter(t => !t.isManual).length
};
});
return appStats;
};
/**
* Calcula estadísticas de tags utilizados
* @param {Array} tests - Array de tests
* @returns {Object} Estadísticas de tags
*/
const getTagStatistics = (tests) => {
const tagCounts = {};
tests.forEach(test => {
test.tags.forEach(tag => {
tagCounts[tag] = (tagCounts[tag] || 0) + 1;
});
});
return {
totalUniqueTags: Object.keys(tagCounts).length,
tagCounts: tagCounts,
mostCommonTags: Object.entries(tagCounts)
.sort(([,a], [,b]) => b - a)
.slice(0, 5)
.map(([tag, count]) => ({ tag, count }))
};
};
/**
* Genera un mapa de rutas para navegación rápida
* @param {Object} hierarchicalData - Datos jerárquicos
* @returns {Object} Mapa de rutas
*/
const generatePathMap = (hierarchicalData) => {
const pathMap = {};
const processNode = (node, pathArray = []) => {
const currentPath = [...pathArray, node.name];
pathMap[node.id] = {
path: currentPath,
breadcrumb: currentPath.join(' / '),
hasSubfolders: node.hasSubfolders,
hasFeatures: node.hasFeatures,
testCount: node.testCases.length
};
// Procesar hijos recursivamente
Object.values(node.children || {}).forEach(child => {
processNode(child, currentPath);
});
};
Object.values(hierarchicalData.apps).forEach(app => {
processNode(app, []);
});
return pathMap;
};
/**
* Valida la integridad de los datos jerárquicos
* @param {Object} hierarchicalData - Datos a validar
* @returns {Object} Reporte de validación
*/
const validateHierarchicalData = (hierarchicalData) => {
const errors = [];
const warnings = [];
const stats = {
totalNodes: 0,
totalTests: 0,
duplicateIds: []
};
const seenIds = new Set();
const validateNode = (node, path = []) => {
stats.totalNodes++;
// Verificar ID único
if (seenIds.has(node.id)) {
errors.push(`ID duplicado encontrado: ${node.id} en ${path.join('/')}`);
stats.duplicateIds.push(node.id);
} else {
seenIds.add(node.id);
}
// Verificar estructura del nodo
if (!node.name || typeof node.name !== 'string') {
errors.push(`Nodo sin nombre válido: ${node.id}`);
}
if (!Array.isArray(node.testCases)) {
errors.push(`TestCases no es array en nodo: ${node.id}`);
} else {
stats.totalTests += node.testCases.length;
// Validar test cases
node.testCases.forEach(testCase => {
if (!testCase.id || !testCase.name) {
errors.push(`Test case inválido en ${node.id}: falta ID o nombre`);
}
if (!Array.isArray(testCase.steps)) {
errors.push(`Steps no es array en test: ${testCase.id}`);
}
});
}
// Validar flags de consistencia
const actualHasSubfolders = Object.keys(node.children || {}).length > 0;
const actualHasFeatures = node.testCases.length > 0;
if (node.hasSubfolders !== actualHasSubfolders) {
warnings.push(`Flag hasSubfolders inconsistente en ${node.id}`);
}
if (node.hasFeatures !== actualHasFeatures) {
warnings.push(`Flag hasFeatures inconsistente en ${node.id}`);
}
// Validar hijos recursivamente
Object.values(node.children || {}).forEach(child => {
validateNode(child, [...path, node.name]);
});
};
Object.values(hierarchicalData.apps || {}).forEach(app => {
validateNode(app, []);
});
return {
isValid: errors.length === 0,
errors,
warnings,
stats
};
};
module.exports = {
generateTestsMetadata,
generatePathMap,
validateHierarchicalData,
getTestsByApp,
getTagStatistics
};