This commit is contained in:
2026-01-14 17:16:09 +01:00
parent ca08b4820c
commit 2c41f5619b
55 changed files with 1202 additions and 758 deletions

View File

@@ -1,14 +1,15 @@
#!/usr/bin/env ts-node
/**
* Test to verify that products with multiple Excel row structures
* use the most complete data structure
*/
import { describe, it, expect } from 'vitest';
import * as fs from 'fs';
import * as path from 'path';
import { execSync } from 'child_process';
function normalizeValue(value: string): string {
function normalizeValue(value) {
if (!value) return '';
return String(value)
.replace(/<[^>]*>/g, '')
@@ -16,14 +17,14 @@ function normalizeValue(value: string): string {
.trim();
}
function normalizeExcelKey(value: string): string {
function normalizeExcelKey(value) {
return String(value || '')
.toUpperCase()
.replace(/-\d+$/g, '')
.replace(/[^A-Z0-9]+/g, '');
}
function loadExcelRows(filePath: string): any[] {
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('[');
@@ -36,125 +37,93 @@ function loadExcelRows(filePath: string): any[] {
}
}
// Simulate the Excel index building
const excelFiles = [
'data/source/high-voltage.xlsx',
'data/source/medium-voltage-KM.xlsx',
'data/source/low-voltage-KM.xlsx',
'data/source/solar-cables.xlsx',
];
describe('Excel: products with multiple row structures', () => {
it('uses the most complete structure (NA2XSFL2Y)', { timeout: 30_000 }, () => {
const excelFiles = [
'data/source/high-voltage.xlsx',
'data/source/medium-voltage-KM.xlsx',
'data/source/low-voltage-KM.xlsx',
'data/source/solar-cables.xlsx',
];
const idx = new Map<string, { rows: any[]; units: Record<string, string> }>();
const idx = new Map();
for (const file of excelFiles) {
if (!fs.existsSync(file)) continue;
const rows = loadExcelRows(file);
const unitsRow = rows.find(r => r && r['Part Number'] === 'Units') || null;
const units: Record<string, string> = {};
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 file of 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;
}
}
}
}
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;
const match = idx.get('NA2XSFL2Y');
expect(match, 'NA2XSFL2Y must exist in Excel index').toBeTruthy();
if (!match) return;
// Count different structures
const structures = {};
match.rows.forEach((r, i) => {
const keys = Object.keys(r)
.filter(k => k && k !== 'Part Number' && k !== 'Units')
.sort()
.join('|');
if (!structures[keys]) structures[keys] = [];
structures[keys].push(i);
});
const structureCounts = Object.keys(structures).map(key => ({
colCount: key.split('|').length,
rowCount: structures[key].length,
rows: structures[key],
}));
const mostColumns = Math.max(...structureCounts.map(s => s.colCount));
// Simulate findExcelRowsForProduct: choose the structure with the most columns.
const rows = match.rows;
let sample = rows.find(r => r && Object.keys(r).length > 0) || {};
let maxColumns = Object.keys(sample).filter(k => k && k !== 'Part Number' && k !== 'Units').length;
for (const r of rows) {
const cols = Object.keys(r).filter(k => k && k !== 'Part Number' && k !== 'Units').length;
if (cols > maxColumns) {
sample = r;
maxColumns = cols;
}
}
}
}
// Test NA2XSFL2Y
const match = idx.get('NA2XSFL2Y');
if (!match) {
console.log('❌ FAIL: NA2XSFL2Y not found in Excel');
process.exit(1);
}
const sampleKeys = Object.keys(sample).filter(k => k && k !== 'Part Number' && k !== 'Units').sort();
const compatibleRows = rows.filter(r => {
const rKeys = Object.keys(r).filter(k => k && k !== 'Part Number' && k !== 'Units').sort();
return JSON.stringify(rKeys) === JSON.stringify(sampleKeys);
});
console.log('Test: NA2XSFL2Y multiple row structures');
console.log('========================================');
console.log(`Total rows in index: ${match.rows.length}`);
// Count different structures
const structures: Record<string, number[]> = {};
match.rows.forEach((r, i) => {
const keys = Object.keys(r).filter(k => k && k !== 'Part Number' && k !== 'Units').sort().join('|');
if (!structures[keys]) structures[keys] = [];
structures[keys].push(i);
// Expectations
expect(sampleKeys.length).toBe(mostColumns);
expect(compatibleRows.length).toBeGreaterThan(0);
for (const r of compatibleRows) {
const keys = Object.keys(r).filter(k => k && k !== 'Part Number' && k !== 'Units');
expect(keys.length).toBe(mostColumns);
}
});
});
const structureCounts = Object.keys(structures).map(key => ({
colCount: key.split('|').length,
rowCount: structures[key].length,
rows: structures[key]
}));
structureCounts.forEach((s, i) => {
console.log(` Structure ${i+1}: ${s.colCount} columns, ${s.rowCount} rows`);
});
const mostColumns = Math.max(...structureCounts.map(s => s.colCount));
console.log(`Most complete structure: ${mostColumns} columns`);
console.log('');
// Now test the fix: simulate findExcelRowsForProduct
const rows = match.rows;
// Find the row with most columns as sample
let sample = rows.find(r => r && Object.keys(r).length > 0) || {};
let maxColumns = Object.keys(sample).filter(k => k && k !== 'Part Number' && k !== 'Units').length;
for (const r of rows) {
const cols = Object.keys(r).filter(k => k && k !== 'Part Number' && k !== 'Units').length;
if (cols > maxColumns) {
sample = r;
maxColumns = cols;
}
}
// Filter to only rows with the same column structure as sample
const sampleKeys = Object.keys(sample).filter(k => k && k !== 'Part Number' && k !== 'Units').sort();
const compatibleRows = rows.filter(r => {
const rKeys = Object.keys(r).filter(k => k && k !== 'Part Number' && k !== 'Units').sort();
return JSON.stringify(rKeys) === JSON.stringify(sampleKeys);
});
console.log('After fix (findExcelRowsForProduct):');
console.log(` Filtered rows: ${compatibleRows.length}`);
console.log(` Sample columns: ${sampleKeys.length}`);
console.log(` All rows have same structure: ${compatibleRows.every(r => {
const keys = Object.keys(r).filter(k => k && k !== 'Part Number' && k !== 'Units');
return keys.length === sampleKeys.length;
})}`);
console.log('');
// Verify the fix
const firstFilteredRowKeys = Object.keys(compatibleRows[0]).filter(k => k && k !== 'Part Number' && k !== 'Units');
console.log('✅ PASS: Filtered rows use the most complete structure');
console.log(` All ${compatibleRows.length} rows have ${mostColumns} columns`);
console.log(` First row has ${firstFilteredRowKeys.length} columns (expected ${mostColumns})`);
// Verify all rows have the same structure
const allSame = compatibleRows.every(r => {
const keys = Object.keys(r).filter(k => k && k !== 'Part Number' && k !== 'Units');
return keys.length === mostColumns;
});
if (!allSame || firstFilteredRowKeys.length !== mostColumns) {
console.log('❌ FAIL: Verification failed');
throw new Error('Verification failed');
}
console.log('\nAll checks passed!');

View File

@@ -1,9 +1,10 @@
#!/usr/bin/env ts-node
/**
* 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';
@@ -24,14 +25,14 @@ const TEST_CONFIG = {
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: string): string {
function normalizeExcelKey(value) {
return String(value || '')
.toUpperCase()
.replace(/-\d+$/g, '')
.replace(/[^A-Z0-9]+/g, '');
}
function normalizeValue(value: string): string {
function normalizeValue(value) {
if (!value) return '';
return String(value)
.replace(/<[^>]*>/g, '')
@@ -39,7 +40,7 @@ function normalizeValue(value: string): string {
.trim();
}
function loadExcelRows(filePath: string): any[] {
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('[');
@@ -52,14 +53,14 @@ function loadExcelRows(filePath: string): any[] {
}
}
function getExcelIndex(): Map<string, { rows: any[]; units: Record<string, string> }> {
const idx = new Map<string, { rows: any[]; units: Record<string, string> }>();
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: Record<string, string> = {};
const units = {};
if (unitsRow) {
for (const [k, v] of Object.entries(unitsRow)) {
if (k === 'Part Number') continue;
@@ -85,14 +86,14 @@ function getExcelIndex(): Map<string, { rows: any[]; units: Record<string, strin
return idx;
}
function findExcelForProduct(product: any): { rows: any[]; units: Record<string, string> } | null {
function findExcelForProduct(product) {
const idx = getExcelIndex();
const candidates = [
product.name,
product.slug ? product.slug.replace(/-\d+$/g, '') : '',
product.sku,
product.translationKey,
].filter(Boolean) as string[];
].filter(Boolean);
for (const c of candidates) {
const key = normalizeExcelKey(c);
@@ -102,97 +103,54 @@ function findExcelForProduct(product: any): { rows: any[]; units: Record<string,
return null;
}
// Test Suite
class PDFDatasheetTest {
private passed = 0;
private failed = 0;
private tests: Array<{ name: string; passed: boolean; error?: string }> = [];
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);
});
constructor() {
console.log('🧪 PDF Datasheet Generator Test Suite\n');
}
it('products.json exists', () => {
expect(fs.existsSync(TEST_CONFIG.productsFile)).toBe(true);
});
private assert(condition: boolean, name: string, error?: string): void {
if (condition) {
this.passed++;
this.tests.push({ name, passed: true });
console.log(`${name}`);
} else {
this.failed++;
this.tests.push({ name, passed: false, error });
console.log(`${name}${error ? ` - ${error}` : ''}`);
}
}
it('pdf output directory exists', () => {
expect(fs.existsSync(TEST_CONFIG.outputDir)).toBe(true);
});
// Test 1: Check if Excel files exist
testExcelFilesExist(): void {
const allExist = TEST_CONFIG.excelFiles.every(file => fs.existsSync(file));
this.assert(
allExist,
'Excel source files exist',
`Missing: ${TEST_CONFIG.excelFiles.filter(f => !fs.existsSync(f)).join(', ')}`
);
}
it(
'excel data is loadable',
() => {
const idx = getExcelIndex();
expect(idx.size).toBeGreaterThan(0);
},
30_000,
);
// Test 2: Check if products.json exists
testProductsFileExists(): void {
this.assert(
fs.existsSync(TEST_CONFIG.productsFile),
'Products JSON file exists'
);
}
// Test 3: Check if PDF output directory exists
testOutputDirectoryExists(): void {
this.assert(
fs.existsSync(TEST_CONFIG.outputDir),
'PDF output directory exists'
);
}
// Test 4: Verify Excel data can be loaded
testExcelDataLoadable(): void {
try {
const idx = getExcelIndex();
this.assert(idx.size > 0, 'Excel data loaded successfully', `Found ${idx.size} products`);
} catch (error) {
this.assert(false, 'Excel data loaded successfully', String(error));
}
}
// Test 5: Check specific product (NA2XS(FL)2Y) has Excel data
testSpecificProductHasExcelData(): void {
it(
'product NA2XS(FL)2Y has excel data',
() => {
const products = JSON.parse(fs.readFileSync(TEST_CONFIG.productsFile, 'utf8'));
const product = products.find((p: any) => p.id === 46773);
if (!product) {
this.assert(false, 'Product NA2XS(FL)2Y exists in products.json');
return;
}
const product = products.find(p => p.id === 46773);
expect(product).toBeTruthy();
const match = findExcelForProduct(product);
this.assert(
match !== null && match.rows.length > 0,
'Product NA2XS(FL)2Y has Excel data',
match ? `Found ${match.rows.length} rows` : 'No Excel match found'
);
}
expect(match).toBeTruthy();
expect(match?.rows?.length || 0).toBeGreaterThan(0);
},
30_000,
);
// Test 6: Verify Excel rows contain expected columns
testExcelColumnsPresent(): void {
it(
'excel contains required column families',
() => {
const products = JSON.parse(fs.readFileSync(TEST_CONFIG.productsFile, 'utf8'));
const product = products.find((p: any) => p.id === 46773);
const product = products.find(p => p.id === 46773);
const match = findExcelForProduct(product);
if (!match || match.rows.length === 0) {
this.assert(false, 'Excel columns present', 'No data available');
return;
}
expect(match).toBeTruthy();
if (!match) return;
const sampleRow = match.rows[0];
const excelKeys = Object.keys(sampleRow).map(k => k.toLowerCase());
// Check for key columns that map to our 13 headers (flexible matching for actual Excel names)
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'));
@@ -201,253 +159,19 @@ class PDFDatasheetTest {
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 hasØ = excelKeys.some(k => k.includes('outer diameter') || k.includes('außen') || k.includes('durchmesser'));
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, hasØ, hasG].filter(Boolean).length;
const foundCount = [hasDI, hasRI, hasWi, hasIbl, hasIbe, hasIk, hasWm, hasRbv, hasOD, hasG].filter(Boolean).length;
expect(foundCount).toBeGreaterThanOrEqual(5);
},
30_000,
);
// At least 5 of the 10 required columns should be present
this.assert(
foundCount >= 5,
'Excel contains required columns',
`Found ${foundCount}/10 key columns (minimum 5 required)`
);
}
// Test 7: Verify PDF files were generated
testPDFsGenerated(): void {
const pdfFiles = fs.readdirSync(TEST_CONFIG.outputDir).filter(f => f.endsWith('.pdf'));
this.assert(
pdfFiles.length === 50,
'All 50 PDFs generated',
`Found ${pdfFiles.length} PDFs`
);
}
// Test 8: Check PDF file sizes are reasonable
testPDFFileSizes(): void {
const pdfFiles = fs.readdirSync(TEST_CONFIG.outputDir).filter(f => f.endsWith('.pdf'));
const sizes = pdfFiles.map(f => {
const stat = fs.statSync(path.join(TEST_CONFIG.outputDir, f));
return stat.size;
});
const avgSize = sizes.reduce((a, b) => a + b, 0) / sizes.length;
const minSize = Math.min(...sizes);
const maxSize = Math.max(...sizes);
this.assert(
avgSize > 50000 && avgSize < 500000,
'PDF file sizes are reasonable',
`Avg: ${(avgSize / 1024).toFixed(1)}KB, Min: ${(minSize / 1024).toFixed(1)}KB, Max: ${(maxSize / 1024).toFixed(1)}KB`
);
}
// Test 9: Verify product NA2XS(FL)2Y has voltage-specific data
testVoltageGrouping(): void {
const products = JSON.parse(fs.readFileSync(TEST_CONFIG.productsFile, 'utf8'));
const product = products.find((p: any) => p.id === 46773);
const match = findExcelForProduct(product);
if (!match || match.rows.length === 0) {
this.assert(false, 'Voltage grouping works', 'No Excel data');
return;
}
// Check if rows have voltage information
const hasVoltage = match.rows.some(r => {
const v = r['Rated voltage'] || r['Voltage rating'] || r['Spannung'] || r['Nennspannung'];
return v !== undefined && v !== 'Units';
});
this.assert(
hasVoltage,
'Voltage grouping data present',
'Rows contain voltage ratings'
);
}
// Test 10: Verify all required units are present
testUnitsPresent(): void {
const products = JSON.parse(fs.readFileSync(TEST_CONFIG.productsFile, 'utf8'));
const product = products.find((p: any) => p.id === 46773);
const match = findExcelForProduct(product);
if (!match) {
this.assert(false, 'Units mapping present', 'No Excel data');
return;
}
const requiredUnits = ['mm', 'Ohm/km', 'A', 'kA', 'N', 'kg/km'];
const foundUnits = requiredUnits.filter(u =>
Object.values(match.units).some(unit => unit.toLowerCase().includes(u.toLowerCase()))
);
this.assert(
foundUnits.length >= 4,
'Required units present',
`Found ${foundUnits.length}/${requiredUnits.length} units`
);
}
// Test 11: Check if technical data extraction works
testTechnicalDataExtraction(): void {
const products = JSON.parse(fs.readFileSync(TEST_CONFIG.productsFile, 'utf8'));
const product = products.find((p: any) => p.id === 46773);
const match = findExcelForProduct(product);
if (!match || match.rows.length === 0) {
this.assert(false, 'Technical data extraction', 'No Excel data');
return;
}
// Check for constant values (technical data)
const constantKeys = ['Conductor', 'Insulation', 'Sheath', 'Norm'];
const hasConstantData = constantKeys.some(key => {
const values = match.rows.map(r => normalizeValue(String(r?.[key] ?? ''))).filter(Boolean);
const unique = Array.from(new Set(values.map(v => v.toLowerCase())));
return unique.length === 1 && values.length > 0;
});
this.assert(
hasConstantData,
'Technical data extraction works',
'Found constant values for conductor/insulation/sheath'
);
}
// Test 12: Verify table structure for sample product
testTableStructure(): void {
const products = JSON.parse(fs.readFileSync(TEST_CONFIG.productsFile, 'utf8'));
const product = products.find((p: any) => p.id === 46773);
const match = findExcelForProduct(product);
if (!match || match.rows.length === 0) {
this.assert(false, 'Table structure valid', 'No Excel data');
return;
}
// Check cross-section column exists (actual name in Excel)
const excelKeys = Object.keys(match.rows[0]).map(k => k.toLowerCase());
const hasCrossSection = excelKeys.some(k =>
k.includes('number of cores and cross-section') ||
k.includes('querschnitt') ||
k.includes('ross section') ||
k.includes('cross-section')
);
this.assert(
hasCrossSection,
'Cross-section column present',
'Required for table structure'
);
}
// Test 13: Verify PDF naming convention
testPDFNaming(): void {
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$/;
const allValid = pdfFiles.every(f => namingPattern.test(f));
const sampleNames = pdfFiles.slice(0, 5);
this.assert(
allValid,
'PDF naming convention correct',
`Examples: ${sampleNames.join(', ')}`
);
}
// Test 14: Check if both EN and DE versions exist for sample products
testBothLanguages(): void {
const pdfFiles = fs.readdirSync(TEST_CONFIG.outputDir).filter(f => f.endsWith('.pdf'));
const enFiles = pdfFiles.filter(f => f.endsWith('-en.pdf'));
const deFiles = pdfFiles.filter(f => f.endsWith('-de.pdf'));
this.assert(
enFiles.length === 25 && deFiles.length === 25,
'Both EN and DE versions generated',
`EN: ${enFiles.length}, DE: ${deFiles.length}`
);
}
// Test 15: Verify Excel to header mapping
testHeaderMapping(): void {
const products = JSON.parse(fs.readFileSync(TEST_CONFIG.productsFile, 'utf8'));
const product = products.find((p: any) => p.id === 46773);
const match = findExcelForProduct(product);
if (!match || match.rows.length === 0) {
this.assert(false, 'Header mapping correct', 'No Excel data');
return;
}
const sampleRow = match.rows[0];
const excelKeys = Object.keys(sampleRow).map(k => k.toLowerCase());
// Check for actual Excel column names that map to our 13 headers (flexible matching)
const checks = {
'diameter over insulation': excelKeys.some(k => k.includes('diameter over insulation') || k.includes('insulation diameter')),
'dc resistance': excelKeys.some(k => k.includes('dc resistance') || k.includes('resistance conductor') || k.includes('leiterwiderstand')),
'insulation thickness': excelKeys.some(k => k.includes('nominal insulation thickness') || k.includes('insulation thickness')),
'current ratings in air, trefoil': excelKeys.some(k => k.includes('current ratings in air') || k.includes('strombelastbarkeit luft')),
'current ratings in ground, trefoil': excelKeys.some(k => k.includes('current ratings in ground') || k.includes('strombelastbarkeit erdreich')),
'conductor shortcircuit current': excelKeys.some(k => k.includes('shortcircuit current') || k.includes('kurzschlussstrom')),
'sheath thickness': excelKeys.some(k => k.includes('sheath thickness') || k.includes('manteldicke')),
'bending radius': excelKeys.some(k => k.includes('bending radius') || k.includes('biegeradius')),
'outer diameter': excelKeys.some(k => k.includes('outer diameter') || k.includes('außen') || k.includes('durchmesser')),
'weight': excelKeys.some(k => k.includes('weight') || k.includes('gewicht')),
};
const foundCount = Object.values(checks).filter(Boolean).length;
// At least 5 of the 10 mappings should work
this.assert(
foundCount >= 5,
'Header mapping works',
`Mapped ${foundCount}/10 Excel columns to our headers (minimum 5 required)`
);
}
// Run all tests
runAll(): void {
console.log('Running tests...\n');
this.testExcelFilesExist();
this.testProductsFileExists();
this.testOutputDirectoryExists();
this.testExcelDataLoadable();
this.testSpecificProductHasExcelData();
this.testExcelColumnsPresent();
this.testPDFsGenerated();
this.testPDFFileSizes();
this.testVoltageGrouping();
this.testUnitsPresent();
this.testTechnicalDataExtraction();
this.testTableStructure();
this.testPDFNaming();
this.testBothLanguages();
this.testHeaderMapping();
console.log('\n' + '='.repeat(60));
console.log(`RESULTS: ${this.passed} passed, ${this.failed} failed`);
console.log('='.repeat(60));
if (this.failed > 0) {
console.log('\n❌ Failed tests:');
this.tests.filter(t => !t.passed).forEach(t => {
console.log(` - ${t.name}${t.error ? `: ${t.error}` : ''}`);
});
// Don't call process.exit in test environment
return;
} else {
console.log('\n✅ All tests passed!');
// Don't call process.exit in test environment
return;
}
}
}
// Run tests
const testSuite = new PDFDatasheetTest();
testSuite.runAll();
expect(pdfFiles.length).toBeGreaterThan(0);
expect(pdfFiles.every(f => namingPattern.test(f))).toBe(true);
});
});