cleanup
This commit is contained in:
@@ -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);
|
||||
}
|
||||
});
|
||||
});
|
||||
@@ -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?');
|
||||
});
|
||||
});
|
||||
});
|
||||
Reference in New Issue
Block a user