diff --git a/doc/11-PDF-EXPORT.md b/doc/11-PDF-EXPORT.md index d2926a4..bddcba6 100644 --- a/doc/11-PDF-EXPORT.md +++ b/doc/11-PDF-EXPORT.md @@ -710,8 +710,49 @@ curl -I http://localhost:1999/cv-jamr-2025-en.pdf # Test invalid year (should fail) curl -I http://localhost:1999/cv-jamr-2024-en.pdf # Expected: HTTP/1.1 404 Not Found + +# Run automated test +bun tests/mjs/28-references-pdf-download.test.mjs ``` +### References Section Integration + +The shortcut URLs are integrated into the **References section** of the CV for easy access: + +**Template Integration** (`templates/partials/sections/references.html`): +```html +{{if eq .Action "downloadPDF"}} + + {{.LinkText}} + +{{end}} +``` + +**Data Configuration** (`data/cv-en.json`, `data/cv-es.json`): +```json +{ + "title": "Download this curriculum in English", + "action": "downloadPDF", + "textBefore": "Download this curriculum in", + "linkText": "English" +} +``` + +**Key Features:** +- **Direct link** - No modal, no JavaScript dependency +- **Language-aware** - Spanish CV gets Spanish link, English CV gets English link +- **Year-aware** - Automatically uses current year +- **Works everywhere** - PDF exports, emails, external shares +- **Security** - Proper `target="_blank"` and `rel="noopener noreferrer"` attributes + +**Use Cases:** +- ✅ Works in printed PDFs (link remains functional) +- ✅ Works in email clients (direct download link) +- ✅ Works without JavaScript (static HTML link) +- ✅ Works when shared externally (permanent, memorable URL) + ## Design Philosophy ### Why This Naming Convention? diff --git a/tests/mjs/28-references-pdf-download.test.mjs b/tests/mjs/28-references-pdf-download.test.mjs index 596f577..88f75f0 100755 --- a/tests/mjs/28-references-pdf-download.test.mjs +++ b/tests/mjs/28-references-pdf-download.test.mjs @@ -1,30 +1,32 @@ #!/usr/bin/env bun /** - * REFERENCES SECTION - PDF DOWNLOAD INTEGRATION TEST - * =================================================== - * Tests the dynamic PDF download links in the References section + * REFERENCES SECTION - DIRECT PDF SHORTCUT LINK TEST + * ==================================================== + * Tests the direct PDF download shortcut links in the References section * * Coverage: * - References section renders with "Download this curriculum" links - * - Links have action="downloadPDF" attribute - * - Clicking the link opens the PDF modal (not direct download) - * - Modal "Current View" option reads localStorage settings - * - Dynamic URL generation based on current view (length, icons, theme) - * - Both English and Spanish variants work correctly + * - Links point directly to shortcut URLs (no modal) + * - English CV: /cv-jamr-{year}-en.pdf + * - Spanish CV: /cv-jamr-{year}-es.pdf + * - Year is dynamic (current year) + * - Links have target="_blank" and proper security attributes + * - No onclick handlers (direct navigation) + * - Works in non-browser environments (PDF exports, emails, etc.) * * Integration: * - Data: cv-en.json and cv-es.json with action: "downloadPDF" - * - Template: references.html with {{if eq .Action "downloadPDF"}} logic - * - Modal: pdf-modal.html with downloadPDF() function - * - Backend: ExportPDF handler with parameter validation + * - Template: references.html with shortcut URL logic + * - Backend: DefaultCVShortcut handler for year validation */ import { chromium } from 'playwright'; const URL = "http://localhost:1999"; +const CURRENT_YEAR = new Date().getFullYear(); -async function testReferencesPDFDownload() { - console.log('📚 REFERENCES SECTION - PDF DOWNLOAD INTEGRATION TEST\n'); +async function testReferencesPDFShortcutLinks() { + console.log('📚 REFERENCES SECTION - DIRECT PDF SHORTCUT LINK TEST\n'); console.log('='.repeat(70)); const browser = await chromium.launch({ headless: false }); @@ -42,9 +44,9 @@ async function testReferencesPDFDownload() { }); // ======================================================================== - // TEST 1: English Version - References Section Structure + // TEST 1: English Version - Direct Shortcut URL // ======================================================================== - console.log("\n1️⃣ Testing English References Section..."); + console.log("\n1️⃣ Testing English Direct Shortcut URL..."); await page.goto(`${URL}/?lang=en`); await page.waitForTimeout(1500); @@ -58,19 +60,17 @@ async function testReferencesPDFDownload() { }); await page.waitForTimeout(1000); - const enReferences = await page.evaluate(() => { + const enReferences = await page.evaluate((currentYear) => { const section = document.querySelector('#references'); if (!section) return { found: false }; // Find the reference-item div that contains "Download this curriculum" const referenceItems = section.querySelectorAll('.reference-item'); let downloadLink = null; - let parentText = ''; for (const item of referenceItems) { if (item.textContent.includes('Download this curriculum')) { downloadLink = item.querySelector('a'); - parentText = item.textContent.trim(); break; } } @@ -80,94 +80,39 @@ async function testReferencesPDFDownload() { sectionExists: !!section, downloadLinkExists: !!downloadLink, downloadLinkText: downloadLink ? downloadLink.textContent.trim() : null, - fullText: parentText, + href: downloadLink ? downloadLink.getAttribute('href') : null, + target: downloadLink ? downloadLink.getAttribute('target') : null, + rel: downloadLink ? downloadLink.getAttribute('rel') : null, hasOnClick: downloadLink ? downloadLink.hasAttribute('onclick') : false, - onClickContent: downloadLink ? downloadLink.getAttribute('onclick') : null, - href: downloadLink ? downloadLink.getAttribute('href') : null + expectedHref: `/cv-jamr-${currentYear}-en.pdf`, + hrefMatches: downloadLink ? downloadLink.getAttribute('href') === `/cv-jamr-${currentYear}-en.pdf` : false }; - }); + }, CURRENT_YEAR); console.log(` References section: ${enReferences.sectionExists ? '✅' : '❌'}`); console.log(` Download link exists: ${enReferences.downloadLinkExists ? '✅' : '❌'}`); console.log(` Link text: "${enReferences.downloadLinkText}"`); - console.log(` Has onClick handler: ${enReferences.hasOnClick ? '✅' : '❌'}`); - console.log(` OnClick calls openPdfModal: ${enReferences.onClickContent?.includes('openPdfModal') ? '✅' : '❌'}`); console.log(` Href: "${enReferences.href}"`); + console.log(` Expected: "${enReferences.expectedHref}"`); + console.log(` Href matches: ${enReferences.hrefMatches ? '✅' : '❌'}`); + console.log(` Target="_blank": ${enReferences.target === '_blank' ? '✅' : '❌'}`); + console.log(` Rel security attrs: ${enReferences.rel === 'noopener noreferrer' ? '✅' : '❌'}`); + console.log(` No onclick handler: ${!enReferences.hasOnClick ? '✅' : '❌'}`); const test1Passed = enReferences.sectionExists && enReferences.downloadLinkExists && - enReferences.hasOnClick && - enReferences.onClickContent?.includes('openPdfModal'); + enReferences.hrefMatches && + enReferences.target === '_blank' && + enReferences.rel === 'noopener noreferrer' && + !enReferences.hasOnClick; - console.log(` ${test1Passed ? '✅ PASS' : '❌ FAIL'} - English references structure`); - testResults.push({ test: 'EN References Structure', passed: test1Passed }); + console.log(` ${test1Passed ? '✅ PASS' : '❌ FAIL'} - English direct shortcut URL`); + testResults.push({ test: 'EN Direct Shortcut URL', passed: test1Passed }); // ======================================================================== - // TEST 2: Click English Download Link - Opens PDF Modal + // TEST 2: Spanish Version - Direct Shortcut URL // ======================================================================== - console.log("\n2️⃣ Testing Click Opens PDF Modal (English)..."); - - // Check modal is initially hidden - const modalInitiallyHidden = await page.evaluate(() => { - const modal = document.querySelector('#pdf-modal'); - return modal ? !modal.open : false; - }); - - console.log(` Modal initially hidden: ${modalInitiallyHidden ? '✅' : '❌'}`); - - // Click the download link - const linkClicked = await page.evaluate(() => { - const section = document.querySelector('#references'); - if (!section) return false; - - // Find the reference-item div that contains "Download this curriculum" - const referenceItems = section.querySelectorAll('.reference-item'); - for (const item of referenceItems) { - if (item.textContent.includes('Download this curriculum')) { - const downloadLink = item.querySelector('a'); - if (downloadLink) { - downloadLink.click(); - return true; - } - } - } - return false; - }); - - console.log(` Link clicked: ${linkClicked ? '✅' : '❌'}`); - await page.waitForTimeout(500); - - // Verify modal is now open - const modalOpen = await page.evaluate(() => { - const modal = document.querySelector('#pdf-modal'); - return modal ? modal.open : false; - }); - - console.log(` Modal opened: ${modalOpen ? '✅' : '❌'}`); - - // Verify modal has the three options - const modalOptions = await page.evaluate(() => { - const cards = document.querySelectorAll('#pdf-modal .pdf-option-card'); - return { - count: cards.length, - formats: Array.from(cards).map(card => card.getAttribute('data-cv-format')) - }; - }); - - console.log(` Modal options: ${modalOptions.count} cards (${modalOptions.formats.join(', ')})`); - - const test2Passed = modalInitiallyHidden && linkClicked && modalOpen && modalOptions.count === 3; - console.log(` ${test2Passed ? '✅ PASS' : '❌ FAIL'} - Click opens modal`); - testResults.push({ test: 'EN Click Opens Modal', passed: test2Passed }); - - // Close modal for next test - await page.keyboard.press('Escape'); - await page.waitForTimeout(500); - - // ======================================================================== - // TEST 3: Spanish Version - References Section Structure - // ======================================================================== - console.log("\n3️⃣ Testing Spanish References Section..."); + console.log("\n2️⃣ Testing Spanish Direct Shortcut URL..."); await page.goto(`${URL}/?lang=es`); await page.waitForTimeout(1500); @@ -181,19 +126,17 @@ async function testReferencesPDFDownload() { }); await page.waitForTimeout(1000); - const esReferences = await page.evaluate(() => { + const esReferences = await page.evaluate((currentYear) => { const section = document.querySelector('#references'); if (!section) return { found: false }; // Find the reference-item div that contains "Descargar este currículum" const referenceItems = section.querySelectorAll('.reference-item'); let downloadLink = null; - let parentText = ''; for (const item of referenceItems) { if (item.textContent.includes('Descargar este currículum')) { downloadLink = item.querySelector('a'); - parentText = item.textContent.trim(); break; } } @@ -203,209 +146,124 @@ async function testReferencesPDFDownload() { sectionExists: !!section, downloadLinkExists: !!downloadLink, downloadLinkText: downloadLink ? downloadLink.textContent.trim() : null, - fullText: parentText, + href: downloadLink ? downloadLink.getAttribute('href') : null, + target: downloadLink ? downloadLink.getAttribute('target') : null, + rel: downloadLink ? downloadLink.getAttribute('rel') : null, hasOnClick: downloadLink ? downloadLink.hasAttribute('onclick') : false, - onClickContent: downloadLink ? downloadLink.getAttribute('onclick') : null, - href: downloadLink ? downloadLink.getAttribute('href') : null + expectedHref: `/cv-jamr-${currentYear}-es.pdf`, + hrefMatches: downloadLink ? downloadLink.getAttribute('href') === `/cv-jamr-${currentYear}-es.pdf` : false }; - }); + }, CURRENT_YEAR); console.log(` References section: ${esReferences.sectionExists ? '✅' : '❌'}`); console.log(` Download link exists: ${esReferences.downloadLinkExists ? '✅' : '❌'}`); console.log(` Link text: "${esReferences.downloadLinkText}"`); - console.log(` Has onClick handler: ${esReferences.hasOnClick ? '✅' : '❌'}`); - console.log(` OnClick calls openPdfModal: ${esReferences.onClickContent?.includes('openPdfModal') ? '✅' : '❌'}`); console.log(` Href: "${esReferences.href}"`); + console.log(` Expected: "${esReferences.expectedHref}"`); + console.log(` Href matches: ${esReferences.hrefMatches ? '✅' : '❌'}`); + console.log(` Target="_blank": ${esReferences.target === '_blank' ? '✅' : '❌'}`); + console.log(` Rel security attrs: ${esReferences.rel === 'noopener noreferrer' ? '✅' : '❌'}`); + console.log(` No onclick handler: ${!esReferences.hasOnClick ? '✅' : '❌'}`); - const test3Passed = esReferences.sectionExists && + const test2Passed = esReferences.sectionExists && esReferences.downloadLinkExists && - esReferences.hasOnClick && - esReferences.onClickContent?.includes('openPdfModal'); + esReferences.hrefMatches && + esReferences.target === '_blank' && + esReferences.rel === 'noopener noreferrer' && + !esReferences.hasOnClick; - console.log(` ${test3Passed ? '✅ PASS' : '❌ FAIL'} - Spanish references structure`); - testResults.push({ test: 'ES References Structure', passed: test3Passed }); + console.log(` ${test2Passed ? '✅ PASS' : '❌ FAIL'} - Spanish direct shortcut URL`); + testResults.push({ test: 'ES Direct Shortcut URL', passed: test2Passed }); // ======================================================================== - // TEST 4: Click Spanish Download Link - Opens PDF Modal + // TEST 3: Shortcut URL Responds with 301 Redirect // ======================================================================== - console.log("\n4️⃣ Testing Click Opens PDF Modal (Spanish)..."); + console.log("\n3️⃣ Testing Shortcut URL Redirects..."); - // Click the download link - const esLinkClicked = await page.evaluate(() => { - const section = document.querySelector('#references'); - if (!section) return false; - - // Find the reference-item div that contains "Descargar este currículum" - const referenceItems = section.querySelectorAll('.reference-item'); - for (const item of referenceItems) { - if (item.textContent.includes('Descargar este currículum')) { - const downloadLink = item.querySelector('a'); - if (downloadLink) { - downloadLink.click(); - return true; - } - } - } - return false; + // Test English shortcut + const enResponse = await page.request.get(`${URL}/cv-jamr-${CURRENT_YEAR}-en.pdf`, { + maxRedirects: 0, + failOnStatusCode: false }); - console.log(` Link clicked: ${esLinkClicked ? '✅' : '❌'}`); - await page.waitForTimeout(500); + const enStatus = enResponse.status(); + const enLocation = enResponse.headers()['location']; - // Verify modal is now open - const esModalOpen = await page.evaluate(() => { - const modal = document.querySelector('#pdf-modal'); - return modal ? modal.open : false; + console.log(` English shortcut HTTP status: ${enStatus} ${enStatus === 301 ? '✅' : '❌'}`); + console.log(` Redirect location: ${enLocation}`); + console.log(` Points to export/pdf: ${enLocation?.includes('/export/pdf') ? '✅' : '❌'}`); + console.log(` Has lang=en: ${enLocation?.includes('lang=en') ? '✅' : '❌'}`); + console.log(` Has length=short: ${enLocation?.includes('length=short') ? '✅' : '❌'}`); + console.log(` Has version=with_skills: ${enLocation?.includes('version=with_skills') ? '✅' : '❌'}`); + + // Test Spanish shortcut + const esResponse = await page.request.get(`${URL}/cv-jamr-${CURRENT_YEAR}-es.pdf`, { + maxRedirects: 0, + failOnStatusCode: false }); - console.log(` Modal opened: ${esModalOpen ? '✅' : '❌'}`); + const esStatus = esResponse.status(); + const esLocation = esResponse.headers()['location']; - const test4Passed = esLinkClicked && esModalOpen; - console.log(` ${test4Passed ? '✅ PASS' : '❌ FAIL'} - Spanish click opens modal`); - testResults.push({ test: 'ES Click Opens Modal', passed: test4Passed }); + console.log(` Spanish shortcut HTTP status: ${esStatus} ${esStatus === 301 ? '✅' : '❌'}`); + console.log(` Redirect location: ${esLocation}`); + console.log(` Has lang=es: ${esLocation?.includes('lang=es') ? '✅' : '❌'}`); + + const test3Passed = enStatus === 301 && + esStatus === 301 && + enLocation?.includes('/export/pdf') && + enLocation?.includes('lang=en') && + esLocation?.includes('lang=es'); + + console.log(` ${test3Passed ? '✅ PASS' : '❌ FAIL'} - Shortcut URL redirects`); + testResults.push({ test: 'Shortcut URL Redirects', passed: test3Passed }); // ======================================================================== - // TEST 5: Current View Option Reads localStorage Settings + // TEST 4: Invalid Year Returns 404 // ======================================================================== - console.log("\n5️⃣ Testing 'Current View' Reads localStorage..."); + console.log("\n4️⃣ Testing Invalid Year Validation..."); - // Set specific localStorage values - await page.evaluate(() => { - localStorage.setItem('cv-length', 'long'); - localStorage.setItem('cv-icons', 'hide'); - localStorage.setItem('cv-theme', 'clean'); + const invalidYear = CURRENT_YEAR - 1; // Last year + const futureYear = CURRENT_YEAR + 1; // Next year + + const pastResponse = await page.request.get(`${URL}/cv-jamr-${invalidYear}-en.pdf`, { + failOnStatusCode: false }); - console.log(` Set localStorage: length=long, icons=hide, theme=clean`); - - // Select "Current View" option - const currentViewSelected = await page.evaluate(() => { - const currentCard = document.querySelector('#pdf-modal .pdf-option-card[data-cv-format="current"]'); - if (currentCard) { - currentCard.click(); - return true; - } - return false; + const futureResponse = await page.request.get(`${URL}/cv-jamr-${futureYear}-en.pdf`, { + failOnStatusCode: false }); - console.log(` Selected 'Current View' card: ${currentViewSelected ? '✅' : '❌'}`); - await page.waitForTimeout(300); + const pastStatus = pastResponse.status(); + const futureStatus = futureResponse.status(); - // Verify download button is enabled - const downloadBtnEnabled = await page.evaluate(() => { - const btn = document.querySelector('#pdf-modal .pdf-download-btn'); - return btn ? !btn.disabled : false; - }); + console.log(` Past year (${invalidYear}) status: ${pastStatus} ${pastStatus === 404 ? '✅' : '❌'}`); + console.log(` Future year (${futureYear}) status: ${futureStatus} ${futureStatus === 404 ? '✅' : '❌'}`); - console.log(` Download button enabled: ${downloadBtnEnabled ? '✅' : '❌'}`); + const test4Passed = pastStatus === 404 && futureStatus === 404; - // Intercept navigation to capture the PDF URL - let capturedURL = null; - page.on('framenavigated', frame => { - if (frame === page.mainFrame()) { - capturedURL = frame.url(); - } - }); - - // Listen for navigation events - const navigationPromise = page.waitForEvent('framenavigated', { timeout: 3000 }).catch(() => null); - - // Click download button - await page.evaluate(() => { - const btn = document.querySelector('#pdf-modal .pdf-download-btn'); - if (btn) btn.click(); - }); - - console.log(` Clicked download button`); - - // Wait a bit for navigation attempt - await Promise.race([navigationPromise, page.waitForTimeout(1500)]); - - // Check if download was triggered (URL change or download event) - const downloadTriggered = await page.evaluate(() => { - // Check if window.location.href was changed by downloadPDF() - // Note: In test environment, we can't actually download, but we can verify the function ran - return true; // If we got here without errors, download was triggered - }); - - console.log(` Download triggered: ${downloadTriggered ? '✅' : '❌'}`); - - const test5Passed = currentViewSelected && downloadBtnEnabled && downloadTriggered; - console.log(` ${test5Passed ? '✅ PASS' : '❌ FAIL'} - Current view with localStorage`); - testResults.push({ test: 'Current View localStorage', passed: test5Passed }); + console.log(` ${test4Passed ? '✅ PASS' : '❌ FAIL'} - Year validation (404 for invalid years)`); + testResults.push({ test: 'Year Validation', passed: test4Passed }); // ======================================================================== - // TEST 6: Verify downloadPDF() Function Exists and Logic + // TEST 5: Link Works in PDF Export Context (No JavaScript) // ======================================================================== - console.log("\n6️⃣ Testing downloadPDF() Function Logic..."); + console.log("\n5️⃣ Testing Link Works Without JavaScript..."); - // Navigate back to page - await page.goto(`${URL}/?lang=en`); - await page.waitForTimeout(1500); - - // Check if downloadPDF function exists and inspect its logic - const downloadFunctionCheck = await page.evaluate(() => { - if (typeof downloadPDF !== 'function') { - return { exists: false }; - } - - // Convert function to string to inspect its logic - const funcString = downloadPDF.toString(); - - return { - exists: true, - readsLength: funcString.includes("localStorage.getItem('cv-length')"), - readsIcons: funcString.includes("localStorage.getItem('cv-icons')"), - readsTheme: funcString.includes("localStorage.getItem('cv-theme')"), - buildsURL: funcString.includes('/export/pdf'), - hasShortCase: funcString.includes("selectedFormat === 'short'"), - hasLongCase: funcString.includes("selectedFormat === 'long'"), - hasCurrentCase: funcString.includes("selectedFormat === 'current'") - }; + // Navigate with JavaScript disabled + const noJSContext = await browser.newContext({ + javaScriptEnabled: false, + viewport: { width: 1920, height: 1080 } }); + const noJSPage = await noJSContext.newPage(); - console.log(` downloadPDF() exists: ${downloadFunctionCheck.exists ? '✅' : '❌'}`); - console.log(` Reads cv-length: ${downloadFunctionCheck.readsLength ? '✅' : '❌'}`); - console.log(` Reads cv-icons: ${downloadFunctionCheck.readsIcons ? '✅' : '❌'}`); - console.log(` Reads cv-theme: ${downloadFunctionCheck.readsTheme ? '✅' : '❌'}`); - console.log(` Builds /export/pdf URL: ${downloadFunctionCheck.buildsURL ? '✅' : '❌'}`); - console.log(` Handles 'short' format: ${downloadFunctionCheck.hasShortCase ? '✅' : '❌'}`); - console.log(` Handles 'long' format: ${downloadFunctionCheck.hasLongCase ? '✅' : '❌'}`); - console.log(` Handles 'current' format: ${downloadFunctionCheck.hasCurrentCase ? '✅' : '❌'}`); + await noJSPage.goto(`${URL}/?lang=en`); + await noJSPage.waitForTimeout(1500); - const test6Passed = downloadFunctionCheck.exists && - downloadFunctionCheck.readsLength && - downloadFunctionCheck.readsIcons && - downloadFunctionCheck.readsTheme && - downloadFunctionCheck.buildsURL && - downloadFunctionCheck.hasCurrentCase; - - console.log(` ${test6Passed ? '✅ PASS' : '❌ FAIL'} - downloadPDF() function logic`); - testResults.push({ test: 'downloadPDF() Function', passed: test6Passed }); - - // ======================================================================== - // TEST 7: No Direct Download - Always Opens Modal - // ======================================================================== - console.log("\n7️⃣ Testing No Direct Download (Modal Required)..."); - - await page.goto(`${URL}/?lang=en`); - await page.waitForTimeout(1500); - - // Scroll to references - await page.evaluate(() => { - const referencesSection = document.querySelector('#references'); - if (referencesSection) { - referencesSection.scrollIntoView({ behavior: 'smooth', block: 'center' }); - } - }); - await page.waitForTimeout(1000); - - // Verify the link does NOT directly navigate to PDF - const linkBehavior = await page.evaluate(() => { + const linkWorksWithoutJS = await noJSPage.evaluate((currentYear) => { const section = document.querySelector('#references'); if (!section) return { found: false }; - // Find the reference-item div that contains "Download this curriculum" const referenceItems = section.querySelectorAll('.reference-item'); let downloadLink = null; @@ -416,33 +274,27 @@ async function testReferencesPDFDownload() { } } - if (!downloadLink) return { found: false }; - - const href = downloadLink.getAttribute('href'); - const onclick = downloadLink.getAttribute('onclick'); - return { found: true, - hrefIsNotPDF: !href?.includes('.pdf'), - hrefIsHash: href === '#' || href?.startsWith('#'), - onClickPreventsDefault: onclick?.includes('preventDefault'), - onClickOpensModal: onclick?.includes('openPdfModal') + linkExists: !!downloadLink, + href: downloadLink ? downloadLink.getAttribute('href') : null, + isDirectLink: downloadLink ? downloadLink.getAttribute('href') === `/cv-jamr-${currentYear}-en.pdf` : false, + noOnClick: downloadLink ? !downloadLink.hasAttribute('onclick') : false }; - }); + }, CURRENT_YEAR); - console.log(` Link found: ${linkBehavior.found ? '✅' : '❌'}`); - console.log(` Href is NOT direct PDF: ${linkBehavior.hrefIsNotPDF ? '✅' : '❌'}`); - console.log(` Href is hash (#): ${linkBehavior.hrefIsHash ? '✅' : '❌'}`); - console.log(` OnClick prevents default: ${linkBehavior.onClickPreventsDefault ? '✅' : '❌'}`); - console.log(` OnClick opens modal: ${linkBehavior.onClickOpensModal ? '✅' : '❌'}`); + console.log(` Link exists without JS: ${linkWorksWithoutJS.linkExists ? '✅' : '❌'}`); + console.log(` Href is direct PDF link: ${linkWorksWithoutJS.isDirectLink ? '✅' : '❌'}`); + console.log(` No onclick dependency: ${linkWorksWithoutJS.noOnClick ? '✅' : '❌'}`); - const test7Passed = linkBehavior.found && - linkBehavior.hrefIsNotPDF && - linkBehavior.onClickPreventsDefault && - linkBehavior.onClickOpensModal; + const test5Passed = linkWorksWithoutJS.linkExists && + linkWorksWithoutJS.isDirectLink && + linkWorksWithoutJS.noOnClick; - console.log(` ${test7Passed ? '✅ PASS' : '❌ FAIL'} - No direct PDF download`); - testResults.push({ test: 'No Direct Download', passed: test7Passed }); + console.log(` ${test5Passed ? '✅ PASS' : '❌ FAIL'} - Works without JavaScript`); + testResults.push({ test: 'No JavaScript Required', passed: test5Passed }); + + await noJSContext.close(); // ======================================================================== // FINAL RESULTS @@ -480,7 +332,7 @@ async function testReferencesPDFDownload() { process.exit(allPassed ? 0 : 1); } -testReferencesPDFDownload().catch(err => { +testReferencesPDFShortcutLinks().catch(err => { console.error('❌ FATAL ERROR:', err); process.exit(1); });