#!/usr/bin/env tsx /** * Updated link test for the blog with file examples * Tests: All references are valid, files exist */ import fs from 'fs'; import path from 'path'; console.log('šŸ”— Checking links and references...\n'); let passed = 0; let failed = 0; function test(name: string, fn: () => void): void { try { fn(); console.log(`āœ… ${name}`); passed++; } catch (error) { console.log(`āŒ ${name}`); if (error instanceof Error) { console.log(` Error: ${error.message}`); } failed++; } } // Test 1: Check that blog posts reference valid data test('Blog posts reference valid data', () => { const blogPostsPath = path.join(process.cwd(), 'src/data/blogPosts.ts'); const content = fs.readFileSync(blogPostsPath, 'utf8'); // Extract all slugs const slugMatches = content.match(/slug:\s*['"]([^'"]+)['"]/g) || []; const slugs = slugMatches.map(m => m.match(/['"]([^'"]+)['"]/)?.[1]); if (slugs.length === 0) { throw new Error('No slugs found in blogPosts.ts'); } // Verify [slug].astro exists for dynamic routing const slugPagePath = path.join(process.cwd(), 'src/pages/blog/[slug].astro'); if (!fs.existsSync(slugPagePath)) { throw new Error('Dynamic slug page [slug].astro does not exist'); } }); // Test 2: Verify tag references are valid test('Tag references are valid', () => { const blogPostsPath = path.join(process.cwd(), 'src/data/blogPosts.ts'); const content = fs.readFileSync(blogPostsPath, 'utf8'); // Extract all tags const tagMatches = content.match(/tags:\s*\[([^\]]+)\]/g) || []; if (tagMatches.length === 0) { throw new Error('No tags found in blogPosts.ts'); } // Verify tag page exists const tagPagePath = path.join(process.cwd(), 'src/pages/tags/[tag].astro'); if (!fs.existsSync(tagPagePath)) { throw new Error('Tag page [tag].astro does not exist'); } }); // Test 3: Verify all component imports are valid test('All component imports are valid', () => { const components = [ 'src/components/MediumCard.astro', 'src/components/SearchBar.tsx', 'src/components/ArticleBlockquote.tsx', 'src/components/ArticleHeading.tsx', 'src/components/ArticleParagraph.tsx', 'src/components/ArticleList.tsx', 'src/components/Footer.tsx', 'src/components/Hero.tsx', 'src/components/Tag.astro', 'src/components/FileExample.astro', 'src/components/FileExamplesList.astro' ]; for (const component of components) { const componentPath = path.join(process.cwd(), component); if (!fs.existsSync(componentPath)) { throw new Error(`Component missing: ${component}`); } } }); // Test 4: Verify all required pages exist test('All required pages exist', () => { const requiredPages = [ 'src/pages/index.astro', 'src/pages/blog/[slug].astro', 'src/pages/tags/[tag].astro', 'src/pages/api/download-zip.ts' ]; for (const page of requiredPages) { const pagePath = path.join(process.cwd(), page); if (!fs.existsSync(pagePath)) { throw new Error(`Required page missing: ${page}`); } } }); // Test 5: Verify layout files are valid test('Layout files are valid', () => { const layoutPath = path.join(process.cwd(), 'src/layouts/BaseLayout.astro'); if (!fs.existsSync(layoutPath)) { throw new Error('Layout missing: src/layouts/BaseLayout.astro'); } const content = fs.readFileSync(layoutPath, 'utf8'); if (!content.includes('')) { throw new Error('BaseLayout does not contain proper HTML structure'); } if (!content.includes('')) { throw new Error('BaseLayout missing head section'); } if (!content.includes('')) { throw new Error('BaseLayout missing body section'); } }); // Test 6: Verify global styles are properly imported test('Global styles are properly imported', () => { const stylesPath = path.join(process.cwd(), 'src/styles/global.css'); if (!fs.existsSync(stylesPath)) { throw new Error('Global styles file missing'); } const content = fs.readFileSync(stylesPath, 'utf8'); // Check for Tailwind imports if (!content.includes('@tailwind base') || !content.includes('@tailwind components') || !content.includes('@tailwind utilities')) { throw new Error('Global styles missing Tailwind imports'); } // Check for required classes const requiredClasses = ['.container', '.post-card', '.highlighter-tag']; for (const className of requiredClasses) { if (!content.includes(className)) { throw new Error(`Global styles missing required class: ${className}`); } } }); // Test 7: Verify file examples data structure test('File examples data structure is valid', () => { const fileExamplesPath = path.join(process.cwd(), 'src/data/fileExamples.ts'); if (!fs.existsSync(fileExamplesPath)) { throw new Error('File examples data file missing'); } const content = fs.readFileSync(fileExamplesPath, 'utf8'); if (!content.includes('export interface FileExample')) { throw new Error('FileExample interface not exported'); } if (!content.includes('export const sampleFileExamples')) { throw new Error('sampleFileExamples not exported'); } // Check for required fields in interface const requiredFields = ['id', 'filename', 'content', 'language']; for (const field of requiredFields) { if (!content.includes(field)) { throw new Error(`FileExample interface missing field: ${field}`); } } }); // Test 8: Verify API endpoint structure test('API endpoint structure is valid', () => { const apiPath = path.join(process.cwd(), 'src/pages/api/download-zip.ts'); if (!fs.existsSync(apiPath)) { throw new Error('API endpoint missing'); } const content = fs.readFileSync(apiPath, 'utf8'); if (!content.includes('export const POST')) { throw new Error('API missing POST handler'); } if (!content.includes('export const GET')) { throw new Error('API missing GET handler'); } if (!content.includes('FileExampleManager')) { throw new Error('API does not use FileExampleManager'); } }); // Test 9: Verify blog template imports file examples test('Blog template imports file examples', () => { const blogTemplatePath = path.join(process.cwd(), 'src/pages/blog/[slug].astro'); if (!fs.existsSync(blogTemplatePath)) { throw new Error('Blog template missing'); } const content = fs.readFileSync(blogTemplatePath, 'utf8'); if (!content.includes('FileExamplesList')) { throw new Error('Blog template does not import FileExamplesList'); } if (!content.includes('FileExamplesList')) { throw new Error('Blog template does not reference file examples'); } }); // Summary console.log('\n' + '='.repeat(50)); console.log(`Tests passed: ${passed}`); console.log(`Tests failed: ${failed}`); console.log('='.repeat(50)); if (failed === 0) { console.log('\nšŸŽ‰ All link checks passed! All references are valid.'); console.log('\nVerified:'); console.log(' āœ… Blog posts data and routing'); console.log(' āœ… Tag filtering system'); console.log(' āœ… All components exist'); console.log(' āœ… All pages exist'); console.log(' āœ… Layout structure'); console.log(' āœ… File examples functionality'); console.log(' āœ… API endpoints'); process.exit(0); } else { console.log('\nāŒ Some checks failed. Please fix the errors above.'); process.exit(1); }