Files
Genarrative/apps/ai-game-creator-shell/tests/resourceCanvasGenerationEntry.test.tsx
T
suzmii 0035d16efa AGC 资源画布新增生成素材浮层与生成入口模型
- 新增 resourceCanvasGenerationModel:按本地契约锁定无源生成放行的视频 / 音效 / 背景音乐三类,提供 create:<operationId> 源标识、入口渲染门禁与请求身份工厂
- 新增 ResourceCanvasGenerationPanelView:独立浮层面板承载类型选择、草稿与失败重试,失败后锁定原请求并复用同一 operationId / idempotencyKey
- 浮层只呈现三类真实可用类型,不出现图片专属的模型 / 比例 / 尺寸 / 像素艺术与泥点价
- resourceCanvasChrome.css 补浮层宿主样式
- 新增 resourceCanvasGenerationEntry 定向测试 6 条
2026-09-10 16:37:39 +08:00

138 lines
4.6 KiB
TypeScript

// @vitest-environment jsdom
import {
cleanup,
render,
screen,
waitFor,
within,
} from '@testing-library/react';
import userEvent from '@testing-library/user-event';
import { afterEach, describe, expect, test, vi } from 'vitest';
import {
isResourceCanvasGenerationAvailable,
RESOURCE_CANVAS_GENERATION_OPTIONS,
resourceCanvasGenerationSourceId,
} from '../src/features/resource-canvas/resourceCanvasGenerationModel';
import { ResourceCanvasGenerationPanelView } from '../src/features/resource-canvas/ResourceCanvasGenerationPanelView';
afterEach(() => {
cleanup();
});
describe('resourceCanvasGenerationModel', () => {
test('只放行本地契约支持无源生成的三类素材', () => {
expect(RESOURCE_CANVAS_GENERATION_OPTIONS.map((item) => item.kind)).toEqual(
['video', 'sound-effect', 'background-music'],
);
expect(
RESOURCE_CANVAS_GENERATION_OPTIONS.map((item) => item.editKind),
).toEqual(['video', 'sound-effect', 'background-music']);
});
test('没有客户端桥或项目未就绪时生成能力不成立', () => {
expect(
isResourceCanvasGenerationAvailable({
hasRuntimeInvoke: false,
projectPath: '/tmp/project',
projectId: 'project-1',
}),
).toBe(false);
expect(
isResourceCanvasGenerationAvailable({
hasRuntimeInvoke: true,
projectPath: ' ',
projectId: 'project-1',
}),
).toBe(false);
expect(
isResourceCanvasGenerationAvailable({
hasRuntimeInvoke: true,
projectPath: '/tmp/project',
projectId: '',
}),
).toBe(false);
expect(
isResourceCanvasGenerationAvailable({
hasRuntimeInvoke: true,
projectPath: '/tmp/project',
projectId: 'project-1',
}),
).toBe(true);
});
test('无源生成的稳定源标识挂在 operationId 上', () => {
expect(resourceCanvasGenerationSourceId(' op-1 ')).toBe('create:op-1');
});
});
describe('ResourceCanvasGenerationPanelView', () => {
test('只呈现三类可无源生成的素材,也不展示图片专属选项或泥点价', () => {
render(
<ResourceCanvasGenerationPanelView
onSubmit={async () => undefined}
onClose={() => undefined}
/>,
);
const panel = screen.getByRole('dialog', { name: '生成素材' });
for (const label of ['视频', '音效', '背景音乐']) {
expect(within(panel).getByRole('button', { name: label })).not.toBeNull();
}
expect(panel.textContent).not.toContain('泥点');
expect(panel.textContent).not.toContain('像素艺术');
expect(panel.textContent).not.toContain('图片');
});
test('失败后锁定原请求并用同一 operationId 与幂等键重试', async () => {
const user = userEvent.setup();
const onSubmit = vi
.fn<(input: unknown) => Promise<void>>()
.mockRejectedValueOnce(new Error('result-unknown: 测试网络中断'))
.mockResolvedValueOnce(undefined);
render(
<ResourceCanvasGenerationPanelView
onSubmit={onSubmit}
onClose={() => undefined}
/>,
);
const prompt = screen.getByLabelText('生成提示词');
const assetName = screen.getByLabelText('素材名称');
expect((assetName as HTMLInputElement).value).toBe('新视频');
await user.type(prompt, '一段片头动画,镜头缓慢推进');
await user.click(screen.getByRole('button', { name: '生成视频' }));
expect((await screen.findByRole('alert')).textContent).toContain(
'result-unknown: 测试网络中断',
);
expect((prompt as HTMLTextAreaElement).disabled).toBe(true);
expect((assetName as HTMLInputElement).disabled).toBe(true);
await user.click(screen.getByRole('button', { name: '使用原请求重试' }));
await waitFor(() => expect(onSubmit).toHaveBeenCalledTimes(2));
expect(onSubmit.mock.calls[0]?.[0]).toEqual(onSubmit.mock.calls[1]?.[0]);
expect(onSubmit.mock.calls[0]?.[0]).toMatchObject({
kind: 'video',
prompt: '一段片头动画,镜头缓慢推进',
assetName: '新视频',
});
});
test('切换类型后默认名称与提交文案跟随类型', async () => {
const user = userEvent.setup();
render(
<ResourceCanvasGenerationPanelView
onSubmit={async () => undefined}
onClose={() => undefined}
/>,
);
await user.click(screen.getByRole('button', { name: '背景音乐' }));
expect((screen.getByLabelText('素材名称') as HTMLInputElement).value).toBe(
'新背景音乐',
);
expect(screen.getByRole('button', { name: '生成背景音乐' })).not.toBeNull();
});
});