/** * Copyright (c) Paymium. * * This source code is licensed under the MIT license found in the * LICENSE file in the root of this projects source tree. */ import { render } from '@crossed/test'; import { DropDownMenu } from '../index'; import { Popover, useFloatingContext } from '../../overlay'; import { MenuList } from '../MenuList'; import { Divider } from '../../layout/Divider'; import { useMedia } from '../../useMedia'; import { Text } from '../../typography'; jest.mock('../../overlay', () => ({ Popover: Object.assign( jest.fn(({ children }) => children), { Trigger: jest.fn(({ children }) => children), Content: jest.fn(({ children }) => children), } ), useFloatingContext: jest.fn(), })); jest.mock('../MenuList', () => ({ MenuList: Object.assign( jest.fn(({ children }) => children), { Item: jest.fn(({ children }) => children), Label: jest.fn(({ children }) => children), Title: jest.fn(({ children }) => children), } ), })); jest.mock('../../layout/Divider', () => ({ Divider: jest.fn(() => null), })); jest.mock('../../useMedia', () => ({ useMedia: jest.fn(), })); const PopoverMocked = jest.mocked(Popover); const MenuListMocked = jest.mocked(MenuList); const useFloatingContextMocked = jest.mocked(useFloatingContext); const useMediaMocked = jest.mocked(useMedia); const DividerMocked = jest.mocked(Divider); const mockUseMedia = (md: boolean) => { useMediaMocked.mockReturnValue({ md, sm: false, lg: false, xl: false, } as any); }; const mockFloatingContext = (onClose = jest.fn()) => { useFloatingContextMocked.mockReturnValue({ onClose, open: true, onOpen: jest.fn(), } as any); }; describe('DropDownMenu', () => { beforeEach(() => { mockUseMedia(true); mockFloatingContext(); }); afterEach(() => { jest.clearAllMocks(); }); test('renders with default props', () => { render( Open Menu Item 1 ); const call = PopoverMocked.mock.calls[0][0]; expect(call).toHaveProperty('placement', 'bottom-end'); expect(call).toHaveProperty('triggerStrategy', 'onPress'); expect(call).toHaveProperty('children'); }); test('accepts custom placement', () => { render( Open Menu Item 1 ); const call = PopoverMocked.mock.calls[0][0]; expect(call).toHaveProperty('placement', 'top-start'); }); test('accepts custom triggerStrategy', () => { render( Open Menu Item 1 ); const call = PopoverMocked.mock.calls[0][0]; expect(call).toHaveProperty('triggerStrategy', 'onPointerEnter'); }); test('forwards additional props to Popover', () => { render( Open Menu Item 1 ); const call = PopoverMocked.mock.calls[0][0]; expect(call).toHaveProperty('offsetValue', 15); }); test('renders all static components', () => { expect(DropDownMenu.Trigger).toBeDefined(); expect(DropDownMenu.Content).toBeDefined(); expect(DropDownMenu.Item).toBeDefined(); expect(DropDownMenu.Label).toBeDefined(); expect(DropDownMenu.Title).toBeDefined(); expect(DropDownMenu.Divider).toBeDefined(); }); test('Content renders MenuList with bordered=true on desktop', () => { mockUseMedia(true); MenuListMocked.mockClear(); render( Open Menu Item 1 ); const menuListCall = MenuListMocked.mock.calls[0][0]; expect(menuListCall).toHaveProperty('bordered', true); }); test('Content renders MenuList with bordered=false on mobile', () => { mockUseMedia(false); MenuListMocked.mockClear(); render( Open Menu Item 1 ); const menuListCall = MenuListMocked.mock.calls[0][0]; expect(menuListCall).toHaveProperty('bordered', false); }); test('Item closes popover on press', () => { const onClose = jest.fn(); mockFloatingContext(onClose); const MenuListItemMocked = jest.mocked(MenuList.Item); render( Open Menu {}}> Item 1 ); const itemCall = MenuListItemMocked.mock.calls[0][0]; expect(itemCall).toHaveProperty('onPress'); if ('onPress' in itemCall && typeof itemCall.onPress === 'function') { itemCall.onPress({} as any); } expect(onClose).toHaveBeenCalledTimes(1); }); test('Item composes custom onPress with onClose', () => { const onClose = jest.fn(); const customOnPress = jest.fn(); mockFloatingContext(onClose); const MenuListItemMocked = jest.mocked(MenuList.Item); render( Open Menu Item 1 ); const itemCall = MenuListItemMocked.mock.calls[0][0]; if ('onPress' in itemCall && typeof itemCall.onPress === 'function') { itemCall.onPress({} as any); } expect(customOnPress).toHaveBeenCalledTimes(1); expect(onClose).toHaveBeenCalledTimes(1); }); test('Label renders MenuList.Label', () => { const MenuListLabelMocked = jest.mocked(MenuList.Label); render( Open Menu Item 1 ); expect(MenuListLabelMocked).toHaveBeenCalled(); }); test('Title renders MenuList.Title', () => { const MenuListTitleMocked = jest.mocked(MenuList.Title); render( Open Menu Menu Title ); expect(MenuListTitleMocked).toHaveBeenCalled(); }); test('Divider renders Divider component', () => { render( Open Menu Item 1 Item 2 ); expect(DividerMocked).toHaveBeenCalled(); }); test('Divider forwards props', () => { DividerMocked.mockClear(); render( Open Menu ); const dividerCall = DividerMocked.mock.calls[0][0]; expect(dividerCall).toHaveProperty('direction', 'vertical'); }); });