import { describe, expect, it, vi } from 'vitest'; import { cleanup, fireEvent, render, screen, within } from '@testing-library/react'; import { afterEach } from 'vitest'; import BrandPersonasCard from '../app/_components/BrandPersonasCard'; import { personas, type PersonaSlug } from '../app/_data/personas'; afterEach(() => { cleanup(); }); describe('BrandPersonasCard — landing variant', () => { it('renders 13 persona tiles + 3 filler tiles by default', () => { render(); const tiles = screen.getAllByTestId(/^persona-tile-/); expect(tiles).toHaveLength(13); const fillers = screen.getAllByTestId('brand-personas-filler'); expect(fillers).toHaveLength(3); // Fillers must be marked aria-hidden to stay out of AT navigation. for (const filler of fillers) { expect(filler).toHaveAttribute('aria-hidden', 'true'); } }); it('renders correct title + role copy for all 13 personas (verbatim from locked decision)', () => { render(); for (const persona of personas) { const tile = screen.getByTestId(`persona-tile-${persona.slug}`); const scope = within(tile); expect(scope.getByText(persona.title)).toBeInTheDocument(); expect(scope.getByText(persona.role)).toBeInTheDocument(); } }); it('fires onPersonaClick with the correct slug when a tile is clicked', () => { const handler = vi.fn<(slug: PersonaSlug) => void>(); render(); const hunterTile = screen.getByTestId('persona-tile-hunter'); const button = within(hunterTile).getByRole('button', { name: /Waggle The Hunter bee mascot/i, }); fireEvent.click(button); expect(handler).toHaveBeenCalledTimes(1); expect(handler).toHaveBeenCalledWith('hunter'); }); it('fires onTileHover with the correct slug on mouseenter', () => { const handler = vi.fn<(slug: PersonaSlug) => void>(); render(); const connectorTile = screen.getByTestId('persona-tile-connector'); fireEvent.mouseEnter(connectorTile); expect(handler).toHaveBeenCalledTimes(1); expect(handler).toHaveBeenCalledWith('connector'); }); it('renders the cta slot below the grid', () => { render( Meet the hive} />, ); const ctaSlot = screen.getByTestId('brand-personas-cta'); expect(ctaSlot).toBeInTheDocument(); expect( within(ctaSlot).getByRole('link', { name: /Meet the hive/i }), ).toHaveAttribute('href', 'https://example.test/cta'); }); it('hides filler tiles when showFillerTiles={false}', () => { render(); expect(screen.queryAllByTestId('brand-personas-filler')).toHaveLength(0); // Persona tiles remain intact — feature is filler-scoped. expect(screen.getAllByTestId(/^persona-tile-/)).toHaveLength(13); }); it('flips to a placeholder when a persona asset fails to load', () => { render(); const writerTile = screen.getByTestId('persona-tile-writer'); const img = within(writerTile).getByRole('img', { name: /Waggle The Writer bee mascot/i, }); // Simulate the 404/onerror path that fires when an asset is missing // (e.g., mid-regen Task #24 state before the new PNG lands). fireEvent.error(img); expect(writerTile).toHaveAttribute('data-placeholder', 'true'); expect( screen.getByTestId('persona-placeholder-writer'), ).toBeInTheDocument(); // Role copy must stay readable even while the image degrades. expect( within(writerTile).getByText( 'Shapes the story the memory wants to tell.', ), ).toBeInTheDocument(); }); it('supports keyboard activation (Enter + Space) when onPersonaClick is provided', () => { const handler = vi.fn<(slug: PersonaSlug) => void>(); render(); const teamTile = screen.getByTestId('persona-tile-team'); const button = within(teamTile).getByRole('button'); fireEvent.keyDown(button, { key: 'Enter' }); fireEvent.keyDown(button, { key: ' ' }); // A non-activation key must not fire the handler. fireEvent.keyDown(button, { key: 'Tab' }); expect(handler).toHaveBeenCalledTimes(2); expect(handler).toHaveBeenNthCalledWith(1, 'team'); expect(handler).toHaveBeenNthCalledWith(2, 'team'); }); it('honours custom heading + subtitle overrides', () => { render( , ); expect( screen.getByRole('heading', { name: 'Internal reference' }), ).toBeInTheDocument(); expect(screen.getByText('Brand canon for the team.')).toBeInTheDocument(); }); }); describe('BrandPersonasCard — compact variant', () => { it('renders the compact stub without errors and exposes the data-variant hook', () => { render(); const stub = screen.getByTestId('brand-personas-card-compact'); expect(stub).toBeInTheDocument(); expect(stub).toHaveAttribute('data-variant', 'compact'); // Landing DOM must not leak into compact path. expect( screen.queryByTestId('brand-personas-card'), ).not.toBeInTheDocument(); }); });