createRoot(...): Target container is not a DOM element in React Test file - javascript

I'm using React-18.0.0 in my project and in the test file I'm getting an error something below
createRoot(...): Target container is not a DOM element.
My test file is :
import ReactDOM from 'react-dom/client';
import { render, screen } from "#testing-library/react";
import App from "./App";
const root = ReactDOM.createRoot(document.getElementById("root"));
test("renders learn react link", () => {
root.render(<App />);
const linkElement = screen.getByText(/Hello React/i);
expect(linkElement).toBeInTheDocument();
});

Your test uses root.render(<App />) while React's testing-library provides there own render function to use inside a test
Retrieving the root isn't needed, and is causing the error you're showing.
So, apply the following change:
// Remove
root.render(<App />);
// Replace with
render(<App />); // Imported from #testing-library/react
Example of working App.test.js:
import { render, screen } from '#testing-library/react';
import App from './App';
test('renders learn react link', () => {
render(<App />);
const linkElement = screen.getByText(/this should exist/i);
expect(linkElement).toBeInTheDocument();
});

Related

determine how to render component based on react-dom version

According to the react documentation, the way to render react components, is different based on the version of react-dom:
// Before (react-dom#^17)
import { render } from 'react-dom';
const container = document.getElementById('app');
render(<App tab="home" />, container);
// After (react-dom#^18)
import { createRoot } from 'react-dom/client';
const container = document.getElementById('app');
const root = createRoot(container); // createRoot(container!) if you use TypeScript
root.render(<App tab="home" />);
Since I'm writing a library that's supposed to support both react 18 and lower versions, I need a way to render it differently based on the version to maintain backward compatibility.
In order to do that, I need to import a module from a different path for each version, and call a different function as well. The problem is that I'm unsure how to import from react-dom/client when it doesn't exist in older versions. The only way I could think of is to import it async:
async function renderBasedOnVersion() {
const { render } = await import('react-dom');
const container = document.getElementById('app');
return render(<App tab="home" />, container);
if (isReact18) {
const { createRoot } = await import('react-dom/client');
const container = document.getElementById('app');
const root = createRoot(container); // createRoot(container!) if you use TypeScript
return root.render(<App tab="home" />);
}
}

Why do ReactDOMServer and ReactDOM give the "root.hydrate is not a function" error

When creating an app using create-react-app I get the following error when trying to use hydrate instead of the default .render. Here is the src/index.js file's contents.
import React from 'react';
import ReactDOMServer from 'react-dom/client';
import './index.css';
import App from './App';
const root = ReactDOMServer.createRoot(document.getElementById('root'));
root.hydrate(
<React.StrictMode>
<App />
</React.StrictMode>
);
Running the above code npm start produces the following error
Uncaught TypeError: root.hydrate is not a function
The error is not present if I change the .hydrate back to .render and I am not sure why this is happening.
hydrate has been replaced with hydrateRoot in React 18.
hydrateRoot(container, element[, options])
You can check for more info about hydrateRoot here : Documentation
// Before React 18
import { hydrate } from 'react-dom';
const container = document.getElementById('app');
hydrate(<App tab="home" />, container);
// After React 18+
import { hydrateRoot } from 'react-dom/client';
const container = document.getElementById('app');
const root = hydrateRoot(container, <App tab="home" />);
// Unlike with createRoot, you don't need a separate root.render() call here.
Thank you #PR7 that worked perfectly.
Please note, as my original file syntax was slightly different (from your example), I have written my "before" and "after" below; in case it can help anyone else.
// Before
import ReactDOM from 'react-dom/client';
const root = ReactDOM.createRoot(document.getElementById('root'));
root.hydrate(
  <React.StrictMode>
   <App />
 </React.StrictMode>
);
// After
import { hydrateRoot } from 'react-dom/client';
const container = document.getElementById('root');
const root = hydrateRoot(container, <App />);

Simple React button test fails

Im using jest to test a simple button in React and it keeps failing. My latest iteration complains about render. I'm new to testing and I've been at it for a while and cannot figure this out. What am I missing here?
Here's my App.js
function clickMe() {
alert("Hello")
}
function App() {
return (
<div className="App">
<button id="btn" onClick={clickMe}>Click Me!!</button>
</div>
)
}
export default App;
Here's my App.test.js
import React from 'react'
import {render} from 'react-dom'
import App from './App'
test("Click", () => {
const {container} = render(<App />)
const button = getByTestId(container, 'btn')
fireEvent.click(button)
})
You can simulate some events with the enzyme library
For this first install this by npm, then import that
import {shallow} from 'enzyme';
After using this structure to simulate a click on a bottom
Create a wrapper
let wrapper = shallow(<App />);
beforeEach( ()=>{
wrapper = shallow(<CounterApp />);
});
This creates a simulation of render components and gives you the capacity to simulate events, this method is working in my project counterApp
test('Click', ()=>{
wrapper.find('button').at(0).simulate('click');
expect(//The action of your botton).toBe(//the result expected);
});
Not exactly the same but something like this also worked
import { shallow } from 'enzyme'
import { configure } from 'enzyme'
import Adapter from 'enzyme-adapter-react-16'
import App from './App'
configure({ adapter: new Adapter() })
it('renders the link inside the output area', () => {
const output = shallow(<App />)
expect(output.find('div').find('button').length).toEqual(1)
})

Unit testing of the updated component in react using jest and enzyme

I am trying to write the testing file for the following code.
with my present code
import React from 'react';
import renderer from 'react-test-renderer';
// import { mount } from 'enzyme';
import LazyToastMessage from '../LazyToastMessage';
import IntlHelper from "test/util/Mount";
import nls from "src/nls/homepageHeader.json";
describe('the suspended toast message component renders correctly', () => {
const mountWithNLS = new IntlHelper(nls);
it('LazyToastMessage fallback', () => {
const mockHandler = jest.fn();
const wrapper = renderer.create(mountWithNLS.mountWithIntl(
<LazyToastMessage
state
message="test message"
iconType="success"
handleToasterStateChange={mockHandler}
/>)
)
expect(wrapper.toJSON()).toMatchSnapshot();
})
});
The result of the snapshot is giving as null. How can I test the updated component and take its snapshot?
The Lazy component is the following:
import React, {lazy, Suspense} from "react";
import {IAppProps} from 'src/js/components/misc/ToastMessage'
const LazyToastMessage = lazy(() =>
import(/* webpackChunkName: "toast-message" */ "./ToastMessage"),
);
export default function(props: IAppProps) {
return (
<Suspense fallback={null}>
<LazyToastMessage {...props} />
</Suspense>
);
}
To test it properly, you have to wrap your lazily loaded component in Suspense in test.
First, in your component:
export const LazyToastMessage = ...
Then, in your test:
import SuspendedLazyToastMesssage, {LazyToastMessage} from '../LazyToastMessage';
.
.
.
const wrapper = renderer.create(mountIthNLS(mountWithIntl(
<Suspense fallback=null>
<SuspendedLazyToastMessage/>
</Suspense>
));
await LazyToastMessage;
expect(wrapper.toJSON()).toMatchSnapshot();
Or something like that, but you get the idea...

How to test child component method without Enzyme?

I need to access and test a method of a child component in react using Jest. I am not using Enzyme, so this is not a duplicate of this question or this question. I would like to use React Testing Library instead of Enzyme.
Earlier I was happily accessing the methods I needed to test like this:
import React from "react";
import { render, unmountComponentAtNode } from "react-dom";
import App from "./App";
let container: any = null;
beforeEach(() => {
// setup a DOM element as a render target
container = document.createElement("div");
document.body.appendChild(container);
});
afterEach(() => {
// cleanup on exiting
unmountComponentAtNode(container);
container.remove();
container = null;
});
test("methodToTest should do what I want it to", () => {
const { methodToTest } = render(<App />, container);
methodToTest("some input");
const output = document.querySelector(".outputFromMethod");
expect(output.innerHTML).toBe("You gave me some input");
});
But now I need to wrap my <App /> component in withRouter() from react-router-dom, so I have to do something like this: (Based on the recipe suggested by React Testing Library)
import React from "react";
import { BrowserRouter as Router } from "react-router-dom";
import { render, unmountComponentAtNode } from "react-dom";
import App from "./App";
let container: any = null;
beforeEach(() => {
// setup a DOM element as a render target
container = document.createElement("div");
document.body.appendChild(container);
});
afterEach(() => {
// cleanup on exiting
unmountComponentAtNode(container);
container.remove();
container = null;
});
test("methodToTest should do what I want it to", () => {
// THIS DOESN'T WORK NOW FOR GETTING methodToTest
const { methodToTest } = render(<Router><App /></Router>, container);
const output = document.querySelector(".outputFromMethod");
expect(output.innerHTML).toBe("You gave me some input");
});
I understand that it's not ideal to try to test methods on a child component. But I need to do this because I have to have this component render inside of a <Router>. Is there any way to access the <App /> components methods without using Enzyme, or using React Testing Library if necessary?
You can't do that with Testing Library, that's against the principles. You're also using a strange style for testing. Have you tried to do this:
import React from "react";
import { BrowserRouter as Router } from "react-router-dom";
import { render } from "#testing-library/react";
import App from "./App";
import "#testing-library/jest-dom/extend-expect";
test("methodToTest should do what I want it to", () => {
const { getByText } = render(<Router><App /></Router>);
expect(getByText("You gave me some input")).toBeInTheDocument();
});

Categories