Mock antd useBreakpoint hook

Viewed 219

I want to test the modal component, but there is an error with defining the cancel button, it renders only if it's not mobile.

isMobile is a variable that is a boolean value from hook - useBreakpoint (ant design library hook).

I don't know how to mock that value, or how to click that button.

Note: if I remove the isMobile check, the button clicks well:)

import React from 'react'
import {Grid, Typography} from 'antd'
import {Button} from '@/components/Button'
import {Modal} from '@/components/Modal'
import translations from './translations'
import {ConfirmationModalProps} from './props'

const {Text} = Typography
const {useBreakpoint} = Grid

export const ConfirmationModal = ({visible, onClose, children}: ConfirmationModalProps) => {
    const screens = useBreakpoint()
    const isMobile = screens.xs

    return (
        <Modal
            title={translations().chargeConfirmation}
            visible={visible}
            onOk={onClose}
            onCancel={onClose}
            footer={[
                !isMobile && (
                    <Button role={'cancel-button'} type={'ghost'} key={'cancel'} onClick={onClose}>
                        { translations().cancel }
                    </Button>
                ),
                <Button type={'primary'} key={'charge'} onClick={onClose}>
                    { translations().confirm }
                </Button>
            ]}
        >
            <Text>{translations().confirmationText(children)}</Text>
        </Modal>
    )
}


describe('ConfirmationModal', () => {
    it('should should the children and close button', async () => {
        const onClose = jest.fn()

        jest.mock('antd/es/grid/hooks/useBreakpoint', () => ({
            xs: false
        }))

        render(<ConfirmationModal onClose={onClose} visible={true}>100</ConfirmationModal>)

        const child = screen.getByText('Are you sure you want to charge 100')

        expect(child).toBeTruthy()

        expect(screen.queryByTestId('cancel')).toBeDefined()

        await waitFor(() => screen.queryByTestId('cancel'))

        fireEvent.click(screen.queryByRole('cancel-button'))

        expect(onClose).toHaveBeenCalledTimes(1)
    })
})

Errors are:

  1. Error: Unable to fire a "click" event - please provide a DOM element.
  2. Unable to find an accessible element with the role "cancel-button"

Depending on queryByRole or getByRole selector.

What is wrong?

1 Answers

Let's take a look at the source code of the useBreakpoint hook.

import { useEffect, useRef } from 'react';
import useForceUpdate from '../../_util/hooks/useForceUpdate';
import type { ScreenMap } from '../../_util/responsiveObserve';
import ResponsiveObserve from '../../_util/responsiveObserve';

function useBreakpoint(refreshOnChange: boolean = true): ScreenMap {
  const screensRef = useRef<ScreenMap>({});
  const forceUpdate = useForceUpdate();

  useEffect(() => {
    const token = ResponsiveObserve.subscribe(supportScreens => {
      screensRef.current = supportScreens;
      if (refreshOnChange) {
        forceUpdate();
      }
    });

    return () => ResponsiveObserve.unsubscribe(token);
  }, []);

  return screensRef.current;
}

export default useBreakpoint;

It uses ResponsiveObserve.subscribe() to get the supportScreens, it calls ResponsiveObserve.register(), the .register() method use window.matchMedia() underly. jestjs use JSDOM(a DOM implementation) as its test environment, but JSDOM does not implement window.matchMedia() yet. So we need to mock it, see Mocking methods which are not implemented in JSDOM

E.g.

import { render } from '@testing-library/react';
import React from 'react';
import { Grid } from 'antd';

const { useBreakpoint } = Grid;

describe('72021761', () => {
  test('should pass', () => {
    Object.defineProperty(window, 'matchMedia', {
      writable: true,
      value: jest.fn().mockImplementation(
        (query) =>
          ({
            addListener: (cb: (e: { matches: boolean }) => void) => {
              cb({ matches: query === '(max-width: 575px)' });
            },
            removeListener: jest.fn(),
            matches: query === '(max-width: 575px)',
          } as any)
      ),
    });

    let screensVar;
    function Demo() {
      const screens = useBreakpoint();
      screensVar = screens;
      return <div />;
    }
    render(<Demo />);

    expect(screensVar).toEqual({
      xs: true,
      sm: false,
      md: false,
      lg: false,
      xl: false,
      xxl: false,
    });
  });
});
Related