import { spyElementPrototype } from 'rc-util/lib/test/domHook'; import React from 'react'; import type { TooltipPlacement } from '..'; import Tooltip from '..'; import mountTest from '../../../tests/shared/mountTest'; import rtlTest from '../../../tests/shared/rtlTest'; import { fireEvent, render, waitFakeTimer, waitFor, act } from '../../../tests/utils'; import Button from '../../button'; import DatePicker from '../../date-picker'; import Input from '../../input'; import Group from '../../input/Group'; import Switch from '../../switch'; import Radio from '../../radio'; import { resetWarned } from '../../_util/warning'; describe('Tooltip', () => { mountTest(Tooltip); rtlTest(Tooltip); beforeAll(() => { spyElementPrototype(HTMLElement, 'offsetParent', { get: () => ({}), }); }); it('check `onOpenChange` arguments', () => { const onOpenChange = jest.fn(); const ref = React.createRef(); const { container, rerender } = render(
Hello world!
, ); // `title` is empty. const divElement = container.querySelector('#hello'); fireEvent.mouseEnter(divElement!); expect(onOpenChange).not.toHaveBeenCalled(); expect(ref.current.props.visible).toBe(false); expect(container.querySelector('.ant-tooltip-open')).toBeNull(); fireEvent.mouseLeave(divElement!); expect(onOpenChange).not.toHaveBeenCalled(); expect(ref.current.props.visible).toBe(false); expect(container.querySelector('.ant-tooltip-open')).toBeNull(); // update `title` value. rerender(
Hello world!
, ); fireEvent.mouseEnter(divElement!); expect(onOpenChange).toHaveBeenLastCalledWith(true); expect(ref.current.props.visible).toBe(true); expect(container.querySelector('.ant-tooltip-open')).not.toBeNull(); fireEvent.mouseLeave(divElement!); expect(onOpenChange).toHaveBeenLastCalledWith(false); expect(ref.current.props.visible).toBe(false); expect(container.querySelector('.ant-tooltip-open')).toBeNull(); // add `open` props. rerender(
Hello world!
, ); fireEvent.mouseEnter(divElement!); expect(onOpenChange).toHaveBeenLastCalledWith(true); const lastCount = onOpenChange.mock.calls.length; expect(ref.current.props.visible).toBe(false); expect(container.querySelector('.ant-tooltip-open')).toBeNull(); // always trigger onOpenChange fireEvent.mouseLeave(divElement!); expect(onOpenChange.mock.calls.length).toBe(lastCount); // no change with lastCount expect(ref.current.props.visible).toBe(false); expect(container.querySelector('.ant-tooltip-open')).toBeNull(); }); it('should hide when mouse leave native disabled button', () => { const onOpenChange = jest.fn(); const ref = React.createRef(); const { container } = render( , ); expect(container.getElementsByTagName('span')).toHaveLength(1); const button = container.getElementsByTagName('span')[0]; fireEvent.mouseEnter(button); expect(onOpenChange).toHaveBeenCalledWith(true); expect(ref.current?.props.visible).toBe(true); expect(container.querySelector('.ant-tooltip-open')).not.toBeNull(); fireEvent.mouseLeave(button); expect(onOpenChange).toHaveBeenCalledWith(false); expect(ref.current?.props.visible).toBe(false); expect(container.querySelector('.ant-tooltip-open')).toBeNull(); }); describe('should hide when mouse leave antd disabled component', () => { function testComponent(name: string, Component: typeof Button | typeof Switch) { it(name, () => { const onOpenChange = jest.fn(); const ref = React.createRef(); const { container } = render( , ); expect(container.children[0]).toMatchSnapshot(); const button = container.getElementsByTagName('span')[0]; fireEvent.mouseEnter(button); expect(onOpenChange).toHaveBeenCalledWith(true); expect(ref.current.props.visible).toBe(true); expect(container.querySelector('.ant-tooltip-open')).not.toBeNull(); fireEvent.mouseLeave(button); expect(onOpenChange).toHaveBeenCalledWith(false); expect(ref.current.props.visible).toBe(false); expect(container.querySelector('.ant-tooltip-open')).toBeNull(); }); } testComponent('Button', Button); testComponent('Switch', Switch); }); it('should render disabled Button style properly', () => { const { container: containerInline } = render( , ); const { container: containerBlock } = render( , ); expect(containerInline.getElementsByTagName('span')[0].style.display).toBe('inline-block'); expect(containerBlock.getElementsByTagName('span')[0].style.display).toBe('block'); }); it('should works for arrowPointAtCenter', () => { const arrowWidth = 5; const horizontalArrowShift = 16; const triggerWidth = 200; const suit = () => { const { container } = render( , ); fireEvent.click(container.getElementsByTagName('button')[0]); const popupLeftDefault = parseInt( container.querySelector('.default-element')?.style?.left!, 10, ); const { container: container2 } = render( , ); fireEvent.click(container2.getElementsByTagName('button')[0]); const popupLeftArrowPointAtCenter = parseInt( container.querySelector('.point-center-element')?.style?.left!, 10, ); expect(popupLeftArrowPointAtCenter - popupLeftDefault).toBe( triggerWidth / 2 - horizontalArrowShift - arrowWidth, ); }; (jest.dontMock as any)('rc-trigger', suit); }); it('should works for date picker', async () => { jest.useFakeTimers(); const onOpenChange = jest.fn(); const ref = React.createRef(); const { container } = render( , ); expect(container.getElementsByClassName('ant-picker')).toHaveLength(1); const picker = container.getElementsByClassName('ant-picker')[0]; fireEvent.mouseEnter(picker); await waitFakeTimer(); expect(onOpenChange).toHaveBeenCalledWith(true); expect(ref.current?.props.visible).toBe(true); expect(container.querySelector('.ant-tooltip-open')).not.toBeNull(); fireEvent.mouseLeave(picker); await waitFakeTimer(); expect(onOpenChange).toHaveBeenCalledWith(false); expect(ref.current?.props.visible).toBe(false); expect(container.querySelector('.ant-tooltip-open')).toBeNull(); jest.clearAllTimers(); jest.useRealTimers(); }); it('should works for input group', async () => { jest.useFakeTimers(); const onOpenChange = jest.fn(); const ref = React.createRef(); const { container } = render( , ); expect(container.getElementsByClassName('ant-input-group')).toHaveLength(1); const inputGroup = container.getElementsByClassName('ant-input-group')[0]; fireEvent.mouseEnter(inputGroup); await waitFakeTimer(); expect(onOpenChange).toHaveBeenCalledWith(true); expect(ref.current.props.visible).toBe(true); expect(container.querySelector('.ant-tooltip-open')).not.toBeNull(); fireEvent.mouseLeave(inputGroup); await waitFakeTimer(); expect(onOpenChange).toHaveBeenCalledWith(false); expect(ref.current.props.visible).toBe(false); expect(container.querySelector('.ant-tooltip-open')).toBeNull(); jest.clearAllTimers(); jest.useRealTimers(); }); // https://github.com/ant-design/ant-design/issues/20891 it('should display zero', () => { const { container } = render(
, ); expect(container.querySelector('.ant-tooltip-inner')?.innerHTML).toBe('0'); }); it('autoAdjustOverflow should be object or undefined', () => { expect(() => { render(
, ); }).not.toThrow(); expect(() => { render(
, ); }).not.toThrow(); }); describe('support other placement when mouse enter', () => { beforeAll(() => { jest.useFakeTimers(); }); afterAll(() => { jest.useRealTimers(); }); afterEach(() => { jest.clearAllTimers(); }); const placementList = [ 'top', 'left', 'right', 'bottom', 'topLeft', 'topRight', 'bottomLeft', 'bottomRight', 'leftTop', 'leftBottom', 'rightTop', 'rightBottom', ] as const; const testPlacement = (name: string, placement: TooltipPlacement) => { it(name, async () => { const { container } = render( Hello world! , ); expect(container.getElementsByTagName('span')).toHaveLength(1); const element = container.getElementsByTagName('span')[0]; fireEvent.mouseEnter(element); await waitFakeTimer(); await waitFor(() => { expect(document.querySelector(`.ant-tooltip-placement-${placement}`)).not.toBeNull(); }); }); }; placementList.forEach((placement) => testPlacement(`Placement ${placement}`, placement)); }); it('should works for mismatch placement', async () => { const { container } = render( Hello world! , ); const button = container.getElementsByTagName('span')[0]; fireEvent.mouseEnter(button); await waitFakeTimer(); expect(document.querySelector('.ant-tooltip')).not.toBeNull(); }); it('should pass overlayInnerStyle through to the inner component', () => { const { container } = render(
, ); expect(container.querySelector('.ant-tooltip-inner')?.style?.color).toBe('red'); }); it('should work with loading switch', () => { const onOpenChange = jest.fn(); const { container } = render( , ); const wrapperEl = container.querySelectorAll('.ant-tooltip-disabled-compatible-wrapper'); expect(wrapperEl).toHaveLength(1); fireEvent.mouseEnter(container.getElementsByTagName('span')[0]); expect(onOpenChange).toHaveBeenLastCalledWith(true); expect(container.querySelector('.ant-tooltip-open')).not.toBeNull(); }); it('should work with disabled Radio', () => { const onOpenChange = jest.fn(); const { container } = render( , ); const wrapperEl = container.querySelectorAll('.ant-tooltip-disabled-compatible-wrapper'); expect(wrapperEl).toHaveLength(1); fireEvent.mouseEnter(container.getElementsByTagName('span')[0]); expect(onOpenChange).toHaveBeenLastCalledWith(true); expect(container.querySelector('.ant-tooltip-open')).not.toBeNull(); }); it('should work with Fragment children', () => { const onOpenChange = jest.fn(); const ref = React.createRef(); const { container } = render( <>
Hello world!
Hello world!
, ); const divElement = container.querySelector('.hello'); fireEvent.mouseEnter(divElement!); expect(onOpenChange).toHaveBeenLastCalledWith(true); expect(ref.current.props.visible).toBe(true); expect(container.querySelector('.ant-tooltip-open')).not.toBeNull(); fireEvent.mouseLeave(divElement!); expect(onOpenChange).toHaveBeenLastCalledWith(false); expect(ref.current.props.visible).toBe(false); expect(container.querySelector('.ant-tooltip-open')).toBeNull(); }); it('deprecated warning', () => { resetWarned(); jest.useFakeTimers(); const errSpy = jest.spyOn(console, 'error').mockImplementation(() => {}); // defaultVisible const { container, rerender } = render( , ); act(() => { jest.runAllTimers(); }); expect(errSpy).toHaveBeenCalledWith( 'Warning: [antd: Tooltip] `defaultVisible` is deprecated, please use `defaultOpen` instead.', ); expect(document.querySelector('.ant-tooltip')).toBeTruthy(); // visible rerender( , ); expect(errSpy).toHaveBeenCalledWith( 'Warning: [antd: Tooltip] `visible` is deprecated, please use `open` instead.', ); rerender( , ); act(() => { jest.runAllTimers(); }); if (container.querySelector('.ant-zoom-big-fast-leave-active')) { fireEvent.animationEnd(container.querySelector('.ant-zoom-big-fast-leave-active')!); } expect(document.querySelector('.ant-tooltip-hidden')).toBeTruthy(); // onVisibleChange rerender( {}} title="bamboo"> , ); expect(errSpy).toHaveBeenCalledWith( 'Warning: [antd: Tooltip] `onVisibleChange` is deprecated, please use `onOpenChange` instead.', ); // afterVisibleChange rerender( {}} title="bamboo"> , ); expect(errSpy).toHaveBeenCalledWith( 'Warning: [antd: Tooltip] `afterVisibleChange` is deprecated, please use `afterOpenChange` instead.', ); // Event Trigger const onVisibleChange = jest.fn(); const afterVisibleChange = jest.fn(); rerender( , ); fireEvent.mouseLeave(container.querySelector('a')!); act(() => { jest.runAllTimers(); }); expect(onVisibleChange).toHaveBeenCalled(); expect(afterVisibleChange).toHaveBeenCalled(); jest.useRealTimers(); errSpy.mockRestore(); }); it('not inject className when children className is not string type', () => { const HOC = ({ className }: { className: Function }) => ; const { container } = render( 'bamboo'} /> , ); expect(container.querySelector('.bamboo')).toBeTruthy(); expect(container.querySelector('.ant-tooltip')).toBeTruthy(); }); });