简体   繁体   中英

How to use an async function as the second parameter to jest.mock?

I need to use jest.mock together with async , await and import() so that I can use a function from another file inside the mocked module. Otherwise I must copy and paste a few hundreds of slocs or over 1000 slocs, or probably it is not even possible.

An example

This does work well:

jest.mock('./myLin.jsx', () => {
  return {
    abc: 967,
  }
});

Everywhere I use abc later it has 967 as its value, which is different than the original one.

This does not work:

jest.mock('./myLin.jsx', async () => {
  return {
    abc: 967,
  }
});

abc seems to not be available.

Actual issue

I need async to be able to do this:

jest.mock('~/config', async () => {
  const { blockTagDeserializer } = await import(
    '../editor/deserialize' // or 'volto-slate/editor/deserialize'
  );

  // … here return an object which contains a call to
  // blockTagDeserializer declared above; if I can't do this
  // I cannot use blockTagDeserializer since it is outside of
  // the scope of this function
}

Actual results

I get errors like:

TypeError: Cannot destructure property 'slate' of '((cov_1viq84mfum.s[13]++), _config.settings)' as it is undefined.

where _config , I think, is the ~/config module object and slate is a property that should be available on _config.settings .

Expected results

No error, blockTagDeserializer works in the mocked module and the unit test is passed.

The unit test code

The code below is a newer not-working code based on this file on GitHub .

import React from 'react';
import renderer from 'react-test-renderer';
import WysiwygWidget from './WysiwygWidget';
import configureStore from 'redux-mock-store';
import { Provider } from 'react-intl-redux';

const mockStore = configureStore();

global.__SERVER__ = true; // eslint-disable-line no-underscore-dangle
global.__CLIENT__ = false; // eslint-disable-line no-underscore-dangle

jest.mock('~/config', async () => {
  const { blockTagDeserializer } = await import(
    '../editor/deserialize' // or 'volto-slate/editor/deserialize'
  );

  const createEmptyParagraph = () => {
    return {
      type: 'p',
      children: [{ text: '' }],
    };
  };
  return {
    settings: {
      supportedLanguages: [],
      slate: {
        elements: {
          default: ({ attributes, children }) => (
            <p {...attributes}>{children}</p>
          ),
          strong: ({ children }) => {
            return <strong>{children}</strong>;
          },
        },
        leafs: {},
        defaultBlockType: 'p',
        textblockExtensions: [],
        extensions: [
          (editor) => {
            editor.htmlTagsToSlate = {
              STRONG: blockTagDeserializer('strong'),
            };
            return editor;
          },
        ],
        defaultValue: () => {
          return [createEmptyParagraph()];
        },
      },
    },
  };
});

window.getSelection = () => ({});

test('renders a WysiwygWidget component', () => {
  const store = mockStore({
    intl: {
      locale: 'en',
      messages: {},
    },
  });
  const component = renderer.create(
    <Provider store={store}>
      <WysiwygWidget
        id="qwertyu"
        title="My Widget"
        description="My little description."
        required={true}
        value={{ data: 'abc <strong>def</strong>' }}
        onChange={(id, data) => {
          // console.log('changed', data.data);
          // setHtml(data.data);
        }}
      />
    </Provider>,
  );
  const json = component.toJSON();
  expect(json).toMatchSnapshot();
});

What I've tried

  1. The code snippets above show partially what I have tried.
  2. I searched the web for 'jest mock async await import' and did not found something relevant.

The question

If jest.mock is not made to work with async , what else can I do to make my unit test work?

Update 1

In the last snippet of code above, the line

              STRONG: blockTagDeserializer('strong'),

uses blockTagDeserializer defined here which uses deserializeChildren , createEmptyParagraph (which is imported from another module), normalizeBlockNodes (which is imported from another module) and jsx (which is imported from another module) functions, which use deserialize which uses isWhitespace which is imported from another module and typeDeserialize which uses jsx and deserializeChildren .

Without using await import(...) syntax how can I fully mock the module so that my unit test works?

If you want to dig into our code, please note that the volto-slate/ prefix in the import statements is for the src/ folder in the repo.

Thank you.

I'd advise not doing any "heavy" stuff (whatever that means) in a callback of jest.mock , – it is designed only for mocking values.

Given your specific example, I'd just put whatever the output of blockTagDeserializer('strong') right inside the config:

jest.mock('~/config', () => {
  // ...

        extensions: [
          (editor) => {
            editor.htmlTagsToSlate = {
              STRONG: ({ children }) => <strong>{children}</strong>, // or whatever this function actually returns for 'strong'
            };
            return editor;
          },
        ],

  // ...
});

This doesn't require anything asynchronous to be done.

If you need this setup to be present in a lot of files, extracting it in a setup file seems to be the next best thing.

I found a solution. I have a ref callback that sets the htmlTagsToSlate property of the editor in the actual code of the module, conditioned by global.__JEST__ which is defined as true in Jest command line usage:

import { htmlTagsToSlate } from 'volto-slate/editor/config';

[...]

testingEditorRef={(val) => {
  ref.current = val;
  if (val && global.__JEST__) {
    val.htmlTagsToSlate = { ...htmlTagsToSlate };
  }
}}

Now the jest.mock call for ~/config is simple, there is no need to do an import in it.

I also use this function:

const handleEditorRef = (editor, ref) => {
  if (typeof ref === 'function') {
    ref(editor);
  } else if (typeof ref === 'object') {
    ref.current = editor;
  }
  return editor;
};

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM