From 16194328b61ffd82799cea852fdfc3244527dd58 Mon Sep 17 00:00:00 2001 From: juanatsap Date: Thu, 20 Nov 2025 12:43:29 +0000 Subject: [PATCH] docs+tests: Document and test references section shortcut URLs MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ## Documentation Updates (doc/11-PDF-EXPORT.md) Added "References Section Integration" section with: - Template code examples for shortcut URL usage - Data configuration structure - Key features and use cases - Security attributes documentation - Test command reference ## Test Updates (tests/mjs/28-references-pdf-download.test.mjs) Completely rewrote test to match new direct link behavior: - ❌ OLD: Tested modal opening, onclick handlers - ✅ NEW: Tests direct shortcut URLs New test coverage (5 tests): 1. English direct shortcut URL validation 2. Spanish direct shortcut URL validation 3. HTTP 301 redirect verification 4. Year validation (404 for invalid years) 5. Works without JavaScript (PDF export context) Verifies: - Correct href: /cv-jamr-{year}-{lang}.pdf - No onclick handlers (pure HTML link) - Security attributes (target="_blank", rel="noopener noreferrer") - Year-aware dynamic URLs - Language-aware (es/en) - Backend 301 redirects working - 404 for past/future years ## Benefits - Comprehensive test coverage for new behavior - Complete documentation with examples - Easy to maintain and understand - Validates entire shortcut URL feature end-to-end --- doc/11-PDF-EXPORT.md | 41 ++ tests/mjs/28-references-pdf-download.test.mjs | 410 ++++++------------ 2 files changed, 172 insertions(+), 279 deletions(-) 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); });