mirror of
https://mirror.skon.top/github.com/langgenius/dify.git
synced 2026-04-30 17:50:29 +08:00
test(dify-ui): disable base ui animations globally (#35467)
This commit is contained in:
@@ -90,6 +90,22 @@ See `[web/docs/overlay-migration.md](../../web/docs/overlay-migration.md)` for t
|
||||
- `pnpm -C packages/dify-ui storybook` — Storybook on the default port. Each primitive has `index.stories.tsx`.
|
||||
- `pnpm -C packages/dify-ui type-check` — `tsgo --noEmit` for this package only.
|
||||
|
||||
### Disabling Animations In Tests
|
||||
|
||||
Base UI can wait for `element.getAnimations()` to finish before it unmounts overlays, panels, and transition-driven components. Browser-based test runners can make that timing unstable, especially when tests assert final DOM state rather than animation behavior.
|
||||
|
||||
Set the Base UI test flag in a Vitest setup file to skip those waits:
|
||||
|
||||
```ts
|
||||
(
|
||||
globalThis as typeof globalThis & {
|
||||
BASE_UI_ANIMATIONS_DISABLED: boolean
|
||||
}
|
||||
).BASE_UI_ANIMATIONS_DISABLED = true
|
||||
```
|
||||
|
||||
`packages/dify-ui/vitest.setup.ts` already applies this for primitive tests.
|
||||
|
||||
See `[AGENTS.md](./AGENTS.md)` for:
|
||||
|
||||
- Component authoring rules (one-component-per-folder, `cva` + `cn`, relative imports inside the package, subpath imports from consumers).
|
||||
|
||||
@@ -3,19 +3,20 @@ import { toast, ToastHost } from '../index'
|
||||
|
||||
const asHTMLElement = (element: HTMLElement | SVGElement) => element as HTMLElement
|
||||
|
||||
declare global {
|
||||
// eslint-disable-next-line vars-on-top
|
||||
var BASE_UI_ANIMATIONS_DISABLED: boolean | undefined
|
||||
const dispatchToastMouseOver = (element: HTMLElement | SVGElement) => {
|
||||
element.dispatchEvent(new MouseEvent('mouseover', {
|
||||
bubbles: true,
|
||||
}))
|
||||
}
|
||||
|
||||
const dispatchToastMouseOut = (element: HTMLElement | SVGElement) => {
|
||||
element.dispatchEvent(new MouseEvent('mouseout', {
|
||||
bubbles: true,
|
||||
relatedTarget: document.body,
|
||||
}))
|
||||
}
|
||||
|
||||
describe('@langgenius/dify-ui/toast', () => {
|
||||
beforeAll(() => {
|
||||
// Base UI waits for `requestAnimationFrame` + `getAnimations().finished`
|
||||
// before unmounting a toast. Fake timers can't reliably drive that path,
|
||||
// so short-circuit it to keep auto-dismiss assertions deterministic in CI.
|
||||
globalThis.BASE_UI_ANIMATIONS_DISABLED = true
|
||||
})
|
||||
|
||||
beforeEach(() => {
|
||||
vi.clearAllMocks()
|
||||
vi.useFakeTimers()
|
||||
@@ -28,10 +29,6 @@ describe('@langgenius/dify-ui/toast', () => {
|
||||
vi.useRealTimers()
|
||||
})
|
||||
|
||||
afterAll(() => {
|
||||
globalThis.BASE_UI_ANIMATIONS_DISABLED = undefined
|
||||
})
|
||||
|
||||
it('should render a success toast when called through the typed shortcut', async () => {
|
||||
const screen = await render(<ToastHost />)
|
||||
|
||||
@@ -62,13 +59,13 @@ describe('@langgenius/dify-ui/toast', () => {
|
||||
expect(document.body.querySelectorAll('[role="dialog"]')).toHaveLength(3)
|
||||
expect(document.body.querySelectorAll('button[aria-label="Close notification"][aria-hidden="true"]')).toHaveLength(3)
|
||||
|
||||
screen.getByRole('region', { name: 'Notifications' }).element().dispatchEvent(new MouseEvent('mouseover', {
|
||||
bubbles: true,
|
||||
}))
|
||||
const viewport = screen.getByRole('region', { name: 'Notifications' }).element()
|
||||
dispatchToastMouseOver(viewport)
|
||||
|
||||
await vi.waitFor(() => {
|
||||
expect(document.body.querySelector('button[aria-label="Close notification"][aria-hidden="true"]')).not.toBeInTheDocument()
|
||||
})
|
||||
dispatchToastMouseOut(viewport)
|
||||
})
|
||||
|
||||
it('should render a neutral toast when called directly', async () => {
|
||||
@@ -115,11 +112,11 @@ describe('@langgenius/dify-ui/toast', () => {
|
||||
onClose,
|
||||
})
|
||||
|
||||
screen.getByRole('region', { name: 'Notifications' }).element().dispatchEvent(new MouseEvent('mouseover', {
|
||||
bubbles: true,
|
||||
}))
|
||||
const viewport = screen.getByRole('region', { name: 'Notifications' }).element()
|
||||
dispatchToastMouseOver(viewport)
|
||||
|
||||
await expect.element(screen.getByRole('button', { name: 'Close notification' })).toBeInTheDocument()
|
||||
dispatchToastMouseOut(viewport)
|
||||
asHTMLElement(screen.getByRole('button', { name: 'Close notification' }).element()).click()
|
||||
|
||||
await vi.waitFor(() => {
|
||||
@@ -128,21 +125,6 @@ describe('@langgenius/dify-ui/toast', () => {
|
||||
expect(onClose).toHaveBeenCalledTimes(1)
|
||||
})
|
||||
|
||||
it('should auto dismiss toasts with the Base UI default timeout', async () => {
|
||||
const screen = await render(<ToastHost />)
|
||||
|
||||
toast('Default timeout')
|
||||
await expect.element(screen.getByText('Default timeout')).toBeInTheDocument()
|
||||
|
||||
await vi.advanceTimersByTimeAsync(4999)
|
||||
expect(document.body).toHaveTextContent('Default timeout')
|
||||
|
||||
await vi.advanceTimersByTimeAsync(1)
|
||||
await vi.waitFor(() => {
|
||||
expect(document.body).not.toHaveTextContent('Default timeout')
|
||||
})
|
||||
})
|
||||
|
||||
it('should respect the host timeout configuration', async () => {
|
||||
const screen = await render(<ToastHost timeout={3000} />)
|
||||
|
||||
|
||||
@@ -11,6 +11,7 @@ export default defineConfig({
|
||||
},
|
||||
test: {
|
||||
globals: true,
|
||||
setupFiles: ['./vitest.setup.ts'],
|
||||
browser: {
|
||||
enabled: true,
|
||||
provider: playwright(),
|
||||
|
||||
5
packages/dify-ui/vitest.setup.ts
Normal file
5
packages/dify-ui/vitest.setup.ts
Normal file
@@ -0,0 +1,5 @@
|
||||
(
|
||||
globalThis as typeof globalThis & {
|
||||
BASE_UI_ANIMATIONS_DISABLED: boolean
|
||||
}
|
||||
).BASE_UI_ANIMATIONS_DISABLED = true
|
||||
Reference in New Issue
Block a user