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) # Test invalid year (should fail)
curl -I http://localhost:1999/cv-jamr-2024-en.pdf curl -I http://localhost:1999/cv-jamr-2024-en.pdf
# Expected: HTTP/1.1 404 Not Found # 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 ## Design Philosophy
### Why This Naming Convention? ### Why This Naming Convention?
+131 -279
View File
@@ -1,30 +1,32 @@
#!/usr/bin/env bun #!/usr/bin/env bun
/** /**
* REFERENCES SECTION - PDF DOWNLOAD INTEGRATION TEST * REFERENCES SECTION - DIRECT PDF SHORTCUT LINK TEST
* =================================================== * ====================================================
* Tests the dynamic PDF download links in the References section * Tests the direct PDF download shortcut links in the References section
* *
* Coverage: * Coverage:
* - References section renders with "Download this curriculum" links * - References section renders with "Download this curriculum" links
* - Links have action="downloadPDF" attribute * - Links point directly to shortcut URLs (no modal)
* - Clicking the link opens the PDF modal (not direct download) * - English CV: /cv-jamr-{year}-en.pdf
* - Modal "Current View" option reads localStorage settings * - Spanish CV: /cv-jamr-{year}-es.pdf
* - Dynamic URL generation based on current view (length, icons, theme) * - Year is dynamic (current year)
* - Both English and Spanish variants work correctly * - Links have target="_blank" and proper security attributes
* - No onclick handlers (direct navigation)
* - Works in non-browser environments (PDF exports, emails, etc.)
* *
* Integration: * Integration:
* - Data: cv-en.json and cv-es.json with action: "downloadPDF" * - Data: cv-en.json and cv-es.json with action: "downloadPDF"
* - Template: references.html with {{if eq .Action "downloadPDF"}} logic * - Template: references.html with shortcut URL logic
* - Modal: pdf-modal.html with downloadPDF() function * - Backend: DefaultCVShortcut handler for year validation
* - Backend: ExportPDF handler with parameter validation
*/ */
import { chromium } from 'playwright'; import { chromium } from 'playwright';
const URL = "http://localhost:1999"; const URL = "http://localhost:1999";
const CURRENT_YEAR = new Date().getFullYear();
async function testReferencesPDFDownload() { async function testReferencesPDFShortcutLinks() {
console.log('📚 REFERENCES SECTION - PDF DOWNLOAD INTEGRATION TEST\n'); console.log('📚 REFERENCES SECTION - DIRECT PDF SHORTCUT LINK TEST\n');
console.log('='.repeat(70)); console.log('='.repeat(70));
const browser = await chromium.launch({ headless: false }); 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.goto(`${URL}/?lang=en`);
await page.waitForTimeout(1500); await page.waitForTimeout(1500);
@@ -58,19 +60,17 @@ async function testReferencesPDFDownload() {
}); });
await page.waitForTimeout(1000); await page.waitForTimeout(1000);
const enReferences = await page.evaluate(() => { const enReferences = await page.evaluate((currentYear) => {
const section = document.querySelector('#references'); const section = document.querySelector('#references');
if (!section) return { found: false }; if (!section) return { found: false };
// Find the reference-item div that contains "Download this curriculum" // Find the reference-item div that contains "Download this curriculum"
const referenceItems = section.querySelectorAll('.reference-item'); const referenceItems = section.querySelectorAll('.reference-item');
let downloadLink = null; let downloadLink = null;
let parentText = '';
for (const item of referenceItems) { for (const item of referenceItems) {
if (item.textContent.includes('Download this curriculum')) { if (item.textContent.includes('Download this curriculum')) {
downloadLink = item.querySelector('a'); downloadLink = item.querySelector('a');
parentText = item.textContent.trim();
break; break;
} }
} }
@@ -80,94 +80,39 @@ async function testReferencesPDFDownload() {
sectionExists: !!section, sectionExists: !!section,
downloadLinkExists: !!downloadLink, downloadLinkExists: !!downloadLink,
downloadLinkText: downloadLink ? downloadLink.textContent.trim() : null, 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, hasOnClick: downloadLink ? downloadLink.hasAttribute('onclick') : false,
onClickContent: downloadLink ? downloadLink.getAttribute('onclick') : null, expectedHref: `/cv-jamr-${currentYear}-en.pdf`,
href: downloadLink ? downloadLink.getAttribute('href') : null hrefMatches: downloadLink ? downloadLink.getAttribute('href') === `/cv-jamr-${currentYear}-en.pdf` : false
}; };
}); }, CURRENT_YEAR);
console.log(` References section: ${enReferences.sectionExists ? '✅' : '❌'}`); console.log(` References section: ${enReferences.sectionExists ? '✅' : '❌'}`);
console.log(` Download link exists: ${enReferences.downloadLinkExists ? '✅' : '❌'}`); console.log(` Download link exists: ${enReferences.downloadLinkExists ? '✅' : '❌'}`);
console.log(` Link text: "${enReferences.downloadLinkText}"`); 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(` 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 && const test1Passed = enReferences.sectionExists &&
enReferences.downloadLinkExists && enReferences.downloadLinkExists &&
enReferences.hasOnClick && enReferences.hrefMatches &&
enReferences.onClickContent?.includes('openPdfModal'); enReferences.target === '_blank' &&
enReferences.rel === 'noopener noreferrer' &&
!enReferences.hasOnClick;
console.log(` ${test1Passed ? '✅ PASS' : '❌ FAIL'} - English references structure`); console.log(` ${test1Passed ? '✅ PASS' : '❌ FAIL'} - English direct shortcut URL`);
testResults.push({ test: 'EN References Structure', passed: test1Passed }); 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)..."); console.log("\n2️⃣ Testing Spanish Direct Shortcut URL...");
// 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...");
await page.goto(`${URL}/?lang=es`); await page.goto(`${URL}/?lang=es`);
await page.waitForTimeout(1500); await page.waitForTimeout(1500);
@@ -181,19 +126,17 @@ async function testReferencesPDFDownload() {
}); });
await page.waitForTimeout(1000); await page.waitForTimeout(1000);
const esReferences = await page.evaluate(() => { const esReferences = await page.evaluate((currentYear) => {
const section = document.querySelector('#references'); const section = document.querySelector('#references');
if (!section) return { found: false }; if (!section) return { found: false };
// Find the reference-item div that contains "Descargar este currículum" // Find the reference-item div that contains "Descargar este currículum"
const referenceItems = section.querySelectorAll('.reference-item'); const referenceItems = section.querySelectorAll('.reference-item');
let downloadLink = null; let downloadLink = null;
let parentText = '';
for (const item of referenceItems) { for (const item of referenceItems) {
if (item.textContent.includes('Descargar este currículum')) { if (item.textContent.includes('Descargar este currículum')) {
downloadLink = item.querySelector('a'); downloadLink = item.querySelector('a');
parentText = item.textContent.trim();
break; break;
} }
} }
@@ -203,209 +146,124 @@ async function testReferencesPDFDownload() {
sectionExists: !!section, sectionExists: !!section,
downloadLinkExists: !!downloadLink, downloadLinkExists: !!downloadLink,
downloadLinkText: downloadLink ? downloadLink.textContent.trim() : null, 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, hasOnClick: downloadLink ? downloadLink.hasAttribute('onclick') : false,
onClickContent: downloadLink ? downloadLink.getAttribute('onclick') : null, expectedHref: `/cv-jamr-${currentYear}-es.pdf`,
href: downloadLink ? downloadLink.getAttribute('href') : null hrefMatches: downloadLink ? downloadLink.getAttribute('href') === `/cv-jamr-${currentYear}-es.pdf` : false
}; };
}); }, CURRENT_YEAR);
console.log(` References section: ${esReferences.sectionExists ? '✅' : '❌'}`); console.log(` References section: ${esReferences.sectionExists ? '✅' : '❌'}`);
console.log(` Download link exists: ${esReferences.downloadLinkExists ? '✅' : '❌'}`); console.log(` Download link exists: ${esReferences.downloadLinkExists ? '✅' : '❌'}`);
console.log(` Link text: "${esReferences.downloadLinkText}"`); 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(` 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.downloadLinkExists &&
esReferences.hasOnClick && esReferences.hrefMatches &&
esReferences.onClickContent?.includes('openPdfModal'); esReferences.target === '_blank' &&
esReferences.rel === 'noopener noreferrer' &&
!esReferences.hasOnClick;
console.log(` ${test3Passed ? '✅ PASS' : '❌ FAIL'} - Spanish references structure`); console.log(` ${test2Passed ? '✅ PASS' : '❌ FAIL'} - Spanish direct shortcut URL`);
testResults.push({ test: 'ES References Structure', passed: test3Passed }); 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 // Test English shortcut
const esLinkClicked = await page.evaluate(() => { const enResponse = await page.request.get(`${URL}/cv-jamr-${CURRENT_YEAR}-en.pdf`, {
const section = document.querySelector('#references'); maxRedirects: 0,
if (!section) return false; failOnStatusCode: 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;
}); });
console.log(` Link clicked: ${esLinkClicked ? '✅' : '❌'}`); const enStatus = enResponse.status();
await page.waitForTimeout(500); const enLocation = enResponse.headers()['location'];
// Verify modal is now open console.log(` English shortcut HTTP status: ${enStatus} ${enStatus === 301 ? '✅' : '❌'}`);
const esModalOpen = await page.evaluate(() => { console.log(` Redirect location: ${enLocation}`);
const modal = document.querySelector('#pdf-modal'); console.log(` Points to export/pdf: ${enLocation?.includes('/export/pdf') ? '✅' : '❌'}`);
return modal ? modal.open : false; 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(` Spanish shortcut HTTP status: ${esStatus} ${esStatus === 301 ? '✅' : '❌'}`);
console.log(` ${test4Passed ? '✅ PASS' : '❌ FAIL'} - Spanish click opens modal`); console.log(` Redirect location: ${esLocation}`);
testResults.push({ test: 'ES Click Opens Modal', passed: test4Passed }); 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 const invalidYear = CURRENT_YEAR - 1; // Last year
await page.evaluate(() => { const futureYear = CURRENT_YEAR + 1; // Next year
localStorage.setItem('cv-length', 'long');
localStorage.setItem('cv-icons', 'hide'); const pastResponse = await page.request.get(`${URL}/cv-jamr-${invalidYear}-en.pdf`, {
localStorage.setItem('cv-theme', 'clean'); failOnStatusCode: false
}); });
console.log(` Set localStorage: length=long, icons=hide, theme=clean`); const futureResponse = await page.request.get(`${URL}/cv-jamr-${futureYear}-en.pdf`, {
failOnStatusCode: false
// 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;
}); });
console.log(` Selected 'Current View' card: ${currentViewSelected ? '✅' : '❌'}`); const pastStatus = pastResponse.status();
await page.waitForTimeout(300); const futureStatus = futureResponse.status();
// Verify download button is enabled console.log(` Past year (${invalidYear}) status: ${pastStatus} ${pastStatus === 404 ? '✅' : '❌'}`);
const downloadBtnEnabled = await page.evaluate(() => { console.log(` Future year (${futureYear}) status: ${futureStatus} ${futureStatus === 404 ? '✅' : '❌'}`);
const btn = document.querySelector('#pdf-modal .pdf-download-btn');
return btn ? !btn.disabled : false;
});
console.log(` Download button enabled: ${downloadBtnEnabled ? '✅' : '❌'}`); const test4Passed = pastStatus === 404 && futureStatus === 404;
// Intercept navigation to capture the PDF URL console.log(` ${test4Passed ? '✅ PASS' : '❌ FAIL'} - Year validation (404 for invalid years)`);
let capturedURL = null; testResults.push({ test: 'Year Validation', passed: test4Passed });
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 });
// ======================================================================== // ========================================================================
// 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 // Navigate with JavaScript disabled
await page.goto(`${URL}/?lang=en`); const noJSContext = await browser.newContext({
await page.waitForTimeout(1500); javaScriptEnabled: false,
viewport: { width: 1920, height: 1080 }
// 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'")
};
}); });
const noJSPage = await noJSContext.newPage();
console.log(` downloadPDF() exists: ${downloadFunctionCheck.exists ? '✅' : '❌'}`); await noJSPage.goto(`${URL}/?lang=en`);
console.log(` Reads cv-length: ${downloadFunctionCheck.readsLength ? '✅' : '❌'}`); await noJSPage.waitForTimeout(1500);
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 ? '✅' : '❌'}`);
const test6Passed = downloadFunctionCheck.exists && const linkWorksWithoutJS = await noJSPage.evaluate((currentYear) => {
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 section = document.querySelector('#references'); const section = document.querySelector('#references');
if (!section) return { found: false }; if (!section) return { found: false };
// Find the reference-item div that contains "Download this curriculum"
const referenceItems = section.querySelectorAll('.reference-item'); const referenceItems = section.querySelectorAll('.reference-item');
let downloadLink = null; 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 { return {
found: true, found: true,
hrefIsNotPDF: !href?.includes('.pdf'), linkExists: !!downloadLink,
hrefIsHash: href === '#' || href?.startsWith('#'), href: downloadLink ? downloadLink.getAttribute('href') : null,
onClickPreventsDefault: onclick?.includes('preventDefault'), isDirectLink: downloadLink ? downloadLink.getAttribute('href') === `/cv-jamr-${currentYear}-en.pdf` : false,
onClickOpensModal: onclick?.includes('openPdfModal') noOnClick: downloadLink ? !downloadLink.hasAttribute('onclick') : false
}; };
}); }, CURRENT_YEAR);
console.log(` Link found: ${linkBehavior.found ? '✅' : '❌'}`); console.log(` Link exists without JS: ${linkWorksWithoutJS.linkExists ? '✅' : '❌'}`);
console.log(` Href is NOT direct PDF: ${linkBehavior.hrefIsNotPDF ? '✅' : '❌'}`); console.log(` Href is direct PDF link: ${linkWorksWithoutJS.isDirectLink ? '✅' : '❌'}`);
console.log(` Href is hash (#): ${linkBehavior.hrefIsHash ? '✅' : '❌'}`); console.log(` No onclick dependency: ${linkWorksWithoutJS.noOnClick ? '✅' : '❌'}`);
console.log(` OnClick prevents default: ${linkBehavior.onClickPreventsDefault ? '✅' : '❌'}`);
console.log(` OnClick opens modal: ${linkBehavior.onClickOpensModal ? '✅' : '❌'}`);
const test7Passed = linkBehavior.found && const test5Passed = linkWorksWithoutJS.linkExists &&
linkBehavior.hrefIsNotPDF && linkWorksWithoutJS.isDirectLink &&
linkBehavior.onClickPreventsDefault && linkWorksWithoutJS.noOnClick;
linkBehavior.onClickOpensModal;
console.log(` ${test7Passed ? '✅ PASS' : '❌ FAIL'} - No direct PDF download`); console.log(` ${test5Passed ? '✅ PASS' : '❌ FAIL'} - Works without JavaScript`);
testResults.push({ test: 'No Direct Download', passed: test7Passed }); testResults.push({ test: 'No JavaScript Required', passed: test5Passed });
await noJSContext.close();
// ======================================================================== // ========================================================================
// FINAL RESULTS // FINAL RESULTS
@@ -480,7 +332,7 @@ async function testReferencesPDFDownload() {
process.exit(allPassed ? 0 : 1); process.exit(allPassed ? 0 : 1);
} }
testReferencesPDFDownload().catch(err => { testReferencesPDFShortcutLinks().catch(err => {
console.error('❌ FATAL ERROR:', err); console.error('❌ FATAL ERROR:', err);
process.exit(1); process.exit(1);
}); });