让状态不变量校验先判字段类型再取值
src/features/ui-editor/stateInvariants.ts 在取 path.trim 与 pixel_size.every 前先判 string / Array.isArray,残缺持久化文档此前会在 firstUiDesignInvariantMessage 里抛 TypeError,被保存流程吞成通用失败文案 tests/uiEditorState.test.ts 补三组残缺界面图都回落到 invalid-image 的用例
This commit is contained in:
@@ -14,8 +14,12 @@ export function validateUiDesignState(state: State): UiDesignInvariantIssue[] {
|
||||
const issues: UiDesignInvariantIssue[] = [];
|
||||
const nodeIds = new Set<string>();
|
||||
for (const [id, image] of Object.entries(state.ui_design_images)) {
|
||||
// 这个 seam 专门接住可能残缺的持久化 State,取字段前先判类型,别把 TypeError
|
||||
// 漏给调用方,否则保存失败只会退化成通用文案。
|
||||
if (
|
||||
typeof image.path !== 'string' ||
|
||||
image.path.trim() === '' ||
|
||||
!Array.isArray(image.pixel_size) ||
|
||||
!image.pixel_size.every((value) => Number.isFinite(value) && value > 0) ||
|
||||
!Number.isFinite(image.pixels_per_unit) ||
|
||||
image.pixels_per_unit <= 0
|
||||
|
||||
@@ -163,6 +163,22 @@ describe('useUiEditorState', () => {
|
||||
]);
|
||||
});
|
||||
|
||||
it('reports malformed images instead of throwing', () => {
|
||||
const state = structuredClone(EMPTY_UI_EDITOR_STATE);
|
||||
for (const malformed of [
|
||||
{ pixel_size: [100, 80], pixels_per_unit: 1 },
|
||||
{ path: 'assets/page.png', pixels_per_unit: 1 },
|
||||
{ path: 'assets/page.png', pixel_size: [0, 80], pixels_per_unit: 1 },
|
||||
]) {
|
||||
state.ui_design_images = {
|
||||
page: malformed as unknown as UIDesignImage,
|
||||
};
|
||||
expect(validateUiDesignState(state)).toEqual([
|
||||
expect.objectContaining({ code: 'invalid-image' }),
|
||||
]);
|
||||
}
|
||||
});
|
||||
|
||||
it('moves a subtree between UI trees without changing its transform', () => {
|
||||
const moved = nodeWithSprite('moved');
|
||||
const initial: State = {
|
||||
|
||||
Reference in New Issue
Block a user