76 lines
3.1 KiB
TypeScript
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('');
|
|
});
|