This commit is contained in:
2026-01-16 18:24:45 +01:00
parent 815c410092
commit 36e2a84a54
223 changed files with 2 additions and 272264 deletions

View File

@@ -1,129 +0,0 @@
/**
* 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);
}
});
});

View File

@@ -1,311 +0,0 @@
/**
* Vitest tests for WordPress data processing pipeline
* Tests the complete flow from raw data to processed content
*/
import { describe, it, expect } from 'vitest';
import { processHTML, processShortcodes } from '../lib/html-compat';
describe('WordPress Data Processing Pipeline', () => {
describe('HTML Entity Decoding', () => {
it('should decode HTML entities in content', () => {
const input = 'Test ”quoted″ text with dash';
const result = processHTML(input);
// After fix: HTML entities should be decoded
expect(result).toContain('"quoted"');
// Note: The en-dash () is preserved in the output
expect(result).toContain(' dash');
});
it('should handle multiple entity types', () => {
const input = '© 2024 • €100 ± 5% & "quotes" \'apostrophes\'';
const result = processHTML(input);
expect(result).toContain('©');
expect(result).toContain('€');
expect(result).toContain('±');
expect(result).toContain('&');
});
});
describe('Shortcode Processing', () => {
it('should convert vc_row with bg_image ID to HTML', () => {
const input = '[vc_row bg_image="10440" bg_color="#ffffff" color_overlay="#000000" overlay_strength="0.8"]content[/vc_row]';
const result = processShortcodes(input);
expect(result).toContain('vc-row');
expect(result).toContain('bg-cover');
expect(result).toContain('bg-center');
expect(result).toContain('relative');
expect(result).toContain('content');
// Should resolve bg_image ID to local path in style
expect(result).toContain('background-image: url(/media/10440-DSC07655-Large.webp)');
expect(result).toContain('background-color: #ffffff');
});
it('should handle vc_row with local path bg_image', () => {
const input = '[vc_row bg_image="/media/10440-DSC07655-Large.webp"]content[/vc_row]';
const result = processShortcodes(input);
expect(result).toContain('vc-row');
expect(result).toContain('background-image: url(/media/10440-DSC07655-Large.webp)');
expect(result).toContain('bg-cover');
expect(result).toContain('bg-center');
});
it('should handle full_width_background type', () => {
const input = '[vc_row type="full_width_background" bg_image="45569" top_padding="15%" bottom_padding="13%"]content[/vc_row]';
const result = processShortcodes(input);
expect(result).toContain('vc-row');
expect(result).toContain('background-image: url(/media/45569-Still-2025-02-10-104337_1.1.1.webp)');
// Note: Padding extraction is a known issue - the core functionality works
});
it('should handle video background', () => {
const input = '[vc_row video_bg="use_video" video_mp4="https://example.com/video.mp4" video_webm="https://example.com/video.webm"]content[/vc_row]';
const result = processShortcodes(input);
expect(result).toContain('data-video-bg="true"');
expect(result).toContain('data-video-mp4');
expect(result).toContain('data-video-webm');
});
it('should handle gradient overlay', () => {
const input = '[vc_row enable_gradient="true" color_overlay="#000000" color_overlay_2="rgba(10,10,10,0.5)" gradient_direction="left_to_right"]content[/vc_row]';
const result = processShortcodes(input);
expect(result).toContain('vc-row');
expect(result).toContain('relative');
expect(result).toContain('absolute inset-0');
expect(result).toContain('linear-gradient(to right');
expect(result).toContain('opacity: 0.32');
});
it('should handle vc_column with width', () => {
const input = '[vc_row][vc_column width="6"]content[/vc_column][/vc_row]';
const result = processShortcodes(input);
expect(result).toContain('w-full md:w-1/2');
});
it('should handle nested vc_row structures', () => {
const input = '[vc_row][vc_column][vc_row bg_image="10440"]nested[/vc_row][/vc_column][/vc_row]';
const result = processShortcodes(input);
expect(result).toContain('vc-row');
expect(result).toContain('nested');
});
it('should handle HTML entities in shortcode attributes', () => {
// Test with actual HTML entities that decode to proper values
const input = '[vc_row bg_image="10440" color_overlay="#000000"]content[/vc_row]';
const result = processShortcodes(input);
expect(result).toContain('vc-row');
expect(result).toContain('background-image: url(/media/10440-DSC07655-Large.webp)');
expect(result).toContain('background-color: #000000');
});
it('should handle vc_btn shortcodes', () => {
const input = '[vc_btn title="Click Here" href="https://example.com" color="primary" size="lg"]';
const result = processShortcodes(input);
expect(result).toContain('vc-btn');
expect(result).toContain('Click Here');
expect(result).toContain('https://example.com');
expect(result).toContain('bg-primary');
expect(result).toContain('px-6');
});
it('should handle vc_separator shortcodes', () => {
const input = '[vc_separator color="primary" width="50" thickness="2"]';
const result = processShortcodes(input);
expect(result).toContain('vc-separator');
expect(result).toContain('border-primary');
expect(result).toContain('width: 50%');
expect(result).toContain('border-top-width: 2px');
});
});
describe('Complete Processing Pipeline', () => {
it('should process mixed HTML and shortcode content', () => {
const input = '<div class="vc-row"><div class="vc-column">[vc_column_text]Text[/vc_column_text]</div></div>';
const result = processHTML(input);
// The HTML gets converted to shortcodes first, then processed
// The vc-row and vc-column get converted to shortcodes then to HTML
// Note: Empty vc_row/vc_column without proper closing may be removed
// The important thing is that the content is processed
expect(result).toBeTruthy();
expect(result).toContain('Text');
});
it('should handle empty or null content', () => {
expect(processHTML(null)).toBe('');
expect(processHTML(undefined)).toBe('');
expect(processHTML('')).toBe('');
});
it('should sanitize dangerous content', () => {
const input = '<div class="vc-row"><script>alert("xss")</script>content</div>';
const result = processHTML(input);
expect(result).not.toContain('<script>');
expect(result).not.toContain('alert');
// The vc-row gets converted to shortcode then processed
// The script gets removed, leaving just the content
expect(result).toContain('content');
});
it('should handle complex home page excerpt', () => {
const homeExcerpt = '[vc_row type="full_width_background" full_screen_row_position="middle" bg_color="#d1d1ca" bg_image="/media/45569-Still-2025-02-10-104337_1.1.1.webp" bg_position="center bottom" bg_repeat="no-repeat" video_bg="use_video" video_mp4="https://klz-cables.com/wp-content/uploads/2025/02/header.mp4" video_webm="https://klz-cables.com/wp-content/uploads/2025/02/header.webm" top_padding="15%" bottom_padding="13%" text_color="light" text_align="left" enable_gradient="true" color_overlay="rgba(0,0,0,0.01)" color_overlay_2="rgba(0,0,0,0.32)" overlay_strength="0.8" gradient_direction="left_to_right"]...[/vc_row]';
const result = processShortcodes(homeExcerpt);
// Should contain all the processed attributes
expect(result).toContain('vc-row');
expect(result).toContain('data-video-bg="true"');
expect(result).toContain('data-video-mp4');
expect(result).toContain('data-video-webm');
// Note: Padding extraction is a known issue
expect(result).toContain('text-white');
expect(result).toContain('text-left');
expect(result).toContain('relative');
// Video background takes precedence, overlay div not added
});
it('should handle multiple vc_rows in sequence', () => {
const input = '[vc_row bg_image="10440"]Row 1[/vc_row][vc_row bg_color="#ffffff"]Row 2[/vc_row]';
const result = processShortcodes(input);
expect(result).toContain('vc-row');
expect(result).toContain('Row 1');
expect(result).toContain('Row 2');
expect(result.match(/vc-row/g)?.length).toBeGreaterThanOrEqual(2);
});
it('should clean up empty divs after processing', () => {
const input = '[vc_row][/vc_row]';
const result = processShortcodes(input);
// Should not contain empty divs
expect(result).not.toMatch(/<div[^>]*>\s*<\/div>/);
});
it('should handle unprocessed shortcodes gracefully', () => {
const input = '[vc_row][unknown_shortcode]content[/unknown_shortcode][/vc_row]';
const result = processShortcodes(input);
// Should still process the vc_row
expect(result).toContain('vc-row');
// Unknown shortcode should be removed
expect(result).not.toContain('[unknown_shortcode]');
});
});
describe('Edge Cases', () => {
it('should handle very long content', () => {
const longContent = '[vc_row]' + 'Lorem ipsum '.repeat(1000) + '[/vc_row]';
const result = processShortcodes(longContent);
expect(result).toContain('vc-row');
expect(result.length).toBeGreaterThan(100);
});
it('should handle special characters in attributes', () => {
const input = '[vc_row bg_image="image with spaces.jpg" color_overlay="rgba(255, 0, 0, 0.5)"]content[/vc_row]';
const result = processShortcodes(input);
expect(result).toContain('vc-row');
expect(result).toContain('background-image: url(image with spaces.jpg)');
expect(result).toContain('rgba(255, 0, 0, 0.5)');
});
it('should handle missing attributes gracefully', () => {
const input = '[vc_row]content[/vc_row]';
const result = processShortcodes(input);
expect(result).toContain('vc-row');
expect(result).toContain('content');
});
it('should handle malformed HTML entities', () => {
const input = 'Test 󴈿 invalid entity &unknown; text';
const result = processHTML(input);
// Should not crash, should handle gracefully
expect(result).toBeTruthy();
expect(result).toContain('Test');
expect(result).toContain('text');
});
it('should handle nested shortcodes with attributes', () => {
const input = '[vc_row bg_image="10440" color_overlay="#000000" overlay_strength="0.8"][vc_column width="6"][vc_column_text]Text[/vc_column_text][/vc_column][/vc_row]';
const result = processShortcodes(input);
expect(result).toContain('vc-row');
expect(result).toContain('vc-column');
expect(result).toContain('Text');
expect(result).toContain('background-image: url(/media/10440-DSC07655-Large.webp)');
});
it('should handle mixed case shortcode names', () => {
const input = '[VC_ROW bg_image="10440"]content[/VC_ROW]';
const result = processShortcodes(input);
// Mixed case shortcodes are not processed by the current regex
// They remain as unprocessed shortcodes and get removed
expect(result).toContain('content');
expect(result).not.toContain('vc-row');
});
it('should handle attributes with spaces', () => {
const input = '[vc_row bg_image="10440" color_overlay="#000000" overlay_strength="0.8" top_padding="10%" bottom_padding="5%"]content[/vc_row]';
const result = processShortcodes(input);
expect(result).toContain('vc-row');
expect(result).toContain('background-image: url(/media/10440-DSC07655-Large.webp)');
// Note: Padding extraction is a known issue
});
});
describe('Real-world Data Examples', () => {
it('should process home page excerpt from actual data', () => {
// Simplified test with complete shortcode
const homeExcerpt = '[vc_row bg_image="/media/45569-Still-2025-02-10-104337_1.1.1.webp" bg_color="#d1d1ca" top_padding="15%" bottom_padding="13%" text_color="light" enable_gradient="true" color_overlay="rgba(0,0,0,0.01)" color_overlay_2="rgba(0,0,0,0.32)" overlay_strength="0.8" gradient_direction="left_to_right"][vc_column width="12"]<h1>Home Content</h1>[/vc_column][/vc_row]';
const result = processShortcodes(homeExcerpt);
// Should process the shortcode
expect(result).toContain('vc-row');
expect(result).toContain('bg-cover');
expect(result).toContain('background-image: url(/media/45569-Still-2025-02-10-104337_1.1.1.webp)');
expect(result).toContain('Home Content');
});
it('should process team page excerpt with background', () => {
// Complete shortcode with local path
const teamExcerpt = '[vc_row bg_image="/media/10440-DSC07655-Large.webp" bg_color="#ffffff" top_padding="14%" bottom_padding="12%" text_color="light" enable_gradient="true" color_overlay="#0a0000" color_overlay_2="rgba(10,10,10,0.5)" overlay_strength="0.8" gradient_direction="left_to_right"][vc_column width="12"]<h2>Team Section</h2>[/vc_column][/vc_row]';
const result = processShortcodes(teamExcerpt);
expect(result).toContain('vc-row');
expect(result).toContain('bg-cover');
expect(result).toContain('relative');
expect(result).toContain('background-image: url(/media/10440-DSC07655-Large.webp)');
expect(result).toContain('Team Section');
});
it('should process contact page with form', () => {
// Complete shortcode structure
const contactContent = '[vc_row][vc_column width="12"]<h5>How can we help you?</h5><h2>Have a project in mind?</h2>[/vc_column][/vc_row]';
const result = processShortcodes(contactContent);
expect(result).toContain('vc-row');
expect(result).toContain('vc-column');
expect(result).toContain('How can we help you?');
});
});
});