Files
makelore/tests/unit/preview-clearance.test.tsx

76 lines
3.1 KiB
TypeScript

import { useRef } from 'react';
import { act, cleanup, render, screen } from '@testing-library/react';
import { afterEach, beforeEach, expect, it, vi } from 'vitest';
import { usePreviewClearance } from '@/pages/Chat/use-preview-clearance';
let resize: () => void;
let bubbleRect: DOMRect;
let pageRect: DOMRect;
const bubble = document.createElement('div');
const rect = (x: number, y: number, width: number, height: number) => new DOMRect(x, y, width, height);
function Harness({ visible = true, open = true }: { visible?: boolean; open?: boolean }) {
const panel = useRef<HTMLDivElement>(null);
const viewport = useRef<HTMLDivElement>(null);
usePreviewClearance(panel, viewport, visible ? bubble : null, open);
return <div ref={panel} data-testid="panel"><div ref={viewport} data-testid="viewport" /></div>;
}
beforeEach(() => {
bubbleRect = rect(960, 48, 340, 164);
pageRect = rect(192, 184, 1200, 600);
vi.stubGlobal('ResizeObserver', class {
constructor(callback: () => void) { resize = callback; }
observe() {}
disconnect() {}
});
vi.spyOn(HTMLElement.prototype, 'getBoundingClientRect').mockImplementation(function (this: HTMLElement) {
if (this === bubble) return bubbleRect;
const offset = Number.parseFloat(this.parentElement?.style.marginTop ?? '') || 0;
return rect(pageRect.x, pageRect.y + offset, pageRect.width, pageRect.height - offset);
});
});
afterEach(() => { cleanup(); vi.restoreAllMocks(); vi.unstubAllGlobals(); });
it('keeps the work at the top when the bubble is beside it in the teacher pane', () => {
pageRect = rect(192, 184, 650, 600);
render(<Harness />);
expect(screen.getByTestId('panel').style.marginTop).toBe('');
});
it('reserves only the missing native-page clearance and stays stable across repeated resize notifications', () => {
render(<Harness />);
expect(screen.getByTestId('panel').style.marginTop).toBe('36px');
act(() => { resize(); resize(); resize(); });
expect(screen.getByTestId('panel').style.marginTop).toBe('36px');
bubbleRect = rect(960, 48, 340, 110);
act(() => resize());
expect(screen.getByTestId('panel').style.marginTop).toBe('');
});
it('returns space when the side pane opens and recalculates when it closes', () => {
render(<Harness />);
pageRect = rect(192, 184, 650, 600);
act(() => resize());
expect(screen.getByTestId('panel').style.marginTop).toBe('');
pageRect = rect(192, 184, 1200, 600);
act(() => window.dispatchEvent(new Event('resize')));
expect(screen.getByTestId('panel').style.marginTop).toBe('36px');
});
it('needs no extra space when the existing toolbar already clears the bubble', () => {
bubbleRect = rect(960, 48, 340, 110);
render(<Harness />);
expect(screen.getByTestId('panel').style.marginTop).toBe('');
});
it('removes clearance when the bubble is dismissed or Work closes', () => {
const view = render(<Harness />);
view.rerender(<Harness visible={false} />);
expect(screen.getByTestId('panel').style.marginTop).toBe('');
view.rerender(<Harness />);
expect(screen.getByTestId('panel').style.marginTop).toBe('36px');
view.rerender(<Harness open={false} />);
expect(screen.getByTestId('panel').style.marginTop).toBe('');
});