6dde321bd1
Project CI / AI game creator shell Rust smoke (pull_request) Successful in 2m5s
Project CI / Backend tests (pull_request) Failing after 11s
Project CI / AI game creator shell Rust crates (pull_request) Successful in 1m13s
Project CI / Frontend tests (pull_request) Successful in 3m15s
Project CI / Repository checks (pull_request) Failing after 15s
Project CI / AI game creator shell Rust lane 1/2 (pull_request) Successful in 7m56s
Project CI / AI game creator shell Rust lane 2/2 (pull_request) Successful in 7m55s
Project CI / AI game creator shell web tests (pull_request) Failing after 3m21s
Project CI / Native shell tests (pull_request) Successful in 8m53s
- executeRunLocal 的三条失败路径(缺 Tauri / 缺项目 / 启动预览报错)也改走 onRunNotice 失败色提示,对话区不再出现过程行;随之删除已无调用方的 announceProjectChatMessage 与不再有意义的 announceToChat 参数 - RunNoticeToast 的失败提示延长到 6 秒(成功仍 2.6 秒),避免错误一闪而过 - 版本入口在 UI 编辑器壳里不渲染;版本名改由一层 span 承载省略号,长版本名不再被硬裁 - 视图拿不到 onNotice 时把「在浏览器打开失败」写进 console,不再静默吞掉 - check-native-shells 增加「运行页视图里 openUrl( 只有一个调用点」的判据,堵住自动开浏览器的口子 - 新增外壳级接线用例 runNoticeShellWiring;previewActivation 补「启动失败走失败色提示且不写对话区」;runNoticeToast 补失败时长;样式守卫补省略号 - decision-log 收敛到最终口径(成功与失败都出对话区、失败留 6 秒、编辑器不挂版本入口)
121 lines
3.0 KiB
TypeScript
121 lines
3.0 KiB
TypeScript
/** @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(<RunNoticeToast notice={null} onDismiss={vi.fn()} />);
|
|
|
|
expect(toastElement()).toBeNull();
|
|
expect(screen.queryByText(RUN_MESSAGE)).toBeNull();
|
|
});
|
|
|
|
it('在浮层里显示提示,并在到点后自动收起', () => {
|
|
vi.useFakeTimers();
|
|
const onDismiss = vi.fn();
|
|
|
|
render(
|
|
<RunNoticeToast
|
|
notice={{ id: 1, message: RUN_MESSAGE }}
|
|
onDismiss={onDismiss}
|
|
/>,
|
|
);
|
|
|
|
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(
|
|
<RunNoticeToast
|
|
notice={{ id: 1, message: RUN_MESSAGE }}
|
|
onDismiss={onDismiss}
|
|
/>,
|
|
);
|
|
|
|
rerender(
|
|
<RunNoticeToast
|
|
notice={{ id: 2, message: RUN_MESSAGE }}
|
|
onDismiss={onDismiss}
|
|
/>,
|
|
);
|
|
act(() => {
|
|
vi.advanceTimersByTime(2_000);
|
|
});
|
|
expect(onDismiss).not.toHaveBeenCalled();
|
|
|
|
act(() => {
|
|
vi.advanceTimersByTime(600);
|
|
});
|
|
expect(onDismiss).toHaveBeenCalledTimes(1);
|
|
});
|
|
|
|
it('失败提示用错误色调,不需要调用方再拼一套文案', () => {
|
|
render(
|
|
<RunNoticeToast
|
|
notice={{
|
|
id: 1,
|
|
tone: 'error',
|
|
message: '在浏览器打开失败:permission denied',
|
|
}}
|
|
onDismiss={vi.fn()}
|
|
/>,
|
|
);
|
|
|
|
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(
|
|
<RunNoticeToast
|
|
notice={{
|
|
id: 1,
|
|
tone: 'error',
|
|
message: '运行游戏失败:预览端口被占用',
|
|
}}
|
|
onDismiss={onDismiss}
|
|
/>,
|
|
);
|
|
|
|
act(() => {
|
|
vi.advanceTimersByTime(2_600);
|
|
});
|
|
expect(onDismiss).not.toHaveBeenCalled();
|
|
|
|
act(() => {
|
|
vi.advanceTimersByTime(3_400);
|
|
});
|
|
expect(onDismiss).toHaveBeenCalledTimes(1);
|
|
});
|
|
});
|