#!/usr/bin/env bun /** * 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 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 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 testReferencesPDFShortcutLinks() { console.log('📚 REFERENCES SECTION - DIRECT PDF SHORTCUT LINK TEST\n'); console.log('='.repeat(70)); const browser = await chromium.launch({ headless: false }); const context = await browser.newContext({ viewport: { width: 1920, height: 1080 } }); const page = await context.newPage(); const errors = []; const testResults = []; page.on('console', msg => { if (msg.type() === 'error') { errors.push(msg.text()); console.log(`❌ CONSOLE ERROR: ${msg.text()}`); } }); // ======================================================================== // TEST 1: English Version - Direct Shortcut URL // ======================================================================== console.log("\n1️⃣ Testing English Direct Shortcut URL..."); await page.goto(`${URL}/?lang=en`); await page.waitForTimeout(1500); // Scroll to references section await page.evaluate(() => { const referencesSection = document.querySelector('#references'); if (referencesSection) { referencesSection.scrollIntoView({ behavior: 'smooth', block: 'center' }); } }); await page.waitForTimeout(1000); 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; for (const item of referenceItems) { if (item.textContent.includes('Download this curriculum')) { downloadLink = item.querySelector('a'); break; } } return { found: true, sectionExists: !!section, downloadLinkExists: !!downloadLink, downloadLinkText: downloadLink ? downloadLink.textContent.trim() : null, href: downloadLink ? downloadLink.getAttribute('href') : null, target: downloadLink ? downloadLink.getAttribute('target') : null, rel: downloadLink ? downloadLink.getAttribute('rel') : null, hasOnClick: downloadLink ? downloadLink.hasAttribute('onclick') : false, 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(` 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.hrefMatches && enReferences.target === '_blank' && enReferences.rel === 'noopener noreferrer' && !enReferences.hasOnClick; console.log(` ${test1Passed ? '✅ PASS' : '❌ FAIL'} - English direct shortcut URL`); testResults.push({ test: 'EN Direct Shortcut URL', passed: test1Passed }); // ======================================================================== // TEST 2: Spanish Version - Direct Shortcut URL // ======================================================================== console.log("\n2️⃣ Testing Spanish Direct Shortcut URL..."); await page.goto(`${URL}/?lang=es`); await page.waitForTimeout(1500); // Scroll to references section await page.evaluate(() => { const referencesSection = document.querySelector('#references'); if (referencesSection) { referencesSection.scrollIntoView({ behavior: 'smooth', block: 'center' }); } }); await page.waitForTimeout(1000); 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; for (const item of referenceItems) { if (item.textContent.includes('Descargar este currículum')) { downloadLink = item.querySelector('a'); break; } } return { found: true, sectionExists: !!section, downloadLinkExists: !!downloadLink, downloadLinkText: downloadLink ? downloadLink.textContent.trim() : null, href: downloadLink ? downloadLink.getAttribute('href') : null, target: downloadLink ? downloadLink.getAttribute('target') : null, rel: downloadLink ? downloadLink.getAttribute('rel') : null, hasOnClick: downloadLink ? downloadLink.hasAttribute('onclick') : false, 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(` 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 test2Passed = esReferences.sectionExists && esReferences.downloadLinkExists && esReferences.hrefMatches && esReferences.target === '_blank' && esReferences.rel === 'noopener noreferrer' && !esReferences.hasOnClick; console.log(` ${test2Passed ? '✅ PASS' : '❌ FAIL'} - Spanish direct shortcut URL`); testResults.push({ test: 'ES Direct Shortcut URL', passed: test2Passed }); // ======================================================================== // TEST 3: Shortcut URL Returns PDF with Correct Filename // ======================================================================== console.log("\n3️⃣ Testing Shortcut URL Direct PDF Download..."); // Test English shortcut const enResponse = await page.request.get(`${URL}/cv-jamr-${CURRENT_YEAR}-en.pdf`, { maxRedirects: 0, failOnStatusCode: false }); const enStatus = enResponse.status(); const enContentDisposition = enResponse.headers()['content-disposition']; const enContentType = enResponse.headers()['content-type']; const enFilename = enContentDisposition?.match(/filename=([^;]+)/)?.[1]; console.log(` English shortcut HTTP status: ${enStatus} ${enStatus === 200 ? '✅' : '❌'}`); console.log(` Content-Type: ${enContentType} ${enContentType === 'application/pdf' ? '✅' : '❌'}`); console.log(` Content-Disposition: ${enContentDisposition}`); console.log(` Filename: ${enFilename} ${enFilename === 'cv-jamr-2025-en.pdf' ? '✅' : '❌'}`); // Test Spanish shortcut const esResponse = await page.request.get(`${URL}/cv-jamr-${CURRENT_YEAR}-es.pdf`, { maxRedirects: 0, failOnStatusCode: false }); const esStatus = esResponse.status(); const esContentDisposition = esResponse.headers()['content-disposition']; const esContentType = esResponse.headers()['content-type']; const esFilename = esContentDisposition?.match(/filename=([^;]+)/)?.[1]; console.log(` Spanish shortcut HTTP status: ${esStatus} ${esStatus === 200 ? '✅' : '❌'}`); console.log(` Content-Type: ${esContentType} ${esContentType === 'application/pdf' ? '✅' : '❌'}`); console.log(` Content-Disposition: ${esContentDisposition}`); console.log(` Filename: ${esFilename} ${esFilename === 'cv-jamr-2025-es.pdf' ? '✅' : '❌'}`); const test3Passed = enStatus === 200 && esStatus === 200 && enContentType === 'application/pdf' && esContentType === 'application/pdf' && enFilename === 'cv-jamr-2025-en.pdf' && esFilename === 'cv-jamr-2025-es.pdf'; console.log(` ${test3Passed ? '✅ PASS' : '❌ FAIL'} - Shortcut URL direct download with correct filename`); testResults.push({ test: 'Shortcut URL Direct Download', passed: test3Passed }); // ======================================================================== // TEST 4: Invalid Year Returns 404 // ======================================================================== console.log("\n4️⃣ Testing Invalid Year Validation..."); 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 }); const futureResponse = await page.request.get(`${URL}/cv-jamr-${futureYear}-en.pdf`, { failOnStatusCode: false }); const pastStatus = pastResponse.status(); const futureStatus = futureResponse.status(); console.log(` Past year (${invalidYear}) status: ${pastStatus} ${pastStatus === 404 ? '✅' : '❌'}`); console.log(` Future year (${futureYear}) status: ${futureStatus} ${futureStatus === 404 ? '✅' : '❌'}`); const test4Passed = pastStatus === 404 && futureStatus === 404; console.log(` ${test4Passed ? '✅ PASS' : '❌ FAIL'} - Year validation (404 for invalid years)`); testResults.push({ test: 'Year Validation', passed: test4Passed }); // ======================================================================== // TEST 5: Link Works in PDF Export Context (No JavaScript) // ======================================================================== console.log("\n5️⃣ Testing Link Works Without JavaScript..."); // Navigate with JavaScript disabled const noJSContext = await browser.newContext({ javaScriptEnabled: false, viewport: { width: 1920, height: 1080 } }); const noJSPage = await noJSContext.newPage(); await noJSPage.goto(`${URL}/?lang=en`); await noJSPage.waitForTimeout(1500); const linkWorksWithoutJS = await noJSPage.evaluate((currentYear) => { const section = document.querySelector('#references'); if (!section) return { found: false }; const referenceItems = section.querySelectorAll('.reference-item'); let downloadLink = null; for (const item of referenceItems) { if (item.textContent.includes('Download this curriculum')) { downloadLink = item.querySelector('a'); break; } } return { found: true, 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 exists without JS: ${linkWorksWithoutJS.linkExists ? '✅' : '❌'}`); console.log(` Href is direct PDF link: ${linkWorksWithoutJS.isDirectLink ? '✅' : '❌'}`); console.log(` No onclick dependency: ${linkWorksWithoutJS.noOnClick ? '✅' : '❌'}`); const test5Passed = linkWorksWithoutJS.linkExists && linkWorksWithoutJS.isDirectLink && linkWorksWithoutJS.noOnClick; console.log(` ${test5Passed ? '✅ PASS' : '❌ FAIL'} - Works without JavaScript`); testResults.push({ test: 'No JavaScript Required', passed: test5Passed }); await noJSContext.close(); // ======================================================================== // TEST 6: "See this CV in..." Language Switching Links // ======================================================================== console.log("\n6️⃣ Testing Language Switching Links..."); // Test English page has Spanish link await page.goto(`${URL}/?lang=en`); await page.waitForTimeout(1000); const enLanguageLink = await page.evaluate(() => { const section = document.querySelector('#references'); if (!section) return { found: false }; const referenceItems = section.querySelectorAll('.reference-item'); let spanishLink = null; for (const item of referenceItems) { if (item.textContent.includes('Curriculum Vitae in PDF in')) { spanishLink = item.querySelector('a'); break; } } return { found: true, linkExists: !!spanishLink, linkText: spanishLink ? spanishLink.textContent.trim() : null, href: spanishLink ? spanishLink.getAttribute('href') : null, expectedHref: '/?lang=es', hrefMatches: spanishLink ? spanishLink.getAttribute('href') === '/?lang=es' : false, target: spanishLink ? spanishLink.getAttribute('target') : null, noCorruption: spanishLink ? !spanishLink.getAttribute('href').includes('EXTRA') : false }; }); console.log(` English page - Spanish link exists: ${enLanguageLink.linkExists ? '✅' : '❌'}`); console.log(` Link text: "${enLanguageLink.linkText}"`); console.log(` Href: "${enLanguageLink.href}"`); console.log(` Expected: "${enLanguageLink.expectedHref}"`); console.log(` Href matches: ${enLanguageLink.hrefMatches ? '✅' : '❌'}`); console.log(` No URL corruption: ${enLanguageLink.noCorruption ? '✅' : '❌'}`); console.log(` Target="_blank": ${enLanguageLink.target === '_blank' ? '✅' : '❌'}`); // Test Spanish page has English link await page.goto(`${URL}/?lang=es`); await page.waitForTimeout(1000); const esLanguageLink = await page.evaluate(() => { const section = document.querySelector('#references'); if (!section) return { found: false }; const referenceItems = section.querySelectorAll('.reference-item'); let englishLink = null; for (const item of referenceItems) { if (item.textContent.includes('Currículum Vitae en PDF en')) { englishLink = item.querySelector('a'); break; } } return { found: true, linkExists: !!englishLink, linkText: englishLink ? englishLink.textContent.trim() : null, href: englishLink ? englishLink.getAttribute('href') : null, expectedHref: '/?lang=en', hrefMatches: englishLink ? englishLink.getAttribute('href') === '/?lang=en' : false, target: englishLink ? englishLink.getAttribute('target') : null, noCorruption: englishLink ? !englishLink.getAttribute('href').includes('EXTRA') : false }; }); console.log(` Spanish page - English link exists: ${esLanguageLink.linkExists ? '✅' : '❌'}`); console.log(` Link text: "${esLanguageLink.linkText}"`); console.log(` Href: "${esLanguageLink.href}"`); console.log(` Expected: "${esLanguageLink.expectedHref}"`); console.log(` Href matches: ${esLanguageLink.hrefMatches ? '✅' : '❌'}`); console.log(` No URL corruption: ${esLanguageLink.noCorruption ? '✅' : '❌'}`); console.log(` Target="_blank": ${esLanguageLink.target === '_blank' ? '✅' : '❌'}`); const test6Passed = enLanguageLink.linkExists && enLanguageLink.hrefMatches && enLanguageLink.noCorruption && esLanguageLink.linkExists && esLanguageLink.hrefMatches && esLanguageLink.noCorruption; console.log(` ${test6Passed ? '✅ PASS' : '❌ FAIL'} - Language switching links work correctly`); testResults.push({ test: 'Language Switching Links', passed: test6Passed }); // ======================================================================== // FINAL RESULTS // ======================================================================== console.log('\n' + '='.repeat(70)); console.log('📊 TEST SUMMARY\n'); const totalTests = testResults.length; const passedTests = testResults.filter(r => r.passed).length; const failedTests = totalTests - passedTests; testResults.forEach((result, index) => { const status = result.passed ? '✅ PASS' : '❌ FAIL'; console.log(`${index + 1}. ${status} - ${result.test}`); }); console.log('\n' + '='.repeat(70)); console.log(`Total Tests: ${totalTests}`); console.log(`✅ Passed: ${passedTests}`); console.log(`❌ Failed: ${failedTests}`); if (errors.length > 0) { console.log(`\n⚠️ Console Errors Detected: ${errors.length}`); errors.forEach(err => console.log(` - ${err}`)); } const allPassed = passedTests === totalTests && errors.length === 0; console.log('\n' + '='.repeat(70)); console.log(allPassed ? '🎉 ALL TESTS PASSED!' : '💥 SOME TESTS FAILED'); console.log('='.repeat(70)); await browser.close(); // Exit with appropriate code process.exit(allPassed ? 0 : 1); } testReferencesPDFShortcutLinks().catch(err => { console.error('❌ FATAL ERROR:', err); process.exit(1); });