1
This commit is contained in:
@@ -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();
|
||||
|
||||
Reference in New Issue
Block a user