reactjs – @testing-library/react render Error: `target` and `targetRef` props are `undefined`, it’ required to use one of them

I am trying to write a unit test for a custom react component that use the Dialog from @fluentui/react-northstar when I try to render the component from the test I get an error:

Error: `target` and `targetRef` props are `undefined`, it' required to use one of them.

    at ...node_modules@fluentuireact-component-event-listenerdistcommonjsuseEventListener.ts:30:15
    at invokePassiveEffectCreate (...node_modulesreact-domcjsreact-dom.development.js:23487:20)
    at HTMLUnknownElement.callCallback (...node_modulesreact-domcjsreact-dom.development.js:3945:14)
    at HTMLUnknownElement.callTheUserObjectsOperation (...node_modulesjsdomlibjsdomlivinggeneratedEventListener.js:26:30)
    at innerInvokeEventListeners (...node_modulesjsdomlibjsdomlivingeventsEventTarget-impl.js:338:25)
    at invokeEventListeners (...node_modulesjsdomlibjsdomlivingeventsEventTarget-impl.js:274:3)
    at HTMLUnknownElementImpl._dispatch (...node_modulesjsdomlibjsdomlivingeventsEventTarget-impl.js:221:9)
    at HTMLUnknownElementImpl.dispatchEvent (...node_modulesjsdomlibjsdomlivingeventsEventTarget-impl.js:94:17)
    at HTMLUnknownElement.dispatchEvent (...node_modulesjsdomlibjsdomlivinggeneratedEventTarget.js:231:34)
    at Object.invokeGuardedCallbackDev (...node_modulesreact-domcjsreact-dom.development.js:3994:16)
    at invokeGuardedCallback (...node_modulesreact-domcjsreact-dom.development.js:4056:31)
    at flushPassiveEffectsImpl (...node_modulesreact-domcjsreact-dom.development.js:23574:9)
    at unstable_runWithPriority (...node_modulesschedulercjsscheduler.development.js:468:12)
    at runWithPriority$1 (...node_modulesreact-domcjsreact-dom.development.js:11276:10)
    at flushPassiveEffects (...node_modulesreact-domcjsreact-dom.development.js:23447:14)
    at Object.<anonymous>.flushWork (...node_modulesreact-domcjsreact-dom-test-utils.development.js:992:10)
    at act (...node_modulesreact-domcjsreact-dom-test-utils.development.js:1107:9)
    at render (...node_modules@testing-libraryreactdistpure.js:97:26)
    at Object.<anonymous> (...srcfeaturesManageUsers__tests__SyncModal.test.tsx:35:24)
    at Promise.then.completed (...node_modulesjest-circusbuildutils.js:276:28)
    at new Promise (<anonymous>)
    at callAsyncCircusFn (...node_modulesjest-circusbuildutils.js:216:10)
    at _callCircusTest (...node_modulesjest-circusbuildrun.js:212:40)
    at processTicksAndRejections (node:internal/process/task_queues:96:5)
    at _runTest (...node_modulesjest-circusbuildrun.js:149:3)
    at _runTestsForDescribeBlock (...node_modulesjest-circusbuildrun.js:63:9)
    at _runTestsForDescribeBlock (...node_modulesjest-circusbuildrun.js:57:9)
    at run (...node_modulesjest-circusbuildrun.js:25:3)
    at runAndTransformResultsToJestFormat (...node_modulesjest-circusbuildlegacy-code-todo-rewritejestAdapterInit.js:176:21)
    at jestAdapter (...node_modulesjest-circusbuildlegacy-code-todo-rewritejestAdapter.js:109:19)
    at runTestInternal (...node_modulesjest-runnerbuildrunTest.js:380:16)
    at runTest (...node_modulesjest-runnerbuildrunTest.js:472:34)

The code:

//testutils.tsx
import { render } from '@testing-library/react';
import { IntlProvider } from 'react-intl';
import { QueryClient, QueryClientProvider } from 'react-query';
import { FC, ReactElement } from 'react';
import renderer from 'react-test-renderer';

const createTestQueryClient = () =>
    new QueryClient({
        defaultOptions: {
            queries: {
                retry: false,
            },
        },
    });

export function renderWithClient(ui: React.ReactElement) {
    const testQueryClient = createTestQueryClient();
    const WrapperIntlProvider: FC = ({children}) => {
        return (
            <QueryClientProvider client={testQueryClient}>
                <IntlProvider locale={'en'}>{children}</IntlProvider>
            </QueryClientProvider>
        );

    };
    const { rerender, ...result } = render(ui, { wrapper: WrapperIntlProvider });
    return {
        ...result,
        rerender: (rerenderUi: React.ReactElement) =>
            rerender(
                <WrapperIntlProvider>{rerenderUi}</WrapperIntlProvider>
            ),
    };
}

export function rendererWithClient(ui: ReactElement) {
    return renderer.create(<IntlProvider locale={'en'}>{ui}</IntlProvider>);
}

//SyncModal.test.tsx
import React, { ReactPortal } from 'react';
import { cleanup } from '@testing-library/react';
import SyncModal from '../SyncModal';
import { rendererWithClient, renderWithClient } from '../../../utils/__test__/testutils';
import ReactDOM from 'react-dom';

jest.mock('react-dom', () => ({
    // eslint-disable-next-line
    // @ts-ignore
    ...jest.requireActual('react-dom'),
    createPortal: (node) => node as ReactPortal,
}));

afterEach(() => {
    cleanup();
});

describe('SyncModal', () => {
    test('match snapshot', () => {
        const oldPortal = ReactDOM.createPortal;
        const ref = React.createRef();
        const props = {
            register: jest.fn(() => ref),
            onClose: () => console.log('closed'),
            onSave: () => console.log('saved'),
            isOpen: true,
        };
        ReactDOM.createPortal = (node) => node as ReactPortal; // for components that use Portal
        renderWithClient(<SyncModal {...props} />);
        const tree = rendererWithClient(<SyncModal {...props} />).toJSON();
        console.debug(tree);
        expect(tree).toMatchSnapshot();

        ReactDOM.createPortal = oldPortal;
    });
});


//SyncModal.tsx
import React from 'react';
import { FormattedMessage } from 'react-intl';
import { CloseIcon, Dialog, Flex, Text } from '@fluentui/react-northstar';

import messages from './messages';

const SyncModal = (props: any) => {
    const { isOpen, onClose, onSave, hasFails, failsReason } = props;
    return (
        <Dialog
            id={'sync-modal'}
            open={isOpen}
            headerAction={{
                icon: <CloseIcon />,
                title: 'Close',
                fluid: true,
                onClick: () => {
                    onClose();
                },
            }}
            header={{
                align: 'start',
                content: (
                    <Text align="start">
                        {hasFails ? (
                            <FormattedMessage {...messages.syncHeaderTimeout} />
                        ) : (
                            <FormattedMessage {...messages.syncHeader} />
                        )}
                    </Text>
                ),
            }}
            styles={{ width: '434px', height: 188 }}
            content={
                <Flex column={true} styles={{ width: '100%' }}>
                    <Flex
                        gap="gap.medium"
                        column={true}
                        style={{ marginTop: '21px', marginBottom: '16px' }}
                        hAlign={'center'}
                    >
                        {hasFails ? (
                            <FormattedMessage
                                {...messages.syncContentTimeout}
                                values={{ reason: failsReason ?? 'unknown reason' }}
                            />
                        ) : (
                            <FormattedMessage {...messages.syncContentPart1} />
                        )}
                    </Flex>
                    <Text weight={'semibold'} styles={{ marginBottom: '8px' }}>
                        <FormattedMessage {...messages.syncContentPart2} />
                    </Text>
                    <ul style={{ paddingLeft: 20, margin: 0 }}>
                        <li style={{ marginBottom: '8px' }}>
                            <Text>
                                <FormattedMessage {...messages.syncContentPart3} />
                            </Text>
                        </li>
                        <li style={{ marginBottom: '8px' }}>
                            <Text>
                                <FormattedMessage {...messages.syncContentPart4} />
                            </Text>
                        </li>
                    </ul>
                </Flex>
            }
            onCancel={() => onClose()}
            cancelButton={
                <Flex>
                    <FormattedMessage {...messages.syncCancel} />
                </Flex>
            }
            onConfirm={() => onSave()}
            confirmButton={
                <Flex>
                    <FormattedMessage {...messages.syncConfirm} />
                </Flex>
            }
            style={{ width: '600px' }}
            {...props}
        />
    );
};

export default SyncModal;

I use react-intl for multi-language support and have to wrap my component with IntlProviderI cannot understand why node_modules@fluentuireact-component-event-listenerdistcommonjsuseEventListener.ts throws Error: 'target' and 'targetRef' props are 'undefined', it' required to use one of them.

Library versions:

  • “@fluentui/react-northstar”: “^0.51.4”
  • “react”: “^17.0.1”
  • “react-intl”: “^5.10.18”
  • “@testing-library/react”: “^11.2.7”
  • “@types/react-test-renderer”: “^17.0.2”

Leave a Comment