jest: Invalid variable access _jsxFileName

🐛 Bug Report

After upgrading to new JSX transform, started to receive an error in my mocks that’s returning a React Component/Element, saying about the _jsxFileName variable being outside of scope and could not be accessed.

To Reproduce

Just mock a React Component/Element and start using the new JSX Transform

jest.mock('./MyComponent', () => () => <div id="my-component" />)

About this issue

  • Original URL
  • State: closed
  • Created 4 years ago
  • Reactions: 3
  • Comments: 22 (15 by maintainers)

Most upvoted comments

Fix released in https://github.com/facebook/jest/releases/tag/v26.6.2. Many thanks to @nicolo-ribaudo for helping out with this 🙏

@rickhanlonii you can reproduce using CRA

$ npx create-react-app my-app
$ cd my-app
$ git apply mock.patch
$ npm test
ReferenceError: /Users/simen/repos/my-app/src/App.test.js: The module factory of `jest.mock()` is not allowed to reference any out-of-scope variables.
    Invalid variable access: _jsxFileName

mock.patch:

diff --git i/src/App.test.js w/src/App.test.js
index 1f03afe..ba15b2b 100644
--- i/src/App.test.js
+++ w/src/App.test.js
@@ -1,6 +1,8 @@
 import { render, screen } from '@testing-library/react';
 import App from './App';
 
+jest.mock('./MyComponent', () => () => <div id="my-component" />, { virtual: true })
+
 test('renders learn react link', () => {
   render(<App />);
   const linkElement = screen.getByText(/learn react/i);

Maybe if a mock relies on a constant pure variable in the outer scope, that variable can be hoisted to the top of the file?

Currently the whitelist is globals and known symbols that jest controls, just adding stuff that “random” babel plugins inject seems like a slippery slope. I’m hoping we can solve it more generically via some babel magic. Might not be possible though! @rickhanlonii on the react team is gonna take a look, so let’s wait and see what he says 🙂

If it proves difficult we should just add it to the whitelist to unblock, tho 👍

PR welcome 👍 Error is in babel-plugin-jest-hoist somewhere.

That sounds reasonable @nicolo-ribaudo. We’d do that in our hoist plugin, right? Seems reasonable. Any idea on how to achieve that?

So a workaround is to somehow disable “dev” mode for the transform when running Jest.

https://github.com/facebook/create-react-app/blob/027b03ba8d689e619a912ed0d72c3a11ef22ac2f/packages/babel-preset-react-app/create.js#L95

However, I’d say it’s still a bug even if CRA were to work around it.

If this plugin ran before the React plugin then I would expect this to work because the injected React functions would be in scope. Is there a repro repo I can test on?

Alternatively the plugin could also allow variables prefixed by an underscore (_*), so that it’s generic enough for other libraries to follow the same convention if they need to. I know right now it supports mock prefixed variables, so it wouldn’t be much different.

Meanwhile, anybody with the same problem can use this patch with patch-package (save it in patches/babel-plugin-jest-hoist+26.5.0.patch):

diff --git a/node_modules/babel-plugin-jest-hoist/build/index.js b/node_modules/babel-plugin-jest-hoist/build/index.js
index 6128021..46f2824 100644
--- a/node_modules/babel-plugin-jest-hoist/build/index.js
+++ b/node_modules/babel-plugin-jest-hoist/build/index.js
@@ -141,7 +141,8 @@ FUNCTIONS.mock = args => {
       if (!found) {
         const isAllowedIdentifier =
           (scope.hasGlobal(name) && ALLOWED_IDENTIFIERS.has(name)) ||
-          /^mock/i.test(name) || // Allow istanbul's coverage variable to pass.
+          /^mock/i.test(name) ||
+          /^_/i.test(name) || // Allow istanbul's coverage variable to pass.
           /^(?:__)?cov/.test(name);
 
         if (!isAllowedIdentifier) {

Right, which is why I wanna solve it more generically than adding to the whitelist, and hopefully without the user having to specify any config