import { readFileSync } from 'node:fs'; import { resolve } from 'node:path'; import { describe, expect, it } from 'vitest'; import { validateProductionInputs } from '@/content/schemas'; import { render } from '@/entry-server'; import type { SectionId } from '@/content/types'; const fixture = (name: string): unknown => JSON.parse(readFileSync(resolve(process.cwd(), 'tests/fixtures', name), 'utf8')); const validInputs = validateProductionInputs( fixture('portfolio-content.valid.json'), fixture('asset-manifest.valid.json'), fixture('site-config.preview.json'), new URL('file:///prepared-assets/'), ); const renderedDocument = async (): Promise => { return renderedDocumentFor(validInputs); }; const renderedDocumentFor = async (inputs: typeof validInputs): Promise => { const document = globalThis.document.implementation.createHTMLDocument('Portfolio'); document.body.innerHTML = await render('/', inputs); return document; }; describe('semantic portfolio document', () => { it('renders the complete landmark and heading reading structure on the server', async () => { const document = await renderedDocument(); expect(document.querySelectorAll('.site-header')).toHaveLength(1); expect(document.querySelector('nav[aria-label="Portfolio sections"]')).not.toBeNull(); expect(document.querySelectorAll('main')).toHaveLength(1); expect(document.querySelectorAll('footer')).toHaveLength(1); expect(document.querySelectorAll('h1')).toHaveLength(1); expect(document.querySelector('#start h1')?.textContent).toBe('Luca Pîslaru'); expect([...document.querySelectorAll('main > section')].map((section) => section.id)).toEqual([ 'start', 'profile', 'experience', 'infrastructure', 'selected-builds', 'additional-work', 'toolkit', 'contact', ]); expect([...document.querySelectorAll('main > section')].every((section) => section.hasAttribute('aria-labelledby'))).toBe(true); expect([...document.querySelectorAll('main > section > h2')].map((heading) => heading.textContent)).toEqual([ 'Profile', 'Experience', 'Infrastructure', 'Selected builds', 'Additional work', 'Toolkit', 'Contact', ]); expect([...document.querySelectorAll('main > section')].map((section) => section.getAttribute('data-layout'))).toEqual([ 'hero', 'right-rail', 'left-timeline', 'full-diagram', 'project-stage', 'compact-rail', 'toolkit-field', 'destination', ]); expect(document.querySelectorAll('.route-label')).toHaveLength(0); expect(document.querySelector('h2 + h4')).toBeNull(); }); it('renders the exact first-screen hierarchy and destination actions', async () => { const document = await renderedDocument(); const hero = document.querySelector('.hero-journey__content')!; expect([...hero.children].flatMap((element) => element.classList.contains('hero-journey__actions') ? [...element.children].map((action) => action.textContent?.trim()) : [element.textContent?.trim()], )).toEqual([ 'Luca Pîslaru', 'System Administrator & Full-Stack Developer', validInputs.content.identity.valueProposition, validInputs.content.identity.availability, 'Contact Me', 'Explore work', ]); expect(hero.querySelector('a[href="#contact"]')?.textContent).toBe('Contact Me'); expect(document.querySelector('[data-featured-operation]')?.textContent).toContain( validInputs.content.infrastructure.featuredOperation.title, ); expect(document.querySelector('#contact')?.textContent).toContain('System Administrator'); expect(document.body.textContent).not.toMatch(/[–—]/u); }); it('assigns explicit project priority without promoting research exploration', async () => { const document = await renderedDocument(); const priorities = [...document.querySelectorAll('#selected-builds article')].map((article) => [ article.querySelector('h3')?.textContent, article.getAttribute('data-project-priority'), ]); expect(priorities).toEqual([ ['Private self-hosted medication tracker', 'primary'], ['Footy Tracker', 'primary'], ['Opinion Dynamics Simulation', 'supporting'], ]); }); it('keeps native section links and visible contact available without JavaScript', async () => { const document = await renderedDocument(); const navigation = document.querySelector('nav[aria-label="Portfolio sections"]')!; expect([...navigation.querySelectorAll('a')].map((link) => link.getAttribute('href'))).toEqual([ '#start', '#profile', '#experience', '#infrastructure', '#selected-builds', '#additional-work', '#toolkit', '#contact', ]); const email = document.querySelector('#contact-email-details'); expect(email?.querySelector('summary')?.textContent).toBe('Email Me'); expect(email?.textContent).toContain('hello [at] example [dot] test'); expect(document.querySelector('a[href^="mailto:"]')).toBeNull(); expect(document.body.textContent).toContain('For a current CV or role-specific information, contact me directly.'); }); it('renders one semantic navigation label with no duplicate responsive text', async () => { const document = await renderedDocument(); const experience = document.querySelector('nav a[href="#experience"]')!; const additional = document.querySelector('nav a[href="#additional-work"]')!; expect(experience.hasAttribute('aria-label')).toBe(false); expect(experience.textContent).toBe('Career'); expect(experience.childElementCount).toBe(0); expect(additional.hasAttribute('aria-label')).toBe(false); expect(additional.textContent).toBe('More work'); expect(additional.childElementCount).toBe(0); }); it('binds navigation and section labels to their canonical IDs when content navigation is reordered', async () => { const content = fixture('portfolio-content.valid.json') as { navigation: Array<{ id: SectionId; label: string }>; }; const navigationById = new Map(content.navigation.map((item) => [item.id, item])); const reorderedIds: readonly SectionId[] = [ 'contact', 'toolkit', 'additional-work', 'selected-builds', 'infrastructure', 'experience', 'profile', 'start', ]; content.navigation = reorderedIds.map((id) => navigationById.get(id)!); const reorderedInputs = validateProductionInputs( content, fixture('asset-manifest.valid.json'), fixture('site-config.preview.json'), new URL('file:///prepared-assets/'), ); const document = await renderedDocumentFor(reorderedInputs); expect([...document.querySelectorAll('main > section > h2')].map((heading) => heading.textContent)).toEqual([ 'Profile', 'Experience', 'Infrastructure', 'Selected builds', 'Additional work', 'Toolkit', 'Contact', ]); expect([...document.querySelectorAll('nav a')].map((link) => [link.getAttribute('href'), link.textContent])).toEqual([ ['#start', 'Start'], ['#profile', 'Profile'], ['#experience', 'Career'], ['#infrastructure', 'Infra'], ['#selected-builds', 'Builds'], ['#additional-work', 'More work'], ['#toolkit', 'Toolkit'], ['#contact', 'Contact'], ]); }); it('includes systems and software evidence in server-rendered content', async () => { const document = await renderedDocument(); expect(document.body.textContent).toContain('Professional systems operations'); expect(document.body.textContent).toContain('Secure self-hosted platform and lifecycle'); expect(document.body.textContent).toContain('Private self-hosted medication tracker'); expect(document.body.textContent).toContain('Footy Tracker'); }); });