- Add StyleDetectionEngine with one-layer-deep nested element analysis - Add HTMLPreservationEngine for direct HTML manipulation without lossy conversion - Implement structure-preserving content parsing that maintains element positions - Add multi-property element support for links (href + content), images (src + alt), buttons - Create comprehensive test suite with real DOM element validation - Replace markdown-based system foundation with HTML-first architecture - Preserve all element attributes (classes, IDs, data-*, aria-*) during editing - Generate human-readable style names from detected nested elements - Support template extraction with multiple insertion points for complex elements Foundation complete for Phase 2 style-aware editor interface per CLASSES.md specification.
337 lines
13 KiB
JavaScript
337 lines
13 KiB
JavaScript
/**
|
|
* Simple test runner for style preservation system
|
|
* Tests our implementation with actual DOM elements
|
|
*/
|
|
|
|
import { styleDetectionEngine } from './style-detection.js';
|
|
import { htmlPreservationEngine } from './html-preservation.js';
|
|
|
|
/**
|
|
* Run all style detection tests with real DOM elements
|
|
*/
|
|
export function runStyleDetectionTests() {
|
|
console.log('🧪 Running Style Detection Tests');
|
|
console.log('================================');
|
|
|
|
const results = [];
|
|
|
|
// Test 1: Demo Example 1 - Styled Strong Element
|
|
results.push(testExample1());
|
|
|
|
// Test 2: Demo Example 2 - Styled Link Element
|
|
results.push(testExample2());
|
|
|
|
// Test 3: Demo Example 4 - Multiple Styled Elements
|
|
results.push(testExample4());
|
|
|
|
// Test 4: Demo Example 5 - Button with Data Attributes
|
|
results.push(testExample5());
|
|
|
|
// Summary
|
|
const passed = results.filter(r => r.passed).length;
|
|
const total = results.length;
|
|
|
|
console.log(`\n📊 Test Results: ${passed}/${total} passed`);
|
|
|
|
if (passed === total) {
|
|
console.log('✅ All style detection tests passed!');
|
|
} else {
|
|
console.log('❌ Some tests failed - see details above');
|
|
}
|
|
|
|
return { passed, total, results };
|
|
}
|
|
|
|
/**
|
|
* Test Example 1: <p>Hello <strong class="emph">world</strong> and welcome!</p>
|
|
*/
|
|
function testExample1() {
|
|
console.log('\n🔍 Test 1: Styled Strong Element');
|
|
|
|
try {
|
|
// Create test element
|
|
const container = document.createElement('p');
|
|
container.className = 'insertr';
|
|
container.innerHTML = 'Hello <strong class="emph">world</strong> and welcome!';
|
|
|
|
// Detect styles
|
|
const detectedStyles = styleDetectionEngine.detectStyles(container);
|
|
|
|
// Validate results
|
|
const hasEmphStyle = detectedStyles.has('strong_emph');
|
|
const emphStyle = detectedStyles.get('strong_emph');
|
|
const correctName = emphStyle?.name === 'Emphasis';
|
|
const correctTag = emphStyle?.tagName === 'strong';
|
|
const correctClasses = emphStyle?.classes?.includes('emph');
|
|
|
|
const passed = hasEmphStyle && correctName && correctTag && correctClasses;
|
|
|
|
console.log(` Detected ${detectedStyles.size} style(s)`);
|
|
console.log(` Found "Emphasis" style: ${hasEmphStyle ? '✅' : '❌'}`);
|
|
console.log(` Correct name: ${correctName ? '✅' : '❌'}`);
|
|
console.log(` Result: ${passed ? '✅ PASSED' : '❌ FAILED'}`);
|
|
|
|
return { test: 'Example 1', passed, details: { hasEmphStyle, correctName, correctTag, correctClasses } };
|
|
|
|
} catch (error) {
|
|
console.log(` Error: ${error.message}`);
|
|
console.log(` Result: ❌ FAILED`);
|
|
return { test: 'Example 1', passed: false, error: error.message };
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Test Example 2: <p>Visit our <a class="fancy" href="#about">about page</a> for more info.</p>
|
|
*/
|
|
function testExample2() {
|
|
console.log('\n🔍 Test 2: Styled Link Element');
|
|
|
|
try {
|
|
const container = document.createElement('p');
|
|
container.className = 'insertr';
|
|
container.innerHTML = 'Visit our <a class="fancy" href="#about">about page</a> for more info.';
|
|
|
|
const detectedStyles = styleDetectionEngine.detectStyles(container);
|
|
|
|
const hasFancyStyle = detectedStyles.has('a_fancy');
|
|
const fancyStyle = detectedStyles.get('a_fancy');
|
|
const correctName = fancyStyle?.name === 'Fancy Link';
|
|
const hasHref = fancyStyle?.attributes?.href === '#about';
|
|
|
|
const passed = hasFancyStyle && correctName && hasHref;
|
|
|
|
console.log(` Detected ${detectedStyles.size} style(s)`);
|
|
console.log(` Found "Fancy Link" style: ${hasFancyStyle ? '✅' : '❌'}`);
|
|
console.log(` Preserved href attribute: ${hasHref ? '✅' : '❌'}`);
|
|
console.log(` Result: ${passed ? '✅ PASSED' : '❌ FAILED'}`);
|
|
|
|
return { test: 'Example 2', passed, details: { hasFancyStyle, correctName, hasHref } };
|
|
|
|
} catch (error) {
|
|
console.log(` Error: ${error.message}`);
|
|
console.log(` Result: ❌ FAILED`);
|
|
return { test: 'Example 2', passed: false, error: error.message };
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Test Example 4: Multiple styled elements
|
|
*/
|
|
function testExample4() {
|
|
console.log('\n🔍 Test 4: Multiple Styled Elements');
|
|
|
|
try {
|
|
const container = document.createElement('p');
|
|
container.className = 'insertr';
|
|
container.innerHTML = 'Welcome to <strong class="brand">Acme Corp</strong> where we create <span class="highlight">innovative</span> solutions for <em class="emph">modern</em> businesses.';
|
|
|
|
const detectedStyles = styleDetectionEngine.detectStyles(container);
|
|
|
|
const hasBrand = detectedStyles.has('strong_brand');
|
|
const hasHighlight = detectedStyles.has('span_highlight');
|
|
const hasEmphItalic = detectedStyles.has('em_emph');
|
|
const correctCount = detectedStyles.size === 3;
|
|
|
|
const passed = hasBrand && hasHighlight && hasEmphItalic && correctCount;
|
|
|
|
console.log(` Detected ${detectedStyles.size} style(s) (expected 3)`);
|
|
console.log(` Found "Brand" style: ${hasBrand ? '✅' : '❌'}`);
|
|
console.log(` Found "Highlight" style: ${hasHighlight ? '✅' : '❌'}`);
|
|
console.log(` Found "Emphasis Italic" style: ${hasEmphItalic ? '✅' : '❌'}`);
|
|
console.log(` Result: ${passed ? '✅ PASSED' : '❌ FAILED'}`);
|
|
|
|
return { test: 'Example 4', passed, details: { hasBrand, hasHighlight, hasEmphItalic, correctCount } };
|
|
|
|
} catch (error) {
|
|
console.log(` Error: ${error.message}`);
|
|
console.log(` Result: ❌ FAILED`);
|
|
return { test: 'Example 4', passed: false, error: error.message };
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Test Example 5: Button with data attributes
|
|
*/
|
|
function testExample5() {
|
|
console.log('\n🔍 Test 5: Button with Data Attributes');
|
|
|
|
try {
|
|
const container = document.createElement('p');
|
|
container.className = 'insertr';
|
|
container.innerHTML = 'Ready to start? <button class="btn" data-action="signup" data-analytics="cta-main">Sign Up Now</button> and begin your journey!';
|
|
|
|
const detectedStyles = styleDetectionEngine.detectStyles(container);
|
|
|
|
const hasButtonStyle = detectedStyles.has('button_btn');
|
|
const buttonStyle = detectedStyles.get('button_btn');
|
|
const correctName = buttonStyle?.name === 'Button Style';
|
|
const hasDataAction = buttonStyle?.attributes?.['data-action'] === 'signup';
|
|
const hasDataAnalytics = buttonStyle?.attributes?.['data-analytics'] === 'cta-main';
|
|
|
|
const passed = hasButtonStyle && correctName && hasDataAction && hasDataAnalytics;
|
|
|
|
console.log(` Detected ${detectedStyles.size} style(s)`);
|
|
console.log(` Found "Button Style": ${hasButtonStyle ? '✅' : '❌'}`);
|
|
console.log(` Preserved data-action: ${hasDataAction ? '✅' : '❌'}`);
|
|
console.log(` Preserved data-analytics: ${hasDataAnalytics ? '✅' : '❌'}`);
|
|
console.log(` Result: ${passed ? '✅ PASSED' : '❌ FAILED'}`);
|
|
|
|
return { test: 'Example 5', passed, details: { hasButtonStyle, correctName, hasDataAction, hasDataAnalytics } };
|
|
|
|
} catch (error) {
|
|
console.log(` Error: ${error.message}`);
|
|
console.log(` Result: ❌ FAILED`);
|
|
return { test: 'Example 5', passed: false, error: error.message };
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Run HTML preservation tests
|
|
*/
|
|
export function runHTMLPreservationTests() {
|
|
console.log('\n🧪 Running HTML Preservation Tests');
|
|
console.log('==================================');
|
|
|
|
const results = [];
|
|
|
|
results.push(testHTMLExtraction());
|
|
results.push(testHTMLApplication());
|
|
results.push(testAttributePreservation());
|
|
|
|
const passed = results.filter(r => r.passed).length;
|
|
const total = results.length;
|
|
|
|
console.log(`\n📊 HTML Preservation Results: ${passed}/${total} passed`);
|
|
|
|
return { passed, total, results };
|
|
}
|
|
|
|
function testHTMLExtraction() {
|
|
console.log('\n🔍 HTML Extraction Test');
|
|
|
|
try {
|
|
const element = document.createElement('p');
|
|
element.className = 'insertr test';
|
|
element.id = 'test-element';
|
|
element.innerHTML = 'Hello <strong class="emph">world</strong>!';
|
|
|
|
const extracted = htmlPreservationEngine.extractForEditing(element);
|
|
|
|
const hasHTML = extracted.html === 'Hello <strong class="emph">world</strong>!';
|
|
const hasText = extracted.text === 'Hello world!';
|
|
const hasAttributes = extracted.containerAttributes.class === 'insertr test';
|
|
const detectsNesting = extracted.hasNestedElements === true;
|
|
|
|
const passed = hasHTML && hasText && hasAttributes && detectsNesting;
|
|
|
|
console.log(` HTML extraction: ${hasHTML ? '✅' : '❌'}`);
|
|
console.log(` Text extraction: ${hasText ? '✅' : '❌'}`);
|
|
console.log(` Attribute preservation: ${hasAttributes ? '✅' : '❌'}`);
|
|
console.log(` Nesting detection: ${detectsNesting ? '✅' : '❌'}`);
|
|
console.log(` Result: ${passed ? '✅ PASSED' : '❌ FAILED'}`);
|
|
|
|
return { test: 'HTML Extraction', passed };
|
|
|
|
} catch (error) {
|
|
console.log(` Error: ${error.message}`);
|
|
console.log(` Result: ❌ FAILED`);
|
|
return { test: 'HTML Extraction', passed: false, error: error.message };
|
|
}
|
|
}
|
|
|
|
function testHTMLApplication() {
|
|
console.log('\n🔍 HTML Application Test');
|
|
|
|
try {
|
|
const element = document.createElement('p');
|
|
element.className = 'insertr original';
|
|
|
|
const newHTML = 'Updated <strong class="emph">content</strong>!';
|
|
const success = htmlPreservationEngine.applyFromEditing(element, newHTML);
|
|
|
|
const appliedCorrectly = element.innerHTML === newHTML;
|
|
const preservedClass = element.className === 'insertr original';
|
|
|
|
const passed = success && appliedCorrectly && preservedClass;
|
|
|
|
console.log(` Application success: ${success ? '✅' : '❌'}`);
|
|
console.log(` Content applied: ${appliedCorrectly ? '✅' : '❌'}`);
|
|
console.log(` Container class preserved: ${preservedClass ? '✅' : '❌'}`);
|
|
console.log(` Result: ${passed ? '✅ PASSED' : '❌ FAILED'}`);
|
|
|
|
return { test: 'HTML Application', passed };
|
|
|
|
} catch (error) {
|
|
console.log(` Error: ${error.message}`);
|
|
console.log(` Result: ❌ FAILED`);
|
|
return { test: 'HTML Application', passed: false, error: error.message };
|
|
}
|
|
}
|
|
|
|
function testAttributePreservation() {
|
|
console.log('\n🔍 Attribute Preservation Test');
|
|
|
|
try {
|
|
const element = document.createElement('div');
|
|
const originalAttrs = {
|
|
class: 'insertr test',
|
|
id: 'unique-id',
|
|
'data-value': '123'
|
|
};
|
|
|
|
// Apply original attributes
|
|
Object.entries(originalAttrs).forEach(([key, value]) => {
|
|
element.setAttribute(key, value);
|
|
});
|
|
|
|
// Extract and restore
|
|
const extracted = htmlPreservationEngine.extractElementAttributes(element);
|
|
const newElement = document.createElement('div');
|
|
htmlPreservationEngine.restoreElementAttributes(newElement, extracted);
|
|
|
|
const classRestored = newElement.getAttribute('class') === 'insertr test';
|
|
const idRestored = newElement.getAttribute('id') === 'unique-id';
|
|
const dataRestored = newElement.getAttribute('data-value') === '123';
|
|
|
|
const passed = classRestored && idRestored && dataRestored;
|
|
|
|
console.log(` Class attribute: ${classRestored ? '✅' : '❌'}`);
|
|
console.log(` ID attribute: ${idRestored ? '✅' : '❌'}`);
|
|
console.log(` Data attribute: ${dataRestored ? '✅' : '❌'}`);
|
|
console.log(` Result: ${passed ? '✅ PASSED' : '❌ FAILED'}`);
|
|
|
|
return { test: 'Attribute Preservation', passed };
|
|
|
|
} catch (error) {
|
|
console.log(` Error: ${error.message}`);
|
|
console.log(` Result: ❌ FAILED`);
|
|
return { test: 'Attribute Preservation', passed: false, error: error.message };
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Run all tests
|
|
*/
|
|
export function runAllTests() {
|
|
console.log('🚀 Running All Style Preservation System Tests');
|
|
console.log('==============================================');
|
|
|
|
const styleResults = runStyleDetectionTests();
|
|
const htmlResults = runHTMLPreservationTests();
|
|
|
|
const totalPassed = styleResults.passed + htmlResults.passed;
|
|
const totalTests = styleResults.total + htmlResults.total;
|
|
|
|
console.log(`\n🎯 Overall Results: ${totalPassed}/${totalTests} tests passed`);
|
|
|
|
if (totalPassed === totalTests) {
|
|
console.log('🎉 All tests passed! Style preservation system is working correctly.');
|
|
} else {
|
|
console.log('⚠️ Some tests failed. Review implementation before proceeding.');
|
|
}
|
|
|
|
return {
|
|
passed: totalPassed === totalTests,
|
|
details: { styleResults, htmlResults }
|
|
};
|
|
} |