React Jest --coverage ignore - reactjs

My folder/ project structure for reusable components (i.e. field types) looks like this:
/fields
/fields/text
/fields/radio
/fields/checkbox
/fields/...
/fields/index.jsx (which only reexports each of above for simpler importing purposes)
now when i run jet --coverage i get index.jsx which is just a import/export proxy included into coverage report, is there a way to configure jest runner to ignore "such" files ?
Thanks

You could add the filename to coveragePathIgnorePatterns list of your jest settings
coveragePathIgnorePatterns: ['/fields/index.jsx'],

There are two ways I found:
First (already mentioned by #andreas-köberle) is ignoring files using config or CLI instruction.
Another one is writing some dummy tests:
import {Stuff} from '../index';
describe('check', () => {
it('reexports', () => {
expect(Stuff).toBeTruthy();
});
})
I found this one preferable. In case you write some logic in your reexports-file it reminds you to cover it with tests.

Related

react jest snapshot testing - change directory structure

My current structure looks like this
- Button
- Button.tsx
- Button.test.tsx
When I add a snapshot test, by default it creates a __snapshot__directory like so
- Button
- __snapshots__
- Button.test.tsx.snap
- Button.tsx
- Button.test.tsx
I would like the snapshot file to be colocated next to the other button files like so
- Button
- Button.tsx
- Button.test.tsx
- Button.test.tsx.snap
I have read about a new jest config snapshotResolver but I can't get it working with my react app (which was created using CRA).
If I try and add this to the package.json file, react tells me it only supports a number of jest options, snapshotResolver is not one of them.
I've tried following examples online but I can't get it to work
I have created a jest.config.js in the root of my project which contains
module.exports = {
snapshotResolver: './snapshotResolver.js',
};
And also created snapshotResolver.js also at the project root (this is taken from a github issue I believe)
// https://jestjs.io/docs/en/configuration.html#snapshotresolver-string
module.exports = {
testPathForConsistencyCheck: 'some/example.test.js',
resolveSnapshotPath: (testPath, snapshotExtension) =>
testPath.replace(/\.test\.([tj]sx?)/, `${snapshotExtension}.$1`),
resolveTestPath: (snapshotFilePath, snapshotExtension) =>
snapshotFilePath.replace(snapshotExtension, '.test'),
};
2 things. Firstly, have I setup the jest config and resolver correctly? Secondly, because I am using CRA, does it ignore the jest.config and use whatever defaults it has?
Edit
I found this PR that has been opened since August 2019, https://github.com/facebook/create-react-app/issues/6532
As you have already discovered, create-react-app does not support the snapshotResolver config option.
So, unless you eject your project, you will not be able to tweak where the snapshots are stored.

Shared component library best practices

I am creating a shareable React component library.
The library contains many components but the end user may only need to use a few of them.
When you bundle code with Webpack (or Parcel or Rollup) it creates one single file with all the code.
For performance reasons I do not want to all that code to be downloaded by the browser unless it is actually used.
Am I right in thinking that I should not bundle the components? Should the bundling be left to the consumer of the components?
Do I leave anything else to the consumer of the components? Do I just transpile the JSX and that's it?
If the same repo contains lots of different components, what should be in main.js?
This is an extremely long answer because this question deserves an extremely long and detailed answer as the "best practice" way is more complicated than just a few-line response.
I've maintained our in-house libraries for 3.5+ years in that time I've settled on two ways I think libraries should be bundled the trade-offs depend on how big your library is and personally we compile both ways to please both subsets of consumers.
Method 1: Create an index.ts file with everything you want to be exposed exported and target rollup at this file as its input. Bundle your entire library into a single index.js file and index.css file; With external dependencies inherited from the consumer project to avoid duplication of library code.
(gist included at bottom of example config)
Pros: Easy to consume as project consumers can import everything from the root relative library path import { Foo, Bar } from "library"
Cons: This will never be tree shakable, and before people say to do this with ESM and it will be tree shakeable. NextJS doesn't support ESM at this current stage and neither do a lot of project setups that's why it's still a good idea to compile this build to just CJS. If someone imports 1 of your components they will get all the CSS and all the javascript for all your components.
Method 2: This is for advanced users: Create a new file for every export and use rollup-plugin-multi-input with the option "preserveModules: true" depending on how what CSS system you're using your also need to make sure that your CSS is NOT merged into a single file but that each CSS file requires(".css") statement is left inside the output file after rollup and that CSS file exists.
Pros: When users import { Foo } from "library/dist/foo" they will
only get the code for Foo, and the CSS for Foo, and nothing more.
Cons: This setup involves the consumer having to handle node_modules
require(".css") statements in their build configuration with NextJS
this is done with next-transpile-modules npm package.
Caveat: We use our own babel plugin you can find it here: https://www.npmjs.com/package/babel-plugin-qubic to allow people to import { Foo, Bar } from "library" and then with babel transform it to...
import { Foo } from "library/dist/export/foo"
import { Bar } from "library/dist/export/bar"
We have multiple rollup configurations where we actually use both methods; so for library consumers who don't care for tree shaking can just do "Foo from "library" and import the single CSS file, and for library consumers who do care for tree shaking and only using critical CSS they can just turn on our babel plugin.
Rollup guide for best practice:
whether you are using typescript or not ALWAYS build with "rollup-plugin-babel": "5.0.0-alpha.1"
Make sure your .babelrc looks like this.
{
"presets": [
["#babel/preset-env", {
"targets": {"chrome": "58", "ie": "11"},
"useBuiltIns": false
}],
"#babel/preset-react",
"#babel/preset-typescript"
],
"plugins": [
["#babel/plugin-transform-runtime", {
"absoluteRuntime": false,
"corejs": false,
"helpers": true,
"regenerator": true,
"useESModules": false,
"version": "^7.8.3"
}],
"#babel/plugin-proposal-class-properties",
"#babel/plugin-transform-classes",
["#babel/plugin-proposal-optional-chaining", {
"loose": true
}]
]
}
And with the babel plugin in rollup looking like this...
babel({
babelHelpers: "runtime",
extensions,
include: ["src/**/*"],
exclude: "node_modules/**",
babelrc: true
}),
And your package.json looking ATLEAST like this:
"dependencies": {
"#babel/runtime": "^7.8.3",
"react": "^16.10.2",
"react-dom": "^16.10.2",
"regenerator-runtime": "^0.13.3"
},
"peerDependencies": {
"react": "^16.12.0",
"react-dom": "^16.12.0",
}
And finally your externals in rollup looking ATLEAST like this.
const makeExternalPredicate = externalArr => {
if (externalArr.length === 0) return () => false;
return id => new RegExp(`^(${externalArr.join('|')})($|/)`).test(id);
};
//... rest of rollup config above external.
external: makeExternalPredicate(Object.keys(pkg.peerDependencies || {}).concat(Object.keys(pkg.dependencies || {}))),
// rest of rollup config below external.
Why?
This will bundle your shit to automatically to inherit
react/react-dom and your other peer/external dependencies from the
consumer project meaning they won't be duplicated in your bundle.
This will bundle to ES5
This will automatically require("..") in all the babel helper functions for objectSpread, classes, etc FROM the consumer project which will wipe another 15-25KB from your bundle size and mean that the helper functions for objectSpread won't be duplicated in your library output + the consuming projects bundled output.
Async functions will still work
externals will match anything that starts with that peer-dependency suffix i.e babel-helpers will match external for babel-helpers/helpers/object-spread
Finally here is a gist for an example single index.js file output rollup config file.
https://gist.github.com/ShanonJackson/deb65ebf5b2094b3eac6141b9c25a0e3
Where the target src/export/index.ts looks like this...
export { Button } from "../components/Button/Button";
export * from "../components/Button/Button.styles";
export { Checkbox } from "../components/Checkbox/Checkbox";
export * from "../components/Checkbox/Checkbox.styles";
export { DatePicker } from "../components/DateTimePicker/DatePicker/DatePicker";
export { TimePicker } from "../components/DateTimePicker/TimePicker/TimePicker";
export { DayPicker } from "../components/DayPicker/DayPicker";
// etc etc etc
Let me know if you experience any problems with babel, rollup, or have any questions about bundling/libraries.
When you bundle code with Webpack (or Parcel or Rollup) it creates one single file with all the code.
For performance reasons I do not want to all that code to be downloaded by the browser unless it is actually used
It's possible to have separate files generated for each component. Webpack has such ability by defining multiple entries and outputs. Let's say you have the following structure of a project
- my-cool-react-components
- src // Folder contains all source code
- index.js
- componentA.js
- componentB.js
- ...
- lib // Folder is generated when build
- index.js // Contains components all together
- componentA.js
- componentB.js
- ...
Webpack file would look something like this
const path = require('path');
module.exports = {
entry: {
index: './src/index.js',
componentA: './src/componentA.js',
componentB: './src/componentB.js',
},
output: {
filename: '[name].js',
path: path.resolve(__dirname, 'lib'),
},
};
More info on "code splitting" is here in Webpack docs
If the same repo contains lots of different components, what should be in main.js?
There is a single field in package.json file named main, it's good to put its value lib/index.js according to the project structure above. And in index.js file have all components exported. In case consumer wants to use single component it's reachable by simply doing
const componentX = require('my-cool-react-components/lib/componentX');
Am I right in thinking that I should not bundle the components? Should the bundling be left to the consumer of the components? Do I leave anything else to the consumer of the components? Do I just transpile the JSX and that's it?
Well, it's up to you. I've found that some React libraries are published in original way, others - are in bundled way. If you need some build process, then define it and export bundled version.
Hope, all your questions are answered :)
You can split your components like lodash is doing for their methods.
What you probably have is separate components that you could allow importing separately or through the main component.
Then the consumer could import the whole package
import {MyComponent} from 'my-components';
or its individual parts
import MyComponent from 'my-components/my-component';
Consumers will create their own bundles based on the components they import. That should prevent your whole bundle being downloaded.
You should take a look at Bit, I think this is a good solution to share, reuse and visualize components.
It is very easy to setup. You can install your bit library or just a component with:
npm i #bit/bit.your-library.components.buttons
Then you can import the component in your app with:
import Button3 from '#bit/bit.your-library.components.buttons';
The good part is that you don't have to worry about configuring Webpack and all that jazz. Bit even supports the versioning of your components. This example shows a title-list react component so you can take a look if this meets your requirements or not
There is a configuration in webpack to create chunk files. To start with it will create the main bundle into multiple chunks and get it loaded as when required. if your project has well structured modules, it will not load any code which is not required.

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.

Is it good to use package.json file in react component file?

I have to use version mentioned in package.json file in front-end(react js) file.
{
"name": "asdfg",
"version": "3.5.2", // want to use this
"description": "description",
"scripts": {}
//etc etc etc
......
}
Send package.json [version] to Angularjs Front end for display purposes
I'd gone through above post and found two ways for the same. but none of them I was asked to implement.
#1. During build process
#2. By creating endpoint
So I want to know the approach below is valid/good or not ?
react-front-end-file.js
import packageJson from '../package.json'; // imported
...
...
// Usage which gives me version - 3.5.2
<div className='app-version'>{packageJson.version}</div>
Let me know if this approach is fine.
The below 2 approaches seems to have either dependency or add an extra implentation which might not be needed
During build process - ( has dependency on module bundler like webpack etc.)
By creating endpoint - ( needs an extra code at server just to get version )
Instead, As package.json is a file which takes json object in it so you can use it to import json and use its any keys mentioned in that file ( version in your case but only constraint here is, you should have access to package.json file after application deployment, so dont forget to move file in deployment environment )
So your approach seems to be fine.
I would do something like this:
In your module bundler, require your package json file and define a global variable and use it wherever you want
e.g. I do something like this in webpack:
const packageJson = require('./package.json')
const plugins = [
new webpack.DefinePlugin(
{
'__APPVERSION__': JSON.stringify(packageJson.version)
}
)
]
React Component:
<div className='app-version'>{__APPVERSION__}</div>

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