import { afterAll, afterEach, beforeAll, beforeEach, describe, expect, it, vi, } from 'vitest'; import { MediaHeightController, SET_HEIGHT_DEBOUNCE_SECONDS, } from '../../src/components-lib/media-height-controller'; import { callMutationHandler, callResizeHandler, MutationObserverMock, ResizeObserverMock, } from '../test-utils'; // @vitest-environment jsdom describe('MediaHeightController', () => { beforeAll(() => { vi.stubGlobal('MutationObserver', MutationObserverMock); vi.stubGlobal('ResizeObserver', ResizeObserverMock); }); afterAll(() => { vi.unstubAllGlobals(); }); beforeEach(() => { vi.clearAllMocks(); vi.useFakeTimers(); }); afterEach(() => { vi.useRealTimers(); }); describe('should set height', () => { it('should set height on selection', () => { const host = document.createElement('div'); const controller = new MediaHeightController(host, 'div'); const root = document.createElement('div'); const child = document.createElement('div'); child.getBoundingClientRect = vi.fn().mockReturnValue({ height: 600, }); root.appendChild(child); controller.setRoot(root); // Calling a second time has no effect. controller.setRoot(root); controller.setSelected(0); vi.advanceTimersByTime(SET_HEIGHT_DEBOUNCE_SECONDS * 1000); expect(host.style.maxHeight).toBe(`600px`); }); it('should not set height without children', () => { const host = document.createElement('div'); const controller = new MediaHeightController(host, 'div'); const root = document.createElement('div'); controller.setRoot(root); controller.setSelected(10); vi.advanceTimersByTime(SET_HEIGHT_DEBOUNCE_SECONDS * 1000); expect(host.style.maxHeight).toBe(''); }); it('should respond to resize observer of selected child', () => { const host = document.createElement('div'); const controller = new MediaHeightController(host, 'div'); const root = document.createElement('div'); const child = document.createElement('div'); root.appendChild(child); controller.setRoot(root); controller.setSelected(0); child.getBoundingClientRect = vi.fn().mockReturnValue({ height: 800, }); callResizeHandler([ { target: child, height: 800, width: 400, }, ]); vi.advanceTimersByTime(SET_HEIGHT_DEBOUNCE_SECONDS * 1000); expect(host.style.maxHeight).toBe('800px'); }); it('should not respond to resize observer without a selected child', () => { const host = document.createElement('div'); const controller = new MediaHeightController(host, 'div'); const root = document.createElement('div'); const child = document.createElement('div'); root.appendChild(child); controller.setRoot(root); child.getBoundingClientRect = vi.fn().mockReturnValue({ height: 800, }); callResizeHandler([ { target: child, height: 800, width: 400, }, ]); vi.advanceTimersByTime(SET_HEIGHT_DEBOUNCE_SECONDS * 1000); expect(host.style.maxHeight).toBe(''); }); it('should respond to new children being added', () => { const host = document.createElement('div'); const controller = new MediaHeightController(host, 'div'); const root = document.createElement('div'); const child_0 = document.createElement('div'); child_0.getBoundingClientRect = vi.fn().mockReturnValue({ height: 100, }); root.appendChild(child_0); controller.setRoot(root); const child_1 = document.createElement('div'); child_1.getBoundingClientRect = vi.fn().mockReturnValue({ height: 200, }); root.appendChild(child_1); callMutationHandler(); controller.setSelected(1); vi.advanceTimersByTime(SET_HEIGHT_DEBOUNCE_SECONDS * 1000); expect(host.style.maxHeight).toBe('200px'); }); it('should set height on recalculate', () => { const host = document.createElement('div'); const controller = new MediaHeightController(host, 'div'); const root = document.createElement('div'); const child = document.createElement('div'); child.getBoundingClientRect = vi.fn().mockReturnValue({ height: 700, }); root.appendChild(child); controller.setRoot(root); controller.setSelected(0); vi.advanceTimersByTime(SET_HEIGHT_DEBOUNCE_SECONDS * 1000); expect(host.style.maxHeight).toBe('700px'); child.getBoundingClientRect = vi.fn().mockReturnValue({ height: 900, }); controller.recalculate(); vi.advanceTimersByTime(SET_HEIGHT_DEBOUNCE_SECONDS * 1000); expect(host.style.maxHeight).toBe('900px'); }); it('should not set height when the selected child has no height', () => { const host = document.createElement('div'); const controller = new MediaHeightController(host, 'div'); const root = document.createElement('div'); const child = document.createElement('div'); child.getBoundingClientRect = vi.fn().mockReturnValue({ height: 0, }); root.appendChild(child); controller.setRoot(root); controller.setSelected(0); vi.advanceTimersByTime(SET_HEIGHT_DEBOUNCE_SECONDS * 1000); expect(host.style.maxHeight).toBe(''); }); it('should allow height to shrink when selected child is shorter', () => { const host = document.createElement('div'); const controller = new MediaHeightController(host, 'div'); const root = document.createElement('div'); const child_0 = document.createElement('div'); child_0.getBoundingClientRect = vi.fn().mockReturnValue({ height: 750, }); const child_1 = document.createElement('div'); child_1.getBoundingClientRect = vi.fn().mockReturnValue({ height: 562, }); root.appendChild(child_0); root.appendChild(child_1); controller.setRoot(root); controller.setSelected(0); vi.advanceTimersByTime(SET_HEIGHT_DEBOUNCE_SECONDS * 1000); expect(host.style.maxHeight).toBe('750px'); controller.setSelected(1); vi.advanceTimersByTime(SET_HEIGHT_DEBOUNCE_SECONDS * 1000); expect(host.style.maxHeight).toBe('562px'); }); }); it('should destroy', () => { const host = document.createElement('div'); const controller = new MediaHeightController(host, 'div'); controller.destroy(); // No observable effect. }); });