import OverflowWatcher, { OverFlowWatcherParams, OverflowDirection, OVERFLOWED_CLASS_NAME } from '../../src/utils/OverflowWatcher'; describe('OverflowWatcher', () => { let mockElement: HTMLElement; let mockCallback: jest.Mock; let watcherInstance: OverflowWatcher; beforeEach(() => { mockElement = document.createElement('div'); Object.defineProperty(mockElement, 'clientWidth', { value: 90, configurable: true }); document.body.appendChild(mockElement); mockCallback = jest.fn(); const params: OverFlowWatcherParams = { element: mockElement, callback: mockCallback, overFlowDirection: OverflowDirection.HORIZONTAL, debounce: 1, deltaSize: 1, notOverFlow: ['testId'], }; watcherInstance = new OverflowWatcher(params); mockElement.getBoundingClientRect = jest.fn().mockReturnValue(getMockedRectClientValue(200)); setPrivateField(watcherInstance, '_lastContainerSize', 100); setPrivateField(watcherInstance, '_lastContainerInstance', mockElement); }); afterEach(() => { watcherInstance.destroy(); }); it('should initialize with provided parameters', () => { expect(watcherInstance).toBeDefined(); }); it('should disconect ResizeObserver when destroy is called', () => { const disconnectSpy = jest.spyOn(ResizeObserver.prototype, 'disconnect'); watcherInstance.destroy(); expect(disconnectSpy).toHaveBeenCalled(); }); it('Should call callback on forceUpdate', () => { appendMockedChildren(mockElement); watcherInstance.forceUpdate(); expect(mockCallback).toHaveBeenCalled(); }); it('Should call callback on forceUpdate with childSpan', () => { appendMockedChildren(mockElement); watcherInstance.forceUpdate(); const childSpan = mockElement.children[1]; expect(mockCallback).toHaveBeenCalledWith(expect.arrayContaining([childSpan])); }); it('Should call callback on forceUpdate with childSpan when notOverFlow is empty', () => { appendMockedChildren(mockElement); const params: OverFlowWatcherParams = { element: mockElement, callback: mockCallback, overFlowDirection: OverflowDirection.HORIZONTAL, debounce: 1, deltaSize: 1, }; const withNoOverFlow = new OverflowWatcher(params); setPrivateField(withNoOverFlow, '_lastContainerSize', 100); setPrivateField(withNoOverFlow, '_lastContainerInstance', mockElement); withNoOverFlow.forceUpdate(); const childSpan = mockElement.children[1]; expect(mockCallback).toHaveBeenCalledWith(expect.arrayContaining([childSpan])); }); it('Should call callback on forceUpdate with childSpan considering overflowed elements', () => { appendMockedChildren(mockElement); const childButton = mockElement.children[0]; const childSpan = mockElement.children[1]; childSpan.classList.add(OVERFLOWED_CLASS_NAME); const hiddemItemsMock: Map = new Map(); hiddemItemsMock.set(childSpan, {size: 50, margin: 0}); hiddemItemsMock.set(childButton, {size: 0, margin: 50}); setPrivateField(watcherInstance, '_hiddenItemsProps', hiddemItemsMock); watcherInstance.forceUpdate(); expect(mockCallback).toHaveBeenCalledWith(expect.arrayContaining([childSpan])); }); it('Should call callback on forceUpdate with empty list', () => { appendMockedChildren(mockElement); setPrivateField(watcherInstance, '_lastContainerSize', 200); watcherInstance.forceUpdate(); const childButton = mockElement.children[0]; const childSpan = mockElement.children[1]; expect(mockCallback).not.toHaveBeenCalledWith(expect.arrayContaining([childButton, childSpan])); }); it('should ignore elements that can not overflow', () => { appendMockedChildren(mockElement); const notOverflowElement = document.createElement('div'); notOverflowElement.setAttribute('data-element-id', 'testId') ; notOverflowElement.getBoundingClientRect = jest.fn().mockReturnValue(getMockedRectClientValue(50)); const notOverflowElement2 = document.createElement('div'); notOverflowElement2.setAttribute('data-element-id', 'testId2') ; notOverflowElement2.getBoundingClientRect = jest.fn().mockReturnValue(getMockedRectClientValue(50)); mockElement.appendChild(notOverflowElement); mockElement.appendChild(notOverflowElement2); watcherInstance.addNotOverFlowElement('testId'); watcherInstance.addNotOverFlowElement('testId2'); watcherInstance.forceUpdate(); expect(mockCallback).not.toHaveBeenCalledWith(expect.arrayContaining([notOverflowElement])); }); it('Should not call callback on forceUpdate', () => { watcherInstance.forceUpdate(); expect(mockCallback).not.toHaveBeenCalled(); }); }); function appendMockedChildren(mockElement: HTMLElement) { const chilldButton = document.createElement('button'); const childSpan = document.createElement('span'); chilldButton.getBoundingClientRect = jest.fn().mockReturnValue(getMockedRectClientValue(50)); childSpan.getBoundingClientRect = jest.fn().mockReturnValue(getMockedRectClientValue(50)); mockElement.appendChild(chilldButton); mockElement.appendChild(childSpan); } function getMockedRectClientValue(size: number) { return { x: 0, y: 0, width: size, height: 10, top: 0, right: 0, bottom: 0, left: 0, toJSON: () => {} }; } function setPrivateField(instance: T, fieldName: string, value: any): void { (instance as any)[fieldName] = value; }