import { cleanup, fireEvent, render, screen } from '@testing-library/react'; import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest'; import MobileDrawer from './MobileDrawer'; vi.mock('react-i18next', () => ({ useTranslation: () => ({ t: (key: string) => key, }), })); const originalSetPointerCapture = HTMLElement.prototype.setPointerCapture; function renderDrawer(onClose = vi.fn()) { const view = render(
Area content
} renderProperties={() =>
Properties content
} tab="area" onTabChange={vi.fn()} /> ); const handle = view.container.querySelector('[data-mobile-drawer-drag-handle]'); const root = view.container.querySelector('[data-tutorial="right-pane"]'); const panel = view.container.querySelector('[data-tutorial="right-pane"] > div:last-child'); if (!(handle instanceof HTMLElement)) throw new Error('Expected drawer drag handle'); if (!(root instanceof HTMLElement)) throw new Error('Expected drawer root'); if (!(panel instanceof HTMLElement)) throw new Error('Expected drawer panel'); return { ...view, handle, onClose, panel, root }; } describe('MobileDrawer', () => { beforeEach(() => { HTMLElement.prototype.setPointerCapture = vi.fn(); }); afterEach(() => { cleanup(); Object.defineProperty(HTMLElement.prototype, 'setPointerCapture', { configurable: true, value: originalSetPointerCapture, }); }); it('lowers and stays open when swiped down from the handle', () => { const { handle, onClose, panel } = renderDrawer(); fireEvent.pointerDown(handle, { pointerId: 1, clientY: 120 }); fireEvent.pointerMove(handle, { pointerId: 1, clientY: 230 }); fireEvent.pointerUp(handle, { pointerId: 1, clientY: 230 }); expect(onClose).not.toHaveBeenCalled(); expect(panel.style.transform).toBe('translateY(110px)'); }); it('can be raised again after being lowered', () => { const { handle, onClose, panel } = renderDrawer(); fireEvent.pointerDown(handle, { pointerId: 1, clientY: 120 }); fireEvent.pointerMove(handle, { pointerId: 1, clientY: 230 }); fireEvent.pointerUp(handle, { pointerId: 1, clientY: 230 }); fireEvent.pointerDown(handle, { pointerId: 2, clientY: 230 }); fireEvent.pointerMove(handle, { pointerId: 2, clientY: 170 }); fireEvent.pointerUp(handle, { pointerId: 2, clientY: 170 }); expect(onClose).not.toHaveBeenCalled(); expect(panel.style.transform).toBe('translateY(50px)'); }); it('keeps the close control reachable when dragged down far', () => { const { handle, panel } = renderDrawer(); Object.defineProperty(panel, 'offsetHeight', { configurable: true, value: 200, }); fireEvent.pointerDown(handle, { pointerId: 1, clientY: 120 }); fireEvent.pointerMove(handle, { pointerId: 1, clientY: 420 }); fireEvent.pointerUp(handle, { pointerId: 1, clientY: 420 }); expect(panel.style.transform).toBe('translateY(96px)'); }); it('leaves the rest of the mobile map usable while the panel is open', () => { const { panel, root } = renderDrawer(); const spacer = root.firstElementChild; if (!(spacer instanceof HTMLElement)) throw new Error('Expected drawer spacer'); expect(root.className).toContain('pointer-events-none'); expect(panel.className).toContain('pointer-events-auto'); expect(spacer.className).not.toContain('bg-black'); }); it('closes from the close button', () => { const { onClose } = renderDrawer(); fireEvent.click(screen.getByLabelText('mobileDrawer.closeDrawer')); expect(onClose).toHaveBeenCalledTimes(1); }); });