UNPKG

pdf-reportify

Version:

A Node.js CLI tool to generate PDF reports from test screenshots.

137 lines (111 loc) 5.24 kB
const { PDFDocument } = require('pdf-lib'); const { promises: fs } = require('fs'); const path = require('path'); const { glob } = require('glob'); /** * Creates a PDF from a series of images. * @param {string[]} imagePaths - An array of paths to the images. * @returns {Promise<Uint8Array>} The PDF content as bytes. */ async function createPdfFromImages(imagePaths) { const pdfDoc = await PDFDocument.create(); for (const imagePath of imagePaths) { const imageBytes = await fs.readFile(imagePath); let image; if (imagePath.endsWith('.png')) { image = await pdfDoc.embedPng(imageBytes); } else if (imagePath.endsWith('.jpg') || imagePath.endsWith('.jpeg')) { image = await pdfDoc.embedJpg(imageBytes); } else { console.warn(`Unsupported image format: ${imagePath}`); continue; } const page = pdfDoc.addPage(); const { width, height } = page.getSize(); // Define margens para evitar que as imagens saiam da página const margin = 20; const availableWidth = width - (margin * 2); const availableHeight = height - (margin * 2); // Redimensiona a imagem para ocupar 100% da largura disponível, mantendo a proporção const imageDims = image.scaleToFit(availableWidth, availableHeight); page.drawImage(image, { x: margin, y: page.getHeight() - imageDims.height - margin, width: imageDims.width, height: imageDims.height, }); } return pdfDoc.save(); } /** * Generates PDF files for each test case in a screenshots directory. * @param {string} screenshotsDir - The path to the root screenshots directory. * @param {{ verbose?: boolean }} options - Optional settings. */ async function generateTestReports(screenshotsDir, options = {}) { const verbose = Boolean(options.verbose); const vLog = (...args) => { if (verbose) console.log(...args); }; try { // List test directories (like login.cy.js, signup.cy.js, etc.) const dirEntries = await fs.readdir(screenshotsDir, { withFileTypes: true }); const testDirs = dirEntries .filter((entry) => entry.isDirectory()) .map((entry) => path.join(screenshotsDir, entry.name)); vLog(`Found ${testDirs.length} test directories in: ${screenshotsDir}`); for (const testDir of testDirs) { const testName = path.basename(path.resolve(testDir)); vLog(`Processing test directory: ${testName}`); // List all directories within each test directory const testDirEntries = await fs.readdir(testDir, { withFileTypes: true }); const potentialTestCaseDirs = testDirEntries .filter((entry) => entry.isDirectory()) .map((entry) => path.join(testDir, entry.name)); vLog(`Found ${potentialTestCaseDirs.length} potential test case directories in ${testName}`); for (const testCaseDir of potentialTestCaseDirs) { const testCaseName = path.basename(path.resolve(testCaseDir)); // Check if this directory contains any PNG files const testCaseEntries = await fs.readdir(testCaseDir, { withFileTypes: true }); const hasPngFiles = testCaseEntries.some(entry => !entry.isDirectory() && entry.name.toLowerCase().endsWith('.png') ); if (!hasPngFiles) { vLog(`Directory '${testCaseName}' does not contain PNG files, skipping...`); continue; } vLog(`Directory '${testCaseName}' contains PNG files, processing as test case...`); // Normalize to POSIX path for glob on Windows const posixDir = testCaseDir.replace(/\\/g, '/'); const imagePattern = `${posixDir}/**/*.{png,jpg,jpeg,PNG,JPG,JPEG}`; const imagePaths = await glob(imagePattern, { nocase: true, windowsPathsNoEscape: true }); vLog(`Test case '${testCaseName}' pattern: ${imagePattern}`); vLog(`Found ${imagePaths.length} images in '${testCaseName}'`); if (imagePaths.length === 0) { console.warn(`No screenshots found for test case: ${testCaseName}`); continue; } // Order images by file creation time (birthtime) const filesWithStats = await Promise.all( imagePaths.map(async (imagePath) => ({ imagePath, stat: await fs.stat(imagePath), })) ); filesWithStats.sort((a, b) => a.stat.birthtimeMs - b.stat.birthtimeMs); const orderedImagePaths = filesWithStats.map((f) => f.imagePath); vLog(`Ordering by creation time (oldest first):`, orderedImagePaths.map(p => path.basename(p)).join(', ')); console.log(`Generating report for test case: ${testCaseName}`); const pdfBytes = await createPdfFromImages(orderedImagePaths); const timestamp = new Date().toISOString().replace(/[:.]/g, '-').slice(0, -5); const outputFilePath = path.join(testCaseDir, `${testCaseName}_report_${timestamp}.pdf`); await fs.writeFile(outputFilePath, pdfBytes); console.log(`Report saved to: ${outputFilePath}`); } } console.log('\nReport generation complete!'); } catch (error) { console.error('An error occurred during report generation:', error); } } module.exports = { generateTestReports, };