09ed7b099b
- 原缺陷:预览 Hook 的预取作用域 key 缺省退化为"被喂进来的资源集合签名",而宿主按设计要求恒传**全量投影**(传全量才不会让总览里其它栏目只剩栏目标题栏),换栏目/换视图一字不改 ⇒ key 不变 ⇒ 取消上一视图排队预取的 effect 永不执行,"同级裁决"一并退化为 no-op。已有用例用"改 resources"模拟切视图,测的是生产不存在的调用方形状。
- resourceCanvasController.ts 新增 createResourceCanvasPrefetchScopeKey({ view, target, mode }):由视图身份(main / child:<栏目>)加排序模式算出标识。
- index.tsx 在资源画布处显式算出该标识并传给预览 Hook(栏目取 resourceBookState.view / resourceBookState.category,排序取 sortMode)。
- useProjectResourceCardPreviews.ts 把 prefetchScopeKey 由可选改为**必填**并删掉那段兜底:它能"看起来还在工作",正是它掩盖了漏接线;现在漏传是类型错误而不是静默少取消一次。
- 断言:resourceCanvasController.test.ts 新增标识派生两组用例(换栏目/换视图/换排序模式必须变,同一视图必须稳定);useProjectResourceCardPreviews.test.ts 改写两条既有切视图用例为生产形状(resources 恒为全量、只改视图标识与 eagerResources),并在用例内前置断言"换栏目必须换标识";其余既有用例经用例侧包装补齐标识,形状与生产一致,断言一条未放宽。
- 变异验证(提交前已跑):把 createResourceCanvasPrefetchScopeKey 的栏目分量去掉(只留 view + mode)→ 3 条用例变红(标识派生 / 取消上一视图排队预取 / 取消后重新可见再入队),还原后复跑 34 passed。
- 门禁:`npx vitest run` 上述两文件 34 passed;typecheck 见随后整轮。
77 lines
2.5 KiB
TypeScript
77 lines
2.5 KiB
TypeScript
import { describe, expect, it } from 'vitest';
|
|
|
|
import {
|
|
createResourceCanvasPageCategorySignature,
|
|
createResourceCanvasPrefetchScopeKey,
|
|
} from '../src/view/project-development/resourceCanvasController';
|
|
|
|
describe('resource canvas page wheel controller', () => {
|
|
it('invalidates page categories for same-count category changes without reacting to resource order', () => {
|
|
const original = [{ category: 'document' }, { category: 'scene' }];
|
|
const reordered = [...original].reverse();
|
|
const changed = [{ category: 'document' }, { category: 'audio' }];
|
|
|
|
expect(createResourceCanvasPageCategorySignature(reordered)).toBe(
|
|
createResourceCanvasPageCategorySignature(original),
|
|
);
|
|
expect(createResourceCanvasPageCategorySignature(changed)).not.toBe(
|
|
createResourceCanvasPageCategorySignature(original),
|
|
);
|
|
});
|
|
});
|
|
|
|
describe('资源画布预取作用域标识', () => {
|
|
/**
|
|
* 标识必须由**视图身份**算出:喂给预览 Hook 的资源集合是全量投影,换栏目一字不改,
|
|
* 因此"资源 id 签名"做不到区分视图 —— 用它当标识会让取消上一视图排队预取永不触发。
|
|
*/
|
|
it('changes for every view identity change that a resource-set signature cannot see', () => {
|
|
const childScene = createResourceCanvasPrefetchScopeKey({
|
|
view: 'child',
|
|
target: 'scene',
|
|
mode: 'dependency',
|
|
});
|
|
|
|
// 换栏目:同一批全量资源下的两个不同视图,标识必须不同。
|
|
expect(
|
|
createResourceCanvasPrefetchScopeKey({
|
|
view: 'child',
|
|
target: 'character',
|
|
mode: 'dependency',
|
|
}),
|
|
).not.toBe(childScene);
|
|
// 换视图:栏目页 → 总览。
|
|
expect(
|
|
createResourceCanvasPrefetchScopeKey({
|
|
view: 'main',
|
|
target: null,
|
|
mode: 'dependency',
|
|
}),
|
|
).not.toBe(childScene);
|
|
// 同一栏目内换排序模式:卡片整体重排,对用户就是换了一次视图。
|
|
expect(
|
|
createResourceCanvasPrefetchScopeKey({
|
|
view: 'child',
|
|
target: 'scene',
|
|
mode: 'type',
|
|
}),
|
|
).not.toBe(childScene);
|
|
});
|
|
|
|
it('stays stable for an unchanged view so unrelated renders never cancel prefetches', () => {
|
|
expect(
|
|
createResourceCanvasPrefetchScopeKey({
|
|
view: 'child',
|
|
target: 'scene',
|
|
mode: 'dependency',
|
|
}),
|
|
).toBe(
|
|
createResourceCanvasPrefetchScopeKey({
|
|
view: 'child',
|
|
target: 'scene',
|
|
mode: 'dependency',
|
|
}),
|
|
);
|
|
});
|
|
});
|