2b1fc406a7
新增按界面图隔离的临时节点显示状态 接入 UI Tree 眼睛按钮与 Inspector 显示复选框 让两种预览模式按子树隐藏节点并清理失效状态 补充可见性测试与技术方案记录
167 lines
5.8 KiB
TypeScript
167 lines
5.8 KiB
TypeScript
// @vitest-environment jsdom
|
|
|
|
import {
|
|
act,
|
|
fireEvent,
|
|
render,
|
|
renderHook,
|
|
screen,
|
|
} from '@testing-library/react';
|
|
import { createElement, type ReactNode } from 'react';
|
|
import { describe, expect, it, vi } from 'vitest';
|
|
|
|
vi.mock('../src/components/AssetImporter', () => ({
|
|
AssetImporter: ({ open }: { open: boolean }) =>
|
|
open ? createElement('div', { role: 'dialog' }, '素材导入器') : null,
|
|
}));
|
|
|
|
vi.mock('../src/components/modal/ThemedModal', () => ({
|
|
ThemedModal: ({ open, children }: { open: boolean; children: ReactNode }) =>
|
|
open ? createElement('div', { role: 'dialog' }, children) : null,
|
|
}));
|
|
|
|
import UiEditorPage from '../src/view/ui-editor';
|
|
import { useUiEditorPage } from '../src/view/ui-editor/useUiEditorPage';
|
|
|
|
describe('UiEditorPage', () => {
|
|
it('renders split input tools over a real empty State', () => {
|
|
render(createElement(UiEditorPage, { projectPath: '/tmp/ui-editor' }));
|
|
|
|
expect(
|
|
screen.getByRole('navigation', { name: 'UI 编辑工具' }),
|
|
).toBeTruthy();
|
|
expect(screen.getByRole('button', { name: '导入界面图' })).toBeTruthy();
|
|
expect(screen.getByRole('button', { name: '导入独立素材' })).toBeTruthy();
|
|
expect(screen.getByRole('button', { name: '导入字体' })).toBeTruthy();
|
|
expect(screen.getByText('从界面图开始')).toBeTruthy();
|
|
expect(screen.queryByText('Pause Dialog')).toBeNull();
|
|
});
|
|
|
|
it('switches tools freely without inventing completed workflow state', () => {
|
|
render(createElement(UiEditorPage, { projectPath: '/tmp/ui-editor' }));
|
|
|
|
fireEvent.click(screen.getByRole('button', { name: /布局验收/ }));
|
|
expect(screen.getByRole('heading', { name: '布局验收' })).toBeTruthy();
|
|
expect(screen.getByText('当前界面尚无组件树。')).toBeTruthy();
|
|
|
|
fireEvent.click(screen.getByRole('button', { name: '检查前置数据' }));
|
|
expect(screen.getByText('还不能执行此操作')).toBeTruthy();
|
|
expect(screen.getByText('请先导入界面图')).toBeTruthy();
|
|
});
|
|
|
|
it('opens the design-image importer independently', () => {
|
|
render(createElement(UiEditorPage, { projectPath: '/tmp/ui-editor' }));
|
|
|
|
fireEvent.click(screen.getByRole('button', { name: '导入界面图' }));
|
|
expect(screen.getByText('素材导入器')).toBeTruthy();
|
|
});
|
|
|
|
it('switches from a selected node to the sprite inspector', () => {
|
|
const { result } = renderHook(() => useUiEditorPage('/tmp/ui-editor'));
|
|
act(() => {
|
|
result.current.editor.addDesignImages([
|
|
{
|
|
id: 'page',
|
|
image: {
|
|
metadata: {
|
|
name: 'Page',
|
|
description: '',
|
|
role: 'Page',
|
|
slave_to: null,
|
|
},
|
|
path: 'assets/page.png',
|
|
pixel_size: [320, 180],
|
|
pixels_per_unit: 1,
|
|
},
|
|
},
|
|
]);
|
|
result.current.editor.addSpriteAssets([
|
|
{
|
|
asset_id: 'panel',
|
|
metadata: { name: 'Panel', asset_type: '' },
|
|
path: 'assets/panel.png',
|
|
pixel_size: [32, 32],
|
|
pixels_per_unit: 1,
|
|
border: { left: 0, right: 0, top: 0, bottom: 0 },
|
|
},
|
|
]);
|
|
});
|
|
const rootId = result.current.editor.state.ui_trees[0]!.root.id;
|
|
act(() => {
|
|
result.current.selectDesignImage('page');
|
|
result.current.selectNode(rootId);
|
|
});
|
|
expect(result.current.selectedNodeId).toBe(rootId);
|
|
|
|
act(() => result.current.selectSprite('panel'));
|
|
expect(result.current.selectedNodeId).toBeNull();
|
|
expect(result.current.selectedSpriteId).toBe('panel');
|
|
});
|
|
|
|
it('keeps node preview visibility transient and scoped to each interface image', () => {
|
|
const { result } = renderHook(() => useUiEditorPage('/tmp/ui-editor'));
|
|
act(() => {
|
|
result.current.editor.addDesignImages([
|
|
{
|
|
id: 'page-a',
|
|
image: {
|
|
metadata: {
|
|
name: 'Page A',
|
|
description: '',
|
|
role: 'Page',
|
|
slave_to: null,
|
|
},
|
|
path: 'assets/page-a.png',
|
|
pixel_size: [320, 180],
|
|
pixels_per_unit: 1,
|
|
},
|
|
},
|
|
{
|
|
id: 'page-b',
|
|
image: {
|
|
metadata: {
|
|
name: 'Page B',
|
|
description: '',
|
|
role: 'Page',
|
|
slave_to: null,
|
|
},
|
|
path: 'assets/page-b.png',
|
|
pixel_size: [320, 180],
|
|
pixels_per_unit: 1,
|
|
},
|
|
},
|
|
]);
|
|
});
|
|
|
|
act(() => result.current.selectDesignImage('page-a'));
|
|
const rootId = result.current.treeForActiveImage!.root.id;
|
|
let insertedNodeId: string | undefined;
|
|
act(() => {
|
|
const inserted = result.current.insertNode(rootId);
|
|
if (inserted?.ok) insertedNodeId = inserted.value;
|
|
});
|
|
const stateBeforeToggle = JSON.stringify(result.current.editor.state);
|
|
act(() => {
|
|
result.current.toggleNodePreviewVisibility(rootId);
|
|
});
|
|
expect(result.current.isNodePreviewVisible(rootId)).toBe(false);
|
|
expect(result.current.hiddenNodeIds.has(rootId)).toBe(true);
|
|
expect(JSON.stringify(result.current.editor.state)).toBe(stateBeforeToggle);
|
|
|
|
act(() => result.current.selectDesignImage('page-b'));
|
|
expect(result.current.hiddenNodeIds.size).toBe(0);
|
|
|
|
act(() => result.current.selectDesignImage('page-a'));
|
|
expect(result.current.hiddenNodeIds.has(rootId)).toBe(true);
|
|
expect(insertedNodeId).toBeTruthy();
|
|
act(() => result.current.toggleNodePreviewVisibility(insertedNodeId!));
|
|
expect(result.current.hiddenNodeIds.has(insertedNodeId!)).toBe(true);
|
|
|
|
act(() => result.current.deleteNode(insertedNodeId!));
|
|
expect(result.current.hiddenNodeIds.has(insertedNodeId!)).toBe(false);
|
|
|
|
act(() => result.current.clearState());
|
|
expect(result.current.hiddenNodeIds.size).toBe(0);
|
|
});
|
|
});
|