130 lines
4.0 KiB
TypeScript
130 lines
4.0 KiB
TypeScript
/**
|
|
* 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) {
|
|
if (!value) return '';
|
|
return String(value)
|
|
.replace(/<[^>]*>/g, '')
|
|
.replace(/\s+/g, ' ')
|
|
.trim();
|
|
}
|
|
|
|
function normalizeExcelKey(value) {
|
|
return String(value || '')
|
|
.toUpperCase()
|
|
.replace(/-\d+$/g, '')
|
|
.replace(/[^A-Z0-9]+/g, '');
|
|
}
|
|
|
|
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 [];
|
|
}
|
|
}
|
|
|
|
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();
|
|
|
|
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;
|
|
}
|
|
}
|
|
}
|
|
|
|
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;
|
|
}
|
|
}
|
|
|
|
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);
|
|
});
|
|
|
|
// 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);
|
|
}
|
|
});
|
|
});
|