Files
Genarrative/src/components/image-editor/ImageCanvasTopbarView.test.tsx
kdletters a319d2ae0d 拆分图片画布顶部栏视图
新增 ImageCanvasTopbarView 承载返回项目入口和项目标题区域

新增顶栏视图测试覆盖重命名和导出入口

精简 ImageCanvasEditorView 的顶部栏 JSX

更新图片画布拆分计划和 TRACKING 验证记录
2026-06-17 13:48:48 +08:00

148 lines
4.5 KiB
TypeScript

/* @vitest-environment jsdom */
import { fireEvent, render, screen } from '@testing-library/react';
import { describe, expect, it, vi } from 'vitest';
import type { CanvasLayer } from './ImageCanvasEditorTypes';
import { ImageCanvasTopbarView } from './ImageCanvasTopbarView';
function createLayer(overrides: Partial<CanvasLayer> = {}): CanvasLayer {
const id = overrides.id ?? 'layer-a';
return {
id,
resourceId: `resource-${id}`,
title: id,
src: `data:image/png;base64,${id}`,
x: 0,
y: 0,
width: 320,
height: 180,
originalWidth: 320,
originalHeight: 180,
zIndex: 1,
sourceType: 'uploaded',
...overrides,
};
}
function renderTopbar(
overrides: Partial<
Parameters<typeof ImageCanvasTopbarView>[0]
> = {},
) {
const props: Parameters<typeof ImageCanvasTopbarView>[0] = {
projectId: 'project-a',
projectTitle: '默认项目',
projectRenameValue: '默认项目',
isRenamingProject: false,
isProjectRenameSaving: false,
projectRenameError: null,
layers: [],
assetExportStatus: null,
isExportingAssets: false,
setProjectRenameValue: vi.fn(),
startProjectRename: vi.fn(),
cancelProjectRename: vi.fn(),
submitProjectRename: vi.fn(),
resetProjectRenameError: vi.fn(),
exportCanvasAssets: vi.fn(),
...overrides,
};
render(<ImageCanvasTopbarView {...props} />);
return props;
}
describe('ImageCanvasTopbarView', () => {
it('shows the project title and project gallery link', () => {
const props = renderTopbar();
expect(screen.getByRole('heading', { name: '默认项目' })).toBeTruthy();
expect(
screen.getByRole('link', { name: '返回项目页面' }).getAttribute('href'),
).toBe('/project');
fireEvent.click(screen.getByRole('button', { name: '编辑项目名称' }));
expect(props.startProjectRename).toHaveBeenCalledTimes(1);
});
it('submits, resets, and cancels project rename edits', () => {
const props = renderTopbar({
isRenamingProject: true,
projectRenameValue: '草稿项目',
projectRenameError: '项目名称不能为空',
});
const input = screen.getByLabelText('项目名称');
fireEvent.change(input, { target: { value: '新项目' } });
fireEvent.click(screen.getByRole('button', { name: '保存项目名称' }));
expect(props.setProjectRenameValue).toHaveBeenCalledWith('新项目');
expect(props.resetProjectRenameError).toHaveBeenCalledTimes(1);
expect(props.submitProjectRename).toHaveBeenCalledWith('project-a');
expect(screen.getByRole('alert').textContent).toBe('项目名称不能为空');
fireEvent.keyDown(input, { key: 'Escape' });
expect(props.cancelProjectRename).toHaveBeenCalledTimes(1);
});
it('exports only when canvas has exportable layers and shows export status', () => {
const exportCanvasAssets = vi.fn();
const { rerender } = render(
<ImageCanvasTopbarView
projectId="project-a"
projectTitle="默认项目"
projectRenameValue="默认项目"
isRenamingProject={false}
isProjectRenameSaving={false}
projectRenameError={null}
layers={[]}
assetExportStatus={null}
isExportingAssets={false}
setProjectRenameValue={vi.fn()}
startProjectRename={vi.fn()}
cancelProjectRename={vi.fn()}
submitProjectRename={vi.fn()}
resetProjectRenameError={vi.fn()}
exportCanvasAssets={exportCanvasAssets}
/>,
);
expect(
(screen.getByRole('button', { name: '下载画布素材' }) as HTMLButtonElement)
.disabled,
).toBe(true);
rerender(
<ImageCanvasTopbarView
projectId="project-a"
projectTitle="默认项目"
projectRenameValue="默认项目"
isRenamingProject={false}
isProjectRenameSaving={false}
projectRenameError={null}
layers={[createLayer()]}
assetExportStatus={{
tone: 'success',
message: '画布素材已导出',
}}
isExportingAssets={false}
setProjectRenameValue={vi.fn()}
startProjectRename={vi.fn()}
cancelProjectRename={vi.fn()}
submitProjectRename={vi.fn()}
resetProjectRenameError={vi.fn()}
exportCanvasAssets={exportCanvasAssets}
/>,
);
fireEvent.click(screen.getByRole('button', { name: '下载画布素材' }));
expect(exportCanvasAssets).toHaveBeenCalledTimes(1);
expect(screen.getByRole('status').textContent).toBe('画布素材已导出');
});
});