import { render } from '@testing-library/react';
import { afterEach, describe, expect, it, vi } from 'vitest';
import { useMeasuredHeight, useReportedHeight } from './useReportedHeight';
function Card({ onHeight, open = true }: { onHeight?: (height: number) => void; open?: boolean }) {
const ref = useReportedHeight(onHeight);
return open ?
card
: null;
}
const original = Object.getOwnPropertyDescriptor(HTMLElement.prototype, 'offsetHeight');
afterEach(() => {
if (original) Object.defineProperty(HTMLElement.prototype, 'offsetHeight', original);
vi.unstubAllGlobals();
});
describe('useReportedHeight', () => {
it('REPORTED-HEIGHT-001: reports the height on mount and 0 once the element is gone', () => {
Object.defineProperty(HTMLElement.prototype, 'offsetHeight', { configurable: true, get: () => 412 });
const onHeight = vi.fn();
const { rerender } = render();
expect(onHeight).toHaveBeenLastCalledWith(412);
rerender();
expect(onHeight).toHaveBeenLastCalledWith(0);
});
it('REPORTED-HEIGHT-002: reports again whenever the element resizes', () => {
let height = 300;
Object.defineProperty(HTMLElement.prototype, 'offsetHeight', { configurable: true, get: () => height });
const observed: { resize?: () => void } = {};
const disconnect = vi.fn();
vi.stubGlobal(
'ResizeObserver',
class {
constructor(cb: () => void) {
observed.resize = cb;
}
observe() {}
disconnect = disconnect;
}
);
const onHeight = vi.fn();
const { unmount } = render();
height = 820;
expect(observed.resize).toBeTypeOf('function');
observed.resize?.();
expect(onHeight).toHaveBeenLastCalledWith(820);
unmount();
expect(disconnect).toHaveBeenCalled();
});
it('REPORTED-HEIGHT-003: does nothing without a listener', () => {
expect(() => render()).not.toThrow();
});
});
describe('useMeasuredHeight', () => {
it('REPORTED-HEIGHT-004: holds the measured height as state', () => {
Object.defineProperty(HTMLElement.prototype, 'offsetHeight', { configurable: true, get: () => 512 });
function Probe() {
const { height, measure } = useMeasuredHeight();
return ;
}
const { container } = render();
expect(container.firstElementChild?.getAttribute('data-height')).toBe('512');
});
});