How to test React component without Browserify - javascript

I have a react application that doesn't uses the browserify tool.
It means that the React variable is exported by the script of the react js lib called in the <head>.
// React variable is already available
var MyComponent = React.createClass({});
After implementing this component, I want to create a test for it.
I took a look at Jest documentation and I've created my component test.
/** #jsx React.DOM */
jest.dontMock('../compiled_jsx/components/my-component.js');
describe('MyComponent', function() {
it('The variables are being passed to component', function() {
var React = require('react/addons');
// In the `MyComponent` import I got the error below:
// ReferenceError: /compiled_jsx/components/my-component.js: React is not defined
var myComponent = require('../compiled_jsx/components/my-component.js');
});
In the Jest documentation example, both component and its tests uses the require function for getting the React variable.
Is there any way to expose React variable into the component?
Or it's necessary using browserify for creating this test?

Jest runs in node.js, so you need to use commonjs modules. You don't use browserify with jest. If you're really against commonjs modules you can do this assuming each file is wrapped in an iffe.
var React = typeof require === 'undefined'
? window.React
: require('react/addons');
Or alternatively as the first line of your tests, try:
global.React = require('react/addons');
And either way, export your components using:
try { module.exports = Foo; } catch (e) { window.Foo = Foo };
Personally, I don't think jest is practical if you're not using commonjs modules.

Related

how use local scope with eval

I have a module JS where i use React
import React from 'react'
My component
export default class TaskDetail extends Component {...
I have a a string that represents a code:
str=`props => {
return React.createElement(.....
and I would use this code in a module JS like this:
const MyCustomWidget = eval(str)
so that one it would be equal to write:
const MyCustomWidget = props => {
return React.createElement(.....
I use MyCustomWidget to create a custom element in react-jsonschema-form
the point of my question is:
in my module i have imported React but i have error React is not defined
that is because the result of eval have another scope...
if i write on top of my module:
window.React = React
it works! but I wouldn't want to use
It is possible use eval and use the scope of my module ? I would like to use my imported React variable in my module without use window.React=React
is possible?
If you want to experiment with it...
const evalInContext = a =>
// eslint-disable-next-line no-new-func
new Function('require', 'const React = require("react");' + a).bind(
null,
require
);
See how they evaluate and run react code from a live editor in react-styleguidist
https://github.com/styleguidist/react-styleguidist/blob/34f3c83e76/src/client/rsg-components/ReactExample/ReactExample.spec.js
Once again, if you cannot 100% trust what you eval, don't do it.

Testing React Components: How does it work?

In the jest docs, I found this simple example of testing react components:
// Link.react.test.js
import React from 'react';
import Link from '../Link.react';
import renderer from 'react-test-renderer';
test('Link changes the class when hovered', () => {
const component = renderer.create(
<Link page="http://www.facebook.com">Facebook</Link>,
);
let tree = component.toJSON();
expect(tree).toMatchSnapshot();
// manually trigger the callback
tree.props.onMouseEnter();
// re-rendering
tree = component.toJSON();
expect(tree).toMatchSnapshot();
// manually trigger the callback
tree.props.onMouseLeave();
// re-rendering
tree = component.toJSON();
expect(tree).toMatchSnapshot();
});
Why do we have to import React and react-test-renderer, but not have to import other test specific things, like test, expect?
Can someone explain, how this works under the hood and what actually happens when the tests are run?
It finds the binary jest and executes it with your script, this binary would compile your code first then run it, so those modules for testing would be imported during compiling time when those function keyword was found. You install Jest to your original application to test component. React module or others it really your stuff.
Update
By tracing the repository of Jest
jest/packages/jest-runtime/src/script_transformer.js, We could found out it utilize Node.js module VM to run the script, and it has some method like vm.createContext() and vm.Script().runInContext(), so those internal module should be imported to sandbox programmatically.
Example from VM
const vm = require('vm');
const sandbox = { globalVar: 1 }; // <=> import expect, test
vm.createContext(sandbox);
...
vm.runInContext('globalVar *= 2;', sandbox); // <=> Our test code.
So those module such as expect, and test may be imported like what vm.createContext() does above.
It's hard to exactly know how this be done in a short time, but we still could get some clues:
in jest/packages/jest-runtime/src/cli/index.js
...
import Runtime from '../'; // ---> jest/packages/jest-runtime/src/index.js
export function run(...) {
...
Runtime.createContext(
...
).then(
const runtime = new Runtime(config, environment, hasteMap.resolver);
runtime.requireModule(filePath);
...
)
}
Runtime is a critical class defined in
jest/packages/jest-runtime/src/index.js
...
import Resolver from 'jest-resolve';
...
import ScriptTransformer from './script_transformer';
...
requireModule() {
_execModule(...)
}
...
_execModule() {
...
this._createRequireImplementation(
...
this._createJestObjectFor(...)
}
Many critical works here, require module, detect the environment config, has Resolver to find the module id, to detect what kind of the module, should it be mocked, return jestObject, wrap all to our sandbox for testing.
Here is its core to do mock

How do I add React as a global whilst running unit tests?

In my Rails project, I am using the react-rails gem, which does the following:
window.React = React;
This is pretty handy, but when I run unit tests using Jest, that global is not there and I get an error from the file containing the component I am testing saying that React is not defined.
If I define React in the component file using
import React from 'react';
Then it causes errors due to loading React twice.
How should I define a global React variable in my unit tests so they work?
In your test file, do:
import React from 'react'
describe('something',() => {
window.React = React
// so when you require() your component, window.React is already set
var MyComponent = require('MyComponent').default
it('does something', () => {
// do something
})
})

ReactJS Webpack error type.toUpperCase is not a function

So I have my react components bundled into myApp.js using webpack. I have made a global exposed variable called MyApp in my webpack.config.js file that holds all my react components within it - like so:
entry: {
// create myApp.js, expose MyApp global object
// index.js lists all my react components to include in myApp.js
'myApp.js': 'expose?MyApp!../ReactComponents/index.js'
},
Now in javascript when I use the following syntax to render my react component I get the error type.toUpperCase is not a function:
// MyApp.Employees - MyApp is the global object
// and Employees is my react component within MyApp global object
window.onload = function () {
// type is object and not null
// so it is found
alert(MyApp.Employees);
// react will not render it though
// using this syntax
React.render(React.createElement(MyApp.Employees, null), document.getElementById('react'));
}
I keep getting the error type.toUpperCase is not a function.
What is the syntax to get react to render MyApp.Employees?
Or what may be causing this error to be raised?
thanks for any suggestions!
Seems that you already have Employees component created by React, and you're trying to use createElement on that component.
Try
var Employees = MyApp.Employees;
React.render(<Employees />, document.getElementById('react'));
Try using
String.prototype.toLocaleUpperCase.call('your string');
or
String.prototype.toUpperCase.call('your string');

How do you manually mock one of your own files in Jest?

I'm trying to mock an object (which I created) in Jest so I can provide default behaviour within the react component (so the real implementation isn't used)
This is my react component ChatApp (it's very straight forward)
'use strict';
var React, ChatApp, ChatPanel, i18n;
React = require('react');
ChatPanel = require('./chat_panel');
i18n = require('../support/i18n');
ChatApp = React.createClass({
render() {
return (
<div className="chat-app">
<h1>{i18n.t("app.title")}</h1>
<ChatPanel />
</div>
);
}
});
module.exports = ChatApp;
So I have a custom I18n dependency that does translations (I18n is something I've written that is a wrapper for node-polyglot).
So I want to do a basic test to see if the H1 has the correct word in it, but I don't want to set jest.dontMock() on my I18n object, because I don't want it to use the real object in the ChatApp test.
So following the basic instructions on the jest website, I created a mocks folder and created a mock file for i18n, which generates a mock from the original object and then overrides the t method and adds a method to allow me to set the return string for t.
This is the mock object
'use strict';
var i18nMock, _returnString;
i18nMock = jest.genMockFromModule('../scripts/support/i18n');
_returnString = "";
function __setReturnString(string) {
_returnString = string;
}
function t(key, options = null) {
return _returnString;
}
i18nMock.t.mockImplementation(t);
i18nMock.__setReturnString = __setReturnString;
module.exports = i18nMock;
Now in my ChatApp test I require the mock in a before each, like so:
'use strict';
var React, ChatApp, TestUtils, path;
path = '../../../scripts/components/';
jest.dontMock( path + 'chat_app');
React = require('react/addons');
ChatApp = require( path + 'chat_app');
TestUtils = React.addons.TestUtils;
describe('ChatApp', () => {
beforeEach(() => {
require('i18n').__setReturnString('Chat App');
});
var ChatAppElement = TestUtils.renderIntoDocument(<ChatApp />);
it('renders a title on the page', () => {
var title = TestUtils.findRenderedDOMComponentWithTag(ChatAppElement, 'h1');
expect(title.tagName).toEqual('H1');
expect(title.props.children).toEqual('Chat App');
});
});
If i console.log the i18n object within the test then I get the correct mocked object, the __setReturnString also gets triggered (as if I console.log in that message I see the log).
However, if I console.log the i18n object within the actual React component then it gets a Jest mock but it doesn't get my Jest mock, so the t method is an empty method that doesn't do anything, meaning the test fails.
Any ideas what I'm doing wrong?
Thanks a lot
I've had trouble getting the __mocks__ folder working as well. The way I got around it is by using the jest.setMock(); method.
In your case, you would jest.setMock('../../../scripts/i18n/', require('../__mocks__/i18n');
Obviously, I am not certain of the location of your mock and the location of the real library you're using, but the first parameter should use the path where your real module is stored and the second should use the path where your mock is stored.
This should force your module and all modules that yours require (including React) to use your manually mocked i18n module.
Jest does automatic mocking. Just i18n = require('../support/i18n') should be enough. That's why you usually have to call jest.dontMock in the first place.
You can find more information here: https://facebook.github.io/jest/docs/automatic-mocking.html
What mattykuzyk mentions in his answer did not work at all for me :(
However, what I found out seemed to be the problem for me was the setup of jest: I used moduleNameMapper in the beginning, and for some reason these are never mocked...
So for me the first step was to instead move my module name mapped folder to the moduleDirectories to get anything to work.
After that, I could simply add a __mocks__ file adjacent to the actual implementation (in my case utils/translation.js and utils/__mocks__/translation.js).
As my translations.js default exports a translation function, I also default exported my mock. The entire __mocks__/translations.js is super simply and looks like this:
export default jest.fn((key, unwrap = false) => (
unwrap && `${key}-unwrapped` || `${key}-wrapped`
))
Although I haven't tested it, adding a __setReturnString should be easy enough, for me it was sufficient to actually return my translation key. Hope this helps!

Categories

Resources