Cannot test component inside a Panel using Jest/Enzyme - reactjs

I need to test a component inside a Panel.
The main issue is that a panel is rendered outside of the react root element when it's opened.
When I mount my component the content of the panel is not displayed in the mount. I found that the content is outside of react. I'm able to access the content using the document but I want to leverage Jest/Enzyme to simulateClick, spyOn and so on...
How can I test elements inside the Panel using Jest/Enzyme?

Try to use jsdom in order to get the document or window.
import jsdom from 'jsdom'
const doc = jsdom.jsdom('<!doctype html><html><body></body></html>')
global.document = doc
global.window = doc.defaultView
...

Related

React-leaflet: How to pass dynamic HTML/jsx/react component inside bindPopup?

I want to pass a react component or jsx code to the bindPopup function but it accepts string as input. I tried using ReactDOMServer.renderToString as shown below but the popup is not dynamic. I want an onClick event to be registered inside the PopupTemplate component. Is there any better alternative:
Code:
layer.bindPopup(
ReactDOMServer.renderToString(
<PopupTemplate
layerId={layerId}
/>
)
);
Behaviour:
I can't use the Popup component here as the layer is a drawn polygon using react leaflet draw.

React Component getting added in Vaadin 7 only on button click

I have a vaadin7 application to which I am trying to integrate a sample react application. It is working properly when I implement the React component creation inside a button click. But it is not working if I put the code outside the button click. It shows error "Uncaught TypeError: Cannot read property 'appendChild' of null
at Module.11 (index.js:9)
at u (bootstrap:84)
at t (bootstrap:45)
at Array.r [as push] (bootstrap:32)
at main.90a85973.chunk.js:1" while inspecting.
In vaadin application, i am creating div named "root" and in the index.js of sample react application, I am trying to get the root div created from vaadin and appending "react-root" to "root" div so as to fit the react application in my vaadin layout.
Please find below code for vaadin ui
CustomLayout layout = null;
try {
String dynamicHtml = "<div id=\"root\"></div>";
layout = new CustomLayout(new ByteArrayInputStream(dynamicHtml.getBytes()));
System.out.println("JS Layout");
} catch (IOException e) {
System.out.println("could not create custom layaout"+ e);
}
uimainLayout.addComponent(layout);
//If the below 4 lines of code are added inside the button click, the reactcomponent is rendered inside the layout properly on clicking the button . Otherwise it is showing the "Uncaught TypeError: Cannot read property 'appendChild' of null"
VerticalLayout jsmainLayout = new VerticalLayout();
ReactComponent reactComponent=new ReactComponent();
jsmainLayout.addComponent(reactComponent);
uimainLayout.addComponent(jsmainLayout);
getMainLayout().addComponent(uimainLayout);
setCompositionRoot(getMainLayout());
My Reactcomponent
#JavaScript({"<ip:port>/static/js/2.1f3e22a9.chunk.js","<ip:port>/static/js/3.583f7bad.chunk.js","<ip:port>/static/js/runtime-main.23689a58.js","<ip:port>/static/js/main.90a85973.chunk.js","ReactComponentConnector.js"})
#StyleSheet("<ip:port>/static/css/main.a617e044.chunk.css")
public class ReactComponent extends AbstractJavaScriptComponent {
public ReactComponent() {
System.out.println("Inside ReactComponent");
}
#Override
protected ReactComponentState getState() {
return (ReactComponentState) super.getState();
}
}
ReactComponentState
public class ReactComponentState extends JavaScriptComponentState {
private static final long serialVersionUID = -3283446856435450054L;
}
ReactComponentConnector.js
window.com_p1_p2_ReactComponent = function () {
}
Index.js of my sample react application
const rootEl = document.createElement('div')
rootEl.setAttribute('id', 'react-root');
//error points to the below line
const parentEl =document.getElementById('root');
parentEl.appendChild(rootEl)
ReactDOM.render(
<App />
,
rootEl
);
reportWebVitals();
It's not clear exactly what it is that triggers running the code listed for index.js, but it would seem like it's run before the contents of the custom layout has been initialized. In that way, no element with the id root exists and this would cause the reported error.
I assume creation happens through new ReactComponent(this.getElement()) from inside the JS connector initializer. If this is the case, then they reason for the problem is that both the actions (set custom layout content and initialize the JS connector) are sent to the browser in the same batch, but the connector is initialized in an earlier phase. If the JS component is sent in a batch triggered though a click listener, then this won't be a problem.
To solve this, you could use a direct element reference instead of relying on getElementById in combination with an id defined in the content of a custom layout. The easiest way would be if you'd restructure to use the this.getElement() instance that you're now passing to the ReactComponent constructor.

#testing-library/React : Clicking outside of component not working

I'm using react testing library to test my component built with FluentUI.
Here is link:
https://codesandbox.io/s/keen-borg-2tqmj?file=/src/App.spec.js
The code is basically a pasted snippet of the example code of Dialog component from FluentUI docs site. The behavior that I'm testing is:
User opens the dialog
User clicks outside of the dialog
onDimiss prop of the component should be fired.
It works when I'm playing with it manually however it seems that I failed to simulate a click outside of the component with testing library.
I tried using userEvent.click(document.body) as mentioned in this post but got no luck
Does anyone has any idea how to make test work?
It is not working because the Dialog component is not listening for the onClick event on the body, so what you need to do in this case is to find the actual element that is being clicked, observing the dom you'll find that the overlay is a div with some overlay classes on it.
<div
class="ms-Modal is-open ms-Dialog root-94"
role="document"
>
<div
aria-hidden="true"
class="ms-Overlay ms-Overlay--dark root-99"
/>
The problem now is to find a way to select it. Unfortunately, you cannot select elements in RTL by their className, so you need to use another selector; in this case, we can get the parent element by the role and then access the first child.
const onDismiss = jest.fn();
const { getByRole } = render(<App onDismiss={onDismiss} />);
UserEvent.click(screen.getByText("Open Dialog"));
const document = getByRole("document");
UserEvent.click(document.firstChild);
expect(onDismiss).toHaveBeenCalled();
https://codesandbox.io/s/hungry-joliot-tjcph?file=/src/App.spec.js

React: empty placeholder div gets re-rendered and thereby removes children (asynchr attached chart)

I am working on a widget dashboard which has DC.JS charts as content for each widget.
Widgets are created/ removed using react-grid-layout which creates an empty placeholder node like this:
<div id={"content_" + this.props.id} className="widgetContent"> /* chart is later drawn here */ </div>)
DC.JS later selects the div by Id and attaches its SVG chart as a child.
The problem is that for some events (like toggling static or changing Ids of the widgets), react re-renders the widgets and thereby "overwrites" the existing charts (children) with a brand new empty placeholder div as above.
My question is if that issue can be solved by React-techniques (can I prevent a div from ever being re-rendered?) or if this is an issue with the library itself.
Very similar code can be found here. The code in action is here. Imagine the snippet line above (the empty chart placeholder where a chart is attached later) in line 44.
The common solution here is to wrap this chart in a component where shouldComponentUpdate is set to false. That way react will never alter the element which your charting library modifies. An example wrapper component can be found here (including below)
var React = require('react/addons');
var ReactIgnore = {
displayName: 'ReactIgnore',
shouldComponentUpdate (){
return false;
},
render (){
return React.Children.only(this.props.children);
}
};
module.exports = {
Class: ReactIgnore,
Component: React.createClass(ReactIgnore)
};

How to behaviour test a Component which uses React Bootstrap with Jest

In pseudo code:
MyComponent: React.createClass
doThis: () ->
//do something
render: () ->
<div>
<button className='something' onClick={clickHandler()}>click this button</button>
<ReactBootstrap.Pagination onSelect=(this.doThis) items=3 />
</div>
//tests
component = TestUtils.renderIntoDocument <MyComponent>
//test1
el = TestUtils.findRenderedDOMComponentWithClass component, 'something'
TestUtils.Simulate.click el
//test2
el = TestUtils.srcyRenderedDOMComponentsWithTag component, 'li'
TestUtils.Simulate.click el[0]
In test1 the click is fired. In test2 doThis() is not called
In both cases I definitely have a dom node and am firing the click on it. onSelect is the correct prop to use to pass to ReactBoostrap.Pagination. It works fine in the browser.
The Pagination class in Bootstrap uses onClick and seems to attach it to the li element it renders so I think I am targeting the correct element. (Edit: looking at Bootstrap-react's test for the Pagination component that targets the a tag which is rendered inside the li https://github.com/react-bootstrap/react-bootstrap/blob/master/test/PaginationSpec.js. However; I tried that too so I don't think that is my problem).
It seems to be to do with trying to target a dom node rendered by a child component. But I have no idea how to proceed. (Edit: or perhaps it is specific to react-bootstrap? Maybe I need to not mock some dependency...?)
The answer, for me, was to not mock 'classnames' - which is a dependency of react-bootstrap:
jest.dontMock 'classnames'
The answer was given by: http://racingtadpole.com/blog/test-react-with-jest/ so thanks to racingtadpole.

Resources