docs+tests: Document and test references section shortcut URLs

## 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
This commit is contained in:
juanatsap
2025-11-20 12:43:29 +00:00
parent f25f2de8b3
commit 16194328b6
2 changed files with 172 additions and 279 deletions
+41
View File
@@ -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"}}
<a href="/cv-jamr-{{$.CurrentYear}}-{{$.Lang}}.pdf"
target="_blank"
rel="noopener noreferrer">
<strong>{{.LinkText}}</strong>
</a>
{{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?
+131 -279
View File
@@ -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);
});