Skip to content
This repository has been archived by the owner on Mar 5, 2022. It is now read-only.

Commit

Permalink
Add example using react-app-rewired (#512)
Browse files Browse the repository at this point in the history
  • Loading branch information
bahmutov authored Oct 26, 2020
1 parent d65d2fb commit 283f8c4
Show file tree
Hide file tree
Showing 32 changed files with 644 additions and 0 deletions.
1 change: 1 addition & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -240,6 +240,7 @@ Folder Name | Description
[visual-testing-with-percy](examples/visual-testing-with-percy) | [Visual testing](#visual-testing) for components using 3rd party service [Percy.io](https://percy.io/)
[webpack-file](examples/webpack-file) | Load existing `webpack.config.js` file
[webpack-options](examples/webpack-options) | Using the default Webpack options from `@cypress/webpack-preprocessor` to transpile JSX specs
[rewired](examples/rewired) | Component tests for apps that use [react-app-rewired](https://github.com/timarney/react-app-rewired)
<!-- prettier-ignore-end -->

### External examples
Expand Down
22 changes: 22 additions & 0 deletions circle.yml
Original file line number Diff line number Diff line change
Expand Up @@ -140,6 +140,27 @@ workflows:
npm run only-covered
working_directory: examples/react-scripts

- cypress/run:
name: Example React App Rewired
requires:
- Component Tests
executor: cypress/base-12
install-command: |
ls -la ../..
echo ***Installing cypress-react-unit-test from root TGZ archive***
npm install -D ../../cypress-react-unit-test-0.0.0-development.tgz
echo ***Installing other dependencies***
npm install
echo ***rename root node_modules to avoid accidental dependencies***
mv ../../node_modules ../../no_modules
echo ***React version***
npm ls react react-dom
verify-command: echo 'Already verified'
no-workspace: true
working_directory: examples/rewired
command: npm test
store_artifacts: true

- cypress/run:
name: Example Next.js
requires:
Expand Down Expand Up @@ -506,6 +527,7 @@ workflows:
- Example Component Folder
- Example Cucumber
- Example React Scripts
- Example React App Rewired
- Example Sass
- Example Snapshots
- Example Tailwind
Expand Down
3 changes: 3 additions & 0 deletions examples/rewired/.env
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
# do not complain about babel-loader in this folder
# and in the root folder
SKIP_PREFLIGHT_CHECK=true
1 change: 1 addition & 0 deletions examples/rewired/.npmrc
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
package-lock=false
37 changes: 37 additions & 0 deletions examples/rewired/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
# example: react-app-rewired

Component tests for apps that use [react-app-rewired](https://github.com/timarney/react-app-rewired)

Main idea: find and load Webpack options using [find-webpack](https://github.com/bahmutov/find-webpack) module, then pass it through the [config-overrides.js](config-overrides.js) file. Then give it to the Cypress Webpack preprocessor. See [cypress/plugins/index.js](cypress/plugins/index.js) for details.

TODO: go through the rest of the spec files in [src](src) folder to make sure they work.
TODO: validate the code coverage

## Usage

1. Make sure the root project has been built .

```bash
# in the root of the project
npm install
npm run build
```

2. Run `npm install` in this folder to symlink the `cypress-react-unit-test` dependency.

```bash
# in this folder
npm install
```

3. Start Cypress

```bash
npm run cy:open
# or just run headless tests
npm test
```

## Debugging

You can see the Webpack config by running Cypress with `DEBUG=overrides` environment variable.
17 changes: 17 additions & 0 deletions examples/rewired/config-overrides.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
// to see these debug messages, run the program with
// DEBUG=overrides
const debug = require('debug')('overrides')

module.exports = function override(config, env) {
debug('in config overrides, env "%s"', env)
debug('config %o', config)

// throw an error to stop the app
// useful for debugging to avoid the dev server
// from hiding the console output
// throw new Error('stop here')

//do stuff with the webpack config...

return config
}
7 changes: 7 additions & 0 deletions examples/rewired/cypress.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
{
"testFiles": "*.spec.js",
"viewportWidth": 500,
"viewportHeight": 800,
"experimentalComponentTesting": true,
"componentFolder": "src"
}
5 changes: 5 additions & 0 deletions examples/rewired/cypress/fixtures/example.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
{
"name": "Using fixtures to represent data",
"email": "[email protected]",
"body": "Fixtures are a great way to mock data for responses to routes"
}
31 changes: 31 additions & 0 deletions examples/rewired/cypress/plugins/index.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
// load file preprocessor that comes with this plugin
// https://github.com/bahmutov/cypress-react-unit-test#install
const debug = require('debug')('overrides')
const fw = require('find-webpack')

const env = (process.env.NODE_ENV = process.env.NODE_ENV || 'development')

const webpackPreprocessor = require('@cypress/webpack-preprocessor')

module.exports = (on, config) => {
// require('@cypress/code-coverage/task')(on, config)

const foundWebpackOptions = fw.getWebpackOptions()
const configOverrides = require('react-app-rewired/config-overrides')
const webpackOptions = configOverrides.webpack(foundWebpackOptions, env)

debug('webpackOptions %o', webpackOptions)
fw.cleanForCypress(
{
reactScripts: true,
looseModules: true,
},
webpackOptions,
)

on('file:preprocessor', webpackPreprocessor({ webpackOptions }))

// IMPORTANT to return the config object
// with the any changed environment variables
return config
}
2 changes: 2 additions & 0 deletions examples/rewired/cypress/support/index.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
require('cypress-react-unit-test/support')
require('@cypress/code-coverage/support')
38 changes: 38 additions & 0 deletions examples/rewired/package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
{
"name": "rewired",
"version": "1.0.0",
"description": "Component testing for apps that use react-app-rewired",
"private": true,
"main": "index.js",
"scripts": {
"start": "react-app-rewired start",
"test": "cypress-expect run --passing 3"
},
"keywords": [],
"author": "",
"license": "ISC",
"devDependencies": {
"babel-loader": "8.1.0",
"cypress": "5.4.0",
"cypress-expect": "2.1.2",
"cypress-react-unit-test": "file:../..",
"debug": "4.2.0",
"react": "^16.13.1",
"react-app-rewired": "2.1.6",
"react-dom": "^16.13.1",
"react-scripts": "3.4.4",
"webpack": "4.44.2"
},
"browserslist": {
"production": [
">0.2%",
"not dead",
"not op_mini all"
],
"development": [
"last 1 chrome version",
"last 1 firefox version",
"last 1 safari version"
]
}
}
44 changes: 44 additions & 0 deletions examples/rewired/src/App.css
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
.App {
text-align: center;
}

.App-logo {
height: 40vmin;
pointer-events: none;
}

@media (prefers-reduced-motion: no-preference) {
.App-logo {
animation: App-logo-spin infinite 20s linear;
}
}

.App-header {
background-color: #282c34;
min-height: 100vh;
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
font-size: calc(10px + 2vmin);
color: white;
font-family: 'MyFont';
}

.App-link {
color: #61dafb;
}

@keyframes App-logo-spin {
from {
transform: rotate(0deg);
}
to {
transform: rotate(360deg);
}
}

@font-face {
font-family: 'MyFont';
src: local('MyFont'), url(./Inter-Regular.woff) format('woff');
}
46 changes: 46 additions & 0 deletions examples/rewired/src/App.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
import React from 'react'
// this CSS will be inlined ✅
import './App.css'
import logo from './logo.svg' // => "/__root/src/logo.svg"
import cypressLogo from './cypress-logo-dark.png' // => "/__root/src/cypress-logo-dark.png"
import { getRandomNumber } from './calc'
import Child from './Child'

// large image will be transformed into
// a different url static/media/vans.25e5784d.jpg
// import giantImage from './vans.jpg'

// we cannot load the image from direct url
// import giantImage from '/__root/src/vans.jpg'
// <img src={giantImage} alt="cypress-logo" />

function App() {
return (
<div className="App">
<img src={cypressLogo} className="Cypress-log" alt="Logo" />

<header className="App-header">
<img src={logo} className="App-logo" alt="logo" />
<p>
Edit <code>src/App.js</code> and save to reload.
</p>
<p className="random">This is a random number {getRandomNumber()}</p>
<Child />
<a
className="App-link"
href="https://reactjs.org"
target="_blank"
rel="noopener noreferrer"
>
Learn React
</a>
<p>
<code>.env</code> variable is{' '}
<span id="env-var">{process.env.REACT_APP_NOT_SECRET_CODE}</span>
</p>
</header>
</div>
)
}

export default App
34 changes: 34 additions & 0 deletions examples/rewired/src/App.spec.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
/// <reference types="cypress" />
import React from 'react'
import App from './App'
import { mount } from 'cypress-react-unit-test'
import * as calc from './calc'
import * as Child from './Child'

describe('App', () => {
it('renders learn react link', () => {
mount(<App />, { strict: true })
cy.contains(/Learn React/)
})

it('controls the random number by stubbing named import', () => {
// we are stubbing "getRandomNumber" exported by "calc.js"
// and imported into "App.js" and called.
cy.stub(calc, 'getRandomNumber').returns(777)
mount(<App />)
cy.contains('.random', '777')

// getRandomNumber was also used by the Child component
// let's check that it was mocked too
cy.contains('.child', 'Real child component, random 777')
})

it('can mock the child component', () => {
// Child component we want to stub is the default export
cy.stub(Child, 'default')
.as('child')
.returns(<div className="mock-child">Mock Child component</div>)
mount(<App />)
cy.contains('.mock-child', 'Mock Child')
})
})
10 changes: 10 additions & 0 deletions examples/rewired/src/Child.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
import React from 'react'
const calc = require('./calc')

const Child = () => (
<div className="child">
Real child component, random {calc.getRandomNumber()}
</div>
)

export default Child
Binary file added examples/rewired/src/Inter-Regular.woff
Binary file not shown.
12 changes: 12 additions & 0 deletions examples/rewired/src/Logo.cy-spec.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
/// <reference types="cypress" />
import React from 'react'
import { mount } from 'cypress-react-unit-test'
// import SVG as ReactComponent
import { ReactComponent as Logo } from './logo.svg'

describe('Logo', () => {
it('imports SVG', () => {
mount(<Logo />)
cy.get('path').should('have.attr', 'd')
})
})
44 changes: 44 additions & 0 deletions examples/rewired/src/RemotePizza.cy-spec.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
import React from 'react'
import RemotePizza from './RemotePizza'
import { mount } from 'cypress-react-unit-test'

const ingredients = ['bacon', 'tomato', 'mozzarella', 'pineapples']

describe('RemotePizza', () => {
it('download ingredients from internets (network mock)', () => {
cy.server()
cy.route('https://httpbin.org/anything*', { args: { ingredients } }).as(
'pizza',
)

mount(<RemotePizza />)
cy.contains('button', /cook/i).click()
cy.wait('@pizza') // make sure the network stub was used

for (const ingredient of ingredients) {
cy.contains(ingredient)
}
})

it('stubs via prop (di)', () => {
const fetchIngredients = cy.stub().resolves({ args: { ingredients } })
mount(<RemotePizza fetchIngredients={fetchIngredients} />)
cy.contains('button', /cook/i).click()

for (const ingredient of ingredients) {
cy.contains(ingredient)
}
})

it('mocks default props method', () => {
cy.stub(RemotePizza.defaultProps, 'fetchIngredients').resolves({
args: { ingredients },
})
mount(<RemotePizza />)
cy.contains('button', /cook/i).click()

for (const ingredient of ingredients) {
cy.contains(ingredient)
}
})
})
Loading

0 comments on commit 283f8c4

Please sign in to comment.