codeburn/app/renderer/components/ToastHost.test.tsx
iamtoruk daf944e714 feat(app): motion layer (gsap) — count-ups, chart grow-in, section fade, skeletons, toasts
- one gate (motionEnabled): off under reduced-motion, missing matchMedia,
  and vitest; every path checks it first
- mount/filter-change only: count-up and bar grow-in key off the
  period|provider|range key, so 30s poll refreshes snap values silently
  instead of re-animating
- first-load skeleton shimmer replaces bare scanning text (kept sr-only
  for screen readers); slide-in toast host for Settings/export feedback
  (validation errors stay inline); CSS hover-lift + press micro-
  interactions, all with a reduced-motion escape hatch
- gsap 3.15.0 + @gsap/react 2.1.2 (+74KB raw JS; G4 flame work shares it)

244/244, typecheck + build green.
2026-07-16 04:39:09 -07:00

40 lines
1.2 KiB
TypeScript

// @vitest-environment jsdom
import { act, render, screen } from '@testing-library/react'
import { afterEach, describe, expect, it, vi } from 'vitest'
import { ToastHost } from './ToastHost'
import { dismissToast, showToast } from '../lib/toast'
afterEach(() => {
dismissToast()
vi.useRealTimers()
})
describe('ToastHost', () => {
it('shows an action toast and auto-dismisses it after 3s', () => {
vi.useFakeTimers()
render(<ToastHost />)
act(() => { showToast('Exported to /tmp/out') })
expect(screen.getByRole('status')).toHaveTextContent('Exported to /tmp/out')
act(() => { vi.advanceTimersByTime(2999) })
expect(screen.getByRole('status')).toBeInTheDocument()
act(() => { vi.advanceTimersByTime(1) })
expect(screen.queryByRole('status')).not.toBeInTheDocument()
})
it('keeps only the most recent toast (one at a time)', () => {
vi.useFakeTimers()
render(<ToastHost />)
act(() => { showToast('First') })
act(() => { showToast('Second', 'error') })
const toasts = screen.getAllByRole('status')
expect(toasts).toHaveLength(1)
expect(toasts[0]).toHaveTextContent('Second')
expect(toasts[0]).toHaveClass('toast-error')
})
})