This commit is contained in:
2026-05-10 13:18:46 +08:00
parent dada5a4797
commit 1c16152708
17 changed files with 1197 additions and 99 deletions

View File

@@ -310,6 +310,16 @@ const originalMatchMedia = window.matchMedia;
const originalRequestAnimationFrame = window.requestAnimationFrame;
const originalCancelAnimationFrame = window.cancelAnimationFrame;
function dispatchPointerEvent(
target: HTMLElement,
type: string,
options: { pointerId: number; clientY: number },
) {
const event = new Event(type, { bubbles: true, cancelable: true });
Object.assign(event, options);
target.dispatchEvent(event);
}
const puzzlePublicEntry = {
sourceType: 'puzzle',
workId: 'puzzle-work-public-1',
@@ -1298,6 +1308,132 @@ test('mobile recommend loading state is themed instead of hardcoded black', () =
expect(document.querySelector('.platform-recommend-cover-only')).toBeTruthy();
});
test('logged in recommend runtime preloads adjacent work previews and drag switches like video feed', () => {
vi.useFakeTimers();
const onSelectNextRecommendEntry = vi.fn();
const onSelectPreviousRecommendEntry = vi.fn();
const firstEntry = {
...puzzlePublicEntry,
workId: 'puzzle-work-feed-1',
profileId: 'puzzle-profile-feed-1',
ownerUserId: 'user-feed-1',
publicWorkCode: 'PZ-FEED1',
worldName: '当前拼图',
coverImageSrc: 'current-cover.png',
} satisfies PlatformPublicGalleryCard;
const secondEntry = {
...puzzlePublicEntry,
workId: 'puzzle-work-feed-2',
profileId: 'puzzle-profile-feed-2',
ownerUserId: 'user-feed-2',
publicWorkCode: 'PZ-FEED2',
worldName: '下一拼图',
coverImageSrc: 'next-cover.png',
} satisfies PlatformPublicGalleryCard;
const thirdEntry = {
...puzzlePublicEntry,
workId: 'puzzle-work-feed-3',
profileId: 'puzzle-profile-feed-3',
ownerUserId: 'user-feed-3',
publicWorkCode: 'PZ-FEED3',
worldName: '上一拼图',
coverImageSrc: 'previous-cover.png',
} satisfies PlatformPublicGalleryCard;
render(
<AuthUiContext.Provider
value={{
user: {
id: 'user-1',
publicUserCode: '100001',
username: 'tester',
displayName: '测试玩家',
avatarUrl: null,
phoneNumberMasked: null,
loginMethod: 'password',
bindingStatus: 'active',
wechatBound: false,
createdAt: new Date().toISOString(),
},
canAccessProtectedData: true,
openLoginModal: vi.fn(),
requireAuth: (action) => action(),
openSettingsModal: vi.fn(),
openAccountModal: vi.fn(),
setCurrentUser: vi.fn(),
logout: vi.fn(async () => undefined),
musicVolume: 0.42,
setMusicVolume: vi.fn(),
platformTheme: 'light',
setPlatformTheme: vi.fn(),
isHydratingSettings: false,
isPersistingSettings: false,
settingsError: null,
}}
>
<RpgEntryHomeView
activeTab="home"
onTabChange={vi.fn()}
hasSavedGame={false}
savedSnapshot={null}
saveEntries={[]}
saveError={null}
featuredEntries={[]}
latestEntries={[firstEntry, secondEntry, thirdEntry]}
myEntries={[]}
historyEntries={[]}
profileDashboard={null}
isLoadingPlatform={false}
isLoadingDashboard={false}
isResumingSaveWorldKey={null}
platformError={null}
dashboardError={null}
onContinueGame={vi.fn()}
onResumeSave={vi.fn()}
onOpenCreateWorld={vi.fn()}
onOpenCreateTypePicker={vi.fn()}
onOpenGalleryDetail={vi.fn()}
recommendRuntimeContent={<div data-testid="recommend-runtime" />}
activeRecommendEntryKey="puzzle:user-feed-1:puzzle-profile-feed-1"
onSelectNextRecommendEntry={onSelectNextRecommendEntry}
onSelectPreviousRecommendEntry={onSelectPreviousRecommendEntry}
onOpenLibraryDetail={vi.fn()}
onSearchPublicCode={vi.fn()}
/>
</AuthUiContext.Provider>,
);
expect(screen.getByTestId('recommend-runtime')).toBeTruthy();
expect(
document.querySelectorAll('.platform-recommend-runtime-preview'),
).toHaveLength(2);
expect(
document.querySelectorAll('.platform-recommend-swipe-card'),
).toHaveLength(3);
expect(screen.getAllByText('下一拼图').length).toBeGreaterThanOrEqual(2);
expect(screen.getAllByText('上一拼图').length).toBeGreaterThanOrEqual(2);
const meta = screen.getByLabelText('当前拼图 作品信息') as HTMLElement;
act(() => {
dispatchPointerEvent(meta, 'pointerdown', { pointerId: 1, clientY: 300 });
dispatchPointerEvent(meta, 'pointermove', { pointerId: 1, clientY: 210 });
});
const rail = document.querySelector(
'.platform-recommend-swipe-rail',
) as HTMLElement | null;
expect(rail?.className).toContain('platform-recommend-swipe-rail');
act(() => {
dispatchPointerEvent(meta, 'pointerup', { pointerId: 1, clientY: 210 });
vi.advanceTimersByTime(180);
});
expect(onSelectNextRecommendEntry).toHaveBeenCalledTimes(1);
expect(onSelectPreviousRecommendEntry).not.toHaveBeenCalled();
vi.useRealTimers();
});
test('logged out active recommend bottom tab selects next work without login', async () => {
const user = userEvent.setup();
const onSelectNextRecommendEntry = vi.fn();