React Mocha rendering, DOMException Wrong Document - reactjs

Using React 13.2 and looking to test lifecyles with a setup like the one shown on this gist. If I don't stop subsequent renders (via the shouldComponentUpdate method) then anything causes a render (after the initial) explodes with a DOMException Wrong Document:
DOMException: Wrong document
at core.Node.insertBefore (D:\development\projects\fsm\node_modules\jsdom\lib\jsdom\level1\core.js:583:13)
at core.Node.insertBefore (D:\development\projects\fsm\node_modules\jsdom\lib\jsdom\level2\events.js:326:32)
at insertChildAt (D:\development\projects\fsm\node_modules\react\lib\DOMChildrenOperations.js:34:14)
at Object.DOMChildrenOperations.processUpdates (D:\development\projects\fsm\node_modules\react\lib\DOMChildrenOpertions.js:106:11)
JSDOM bails because the parent node is not a document and it don't share the same owner document as the child being inserted. Yeah. How could the owning document be anything other than that the global unless React is doing something funky under the hood.
Just surprised that I don't see more people having a similar problem? There is nothing odd-ball with my Mocha setup nor the JSX components being rendered. Plus the initial render goes fine.

Update for node 4
With node 4 we can use the latest jsdom and solve this issue in a better way, e.g. using testdom.
This is how I test a React 0.13 component with mocha on node 4:
import testDom from "testdom";
import { expect } from "chai";
testDom("<html><body></body></html>");
const React = require("react/addons");
const MyTestableComponent = require("../src/MyTestableComponent");
const ExecutionEnvironment = require("react/lib/ExecutionEnvironment");
ExecutionEnvironment.canUseDOM = true;
describe("MyTestableComponent", () => {
it("works!", () => {
const component = <MyTestableComponent />;
expect(true).to.equal(true);
})
})
Note that we should require rather than import React and the component.
Previous answer
I could fix this issue by following the OP's own comment to the question.
Since React stores the document in an internal variable when it is required, we need to remove React from the require.cache object, before requiring it again:
var jsdom = require("jsdom").jsdom;
var React, TestUtils;
describe("Example", function() {
beforeEach(function() {
// remove react from the require cache
for (var key in require.cache) {
if (key.match(/\/node_modules\/react\//)) {
delete require.cache[key];
}
}
// init the DOM
global.document = jsdom("<html><head><script></script></head><body></body></html>");
global.window = document.parentWindow;
// require react again
React = require("react/addons");
TestUtils = React.addons.TestUtils;
});
// my tests...
});

Related

renderHook in context wrapper function only works when at the top of imports

So I sort of found a fix for this, but I want to understand what's happening and why.
The background is that I'm testing some classes, each class has a field for the result of a hook for reasons that aren't relevant here. All that happens with the hook in the constructor of each class is that it gets assigned to the member variable.
In order to test each of these classes, I had originally mocked out a few things so that the hook behaved just like a normal function, and that worked fine. But then I discovered the renderHook feature of react testing library and figured it was probably better to use a mature tool instead.
The hook depends on a react context, so I followed the instructions and created this helper function to get my hook
import { mockContext } from './mockContext';
import { renderHook } from '#testing-library/react';
import { OAuthRequests } from '../lib/core/models/auth/OAuthRequests';
import { useOAuth } from '../lib/core/hooks/useOAuth';
import { ClientConfig } from '../lib/core/models/auth/RedditConfig';
export const mockClient: ClientConfig = { clientId: '', clientSecret: '' };
export const mockOAuth = () => {
const wrapper = mockContext();
const {
result: { current: requests },
} = renderHook<OAuthRequests, ClientConfig>(() => useOAuth(mockClient), { wrapper });
return requests;
};
The problem came when I refactored these test suites, all of which were passing. ONLY those suites which were subclasses of one particular superclass were failing. The message with the failure was
TypeError: Class extends value undefined is not a constructor or null
mockOAuth works just fine in every test suite except the ones that extend a certain abstract superclass. I get no runtime errors in my actual app using any of these classes, and like I said, before refactoring to use renderHook it was also all working fine with passing tests. Also, the error seemed to happen at the import stage, not at construction of any objects.
So, after a lot of debugging and trial and error, I discovered that if I placed my import of mockOAuth at the top of the file above the class imports, it works, but if it's below them, it breaks.
It's great that I have a working solution, but I really want to understand why this is happening in case it is a canary for some very obscure bug in my code that won't pop up easily.
Also, I'm tagging the hooks testing library as well here, but I am using react testing library 13.4.0 and react 18.1.0.

How does React Lazy fetch components?

I recently read about React Lazy and how it "loads" the components during run time when they are required to be rendered. I assume that "loading" here means fetching the component from the server and then rendering it.
So my question is, how does React manage this fetching of components? How does it know the exact path from where to fetch this component (given that our code will mention the relative path but fetching will require complete server path)? Does it depend on Webpack for this?
Let's look into the React code. React.lazy is defined as follows.
export function lazy<T, R>(ctor: () => Thenable<T, R>): LazyComponent<T> {
let lazyType = {
$$typeof: REACT_LAZY_TYPE,
_ctor: ctor,
// React uses these fields to store the result.
_status: -1,
_result: null,
};
if (__DEV__) {
// ... additional code only in development mode
}
return lazyType;
}
As you can see, React.lazy requires a Promise which resolves to a module with a default export containing a React component (freely cited by React Docs). This also means that not React resolves the file, but import() does. import() works as documented in the MDN.
The async import() is a new function in ES6 which is not available in all browsers but can be polyfilled by Webpack and Babel/Typescript/others.
What you often see is code like the following, which automatically splits the imported file away by Webpack.
import(/* webpackChunkName: "xyz" */ './component/XYZ')
This creates a new javascript xyz.js next to your bundle script.
If you don't use Webpack, you need to create those files by yourself. Webpack just reduces the work required from you. So you don't absolutely depend on Webpack. This approach might look like the following:
// ./component/xyz.js
export default function() { return <div>Component</div> }
// ./main.js
const OtherComponent = React.lazy(() => import('./component/xyz.js'));
export default function() { return <div>Component</div> }
And the file structure:
| public
|---| main.js
|---| component
|---| --- | main.js
As you see, no webpack required. It just makes your life easier.

Jest Testing Electron/React Component using window.require

I'm currently creating an Electron application that uses React to create the interface. In order to get access the fs, I have been using:
const fs = window.require('fs');
Which works fine when in an Electron window.
The issue is that when I write jest tests for any components that use the window.require('fs'), I get the following error when running the test.
TypeError: window.require is not a function
I have looked through the documentation for Jest and it seems the solution is to generate a mock of window using a manual mock (see "Mocking methods which are not implemented in JSDOM" at https://jestjs.io/docs/en/manual-mocks). However, when I tried to mock window.require by adding at the top of my test file
window.require = jest.fn();
I still get the same TypeError.
I'm very new to create Jest mocks so any help with this would be much appreciated.
My current test file (Component.test.js) looks like
window.require = jest.fn();
import React from 'react';
import renderer from 'react-test-renderer';
import Component from '../index';
describe('Testing', () => {
it('Component renders correctly', () => {
const component = renderer.create(<Component />);
let tree = component.toJSON();
expect(tree).toMatchSnapshot();
});
});
Add this line at the beginning of your test (or in a setupFilesAfterEnv setup file):
window.require = require;
Details
electron supplies window.require but it isn't defined when running unit tests with Jest.
By default Jest provides a browser-like environment using jsdom that includes a window object.
The above line mocks window.require by setting it equal to the current value of require.
I once faced this issue and below was what solved it for me:
I had to leverage on the module react-app-rewired. this module Tweaks the webpack config(s), even for those using create-react-app(CRA) without using 'eject' and without creating a fork of the react-scripts.
all you need is to add a config-overrides.js file in the root of your project, and populate it with the snippet below:
module.exports = function override (config) {
config.target = 'electron-renderer'
return config;
}
then you proceed to your package.json and replace your start script with
"start": "react-app-rewired start" . and you are done. you can thereafter rebuild and run your test script without getting the window.require is not a function error.
I hope I have been able to help.
cheers!

Sonarqube : Create a custom page using react

I would like to create a custom page using react but I cannot find the documentation to do this. On the Sonarqube documentation, there only the way to create a custom page using javascript only and I don’t understand how the example plugin works with react.
Can you tell me if there is a documentation that I can use.
Short answer: There isn't. There is barely anyone (no one in fact, as far as I've seen) using custom pages currently.
However, it IS possible. You need to create a react project with Webpack (or a similar JS packager).
I also recommend using Create-React-App. This fixes a lot of the setup for you. After that, in your index.js you use the example code from the SonarQube wiki.
Here is an example:
/*
PRODUCTION ENTRYPOINT
*/
import React from 'react';
import ReactDOM from 'react-dom';
import Project from './components/Project';
import './main.css';
window.registerExtension('myplugin/coverage', function (options) {
appendCustomCSS();
let isDisplayed = true;
window.SonarRequest.getJSON('/api/measures/component', {
component: options.component.key,
metricKeys: 'coverage'
}).then(function (response) {
if (isDisplayed) {
let obj = JSON.parse(response.component.measures[0].value);
let div = document.createElement('div');
render(obj, div);
options.el.appendChild(div);
}
});
return function () {
isDisplayed = false;
};
});
function appendCustomCSS() {
let fileref = document.createElement("link");
fileref.setAttribute("rel", "stylesheet");
fileref.setAttribute("type", "text/css");
fileref.setAttribute("href", "/static/myplugin/coverage.css");
document.head.append(fileref);
}
function render(objectArray, container) {
ReactDOM.render(<div className="Coverage"><Project objects={objectArray}/></div>, container);
}

Using Browserify to create React Bundle

It's the same question as here, but I don't understand the answer. So, my question is about the answer.
The answer is
Your react.js file/module isn't exposing the variables React and
ReactDOM you instantiate. In node, you make these methods public by
modifying the module.exports object like so:
module.exports = { React: React, ReactDOM: ReactDOM }
Finally, my question is: Where do "you make these methods public"?
You are making these methods public by defining them on the module.exports property. For instance, say you have a file reactExports.js with
var iWontGetThere = 'I am in this module only';
module.exports = { React: React, ReactDOM: ReactDOM }
now in another file, you can require these methods and use them, like so:
var React = require('reactExports').React;
var SweetComponent = React.createClass({
render: function() {
return (
<div>React is cool and I can use it in here!</div>
);
}
});
module.exports = SweetComponent;
Now imagine you want to render SweetComponent in another component. If I hadn't written module.exports = SweetComponent, requiring this module in another component would have no effect as all as you would be importing an empty object {}.
Let's say I tried to console.log(React.iWontGetThere); What would happen? I would get a reference error, as it was not exported with the contents of reactExports.js-it only exists in that module, but it is not exposed.
This is a contrived example, but the interesting story here is the ability to bring in encapsulated modules. I suggest reading more about node modules here
and checking out this answer as well.
and making some examples to get the hang of it.
tl;dr: defining variables and not having a module.exports = some_value statement then requiring that same file will default to an empty object.

Resources