/**
* 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(