Files
Genarrative/src/components/image-editor/useCanvasGenerationDialogs.test.tsx
T
kdletters 387a1c26e3 完成全量检查整改
清理已跟踪原始日志与个人本地配置
修复前后端有效门禁、测试和构建问题
补齐生产 Jenkins、SpacetimeDB 本地命令与文档约束
收紧图片编辑器状态、附件与媒体引用测试
排除已下线旧创作入口测试并清理 warning
2026-07-17 16:56:46 +08:00

284 lines
7.9 KiB
TypeScript

/* @vitest-environment jsdom */
import { act,renderHook } from '@testing-library/react';
import { describe, expect, it, vi } from 'vitest';
import type { CanvasGenerationDialogState } from './ImageCanvasEditorTypes';
import { useCanvasGenerationDialogs } from './useCanvasGenerationDialogs';
function createDialog(
mode: CanvasGenerationDialogState['mode'],
prompt: string,
): Omit<CanvasGenerationDialogState, 'id'> {
return {
mode,
prompt,
status: 'idle',
composerOpen: true,
placeholder: {
x: mode === 'character' ? 30 : 10,
y: mode === 'character' ? 40 : 20,
width: 320,
height: 240,
originalWidth: 320,
originalHeight: 240,
},
};
}
describe('useCanvasGenerationDialogs', () => {
it('archives, activates, updates, and removes canvas generation dialogs', () => {
const onActivate = vi.fn();
const { result } = renderHook(() =>
useCanvasGenerationDialogs({ onActivate }),
);
act(() => {
result.current.openCanvasGenerationDialog(
createDialog('generate', 'first'),
);
});
expect(result.current.activeCanvasGenerationDialog).toEqual(
expect.objectContaining({
prompt: 'first',
composerOpen: true,
}),
);
act(() => {
result.current.openCanvasGenerationDialog(
createDialog('character', 'second'),
);
});
expect(result.current.activeCanvasGenerationDialog).toEqual(
expect.objectContaining({
prompt: 'second',
composerOpen: true,
}),
);
expect(result.current.inactiveGenerateDialogs).toEqual([
expect.objectContaining({
prompt: 'first',
composerOpen: false,
}),
]);
expect(
result.current.canvasGenerationDialogs.map((dialog) => dialog.prompt),
).toEqual(['first', 'second']);
const archivedFirstDialog = result.current.inactiveGenerateDialogs[0];
expect(archivedFirstDialog).toBeDefined();
act(() => {
result.current.activateCanvasGenerationDialog(archivedFirstDialog!);
});
expect(onActivate).toHaveBeenCalledTimes(1);
expect(result.current.activeCanvasGenerationDialog).toEqual(
expect.objectContaining({
prompt: 'first',
composerOpen: true,
}),
);
expect(result.current.inactiveGenerateDialogs).toEqual([
expect.objectContaining({
prompt: 'second',
composerOpen: false,
}),
]);
const activeDialog = result.current.activeCanvasGenerationDialog;
expect(activeDialog).toBeTruthy();
act(() => {
result.current.updateCanvasGenerationDialogById(
activeDialog!.id,
(dialog) => ({
...dialog,
status: 'generating',
placeholder: dialog.placeholder
? {
...dialog.placeholder,
x: 99,
}
: dialog.placeholder,
}),
);
});
expect(
result.current.getGeneratingDialogPlaceholder(activeDialog!)?.x,
).toBe(99);
act(() => {
result.current.updateCanvasGenerationDialogById(
activeDialog!.id,
(dialog) => ({
...dialog,
sourceLayerId: 'layer-a',
}),
);
});
act(() => {
result.current.removeCanvasGenerationDialogsByLayerId('layer-a');
});
expect(result.current.activeCanvasGenerationDialog).toBeNull();
expect(result.current.inactiveGenerateDialogs).toEqual([
expect.objectContaining({
prompt: 'second',
}),
]);
const archivedSecondDialog = result.current.inactiveGenerateDialogs[0];
expect(archivedSecondDialog).toBeDefined();
act(() => {
result.current.activateCanvasGenerationDialog(archivedSecondDialog!);
});
act(() => {
result.current.removeCanvasGenerationDialogById(archivedSecondDialog!.id);
});
expect(result.current.activeCanvasGenerationDialog).toBeNull();
expect(result.current.inactiveGenerateDialogs).toEqual([]);
});
it('restores saved dialogs and continues ids after the saved maximum', () => {
const { result } = renderHook(() => useCanvasGenerationDialogs());
act(() => {
result.current.restoreCanvasGenerationDialogs([
{
id: 'generation-dialog-12',
mode: 'generate',
prompt: 'restored',
status: 'idle',
composerOpen: true,
placeholder: {
x: 12,
y: 24,
width: 320,
height: 240,
originalWidth: 320,
originalHeight: 240,
},
},
]);
});
expect(result.current.activeCanvasGenerationDialog).toEqual(
expect.objectContaining({
id: 'generation-dialog-12',
prompt: 'restored',
}),
);
let nextDialogId = '';
act(() => {
nextDialogId = result.current.openCanvasGenerationDialog(
createDialog('character', 'second'),
);
});
expect(nextDialogId).toBe('generation-dialog-13');
expect(result.current.inactiveGenerateDialogs).toEqual([
expect.objectContaining({
id: 'generation-dialog-12',
prompt: 'restored',
composerOpen: false,
}),
]);
expect(result.current.activeCanvasGenerationDialog).toEqual(
expect.objectContaining({
id: 'generation-dialog-13',
prompt: 'second',
}),
);
});
it('keeps the saved open dialog active when restoring multiple dialogs', () => {
const { result } = renderHook(() => useCanvasGenerationDialogs());
act(() => {
result.current.restoreCanvasGenerationDialogs([
{
id: 'generation-dialog-2',
mode: 'generate',
prompt: 'inactive saved',
status: 'idle',
composerOpen: false,
},
{
id: 'generation-dialog-3',
mode: 'character',
prompt: 'active saved',
status: 'idle',
composerOpen: true,
},
]);
});
expect(result.current.activeCanvasGenerationDialog).toEqual(
expect.objectContaining({
id: 'generation-dialog-3',
prompt: 'active saved',
composerOpen: true,
}),
);
expect(result.current.inactiveGenerateDialogs).toEqual([
expect.objectContaining({
id: 'generation-dialog-2',
prompt: 'inactive saved',
composerOpen: false,
}),
]);
});
it('keeps refs in sync when an archived dialog is activated and immediately updated', () => {
const { result } = renderHook(() => useCanvasGenerationDialogs());
act(() => {
result.current.openCanvasGenerationDialog(
createDialog('generate', 'first'),
);
});
act(() => {
result.current.openCanvasGenerationDialog(
createDialog('character', 'second'),
);
});
const archivedFirstDialog = result.current.inactiveGenerateDialogs[0];
expect(archivedFirstDialog).toEqual(
expect.objectContaining({ prompt: 'first' }),
);
let activeDialogRefId: string | undefined;
act(() => {
result.current.activateCanvasGenerationDialog(archivedFirstDialog!);
activeDialogRefId = result.current.generateDialogRef.current?.id;
result.current.updateCanvasGenerationDialogById(
archivedFirstDialog!.id,
(dialog) => ({
...dialog,
placeholder: dialog.placeholder
? {
...dialog.placeholder,
x: 128,
}
: dialog.placeholder,
}),
);
});
expect(activeDialogRefId).toBe(archivedFirstDialog!.id);
expect(result.current.activeCanvasGenerationDialog).toEqual(
expect.objectContaining({
id: archivedFirstDialog!.id,
prompt: 'first',
composerOpen: true,
placeholder: expect.objectContaining({ x: 128 }),
}),
);
expect(result.current.inactiveGenerateDialogs).toEqual([
expect.objectContaining({
prompt: 'second',
composerOpen: false,
}),
]);
});
});