docs-v2/cypress/support/link-reporter.js

216 lines
6.2 KiB
JavaScript
Raw Blame History

This file contains invisible Unicode characters!

This file contains invisible Unicode characters that may be processed differently from what appears below. If your use case is intentional and legitimate, you can safely ignore this warning. Use the Escape button to reveal hidden characters.

/**
* Broken Links Reporter
* Handles collecting, storing, and reporting broken links found during tests
*/
import fs from 'fs';
export const BROKEN_LINKS_FILE = '/tmp/broken_links_report.json';
export const FIRST_BROKEN_LINK_FILE = '/tmp/first_broken_link.json';
const SOURCES_FILE = '/tmp/test_subjects_sources.json';
/**
* Reads the broken links report from the file system
* @returns {Array} Parsed report data or empty array if file doesn't exist
*/
export function readBrokenLinksReport() {
if (!fs.existsSync(BROKEN_LINKS_FILE)) {
return [];
}
try {
const fileContent = fs.readFileSync(BROKEN_LINKS_FILE, 'utf8');
// Check if the file is empty or contains only an empty array
if (!fileContent || fileContent.trim() === '' || fileContent === '[]') {
return [];
}
// Try to parse the JSON content
try {
const parsedContent = JSON.parse(fileContent);
// Ensure the parsed content is an array
if (!Array.isArray(parsedContent)) {
console.error('Broken links report is not an array');
return [];
}
return parsedContent;
} catch (parseErr) {
console.error(
`Error parsing broken links report JSON: ${parseErr.message}`
);
return [];
}
} catch (err) {
console.error(`Error reading broken links report: ${err.message}`);
return [];
}
}
/**
* Reads the sources mapping file
* @returns {Object} A mapping from URLs to their source files
*/
function readSourcesMapping() {
try {
if (fs.existsSync(SOURCES_FILE)) {
const sourcesData = JSON.parse(fs.readFileSync(SOURCES_FILE, 'utf8'));
return sourcesData.reduce((acc, item) => {
if (item.url && item.source) {
acc[item.url] = item.source;
}
return acc;
}, {});
}
} catch (err) {
console.warn(`Warning: Could not read sources mapping: ${err.message}`);
}
return {};
}
/**
* Formats and displays the broken links report to the console
* @param {Array} brokenLinksReport - The report data to display
* @returns {number} The total number of broken links found
*/
export function displayBrokenLinksReport(brokenLinksReport = null) {
// If no report provided, read from file
if (!brokenLinksReport) {
brokenLinksReport = readBrokenLinksReport();
}
// Check both the report and first broken link file to determine if we have broken links
const firstBrokenLink = readFirstBrokenLink();
// Only report "no broken links" if both checks pass
if (
(!brokenLinksReport || brokenLinksReport.length === 0) &&
!firstBrokenLink
) {
console.log('✅ No broken links detected in the validation report');
return 0;
}
// Special case: check if the single broken link file could be missing from the report
if (
firstBrokenLink &&
(!brokenLinksReport || brokenLinksReport.length === 0)
) {
console.error(
'\n⚠ Warning: First broken link record exists but no links in the report.'
);
console.error('This could indicate a reporting issue.');
}
// Load sources mapping
const sourcesMapping = readSourcesMapping();
// Print a prominent header
console.error('\n\n' + '='.repeat(80));
console.error(' 🚨 BROKEN LINKS DETECTED 🚨 ');
console.error('='.repeat(80));
// Show first failing link if available
if (firstBrokenLink) {
console.error('\n🔴 FIRST FAILING LINK:');
console.error(` URL: ${firstBrokenLink.url}`);
console.error(` Status: ${firstBrokenLink.status}`);
console.error(` Type: ${firstBrokenLink.type}`);
console.error(` Page: ${firstBrokenLink.page}`);
if (firstBrokenLink.linkText) {
console.error(
` Link text: "${firstBrokenLink.linkText.substring(0, 50)}${firstBrokenLink.linkText.length > 50 ? '...' : ''}"`
);
}
console.error('-'.repeat(40));
}
let totalBrokenLinks = 0;
brokenLinksReport.forEach((report) => {
console.error(`\n📄 PAGE: ${report.page}`);
// Add source information if available
const source = sourcesMapping[report.page];
if (source) {
console.error(` PAGE CONTENT SOURCE: ${source}`);
}
console.error('-'.repeat(40));
report.links.forEach((link) => {
console.error(`${link.url}`);
console.error(` - Status: ${link.status}`);
console.error(` - Type: ${link.type}`);
if (link.linkText) {
console.error(
` - Link text: "${link.linkText.substring(0, 50)}${link.linkText.length > 50 ? '...' : ''}"`
);
}
console.error('');
totalBrokenLinks++;
});
});
// Print a prominent summary footer
console.error('='.repeat(80));
console.error(`📊 TOTAL BROKEN LINKS FOUND: ${totalBrokenLinks}`);
console.error('='.repeat(80) + '\n');
return totalBrokenLinks;
}
/**
* Reads the first broken link info from the file system
* @returns {Object|null} First broken link data or null if not found
*/
export function readFirstBrokenLink() {
if (!fs.existsSync(FIRST_BROKEN_LINK_FILE)) {
return null;
}
try {
const fileContent = fs.readFileSync(FIRST_BROKEN_LINK_FILE, 'utf8');
// Check if the file is empty or contains whitespace only
if (!fileContent || fileContent.trim() === '') {
return null;
}
// Try to parse the JSON content
try {
return JSON.parse(fileContent);
} catch (parseErr) {
console.error(
`Error parsing first broken link JSON: ${parseErr.message}`
);
return null;
}
} catch (err) {
console.error(`Error reading first broken link: ${err.message}`);
return null;
}
}
/**
* Initialize the broken links report files
* @returns {boolean} True if initialization was successful
*/
export function initializeReport() {
try {
// Create an empty array for the broken links report
fs.writeFileSync(BROKEN_LINKS_FILE, '[]', 'utf8');
// Reset the first broken link file by creating an empty file
// Using empty string as a clear indicator that no broken link has been recorded yet
fs.writeFileSync(FIRST_BROKEN_LINK_FILE, '', 'utf8');
console.debug('🔄 Initialized broken links reporting system');
return true;
} catch (err) {
console.error(`Error initializing broken links report: ${err.message}`);
return false;
}
}