import { describe, expect, it, vi } from 'vitest'
import { fireEvent, renderWithProviders, screen } from '../../test/utils'
import { triggerDownload } from './_shared/triggerDownload'
import { bubbleVariantForState, bubbleGroupPositionFromStream } from './types'
import MessageAttachment from '.'
vi.mock('./_shared/triggerDownload', () => ({
triggerDownload: vi.fn(() => Promise.resolve()),
}))
const mockedDownload = vi.mocked(triggerDownload)
describe('MessageAttachment behavior', () => {
it('renders file rows with precedence, fallback names, and cancellation', () => {
const onClick = vi.fn(() => false)
const { rerender } = renderWithProviders(
)
expect(screen.getByText('first.pdf')).toBeInTheDocument()
expect(screen.getByText('second.pdf')).toBeInTheDocument()
fireEvent.click(screen.getByRole('button', { name: 'Download first.pdf' }))
expect(onClick).toHaveBeenCalledWith(0)
expect(mockedDownload).not.toHaveBeenCalled()
rerender(
)
fireEvent.click(
screen.getByRole('button', { name: 'Download fallback.txt' })
)
expect(mockedDownload).toHaveBeenCalledWith(
'https://cdn.example.com/fallback.txt',
'fallback.txt'
)
})
it('renders no file when inputs are absent and supports composer dismissal', () => {
const { container } = renderWithProviders(
)
expect(container.firstChild).toBeNull()
const onDismiss = vi.fn()
renderWithProviders(
)
fireEvent.click(screen.getByRole('button', { name: 'Dismiss attachment' }))
expect(onDismiss).toHaveBeenCalledOnce()
})
it('renders image stacks, forwards loading, and supports click cancellation', () => {
const onClick = vi.fn(() => false)
renderWithProviders(
)
expect(screen.getAllByRole('img')[0]).toHaveAttribute('loading', 'lazy')
expect(screen.getAllByRole('img')).toHaveLength(2)
fireEvent.click(screen.getByRole('button', { name: 'Open image 2 of 2' }))
expect(onClick).toHaveBeenCalledWith(1)
expect(screen.queryByTestId('image-viewer')).not.toHaveAttribute('open')
})
it('opens images at the clicked index and suffixes a shared filename', () => {
renderWithProviders(
)
fireEvent.click(screen.getByRole('button', { name: 'Open image 2 of 2' }))
expect(screen.getByTestId('image-viewer')).toHaveAttribute(
'aria-label',
'photos.jpg (2)'
)
expect(screen.getByRole('img', { name: 'photos.jpg (2)' })).toHaveAttribute(
'src',
'https://cdn.example.com/two.jpg'
)
})
it('renders video poster and fallback, and wires viewer attributes', () => {
const onDismiss = vi.fn()
const { rerender } = renderWithProviders(
)
expect(
screen.getByRole('img', { name: 'Video 1 thumbnail' })
).toHaveAttribute('src', '/poster.jpg')
fireEvent.click(screen.getByRole('button', { name: 'Dismiss attachment' }))
expect(onDismiss).toHaveBeenCalledOnce()
rerender(
)
expect(screen.getByLabelText('Play video 1 of 1')).toBeInTheDocument()
})
it('supports PDF titles, download actions, dismiss, and viewer index', () => {
const onDismiss = vi.fn()
renderWithProviders(
)
expect(screen.getByText('Quarterly report')).toBeInTheDocument()
fireEvent.click(screen.getByRole('button', { name: 'Dismiss attachment' }))
expect(onDismiss).toHaveBeenCalledOnce()
renderWithProviders(
)
fireEvent.click(screen.getByRole('button', { name: 'Open two.pdf' }))
expect(screen.getAllByTestId('pdf-viewer').at(-1)).toHaveAttribute(
'aria-label',
'two.pdf'
)
})
it('maps state and stream grouping contracts', () => {
expect(bubbleVariantForState('composer')).toBe('dark')
expect(bubbleVariantForState('sent')).toBe('dark')
expect(bubbleVariantForState('received')).toBe('light')
expect(bubbleGroupPositionFromStream({})).toBe('single')
expect(
bubbleGroupPositionFromStream({
groupedByUser: true,
firstOfGroup: true,
endOfGroup: true,
})
).toBe('single')
expect(
bubbleGroupPositionFromStream({ groupedByUser: true, firstOfGroup: true })
).toBe('first')
expect(
bubbleGroupPositionFromStream({ groupedByUser: true, endOfGroup: true })
).toBe('end')
expect(bubbleGroupPositionFromStream({ groupedByUser: true })).toBe(
'middle'
)
})
})