/** * save-testcases.js * * Writes an array of legacy-shaped testcases back to the individual YAML files. * Tries to preserve existing per-step automation flags and custom data. * * This is part of the transition so that the editor can save directly to YAML. */ const fs = require('fs'); const path = require('path'); const yaml = require('js-yaml'); const TESTCASES_ROOT = path.join(__dirname, '..', '..', 'testcases'); function getYamlPath(legacyTest) { const project = 'pbx'; // transition: all current data lives under pbx project const module = legacyTest.module || 'unknown'; const rawGroup = legacyTest.group || 'unknown'; const group = rawGroup.replace(/^core-|^pbx-/, ''); return path.join(TESTCASES_ROOT, project, module, group, `${legacyTest.id}.yaml`); } function buildCanonicalDoc(legacy, existing = {}) { const cf = legacy.custom_fields || existing.custom_fields || {}; // Support rich steps from new per-step editor (Fase B+) let steps = []; const incomingSteps = legacy.steps || []; if (incomingSteps.length > 0 && typeof incomingSteps[0] === 'object' && incomingSteps[0] !== null) { // Rich steps sent from frontend steps = incomingSteps.map(s => ({ action: s.action || '', expected: s.expected || '', automatable: s.automatable !== undefined ? !!s.automatable : (legacy.status !== 'manual'), ...(s.reason_not_automatable ? { reason_not_automatable: s.reason_not_automatable } : {}) })); } else { // Legacy flat strings steps = incomingSteps.map((action, i) => { const prev = (existing.steps && existing.steps[i]) || {}; return { action: action, expected: prev.expected || '', automatable: prev.automatable !== undefined ? prev.automatable : (legacy.status !== 'manual'), ...(prev.reason_not_automatable ? { reason_not_automatable: prev.reason_not_automatable } : {}) }; }); } // If existing had more detailed steps, prefer their automatable flags if (existing.steps && existing.steps.length === steps.length) { steps = existing.steps.map((prev, i) => ({ ...prev, action: steps[i].action })); } const doc = { id: legacy.id, project: 'pbx', module: legacy.module, group: legacy.group, title: legacy.description || legacy.title, description: legacy.description || legacy.title, priority: existing.priority || 'Medium', tags: existing.tags || [], references: existing.references || [], dependencies: legacy.dependencies || existing.dependencies || [], preconditions: existing.preconditions || [], postconditions: existing.postconditions || [], steps, automation_status: legacy.status, file: legacy.file || existing.file || null, custom_fields: { ...cf, original_action: legacy.action, } }; // Carry over extra legacy fields the UI might send if (legacy.manualReason) doc.custom_fields.manualReason = legacy.manualReason; if (legacy.partialNotes) doc.custom_fields.partialNotes = legacy.partialNotes; return doc; } function saveTestcases(legacyArray) { for (const test of legacyArray) { if (!test || !test.id) continue; const filePath = getYamlPath(test); const dir = path.dirname(filePath); if (!fs.existsSync(dir)) { fs.mkdirSync(dir, { recursive: true }); } let existing = {}; if (fs.existsSync(filePath)) { try { const raw = fs.readFileSync(filePath, 'utf8'); existing = yaml.load(raw) || {}; } catch (e) { // ignore, we'll overwrite } } const doc = buildCanonicalDoc(test, existing); const header = `# Updated via Autotest Editor - ${new Date().toISOString()}\n# Source of truth: this YAML file\n\n`; const content = header + yaml.dump(doc, { lineWidth: 100, noRefs: true }); fs.writeFileSync(filePath, content, 'utf8'); } } module.exports = { saveTestcases, getYamlPath };