bahmutov / cra-ts-code-coverage-example

React App with TypeScript and Cypress code coverage

Geek Repo:Geek Repo

Github PK Tool:Github PK Tool

Example for react-app-rewired

StallionV opened this issue · comments

We created our App using CRA V3 and use Ant design along with it.
Ant design needs a custom version react-app-rewired to run on top of react-scripts.

We did not have Typescript to start with but added later on.

Cypress runs just fine but the code coverage won't run.
Our current start script looks like
"start": "PORT=7000 react-app-rewired start"
and when we change it to
"start": "PORT=7000 react-scripts -r @cypress/instrument-cra start" it won't work

Can you share an example of

  • CRA
  • Using react-app-rewired
  • With Typescript installed

I have forked the repo to https://github.com/bahmutov/cypress-cra-ts-antd-react-app-rewired and added https://github.com/cypress-io/instrument-cra

Starting the app with "start": "PORT=7000 react-app-rewired -r @cypress/instrument-cra start" I see the following

Screen Shot 2020-06-01 at 8 23 52 PM

  • there is window.__coverage__ object, so instrumentation and coverage is there
  • the Layout that comes from antd is missing - is this the error you are seeing? @StallionV

Hmm, I tried an alternative approach - add this plugin via config-overrides.js

const { override, fixBabelImports, addLessLoader, addBabelPlugin } = require('customize-cra')
module.exports = override(
  addBabelPlugin('babel-plugin-istanbul'),
...

But I get the same error - something goes terribly wrong during instrumentation, hmm

Looking at the code.

1st workaround - use require like this

import React from 'react'
// import { Layout, Menu, Breadcrumb } from 'antd'
import './App.less'
const { Layout, Menu, Breadcrumb } = require('antd')
const { Header, Content, Footer } = Layout

Screen Shot 2020-06-01 at 8 41 27 PM

2nd workaround - following umijs/babel-plugin-import#204

import { Layout, Menu, Breadcrumb } from 'antd/lib'

also works

For both, instrument the app either using react-app-rewired -r @cypress/instrument-cra start or config-overrides.js

const { override, fixBabelImports, addLessLoader, addBabelPlugin } = require('customize-cra')
module.exports = override(
  addBabelPlugin('babel-plugin-istanbul'),
  ...

I prefer using addBabelPlugin method (and just adding babel-plugin-istanbul dependency)

Yes this is the same error. Basically react-app-rewired was added to customize web pack.
Reference Ant design v3: https://3x.ant.design/docs/react/use-with-create-react-app#Advanced-Guides
Interestingly they switched to Craco recently with v4: https://ant.design/docs/react/use-with-create-react-app#Advanced-Guides

But yes it is complaining that it is not able to get any Ant modules and the app crashes.
If you revert the line it goes back to working fine

2nd workaround - following ant-design/babel-plugin-import#204

also works

For both, instrument the app either using react-app-rewired -r @cypress/instrument-cra start or config-overrides.js

const { override, fixBabelImports, addLessLoader, addBabelPlugin } = require('customize-cra')
module.exports = override(
  addBabelPlugin('babel-plugin-istanbul'),
  ...

I prefer using addBabelPlugin method (and just adding babel-plugin-istanbul dependency)

Thanks let me give this a shot.
The problem with this is changes all over the app, since I have a huge app.
Any other way instead of the require or changing ant to and/lib

Also do i then still follow the same steps as in https://github.com/bahmutov/cra-ts-code-coverage-example right?

Thanks I will give it a shot and update here. Very much appreciate your quick input.
The code coverage videos were great as well.
You guys are awesome !!

Thanks @bahmutov so I did get it to work using the 2nd approach but am confused about the code coverage
Screen Shot 2020-06-01 at 9 07 26 PM
Here it is giving me 80% coverage stating 4 of the 5 lines are covered even though the whole JSX element in return is not covered. Can you explain ?

I have recorded this short video explaining the above report https://youtu.be/yVvCYtsmkZU

Thanks Gleb , a follow up question which I also left in the comments in the video link.
I totally get what you said. But then isn't the coverage kind of superficial? I know it cannot be covered completely due to code transpilers. I would also like to test the fact that e.g. from the JSX I expect 3 menu items, and if one is missing it is a broken test case. I know I can do this using Cypress, but the fact that it will have 0 impact on my code coverage gives an uneasy feeling. How to we cover this gray area? Unit test ? I really believe Cypress can be a one wholesome solution for testing but would like to cover these real world scenarios as well.

Yeah, not sure about changing the imports all over the place, understand this is a hassle, but who knows why these things don't play nicely together. After that you should just use the @cypress/code-coverage plugin to report results I think Gleb

I did end up doing a replace all from 'ant' to from 'ant/lib' and it was not that bad. The reports do show up for the complete app. Just wanted to confirm that this really worked.

:) I don't think you want to cover every line of JSX markup. The important thing is what the users gets. Let's say the most important part for you is that there are 3 items, right. Then you write an assertion to confirm it

import App from './App'
mount(<App />) // or cy.visit('/')
// assuming MenuItem renders something with class="menu-item"
cy.get('.menu-item').should('have.length', 3)

Ok, but maybe you are not sure or had regression about the text in the middle item

import App from './App'
mount(<App />)
// assuming MenuItem renders something with class="menu-item"
cy.get('.menu-item').eq(1).should('contain', 'nav 2')
// or any item contains "nav 2"
cy.contains('.menu-item', 'nav 2')

going even further, maybe you want to confirm the markup produced, then you can bring a snapshot plugin from https://on.cypress.io/plugins and confirm entire HTML

import App from './App'
mount(<App />)
cy.get('.layout').invoke('html').toMatchSnapshot()

I don't like HTML snapshots - they are brittle and had to understand. Instead, I recommend setting up a visual testing https://on.cypress.io/visual-testing plugin. Then you can confirm the entire application works and looks as expected

cy.visit('/')
// confirm the page has rendered
cy.get('.menu-item').should('have.length', 3)
cy.get('.layout').visualSnapshot()

You can find multiple examples of image snapshot testing in our visual testing guide, and even see long list of videos using the open-source plugin at https://github.com/bahmutov/sudoku

Functional tests plus visual snapshots should give you full confidence in the application working. Code coverage is more like a map of remaining features to test, as I talk about in https://www.youtube.com/watch?v=JL3QKQO80fs presentation

Thanks that is pretty awesome. Will explore it, that may help bridge the gap !!

ok, seems to be resolved

@bahmutov I'm having a problem with Cypress code coverage. My application has react code in different modules but Cypress is only installed in one of them. When executing Cypress with code coverage, only the react scripts located in the module where Cypress is installed are instrumented and appear in the report. Is there a way to instrument and collect coverage of every module?