import { spyElementPrototype } from 'rc-util/lib/test/domHook'; import React from 'react'; import { act } from 'react-dom/test-utils'; import type { TooltipPlacement } from '..'; import Tooltip from '..'; import mountTest from '../../../tests/shared/mountTest'; import rtlTest from '../../../tests/shared/rtlTest'; import { fireEvent, render, waitFakeTimer } from '../../../tests/utils'; import getPlacements from '../../_util/placements'; import { resetWarned } from '../../_util/warning'; import Button from '../../button'; import DatePicker from '../../date-picker'; import Input from '../../input'; import Group from '../../input/Group'; import Radio from '../../radio'; import Switch from '../../switch'; import { isTooltipOpen } from './util'; describe('Tooltip', () => { mountTest(Tooltip); rtlTest(Tooltip); beforeEach(() => { jest.useFakeTimers(); }); afterEach(() => { jest.useRealTimers(); jest.clearAllTimers(); }); beforeAll(() => { spyElementPrototype(HTMLElement, 'offsetParent', { get: () => ({}), }); }); it('check `onOpenChange` arguments', async () => { 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!); await waitFakeTimer(); expect(onOpenChange).not.toHaveBeenCalled(); expect(isTooltipOpen()).toBeFalsy(); expect(container.querySelector('.ant-tooltip-open')).toBeNull(); fireEvent.mouseLeave(divElement!); await waitFakeTimer(); expect(onOpenChange).not.toHaveBeenCalled(); expect(isTooltipOpen()).toBeFalsy(); expect(container.querySelector('.ant-tooltip-open')).toBeNull(); // update `title` value. rerender(
Hello world!
, ); fireEvent.mouseEnter(divElement!); await waitFakeTimer(); expect(onOpenChange).toHaveBeenLastCalledWith(true); expect(isTooltipOpen()).toBeTruthy(); expect(container.querySelector('.ant-tooltip-open')).not.toBeNull(); fireEvent.mouseLeave(divElement!); await waitFakeTimer(); expect(onOpenChange).toHaveBeenLastCalledWith(false); expect(isTooltipOpen()).toBeFalsy(); expect(container.querySelector('.ant-tooltip-open')).toBeNull(); // add `open` props. rerender(
Hello world!
, ); fireEvent.mouseEnter(divElement!); await waitFakeTimer(); expect(onOpenChange).toHaveBeenLastCalledWith(true); const lastCount = onOpenChange.mock.calls.length; expect(isTooltipOpen()).toBeFalsy(); expect(container.querySelector('.ant-tooltip-open')).toBeNull(); // always trigger onOpenChange fireEvent.mouseLeave(divElement!); await waitFakeTimer(); expect(onOpenChange.mock.calls.length).toBe(lastCount); // no change with lastCount expect(isTooltipOpen()).toBeFalsy(); expect(container.querySelector('.ant-tooltip-open')).toBeNull(); }); it('should hide when mouse leave native disabled button', async () => { const onOpenChange = jest.fn(); const ref = React.createRef(); const { container } = render( , ); const button = container.getElementsByTagName('button')[0]; fireEvent.pointerEnter(button); await waitFakeTimer(); expect(onOpenChange).toHaveBeenCalledWith(true); expect(isTooltipOpen()).toBeTruthy(); expect(container.querySelector('.ant-tooltip-open')).not.toBeNull(); fireEvent.pointerLeave(button); await waitFakeTimer(); expect(onOpenChange).toHaveBeenCalledWith(false); expect(isTooltipOpen()).toBeFalsy(); 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, async () => { const onOpenChange = jest.fn(); const ref = React.createRef(); const { container } = render( , ); expect(container.children[0]).toMatchSnapshot(); const button = container.getElementsByTagName('button')[0]; fireEvent.pointerEnter(button); await waitFakeTimer(); expect(onOpenChange).toHaveBeenCalledWith(true); expect(isTooltipOpen()).toBeTruthy(); expect(container.querySelector('.ant-tooltip-open')).not.toBeNull(); fireEvent.pointerLeave(button); await waitFakeTimer(); expect(onOpenChange).toHaveBeenCalledWith(false); expect(isTooltipOpen()).toBeFalsy(); 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(getComputedStyle(containerInline.querySelector('button')!)?.display).toBe( 'inline-block', ); expect(getComputedStyle(containerBlock.querySelector('button')!)?.display).toBe('block'); }); it('should warn for arrowPointAtCenter', async () => { const warnSpy = jest.spyOn(console, 'error').mockImplementation(() => {}); render( , ); expect(warnSpy).toHaveBeenLastCalledWith( expect.stringContaining('`arrowPointAtCenter` is deprecated'), ); render( , ); expect(warnSpy).toHaveBeenCalledWith( expect.stringContaining('`arrowPointAtCenter` in `arrow` is deprecated'), ); }); it('should works for date picker', async () => { 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(isTooltipOpen()).toBeTruthy(); expect(container.querySelector('.ant-tooltip-open')).not.toBeNull(); fireEvent.mouseLeave(picker); await waitFakeTimer(); expect(onOpenChange).toHaveBeenCalledWith(false); expect(isTooltipOpen()).toBeFalsy(); expect(container.querySelector('.ant-tooltip-open')).toBeNull(); }); it('should works for input group', async () => { 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(isTooltipOpen()).toBeTruthy(); expect(container.querySelector('.ant-tooltip-open')).not.toBeNull(); fireEvent.mouseLeave(inputGroup); await waitFakeTimer(); expect(onOpenChange).toHaveBeenCalledWith(false); expect(isTooltipOpen()).toBeFalsy(); expect(container.querySelector('.ant-tooltip-open')).toBeNull(); }); // 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', () => { 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(); expect(document.querySelector(`.ant-tooltip-placement-${placement}`)).toBeTruthy(); }); it(`${name} with arrowPointAtCenter`, async () => { const placementInfo: Record = getPlacements({ arrowPointAtCenter: true, autoAdjustOverflow: false, arrowWidth: 0, borderRadius: 10, offset: 0, }); // Safe to rewrite follow all check const { offset } = placementInfo[placement]; const existO = offset[0] !== 0 || offset[1] !== 0; if (['left', 'right', 'top', 'bottom'].includes(placement)) { expect(existO).toBeFalsy(); } else { expect(existO).toBeTruthy(); } }); }; 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( , ); fireEvent.pointerEnter(container.getElementsByTagName('button')[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( , ); fireEvent.pointerEnter(container.getElementsByTagName('input')[0]); expect(onOpenChange).toHaveBeenLastCalledWith(true); expect(container.querySelector('.ant-tooltip-open')).not.toBeNull(); }); it('should work with Fragment children', async () => { 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); await waitFakeTimer(); expect(isTooltipOpen()).toBeTruthy(); expect(container.querySelector('.ant-tooltip-open')).not.toBeNull(); fireEvent.mouseLeave(divElement!); expect(onOpenChange).toHaveBeenLastCalledWith(false); await waitFakeTimer(); expect(isTooltipOpen()).toBeFalsy(); expect(container.querySelector('.ant-tooltip-open')).toBeNull(); }); it('deprecated warning', async () => { resetWarned(); const errSpy = jest.spyOn(console, 'error').mockImplementation(() => {}); // defaultVisible const { container, rerender } = render( , ); await waitFakeTimer(); expect(errSpy).toHaveBeenCalledWith( 'Warning: [antd: Tooltip] `defaultVisible` is deprecated. Please use `defaultOpen` instead.', ); expect(isTooltipOpen()).toBeTruthy(); // visible rerender( , ); expect(errSpy).toHaveBeenCalledWith( 'Warning: [antd: Tooltip] `visible` is deprecated. Please use `open` instead.', ); rerender( , ); await waitFakeTimer(); if (container.querySelector('.ant-zoom-big-fast-leave-active')) { fireEvent.animationEnd(container.querySelector('.ant-zoom-big-fast-leave-active')!); } expect(isTooltipOpen()).toBeFalsy(); // 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')!); await waitFakeTimer(); expect(onVisibleChange).toHaveBeenCalled(); expect(afterVisibleChange).toHaveBeenCalled(); 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(); }); it('support arrow props pass false to hide arrow', () => { const { container } = render(
target
, ); expect(container).toMatchSnapshot(); }); it('support arrow props by default', () => { const { container } = render(
target
, ); expect(container).toMatchSnapshot(); }); it('use ref.current.forcePopupAlign', async () => { const ref = React.createRef(); const error = jest.spyOn(console, 'error').mockImplementation(() => {}); render(); act(() => { ref.current.forcePopupAlign(); jest.runAllTimers(); }); expect(error).toHaveBeenCalled(); error.mockRestore(); }); });