/** * Tests for MutedContentWrapper component. * Collapses content matching muted words with accessible expand/collapse. * @see specs/prd-web.md Section M8 */ import { describe, it, expect } from 'vitest' import { render, screen } from '@testing-library/react' import userEvent from '@testing-library/user-event' import { axe } from 'vitest-axe' import { MutedContentWrapper } from './muted-content-wrapper' describe('MutedContentWrapper', () => { it('renders children when no muted words match', () => { render(

Hello world

) expect(screen.getByText('Hello world')).toBeInTheDocument() expect(screen.queryByText(/content hidden/i)).not.toBeInTheDocument() }) it('collapses content when a muted word matches', () => { render(

This is spam content

) expect(screen.queryByText('This is spam content')).not.toBeInTheDocument() expect(screen.getByText(/content hidden \(muted word: spam\)/i)).toBeInTheDocument() }) it('matches muted words case-insensitively', () => { render(

This is SPAM content

) expect(screen.getByText(/content hidden \(muted word: spam\)/i)).toBeInTheDocument() }) it('shows the first matching muted word in the label', () => { render(

offensive spam text

) // Should show whichever word matched first in the muted words list expect(screen.getByText(/content hidden \(muted word: spam\)/i)).toBeInTheDocument() }) it('expands content on click', async () => { const user = userEvent.setup() render(

spam post here

) // Content hidden initially expect(screen.queryByText('spam post here')).not.toBeInTheDocument() // Click to expand await user.click(screen.getByRole('button')) expect(screen.getByText('spam post here')).toBeInTheDocument() }) it('collapses content again on second click', async () => { const user = userEvent.setup() render(

spam post here

) // Expand await user.click(screen.getByRole('button')) expect(screen.getByText('spam post here')).toBeInTheDocument() // Collapse again await user.click(screen.getByRole('button')) expect(screen.queryByText('spam post here')).not.toBeInTheDocument() }) it('sets aria-expanded correctly', async () => { const user = userEvent.setup() render(

spam content

) const button = screen.getByRole('button') expect(button).toHaveAttribute('aria-expanded', 'false') await user.click(button) expect(button).toHaveAttribute('aria-expanded', 'true') }) it('renders children directly when mutedWords is empty', () => { render(

Any content

) expect(screen.getByText('Any content')).toBeInTheDocument() }) it('matches whole words only (not substrings)', () => { render(

This is a classic example

) // "classic" contains "ass" but should not match as a whole word expect(screen.getByText('This is a classic example')).toBeInTheDocument() expect(screen.queryByText(/content hidden/i)).not.toBeInTheDocument() }) it('announces state change to screen readers via aria-live', async () => { const user = userEvent.setup() render(

spam content

) // Should have a live region for state announcements const liveRegion = screen.getByRole('status') expect(liveRegion).toBeInTheDocument() await user.click(screen.getByRole('button')) expect(liveRegion).toHaveTextContent(/content revealed/i) }) it('passes axe accessibility check when collapsed', async () => { const { container } = render(

spam content

) const results = await axe(container) expect(results).toHaveNoViolations() }) it('passes axe accessibility check when expanded', async () => { const user = userEvent.setup() const { container } = render(

spam content

) await user.click(screen.getByRole('button')) const results = await axe(container) expect(results).toHaveNoViolations() }) })