From 42373521f021559a90fa536035d1992c16b6369d Mon Sep 17 00:00:00 2001 From: Ken Date: Tue, 26 May 2026 22:49:09 +0000 Subject: [PATCH] feat: add PostHeader component with badge, title, meta, and responsive styles Built the PostHeader.astro component per the design spec. It accepts props {title, date, type, readingTime}, formats dates with toLocaleDateString, renders a centered header with content type badge, h1 title, and meta line with date/reading time. Has responsive typography stepping down at 719px and 479px breakpoints. All 39 tests pass and npm run build succeeds. Generated with Amplifier Co-Authored-By: Amplifier <240397093+microsoft-amplifier@users.noreply.github.com> --- src/components/PostHeader.astro | 88 ++++++++++++++++++ tests/check-post-header.mjs | 159 ++++++++++++++++++++++++++++++++ 2 files changed, 247 insertions(+) create mode 100644 src/components/PostHeader.astro create mode 100644 tests/check-post-header.mjs diff --git a/src/components/PostHeader.astro b/src/components/PostHeader.astro new file mode 100644 index 0000000..b939ec2 --- /dev/null +++ b/src/components/PostHeader.astro @@ -0,0 +1,88 @@ +--- +interface Props { + title: string; + date: Date; + type?: string; + readingTime?: number; +} + +const { title, date, type = 'post', readingTime } = Astro.props; + +const formattedDate = date.toLocaleDateString('en-US', { + year: 'numeric', + month: 'long', + day: 'numeric', +}); +const isoDate = date.toISOString().split('T')[0]; +--- + +
+ {type} +

{title}

+

+ {readingTime && ` · ${readingTime} min read`} +

+
+ + \ No newline at end of file diff --git a/tests/check-post-header.mjs b/tests/check-post-header.mjs new file mode 100644 index 0000000..5b16b07 --- /dev/null +++ b/tests/check-post-header.mjs @@ -0,0 +1,159 @@ +/** + * Build-output test for PostHeader.astro + * Checks source content for expected structure, props, styles, and responsive breakpoints. + */ +import { readFileSync, existsSync } from 'fs'; +import { resolve } from 'path'; + +const srcPath = resolve('src/components/PostHeader.astro'); + +let failures = 0; +let passes = 0; + +function assert(condition, label) { + if (condition) { + console.log(` ✓ ${label}`); + passes++; + } else { + console.error(` ✗ FAIL: ${label}`); + failures++; + } +} + +// ── Source file existence ── +console.log('\n--- Source file checks ---'); + +assert( + existsSync(srcPath), + 'src/components/PostHeader.astro exists' +); + +if (existsSync(srcPath)) { + const src = readFileSync(srcPath, 'utf-8'); + + // ── Props interface ── + console.log('\n--- Props interface checks ---'); + + assert( + /title:\s*string/.test(src), + 'Props has title: string' + ); + assert( + /date:\s*Date/.test(src), + 'Props has date: Date' + ); + assert( + /type\?:\s*string/.test(src), + 'Props has type?: string' + ); + assert( + /readingTime\?:\s*number/.test(src), + 'Props has readingTime?: number' + ); + + // ── Default values ── + console.log('\n--- Default value checks ---'); + + assert( + src.includes("'post'") || src.includes('"post"'), + 'type defaults to "post"' + ); + + // ── Date formatting ── + console.log('\n--- Date formatting checks ---'); + + assert( + src.includes("toLocaleDateString('en-US'") || src.includes('toLocaleDateString("en-US"'), + 'Uses toLocaleDateString with en-US locale' + ); + assert( + src.includes("'numeric'") || src.includes('"numeric"'), + 'Date format includes numeric option' + ); + assert( + src.includes("'long'") || src.includes('"long"'), + 'Date format includes long month' + ); + assert( + src.includes("toISOString()"), + 'Computes ISO date string' + ); + + // ── HTML structure ── + console.log('\n--- HTML structure checks ---'); + + assert( + src.includes('post-header'), + 'Has .post-header class' + ); + assert( + /]/.test(src), + 'Uses
element' + ); + assert( + src.includes('badge'), + 'Has .badge class' + ); + assert( + /]*class.*badge/.test(src) || /]*badge/.test(src), + 'Badge uses element' + ); + assert( + /

element for title' + ); + assert( + src.includes('title') && src.includes('class'), + 'Has .title class' + ); + assert( + /]*class.*meta/.test(src) || src.includes("class=\"meta\"") || src.includes("class='meta'"), + 'Meta uses

element with .meta class' + ); + assert( + /]*datetime/.test(src), + 'Has