29b99f213d
- index.tsx:整条删除 `ResourceBookOutlineNav` 组件(`.game-resource-outline` / `aria-label="资源栏目大纲"` / 栏目图标 + 栏目文字 + 未读红点)以及栏目页与「所有资源」页两处挂载点;`ResourceBookAllResourcesPage` 不再接收只服务导航的 `unreadCategories` / `onOpen`。 - index.tsx:一并清掉只服务这条导航的「未读红点」状态链(`unreadResourceCategoryState`、`resourceCategorySnapshotRef`、`resourceIdsByCategory`、`viewedResourceCategory` 与对应 effect),不留死代码、不留墓碑注释。 - styles.css:删除全部 `.game-resource-outline*` 规则(悬浮 Dock、常态/悬停态、图标列宽、`.game-resource-outline-label`、`.game-resource-outline-unread`,共 106 行)。 - 保留未动:分区栏目本身、栏目标题栏、资源卡、总览缩略卡片、第 0 张「所有资源」入口、「所有资源」汇总页、搜索、总览、快速编辑与选中工具条。 - 测试:切栏目 helper(appSurface/project-development、appSurface/home、resourceRename、resourceVersionSwitch)改为走「资源总览」缩略卡片(需要时先点「收起资源」回总览),不再点导航;栏目顺序断言改钉在总览缩略卡片的 `aria-label` 序列上(第 0 张「所有资源」+ 固定七栏目),断言内容未放宽。 - 测试:按「四不写」删除只守这条导航的断言——大纲常态可读性的 CSS 声明用例、大纲条目图标/文字的 DOM 用例(该用例保留其中与搜索浮层相关的断言并改名)、未读红点用例(该用例保留"只登记游戏代码的项目照常分页并落在待归类"的部分并改名)、空项目与「所有资源」页里的导航存在性断言。 - 测试:新增反向守卫 + 切栏目链路用例(`has no outline nav and keeps every section reachable through the remaining entries`):断言 `资源栏目大纲` 标签、`.game-resource-outline` 类名与对应 `<nav>` 都不再渲染,并逐条走通总览缩略卡片(含空栏目)→ 下一页 → 标题栏「资源总览」→ 「所有资源」入口往返。 - 测试:`preserves independent art viewports across sort and workbench mode switches` 保留全部原断言,只去掉排序模式切换后那些"点当前栏目"的空操作往返(旧导航点在同栏目上是提前返回,改 helper 后变成真实往返,会在 jsdom 里触发重新 fit)。
134 lines
4.0 KiB
TypeScript
134 lines
4.0 KiB
TypeScript
// @vitest-environment jsdom
|
|
import { afterEach, describe, expect, test, vi } from 'vitest';
|
|
|
|
import type { GameCreationAppManifest } from '../../../packages/shared/src/contracts/gameCreationApp';
|
|
import {
|
|
cleanup,
|
|
createGameCreationAppManifest,
|
|
createGameCreationAppSeedTasks,
|
|
ProjectDevelopmentView,
|
|
React,
|
|
render,
|
|
screen,
|
|
waitFor,
|
|
} from './appSurface/harness';
|
|
|
|
const PROJECT_PATH = '/tmp/workbench-code-only-project';
|
|
|
|
function installResourceCardIntersectionObserver() {
|
|
class ResourceCardIntersectionObserver {
|
|
readonly root = null;
|
|
readonly rootMargin = '160px';
|
|
readonly thresholds = [0];
|
|
readonly observed = new Set<Element>();
|
|
|
|
constructor(readonly callback: IntersectionObserverCallback) {}
|
|
|
|
observe(element: Element) {
|
|
this.observed.add(element);
|
|
}
|
|
|
|
unobserve(element: Element) {
|
|
this.observed.delete(element);
|
|
}
|
|
|
|
disconnect() {
|
|
this.observed.clear();
|
|
}
|
|
|
|
takeRecords() {
|
|
return [];
|
|
}
|
|
}
|
|
|
|
Object.defineProperty(window, 'IntersectionObserver', {
|
|
configurable: true,
|
|
value: ResourceCardIntersectionObserver,
|
|
});
|
|
}
|
|
|
|
/**
|
|
* 只登记游戏代码的项目:分区轴上没有代码栏目,代码资产(manifest 资产与已完成任务登记
|
|
* 的产物)按资产功能分类落入「待归类」。旧四栏目口径会把它们整体排除在可见栏目之外,
|
|
* 于是资源签名非空、画布已进入分页态,却四栏全空、一张卡都不显示。
|
|
*/
|
|
function createCodeOnlyManifest(): GameCreationAppManifest {
|
|
const manifest = createGameCreationAppManifest(
|
|
'workbench-code-only',
|
|
'只登记游戏代码的项目',
|
|
);
|
|
manifest.assets = [
|
|
{
|
|
id: 'asset-game-entry',
|
|
kind: 'code',
|
|
mediaType: 'text/html',
|
|
localPath: 'game/index.html',
|
|
source: { kind: 'generated' },
|
|
},
|
|
];
|
|
manifest.tasks = createGameCreationAppSeedTasks().map((task) =>
|
|
task.id === 'code-prototype'
|
|
? { ...task, status: 'completed' as const, artifacts: ['game/main.js'] }
|
|
: task,
|
|
);
|
|
return manifest;
|
|
}
|
|
|
|
function renderWorkbench(manifest: GameCreationAppManifest) {
|
|
return render(
|
|
React.createElement(ProjectDevelopmentView, {
|
|
projectName: manifest.name,
|
|
projectPath: PROJECT_PATH,
|
|
manifest,
|
|
attachments: [],
|
|
recentRunStatus: null,
|
|
recentRunStopReason: null,
|
|
supervisor: React.createElement('div', null, '项目总控'),
|
|
onHomeOpen: vi.fn(),
|
|
onProjectsOpen: vi.fn(),
|
|
onPlay: vi.fn(),
|
|
}),
|
|
);
|
|
}
|
|
|
|
describe('只登记游戏代码的项目分区', () => {
|
|
afterEach(() => {
|
|
cleanup();
|
|
});
|
|
|
|
test('代码资产落在待归类栏目并渲染出卡片,而不是四栏全空', async () => {
|
|
installResourceCardIntersectionObserver();
|
|
renderWorkbench(createCodeOnlyManifest());
|
|
|
|
// 左侧栏目大纲导航已删除:栏目清单改由「资源总览」缩略卡片承载。
|
|
await waitFor(() =>
|
|
expect(
|
|
document.querySelector('.game-resource-book-thumbnail'),
|
|
).not.toBeNull(),
|
|
);
|
|
const thumbnailLabels = Array.from(
|
|
document.querySelectorAll('.game-resource-book-thumbnail'),
|
|
(thumbnail) => thumbnail.getAttribute('aria-label'),
|
|
);
|
|
expect(thumbnailLabels).toContain('打开待归类');
|
|
// 项目版本仍然是末尾独立栏目,代码栏目不再存在。
|
|
expect(thumbnailLabels).toContain('打开项目版本');
|
|
expect(thumbnailLabels.some((label) => label?.includes('游戏代码'))).toBe(
|
|
false,
|
|
);
|
|
|
|
// 默认停留在顺序里第一个非空栏目,也就是代码资产所在的「待归类」。
|
|
expect(
|
|
await screen.findByRole('region', { name: '待归类' }),
|
|
).not.toBeNull();
|
|
|
|
// manifest 资产与已完成任务的代码产物都必须真的渲染成卡片。
|
|
await screen.findByRole('button', {
|
|
name: /选中资源:待归类 index\.html/,
|
|
});
|
|
await screen.findByRole('button', {
|
|
name: /选中资源:待归类 main\.js/,
|
|
});
|
|
});
|
|
});
|