Files
Genarrative/src/components/square-hole-runtime/SquareHoleRuntimeShell.test.tsx
kdletters d06107f2c6
Some checks failed
CI / verify (push) Has been cancelled
落地方洞挑战图片与运行态交互
2026-05-06 12:52:47 +08:00

138 lines
3.6 KiB
TypeScript

/* @vitest-environment jsdom */
import { fireEvent, render, screen, waitFor } from '@testing-library/react';
import { beforeEach, expect, test, vi } from 'vitest';
import type {
DropSquareHoleShapeRequest,
SquareHoleRunSnapshot,
} from '../../../packages/shared/src/contracts/squareHoleRuntime';
import { SquareHoleRuntimeShell } from './SquareHoleRuntimeShell';
function buildRun(): SquareHoleRunSnapshot {
return {
runId: 'run-1',
profileId: 'profile-1',
ownerUserId: 'user-1',
status: 'running',
snapshotVersion: 3,
startedAtMs: Date.now(),
durationLimitMs: 60_000,
remainingMs: 60_000,
totalShapeCount: 8,
completedShapeCount: 0,
combo: 0,
bestCombo: 0,
score: 0,
ruleLabel: '把当前选项投入指定洞口',
backgroundImageSrc: null,
currentShape: {
shapeId: 'shape-1',
shapeKind: 'square',
label: '当前选项',
targetHoleId: 'hole-b',
color: '#38bdf8',
imageSrc: null,
},
holes: [
{
holeId: 'hole-a',
holeKind: 'hole-a',
label: '洞口 A',
x: 0.28,
y: 0.32,
imageSrc: null,
},
{
holeId: 'hole-b',
holeKind: 'hole-b',
label: '洞口 B',
x: 0.66,
y: 0.52,
imageSrc: null,
},
],
lastFeedback: null,
};
}
function renderRuntime() {
const run = buildRun();
const onDropShape = vi.fn(async (_payload: DropSquareHoleShapeRequest) => ({
feedback: {
accepted: true,
rejectReason: null,
message: '已投入',
},
run,
}));
render(
<SquareHoleRuntimeShell
run={run}
onBack={vi.fn()}
onRestart={vi.fn()}
onDropShape={onDropShape}
/>,
);
return { onDropShape };
}
beforeEach(() => {
Object.defineProperty(HTMLElement.prototype, 'setPointerCapture', {
configurable: true,
value: vi.fn(),
});
Object.defineProperty(HTMLElement.prototype, 'releasePointerCapture', {
configurable: true,
value: vi.fn(),
});
});
test('点击洞口会提交该洞口选择', async () => {
const { onDropShape } = renderRuntime();
fireEvent.click(screen.getByRole('button', { name: '投入 洞口 B' }));
await waitFor(() => expect(onDropShape).toHaveBeenCalledTimes(1));
expect(onDropShape.mock.calls[0]?.[0]).toMatchObject({
runId: 'run-1',
holeId: 'hole-b',
clientSnapshotVersion: 3,
});
});
test('引导高亮不会默认指向当前正确洞口', () => {
renderRuntime();
const correctHole = screen.getByRole('button', { name: '投入 洞口 B' });
const hintedHole = screen.getByRole('button', { name: '投入 洞口 A' });
expect(correctHole.className).not.toContain('ring-2');
expect(hintedHole.className).toContain('ring-2');
});
test('拖拽当前选项到洞口上松开会提交该洞口选择', async () => {
const { onDropShape } = renderRuntime();
const shape = screen.getByRole('button', { name: '拖拽当前选项' });
const hole = screen.getByRole('button', { name: '投入 洞口 A' });
const elementsFromPoint = vi.fn(() => [hole]);
Object.defineProperty(document, 'elementsFromPoint', {
configurable: true,
value: elementsFromPoint,
});
fireEvent.pointerDown(shape, { pointerId: 1, clientX: 20, clientY: 20 });
fireEvent.pointerMove(shape, { pointerId: 1, clientX: 140, clientY: 160 });
fireEvent.pointerUp(shape, { pointerId: 1, clientX: 140, clientY: 160 });
await waitFor(() => expect(onDropShape).toHaveBeenCalledTimes(1));
expect(onDropShape.mock.calls[0]?.[0]).toMatchObject({
runId: 'run-1',
holeId: 'hole-a',
clientSnapshotVersion: 3,
});
expect(elementsFromPoint).toHaveBeenCalled();
});