Unmount / destroy Component in jsdom test - javascript

Is there a way to unmount and garbage collect a React Component that was mounted using TestUtils.renderIntoDocument inside a jsdom test?
I'm trying to test something that happens on componentWillUnmount and TestUtils.renderIntoDocument doesn't return any DOM node to call React. unmountComponentAtNode on.

No, but you can simply use ReactDOM.render manually:
var container = document.createElement('div');
ReactDOM.render(<Component />, container);
// ...
ReactDOM.unmountComponentAtNode(container);
This is exactly what ReactTestUtils does anyway, so there's no reason not to do it this way if you need a reference to the container.

Calling componentWillUnmount directly won't work for any children that need to clean up things on unmount. And you also don't really need to replicate the renderIntoDocument method, either since you can just use parentNode:
React.unmountComponentAtNode(React.findDOMNode(component).parentNode);
Update: as of React 15 you need to use ReactDOM to achieve the same:
import ReactDOM from 'react-dom';
// ...
ReactDOM.unmountComponentAtNode(ReactDOM.findDOMNode(component).parentNode);

Just to update #SophieAlpert answer. React.renderComponent will be deprecated soon so you should use ReactDOM methods instead:
var container = document.createElement('div');
ReactDOM.render(<Component />, container);
// ...
ReactDOM.unmountComponentAtNode(container);

After your test you can call componentWillUnmount() on the component manually.
beforeEach ->
#myComponent = React.addons.TestUtils.renderIntoDocument <MyComponent/>
afterEach ->
#myComponent.componentWillUnmount()

Just stumbled across this question, figure I would provide a way to directly tackle it using the described renderIntoDocument API. This solution works in the context of PhantomJS.
To mount onto the document node:
theComponent = TestUtils.renderIntoDocument(<MyComponent/>);
To unmount from the document node:
React.unmountComponentAtNode(document);

Related

How to destroy root Preact node?

I want to destroy the root Preact DOM node. I initially render my component as follows:
import { h, render } from 'preact';
import App from "./components/App";
render(<App />, document.querySelector("#app");
How do I destroy App? Do I simply unmount the #app DOM node, or does Preact offer a method similar to React's unmountComponentAtNode() method?
Disclaimer: I work on preact.
Any rendered preact app can be easily destroyed by passing null to render:
render(null, document.querySelector("#app"));
We don't need any special functions to do that and have chosen to keep a small API surface area. The implementation for unmountComponentAtNode in preact-compat does literally just call render with null:
// Excerpt from compat for Preact X
function unmountComponentAtNode(container) {
if (container._prevVNode!=null) {
render(null, container);
return true;
}
return false;
}
There is no method like unmountComponentAtNode() without preact-compat.
A workaround is to use the third argument of the render() method to replace the component you want to unnmount by '' or null like suggested here :
https://github.com/developit/preact/issues/53#issuecomment-184868295

How to access and change selectColumns core method inside react-handsontable?

I want to know that, how to access selectColumns menthod inside react-handsontable. I already tried to assign refs to hot table component <HotTable refs="hot"/> but still i cant access selectColumns method.
Thanks for reading this question.
#Sagar Any other approaches? It's not so convenient if we include HotTable in other components and can't render it in such way.
If we speak about this rows from example
const exampleComponent = ReactDOM.render(
<ExampleComponent />, document.getElementById('example')
);
window.hotInstance = exampleComponent.refs.hot.hotInstance;
it's okay, when we render it as a separate component. But if I import it in App component and then render App component via ReactDom.render, I have no refs, just empty object

How can I trigger a state change in a unit test with React DOM?

I'm using the React Test Utilities to unit test some of my code. I call renderIntoDocument to render a custom component and then use findDOMNode to test out what got rendered. The trouble I'm running into is that I'm not sure how to update the state and effectively trigger a re-render within the context of a unit test.
Here's some sample code -- pay attention to the code comment:
import React from 'react';
import ReactDOM from 'react-dom';
import TestUtils from 'react-dom/test-utils';
import MyCustomComponent from '../../app/components/MyCustomComponent';
describe('My Test Suite', () => {
let component, node;
test('verify state change', () => {
const items = [{'value': '1'}];
component = TestUtils.renderIntoDocument(
<MyCustomComponent items={items} />
);
node = ReactDOM.findDOMNode(component);
expect(node.querySelector('input[type=text]').value).toEqual('1');
component.state.items = [{'value': '2'}];
// do something here to trigger a re-render?
expect(node.querySelector('input[type=text]').value).toEqual('2');
});
});
Unfortunately it seems simply changing the state variable doesn't do anything. And I can't call component.componentWillReceiveProps() because that doesn't seem to be defined.
Please note that I do want the same component to call its render function rather than replacing it with, effectively, a brand new component. The reason is because I found a bug where the component was rendering things based on this.props instead of this.state, and I want a test to show that it's always using data from the state and not from the initial values.
Enzyme from AirBnb has some great utilities for this. You'll need to install the dependencies but it's simple enough to get it configured. Then, you can simply call Enzyme's setState method on your component instance. An important note – your "component instance" in this case is a shallow rendered component. Your code would look something like this:
import React from 'react';
import MyCustomComponent from '../../app/components/MyCustomComponent';
import { shallow, configure } from 'enzyme';
import Adapter from 'enzyme-adapter-react-16';
// configure your adapter
configure({ adapter: new Adapter() });
describe('My Test Suite', () => {
test('verify state change', () => {
const items = [{'value': '1'}];
const wrapper = shallow(<MyCustomComponent items={items} />);
// find your shallow rendered component and check its value
expect(wrapper.find('input[type="text"]').value).toEqual('1');
// set the state of the component
wrapper.setState({ items: [{'value': '2'}] });
// state should be updated, make sure its value was properly set
expect(wrapper.find('input[type="text"]').value).toEqual('2');
});
});
All of this assumes that you are using state in your component properly. In your case, items appears to be passed in as a prop. If you are setting state by just copying props, you may want to rethink your strategy. In any case, this approach should be identical to how state updates in React work – you're operating on the same component instance without unmounting and remounting the component. Hope this helps.

How to traverse a shallow component nested in ThemeProvider HOC?

Following up the issue on Github, I have a component Comp that when exported, is wrapped with injectSheet from reactjss. Please see the setup on codesandbox.
In a unit test, I'd like to assert that that component contains <a>, which it does (see codesandbox), but the test fails regardless:
describe("<Comp /> component", () => {
const wrapper = shallow(<Comp />);
it("should render a <a>", () => {
expect(wrapper.find('a')).to.have.length(1);
});
});
I get Error: [undefined] Please use ThemeProvider to be able to use WithTheme. So my natural (perhaps not the correct?) reaction was to wrap the component with ThemeProvider:
const wrapper = shallow(
<ThemeProvider theme={{}}>
<Comp />
</ThemeProvider>
)
Then I get AssertionError: expected { length: 0 } to have a length of 1 but got 0.
I tried a whole slew of approaches, including calling dive, find or first with an extra shallow call, but I would always end up with Please use ThemeProvider to be able to use WithTheme:
// 1. dive(), as suggested in
// https://github.com/cssinjs/react-jss/issues/30#issuecomment-268373765
expect(wrapper.dive('Comp')).to.have.length(1);
expect(wrapper.dive('Comp').find('a')).to.have.length(1);
expect(wrapper.dive().find('a')).to.have.length(1);
// 2. find() / first(), as suggested in https://github.com/airbnb/enzyme/issues/539
expect(wrapper.find(Comp).shallow().find('a')).to.have.length(1);
expect(wrapper.first().shallow().find('a')).to.have.length(1);
Any ideas here? I am a bit new to unit testing with React, so I would appreciate if someone could enlighten me on this ;)
For anyone still struggling with this, one viable approach was suggested on GitHub. Instead of testing the styled component wrapped with injectSheet HOC, you export your stand-alone component and test it in isolation
// Component.js
import React from 'react'
import injectSheet from 'react-jss'
const styles = {
color: 'burlywood'
}
// named export for unit tests
export const Component = props => <h1>Component</h1>
// default export to be used in other components
export default injectSheet(styles)(Component)
which would work for most use cases, since more often than not, you need to unit test the plain component and its logic, and not any of its associated styling. So in your unit test just do
import { Component } from './Component'
instead of (which you would do in the rest of your codebase)
import Component from './Component'

How can I test my react router?

I built a simple MyRouter component based on react-router. Now I'd like to test my routing logic, like this:
it('should show AboutPage for /about`, () => {
let vdom = renderMyRouterForPath('/about');
expect(vdom).toEqual(<AboutPage />);
}
I'm using plain Jasmine in Node, no Jest mocking magic. It would be also nice to avoid rendering the whole thing in DOM (virtual DOM should be enough, I don't mean to test DOM interactions).
I've had some success with setting history={ history.createMemoryHistory(path) } in Router, but this wasn't enough. I tried shallow rendering, but with no luck (it returns the Router build by MyRouter).
How can I implement the renderMyRouterForPath function?

Categories