补齐 UI 编辑器整树拖动的历史与保存落盘验收
- tests/uiEditorPage.test.ts 新增「整树拖动写回一个历史条目,保存后能原样读回」:断言 setTreeOffset 进历史、undo/redo 往返、save 落盘快照里的树偏移就是新值 - 变异验证:setTreeOffset 里 commit 换成 applyState(改状态不进历史)后只有该用例在 canUndo 处变红(1 failed / 37 passed) - 里程碑记录第 2 条已覆盖的两句与仍缺的一句(渲染层随 root 平移的断言方式),并引用既有 pointerup/取消不写回的用例
This commit is contained in:
@@ -1197,4 +1197,57 @@ describe('UiEditorPage', () => {
|
||||
act(() => hook.result.current.input.selectSprite('panel'));
|
||||
expect(hook.result.current.canvas.activeImageId).toBe('page-2');
|
||||
});
|
||||
it('整树拖动写回一个历史条目,保存后能原样读回', async () => {
|
||||
const save = vi
|
||||
.fn()
|
||||
.mockImplementation(async (_assetId, _revision, state: State) => ({
|
||||
status: 'saved' as const,
|
||||
state,
|
||||
revision: 1,
|
||||
committedProjectRevision: 1,
|
||||
}));
|
||||
const stateStore: IUiDesignStateStore = {
|
||||
load: vi.fn().mockResolvedValue({
|
||||
revision: 0,
|
||||
state: stateWithPages(['page-1']),
|
||||
}),
|
||||
save,
|
||||
generateCode: vi.fn().mockRejectedValue(new Error('测试未配置代码生成')),
|
||||
};
|
||||
const hook = renderHook(() =>
|
||||
useUiEditorSession('/tmp/ui-editor-tree-drag', 'ui-resource', stateStore),
|
||||
);
|
||||
await waitFor(() => expect(hook.result.current.save.isLoading).toBe(false));
|
||||
|
||||
const before = hook.result.current.canvas.uiTrees[0]?.root.offset;
|
||||
|
||||
// 拖动落点一次提交:状态更新并进入历史。
|
||||
act(() => {
|
||||
hook.result.current.canvas.setTreeOffset('page-1', [24, 36]);
|
||||
});
|
||||
expect(hook.result.current.canvas.uiTrees[0]?.root.offset?.min).toEqual([
|
||||
24, 36,
|
||||
]);
|
||||
expect(hook.result.current.history.canUndo).toBe(true);
|
||||
|
||||
// 撤销回到拖动前,重做再回到拖动后。
|
||||
act(() => {
|
||||
hook.result.current.history.undo();
|
||||
});
|
||||
expect(hook.result.current.canvas.uiTrees[0]?.root.offset).toEqual(before);
|
||||
act(() => {
|
||||
hook.result.current.history.redo();
|
||||
});
|
||||
expect(hook.result.current.canvas.uiTrees[0]?.root.offset?.min).toEqual([
|
||||
24, 36,
|
||||
]);
|
||||
|
||||
// 保存把新的树偏移写进快照(下一次加载读回的就是这份 state)。
|
||||
await act(async () => {
|
||||
await hook.result.current.save.save();
|
||||
});
|
||||
expect(save).toHaveBeenCalledTimes(1);
|
||||
const savedState = save.mock.calls[0]?.[2] as State;
|
||||
expect(savedState.ui_trees[0]?.root.offset?.min).toEqual([24, 36]);
|
||||
});
|
||||
});
|
||||
|
||||
@@ -52,7 +52,8 @@ UI 编辑器预览同时展示全部界面树。每棵树使用 root.offset 的
|
||||
- 三个独立开关在 `view/ui-editor/index.tsx` 里渲染:`显示框线` / `显示原图` / `显示组件`。
|
||||
- 定向用例:`npx vitest run tests/uiEditorPreview.test.tsx tests/previewWorkspaceZoom.test.tsx tests/previewRightPanGesture.test.ts tests/uiEditorPage.test.ts --root apps/ai-game-creator-shell` → **59 passed / 0 failed**。
|
||||
- 本轮补齐的验收项(第 1 条):新增 `tests/uiEditorState.test.ts` 的「新树默认落在所有现有树右侧并与最小 top 对齐」——直接跑 `createTree` 入口:第一棵落在 `[0,0]`,第二棵落在「第一棵右边界 + `UI_TREE_PADDING`」且 top 与现有最小 top 对齐;把第二棵整体下移后,第三棵仍然按**最小** top(而不是最后一棵的 top)对齐,横向取所有树最右边界。变异验证:把 `deriveTreeOffset` 的 `minY` 改成取最后一棵树的 top 后,只有这条用例变红(1 failed / 17 passed),改回后恢复绿;本轮四套 UI 编辑器用例合计 **72 passed**。
|
||||
- 仍未覆盖的 1 条验收项(不是运行时项,缺的是自动化用例):
|
||||
- 本轮补齐的验收项(第 2 条的两句):`tests/uiEditorPage.test.ts` 新增「整树拖动写回一个历史条目,保存后能原样读回」——`canvas.setTreeOffset('page-1', [24, 36])` 后断言状态更新且 `history.canUndo` 为真,`undo()` 回到拖动前的 `offset`、`redo()` 再回到 `[24, 36]`,最后 `save.save()` 落盘并断言写入快照的 `state.ui_trees[0].root.offset.min` 就是新值(下一次加载读回的就是这份 state)。变异验证:把 `setTreeOffset` 里的 `commit(result.state)` 换成 `applyState(result.state)`(改状态但不进历史)后,只有这条用例在 `canUndo` 处变红(1 failed / 37 passed)。「拖动结束才写回一次、指针取消不写」由既有 `previewRightPanDrag.test.tsx` 的 `commits the tree offset on pointerup but not on pointercancel` 与 `drops an in-flight tree drag when the window loses focus` 覆盖。
|
||||
- 第 2 条还剩一句未单独断言:「原图与组件随 root 一起移动」。缺口很具体——jsdom 里 `getBoundingClientRect` 全为 0,不能直接比绝对坐标;要靠断言树层容器的 `style.left/top` 等于 `root.offset.min`、且根节点是该容器的子节点(从而原图与全部子节点一起随 root 平移)来钉住。
|
||||
- 「原图与组件随 root 一起移动,拖动结束进入 undo/redo 并可保存恢复」:现有 `uiEditorState.test.ts` 只覆盖 `set-tree-offset` 命令本身,没有断言预览渲染跟随 root 位移与拖动落点进 undo/redo、保存后能读回。
|
||||
- 本轮补齐的验收项(第 3 条):在同一条 `shows final-preview node frames and resize handles only when requested` 里补上 root 断言——选中 root 时一个 `/调整控制点$/` 都不出现,紧接着的对照是选中 child 时恰好 8 个。变异验证:把 handle 渲染条件里的 `!isRoot &&` 去掉后只有这一处断言变红(1 failed / 10 passed,且证明 `selectedNodeId=root` 确实进入了选中态、断言不是空转),改回后恢复绿。
|
||||
- 本轮补齐的验收项(第 4 条):`tests/uiEditorPage.test.ts` 新增「默认显示框线与原图、隐藏组件,且三个开关互相独立」——渲染真实 `UiEditorPage` 后按标签取三个复选框,断言初始值 `显示框线=true` / `显示原图=true` / `显示组件=false`,再分别关框线、开组件,确认互不影响。变异验证:把 `view/ui-editor/index.tsx` 的 `showFrame` 初值改成 `false` 后只有这条用例变红(1 failed / 35 passed),改回后四套用例合计 **73 passed**。
|
||||
|
||||
Reference in New Issue
Block a user