mirror of
https://github.com/mauriceboe/TREK.git
synced 2026-10-19 13:36:48 +00:00
106 lines
3.8 KiB
TypeScript
106 lines
3.8 KiB
TypeScript
// FE-HOOK-ELSIZE-001 to FE-HOOK-ELSIZE-006
|
|
import { act, renderHook } from '@testing-library/react';
|
|
import { useElementSize } from './useElementSize';
|
|
|
|
// tests/setup.ts installs a no-op ResizeObserver. Swap in one that hands the
|
|
// callback back so a resize can be simulated, and count the connections so the
|
|
// disconnect-on-detach contract is observable.
|
|
const observers: { el: Element; fire: () => void; disconnected: boolean }[] = [];
|
|
|
|
class ControllableResizeObserver {
|
|
private entry: { el: Element; fire: () => void; disconnected: boolean } | null = null;
|
|
constructor(private cb: ResizeObserverCallback) {}
|
|
observe(el: Element) {
|
|
this.entry = { el, fire: () => this.cb([], this as unknown as ResizeObserver), disconnected: false };
|
|
observers.push(this.entry);
|
|
}
|
|
unobserve() {}
|
|
disconnect() {
|
|
if (this.entry) this.entry.disconnected = true;
|
|
}
|
|
}
|
|
|
|
function element(width: number, height: number): HTMLElement {
|
|
const el = document.createElement('div');
|
|
Object.defineProperty(el, 'offsetWidth', { value: width, configurable: true });
|
|
Object.defineProperty(el, 'offsetHeight', { value: height, configurable: true });
|
|
return el;
|
|
}
|
|
|
|
function resize(el: HTMLElement, width: number, height: number): void {
|
|
Object.defineProperty(el, 'offsetWidth', { value: width, configurable: true });
|
|
Object.defineProperty(el, 'offsetHeight', { value: height, configurable: true });
|
|
}
|
|
|
|
beforeEach(() => {
|
|
observers.length = 0;
|
|
vi.stubGlobal('ResizeObserver', ControllableResizeObserver);
|
|
});
|
|
|
|
afterEach(() => {
|
|
vi.unstubAllGlobals();
|
|
});
|
|
|
|
describe('useElementSize', () => {
|
|
it('FE-HOOK-ELSIZE-001: starts at zero before an element is attached', () => {
|
|
const { result } = renderHook(() => useElementSize());
|
|
expect(result.current.width).toBe(0);
|
|
expect(result.current.height).toBe(0);
|
|
expect(observers).toHaveLength(0);
|
|
});
|
|
|
|
it('FE-HOOK-ELSIZE-002: measures the element as soon as the ref is attached', () => {
|
|
const { result } = renderHook(() => useElementSize());
|
|
act(() => result.current.ref(element(320, 180)));
|
|
|
|
expect(result.current.width).toBe(320);
|
|
expect(result.current.height).toBe(180);
|
|
expect(observers).toHaveLength(1);
|
|
});
|
|
|
|
it('FE-HOOK-ELSIZE-003: picks up a later resize of the same element', () => {
|
|
const { result } = renderHook(() => useElementSize());
|
|
const el = element(320, 180);
|
|
act(() => result.current.ref(el));
|
|
|
|
resize(el, 640, 400);
|
|
act(() => observers[0].fire());
|
|
|
|
expect(result.current.width).toBe(640);
|
|
expect(result.current.height).toBe(400);
|
|
});
|
|
|
|
it('FE-HOOK-ELSIZE-004: keeps the same state object when the size is unchanged', () => {
|
|
const { result } = renderHook(() => useElementSize());
|
|
act(() => result.current.ref(element(200, 100)));
|
|
const before = result.current;
|
|
|
|
act(() => observers[0].fire());
|
|
|
|
// No re-render — the hook bailed out of setState because nothing moved.
|
|
expect(result.current.width).toBe(before.width);
|
|
expect(result.current.height).toBe(before.height);
|
|
});
|
|
|
|
it('FE-HOOK-ELSIZE-005: disconnects the old observer when the ref moves to another element', () => {
|
|
const { result } = renderHook(() => useElementSize());
|
|
act(() => result.current.ref(element(100, 50)));
|
|
act(() => result.current.ref(element(300, 150)));
|
|
|
|
expect(observers).toHaveLength(2);
|
|
expect(observers[0].disconnected).toBe(true);
|
|
expect(observers[1].disconnected).toBe(false);
|
|
expect(result.current.width).toBe(300);
|
|
});
|
|
|
|
it('FE-HOOK-ELSIZE-006: disconnects when React detaches the ref with null', () => {
|
|
const { result } = renderHook(() => useElementSize());
|
|
act(() => result.current.ref(element(100, 50)));
|
|
act(() => result.current.ref(null));
|
|
|
|
expect(observers[0].disconnected).toBe(true);
|
|
// The last measured size is kept — only the observation stops.
|
|
expect(result.current.width).toBe(100);
|
|
});
|
|
});
|