/* @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; }) => src ? ( {alt} ) : 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( {}} />, ); 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(dialog.parentElement?.className).not.toContain('platform-theme--'); 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( {}} />, ); 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); }); });