129 lines
4.3 KiB
JavaScript
129 lines
4.3 KiB
JavaScript
const fs = require('fs');
|
|
const path = require('path');
|
|
|
|
const distDir = path.resolve(__dirname, 'dist');
|
|
|
|
console.log('--------------------------------------------------');
|
|
console.log('🤖 Running Automated QA Release Check...');
|
|
console.log('--------------------------------------------------');
|
|
|
|
// Helper to log test stages
|
|
function runStage(name, fn) {
|
|
try {
|
|
fn();
|
|
console.log(`✅ [PASS] ${name}`);
|
|
} catch (err) {
|
|
console.error(`❌ [FAIL] ${name}`);
|
|
console.error(` 👉 Reason: ${err.message}`);
|
|
process.exit(1);
|
|
}
|
|
}
|
|
|
|
// 1. Verify build directory exists
|
|
runStage('Build Directory Verification', () => {
|
|
if (!fs.existsSync(distDir)) {
|
|
throw new Error('Build output directory "dist/" does not exist. Run "npm run build" first.');
|
|
}
|
|
});
|
|
|
|
const indexHtmlPath = path.join(distDir, 'index.html');
|
|
let htmlContent = '';
|
|
|
|
// 2. Verify index.html exists and is readable
|
|
runStage('index.html Integrity Check', () => {
|
|
if (!fs.existsSync(indexHtmlPath)) {
|
|
throw new Error('index.html is missing from dist/ folder.');
|
|
}
|
|
htmlContent = fs.readFileSync(indexHtmlPath, 'utf8');
|
|
if (!htmlContent || htmlContent.trim().length === 0) {
|
|
throw new Error('index.html is empty.');
|
|
}
|
|
});
|
|
|
|
// 3. Check for SEO and duplicate tag bugs
|
|
runStage('SEO & Metadata Tag Checks', () => {
|
|
// Check for duplicate <title> tags
|
|
const titleMatches = htmlContent.match(/<title[^>]*>([\s\S]*?)<\/title>/gi);
|
|
if (!titleMatches) {
|
|
throw new Error('No <title> tag found in index.html.');
|
|
}
|
|
if (titleMatches.length > 1) {
|
|
throw new Error(`Duplicate <title> tags detected! Found ${titleMatches.length} title tags.`);
|
|
}
|
|
|
|
// Ensure title contains Rafael's name
|
|
const titleText = titleMatches[0];
|
|
if (!titleText.toLowerCase().includes('rafael')) {
|
|
throw new Error('Optimized title does not contain "Rafael" - SEO validation failed.');
|
|
}
|
|
|
|
// Check for duplicate descriptions
|
|
const descMatches = htmlContent.match(/<meta\s+name="description"\s+content="[^"]*"\s*\/?>/gi);
|
|
if (descMatches && descMatches.length > 1) {
|
|
throw new Error(`Duplicate meta description tags detected! Found ${descMatches.length} descriptions.`);
|
|
}
|
|
});
|
|
|
|
// 4. Verify critical routing and search engine assets
|
|
runStage('Core Assets Verification', () => {
|
|
const criticalFiles = ['.htaccess', 'robots.txt', 'sitemap.xml'];
|
|
for (const file of criticalFiles) {
|
|
const filePath = path.join(distDir, file);
|
|
if (!fs.existsSync(filePath)) {
|
|
throw new Error(`Critical file "${file}" is missing from the build directory.`);
|
|
}
|
|
}
|
|
});
|
|
|
|
// 5. Verify local asset references exist in dist/
|
|
runStage('Broken Reference & Asset Verification', () => {
|
|
// Regex to match local src and href attributes in index.html
|
|
const linkRegex = /(src|href)="\/([^"]+)"/g;
|
|
let match;
|
|
const verifiedAssets = [];
|
|
|
|
while ((match = linkRegex.exec(htmlContent)) !== null) {
|
|
const assetPath = match[2];
|
|
|
|
// Skip absolute links, PDF documents, and external URLs
|
|
if (
|
|
assetPath.startsWith('http') ||
|
|
assetPath.startsWith('//') ||
|
|
assetPath.endsWith('.pdf') ||
|
|
assetPath.startsWith('sitemap') ||
|
|
assetPath.startsWith('robots')
|
|
) {
|
|
continue;
|
|
}
|
|
|
|
const fullAssetPath = path.join(distDir, assetPath.split('?')[0]);
|
|
if (!fs.existsSync(fullAssetPath)) {
|
|
throw new Error(`Broken link/reference detected! File "/${assetPath}" was referenced in index.html but does not exist in the build output.`);
|
|
}
|
|
verifiedAssets.push(assetPath);
|
|
}
|
|
|
|
if (verifiedAssets.length > 0) {
|
|
console.log(` 👉 Verified ${verifiedAssets.length} asset references correctly.`);
|
|
}
|
|
});
|
|
|
|
// 6. Verify language flags exist
|
|
runStage('Locale Flag Image Integrity Check', () => {
|
|
const flagsDir = path.join(distDir, 'flags');
|
|
if (!fs.existsSync(flagsDir)) {
|
|
throw new Error('flags/ directory is missing from build output.');
|
|
}
|
|
const requiredFlags = ['gb.png', 'es.png', 'it.png', 'de.png'];
|
|
for (const flag of requiredFlags) {
|
|
const flagPath = path.join(flagsDir, flag);
|
|
if (!fs.existsSync(flagPath)) {
|
|
throw new Error(`Required country flag image "${flag}" is missing from flags/ directory.`);
|
|
}
|
|
}
|
|
});
|
|
|
|
console.log('--------------------------------------------------');
|
|
console.log('🎉 QA Release Check complete. Build is healthy!');
|
|
console.log('--------------------------------------------------');
|
|
process.exit(0);
|