修复UI编辑器合并结果持久化
UI 资源路由接入真实 Tauri State store 传递项目身份并修复保存命令调用 增加合并后加载保存回归测试
This commit is contained in:
@@ -2993,6 +2993,7 @@ export default function ProjectDevelopmentView({
|
||||
<UiEditorPage
|
||||
projectPath={projectPath}
|
||||
resourceId={uiEditorRoute.resourceId}
|
||||
projectId={manifest.projectId}
|
||||
resourceLabel={uiEditorRoute.resourceLabel}
|
||||
onBack={() => setUiEditorRoute(null)}
|
||||
/>
|
||||
|
||||
@@ -1,8 +1,9 @@
|
||||
import { ChevronLeft } from 'lucide-react';
|
||||
import { useEffect, useRef, useState } from 'react';
|
||||
import { useEffect, useMemo, useRef, useState } from 'react';
|
||||
|
||||
import { ThemedModal } from '../../components/modal/ThemedModal';
|
||||
import {
|
||||
createTauriUiDesignStateStore,
|
||||
mockUiDesignStateStore,
|
||||
type UiDesignStateStore,
|
||||
} from '../../features/ui-editor/uiDesignStateStore';
|
||||
@@ -21,17 +22,27 @@ import {
|
||||
export default function UiEditorPage({
|
||||
projectPath,
|
||||
resourceId,
|
||||
projectId,
|
||||
resourceLabel,
|
||||
onBack,
|
||||
stateStore = mockUiDesignStateStore,
|
||||
stateStore,
|
||||
}: {
|
||||
projectPath: string;
|
||||
resourceId?: string;
|
||||
projectId?: string;
|
||||
resourceLabel?: string;
|
||||
onBack?: () => void;
|
||||
stateStore?: UiDesignStateStore;
|
||||
}) {
|
||||
const controller = useUiEditorPage(projectPath, resourceId, stateStore);
|
||||
const resolvedStore = useMemo(
|
||||
() =>
|
||||
stateStore ??
|
||||
(resourceId && projectId
|
||||
? createTauriUiDesignStateStore(projectPath, projectId)
|
||||
: mockUiDesignStateStore),
|
||||
[projectId, projectPath, resourceId, stateStore],
|
||||
);
|
||||
const controller = useUiEditorPage(projectPath, resourceId, resolvedStore);
|
||||
const [savedStateSignature, setSavedStateSignature] = useState<string | null>(
|
||||
null,
|
||||
);
|
||||
@@ -73,7 +84,7 @@ export default function UiEditorPage({
|
||||
const snapshot = structuredClone(controller.editor.state);
|
||||
const snapshotSignature = JSON.stringify(snapshot);
|
||||
try {
|
||||
const result = await stateStore.save(
|
||||
const result = await resolvedStore.save(
|
||||
resourceId,
|
||||
controller.persistedRevision,
|
||||
snapshot,
|
||||
|
||||
@@ -6,10 +6,13 @@ import {
|
||||
render,
|
||||
renderHook,
|
||||
screen,
|
||||
waitFor,
|
||||
} from '@testing-library/react';
|
||||
import { createElement, type ReactNode } from 'react';
|
||||
import { describe, expect, it, vi } from 'vitest';
|
||||
|
||||
vi.mock('@tauri-apps/api/core', () => ({ invoke: vi.fn() }));
|
||||
|
||||
vi.mock('../src/components/AssetImporter', () => ({
|
||||
AssetImporter: ({ open }: { open: boolean }) =>
|
||||
open ? createElement('div', { role: 'dialog' }, '素材导入器') : null,
|
||||
@@ -20,19 +23,81 @@ vi.mock('../src/components/modal/ThemedModal', () => ({
|
||||
open ? createElement('div', { role: 'dialog' }, children) : null,
|
||||
}));
|
||||
|
||||
import { invoke } from '@tauri-apps/api/core';
|
||||
|
||||
import UiEditorPage from '../src/view/ui-editor';
|
||||
import { useUiEditorPage } from '../src/view/ui-editor/useUiEditorPage';
|
||||
|
||||
describe('UiEditorPage', () => {
|
||||
it('uses the durable Tauri store for a project UI resource', async () => {
|
||||
vi.mocked(invoke)
|
||||
.mockResolvedValueOnce({
|
||||
revision: 0,
|
||||
state: {
|
||||
ui_trees: [],
|
||||
ui_design_images: {},
|
||||
sprite_assets: {},
|
||||
font_assets: {},
|
||||
},
|
||||
})
|
||||
.mockResolvedValueOnce({
|
||||
status: 'saved',
|
||||
state: {
|
||||
ui_trees: [],
|
||||
ui_design_images: {},
|
||||
sprite_assets: {},
|
||||
font_assets: {},
|
||||
},
|
||||
revision: 1,
|
||||
committedProjectRevision: 1,
|
||||
});
|
||||
|
||||
render(
|
||||
createElement(UiEditorPage, {
|
||||
projectPath: '/tmp/ui-editor-project',
|
||||
projectId: 'local-project-draft',
|
||||
resourceId: 'generated-ui-1',
|
||||
} as never),
|
||||
);
|
||||
|
||||
await waitFor(() => {
|
||||
expect(invoke).toHaveBeenCalledWith('load_ui_design_state', {
|
||||
input: {
|
||||
projectPath: '/tmp/ui-editor-project',
|
||||
expectedProjectId: 'local-project-draft',
|
||||
assetId: 'generated-ui-1',
|
||||
},
|
||||
});
|
||||
});
|
||||
|
||||
fireEvent.click(screen.getByRole('button', { name: '保存' }));
|
||||
fireEvent.click(screen.getByRole('button', { name: '仍然保存' }));
|
||||
await waitFor(() => {
|
||||
expect(invoke).toHaveBeenCalledWith('save_ui_design_state', {
|
||||
input: {
|
||||
projectPath: '/tmp/ui-editor-project',
|
||||
expectedProjectId: 'local-project-draft',
|
||||
assetId: 'generated-ui-1',
|
||||
expectedRevision: 0,
|
||||
state: {
|
||||
ui_trees: [],
|
||||
ui_design_images: {},
|
||||
sprite_assets: {},
|
||||
font_assets: {},
|
||||
},
|
||||
},
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
it('renders split input tools over a real empty State', () => {
|
||||
render(createElement(UiEditorPage, { projectPath: '/tmp/ui-editor' }));
|
||||
|
||||
expect(
|
||||
screen.getByRole('navigation', { name: 'UI 编辑工具' }),
|
||||
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();
|
||||
});
|
||||
@@ -40,13 +105,8 @@ describe('UiEditorPage', () => {
|
||||
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();
|
||||
fireEvent.click(screen.getByRole('button', { name: /绑定视觉素材/ }));
|
||||
expect(screen.getByRole('heading', { name: '检查发现问题' })).toBeTruthy();
|
||||
});
|
||||
|
||||
it('opens the design-image importer independently', () => {
|
||||
|
||||
Reference in New Issue
Block a user