Context/Setup:
I'm trying to use Jest and React-Testing-Library to test the render of a React component <Main/>, but when I run the test, the client that handles fetch throws an error because it's using document.querySelector() - but when Jest runs there's no document because there's no browser rendered.
My Goal Here: Get Jest and RTL set up so we can start writing tests for all the components. I would like to start with verifying that I can render <Main/> without errors.
Here's Client.js
class Client {
  constructor() {
    console.log("Client initializing")
    console.log(document.querySelector('meta[name="csrf-token"]'))
    console.log(document)
    this.token = document.querySelector('meta[name="csrf-token"]').content;
    
  }
  getData(path) {
    return (
      fetch(`${window.location.origin}${path}`, {
        headers: { "X-CSRF-Token": this.token }
      })
    )
  }
  submitData(path, method, body) {
    return (
      fetch(`${window.location.origin}${path}`, {
        method: method,
        headers: { "X-CSRF-Token": this.token },
        body: body
      })
    )
  }
  deleteData(path) {
    return (
      fetch(`${window.location.origin}${path}`, {
        method: "DELETE",
        headers: {
          "X-CSRF-Token": this.token,
          "Content-Type": "application/json"
        }
      })
    )
  }
}
export default Client;
Here's main.test.js:
/**
 * @jest-environment jsdom
 */
import React from 'react';
import { render, screen } from '@testing-library/react';
// import userEvent from '@testing-library/user-event';
import Main from '../../app/javascript/components/Main';
test("renders without errors", ()=> {
    render(<Main/>);
});
I've also set up a setupTests.js file:
require("jest-fetch-mock").enableMocks();
import '@testing-library/jest-dom';
And called it here in package.json:
"jest": {
        "roots": [
            "test/javascript"
        ],
        "moduleNameMapper": {
            "\\.(svg|png)": "<rootDir>/__mocks__/svgrMock.js"
        },
        "automock": false,
        "setupFilesAfterEnv": [
            "./test/javascript/setupTests.js"
        ]
    },
I have also set testEnvironment: 'jsdom' in the jest.config.js file.
Current Problem:
When I run yarn jest I get the following error:
TypeError: Cannot read properties of null (reading 'content') which points to this.token = document.querySelector('meta[name="csrf-token"]').content; in Client.js
This makes sense to me because it's looking for a DOM element, but Jest runs in Node (no browser render) so there's no DOM to be found.
I think I need to:
- Mock the 
documentso the app can run without being rendered in the browser. Not sure how to do this. - Then mock the fetch calls (maybe?) not sure how to do this either tbh.
 
What I've Tried So Far:
1. I've tried various ways to mock the DOM elements globally (from setupTests.js) including many permutations of something like this:
import { TextDecoder, TextEncoder } from 'util'
global.TextEncoder = TextEncoder
global.TextDecoder = TextDecoder
//variables to mock a csrf token
const csrfToken = 'abcd1234';
const virtualDom = `
<!doctype html>
    <head>
        <meta name="csrf-token" content="${csrfToken}" />
    </head>
  <body>
    <form>
        <input type="hidden" name="csrf-token" value=${csrfToken}>
      </form>
  </body>
</html>
`;
const { JSDOM } = require("jsdom");
//mock a page passing virtualDom to JSDOM
const page = new JSDOM(virtualDom);
const { window } = page;
function copyProps(src, target) {
    const props = Object.getOwnPropertyNames(src)
      .filter(prop => typeof target[prop] === 'undefined')
      .map(prop => Object.getOwnPropertyDescriptor(src, prop));
    Object.defineProperties(target, props);
  }
global.window = window;
global.document = window.document;
global.navigator = {
  userAgent: 'node.js',
};
copyProps(window, global);
But global.window = window never seems to work because if I declare that and then immediately console.log(global.window, window) i get null and then a window.
2. I've tried temporarily downgrading React 18 to React 17 (based on some StackOverflow exchanges) - I know this isn't optimal, but it got me to the point where I would have to mock the fetch() calls at least.
I don't know how to do that properly either tbh, but I also know downgrading React is probably the wrong path here.
Other potentially-important context:
- This React front-end is part of a Rails app (webpack).
 - Using 
yarn - I have a lot of control over how we achieve this, so I'm looking for the simplest/cleanest way to do this.
 
                        
Turns out I had to mock all the data coming in to the components that render from Main. (In addition to the CSRF token mocking)
Once I did that, everything else fell into place.