How to put functions in jest globals? - reactjs

I am setting up jest and it is not finding window.I18n.t() because it's defined by sprockets (ruby on rails, independent of webpack). So I'd like to mock that method. Here is the error:
FAIL spec/javascript/sanity_test.js
● Test suite failed to run
TypeError: Cannot read property 't' of undefined
24 |
25 | export const newOrderPreferenceObject = {
> 26 | label: window.I18n.t("some.path.in.localization"), default_value: "",
in package.json I can do this:
"jest": {
"testEnvironment": "jsdom",
"globals": {
"window": {
"I18n": { "t": {} }
}
},
I change testEnvironment from jsdom to node, as well.
However, t() must be a function, not a data structure. So how do I put a function in window in jest test?

I don't know the circumstances of your project but here's how I setup and mock global objects in my projects.
I create a setupTests.js in the root src directory and mock them there.
setupTests.js
window.I18n = {
t: jest.fn(),
}
https://create-react-app.dev/docs/running-tests/#srcsetuptestsjs-1

Related

React with TypeScript using tsyringe for dependency injection

I am currently having trouble with my React TypeScript project.
I created my project with npx create-react-app my-app --template typescript.
I recently added tsyringe for dependency injection and was trying to implement it for an apiService. After following the readme(https://github.com/microsoft/tsyringe#injecting-primitive-values-named-injection) for adding primitive values I have hit a block. I already add experimentalDecorators and emitDecoratorMetadata to my tsconfig.json file with no success.
The error actual error I am encountering is:
./src/ts/utils/NetworkService.ts 9:14
Module parse failed: Unexpected character '#' (9:14)
File was processed with these loaders:
* ./node_modules/#pmmmwh/react-refresh-webpack-plugin/loader/index.js
* ./node_modules/babel-loader/lib/index.js
You may need an additional loader to handle the result of these loaders.
|
| let NetworkService = (_dec = singleton(), _dec(_class = (_temp = class NetworkService {
> constructor(#inject('SpecialString')
| value) {
| this.str = void 0;
I am fairly sure this problem is caused by Babel, however I created this with npm create react-app --template typescript and do not seem to have access to the Babel configuration.
NetworkService.ts
#singleton()
export default class NetworkService
{
private str: string;
constructor(#inject('SpecialString') value: string) {
this.str = value;
}
}
Invocation method
bob()
{
const inst = container.resolve(NetworkService);
}
Registering Class in index.ts
container.register('SpecialString', {useValue: 'https://myme.test'});
#registry([
{ token: NetworkService, useClass: NetworkService },
])
class RegisterService{}
React-Scripts manages many of the configs related to the project. For many cases, this is fine and actually a nice feature. However, because React-Scripts uses Babel for it's development environment and does not expose the config.
You have to run npm run eject to expose the configurations.
Please note, this is a one-way operation and can not be undone.
Personally, I prefer more control with my configuration.
After this you can edit the webpack.config.js in the newly created config folder.
Find the section related to the babel-loader in the dev-environment and add 'babel-plugin-transform-typescript-metadata' to the plugins array.
Expanding on Jordan Schnur's reply, here are some more pitfalls I encountered when adding TSyringe to my CRA app:
Use import type with #inject
If you get this error "TS1272: A type referenced in a decorated signature must be imported with 'import type' or a namespace import when 'isolatedModules' and 'emitDecoratorMetadata' are enabled." replace import with import type for the offending imports. You will encounter this when working with #inject
E.g. replace import { IConfig } from "iconfig" with import type { IConfig } from "iconfig"
Fixing Jest
Your Jest tests will also break with TSyringe, especially when using #inject. I got the error "Jest encountered an unexpected token" with details constructor(#((0, _tsyringe.inject)("")) ("#" marked as the offending token). I took the following steps to fix that in CRA:
Add the line import "reflect-metadata"; to the top of the file src/setupTests.ts
In config/jest/babelTransform.js replace line 18 and following:
From
module.exports = babelJest.createTransformer({
presets: [
[
require.resolve('babel-preset-react-app'),
{
runtime: hasJsxRuntime ? 'automatic' : 'classic',
},
],
],
babelrc: false,
configFile: false,
});
to:
module.exports = babelJest.createTransformer({
presets: [
[
require.resolve('babel-preset-react-app'),
{
runtime: hasJsxRuntime ? 'automatic' : 'classic',
},
],
],
plugins: [
require.resolve('babel-plugin-transform-typescript-metadata')
],
babelrc: false,
configFile: false,
});
Instead of eject, you may use a lib that "overrides" some of your params.
I used craco : https://www.npmjs.com/package/#craco/craco
I've created an simpler DI library that doesn't need decorators or polyfill. Works with CRA like a charm and has cool React bindings
iti
import { useContainer } from "./_containers/main-app"
function Profile() {
const [auth, authErr] = useContainer().auth
if (authErr) return <div>failed to load</div>
if (!auth) return <div>loading...</div>
return <div>hello {auth.profile.name}!</div>
}

How to import other applications exported files in .test.js files in single-spa environment

Hi I actually need to import exported files from one application(commons) to other application(login) in some jest files, but it is not getting detected by jest.
Jest Test file:
import { auth as ServiceAuth } from "#dfs/standard"
import { ContextAlert } from '#dfs/standard';
describe("login page", () => {
it("test case 1", () => {
const ContextAlert = useContext(ContextAlert );
expect(ContextAlert).toBeTruthy()
})
})
Error Message:
FAIL src/Components/LoginPage.test.js
● Test suite failed to run
Cannot find module '#dfs/standard' from 'LoginPage.test.js'
> 1 | import { auth as ServiceAuth } from "#dfs/standard"
| ^
2 | import { ContextAlert } from '#dfs/standard';
3 |
4 | describe("login page", () => {
at Resolver.resolveModule (node_modules/jest-resolve/build/index.js:299:11)
at Object.<anonymous> (src/Components/LoginPage.test.js:1:1)
jest.config.js
module.exports = {
transform: {
"^.+\\.(j|t)sx?$": "babel-jest"
},
moduleNameMapper: {
"\\.(css)$": "identity-obj-proxy",
"\\.(jpg|jpeg|png|gif|eot|otf|webp|svg|ttf|woff|woff2|mp4|webm|wav|mp3|m4a|aac|oga)$": "identity-
obj-proxy",
}
};
But the file is getting imported in the component files with the same syntax.
How can I import the file in the jest test file ?
Jest is a very different environment than the browser, and resolving modules is also different. The code being loaded in Jest is not bundled and has not been processed through Webpack so externals are not resolved at run-time.
To enable unit tests with cross microfrontend imports, these are your options:
Publish mocks for that shared dependency (so that the mock and the actual implementation are nearest to each other)
Mock the shared dependency with Jest’s moduleNameMapper configuration
Mock the shared dependency locally in a __mocks__ file
Publish the shared dependency to a registry (npm or some other internal registry such as artifactory) and install it as a devDependency locally so that Jest can resolve it locally (though this will likely require a separate build to execute in jest/node environment)
The option you choose depends on your organization's needs.
See also: https://github.com/single-spa/single-spa.js.org/issues/389

Use scoped packages with Jest

I am developing an app with react-native and typescript and doing the tests with Jest, but I have a problem when I use scoped packages (#assets), jest can not find the path and gives error.
The directory structure looks like this:
project/
assets/
img/
foo.png
package.json
src/
Foo.ts
build/
Foo.js
// assets/package.json
{
"name": "#assets" // My #assets scope
}
// build/Foo.js
const image = require('#assets/img/foo.png'); // <- error in Jest
So when I run the jest:
npm run jest build/
It can not find '#assets/img/foo.png' and throws the error:
Cannot find module '#assets/img/logo.png' from 'Foo.js'
How can I use scope package in Jest?
Jest version: 20.0.4
thanks
For those that get here that are using private packages under a scoped org, here's how I tackled this:
"jest": {
"moduleNameMapper": {
"^#org-name/(.*)$": "<rootDir>/node_modules/#org-name/$1/dist/$1.es5.js"
}
}
This assumes that all of your scoped packages have a similar path to their exported module. If they don't, you can specify them individually:
"jest": {
"moduleNameMapper": {
"^#org-name/package-one$": "<rootDir>/node_modules/org-name/package-one/dist/package.js",
"^#org-name/package-two$": "<rootDir>/node_modules/org-name/package-two/build/index.js"
}
}
Necessary only define the moduleNameMapper in jest config:
// package.json
"jest": {
"moduleNameMapper": {
"^#assets.+\\.(png)$": "<rootDir>/assetsTransformer.js"
},
}
// assetsTransformers.js
const path = require('path');
module.exports = {
process(src, filename, config, options) {
return 'module.exports = ' + JSON.stringify(path.basename(filename)) + ';';
},
};
Thanks for this comment :-)

Jest + ES2015 import

I am making an exercise with ES2015, Jest, React and I get this error:
TypeError: Property description must be an object: undefined
at defineProperties (native)
at Object.eval (<PROJECT>/node_modules/event-emitter/index.js:127:8)
After digging into it, I think it is related to the import of the nodeModule EventEmitter or by extending the class by it.
This is the code of the script file:
import EventEmitter from 'event-emitter';
import AppDispatcher from '../dispatcher/app-dispatcher';
import {
ACTION_CURSOR_POSITION_CHANGED,
ACTION_IS_DRAGGING_CHANGED
} from '../constants/actions';
let _draggingStoreInstance = null;
/**
* DraggingStore class
*/
export default class DraggingStore extends EventEmitter
{
/**
* Constructor
*/
constructor () {
// ...
The source code of the test file looks like this:
import '../unmock/dragging-store.unmock.js';
import DraggingStore from '../../src/stores/dragging-store';
describe('Dragging Store', () => {
let draggingStoreInstance = null;
beforeEach(() => {
draggingStoreInstance = DraggingStore.getInstance();
});
it('should be defined', () => {
expect(DraggingStore).toBeDefined();
expect(draggingStoreInstance).toBeDefined();
});
});
I made an extra file for excluding mocks:
jest.dontMock('../../src/stores/dragging-store.js');
jest.dontMock('../../src/dispatcher/app-dispatcher.js');
jest.dontMock('../../src/constants/actions.js');
The code itself runs smoothly in the browser after compiling, but the test engine gives the error.
I added this in my package.json:
"scripts": {
"test": "jest"
},
"jest": {
"scriptPreprocessor": "./node_modules/babel-jest",
"unmockedModulePathPatterns": [
"./node_modules/react"
],
"collectCoverage": true,
"testDirectoryName": "spec",
"moduleFileExtensions": [
"js"
],
"collectCoverageOnlyFrom": {
// All files to test
}
}
Does anyone have a clue how to get around the problem?
Thanks in advance...
Update: full source code can be found here: https://github.com/dejakob/unlease-chess
I realize this is very late, but, a lot has changed in the time you have posted this question. With Jest v19+, and assuming you are using the latest version of Babel as well, you can follow the instructions here:
http://facebook.github.io/jest/docs/webpack.html#using-with-webpack-2
since you are using modules, you will need to tell Babel to transpile them to commonjs requires so that they can be run in the node environment, which is how Jest works.

React.addons.TestUtils.renderIntoDocument always returns null

I am learning Jest & trying to integrated unit tests into my existing ES6 React application. For some reason, React.addons.TestUtils.renderIntoDocument is always returning null. Can anyone see what i am doing wrong?
Many thanks.
package.json
{
"name": "test.jest",
"version": "1.0.0",
"description": "",
"main": "index.js",
"scripts": {
"test": "jest"
},
"author": "",
"license": "ISC",
"devDependencies": {
"babel-jest": "^5.2.0",
"jest-cli": "^0.4.5"
},
"dependencies": {
"react": "^0.13.3"
},
"jest": {
"scriptPreprocessor": "<rootDir>/node_modules/babel-jest",
"unmockedModulePathPatterns": [
"<rootDir>/node_modules/react"
],
"testFileExtensions": [
"js",
"jsx"
],
"moduleFileExtensions": [
"js",
"jsx",
"json"
]
}
}
__tests__/foo-test.jsx
/* global describe, it, expect */
'use strict'
import React from 'react/addons'
const { addons: { TestUtils } } = React
describe('Foo', () => {
it('is a react element', () => {
let component = TestUtils.renderIntoDocument(
<div>foo</div>
)
expect(TestUtils.isElement(component)).toBeTruthy()
})
})
Results
$ npm test
> test.jest#1.0.0 test /home/markus/Desktop/test.jest
> jest
Using Jest CLI v0.4.5
FAIL src/__tests__/foo-test.jsx (1.287s)
● Foo › it is a react element
- Expected false to be truthy.
at Spec.<anonymous> (/home/markus/Desktop/test.jest/src/__tests__/foo-test.jsx:15:44)
at Timer.listOnTimeout [as ontimeout] (timers.js:112:15)
1 test failed, 0 tests passed (1 total)
Run time: 1.555s
npm ERR! Test failed. See above for more details.
Update
The ES6 example is also not working. Throws a ton of warnings before failing when it tries to read from a null value. The ES5 example, however, does work. Might be an upstream babel-jest problem?
Results
$ npm test
> # test /home/markus/Desktop/jest/examples/react-es6
> node ../../bin/jest.js
Using Jest CLI v0.4.5
FAIL __tests__/CheckboxWithLabel-test.js (1.697s)
Warning: getDOMNode(...) is deprecated in plain JavaScript React classes. Use React.findDOMNode(component) instead.
Warning: isMounted(...) is deprecated in plain JavaScript React classes. Instead, make sure to clean up subscriptions and pending requests in componentWillUnmount to prevent memory leaks.
Warning: replaceProps(...) is deprecated in plain JavaScript React classes. Instead, call React.render again at the top level.
Warning: replaceState(...) is deprecated in plain JavaScript React classes. Refactor your code to use setState instead (see https://github.com/facebook/react/issues/3236).
Warning: setProps(...) is deprecated in plain JavaScript React classes. Instead, call React.render again at the top level.
● CheckboxWithLabel › it changes the text after click
- TypeError: Cannot read property 'textContent' of null
at Spec.<anonymous> (/home/markus/Desktop/jest/examples/react-es6/__tests__/CheckboxWithLabel-test.js:19:24)
at jasmine.Block.execute (/home/markus/Desktop/jest/vendor/jasmine/jasmine-1.3.0.js:1065:17)
at jasmine.Queue.next_ (/home/markus/Desktop/jest/vendor/jasmine/jasmine-1.3.0.js:2098:31)
at null._onTimeout (/home/markus/Desktop/jest/vendor/jasmine/jasmine-1.3.0.js:2088:18)
at Timer.listOnTimeout [as ontimeout] (timers.js:112:15)
1 test failed, 0 tests passed (1 total)
Run time: 1.946s
npm ERR! Test failed. See above for more details.
Seems like "isElement" checks for a React component instance, not a rendered tree. See the example below:
import ReactDomTestUtils from 'react-dom/test-utils';
isDOMComponent:
const reactTree = ReactDomTestUtils.renderIntoDocument(<div />);
expect(ReactDomTestUtils.isDOMComponent(reactTree)).toBe(true);
expect(ReactDomTestUtils.isElement(reactTree)).toBe(false);
isElement:
const reactElement = <div />;
expect(ReactDomTestUtils.isDOMComponent(reactElement)).toBe(false);
expect(ReactDomTestUtils.isElement(reactElement)).toBe(true);
I am not sure, but i think it had something do do with my node.js version. Neither v0.12.4 nor iojs v2.2.1 was working. But node v0.10.38 seems to work. Here's hoping for an update :)

Resources