refactor: Major hyperscript refactoring and JS elimination

Inline Hyperscript Refactoring:
- Body tag keyboard handlers: 20→8 lines (using helper functions)
- Zoom control handlers: 85→35 lines (using zoom._hs)
- PDF modal card selection: 90→6 lines (3 identical blocks eliminated)

New Hyperscript Files:
- zoom._hs: handleZoomInput, handleZoomReset, initZoomControl
- pdf-modal._hs: selectPdfCard, handlePdfCardKey

JavaScript Elimination (232 lines removed):
- cv-functions.js: REMOVED - hyperscript defs are globally available
- scroll-at-bottom-handler.js: REMOVED - duplicate of handleScroll()
- footer-buttons-interaction.js: REMOVED - moved to hyperscript

Added Tests:
- 32-hyperscript-multi-src.test.mjs: Verifies multi-file loading
- 33-keyboard-shortcuts-refactored.test.mjs: Keyboard shortcuts
- 34-hyperscript-refactor-comprehensive.test.mjs: Full test suite

Key Findings:
- No hyperscript multi-file bug in 0.9.14
- Hyperscript def statements are globally accessible
- Previous refactoring failures were syntax errors, not library bugs
This commit is contained in:
juanatsap
2025-11-30 05:58:44 +00:00
parent 4a02c0a328
commit ba44b435e7
12 changed files with 841 additions and 266 deletions
+284
View File
@@ -0,0 +1,284 @@
/**
* Test: Hyperscript Multiple External SRC Files Bug Investigation
*
* HYPOTHESIS: Hyperscript 0.9.14 has issues when loading multiple
* <script type="text/hyperscript" src="..."> files, even though:
* - Each file parses correctly individually
* - All content works when concatenated inline
*
* This test aims to create a minimal reproducible case.
*/
import puppeteer from 'puppeteer';
const BASE_URL = 'http://localhost:1999';
async function testMultiFileSrcBug() {
console.log('🧪 HYPERSCRIPT MULTI-SRC FILE BUG INVESTIGATION\n');
console.log('=' .repeat(70) + '\n');
const browser = await puppeteer.launch({
headless: false,
args: ['--no-sandbox', '--disable-setuid-sandbox'],
defaultViewport: { width: 1280, height: 900 }
});
const page = await browser.newPage();
// Collect console messages and errors
const consoleMessages = [];
const errors = [];
page.on('console', msg => {
consoleMessages.push({ type: msg.type(), text: msg.text() });
if (msg.type() === 'error') {
console.log(` ❌ Console Error: ${msg.text()}`);
}
});
page.on('pageerror', error => {
errors.push(error.message);
console.log(` ❌ Page Error: ${error.message}`);
});
try {
// =====================================================================
// TEST 1: Load actual CV page and check hyperscript status
// =====================================================================
console.log('1️⃣ Loading CV page to check hyperscript file loading...\n');
await page.goto(BASE_URL, { waitUntil: 'networkidle2', timeout: 30000 });
// Check which hyperscript files were loaded
const scriptTags = await page.evaluate(() => {
const scripts = document.querySelectorAll('script[type="text/hyperscript"]');
return Array.from(scripts).map(s => ({
src: s.src || 'INLINE',
hasContent: s.textContent.length > 0,
contentLength: s.textContent.length,
contentPreview: s.textContent.substring(0, 100)
}));
});
console.log(` Found ${scriptTags.length} hyperscript script tags:\n`);
scriptTags.forEach((tag, i) => {
if (tag.src === 'INLINE') {
console.log(` ${i + 1}. INLINE (${tag.contentLength} chars)`);
} else {
console.log(` ${i + 1}. SRC: ${tag.src}`);
}
});
console.log();
// =====================================================================
// TEST 2: Check if hyperscript is loaded and functioning
// =====================================================================
console.log('2️⃣ Checking hyperscript functionality...\n');
const hyperscriptStatus = await page.evaluate(() => {
return {
hyperscriptExists: typeof _hyperscript !== 'undefined',
browserUtilsExists: typeof _hyperscript !== 'undefined' && _hyperscript.browserInit,
initScrollBehaviorExists: typeof initScrollBehavior === 'function',
handleScrollExists: typeof handleScroll === 'function',
printFriendlyExists: typeof printFriendly === 'function'
};
});
console.log(` _hyperscript loaded: ${hyperscriptStatus.hyperscriptExists ? '✅' : '❌'}`);
console.log(` initScrollBehavior: ${hyperscriptStatus.initScrollBehaviorExists ? '✅' : '❌'}`);
console.log(` handleScroll: ${hyperscriptStatus.handleScrollExists ? '✅' : '❌'}`);
console.log(` printFriendly: ${hyperscriptStatus.printFriendlyExists ? '✅' : '❌'}`);
console.log();
// =====================================================================
// TEST 3: Test keyboard shortcuts (which use inline hyperscript)
// =====================================================================
console.log('3️⃣ Testing keyboard shortcuts (inline hyperscript)...\n');
// Test '?' shortcut to open shortcuts modal
await page.keyboard.press('?');
await new Promise(r => setTimeout(r, 500));
const shortcutsModalOpen = await page.evaluate(() => {
const modal = document.getElementById('shortcuts-modal');
return modal && modal.open;
});
console.log(` '?' key opens shortcuts modal: ${shortcutsModalOpen ? '✅ WORKS' : '❌ FAILS'}`);
// Close modal
if (shortcutsModalOpen) {
await page.keyboard.press('Escape');
await new Promise(r => setTimeout(r, 300));
}
// Test 'V' shortcut to toggle theme
const themeStateBefore = await page.evaluate(() => {
const toggle = document.getElementById('themeToggle');
return toggle ? toggle.checked : null;
});
await page.keyboard.press('v');
await new Promise(r => setTimeout(r, 300));
const themeStateAfter = await page.evaluate(() => {
const toggle = document.getElementById('themeToggle');
return toggle ? toggle.checked : null;
});
const vKeyWorks = themeStateBefore !== null && themeStateAfter !== null && themeStateBefore !== themeStateAfter;
console.log(` 'V' key toggles theme: ${vKeyWorks ? '✅ WORKS' : '❌ FAILS'}`);
console.log();
// =====================================================================
// TEST 4: Test scroll behavior (uses external file functions)
// =====================================================================
console.log('4️⃣ Testing scroll behavior (external file functions)...\n');
// Scroll down
await page.evaluate(() => window.scrollTo(0, 500));
await new Promise(r => setTimeout(r, 500));
const scrollDownResult = await page.evaluate(() => {
const actionBar = document.querySelector('.action-bar');
return actionBar ? actionBar.classList.contains('header-hidden') : null;
});
console.log(` Header hides on scroll down: ${scrollDownResult ? '✅ WORKS' : scrollDownResult === false ? '⚠️ Not hidden (mobile mode?)' : '❌ FAILS'}`);
// Scroll back up
await page.evaluate(() => window.scrollTo(0, 0));
await new Promise(r => setTimeout(r, 500));
const scrollUpResult = await page.evaluate(() => {
const actionBar = document.querySelector('.action-bar');
return actionBar ? !actionBar.classList.contains('header-hidden') : null;
});
console.log(` Header shows on scroll up: ${scrollUpResult ? '✅ WORKS' : '❌ FAILS'}`);
console.log();
// =====================================================================
// TEST 5: Create minimal reproduction page
// =====================================================================
console.log('5️⃣ Creating minimal reproduction test page...\n');
// Create a new page with minimal multi-file setup
const testPage = await browser.newPage();
testPage.on('console', msg => {
if (msg.type() === 'error') {
console.log(` ❌ Test Page Error: ${msg.text()}`);
}
});
testPage.on('pageerror', error => {
console.log(` ❌ Test Page Exception: ${error.message}`);
});
// Create test HTML that loads hyperscript like the CV does
const testHTML = `
<!DOCTYPE html>
<html>
<head>
<title>Hyperscript Multi-File Test</title>
<script type="text/hyperscript" src="/static/hyperscript/utils._hs"></script>
<script type="text/hyperscript" src="/static/hyperscript/toggles._hs"></script>
<script type="text/hyperscript" src="/static/hyperscript/hover-sync._hs"></script>
<script type="text/hyperscript" src="/static/hyperscript/keyboard._hs"></script>
<script src="https://unpkg.com/hyperscript.org@0.9.14"></script>
</head>
<body>
<h1>Multi-File Hyperscript Test</h1>
<div id="test-target">Original</div>
<button id="test-btn" _="on click put 'Clicked!' into #test-target">Click Me</button>
<div id="status"></div>
<script>
document.getElementById('status').textContent =
'initScrollBehavior: ' + (typeof initScrollBehavior === 'function' ? 'YES' : 'NO') +
', handleScroll: ' + (typeof handleScroll === 'function' ? 'YES' : 'NO') +
', printFriendly: ' + (typeof printFriendly === 'function' ? 'YES' : 'NO');
</script>
</body>
</html>
`;
// We can't serve arbitrary HTML, but we can check the existing hyperscript files
console.log(' Checking hyperscript file contents from server...\n');
// Fetch each hyperscript file
const hsFiles = [
'/static/hyperscript/utils._hs',
'/static/hyperscript/toggles._hs',
'/static/hyperscript/hover-sync._hs',
'/static/hyperscript/keyboard._hs'
];
for (const file of hsFiles) {
try {
const response = await page.evaluate(async (url) => {
const res = await fetch(url);
const text = await res.text();
return {
status: res.status,
contentType: res.headers.get('content-type'),
length: text.length,
preview: text.substring(0, 200)
};
}, BASE_URL + file);
console.log(` ${file}:`);
console.log(` Status: ${response.status}, Size: ${response.length} chars`);
console.log(` Content-Type: ${response.contentType}`);
} catch (err) {
console.log(` ${file}: ❌ Error - ${err.message}`);
}
}
await testPage.close();
// =====================================================================
// SUMMARY
// =====================================================================
console.log('\n' + '=' .repeat(70));
console.log('📊 TEST SUMMARY\n');
const allWorking = shortcutsModalOpen && vKeyWorks && (scrollDownResult !== null) && (scrollUpResult !== null);
if (allWorking) {
console.log(' ✅ All hyperscript features are working!');
console.log(' ✅ Both inline and external file functions work correctly.');
console.log('\n CONCLUSION: No multi-file bug detected in current setup.');
console.log(' The previous errors may have been caused by:');
console.log(' - Syntax errors in the refactored code');
console.log(' - Order of script loading');
console.log(' - Content-Type headers for ._hs files');
} else {
console.log(' ⚠️ Some features not working. Check errors above.');
console.log('\n This may indicate a multi-file loading issue.');
}
console.log('\n Console errors detected: ' + consoleMessages.filter(m => m.type === 'error').length);
console.log(' Page errors detected: ' + errors.length);
if (errors.length > 0) {
console.log('\n Error details:');
errors.forEach((err, i) => console.log(` ${i + 1}. ${err}`));
}
console.log('\n' + '=' .repeat(70));
console.log('\nBrowser staying open for manual inspection...');
console.log('Press Ctrl+C when done.\n');
// Keep browser open for inspection
await new Promise(() => {});
} catch (error) {
console.error('Test error:', error);
await browser.close();
process.exit(1);
}
}
testMultiFileSrcBug();
@@ -0,0 +1,86 @@
/**
* Test keyboard shortcuts after refactoring to use external helper functions
*/
import puppeteer from 'puppeteer';
const BASE_URL = 'http://localhost:1999';
async function testKeyboardShortcuts() {
console.log('🧪 KEYBOARD SHORTCUTS TEST (Refactored)\n');
const browser = await puppeteer.launch({
headless: true,
args: ['--no-sandbox']
});
const page = await browser.newPage();
const errors = [];
page.on('pageerror', err => errors.push(err.message));
page.on('console', msg => {
if (msg.type() === 'error') errors.push(msg.text());
});
try {
await page.goto(BASE_URL, { waitUntil: 'networkidle2', timeout: 15000 });
// Test '?' shortcut - opens modal
console.log("Testing '?' shortcut...");
await page.keyboard.press('?');
await new Promise(r => setTimeout(r, 400));
const modalOpen = await page.evaluate(() => {
const m = document.getElementById('shortcuts-modal');
return m && m.open;
});
console.log(` '?' opens modal: ${modalOpen ? '✅ PASS' : '❌ FAIL'}`);
// Close modal
if (modalOpen) {
await page.keyboard.press('Escape');
await new Promise(r => setTimeout(r, 200));
}
// Test 'V' shortcut - toggles theme
console.log("Testing 'V' shortcut...");
const vBefore = await page.evaluate(() => document.getElementById('themeToggle')?.checked);
await page.keyboard.press('v');
await new Promise(r => setTimeout(r, 400));
const vAfter = await page.evaluate(() => document.getElementById('themeToggle')?.checked);
const vWorks = vBefore !== vAfter;
console.log(` 'V' toggles theme: ${vWorks ? '✅ PASS' : '❌ FAIL'} (${vBefore}${vAfter})`);
// Test 'L' shortcut - toggles length
console.log("Testing 'L' shortcut...");
const lBefore = await page.evaluate(() => document.getElementById('lengthToggle')?.checked);
await page.keyboard.press('l');
await new Promise(r => setTimeout(r, 400));
const lAfter = await page.evaluate(() => document.getElementById('lengthToggle')?.checked);
const lWorks = lBefore !== lAfter;
console.log(` 'L' toggles length: ${lWorks ? '✅ PASS' : '❌ FAIL'} (${lBefore}${lAfter})`);
// Test 'I' shortcut - toggles icons
console.log("Testing 'I' shortcut...");
const iBefore = await page.evaluate(() => document.getElementById('iconToggle')?.checked);
await page.keyboard.press('i');
await new Promise(r => setTimeout(r, 400));
const iAfter = await page.evaluate(() => document.getElementById('iconToggle')?.checked);
const iWorks = iBefore !== iAfter;
console.log(` 'I' toggles icons: ${iWorks ? '✅ PASS' : '❌ FAIL'} (${iBefore}${iAfter})`);
// Summary
console.log('\n=== SUMMARY ===');
const allPass = modalOpen && vWorks && lWorks && iWorks;
console.log(`All shortcuts work: ${allPass ? '✅ ALL PASS' : '❌ SOME FAILED'}`);
console.log(`Console errors: ${errors.length ? errors.join(', ') : 'None'}`);
await browser.close();
process.exit(allPass && errors.length === 0 ? 0 : 1);
} catch (error) {
console.error('Test error:', error.message);
await browser.close();
process.exit(1);
}
}
testKeyboardShortcuts();
@@ -0,0 +1,200 @@
/**
* Comprehensive test for all hyperscript refactoring
* Tests: keyboard shortcuts, zoom control, PDF modal card selection
*/
import puppeteer from 'puppeteer';
const BASE_URL = 'http://localhost:1999';
async function testAll() {
console.log('🧪 COMPREHENSIVE HYPERSCRIPT REFACTORING TEST\n');
console.log('=' .repeat(60) + '\n');
const browser = await puppeteer.launch({
headless: true,
args: ['--no-sandbox']
});
const page = await browser.newPage();
const errors = [];
page.on('pageerror', err => errors.push(err.message));
page.on('console', msg => {
if (msg.type() === 'error') errors.push(msg.text());
});
let allPass = true;
try {
await page.goto(BASE_URL, { waitUntil: 'networkidle2', timeout: 15000 });
// ==============================================================
// TEST 1: Hyperscript Functions Loaded
// ==============================================================
console.log('1️⃣ Testing hyperscript functions loaded...\n');
const funcs = await page.evaluate(() => ({
// Keyboard functions
handleToggleShortcut: typeof handleToggleShortcut === 'function',
openModalShortcut: typeof openModalShortcut === 'function',
// Zoom functions
initZoomControl: typeof initZoomControl === 'function',
handleZoomInput: typeof handleZoomInput === 'function',
handleZoomReset: typeof handleZoomReset === 'function',
// PDF modal functions
selectPdfCard: typeof selectPdfCard === 'function',
handlePdfCardKey: typeof handlePdfCardKey === 'function',
// Core functions
initScrollBehavior: typeof initScrollBehavior === 'function',
handleScroll: typeof handleScroll === 'function',
printFriendly: typeof printFriendly === 'function'
}));
const funcResults = Object.entries(funcs).map(([name, exists]) => {
const status = exists ? '✅' : '❌';
if (!exists) allPass = false;
return ` ${name}: ${status}`;
});
console.log(funcResults.join('\n'));
console.log();
// ==============================================================
// TEST 2: Keyboard Shortcuts
// ==============================================================
console.log('2️⃣ Testing keyboard shortcuts...\n');
// Test '?' shortcut
await page.keyboard.press('?');
await new Promise(r => setTimeout(r, 400));
const modalOpen = await page.evaluate(() => {
const m = document.getElementById('shortcuts-modal');
return m && m.open;
});
console.log(` '?' opens modal: ${modalOpen ? '✅' : '❌'}`);
if (!modalOpen) allPass = false;
if (modalOpen) {
await page.keyboard.press('Escape');
await new Promise(r => setTimeout(r, 200));
}
// Test 'L' shortcut
const lBefore = await page.evaluate(() => document.getElementById('lengthToggle')?.checked);
await page.keyboard.press('l');
await new Promise(r => setTimeout(r, 400));
const lAfter = await page.evaluate(() => document.getElementById('lengthToggle')?.checked);
const lWorks = lBefore !== lAfter;
console.log(` 'L' toggles length: ${lWorks ? '✅' : '❌'}`);
if (!lWorks) allPass = false;
console.log();
// ==============================================================
// TEST 3: Zoom Control Functions
// ==============================================================
console.log('3️⃣ Testing zoom control...\n');
// Test zoom slider functionality via function call
const zoomTest = await page.evaluate(() => {
const slider = document.getElementById('zoom-slider');
if (!slider) return { error: 'No slider found' };
// Set slider to 150
slider.value = 150;
handleZoomInput(slider);
const wrapper = document.getElementById('zoom-wrapper');
const valueDisplay = document.getElementById('zoom-value-current');
return {
sliderValue: slider.value,
wrapperZoom: wrapper ? wrapper.style.zoom : null,
displayValue: valueDisplay ? valueDisplay.textContent : null
};
});
const zoomSliderWorks = zoomTest.sliderValue === '150' && zoomTest.displayValue === '150';
console.log(` Zoom slider at 150%: ${zoomSliderWorks ? '✅' : '❌'}`);
if (!zoomSliderWorks) allPass = false;
// Test reset function
const resetTest = await page.evaluate(() => {
handleZoomReset();
const slider = document.getElementById('zoom-slider');
const valueDisplay = document.getElementById('zoom-value-current');
return {
sliderValue: slider ? slider.value : null,
displayValue: valueDisplay ? valueDisplay.textContent : null
};
});
const resetWorks = resetTest.sliderValue === '100' && resetTest.displayValue === '100';
console.log(` Zoom reset to 100%: ${resetWorks ? '✅' : '❌'}`);
if (!resetWorks) allPass = false;
console.log();
// ==============================================================
// TEST 4: PDF Modal Card Selection
// ==============================================================
console.log('4️⃣ Testing PDF modal card selection...\n');
// Open PDF modal
await page.evaluate(() => document.getElementById('pdf-modal').showModal());
await new Promise(r => setTimeout(r, 500));
const pdfModalOpen = await page.evaluate(() => {
const modal = document.getElementById('pdf-modal');
return modal && modal.open;
});
console.log(` PDF modal opens: ${pdfModalOpen ? '✅' : '❌'}`);
if (!pdfModalOpen) allPass = false;
if (pdfModalOpen) {
// Test card selection
const cardTest = await page.evaluate(() => {
const shortCard = document.querySelector('[data-cv-format="short"]');
if (!shortCard) return { error: 'Short card not found' };
// Click short card
selectPdfCard(shortCard);
return {
shortSelected: shortCard.classList.contains('selected'),
shortAriaChecked: shortCard.getAttribute('aria-checked'),
defaultNotSelected: !document.querySelector('[data-cv-format="default"]').classList.contains('selected'),
formatStored: window.selectedPdfFormat
};
});
const cardSelectWorks = cardTest.shortSelected && cardTest.shortAriaChecked === 'true' && cardTest.defaultNotSelected;
console.log(` Card selection works: ${cardSelectWorks ? '✅' : '❌'}`);
if (!cardSelectWorks) allPass = false;
// Close modal
await page.keyboard.press('Escape');
await new Promise(r => setTimeout(r, 200));
}
console.log();
// ==============================================================
// SUMMARY
// ==============================================================
console.log('=' .repeat(60));
console.log('\n📊 TEST SUMMARY\n');
console.log(` Overall: ${allPass ? '✅ ALL TESTS PASSED' : '❌ SOME TESTS FAILED'}`);
console.log(` Console errors: ${errors.length ? errors.join(', ') : 'None'}`);
console.log();
await browser.close();
process.exit(allPass && errors.length === 0 ? 0 : 1);
} catch (error) {
console.error('Test error:', error.message);
await browser.close();
process.exit(1);
}
}
testAll();