Files
Genarrative/src/components/rpg-entry/rpgEntryWorldPresentation.test.ts

365 lines
12 KiB
TypeScript

import { expect, test } from 'vitest';
import {
buildPlatformWorldDisplayTags,
buildPuzzleWorkCoverSlides,
EDUTAINMENT_BABY_OBJECT_MATCH_TEMPLATE_ID,
EDUTAINMENT_BABY_OBJECT_MATCH_TEMPLATE_NAME,
formatPlatformWorkDisplayName,
formatPlatformWorkDisplayTags,
formatPlatformWorldTime,
isBarkBattleGalleryEntry,
isEdutainmentGalleryEntry,
isVisualNovelGalleryEntry,
isWoodenFishGalleryEntry,
mapBabyObjectMatchDraftToPlatformGalleryCard,
mapBarkBattleWorkToPlatformGalleryCard,
mapVisualNovelWorkToPlatformGalleryCard,
mapWoodenFishWorkToPlatformGalleryCard,
type PlatformEdutainmentGalleryCard,
type PlatformPuzzleGalleryCard,
resolvePlatformPublicWorkCode,
resolvePlatformWorldFallbackCoverImage,
} from './rpgEntryWorldPresentation';
test('formatPlatformWorldTime formats backend seconds timestamp text as date', () => {
expect(formatPlatformWorldTime('1777110165.990127Z')).toBe('2026-04-25');
});
test('formatPlatformWorldTime keeps full year for iso date strings', () => {
expect(formatPlatformWorldTime('2026-04-25T12:00:00.000Z')).toBe(
'2026-04-25',
);
});
test('formatPlatformWorldTime uses utc calendar date for zulu time', () => {
expect(formatPlatformWorldTime('2026-04-25T00:30:00.000Z')).toBe(
'2026-04-25',
);
});
test('formatPlatformWorldTime keeps fallback text for invalid values', () => {
expect(formatPlatformWorldTime(null)).toBe('未发布');
expect(formatPlatformWorldTime('not-a-date')).toBe('not-a-date');
});
test('platform work display text limits names and tags by character count', () => {
expect(formatPlatformWorkDisplayName('热门高分拼图超长标题')).toBe(
'热门高分拼图超长',
);
expect(
formatPlatformWorkDisplayTags(['超长机关标签', '星桥', '超长机关标签']),
).toEqual(['超长机关', '星桥']);
});
test('platform public cards use play type reference images as cover fallback', () => {
const puzzleCard: PlatformPuzzleGalleryCard = {
sourceType: 'puzzle',
workId: 'puzzle-work-1',
profileId: 'puzzle-profile-1',
publicWorkCode: 'PZ-PUZZLE1',
ownerUserId: 'user-1',
authorDisplayName: '玩家',
worldName: '机关拼图',
subtitle: '拼图关卡',
summaryText: '公开作品',
coverImageSrc: '/generated-puzzle-assets/session/cover/image.png',
themeTags: ['拼图'],
playCount: 1,
remixCount: 0,
likeCount: 0,
visibility: 'published',
publishedAt: '2026-05-18T00:00:00.000Z',
updatedAt: '2026-05-18T00:00:00.000Z',
};
expect(resolvePlatformWorldFallbackCoverImage(puzzleCard)).toBe(
'/creation-type-references/puzzle.webp',
);
});
test('buildPuzzleWorkCoverSlides prefers each level formal image', () => {
const slides = buildPuzzleWorkCoverSlides({
workId: 'work-1',
profileId: 'profile-1',
ownerUserId: 'user-1',
authorDisplayName: '玩家',
levelName: '第一关',
summary: '拼图摘要',
themeTags: ['拼图'],
coverImageSrc: '/cover.png',
publicationStatus: 'published',
updatedAt: '2026-04-25T00:00:00.000Z',
publishedAt: '2026-04-25T00:00:00.000Z',
publishReady: true,
levels: [
{
levelId: 'level-1',
levelName: '石桥',
pictureDescription: '石桥画面',
selectedCandidateId: 'candidate-2',
coverImageSrc: '/level-1-cover.png',
coverAssetId: null,
generationStatus: 'ready',
candidates: [
{
candidateId: 'candidate-1',
imageSrc: '/level-1-a.png',
assetId: 'asset-1',
prompt: '',
sourceType: 'generated',
selected: false,
},
{
candidateId: 'candidate-2',
imageSrc: '/level-1-b.png',
assetId: 'asset-2',
prompt: '',
sourceType: 'generated',
selected: false,
},
],
},
{
levelId: 'level-2',
levelName: '星港',
pictureDescription: '星港画面',
selectedCandidateId: null,
coverImageSrc: '/level-2-cover.png',
coverAssetId: null,
generationStatus: 'ready',
candidates: [],
},
],
});
expect(slides).toEqual([
{
id: 'level-1',
imageSrc: '/level-1-b.png',
label: '石桥',
},
{
id: 'level-2',
imageSrc: '/level-2-cover.png',
label: '星港',
},
]);
});
test('maps visual novel work to platform gallery card with VN public code', () => {
const card = mapVisualNovelWorkToPlatformGalleryCard({
runtimeKind: 'visual-novel',
profileId: 'vn-profile-demo-12345678',
ownerUserId: 'user-1',
title: '雨夜终章',
description: '失踪列车上的选择。',
coverImageSrc: '/vn-cover.png',
tags: ['悬疑', '列车'],
publishStatus: 'published',
publishReady: true,
playCount: 7,
updatedAt: '2026-05-07T00:00:00.000Z',
publishedAt: '2026-05-07T00:00:00.000Z',
});
expect(isVisualNovelGalleryEntry(card)).toBe(true);
expect(card.publicWorkCode).toBe('VN-12345678');
expect(resolvePlatformPublicWorkCode(card)).toBe('VN-12345678');
expect(buildPlatformWorldDisplayTags(card, 2)).toEqual(['悬疑', '列车']);
});
test('maps wooden fish work to platform gallery card with WF public code', () => {
const card = mapWoodenFishWorkToPlatformGalleryCard({
publicWorkCode: '',
workId: 'wooden-fish-work-1',
profileId: 'wooden-fish-profile-12345678',
ownerUserId: 'user-1',
authorDisplayName: '玩家',
workTitle: '每日一敲',
workDescription: '敲一下,好事发生。',
coverImageSrc: '/generated-wooden-fish-assets/profile/hit-object.png',
themeTags: [],
publicationStatus: 'published',
playCount: 12,
updatedAt: '2026-05-20T00:00:00.000Z',
publishedAt: '2026-05-20T00:00:00.000Z',
generationStatus: 'ready',
});
expect(isWoodenFishGalleryEntry(card)).toBe(true);
expect(card.sourceType).toBe('wooden-fish');
expect(card.publicWorkCode).toBe('WF-12345678');
expect(resolvePlatformPublicWorkCode(card)).toBe('WF-12345678');
expect(resolvePlatformWorldFallbackCoverImage(card)).toBe(
'/wooden-fish/default-hit-object.png',
);
expect(buildPlatformWorldDisplayTags(card, 2)).toEqual(['敲木鱼']);
});
test('keeps baby object match public card code and template label intact', () => {
const card: PlatformEdutainmentGalleryCard = {
sourceType: 'edutainment',
templateId: EDUTAINMENT_BABY_OBJECT_MATCH_TEMPLATE_ID,
templateName: EDUTAINMENT_BABY_OBJECT_MATCH_TEMPLATE_NAME,
workId: 'baby-object-match-work-1',
profileId: 'baby-object-match-profile-1',
sourceSessionId: 'baby-object-match-session-1',
publicWorkCode: 'EDU-BABY01',
ownerUserId: 'user-1',
authorDisplayName: '陶泥儿主',
worldName: '宝贝识物水果篮',
subtitle: EDUTAINMENT_BABY_OBJECT_MATCH_TEMPLATE_NAME,
summaryText: '将物品放入对应的篮子里。',
coverImageSrc: null,
themeTags: ['寓教于乐'],
playCount: 3,
remixCount: 0,
likeCount: 1,
recentPlayCount7d: 3,
visibility: 'published',
publishedAt: '2026-05-11T10:00:00.000Z',
updatedAt: '2026-05-11T10:00:00.000Z',
};
expect(isEdutainmentGalleryEntry(card)).toBe(true);
expect(resolvePlatformPublicWorkCode(card)).toBe('EDU-BABY01');
expect(buildPlatformWorldDisplayTags(card, 2)).toEqual(['寓教于乐']);
});
test('maps baby object match draft to edutainment public card', () => {
const card = mapBabyObjectMatchDraftToPlatformGalleryCard({
draftId: 'baby-object-draft-1',
profileId: 'baby-object-profile-12345678',
templateId: EDUTAINMENT_BABY_OBJECT_MATCH_TEMPLATE_ID,
templateName: EDUTAINMENT_BABY_OBJECT_MATCH_TEMPLATE_NAME,
workTitle: '宝贝识物水果篮',
workDescription: '苹果和香蕉识物分类',
itemNames: ['苹果', '香蕉'],
itemAssets: [
{
itemId: 'baby-object-item-1',
itemName: '苹果',
imageSrc: '/apple.png',
assetObjectId: null,
generationProvider: 'placeholder',
prompt: '苹果',
},
{
itemId: 'baby-object-item-2',
itemName: '香蕉',
imageSrc: '/banana.png',
assetObjectId: null,
generationProvider: 'placeholder',
prompt: '香蕉',
},
],
visualPackage: null,
themeTags: ['寓教于乐', '宝贝识物'],
publicationStatus: 'published',
createdAt: '2026-05-11T10:00:00.000Z',
updatedAt: '2026-05-11T12:00:00.000Z',
publishedAt: '2026-05-11T12:00:00.000Z',
});
expect(isEdutainmentGalleryEntry(card)).toBe(true);
expect(card.publicWorkCode).toBe('BO-12345678');
expect(card.coverImageSrc).toBe('/apple.png');
expect(card.themeTags[0]).toBe('寓教于乐');
});
test('maps bark battle work to BB public card with scene roles cover', () => {
const card = mapBarkBattleWorkToPlatformGalleryCard({
workId: 'bark-battle-work-abcdef12',
draftId: 'bark-battle-draft-1',
ownerUserId: 'user-1',
authorDisplayName: '玩家',
title: '公园声浪赛',
summary: '柯基和哈士奇比拼声浪。',
themeDescription: '傍晚公园擂台',
playerImageDescription: '红围巾柯基',
opponentImageDescription: '蓝头带哈士奇',
playerCharacterImageSrc: '/generated-bark-battle/player.png',
opponentCharacterImageSrc: '/generated-bark-battle/opponent.png',
uiBackgroundImageSrc: '/generated-bark-battle/background.png',
difficultyPreset: 'hard',
status: 'published',
generationStatus: 'ready',
publishReady: true,
playCount: 9,
recentPlayCount7d: 4,
updatedAt: '2026-05-20T00:00:00.000Z',
publishedAt: '2026-05-20T00:00:00.000Z',
});
expect(isBarkBattleGalleryEntry(card)).toBe(true);
expect(card.publicWorkCode).toBe('BB-ABCDEF12');
expect(resolvePlatformPublicWorkCode(card)).toBe('BB-ABCDEF12');
expect(card.coverImageSrc).toBe('/generated-bark-battle/background.png');
expect(card.coverRenderMode).toBe('scene_with_roles');
expect(card.coverCharacterImageSrcs).toEqual([
'/generated-bark-battle/player.png',
'/generated-bark-battle/opponent.png',
]);
expect(buildPlatformWorldDisplayTags(card, 3)).toEqual([
'汪汪声浪',
'高能',
'傍晚公园',
]);
});
test('maps bark battle public card cover from character or reference fallback', () => {
const characterCoverCard = mapBarkBattleWorkToPlatformGalleryCard({
workId: 'BB-COVER001',
draftId: 'bark-battle-draft-cover',
ownerUserId: 'user-1',
authorDisplayName: '玩家',
title: '角色封面赛',
summary: '',
themeDescription: '草地声浪挑战',
playerImageDescription: '柯基选手',
opponentImageDescription: '哈士奇对手',
playerCharacterImageSrc: '/bark/player-cover.png',
opponentCharacterImageSrc: '/bark/opponent-cover.png',
uiBackgroundImageSrc: null,
difficultyPreset: 'normal',
status: 'published',
generationStatus: 'ready',
publishReady: true,
playCount: 1,
updatedAt: '2026-05-20T00:00:00.000Z',
publishedAt: '2026-05-20T00:00:00.000Z',
});
const fallbackCoverCard = mapBarkBattleWorkToPlatformGalleryCard({
workId: 'BB-COVER002',
draftId: 'bark-battle-draft-cover-fallback',
ownerUserId: 'user-1',
authorDisplayName: '玩家',
title: '默认封面赛',
summary: '',
themeDescription: '夜市声浪挑战',
playerImageDescription: '柴犬选手',
opponentImageDescription: '机器人对手',
playerCharacterImageSrc: null,
opponentCharacterImageSrc: null,
uiBackgroundImageSrc: null,
difficultyPreset: 'easy',
status: 'published',
generationStatus: 'ready',
publishReady: true,
playCount: 1,
updatedAt: '2026-05-20T00:00:00.000Z',
publishedAt: '2026-05-20T00:00:00.000Z',
});
expect(characterCoverCard.coverImageSrc).toBe('/bark/player-cover.png');
expect(characterCoverCard.coverCharacterImageSrcs).toEqual([
'/bark/player-cover.png',
'/bark/opponent-cover.png',
]);
expect(fallbackCoverCard.coverImageSrc).toBe(
'/creation-type-references/bark-battle.webp',
);
expect(fallbackCoverCard.publicWorkCode).toBe('BB-COVER002');
});