b8849b8c01
- 顶栏去掉两侧 flex-wrap、工具条改 overflow: hidden,空间不足按 data-layout 三档降级而不是换行 - 新增 workbenchToolbarModel(档位顺序与 1px 判据)与 useWorkbenchToolbarLayout(ResizeObserver 管宽度、MutationObserver 管内容,实测内容宽度写档位) - 版本入口 margin-left: auto 钉在动作区最右,标识改「版本 N(原因 · 时间)」,窄档只留「版本 N」(序号复用资源画布版本卡编号口径) - 新增 WorkbenchMoreActionsMenu:「打开项目目录 / 资源面板 / 整理画布」在 collapsed-actions 档收进「更多」下拉 - 删除独立「播放」按钮,运行页签前加 ▶ 图标,点页签即切视图并载入游戏资源 - 运行画面右下角新增「刷新运行画面」,LocalGamePreviewFrame 新增 reloadNonce 换 iframe 元素身份重新载入页面 - 用例:新增 workbenchToolbarLayout / runPreviewRefresh;改写 appSurface 顶栏与播放/刷新断言、版本标识断言与顶栏样式断言
129 lines
4.9 KiB
TypeScript
129 lines
4.9 KiB
TypeScript
import { describe, expect, test } from 'vitest';
|
||
|
||
import {
|
||
currentVersionResourceBindingIds,
|
||
formatIterationVersionDetail,
|
||
formatIterationVersionLabel,
|
||
formatIterationVersionTitle,
|
||
isResourceUsedByCurrentVersion,
|
||
} from '../src/features/resource-canvas/resourceCanvasVersionBindingModel';
|
||
|
||
/**
|
||
* 夹具必须与落盘口径一致:`versions[].createdAt` 是 **Unix 秒**。
|
||
*
|
||
* 写入侧见 `project/manifest.rs` 的 `created_at: unix_timestamp()`(`as_secs()`)。
|
||
* `1788075047` 取自真机项目 `What do u wanna do kitten` 的 `versions[0].createdAt`,
|
||
* 它被当成毫秒渲染时正好是 `1970/1/22 00:41:15`,即真机截图里的 1970 年。
|
||
*/
|
||
const versions = [
|
||
{
|
||
versionId: 'version-root',
|
||
parentVersionId: null,
|
||
projectRevision: 3,
|
||
resourceBindings: [
|
||
{ slotId: 'asset:asset-player', resourceId: 'asset-player' },
|
||
],
|
||
createdReason: 'initial' as const,
|
||
createdAt: 1_788_075_047,
|
||
},
|
||
{
|
||
versionId: 'version-child',
|
||
parentVersionId: 'version-root',
|
||
projectRevision: 4,
|
||
resourceBindings: [
|
||
{ slotId: 'asset:asset-town', resourceId: 'asset-town' },
|
||
// 悬空绑定的 slotId 不是恒等映射,不参与「当前使用」判定。
|
||
{ slotId: 'player', resourceId: 'asset-player' },
|
||
],
|
||
createdReason: 'agent-revision' as const,
|
||
createdAt: 1_788_075_104,
|
||
},
|
||
];
|
||
|
||
describe('运行模块版本绑定判定', () => {
|
||
test('falls back to the newest manifest version when no version is selected', () => {
|
||
expect(Array.from(currentVersionResourceBindingIds({ versions }))).toEqual([
|
||
'asset-town',
|
||
]);
|
||
});
|
||
|
||
test('resolves the binding ids of the explicitly selected version', () => {
|
||
expect(
|
||
Array.from(
|
||
currentVersionResourceBindingIds({ versions }, 'version-root'),
|
||
),
|
||
).toEqual(['asset-player']);
|
||
});
|
||
|
||
test('returns no bindings for a missing version or a manifest without versions', () => {
|
||
expect(
|
||
currentVersionResourceBindingIds({ versions }, 'version-missing').size,
|
||
).toBe(0);
|
||
expect(currentVersionResourceBindingIds({}).size).toBe(0);
|
||
expect(currentVersionResourceBindingIds({ versions: [] }).size).toBe(0);
|
||
});
|
||
|
||
test('marks only registered assets bound by the current version as in use', () => {
|
||
const bindingIds = currentVersionResourceBindingIds(
|
||
{ versions },
|
||
'version-root',
|
||
);
|
||
|
||
expect(
|
||
isResourceUsedByCurrentVersion(
|
||
{ manifestAssetId: 'asset-player' },
|
||
bindingIds,
|
||
),
|
||
).toBe(true);
|
||
expect(
|
||
isResourceUsedByCurrentVersion(
|
||
{ manifestAssetId: 'asset-town' },
|
||
bindingIds,
|
||
),
|
||
).toBe(false);
|
||
// 未登记为 manifest 素材的资源永远不算「当前使用」。
|
||
expect(
|
||
isResourceUsedByCurrentVersion({ manifestAssetId: null }, bindingIds),
|
||
).toBe(false);
|
||
});
|
||
|
||
test('labels a version with its Chinese created reason and creation time', () => {
|
||
// 完整标识=`版本 N(原因 · 时间)`;`N` 取 manifest 里的落盘顺序(与资源画布
|
||
// 「项目卡片」上的 `版本 N` 同一编号)。
|
||
expect(formatIterationVersionLabel(versions[0]!, 0)).toBe(
|
||
`版本 1(初始版本 · ${new Date(1_788_075_047 * 1000).toLocaleString('zh-CN')})`,
|
||
);
|
||
expect(formatIterationVersionLabel(versions[1]!, 1)).toBe(
|
||
`版本 2(智能体修订 · ${new Date(1_788_075_104 * 1000).toLocaleString('zh-CN')})`,
|
||
);
|
||
// 顶栏窄到只放得下序号时显示的就是 `formatIterationVersionTitle`,括号里那一截整段收起
|
||
// (样式按 `data-layout` 隐藏 `.game-run-version-trigger-detail`)。两半各自是独立口径。
|
||
expect(formatIterationVersionTitle(1)).toBe('版本 2');
|
||
expect(formatIterationVersionDetail(versions[1]!)).toBe(
|
||
`智能体修订 · ${new Date(1_788_075_104 * 1000).toLocaleString('zh-CN')}`,
|
||
);
|
||
});
|
||
|
||
test('把版本 createdAt 的 Unix 秒换算成毫秒,不再渲染成 1970 年', () => {
|
||
// 期望值按同一个本地时区口径算出,用例结果不随运行机器时区漂移。
|
||
// 1788075047 秒 = 2026-08-30T07:30:47Z。
|
||
const createdAtLocal = new Date(1_788_075_047 * 1000).toLocaleString(
|
||
'zh-CN',
|
||
);
|
||
const label = formatIterationVersionLabel(versions[0]!, 0);
|
||
expect(label).toBe(`版本 1(初始版本 · ${createdAtLocal})`);
|
||
|
||
// 年 / 月 / 日必须来自真实时刻,而不是 1970。这里用显式数值选项断言,
|
||
// 不依赖 `toLocaleString` 的具体排版(不同 ICU 版本会给出不同分隔符)。
|
||
expect(
|
||
new Intl.DateTimeFormat('zh-CN', {
|
||
year: 'numeric',
|
||
month: 'numeric',
|
||
day: 'numeric',
|
||
}).format(new Date(1_788_075_047 * 1000)),
|
||
).toBe('2026/8/30');
|
||
// 回归守卫:按毫秒解释同一个值会落到 1970/1/22,这正是真机截图里的错误显示。
|
||
expect(label).not.toContain('1970');
|
||
});
|
||
});
|