6

I have an error in my tests that flag up a window env variable as undefined. I understand the error as it is based on the runtime of the app using the variables and maybe they are undefined on running the app. But I don't know where in the setupTests.tsx, I would need to define it. So far the variable is used as so:

index.html

<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="utf-8" />
    <link rel="icon" href="%PUBLIC_URL%/favicon.ico" />
    <meta name="viewport" content="width=device-width, initial-scale=1" />
    <script src="%PUBLIC_URL%/config.js"></script>
  </head>
  <body>
    <noscript>You need to enable JavaScript to run this app.</noscript>
    <div id="root"></div>
  </body>
</html>

config.js

window._env_ = {
  REACT_APP_URL: "https://apiurl.com"
}

how it is used in the app:

declare const window: Window &
    typeof globalThis & {
        _env_: any
    }

const url = window._env_.REACT_APP_URL;
export const apiUrl = url;

setupTests.tsx I did try adding it here but it's still not working

import '@testing-library/jest-dom';
import { setLogger } from 'react-query'
import { server } from './mocks/server'
declare const window: Window &

typeof globalThis & {
    _env_: any
}

window._env_.REACT_APP_URL = "https://wwww.xxxxx.com"

beforeAll(() => server.listen())
// Reset any request handlers that we may add during the tests,
// so they don't affect other tests.


afterEach(() => server.resetHandlers())

// Clean up after the tests are finished.
afterAll(() => server.close())

The error that stops the tests:

  ● Test suite failed to run

    TypeError: Cannot read properties of undefined (reading 'REACT_APP_URL')

      4 |     }
      5 |
    > 6 | const url = window._env_.REACT_APP_URL;
        |                          ^
      7 | export const apiUrl = url;
      8 |
      9 |
  at Object.<anonymous> (src/utils/Url.tsx:6:26)
  at Object.<anonymous> (src/mocks/handlers.tsx:3:1)
  at Object.<anonymous> (src/mocks/server.tsx:2:1)
  at Object.<anonymous> (src/setupTests.tsx:7:1)
Sole
  • 3,100
  • 14
  • 58
  • 112

1 Answers1

3

The reason why you are seeing an error: The jest library that automatically comes with a new create-react-app project has already been pre-configured to use jsdom as it's test environment (ref), meaning that the window property will be defined during the runtime of your tests. However, _env_ is not a default property of the window variable, meaning that it is undefined and an attempt to access its properties will give you an error. This issue can be fixed by assigning a new object to the _env_ property, however there is another problem which stands in our way - the following assignment const url = window._env_.REACT_APP_URL; will always be evaluated before your tests get executed. The reason for that is because when a file (in this case setupTests.tsx) imports another file (e.g. Url.tsx), the imported file's logic gets immediately evaluated (almost like triggering an implicitly invoked function) and any variable assignments within it get immediately executed, hence overwriting the _env_ property in your tests will not work as it will be too late.

To fix this problem: you can mock out the entire file that contains the variables and have it return what you need using the following code which should be placed at the top of the test file:

jest.mock('./src/utils/Url', () => ({
  get apiUrl () {
    return "https://wwww.xxxxx.com";
  }
}));
Ovidijus Parsiunas
  • 2,512
  • 2
  • 8
  • 18
  • Is that in the test files? or in the setupTests file? – Sole May 05 '22 at 11:45
  • Could you elaborate on your question – Ovidijus Parsiunas May 05 '22 at 11:47
  • So you know where you have specified the beforeEach function, would that go in the test suite? – Sole May 05 '22 at 12:09
  • This is the create-react-app setup, the create-react-app does not have the `jest.config.js` it has setupTests as a file which I have outlined above – Sole May 05 '22 at 12:09
  • I am not 100% aware of your full application setup, but the most direct way is to set the ```testEnvironment``` property to ```'jsdom'``` in the configuration. Have a look at the following answer to see if you can get access to the configuration: https://stackoverflow.com/questions/59878153/how-to-use-jest-config-js-with-create-react-app – Ovidijus Parsiunas May 05 '22 at 12:26
  • So the app is setup using the normal create-react-app https://create-react-app.dev/docs/running-tests/#configuration. I am just wondering how your answer first into this configuration.? – Sole May 05 '22 at 16:09
  • My answer is a standard pattern for testing browser based js code via jest. And many React projects are setup without the create-react-app template, hence they tend to have the ```jest.config.js``` file. However, I can see your case appears to be quite specific which is understandable and if it doesn't work please let me know and I will change my answer. – Ovidijus Parsiunas May 05 '22 at 17:09
  • Thanks but this did not work for me. – Sole May 05 '22 at 22:34
  • - did you have an alternative approach for Create-react-app setup? – Sole May 14 '22 at 13:46
  • I'm going to give it another go, I'll let you know what my findings are. – Ovidijus Parsiunas May 14 '22 at 14:16
  • I got it to work and have updated my answer :) – Ovidijus Parsiunas May 14 '22 at 14:46
  • So, did you update this in the test or the actual component? – Sole May 14 '22 at 23:48
  • hmmmm, just tried that and it did not work for some reason, how have you configured the setUp and the test? is it using create-react-app? is it possible to recreate in a codesanbox? can you share the code? – Sole May 14 '22 at 23:54
  • Ok, I think I pinpointed the problem. The ```const url = window._env_.REACT_APP_URL;``` assignment will always be run before your tests are executed. The reason for that is because when a test file imports another file (e.g. a component file), that file's logic gets pre-computed, and because the url has a simple variable assignment, that gets run and obviously fails. To prevent this you have 2 options: 1. Instead of using the exported ```url``` variable, your files could be using the ```window._env_.REACT_APP_URL``` instead. Which is perfectly normal. I'll describe the second approach below. – Ovidijus Parsiunas May 15 '22 at 01:22
  • 1
    2. To avoid your consts getting pre-computed and also running into this problem with other variables, you can mock out the file that contains the variables and have them return what you need using the following code: ```jest.mock('./src/utils/Url', () => ({ get apiUrl () { return true; } }));``` I am not quite sure what the Url directory is relative to the test, but you will probably need to augment it as appropriate and that should get your tests to pass how you want it. If this works I will update my answer appropriately. – Ovidijus Parsiunas May 15 '22 at 01:27
  • So I did the second approach but put that jest.mock function in the setup tests file and they work, thanks very much for all your help, please update your answer and I can accept. – Sole May 15 '22 at 22:34
  • Glad to hear this fixed your problem! I have updated my answer accordingly. – Ovidijus Parsiunas May 16 '22 at 01:15
  • forgot to award the bounty, but just done it. Thanks again! – Sole May 21 '22 at 15:10
  • oh wow, was not expecting that! Thankyou so much! – Ovidijus Parsiunas May 21 '22 at 15:12