Files
Genarrative/src/components/image-editor/ImageCanvasBottomToolbarView.test.tsx
T
menghao f9847c8f15
Project CI / Repository checks (pull_request) Failing after 8s
Project CI / Backend tests (pull_request) Failing after 8s
Project CI / Frontend tests (pull_request) Successful in 2m55s
Project CI / Native shell tests (pull_request) Successful in 13m59s
统一主站画布共享视觉组件
新增共享画布按钮、工具栏、分组与分隔符及作用域样式
主站图片画布默认动作与底部工具栏改用共享实现
补齐 UI 对齐合同、决策记录和定向测试
2026-08-06 12:54:20 +08:00

168 lines
5.3 KiB
TypeScript

/* @vitest-environment jsdom */
import { render, screen, within } from '@testing-library/react';
import userEvent from '@testing-library/user-event';
import { createRef, type RefObject } from 'react';
import { describe, expect, it, vi } from 'vitest';
import { ImageCanvasBottomToolbarView } from './ImageCanvasBottomToolbarView';
describe('ImageCanvasBottomToolbarView', () => {
function renderToolbar(
overrides: Partial<Parameters<typeof ImageCanvasBottomToolbarView>[0]> = {},
) {
const switchTool = vi.fn();
const specToolWrapRef = createRef<HTMLSpanElement>();
const musicToolWrapRef = createRef<HTMLSpanElement>();
const publicationToolWrapRef = createRef<HTMLSpanElement>();
const view = render(
<ImageCanvasBottomToolbarView
specToolWrapRef={specToolWrapRef}
musicToolWrapRef={musicToolWrapRef}
publicationToolWrapRef={publicationToolWrapRef}
effectiveTool="generate"
onSwitchTool={switchTool}
onOpenToolOptions={vi.fn()}
onCloseToolOptions={vi.fn()}
{...overrides}
/>,
);
return {
...view,
toolbar: screen.getByRole('toolbar', { name: 'AI画布工具栏' }),
switchTool,
refs: {
specToolWrapRef,
musicToolWrapRef,
publicationToolWrapRef,
},
};
}
function renderToolbarView(
effectiveTool: Parameters<
typeof ImageCanvasBottomToolbarView
>[0]['effectiveTool'],
refs: {
specToolWrapRef: RefObject<HTMLSpanElement | null>;
musicToolWrapRef: RefObject<HTMLSpanElement | null>;
publicationToolWrapRef: RefObject<HTMLSpanElement | null>;
},
) {
return (
<ImageCanvasBottomToolbarView
specToolWrapRef={refs.specToolWrapRef}
musicToolWrapRef={refs.musicToolWrapRef}
publicationToolWrapRef={refs.publicationToolWrapRef}
effectiveTool={effectiveTool}
onSwitchTool={vi.fn()}
onOpenToolOptions={vi.fn()}
onCloseToolOptions={vi.fn()}
/>
);
}
it('renders the expected canvas tools and forwards user selections', async () => {
const user = userEvent.setup();
const { toolbar, switchTool } = renderToolbar();
expect(toolbar.className).toContain('genarrative-image-canvas__toolbar');
expect(toolbar.className).toContain(
'genarrative-image-canvas__toolbar--floating',
);
expect(
toolbar.querySelectorAll('.genarrative-image-canvas__toolbar-group'),
).toHaveLength(11);
expect(
toolbar.querySelectorAll('.genarrative-image-canvas__toolbar-divider'),
).toHaveLength(2);
const toolExpectations = [
['选择工具', 'select'],
['抓手工具', 'hand'],
['上传工具', 'upload'],
['生成图片', 'generate'],
['生成视频', 'video'],
['生成音乐', 'music'],
['生成规范', 'spec'],
['生成角色形象', 'character'],
['生成图标素材', 'icon'],
['生成UI设计图', 'ui-design'],
['宣发素材', 'publication'],
] as const;
for (const [label, tool] of toolExpectations) {
await user.click(within(toolbar).getByRole('button', { name: label }));
expect(switchTool).toHaveBeenLastCalledWith(tool);
}
expect(switchTool).toHaveBeenCalledTimes(toolExpectations.length);
});
it('only exposes persistent pressed state for navigation tools', () => {
const { toolbar, rerender, refs } = renderToolbar({
effectiveTool: 'select',
});
expect(
within(toolbar)
.getByRole('button', { name: '选择工具' })
.getAttribute('aria-pressed'),
).toBe('true');
expect(
within(toolbar)
.getByRole('button', { name: '生成图片' })
.getAttribute('aria-pressed'),
).toBe('false');
rerender(renderToolbarView('hand', refs));
expect(
within(toolbar)
.getByRole('button', { name: '抓手工具' })
.getAttribute('aria-pressed'),
).toBe('true');
rerender(renderToolbarView('music', refs));
expect(
within(toolbar)
.getByRole('button', { name: '生成音乐' })
.getAttribute('aria-pressed'),
).toBe('false');
});
it('opens and closes bottom option tools on pointer and keyboard focus', async () => {
const user = userEvent.setup();
const openToolOptions = vi.fn();
const closeToolOptions = vi.fn();
const { toolbar, refs } = renderToolbar({
onOpenToolOptions: openToolOptions,
onCloseToolOptions: closeToolOptions,
});
await user.hover(within(toolbar).getByRole('button', { name: '生成规范' }));
await user.unhover(
within(toolbar).getByRole('button', { name: '生成规范' }),
);
await user.hover(within(toolbar).getByRole('button', { name: '生成音乐' }));
await user.unhover(
within(toolbar).getByRole('button', { name: '生成音乐' }),
);
const publicationTool = within(toolbar).getByRole('button', {
name: '宣发素材',
});
publicationTool.focus();
publicationTool.blur();
expect(openToolOptions).toHaveBeenCalledWith('spec');
expect(closeToolOptions).toHaveBeenCalledWith('spec');
expect(openToolOptions).toHaveBeenCalledWith('music');
expect(closeToolOptions).toHaveBeenCalledWith('music');
expect(openToolOptions).toHaveBeenCalledWith('publication');
expect(closeToolOptions).toHaveBeenCalledWith('publication');
});
});