import { expect, test } from 'vitest'; import type { BarkBattleWorkSummary } from '../../../packages/shared/src/contracts/barkBattle'; import type { BigFishWorkSummary } from '../../../packages/shared/src/contracts/bigFishWorkSummary'; import type { BabyObjectMatchDraft } from '../../../packages/shared/src/contracts/edutainmentBabyObject'; import type { JumpHopGalleryCardResponse } from '../../../packages/shared/src/contracts/jumpHop'; import type { Match3DWorkSummary } from '../../../packages/shared/src/contracts/match3dWorks'; import type { PuzzleWorkSummary } from '../../../packages/shared/src/contracts/puzzleWorkSummary'; import type { CustomWorldGalleryCard } from '../../../packages/shared/src/contracts/runtime'; import { EDUTAINMENT_BABY_OBJECT_MATCH_TEMPLATE_ID, EDUTAINMENT_BABY_OBJECT_MATCH_TEMPLATE_NAME, type PlatformPublicGalleryCard, } from '../rpg-entry/rpgEntryWorldPresentation'; import { buildPlatformPublicGalleryFeeds, getPlatformPublicGalleryEntryKey, getPlatformPublicGalleryEntryTime, getPlatformRecommendRuntimeKind, isPlatformRecommendRuntimeReadyForEntry, isSamePlatformPublicGalleryEntry, mergePlatformPublicGalleryEntries, type PlatformRecommendRuntimeStartIntentDeps, type RecommendRuntimeKind, resolvePlatformRecommendRuntimeAutoStartDecision, resolvePlatformRecommendRuntimeStartIntent, } from './platformPublicGalleryFlow'; import { mapBarkBattlePublicDetailToWorkSummary, mapPublicWorkDetailToBigFishWork, mapPublicWorkDetailToPuzzleWork, mapPublicWorkDetailToSquareHoleWork, } from './platformPublicWorkDetailFlow'; type TypedPlatformPublicGalleryCard = Extract< PlatformPublicGalleryCard, { sourceType: string } >; type PlatformGallerySourceType = TypedPlatformPublicGalleryCard['sourceType']; type TypedPlatformPublicGalleryCardOverrides = Partial< Omit >; function buildRpgEntry( overrides: Partial = {}, ): CustomWorldGalleryCard { return { ownerUserId: 'user-1', profileId: 'rpg-profile', publicWorkCode: 'CW-RPG', authorPublicUserCode: null, visibility: 'published', publishedAt: '2026-06-01T00:00:00.000Z', updatedAt: '2026-06-01T01:00:00.000Z', authorDisplayName: '玩家', worldName: 'RPG 世界', subtitle: '公开作品', summaryText: '公开作品摘要', coverImageSrc: null, themeMode: 'martial', playableNpcCount: 1, landmarkCount: 1, ...overrides, }; } function buildBigFishWork( overrides: Partial = {}, ): BigFishWorkSummary { return { workId: 'big-fish-work', sourceSessionId: 'big-fish-session', ownerUserId: 'user-1', authorDisplayName: '玩家', title: '大鱼吃小鱼', subtitle: '海湾', summary: '一路长大。', coverImageSrc: '/big-fish-cover.png', status: 'published', updatedAt: '2026-06-01T02:00:00.000Z', publishedAt: '2026-06-01T02:00:00.000Z', publishReady: true, levelCount: 1, levelMainImageReadyCount: 1, levelMotionReadyCount: 1, backgroundReady: true, playCount: 1, ...overrides, }; } function buildBabyObjectMatchDraft( overrides: Partial = {}, ): BabyObjectMatchDraft { const itemAsset = { itemId: 'item-a', itemName: '苹果', imageSrc: '/apple.png', assetObjectId: null, generationProvider: 'placeholder' as const, prompt: '苹果', }; return { draftId: 'baby-draft', profileId: 'baby-profile', templateId: EDUTAINMENT_BABY_OBJECT_MATCH_TEMPLATE_ID, templateName: EDUTAINMENT_BABY_OBJECT_MATCH_TEMPLATE_NAME, workTitle: '宝贝识物', workDescription: '认识水果。', itemNames: ['苹果', '香蕉'], itemAssets: [itemAsset, { ...itemAsset, itemId: 'item-b', itemName: '香蕉' }], visualPackage: null, themeTags: ['寓教于乐'], publicationStatus: 'published', createdAt: '2026-06-01T00:00:00.000Z', updatedAt: '2026-06-01T03:00:00.000Z', publishedAt: '2026-06-01T03:00:00.000Z', ...overrides, }; } function buildJumpHopEntry( overrides: Partial = {}, ): JumpHopGalleryCardResponse { return { publicWorkCode: 'JH-JUMP', workId: 'jump-hop-work', profileId: 'jump-hop-profile', ownerUserId: 'user-1', authorDisplayName: '玩家', themeText: '一路向前', workTitle: '跳一跳', workDescription: '一路向前。', coverImageSrc: '/jump-hop-cover.png', themeTags: ['跳一跳'], difficulty: 'standard', stylePreset: 'minimal-blocks', publicationStatus: 'published', playCount: 1, updatedAt: '2026-06-04T00:00:00.000Z', publishedAt: '2026-06-04T00:00:00.000Z', generationStatus: 'ready', ...overrides, }; } function buildTypedEntry( sourceType: PlatformGallerySourceType, overrides: TypedPlatformPublicGalleryCardOverrides = {}, ): PlatformPublicGalleryCard { const common = { workId: `${sourceType}-work`, profileId: `${sourceType}-profile`, publicWorkCode: `${sourceType}-code`, ownerUserId: 'user-1', authorDisplayName: '玩家', worldName: `${sourceType} 作品`, subtitle: '公开作品', summaryText: '公开作品摘要', coverImageSrc: null, themeTags: [sourceType], visibility: 'published' as const, publishedAt: '2026-06-01T00:00:00.000Z', updatedAt: '2026-06-01T01:00:00.000Z', }; switch (sourceType) { case 'puzzle': return { ...common, ...overrides, sourceType }; case 'puzzle-clear': return { ...common, ...overrides, sourceType, themePrompt: '拼消消主题', }; case 'big-fish': return { ...common, ...overrides, sourceType }; case 'match3d': return { ...common, ...overrides, sourceType }; case 'square-hole': return { ...common, ...overrides, sourceType }; case 'visual-novel': return { ...common, ...overrides, sourceType }; case 'jump-hop': return { ...common, ...overrides, sourceType }; case 'wooden-fish': return { ...common, ...overrides, sourceType }; case 'edutainment': return { ...common, ...overrides, sourceType, templateId: EDUTAINMENT_BABY_OBJECT_MATCH_TEMPLATE_ID, templateName: EDUTAINMENT_BABY_OBJECT_MATCH_TEMPLATE_NAME, }; case 'bark-battle': return { ...common, ...overrides, sourceType, authorPublicUserCode: null, coverRenderMode: 'image', coverCharacterImageSrcs: [], themeMode: 'martial', playableNpcCount: 1, landmarkCount: 1, }; default: { const exhaustive: never = sourceType; return exhaustive; } } } function buildPuzzleWork( overrides: Partial = {}, ): PuzzleWorkSummary { return { workId: 'puzzle-work', profileId: 'puzzle-profile', ownerUserId: 'user-1', sourceSessionId: 'puzzle-session', authorDisplayName: '玩家', levelName: '拼图作品', summary: '拼图摘要', themeTags: ['拼图'], coverImageSrc: '/puzzle-cover.png', publicationStatus: 'published', updatedAt: '2026-06-01T01:00:00.000Z', publishedAt: '2026-06-01T00:00:00.000Z', playCount: 3, remixCount: 2, likeCount: 1, pointIncentiveTotalHalfPoints: 0, pointIncentiveClaimedPoints: 0, pointIncentiveTotalPoints: 0, pointIncentiveClaimablePoints: 0, publishReady: true, ...overrides, }; } function buildMatch3DWork( overrides: Partial = {}, ): Match3DWorkSummary { return { workId: 'match3d-work', profileId: 'match3d-profile', ownerUserId: 'user-1', sourceSessionId: 'match3d-session', gameName: '抓大鹅作品', themeText: '经典消除', summary: '抓大鹅摘要', tags: ['抓大鹅'], coverImageSrc: '/match3d-cover.png', referenceImageSrc: null, clearCount: 12, difficulty: 4, publicationStatus: 'published', playCount: 10, updatedAt: '2026-06-01T01:00:00.000Z', publishedAt: '2026-06-01T00:00:00.000Z', publishReady: true, generatedItemAssets: [], ...overrides, }; } function buildBarkBattleWork( overrides: Partial = {}, ): BarkBattleWorkSummary { return { workId: 'bark-battle-work', draftId: 'bark-battle-draft', ownerUserId: 'user-1', authorDisplayName: '玩家', title: '汪汪声浪作品', summary: '汪汪摘要', themeDescription: '森林擂台', playerImageDescription: '小狗', opponentImageDescription: '对手', playerCharacterImageSrc: '/player.png', opponentCharacterImageSrc: '/opponent.png', uiBackgroundImageSrc: '/bark-bg.png', difficultyPreset: 'normal', status: 'published', generationStatus: 'ready', publishReady: true, playCount: 9, recentPlayCount7d: 2, updatedAt: '2026-06-01T01:00:00.000Z', publishedAt: '2026-06-01T00:00:00.000Z', ...overrides, }; } function buildRecommendRuntimeStartDeps( overrides: Partial = {}, ): PlatformRecommendRuntimeStartIntentDeps { return { selectedPuzzleDetail: null, barkBattleGalleryEntries: [], mapMatch3DWork: () => buildMatch3DWork(), ...overrides, }; } test('platform public gallery flow resolves stable key and runtime kind for every play kind', () => { const cases: Array< [sourceType: PlatformGallerySourceType, keyKind: string, kind: RecommendRuntimeKind] > = [ ['big-fish', 'big-fish', 'big-fish'], ['puzzle', 'puzzle', 'puzzle'], ['jump-hop', 'jump-hop', 'jump-hop'], ['wooden-fish', 'wooden-fish', 'wooden-fish'], ['match3d', 'match3d', 'match3d'], ['square-hole', 'square-hole', 'square-hole'], ['visual-novel', 'visual-novel', 'visual-novel'], ['bark-battle', 'bark-battle', 'bark-battle'], [ 'edutainment', `edutainment:${EDUTAINMENT_BABY_OBJECT_MATCH_TEMPLATE_ID}`, 'edutainment', ], ]; cases.forEach(([sourceType, keyKind, kind]) => { const entry = buildTypedEntry(sourceType); expect(getPlatformPublicGalleryEntryKey(entry)).toBe( `${keyKind}:user-1:${sourceType}-profile`, ); expect(getPlatformRecommendRuntimeKind(entry)).toBe(kind); }); const rpgEntry = buildRpgEntry(); expect(getPlatformPublicGalleryEntryKey(rpgEntry)).toBe( 'rpg:user-1:rpg-profile', ); expect(getPlatformRecommendRuntimeKind(rpgEntry)).toBe('rpg'); }); test('platform public gallery flow compares entries by resolved identity', () => { const left = buildTypedEntry('puzzle'); const sameIdentity = buildTypedEntry('puzzle', { workId: 'other-work', worldName: '新标题', }); const otherKind = buildTypedEntry('match3d', { ownerUserId: left.ownerUserId, profileId: left.profileId, }); expect(isSamePlatformPublicGalleryEntry(left, sameIdentity)).toBe(true); expect(isSamePlatformPublicGalleryEntry(left, otherKind)).toBe(false); }); test('platform public gallery flow resolves recommend runtime start intent', () => { const bigFishEntry = buildTypedEntry('big-fish'); expect( resolvePlatformRecommendRuntimeStartIntent( bigFishEntry, buildRecommendRuntimeStartDeps(), ), ).toEqual({ type: 'start-big-fish', work: mapPublicWorkDetailToBigFishWork(bigFishEntry), returnStage: 'platform', embedded: true, }); const selectedPuzzleDetail = buildPuzzleWork({ profileId: 'puzzle-profile', }); expect( resolvePlatformRecommendRuntimeStartIntent( buildTypedEntry('puzzle'), buildRecommendRuntimeStartDeps({ selectedPuzzleDetail }), ), ).toEqual({ type: 'start-puzzle', work: selectedPuzzleDetail, returnStage: 'platform', embedded: true, }); const puzzleEntry = buildTypedEntry('puzzle', { profileId: 'fallback-puzzle-profile', }); expect( resolvePlatformRecommendRuntimeStartIntent( puzzleEntry, buildRecommendRuntimeStartDeps({ selectedPuzzleDetail: buildPuzzleWork({ profileId: 'stale-profile' }), }), ), ).toEqual({ type: 'start-puzzle', work: mapPublicWorkDetailToPuzzleWork(puzzleEntry), returnStage: 'platform', embedded: true, }); expect( resolvePlatformRecommendRuntimeStartIntent( buildTypedEntry('jump-hop'), buildRecommendRuntimeStartDeps(), ), ).toEqual({ type: 'start-jump-hop', profileId: 'jump-hop-profile', returnStage: 'platform', embedded: true, }); expect( resolvePlatformRecommendRuntimeStartIntent( buildTypedEntry('wooden-fish'), buildRecommendRuntimeStartDeps(), ), ).toEqual({ type: 'start-wooden-fish', profileId: 'wooden-fish-profile', returnStage: 'platform', embedded: true, }); expect( resolvePlatformRecommendRuntimeStartIntent( buildTypedEntry('visual-novel'), buildRecommendRuntimeStartDeps(), ), ).toEqual({ type: 'start-visual-novel', profileId: 'visual-novel-profile', returnStage: 'platform', embedded: true, }); expect( resolvePlatformRecommendRuntimeStartIntent( buildTypedEntry('edutainment'), buildRecommendRuntimeStartDeps(), ), ).toEqual({ type: 'start-edutainment', entry: buildTypedEntry('edutainment'), returnStage: 'platform', embedded: true, }); expect( resolvePlatformRecommendRuntimeStartIntent( buildRpgEntry(), buildRecommendRuntimeStartDeps(), ), ).toEqual({ type: 'mark-ready', }); }); test('platform public gallery flow resolves recommend runtime mapper-backed start intent', () => { const match3DEntry = buildTypedEntry('match3d'); const match3DWork = buildMatch3DWork({ workId: 'mapped-match3d-work' }); expect( resolvePlatformRecommendRuntimeStartIntent( match3DEntry, buildRecommendRuntimeStartDeps({ mapMatch3DWork: (entry) => entry === match3DEntry ? match3DWork : null, }), ), ).toEqual({ type: 'start-match3d', work: match3DWork, returnStage: 'work-detail', embedded: true, }); expect( resolvePlatformRecommendRuntimeStartIntent( match3DEntry, buildRecommendRuntimeStartDeps({ mapMatch3DWork: () => null }), ), ).toEqual({ type: 'blocked', errorTarget: 'match3d', errorMessage: '当前抓大鹅作品信息不完整,暂时无法进入玩法。', }); const squareHoleEntry = buildTypedEntry('square-hole'); expect( resolvePlatformRecommendRuntimeStartIntent( squareHoleEntry, buildRecommendRuntimeStartDeps(), ), ).toEqual({ type: 'start-square-hole', work: mapPublicWorkDetailToSquareHoleWork(squareHoleEntry), returnStage: 'platform', embedded: true, }); }); test('platform public gallery flow resolves recommend runtime bark battle priority', () => { const entry = buildTypedEntry('bark-battle'); const galleryWork = buildBarkBattleWork({ workId: 'bark-battle-work', title: '推荐缓存', }); expect( resolvePlatformRecommendRuntimeStartIntent( entry, buildRecommendRuntimeStartDeps({ barkBattleGalleryEntries: [galleryWork], }), ), ).toEqual({ type: 'start-bark-battle', work: galleryWork, returnStage: 'platform', embedded: true, }); expect( resolvePlatformRecommendRuntimeStartIntent( entry, buildRecommendRuntimeStartDeps(), ), ).toEqual({ type: 'start-bark-battle', work: mapBarkBattlePublicDetailToWorkSummary(entry), returnStage: 'platform', embedded: true, }); }); test('platform public gallery flow resolves recommend runtime readiness', () => { expect( isPlatformRecommendRuntimeReadyForEntry(buildTypedEntry('big-fish'), { activeKind: 'puzzle', hasBigFishRun: true, }), ).toBe(false); expect( isPlatformRecommendRuntimeReadyForEntry(buildTypedEntry('big-fish'), { activeKind: 'big-fish', hasBigFishRun: true, }), ).toBe(true); expect( isPlatformRecommendRuntimeReadyForEntry(buildTypedEntry('jump-hop'), { activeKind: 'jump-hop', hasJumpHopRun: true, }), ).toBe(true); expect( isPlatformRecommendRuntimeReadyForEntry(buildTypedEntry('wooden-fish'), { activeKind: 'wooden-fish', hasWoodenFishRun: true, }), ).toBe(true); expect( isPlatformRecommendRuntimeReadyForEntry(buildTypedEntry('match3d'), { activeKind: 'match3d', hasMatch3DRun: true, }), ).toBe(true); expect( isPlatformRecommendRuntimeReadyForEntry(buildTypedEntry('square-hole'), { activeKind: 'square-hole', hasSquareHoleRun: true, }), ).toBe(true); expect( isPlatformRecommendRuntimeReadyForEntry(buildTypedEntry('visual-novel'), { activeKind: 'visual-novel', hasVisualNovelRun: true, }), ).toBe(true); expect( isPlatformRecommendRuntimeReadyForEntry(buildTypedEntry('bark-battle'), { activeKind: 'bark-battle', }), ).toBe(true); expect( isPlatformRecommendRuntimeReadyForEntry(buildRpgEntry(), { activeKind: 'rpg', }), ).toBe(true); }); test('platform public gallery flow resolves puzzle and edutainment readiness details', () => { const puzzleEntry = buildTypedEntry('puzzle', { profileId: 'puzzle-profile', }); expect( isPlatformRecommendRuntimeReadyForEntry(puzzleEntry, { activeKind: 'puzzle', puzzleRunEntryProfileId: 'other-profile', puzzleRunCurrentLevelProfileId: 'puzzle-profile', }), ).toBe(true); expect( isPlatformRecommendRuntimeReadyForEntry(puzzleEntry, { activeKind: 'puzzle', puzzleRunEntryProfileId: 'other-profile', puzzleRunCurrentLevelProfileId: 'another-profile', }), ).toBe(false); expect( isPlatformRecommendRuntimeReadyForEntry(buildTypedEntry('edutainment'), { activeKind: 'edutainment', hasBabyObjectMatchDraft: true, }), ).toBe(true); expect( isPlatformRecommendRuntimeReadyForEntry(buildTypedEntry('edutainment'), { activeKind: 'edutainment', hasBabyObjectMatchDraft: false, }), ).toBe(false); }); test('platform public gallery flow resolves recommend runtime auto-start gates', () => { const entry = buildTypedEntry('big-fish'); const baseInput: Parameters< typeof resolvePlatformRecommendRuntimeAutoStartDecision >[0] = { isDesktopLayout: false, selectionStage: 'platform', platformTab: 'home', isLoadingPlatform: false, entries: [entry], activeEntryKey: null, isStarting: false, readyState: { activeKind: null }, }; expect( resolvePlatformRecommendRuntimeAutoStartDecision({ ...baseInput, isDesktopLayout: true, }), ).toEqual({ type: 'noop' }); expect( resolvePlatformRecommendRuntimeAutoStartDecision({ ...baseInput, platformTab: 'discover', }), ).toEqual({ type: 'noop' }); expect( resolvePlatformRecommendRuntimeAutoStartDecision({ ...baseInput, entries: [], }), ).toEqual({ type: 'clear' }); expect( resolvePlatformRecommendRuntimeAutoStartDecision({ ...baseInput, isStarting: true, }), ).toEqual({ type: 'noop' }); expect( resolvePlatformRecommendRuntimeAutoStartDecision({ ...baseInput, activeEntryKey: getPlatformPublicGalleryEntryKey(entry), hasStartError: true, }), ).toEqual({ type: 'noop' }); }); test('platform public gallery flow resolves recommend runtime auto-start target', () => { const firstEntry = buildTypedEntry('big-fish', { profileId: 'big-fish-first', }); const activeEntry = buildTypedEntry('puzzle', { profileId: 'puzzle-active', }); const activeEntryKey = getPlatformPublicGalleryEntryKey(activeEntry); const baseInput: Parameters< typeof resolvePlatformRecommendRuntimeAutoStartDecision >[0] = { isDesktopLayout: false, selectionStage: 'platform', platformTab: 'home', isLoadingPlatform: false, entries: [firstEntry, activeEntry], activeEntryKey, isStarting: false, readyState: { activeKind: 'puzzle' }, }; expect( resolvePlatformRecommendRuntimeAutoStartDecision({ ...baseInput, readyState: { activeKind: 'puzzle', puzzleRunEntryProfileId: 'puzzle-active', }, }), ).toEqual({ type: 'noop' }); expect(resolvePlatformRecommendRuntimeAutoStartDecision(baseInput)).toEqual({ type: 'start', entry: activeEntry, }); expect( resolvePlatformRecommendRuntimeAutoStartDecision({ ...baseInput, readyState: { activeKind: null }, }), ).toEqual({ type: 'start', entry: activeEntry, }); expect( resolvePlatformRecommendRuntimeAutoStartDecision({ ...baseInput, activeEntryKey: 'missing-entry', }), ).toEqual({ type: 'start', entry: firstEntry, }); }); test('platform public gallery flow merges duplicate identities and sorts newest first', () => { const staleRpgEntry = buildRpgEntry({ profileId: 'shared-rpg', worldName: '旧版 RPG', publishedAt: '2026-06-01T00:00:00.000Z', }); const freshRpgEntry = buildRpgEntry({ profileId: 'shared-rpg', worldName: '新版 RPG', publishedAt: '2026-06-04T00:00:00.000Z', }); const middleRpgEntry = buildRpgEntry({ profileId: 'middle-rpg', worldName: '中间 RPG', publishedAt: '2026-06-02T00:00:00.000Z', }); const updatedOnlyEntry = buildTypedEntry('big-fish', { profileId: 'updated-only', publishedAt: null, updatedAt: '2026-06-03T00:00:00.000Z', }); const invalidTimeEntry = buildTypedEntry('puzzle', { profileId: 'invalid-time', publishedAt: 'not-a-date', updatedAt: 'still-not-a-date', }); const merged = mergePlatformPublicGalleryEntries( [staleRpgEntry, middleRpgEntry], [invalidTimeEntry, updatedOnlyEntry, freshRpgEntry], ); expect(merged).toHaveLength(4); expect(merged.map((entry) => entry.profileId)).toEqual([ 'shared-rpg', 'updated-only', 'middle-rpg', 'invalid-time', ]); expect(merged[0]?.worldName).toBe('新版 RPG'); expect(getPlatformPublicGalleryEntryTime(invalidTimeEntry)).toBe(0); }); test('platform public gallery flow builds feeds with visibility gates and bark battle fallback', () => { const hiddenBigFish = buildBigFishWork({ workId: 'hidden-big-fish', sourceSessionId: 'hidden-big-fish-session', }); const hiddenBabyDraft = buildBabyObjectMatchDraft({ profileId: 'hidden-baby', }); const publishedBarkFallback = buildBarkBattleWork({ workId: 'fallback-bark', publishedAt: '2026-06-04T00:00:00.000Z', updatedAt: '2026-06-04T00:00:00.000Z', }); const draftBarkFallback = buildBarkBattleWork({ workId: 'draft-bark', status: 'draft', }); const hiddenFeeds = buildPlatformPublicGalleryFeeds({ rpgEntries: [ buildRpgEntry({ profileId: 'rpg-visible', publishedAt: '2026-06-01T00:00:00.000Z', }), ], bigFishEntries: [hiddenBigFish], match3dEntries: [], puzzleEntries: [], puzzleClearEntries: [], barkBattleGalleryEntries: [], barkBattleWorks: [draftBarkFallback, publishedBarkFallback], jumpHopEntries: [], woodenFishEntries: [], squareHoleEntries: [], visualNovelEntries: [], babyObjectMatchDrafts: [hiddenBabyDraft], isBigFishCreationVisible: false, isBabyObjectMatchVisible: false, isVisualNovelCreationOpen: false, }); expect( hiddenFeeds.latestEntries.map((entry) => 'sourceType' in entry ? entry.sourceType : 'rpg', ), ).toEqual(['bark-battle', 'rpg']); expect(hiddenFeeds.latestEntries[0]?.profileId).toBe('fallback-bark'); const visibleFeeds = buildPlatformPublicGalleryFeeds({ rpgEntries: [], bigFishEntries: [hiddenBigFish], match3dEntries: [], puzzleEntries: [], puzzleClearEntries: [], barkBattleGalleryEntries: [ buildBarkBattleWork({ workId: 'gallery-bark', publishedAt: '2026-06-05T00:00:00.000Z', updatedAt: '2026-06-05T00:00:00.000Z', }), ], barkBattleWorks: [publishedBarkFallback], jumpHopEntries: [], woodenFishEntries: [], squareHoleEntries: [], visualNovelEntries: [], babyObjectMatchDrafts: [hiddenBabyDraft], isBigFishCreationVisible: true, isBabyObjectMatchVisible: true, isVisualNovelCreationOpen: false, }); expect(visibleFeeds.latestEntries.map((entry) => entry.profileId)).toEqual([ 'gallery-bark', 'hidden-baby', 'hidden-big-fish-session', ]); expect(visibleFeeds.featuredEntries).toEqual( visibleFeeds.latestEntries.slice(0, 6), ); }); test('platform public gallery flow preserves feed tie order and featured slice', () => { const sameTime = '2026-06-04T00:00:00.000Z'; const tieFeeds = buildPlatformPublicGalleryFeeds({ rpgEntries: [], bigFishEntries: [], match3dEntries: [], puzzleEntries: [], puzzleClearEntries: [], barkBattleGalleryEntries: [], barkBattleWorks: [ buildBarkBattleWork({ workId: 'fallback-bark', publishedAt: sameTime, updatedAt: sameTime, }), ], jumpHopEntries: [ buildJumpHopEntry({ profileId: 'jump-hop', publishedAt: sameTime, updatedAt: sameTime, }), ], woodenFishEntries: [], squareHoleEntries: [], visualNovelEntries: [], babyObjectMatchDrafts: [], isBigFishCreationVisible: false, isBabyObjectMatchVisible: false, isVisualNovelCreationOpen: false, }); expect(tieFeeds.latestEntries.map((entry) => entry.profileId)).toEqual([ 'jump-hop', 'fallback-bark', ]); const sliceFeeds = buildPlatformPublicGalleryFeeds({ rpgEntries: Array.from({ length: 7 }, (_, index) => buildRpgEntry({ profileId: `rpg-${index}`, publishedAt: `2026-06-0${index + 1}T00:00:00.000Z`, updatedAt: `2026-06-0${index + 1}T00:00:00.000Z`, }), ), bigFishEntries: [], match3dEntries: [], puzzleEntries: [], puzzleClearEntries: [], barkBattleGalleryEntries: [], barkBattleWorks: [], jumpHopEntries: [], woodenFishEntries: [], squareHoleEntries: [], visualNovelEntries: [], babyObjectMatchDrafts: [], isBigFishCreationVisible: false, isBabyObjectMatchVisible: false, isVisualNovelCreationOpen: false, }); expect(sliceFeeds.featuredEntries).toHaveLength(6); });