#!/usr/bin/env zx import { $, fs, path } from 'zx'; import { XMLParser, XMLBuilder } from 'fast-xml-parser'; const junitDir = path.resolve(process.cwd(), 'coverage', 'junit'); const mergedOutputFile = path.join(junitDir, 'complete-junit.xml'); try { const fileExists = await fs.exists(mergedOutputFile); if (fileExists) { await fs.remove(mergedOutputFile); console.log(`Removed existing ${mergedOutputFile}`); } } catch (error) { if (error.code !== 'ENOENT') { console.error(`Error removing existing ${mergedOutputFile}:`, error); } } const files = await fs.readdir(junitDir); const reportFiles = files.filter( (file) => file.endsWith('.junit.xml') && file !== 'complete-junit.xml', ); console.log(`Found ${reportFiles.length} report files to process.`); for (const reportFile of reportFiles) { const projectNameMatch = reportFile.match(/^(.*?)\.junit\.xml$/); if (!projectNameMatch) { console.warn( `Could not extract project name from ${reportFile}, skipping.`, ); continue; } const projectName = projectNameMatch[1]; const reportFilePath = path.join(junitDir, reportFile); try { const projectInfoRaw = await $`pnpm nx show project ${projectName} --json`; const projectInfo = JSON.parse(projectInfoRaw.stdout); const projectRoot = projectInfo.root; if (!projectRoot) { console.warn( `Could not find root for project ${projectName}, skipping file ${reportFile}`, ); continue; } console.log( `Processing ${reportFile} for project ${projectName} (root: ${projectRoot})...`, ); const xmlContent = await fs.readFile(reportFilePath, 'utf-8'); const parser = new XMLParser({ ignoreAttributes: false, attributeNamePrefix: '', }); const parsedXml = parser.parse(xmlContent); if (parsedXml.testsuites && parsedXml.testsuites.testsuite) { const suites = Array.isArray(parsedXml.testsuites.testsuite) ? parsedXml.testsuites.testsuite : [parsedXml.testsuites.testsuite]; suites.forEach((suite) => { if (suite.name && !suite.name.startsWith(projectRoot + '/')) { suite.name = path.join(projectRoot, suite.name); } if (suite.testcase) { const cases = Array.isArray(suite.testcase) ? suite.testcase : [suite.testcase]; cases.forEach((testcase) => { if (testcase.file && !testcase.file.startsWith(projectRoot + '/')) { testcase.file = path.join(projectRoot, testcase.file); } }); } }); } else if (parsedXml.testsuite) { const suite = parsedXml.testsuite; if (suite.name && !suite.name.startsWith(projectRoot + '/')) { suite.name = path.join(projectRoot, suite.name); } if (suite.testcase) { const cases = Array.isArray(suite.testcase) ? suite.testcase : [suite.testcase]; cases.forEach((testcase) => { if (testcase.file && !testcase.file.startsWith(projectRoot + '/')) { testcase.file = path.join(projectRoot, testcase.file); } }); } } const builder = new XMLBuilder({ ignoreAttributes: false, attributeNamePrefix: '', format: true, }); const modifiedXmlContent = builder.build(parsedXml); await fs.writeFile(reportFilePath, modifiedXmlContent); console.log(` Processed and saved ${reportFile}`); } catch (error) { console.error( `Error processing file ${reportFile} for project ${projectName}:`, error, ); } } console.log(`Merging reports into ${mergedOutputFile}...`); try { await $`pnpm junit-merge --dir=${junitDir} --out=${mergedOutputFile}`; console.log('Successfully merged JUnit reports.'); } catch (mergeError) { console.error('Error merging JUnit reports:', mergeError); process.exit(1); }