Reading in config data throws error in jest unit test - reactjs

I'm using react in conjunction Jest and enzyme. My unit tests were working as desired until I added a config.json that lives in src/config/config.js and it appears like so:
{
"appName": "Mystery",
"clients": {
"api1": "http://localhost:8082",
"api2": "http://localhost:8083",
"api3": "http://localhost:8084"
},
}
I import this file in my client folder that I use for network requests in this manner:
import CONFIG from '../../config/config.json';
const api = new Client(CONFIG.clients.api1);
Now when I run npm test I get this error on all my files that use that specified api:
TypeError: Cannot read property 'api1' of undefined
After some google foo.... I'm stymied. Any assistance would be greatly appreciated. Please let me know if additional details are required.

Related

TypeError: Cannot read property 'twoArgumentPooler' of undefined

Recently we have upgraded the react-native-web package to latest version 0.17.0 From that time we are getting the issue TypeError: Cannot read property 'twoArgumentPooler' of undefined while running yarn test
To analyse this issue, gone through the code which is implemented by our developers but we didn't have anything like twoArgumentPooler but it's available in react-native-web package in the path
at Object.<anonymous> (node_modules/react-native-web/dist/cjs/exports/Touchable/BoundingDimensions.js:19:46)
How to resolve this issue
Can you show your jest config file? I had a similar issue and it turned out that I was (manually) setting up the moduleNameMapper incorrectly. I had the following:
moduleNameMapper: {
'react-native': 'react-native-web',
},
which, upon running the tests, effectively invalidated an import on line 10 inside react-native-web/dist/exports/Touchable/BoundingDimensions.js (the file mentioned in your stacktrace) and surely a lot of other imports.
This
import PooledClass from '../../vendor/react-native/PooledClass';
var twoArgumentPooler = PooledClass.twoArgumentPooler;
turned into this (notice the changed and incorrect path)
import PooledClass from '../../vendor/react-native-web/PooledClass';
var twoArgumentPooler = PooledClass.twoArgumentPooler;
This ultimately resulted in the exact same error as you got, and was resolved by correctly defining the remapper entry like this:
moduleNameMapper: {
'^react-native$': 'react-native-web',
},
Hope it helps! If nothing else, perhaps this will help someone in the future!

ESLint: 'cy' is not defined (Cypress)

I've just started using Cypress with my React Typescript project. I've gotten some simple tests to run:
describe('settings page', () => {
beforeEach(() => {
cy.visit('http://localhost:3000')
});
it('starts in a waiting state, with no settings.', () => {
cy.contains('Waiting for settings...')
});
it('shows settings once settings are received', () => {
const state = cy.window().its('store').invoke('getState')
console.log(state) // different question: how do I get this to be the state and not a $Chainer?
});
});
It runs in Cypress just fine. But I get Typescript errors in Webstorm, saying that cy is not defined (a TS and ESlint error) and an error on describe saying all files must be modules when the --isolatedModules flag is provided.
I can make it a JS file instead of a TS file, then I still get cy is not defined.
I've tried import cy from 'cypress' but then I get ParseError: 'import' and 'export' may appear only with 'sourceType: module' which is a whole other can of worms (I'm taking baby steps in writing my tests and haven't had to import anything yet...)
/// <reference types="cypress" /> does not work.
Update (sort of)
I've followed instructions here and have made a little progress. To my already very full React webpack.config.dev.js I added the recommended code:
{ // TODO inserted for cypress https://stackoverflow.com/a/56693706/6826164
rules: [
{
test: /\.tsx?$/,
use: 'ts-loader',
exclude: /node_modules/
}
]
},
to the end of the list of rules (just before the file loader).
When I do this as well as setting up the plugins/index file as indicated in the article, the cypress "home screen" runs but when I click to open my tests, it takes very many seconds and then shows lots of errors, starting with
integration\settings.spec.ts
This occurred while Cypress was compiling and bundling your test code. This is usually caused by:
A missing file or dependency
A syntax error in the file or one of its dependencies
Fix the error in your code and re-run your tests.
./cypress/integration/settings.spec.ts
Module build failed (from ./node_modules/ts-loader/index.js):
Error: TypeScript emitted no output for C:\Users\...\...\front_end\cypress\integration\settings.spec.ts.
# multi ./cypress/integration/settings.spec.ts main[0]
Followed by, actually, a lot of Typescript output such as this:
C:\Users\jtuzman\dev\...\...\src\__tests__\Errors.test.tsx
[tsl] ERROR in C:\Users\jtuzman\dev\...\...\src\__tests__\Errors.test.tsx(37,41)
TS2339: Property 'toBeTruthy' does not exist on type 'Assertion'.
C:\Users\jtuzman\dev\...\...\src\__tests__\Errors.test.tsx
[tsl] ERROR in C:\Users\jtuzman\dev\...\...\src\__tests__\Errors.test.tsx(41,45)
TS2339: Property 'toBeDefined' does not exist on type 'Assertion'.
Notice that these are now errors for code outside the test files (although perhaps that makes sense). Many of them are for files in which I'm using Jest rather than Cypress, and many errors, as you can see, seem to be related to it inferring an Assertion type on expect that is not Jest, such that it thinks the toEqual matcher is wrong.
All the while, in Webstorm ESLint is still complaining about all my cy and TypeScript is underlining all those Jest assertions mentioned in the output.
This is all with a ts test file. If I rename the file to js, it says the file has no tests.
Any help? I love Cypress but I'm having a hell of a time getting it to work fully!
I got that error after upgrading to cypress version 4+. I installed the eslint-plugin-cypress
https://github.com/cypress-io/eslint-plugin-cypress
and activated it in the extends configuration either in package.json or in separate config file:
"eslintConfig": {
"extends": [
"plugin:cypress/recommended"
]
},
Add .eslintrc.json to cypress directory
In .eslintrc.json
{
"extends": [
"plugin:cypress/recommended"
]
}
I do not install eslint-plugin-cypress, and it fix the problem
Specify cy in eslintrc globals
Answered here
cy is a global variable. Much like location. So really it is window.cy. You can add it to the globals in Eslint. Don't import cy from cypress.
{
"globals": {
"cy": true
}
}
Added that to my .eslintrc and fixed the issue
The Cypress ESLint plugin will get rid of these warnings:
yarn add -D eslint-plugin-cypress (https://github.com/cypress-io/eslint-plugin-cypress)
add .eslintrc to the root of your project with the following:
{
"plugins": ["cypress"],
"extends": ["plugin:cypress/recommended"],
"rules": {
"jest/expect-expect": "off"
}
}
Try.. import cy from "cypress" this solved the problem for me.
at the top of your file put
/// <reference types="cypress" />
or download the official types
source: official cypress intellisense docs
I struggled a lot then this helped...
by adding same line in two files, eslintrc.json and eslintrc.js
(if u have other dependencies in extends, append them as well after it)
extends: ['plugin:cypress/recommended'],
Just add these lines to your tsconfig.json file for e2e tests:
"compilerOptions": {
"types": ["cypress"]
}
This adds support for cypress types.
/* global cy */
import above in your test file
example:
suppose you have login test ("cypress test file ex: cypress/integration/login.js")
I replaced the old style of type referencing,
/// <reference types="cypress" />
with this silly import
import type {} from 'cypress';
And the IDE now both recognizes Cypress's globals while also avoiding the "isolatedModules" issue it has with tsconfig.json
Seems I found a remedy that works (at least) for me. Adding this import to the top of the test:
import _Cypress from "cypress";
relaxes and comforts the ESLint plugin. Actually any name for the import can be used instead of "_Cypress": any that conforms your sense of beauty, does not conflict with anything and starts with underscore (to not provoke ESLint again). Of course, it looks like a kind of voodoo. I don't know why it works and probably there are better ways to present ESLint Cypress's globals, but I don't know them.
add this to jest.config.js
testPathIgnorePatterns: [
'/cypress',
],
Wrap your config object with defineConfig in the cypress.confi.ts file
like so
import { defineConfig } from "cypress";
export default defineConfig({
e2e: {
setupNodeEvents(on, config) {
// implement node event listeners here
return config;
},
},
component: {
devServer: {
framework: "create-react-app",
bundler: "webpack",
},
},
});
For me adding .eslintignore in root directory and placing *.cy.js for all my test files was only workaround.
It seems that for the rest of us the working solution really is installing eslint-plugin-cypress and adding:
"eslintConfig": {
"extends": [
"plugin:cypress/recommended"
]
},
but idt didn't helped in my case because this plugin is no longer supported (almost for a year now) so it ended with critical error when combined with cypress-axe.

CORS error - Error: Cross origin http://localhost forbidden - in ReactJS/Jest test only

I'm running into a problem where the request I am making to an outside API is working fine in execution, but when runing a Jest/Enzyme test it gives me a CORS error. The function in question is using a JsonRpc implementation from an API, and using fetch from node-fetch. Not sure if there are settings for CORS I can apply somewhere?
I tried many variations of async waits in Jest/Enzyme testing framework but am still running into issues.
test("it should do something", done => {
const component = shallow(<CustomComponent />)
component.instance().customAsyncFunction( result => {
expect(result.length).toEqual(5)
done()
})
// return component.instance().customAsyncFunction().then(data => {
// expect(data.length).toEqual(5)
// })
})
I tried the above and a few other methods (like setTimeout and awaiting it) and get the CORS error.
The results I'm getting are:
console.error
node_modules/jest-environment-jsdom/node_modules/jsdom/lib/jsdom/virtual-console.js:29
Error: Cross origin http://localhost forbidden
at dispatchError (...\node_modules\jest-environment-jsdom\node_modules\jsdom\lib\jsdom\living\xhr-utils.js:65:19)
at Request.client.on.res (...\node_modules\jest-environment-jsdom\node_modules\jsdom\lib\jsdom\living\xmlhttprequest.js:679:38)
at Request.emit (events.js:198:13)
at Request.onRequestResponse (...\node_modules\request\request.js:1066:10)
at ClientRequest.emit (events.js:203:15)
at HTTPParser.parserOnIncomingClient [as onIncoming] (_http_client.js:556:21)
at HTTPParser.parserOnHeadersComplete (_http_common.js:109:17)
at TLSSocket.socketOnData (_http_client.js:442:20) undefined
Any ideas?
Jest allows you to set a setup script file. This file will be required before everything else and gives you a chance to modify the environment in which the tests will run. This way you can unset XMLHttpRequest before axios is loaded and the adapter type evaluated since imports are hoisted.
Link:https://facebook.github.io/jest/docs/configuration.html#setuptestframeworkscriptfile-string
In package.json
{
...,
"jest": {
...,
"setupTestFrameworkScriptFile": "./__tests__/setup.js",
...
},
...
}
__tests__/setup.js
global.XMLHttpRequest = undefined;
The error happens because the external API you call during the tests has a CORS restriction different than Jest's default jsdom one has (http://localhost).
To fix this for Jest v24 - v27 you have to set the appropriate testURL (see the docs) in your Jest config (e.g. jest.config.js):
{
// Other config values here ...
"testURL": "http://localhost:8080"
}
In testURL you have to put the actual URL allowed by CORS at your remote API side.
NB: since v28 the option is organized differentlty that requires different update in Jest config.
Assuming you actually want to call the real API during your tests rather than just mock it, you'll need to make sure the server sends an appropriate "access-control-allow-origin" header (obviously the exact mechanism for doing this depends on your server platform)
1) what you're probably looking for is to instead mock the promise or whatever function is being ran using jest.mock(), then assert that that mock was called (with the correct params)
Jest tests are unit tests that shouldn't really talk to your API
2) most likely something with your env vars while in test mode, process.env.NODE_ENV is set to "test" during jest which might be changing something or maybe one of your own custom config env vars
By settting a testURL option which is a valid URL you want in jest config will resovle this problem.
https://jestjs.io/docs/configuration#testurl-string

NextJS & CSS SyntaxError: Unexpected token

I'm trying to unit test but the only way I can stop the error throwing is to comment out the import './styles.css line.
As soon as I put it back in I get:
Jest encountered an unexpected token
...
SyntaxError: Unexpected token.
1 | import React from 'react';
2 | import PropTypes from 'prop-types';
> 3 | import './styles.css';
| ^
4 |
5 |
I have webpack, babel, jest, enzyme all configured but googling tells me there's a difference between running the app (via webpack) and using .css files vs running tests that can read .css files, which would need to be configured separately.
For love nor money, I cannot find an example where import './styles.css is successfully imported & the tests pass.
Can anyone help?
Managed to get this working by hitting up https://github.com/eddyerburgh/jest-transform-stub
My jest.config.js now looks like this:
module.exports = {
setupFiles: ['<rootDir>/jest.setup.js'], // links to normal "configure({ adapter: new Adapter() })" stuff.
testPathIgnorePatterns: ['<rootDir>/.next/', '<rootDir>/node_modules/'], // ignores test files in .next(js) & node modules
transform: {
"^.+\\.js$": "babel-jest", // anything .js is babel'd for jest to consume
"^.+.(css|styl|less|sass|scss|png|jpg|ttf|woff|woff2)$": "jest-transform-stub" // anything style related is ignored and mapped to jest-transform-stub module
},
moduleNameMapper: {
'\\.css$': '<rootDir>/EmptyModule.js' // <-- had to pop this in the following day to get any separetly imported .css files mapped to an empty module / object. So if i try to do import 'react-style-module/styles/my-styles.css' it would fail, though 'import './styles.css' worked. Now with this mapped correctly also, everything is imported/mapped/passing successfully.
}
}
If anyone else has other neater solutions, please let me know. x
In your package.json file set 'type' property to 'module'
{
"type":"module"
}
I think you declare like this:
import css from './styles.css'
<div className={css.test}>
</div>
Reference: https://github.com/zeit/next-plugins/tree/master/packages/next-css

Importing self-created libraries in reactjs

I'm using React and ES6 using babel and webpack. I am very new to this ecosystem.
I am trying to import some common utility functions into my jsx file but react is unable to find the file
homepage.jsx
var pathToRoot = './../..';
import path from 'path';
import React from 'react';
import ReactDOM from 'react-dom';
var nextWrappedIndex = require(path.join(pathToRoot,'/lib/utils.js')).nextWrappedIndex;
//some react/JSX code
utils.js
var nextWrappedIndex = function(dataArray) {
//some plain js code
return newIndex;
}
exports.nextWrappedIndex = nextWrappedIndex;
Directory structure is as follows:
src
|--app.js
|--components
| |--homepage
| |--homepage.jsx
|
|--lib
| |--utils.js
I am on a windows 10 machine and was facing issues during compilation providing the path by any other means. Using path.join solved compilation issue but the browser while rendering throws this error
Uncaught Error: Cannot find module '../../lib/utils.js'.
How do I accomplish this?
Also, is this the best way to do it(if altogether it is way it is supposed to be done in such ecosystem)?
One of the best and easiest way I have found in such a setup is to use Webpack aliases.
Webpack aliases will simply associate an absolute path to a name that you can use to import the aliased module from anywhere. No need to count "../" anymore.
How to create an alias?
Let's imagine that your Webpack config is in the parent folder of your src folder.
You would add the following resolve section in your config.
const SRC_FOLDER = path.join(__dirname, 'src')
resolve: {
alias: {
'my-utils': path.join(SRC_FOLDER, 'lib', 'utils')
}
}
Now, anywhere in your app, in any of your modules or React component you can do the following:
import utils from 'my-utils'
class MyComponent extends React.component {
render () {
utils.doSomething()
}
}
Small note about this method. If you run unit tests with a tool like enzyme and you don't run the component tested through Webpack, you will need to use the babel-plugin-webpack-alias.
More info on Webpack website: Webpack aliases
I solved this by replacing
var nextWrappedIndex = require(path.join(pathToRoot,'/lib/utils.js')).nextWrappedIndex;
with
import nextWrappedIndex from './../../lib/utils.js';
I tried to reproduce your code and Webpack printed me the following error:
WARNING in ./app/components/homepage/homepage.jsx
Critical dependencies:
50:0-45 the request of a dependency is an expression
# ./app/components/homepage/homepage.jsx 50:0-45
It means that Webpack couldn't recognize your require() expression because it works only with static paths. So, it discourages the way you are doing.
If you would like to avoid long relative paths in your import, I'd recommend you to set up Webpack.
First, you can set up aliases per Amida's answer.
Also, you can set up an extra module root via resolve.modules to make webpack look into your src folder, when you are importing something absolute, like lib/utils.js

Resources