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'); });