/** * PDF Datasheet Generator Test Suite * Validates that datasheets are generated correctly with all expected values */ import { describe, it, expect } from 'vitest'; import * as fs from 'fs'; import * as path from 'path'; import { execSync } from 'child_process'; // Test configuration const TEST_CONFIG = { productsFile: path.join(process.cwd(), 'data/processed/products.json'), excelFiles: [ path.join(process.cwd(), 'data/source/high-voltage.xlsx'), path.join(process.cwd(), 'data/source/medium-voltage-KM.xlsx'), path.join(process.cwd(), 'data/source/low-voltage-KM.xlsx'), path.join(process.cwd(), 'data/source/solar-cables.xlsx'), ], outputDir: path.join(process.cwd(), 'public/datasheets'), }; // Expected table headers (13 columns as specified) const EXPECTED_HEADERS = ['DI', 'RI', 'Wi', 'Ibl', 'Ibe', 'Ik', 'Wm', 'Rbv', 'Ø', 'Fzv', 'Al', 'Cu', 'G']; // Helper functions (copied from generate-pdf-datasheets.ts for testing) function normalizeExcelKey(value) { return String(value || '') .toUpperCase() .replace(/-\d+$/g, '') .replace(/[^A-Z0-9]+/g, ''); } function normalizeValue(value) { if (!value) return ''; return String(value) .replace(/<[^>]*>/g, '') .replace(/\s+/g, ' ') .trim(); } function loadExcelRows(filePath) { const out = execSync(`npx -y xlsx-cli -j "${filePath}"`, { encoding: 'utf8', stdio: ['ignore', 'pipe', 'ignore'] }); const trimmed = out.trim(); const jsonStart = trimmed.indexOf('['); if (jsonStart < 0) return []; const jsonText = trimmed.slice(jsonStart); try { return JSON.parse(jsonText); } catch { return []; } } function getExcelIndex() { const idx = new Map(); for (const file of TEST_CONFIG.excelFiles) { if (!fs.existsSync(file)) continue; const rows = loadExcelRows(file); const unitsRow = rows.find(r => r && r['Part Number'] === 'Units') || null; const units = {}; if (unitsRow) { for (const [k, v] of Object.entries(unitsRow)) { if (k === 'Part Number') continue; const unit = normalizeValue(String(v ?? '')); if (unit) units[k] = unit; } } for (const r of rows) { const pn = r?.['Part Number']; if (!pn || pn === 'Units') continue; const key = normalizeExcelKey(String(pn)); if (!key) continue; const cur = idx.get(key); if (!cur) { idx.set(key, { rows: [r], units }); } else { cur.rows.push(r); if (Object.keys(cur.units).length < Object.keys(units).length) cur.units = units; } } } return idx; } function findExcelForProduct(product) { const idx = getExcelIndex(); const candidates = [ product.name, product.slug ? product.slug.replace(/-\d+$/g, '') : '', product.sku, product.translationKey, ].filter(Boolean); for (const c of candidates) { const key = normalizeExcelKey(c); const match = idx.get(key); if (match && match.rows.length) return match; } return null; } describe('PDF Datasheet Generator (smoke)', () => { it('excel source files exist', () => { const missing = TEST_CONFIG.excelFiles.filter(f => !fs.existsSync(f)); expect(missing, `Missing Excel files: ${missing.join(', ')}`).toHaveLength(0); }); it('products.json exists', () => { expect(fs.existsSync(TEST_CONFIG.productsFile)).toBe(true); }); it('pdf output directory exists', () => { expect(fs.existsSync(TEST_CONFIG.outputDir)).toBe(true); }); it( 'excel data is loadable', () => { const idx = getExcelIndex(); expect(idx.size).toBeGreaterThan(0); }, 30_000, ); it( 'product NA2XS(FL)2Y has excel data', () => { const products = JSON.parse(fs.readFileSync(TEST_CONFIG.productsFile, 'utf8')); const product = products.find(p => p.id === 46773); expect(product).toBeTruthy(); const match = findExcelForProduct(product); expect(match).toBeTruthy(); expect(match?.rows?.length || 0).toBeGreaterThan(0); }, 30_000, ); it( 'excel contains required column families', () => { const products = JSON.parse(fs.readFileSync(TEST_CONFIG.productsFile, 'utf8')); const product = products.find(p => p.id === 46773); const match = findExcelForProduct(product); expect(match).toBeTruthy(); if (!match) return; const sampleRow = match.rows[0]; const excelKeys = Object.keys(sampleRow).map(k => k.toLowerCase()); const hasDI = excelKeys.some(k => k.includes('diameter over insulation') || k.includes('insulation diameter')); const hasRI = excelKeys.some(k => k.includes('dc resistance') || k.includes('resistance conductor') || k.includes('leiterwiderstand')); const hasWi = excelKeys.some(k => k.includes('nominal insulation thickness') || k.includes('insulation thickness')); const hasIbl = excelKeys.some(k => k.includes('current ratings in air') || k.includes('strombelastbarkeit luft')); const hasIbe = excelKeys.some(k => k.includes('current ratings in ground') || k.includes('strombelastbarkeit erdreich')); const hasIk = excelKeys.some(k => k.includes('shortcircuit current') || k.includes('kurzschlussstrom')); const hasWm = excelKeys.some(k => k.includes('sheath thickness') || k.includes('manteldicke')); const hasRbv = excelKeys.some(k => k.includes('bending radius') || k.includes('biegeradius')); const hasOD = excelKeys.some(k => k.includes('outer diameter') || k.includes('außen') || k.includes('durchmesser')); const hasG = excelKeys.some(k => k.includes('weight') || k.includes('gewicht')); const foundCount = [hasDI, hasRI, hasWi, hasIbl, hasIbe, hasIk, hasWm, hasRbv, hasOD, hasG].filter(Boolean).length; expect(foundCount).toBeGreaterThanOrEqual(5); }, 30_000, ); it('pdf naming convention is correct', () => { const pdfFiles = fs.readdirSync(TEST_CONFIG.outputDir).filter(f => f.endsWith('.pdf')); const namingPattern = /^[a-z0-9-]+-(en|de)\.pdf$/; expect(pdfFiles.length).toBeGreaterThan(0); expect(pdfFiles.every(f => namingPattern.test(f))).toBe(true); }); });