This commit is contained in:
2026-05-11 16:15:48 +08:00
parent 0c9254502c
commit e30b733b17
87 changed files with 3527 additions and 1261 deletions

View File

@@ -5,6 +5,7 @@ import userEvent from '@testing-library/user-event';
import { afterEach, expect, test, vi } from 'vitest';
import type { CustomWorldWorkSummary } from '../../../packages/shared/src/contracts/customWorldAgent';
import type { SquareHoleWorkSummary } from '../../../packages/shared/src/contracts/squareHoleWorks';
import { derivePlatformCreationTypes } from '../platform-entry/platformEntryCreationTypes';
import type { CreationEntryConfig } from '../../services/creationEntryConfigService';
import { CustomWorldCreationHub } from './CustomWorldCreationHub';
@@ -51,7 +52,7 @@ const testEntryConfig = {
subtitle: '形状投放挑战',
badge: '可创建',
imageSrc: '/creation-type-references/square-hole.webp',
visible: true,
visible: false,
open: true,
sortOrder: 50,
updatedAtMicros: 1,
@@ -164,6 +165,30 @@ const baseDraftItem: CustomWorldWorkSummary = {
canEnterWorld: false,
};
const hiddenSquareHoleItem: SquareHoleWorkSummary = {
workId: 'square-hole:work-hidden',
profileId: 'square-hole-profile-hidden',
ownerUserId: 'user-1',
gameName: '隐藏方洞挑战',
themeText: '方洞',
twistRule: '隐藏入口',
summary: '入口隐藏后,这条作品不应出现在创作页作品架。',
tags: ['方洞'],
coverImageSrc: null,
backgroundPrompt: '',
backgroundImageSrc: null,
shapeOptions: [],
holeOptions: [],
shapeCount: 0,
difficulty: 1,
publicationStatus: 'draft',
playCount: 0,
updatedAt: new Date('2026-05-10T10:00:00.000Z').toISOString(),
publishedAt: null,
publishReady: false,
sourceSessionId: 'square-hole-session-hidden',
};
test('creation hub reflects updated draft title summary and counts after rerender', async () => {
const user = userEvent.setup();
const onCreateType = vi.fn();
@@ -185,19 +210,20 @@ test('creation hub reflects updated draft title summary and counts after rerende
expect(screen.getByText('玩家是失职返乡的守灯人。')).toBeTruthy();
expect(screen.queryByText('角色 3')).toBeNull();
expect(screen.queryByText('地点 4')).toBeNull();
const puzzleButton = screen.getByRole('button', { name: /.*/u });
const match3dButton = screen.getByRole('button', {
name: /.*/u,
const puzzleButton = screen.getByRole('button', {
name: /.*/u,
});
const match3dButton = screen.getByRole('button', {
name: /.*3D /u,
});
const squareHoleButton = screen.getByRole('button', { name: //u });
expect((squareHoleButton as HTMLButtonElement).disabled).toBe(false);
expect(puzzleButton).toBeTruthy();
expect(match3dButton).toBeTruthy();
expect((puzzleButton as HTMLButtonElement).disabled).toBe(false);
expect((match3dButton as HTMLButtonElement).disabled).toBe(false);
expect(screen.getByText('反直觉形状分拣')).toBeTruthy();
expect(screen.queryByRole('button', { name: //u })).toBeNull();
expect(screen.queryByText('反直觉形状分拣')).toBeNull();
expect(screen.queryByRole('button', { name: //u })).toBeNull();
expect(screen.queryByRole('button', { name: //u })).toBeNull();
expect(screen.queryByRole('button', { name: //u })).toBeNull();
expect(screen.queryByRole('button', { name: //u })).toBeNull();
await user.click(match3dButton);
@@ -234,6 +260,29 @@ test('creation hub reflects updated draft title summary and counts after rerende
expect(screen.queryByText('地点 6')).toBeNull();
});
test('creation hub hides square hole works when the creation type is hidden', () => {
const onOpenSquareHoleDetail = vi.fn();
render(
<CustomWorldCreationHub
items={[]}
squareHoleItems={[hiddenSquareHoleItem]}
loading={false}
error={null}
onRetry={() => {}}
onCreateType={noopCreateType}
onOpenDraft={() => {}}
onEnterPublished={() => {}}
entryConfig={testEntryConfig}
creationTypes={testCreationTypes}
onOpenSquareHoleDetail={onOpenSquareHoleDetail}
/>,
);
expect(screen.queryByText('隐藏方洞挑战')).toBeNull();
expect(screen.queryByText('入口隐藏后,这条作品不应出现在创作页作品架。')).toBeNull();
});
test('creation hub mixes puzzle works into the same grid and uses puzzle tag to distinguish', () => {
render(
<CustomWorldCreationHub

View File

@@ -47,7 +47,7 @@ const testEntryConfig = {
subtitle: '形状投放挑战',
badge: '可创建',
imageSrc: '/creation-type-references/square-hole.webp',
visible: true,
visible: false,
open: true,
sortOrder: 50,
updatedAtMicros: 1,
@@ -120,10 +120,10 @@ test('creation hub draft card renders compiled work summary fields', () => {
expect(html).toContain('玩家是失职返乡的守灯人');
expect(html).toContain('守灯会与沉船商盟争夺航道解释权');
expect(html).toContain('拼图');
expect(html).toContain('创意礼物,生活分享');
expect(html).toContain('拼图关卡创作');
expect(html).toContain('抓大鹅');
expect(html).toContain('经典消除玩法');
expect(html).not.toContain('角色扮演');
expect(html).toContain('3D 消除关卡');
expect(html).not.toContain('文字冒险');
expect(html).not.toContain('大鱼吃小鱼');
});

View File

@@ -9,6 +9,7 @@ import type { SquareHoleWorkSummary } from '../../../packages/shared/src/contrac
import type { VisualNovelWorkSummary } from '../../../packages/shared/src/contracts/visualNovel';
import type { CustomWorldProfile } from '../../types';
import type { CreationEntryConfig } from '../../services/creationEntryConfigService';
import { isPlatformCreationTypeVisible } from '../platform-entry/platformEntryCreationTypes';
import type {
PlatformCreationTypeCard,
PlatformCreationTypeId,
@@ -169,6 +170,10 @@ export function CustomWorldCreationHub({
}: CustomWorldCreationHubProps) {
const [activeFilter, setActiveFilter] =
useState<CustomWorldWorkFilter>('all');
const isSquareHoleCreationVisible = isPlatformCreationTypeVisible(
creationTypes,
'square-hole',
);
const shelfItems = useMemo(
() =>
buildCreationWorkShelfItems({
@@ -176,18 +181,20 @@ export function CustomWorldCreationHub({
rpgLibraryEntries,
bigFishItems,
match3dItems,
squareHoleItems,
squareHoleItems: isSquareHoleCreationVisible ? squareHoleItems : [],
puzzleItems,
visualNovelItems,
canDeleteRpg: Boolean(onDeletePublished),
canDeleteBigFish: Boolean(onDeleteBigFish),
canDeleteMatch3D: Boolean(onDeleteMatch3D),
canDeleteSquareHole: Boolean(onDeleteSquareHole),
canDeleteSquareHole:
isSquareHoleCreationVisible && Boolean(onDeleteSquareHole),
canDeletePuzzle: Boolean(onDeletePuzzle),
canDeleteVisualNovel: Boolean(onDeleteVisualNovel),
}),
[
bigFishItems,
isSquareHoleCreationVisible,
items,
match3dItems,
onDeleteBigFish,