117 lines
4.0 KiB
TypeScript
117 lines
4.0 KiB
TypeScript
// @vitest-environment jsdom
|
|
import { act, createElement } from 'react';
|
|
import { createRoot } from 'react-dom/client';
|
|
import { afterEach, describe, expect, it, vi } from 'vitest';
|
|
import { ElementPickLayer } from '@/components/edit/surfaces/slide/ElementPickLayer';
|
|
import { editableElementDomId } from '@/components/edit/surfaces/slide/renderer-element-dom';
|
|
import { useCanvasStore } from '@/lib/store/canvas';
|
|
import { useStageStore } from '@/lib/store/stage';
|
|
import type { Scene } from '@/lib/types/stage';
|
|
|
|
vi.mock('@/lib/hooks/use-i18n', () => ({
|
|
useI18n: () => ({ t: (key: string) => key }),
|
|
}));
|
|
|
|
const scene = {
|
|
id: 'scene-1',
|
|
stageId: 'stage-1',
|
|
order: 1,
|
|
title: 'Slide',
|
|
type: 'slide',
|
|
content: {
|
|
type: 'slide',
|
|
canvas: {
|
|
id: 'slide-1',
|
|
viewportSize: 1000,
|
|
viewportRatio: 0.5625,
|
|
elements: [
|
|
{
|
|
id: 'title-1',
|
|
type: 'text',
|
|
left: 0,
|
|
top: 0,
|
|
width: 100,
|
|
height: 40,
|
|
rotate: 0,
|
|
content: '<p>Title</p>',
|
|
defaultFontName: 'Inter',
|
|
defaultColor: '#111111',
|
|
},
|
|
],
|
|
},
|
|
},
|
|
actions: [{ id: 'spotlight-1', type: 'spotlight', elementId: '' }],
|
|
} as Scene;
|
|
|
|
afterEach(() => {
|
|
useCanvasStore.getState().resetCanvasState();
|
|
useStageStore.setState({ scenes: [], currentSceneId: null });
|
|
document.body.innerHTML = '';
|
|
vi.unstubAllGlobals();
|
|
});
|
|
|
|
describe('ElementPickLayer renderer DOM integration', () => {
|
|
it('measures a renderer element host and binds the picked id to the timeline action', async () => {
|
|
useStageStore.setState({ scenes: [scene], currentSceneId: scene.id });
|
|
useCanvasStore.getState().setPickTarget({
|
|
sceneId: scene.id,
|
|
actionId: 'spotlight-1',
|
|
cueType: 'spotlight',
|
|
});
|
|
|
|
const rendererHost = document.createElement('div');
|
|
rendererHost.id = editableElementDomId('title-1');
|
|
rendererHost.getBoundingClientRect = () =>
|
|
({ left: 0, top: 0, width: 1000, height: 562.5, right: 1000, bottom: 562.5 }) as DOMRect;
|
|
const hitTarget = document.createElement('div');
|
|
hitTarget.className = 'slide-element-hit-target';
|
|
const paintNode = document.createElement('div');
|
|
paintNode.className = 'base-element-text';
|
|
paintNode.getBoundingClientRect = () =>
|
|
({ left: 40, top: 30, width: 120, height: 50, right: 160, bottom: 80 }) as DOMRect;
|
|
hitTarget.appendChild(paintNode);
|
|
rendererHost.appendChild(hitTarget);
|
|
document.body.appendChild(rendererHost);
|
|
const interactionTarget = document.createElement('div');
|
|
interactionTarget.dataset.selectElementId = 'title-1';
|
|
document.body.appendChild(interactionTarget);
|
|
Object.defineProperty(document, 'elementsFromPoint', {
|
|
configurable: true,
|
|
value: () => [interactionTarget],
|
|
});
|
|
vi.stubGlobal('requestAnimationFrame', (callback: FrameRequestCallback) => {
|
|
callback(0);
|
|
return 1;
|
|
});
|
|
|
|
const container = document.createElement('div');
|
|
document.body.appendChild(container);
|
|
const root = createRoot(container);
|
|
await act(async () => {
|
|
root.render(createElement(ElementPickLayer));
|
|
});
|
|
|
|
const outline = container.querySelector('.ring-violet-400\\/40') as HTMLElement;
|
|
expect(outline.style.left).toBe('40px');
|
|
expect(outline.style.top).toBe('30px');
|
|
expect(outline.style.width).toBe('120px');
|
|
expect(outline.style.height).toBe('50px');
|
|
|
|
const clickCatcher = container.querySelector('.cursor-crosshair') as HTMLElement;
|
|
await act(async () => {
|
|
clickCatcher.dispatchEvent(
|
|
new MouseEvent('mousemove', { bubbles: true, clientX: 60, clientY: 50 }),
|
|
);
|
|
});
|
|
await act(async () => {
|
|
clickCatcher.dispatchEvent(new MouseEvent('click', { bubbles: true }));
|
|
});
|
|
|
|
const action = useStageStore.getState().scenes[0].actions?.[0] as { elementId?: string };
|
|
expect(action.elementId).toBe('title-1');
|
|
expect(useCanvasStore.getState().pickTarget).toBeNull();
|
|
|
|
await act(async () => root.unmount());
|
|
});
|
|
});
|