/** @vitest-environment jsdom */ import { act, cleanup, render, screen } from '@testing-library/react'; import { afterEach, describe, expect, it, vi } from 'vitest'; import { RunNoticeToast } from '../src/features/app-shell/RunNoticeToast'; const RUN_MESSAGE = '运行通过,已载入客户端运行视图'; function toastElement() { return document.querySelector('[data-project-run-notice-toast="true"]'); } describe('运行 / 预览浮层提示', () => { afterEach(() => { cleanup(); vi.useRealTimers(); }); it('没有提示时不渲染任何浮层', () => { render(); expect(toastElement()).toBeNull(); expect(screen.queryByText(RUN_MESSAGE)).toBeNull(); }); it('在浮层里显示提示,并在到点后自动收起', () => { vi.useFakeTimers(); const onDismiss = vi.fn(); render( , ); expect(toastElement()).not.toBeNull(); expect(screen.getByText(RUN_MESSAGE)).not.toBeNull(); act(() => { vi.advanceTimersByTime(2_600); }); expect(onDismiss).toHaveBeenCalledTimes(1); }); it('同一句文案连续触发时重新计时', () => { vi.useFakeTimers(); const onDismiss = vi.fn(); const { rerender } = render( , ); rerender( , ); act(() => { vi.advanceTimersByTime(2_000); }); expect(onDismiss).not.toHaveBeenCalled(); act(() => { vi.advanceTimersByTime(600); }); expect(onDismiss).toHaveBeenCalledTimes(1); }); it('失败提示用错误色调,不需要调用方再拼一套文案', () => { render( , ); const toast = toastElement()?.querySelector( '.platform-runtime-status-toast', ); // `PlatformRuntimeStatusToast` 的失败态就是 alert + assertive,成功态是 status + polite。 expect(toast?.getAttribute('role')).toBe('alert'); expect( screen.getByText('在浏览器打开失败:permission denied'), ).not.toBeNull(); }); it('失败提示比成功提示留得久:2.6 秒不该把错误收走', () => { vi.useFakeTimers(); const onDismiss = vi.fn(); render( , ); act(() => { vi.advanceTimersByTime(2_600); }); expect(onDismiss).not.toHaveBeenCalled(); act(() => { vi.advanceTimersByTime(3_400); }); expect(onDismiss).toHaveBeenCalledTimes(1); }); });