A test file under packages/client now says which face it covers:
`*.client.spec.{ts,tsx}` and its `*.client.{ts,tsx}` helpers belong to the
Client aggregate, `*.host.spec.ts` to the host aggregate. The carrier's four
node-half specs take the Host suffix.
The two suffixes are mutually exclusive, so each aggregate excludes the
other's and both keep one broad test glob: `exclude` wins over `include`, and
`packages/client/**` no longer has to be excluded wholesale from the host
program with per-file `files` entries carved back out of it. A Host-face spec
that reaches only Host source therefore needs no cross-face project
reference, which the split-project rule rejects.
vitest still discovers every file through `**/*.spec.{ts,tsx}`.
58 lines
2.0 KiB
TypeScript
58 lines
2.0 KiB
TypeScript
// @vitest-environment jsdom
|
|
|
|
import { afterEach, describe, expect, it, vi } from 'vitest'
|
|
import { cleanup, fireEvent, render } from '@testing-library/react'
|
|
import { Toast } from '../src/Toast.tsx'
|
|
|
|
afterEach(cleanup)
|
|
|
|
describe('Toast', () => {
|
|
it('announces its text and reports done after the hold-and-fade lifetime', () => {
|
|
vi.useFakeTimers()
|
|
try {
|
|
const onDone = vi.fn()
|
|
const view = render(<Toast text="最多添加 50 张图片" icon={<svg data-testid="icon" />} onDone={onDone} />)
|
|
const banner = view.getByRole('alert')
|
|
expect(banner.textContent).toContain('最多添加 50 张图片')
|
|
expect(view.getByTestId('icon')).toBeTruthy()
|
|
vi.advanceTimersByTime(3999)
|
|
expect(onDone).not.toHaveBeenCalled()
|
|
vi.advanceTimersByTime(1)
|
|
expect(onDone).toHaveBeenCalledTimes(1)
|
|
} finally {
|
|
vi.useRealTimers()
|
|
}
|
|
})
|
|
|
|
it('centers over its anchor and re-measures on window resize', () => {
|
|
vi.useFakeTimers()
|
|
try {
|
|
const anchor = document.createElement('div')
|
|
document.body.appendChild(anchor)
|
|
anchor.getBoundingClientRect = () => ({ left: 100, width: 400 }) as DOMRect
|
|
const view = render(<Toast text="anchored" anchor={anchor} onDone={vi.fn()} />)
|
|
expect(view.getByRole('alert').style.left).toBe('300px')
|
|
anchor.getBoundingClientRect = () => ({ left: 200, width: 400 }) as DOMRect
|
|
fireEvent(window, new Event('resize'))
|
|
expect(view.getByRole('alert').style.left).toBe('400px')
|
|
anchor.remove()
|
|
} finally {
|
|
vi.useRealTimers()
|
|
}
|
|
})
|
|
|
|
it('renders without an icon and cancels its timer on unmount', () => {
|
|
vi.useFakeTimers()
|
|
try {
|
|
const onDone = vi.fn()
|
|
const view = render(<Toast text="plain" onDone={onDone} />)
|
|
expect(view.getByRole('alert').querySelector('[aria-hidden]')).toBeNull()
|
|
view.unmount()
|
|
vi.advanceTimersByTime(10_000)
|
|
expect(onDone).not.toHaveBeenCalled()
|
|
} finally {
|
|
vi.useRealTimers()
|
|
}
|
|
})
|
|
})
|