Files
Genarrative/src/components/common/PlatformImagePreviewModal.test.tsx
T
kdletters a51e63415f 收口生成队列与图片预览
将外部生成队列概览移到我的页签展示

移除生成页和进度页中的队列概览区域

新增全屏黑底图片预览器并支持缩放和边界拖拽

补充队列概览和图片预览的聚焦测试

同步更新玩法链路、运维、UI Kit 和团队共享记忆文档
2026-06-13 22:25:22 +08:00

182 lines
4.6 KiB
TypeScript

/* @vitest-environment jsdom */
import { act, fireEvent, render, screen, waitFor } from '@testing-library/react';
import type React from 'react';
import { expect, test, vi } from 'vitest';
import { PlatformImagePreviewModal } from './PlatformImagePreviewModal';
import { clampPlatformImagePreviewTransform } from './platformImagePreviewModel';
vi.mock('../ResolvedAssetImage', () => ({
ResolvedAssetImage: ({
src,
alt,
className,
style,
draggable,
onLoad,
}: {
src?: string | null;
alt?: string;
className?: string;
style?: React.CSSProperties;
draggable?: boolean;
onLoad?: React.ReactEventHandler<HTMLImageElement>;
}) =>
src ? (
<img
src={src}
alt={alt}
className={className}
style={style}
draggable={draggable}
onLoad={onLoad}
/>
) : null,
}));
test('clamps full-screen image preview zoom and drag within image bounds', () => {
expect(
clampPlatformImagePreviewTransform(
{
scale: 8,
offsetX: 999,
offsetY: -999,
},
{ width: 400, height: 800 },
{ width: 400, height: 400 },
),
).toEqual({
scale: 4,
offsetX: 600,
offsetY: -400,
});
expect(
clampPlatformImagePreviewTransform(
{
scale: 4,
offsetX: -999,
offsetY: 999,
},
{ width: 400, height: 800 },
{ width: 400, height: 400 },
),
).toEqual({
scale: 4,
offsetX: -600,
offsetY: 400,
});
expect(
clampPlatformImagePreviewTransform(
{
scale: 0.25,
offsetX: 80,
offsetY: 80,
},
{ width: 400, height: 800 },
{ width: 400, height: 400 },
),
).toEqual({
scale: 1,
offsetX: 0,
offsetY: 0,
});
});
test('renders full-screen image preview with zoom controls and dark backdrop', () => {
render(
<PlatformImagePreviewModal
open
title="查看关卡图片"
imageSrc="/generated-puzzle-assets/session/level/image.png"
imageAlt="拼图关卡图"
closeLabel="关闭关卡图片预览"
onClose={() => {}}
/>,
);
const dialog = screen.getByRole('dialog', { name: '查看关卡图片' });
expect(dialog.className).toContain('platform-image-preview-modal');
expect(dialog.className).toContain('bg-black');
expect(
dialog.parentElement?.className.includes('!bg-black'),
).toBe(true);
expect(screen.getByRole('button', { name: '放大图片' })).toBeTruthy();
expect(screen.getByRole('button', { name: '缩小图片' })).toBeTruthy();
expect(screen.getByRole('button', { name: '重置图片缩放' })).toBeTruthy();
expect(screen.getByAltText('拼图关卡图').getAttribute('draggable')).toBe(
'false',
);
});
test('zooms to at most four times and clamps dragged image position', async () => {
render(
<PlatformImagePreviewModal
open
title="查看关卡图片"
imageSrc="/generated-puzzle-assets/session/level/image.png"
imageAlt="拼图关卡图"
closeLabel="关闭关卡图片预览"
onClose={() => {}}
/>,
);
const stage = screen.getByTestId('platform-image-preview-stage');
Object.defineProperty(stage, 'getBoundingClientRect', {
configurable: true,
value: () => ({
width: 400,
height: 800,
top: 0,
right: 400,
bottom: 800,
left: 0,
x: 0,
y: 0,
toJSON: () => ({}),
}),
});
const image = screen.getByAltText('拼图关卡图') as HTMLImageElement;
Object.defineProperties(image, {
naturalWidth: { configurable: true, value: 400 },
naturalHeight: { configurable: true, value: 400 },
});
await act(async () => {
window.dispatchEvent(new Event('resize'));
fireEvent.load(image);
});
await waitFor(() => {
expect(image.style.width).toBe('400px');
expect(image.style.height).toBe('400px');
});
for (let index = 0; index < 8; index += 1) {
fireEvent.click(screen.getByRole('button', { name: '放大图片' }));
}
await waitFor(() => {
expect(image.style.transform).toContain(
'translate3d(0px, 0px, 0) scale(4)',
);
});
fireEvent.pointerDown(stage, { pointerId: 1, clientX: 200, clientY: 400 });
fireEvent.pointerMove(stage, { pointerId: 1, clientX: 1200, clientY: 1200 });
fireEvent.pointerUp(stage, { pointerId: 1, clientX: 1200, clientY: 1200 });
await waitFor(() => {
const offsetMatch = image.style.transform.match(
/translate3d\((-?\d+)px, (-?\d+)px, 0\) scale\(4\)/u,
);
expect(offsetMatch).not.toBeNull();
expect(Math.abs(Number(offsetMatch?.[1]))).toBe(600);
expect(Math.abs(Number(offsetMatch?.[2]))).toBe(400);
});
});