Files
cv-site/tests/mjs/28-references-pdf-download.test.mjs
T
juanatsap fb1c781a20 tests: Update references section tests for new behavior
**Test 3 Updated**: Shortcut URL behavior changed
- OLD: Expected HTTP 301 redirect to /export/pdf
- NEW: Expects HTTP 200 with direct PDF and Content-Disposition header
- Verifies correct filename: cv-jamr-2025-{lang}.pdf

**Test 6 Added**: Language switching links
- Tests "See this CV in Spanish/English" links
- Verifies URL correctness (/?lang=es and /?lang=en)
- Ensures no URL corruption from replaceYearPlaceholder fix
- Checks target="_blank" attribute
- Tests both English → Spanish and Spanish → English links

All 6 tests passing:
 EN Direct Shortcut URL
 ES Direct Shortcut URL
 Shortcut URL Direct Download (updated)
 Year Validation
 No JavaScript Required
 Language Switching Links (new)
2025-11-20 13:02:13 +00:00

434 lines
18 KiB
JavaScript
Executable File

#!/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);
});