82 lines
2.9 KiB
TypeScript
82 lines
2.9 KiB
TypeScript
import { act, fireEvent, render, screen } from '@testing-library/react'
|
|
import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest'
|
|
import App from './App'
|
|
import type { CompletionSound } from './features/timer/sound'
|
|
|
|
function renderApp() {
|
|
const sound: CompletionSound = {
|
|
prepare: vi.fn(),
|
|
play: vi.fn(),
|
|
}
|
|
render(<App sound={sound} />)
|
|
return sound
|
|
}
|
|
|
|
describe('Pomodoro app', () => {
|
|
beforeEach(() => {
|
|
vi.useFakeTimers()
|
|
vi.setSystemTime(new Date('2026-07-22T10:08:00'))
|
|
})
|
|
|
|
afterEach(() => {
|
|
vi.useRealTimers()
|
|
})
|
|
|
|
it('shows a live clock and the initial focus timer', () => {
|
|
renderApp()
|
|
|
|
expect(screen.getByLabelText('Current time')).toHaveTextContent('10:08')
|
|
expect(screen.getByLabelText('Time remaining')).toHaveTextContent('25:00')
|
|
expect(screen.getByText('Focus session')).toBeInTheDocument()
|
|
expect(screen.getByText('0 / 4 sessions')).toBeInTheDocument()
|
|
})
|
|
|
|
it('starts, pauses, resumes, and resets the countdown', () => {
|
|
const sound = renderApp()
|
|
|
|
fireEvent.click(screen.getByRole('button', { name: 'Start' }))
|
|
expect(sound.prepare).toHaveBeenCalledOnce()
|
|
expect(screen.getByRole('button', { name: 'Pause' })).toBeInTheDocument()
|
|
|
|
act(() => vi.advanceTimersByTime(60_000))
|
|
expect(screen.getByLabelText('Time remaining')).toHaveTextContent('24:00')
|
|
|
|
fireEvent.click(screen.getByRole('button', { name: 'Pause' }))
|
|
act(() => vi.advanceTimersByTime(60_000))
|
|
expect(screen.getByLabelText('Time remaining')).toHaveTextContent('24:00')
|
|
|
|
fireEvent.click(screen.getByRole('button', { name: 'Resume' }))
|
|
act(() => vi.advanceTimersByTime(1_000))
|
|
expect(screen.getByLabelText('Time remaining')).toHaveTextContent('23:59')
|
|
|
|
fireEvent.click(screen.getByRole('button', { name: 'Reset' }))
|
|
expect(screen.getByLabelText('Time remaining')).toHaveTextContent('25:00')
|
|
expect(screen.getByRole('button', { name: 'Start' })).toBeInTheDocument()
|
|
})
|
|
|
|
it('announces completion and switches to a short break', () => {
|
|
const sound = renderApp()
|
|
|
|
fireEvent.click(screen.getByRole('button', { name: 'Start' }))
|
|
act(() => vi.advanceTimersByTime(25 * 60_000))
|
|
|
|
expect(sound.play).toHaveBeenCalledOnce()
|
|
expect(screen.getByRole('heading', { name: 'Short break' })).toBeInTheDocument()
|
|
expect(screen.getByLabelText('Time remaining')).toHaveTextContent('05:00')
|
|
expect(screen.getByText('1 / 4 sessions')).toBeInTheDocument()
|
|
})
|
|
|
|
it('lets the user select focus and break segments', () => {
|
|
renderApp()
|
|
|
|
fireEvent.click(screen.getByRole('button', { name: 'Long break' }))
|
|
expect(screen.getByLabelText('Time remaining')).toHaveTextContent('15:00')
|
|
|
|
fireEvent.click(screen.getByRole('button', { name: 'Short break' }))
|
|
expect(screen.getByLabelText('Time remaining')).toHaveTextContent('05:00')
|
|
|
|
fireEvent.click(screen.getByRole('button', { name: 'Focus' }))
|
|
expect(screen.getByLabelText('Time remaining')).toHaveTextContent('25:00')
|
|
})
|
|
})
|