Files
Genarrative/apps/ai-game-creator-shell/tests/templateLibraryModel.test.ts
T
kdletters e072c66ce9
Project CI / AI game creator shell Rust lane 1/2 (push) Has been cancelled
Project CI / AI game creator shell Rust lane 2/2 (push) Has been cancelled
Project CI / Backend tests (push) Has been cancelled
Project CI / Native shell tests (push) Has been cancelled
Project CI / Frontend tests (push) Has been cancelled
Project CI / Repository checks (push) Has been cancelled
Project CI / AI game creator shell Rust crates (push) Has been cancelled
Project CI / AI game creator shell web tests (push) Has been cancelled
Project CI / AI game creator shell Rust smoke (push) Has been cancelled
修复 AGC 模板库筛选与卡片状态的表现缺陷
- 模板库筛选区改用共享筛选条与标签 chip:选中为实心品牌填充 + 反白文字,未选为浅底描边
- 未选中悬停不再借用品牌色(品牌色专属已选中),状态阶梯固定为静止 / 悬停 / 按下 / 选中
- 新增 getPlatformCategoryChipClassName 收敛筛选 chip 类名口径,模板库、资源画布、参考图弹窗与平台 Web 端共用
- PlatformSegmentedTabs 增加 accent 选中口径,与筛选 chip 共用 --platform-chip-* 语义色
- 修复卡片文字区按 grid auto 行排版导致的标题 / 简介 / 标签被裁:行高契约拆为分项常量,文字区固定 174
- 修复忙状态下动作行塞入第三个元素导致的按钮文案换行顶出卡片:忙状态写在触发它的按钮上
- 修复经典滚动条占宽导致的卡片区横向滚动条:按竖滚动条预留列宽后再算列宽行高
- 焦点环由 15% 透明度改为实心色并用 outline 绘制,选中项聚焦不再被状态投影盖掉
- 封面加载失败改为隐藏图片留中性底色,不再出现浏览器裂图图标
- 补充状态对比度、行高契约、滚动条预留、封面兜底与忙状态文案的回归测试
- 同步技术方案、踩坑记录与决策记录的口径
2026-09-23 16:51:15 +08:00

203 lines
6.1 KiB
TypeScript

import { describe, expect, it } from 'vitest';
import {
collectGameTemplateRuntimes,
collectGameTemplateTagOptions,
collectGameTemplateTags,
EMPTY_TEMPLATE_LIBRARY_FILTERS,
filterGameTemplates,
formatGameTemplateSize,
type GameTemplateEntry,
isTemplateLibraryFiltersEmpty,
needsTemplateDownload,
templateMatchesQuery,
templateRuntimeLabel,
toggleGameTemplateTag,
} from '../src/features/template-library/templateLibraryModel';
function template(
overrides: Partial<GameTemplateEntry> & Pick<GameTemplateEntry, 'id'>,
): GameTemplateEntry {
return {
title: '未命名模板',
summary: '',
tags: [],
runtime: 'html',
engine: 'phaser',
engineVersion: '4.2.1',
templateVersion: '1.0.0',
updatedAt: '2026-09-17T00:00:00Z',
entry: 'game/index.html',
zipUrl:
'https://agc-dev.oss-rg-china-mainland.aliyuncs.com/templates/v1/demo/template.zip',
zipSizeBytes: 2048,
zipSha256: 'a'.repeat(64),
coverUrl:
'https://agc-dev.oss-rg-china-mainland.aliyuncs.com/templates/v1/demo/cover.png',
coverWidth: 960,
coverHeight: 540,
installed: false,
installedVersion: null,
installedAtMillis: null,
...overrides,
};
}
const matchThree = template({
id: 'match-3',
title: '三消经营',
summary: '三消与模拟经营的融合模板',
tags: ['三消', '经营'],
engine: 'phaser',
installed: true,
installedVersion: '1.0.0',
});
const pixelFarm = template({
id: 'pixel-farm',
title: '像素农场',
summary: '像素风种植玩法',
tags: ['经营', '像素'],
engine: 'godot',
runtime: 'godot',
templateVersion: '2.0.0',
installed: true,
installedVersion: '1.0.0',
});
const spaceShooter = template({
id: 'space-shooter',
title: '太空射击',
summary: '纵版弹幕射击',
tags: ['射击'],
runtime: 'unity',
engine: 'unity',
installed: false,
});
const templates: GameTemplateEntry[] = [matchThree, pixelFarm, spaceShooter];
describe('templateMatchesQuery', () => {
it('matches title, summary, tags and engine case-insensitively', () => {
expect(templateMatchesQuery(matchThree, '三消')).toBe(true);
expect(templateMatchesQuery(matchThree, '经营')).toBe(true);
expect(templateMatchesQuery(matchThree, 'PHASER')).toBe(true);
expect(templateMatchesQuery(matchThree, '弹幕')).toBe(false);
});
it('requires every whitespace separated term to match', () => {
expect(templateMatchesQuery(pixelFarm, '像素 种植')).toBe(true);
expect(templateMatchesQuery(pixelFarm, '像素 弹幕')).toBe(false);
expect(templateMatchesQuery(pixelFarm, ' ')).toBe(true);
});
});
describe('filterGameTemplates', () => {
it('filters by tag, runtime, query and installed state together', () => {
expect(
filterGameTemplates(templates, {
...EMPTY_TEMPLATE_LIBRARY_FILTERS,
tags: ['经营'],
}).map((entry) => entry.id),
).toEqual(['match-3', 'pixel-farm']);
expect(
filterGameTemplates(templates, {
...EMPTY_TEMPLATE_LIBRARY_FILTERS,
runtime: 'godot',
}).map((entry) => entry.id),
).toEqual(['pixel-farm']);
expect(
filterGameTemplates(templates, {
...EMPTY_TEMPLATE_LIBRARY_FILTERS,
installedOnly: true,
query: '经营',
tags: ['像素'],
}).map((entry) => entry.id),
).toEqual(['pixel-farm']);
});
it('returns everything when no filter is active', () => {
expect(
filterGameTemplates(templates, EMPTY_TEMPLATE_LIBRARY_FILTERS),
).toHaveLength(3);
expect(isTemplateLibraryFiltersEmpty(EMPTY_TEMPLATE_LIBRARY_FILTERS)).toBe(
true,
);
expect(
isTemplateLibraryFiltersEmpty({
...EMPTY_TEMPLATE_LIBRARY_FILTERS,
query: ' x ',
}),
).toBe(false);
});
});
describe('tag and runtime options', () => {
it('orders tags by frequency and drops blanks', () => {
const withBlank = [
...templates,
template({ id: 'blank-tag', tags: ['', ' ', '经营'] }),
];
expect(collectGameTemplateTags(withBlank)).toEqual([
'经营',
'三消',
'射击',
'像素',
]);
});
it('keeps the same order while reporting how many templates carry each tag', () => {
// 筛选条上的标签 chip 要显示命中数量,顺序必须与 `collectGameTemplateTags` 完全一致。
const withBlank = [
...templates,
template({ id: 'blank-tag', tags: ['', ' ', '经营'] }),
];
const options = collectGameTemplateTagOptions(withBlank);
expect(options).toEqual([
{ tag: '经营', assetCount: 3 },
{ tag: '三消', assetCount: 1 },
{ tag: '射击', assetCount: 1 },
{ tag: '像素', assetCount: 1 },
]);
expect(options.map((option) => option.tag)).toEqual(
collectGameTemplateTags(withBlank),
);
});
it('collects distinct runtimes and labels them', () => {
expect(collectGameTemplateRuntimes(templates)).toEqual([
'godot',
'html',
'unity',
]);
expect(templateRuntimeLabel('html')).toBe('网页');
expect(templateRuntimeLabel('cocos')).toBe('Cocos');
expect(templateRuntimeLabel('')).toBe('未标注运行时');
expect(templateRuntimeLabel('custom-engine')).toBe('custom-engine');
});
it('toggles tags without mutating the previous filters', () => {
const next = toggleGameTemplateTag(EMPTY_TEMPLATE_LIBRARY_FILTERS, '经营');
expect(next.tags).toEqual(['经营']);
expect(toggleGameTemplateTag(next, '经营').tags).toEqual([]);
expect(EMPTY_TEMPLATE_LIBRARY_FILTERS.tags).toEqual([]);
});
});
describe('needsTemplateDownload', () => {
it('requires a download when missing or when the installed version is stale', () => {
expect(needsTemplateDownload(matchThree)).toBe(false);
expect(needsTemplateDownload(pixelFarm)).toBe(true);
expect(needsTemplateDownload(spaceShooter)).toBe(true);
});
});
describe('formatGameTemplateSize', () => {
it('formats bytes, kilobytes and megabytes', () => {
expect(formatGameTemplateSize(0)).toBe('--');
expect(formatGameTemplateSize(512)).toBe('512 B');
expect(formatGameTemplateSize(2048)).toBe('2.0 KB');
expect(formatGameTemplateSize(5 * 1024 * 1024)).toBe('5.0 MB');
});
});