import { beforeEach, describe, expect, it, vi } from 'vitest'
import {
fireEvent,
renderWithProviders,
screen,
waitFor,
} from '../../../test/utils'
import DownloadAction from './DownloadAction'
import { triggerDownload } from './triggerDownload'
import { VIEWER_ACTION_CLASS } from './viewerChromeClasses'
vi.mock('./triggerDownload', () => ({
triggerDownload: vi.fn(() => Promise.resolve()),
}))
const mockedTriggerDownload = vi.mocked(triggerDownload)
beforeEach(() => {
mockedTriggerDownload.mockReset()
mockedTriggerDownload.mockResolvedValue(undefined)
})
describe('DownloadAction', () => {
it('uses a visible label for the default pill variant', () => {
renderWithProviders()
const button = screen.getByRole('button', { name: 'Download' })
expect(button).toHaveTextContent('Download')
expect(button).not.toHaveAttribute('aria-label')
})
it.each(['inline', 'viewer'] as const)(
'uses an accessible-only label for the %s variant',
(variant) => {
renderWithProviders(
)
const button = screen.getByRole('button', { name: 'Save file' })
expect(button).not.toHaveTextContent('Save file')
}
)
it('allows iconOnly to control the pill label', () => {
const { rerender } = renderWithProviders(
)
expect(screen.getByRole('button', { name: 'Save' })).not.toHaveTextContent(
'Save'
)
rerender()
expect(screen.getByRole('button', { name: 'Save' })).toHaveTextContent(
'Save'
)
})
it.each(['inline', 'viewer'] as const)(
'keeps the %s label visually hidden regardless of iconOnly',
(variant) => {
const { rerender } = renderWithProviders(
)
expect(
screen.getByRole('button', { name: 'Save' })
).not.toHaveTextContent('Save')
rerender(
)
expect(
screen.getByRole('button', { name: 'Save' })
).not.toHaveTextContent('Save')
}
)
it('stops propagation and calls the download once', async () => {
const outerClick = vi.fn()
renderWithProviders(
// eslint-disable-next-line jsx-a11y/click-events-have-key-events, jsx-a11y/no-static-element-interactions
)
fireEvent.click(screen.getByRole('button', { name: 'Download' }))
await waitFor(() =>
expect(mockedTriggerDownload).toHaveBeenCalledWith(
'/file.pdf',
'report.pdf'
)
)
expect(mockedTriggerDownload).toHaveBeenCalledOnce()
expect(outerClick).not.toHaveBeenCalled()
})
it('disables repeated clicks while downloading and settles on success', async () => {
let resolve: () => void = () => undefined
mockedTriggerDownload.mockReturnValueOnce(
new Promise((done) => {
resolve = done
})
)
const onTriggered = vi.fn()
renderWithProviders(
)
const button = screen.getByRole('button', { name: 'Download' })
fireEvent.click(button)
fireEvent.click(button)
expect(button).toBeDisabled()
expect(mockedTriggerDownload).toHaveBeenCalledOnce()
resolve()
await waitFor(() => expect(button).toBeEnabled())
expect(onTriggered).toHaveBeenCalledOnce()
})
it('settles and notifies even when downloading rejects', async () => {
mockedTriggerDownload.mockRejectedValueOnce(new Error('offline'))
const onTriggered = vi.fn()
renderWithProviders(
)
const button = screen.getByRole('button', { name: 'Download' })
fireEvent.click(button)
await waitFor(() => expect(button).toBeEnabled())
expect(onTriggered).toHaveBeenCalledOnce()
})
it('maps tone classes for pill and inline variants', () => {
const { rerender } = renderWithProviders(
)
expect(screen.getByRole('button', { name: 'Download' })).toHaveClass(
'hover:bg-[#2a2928]'
)
rerender()
expect(screen.getByRole('button', { name: 'Download' })).toHaveClass(
'hover:bg-white/90'
)
rerender()
expect(screen.getByRole('button', { name: 'Download' })).toHaveClass(
'hover:text-white'
)
rerender()
expect(screen.getByRole('button', { name: 'Download' })).toHaveClass(
'hover:text-black'
)
})
it('uses the shared viewer action class', () => {
renderWithProviders()
expect(screen.getByRole('button', { name: 'Download' })).toHaveClass(
VIEWER_ACTION_CLASS
)
})
})