Skip to content

Commit 0348170

Browse files
lmiller1990Barthélémy Ledoux
and
Barthélémy Ledoux
authored
fix(cypress/react): add default webpack config path (#16813)
Co-authored-by: Barthélémy Ledoux <bart@cypress.io>
1 parent 98fe58c commit 0348170

28 files changed

+402
-1
lines changed

npm/react/examples.env

+1
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
# /examples/nextjs-webpack-5
44
/examples/react-scripts
55
/examples/react-scripts-typescript
6+
/examples/find-webpack
67
/examples/webpack-file
78
/examples/react-scripts-folder
89
/examples/using-babel-typescript
+103
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,103 @@
1+
As of Cypress 7.0, the new Component Testing runner is now bundled with the Cypress! It takes inspiration and builds on the learnings from the original Component Testing implementation, which was hidden behind the `experimentalComponentTesting` flag.
2+
3+
In this blog post we will see how to set up Cypress Component Testing in a new React app created via Create React App using TypeScript.
4+
5+
You can get the source code for the example used in the blog post [here](https://github.com/lmiller1990/cypress-react-template).
6+
7+
## Creating a new React Project
8+
9+
Create a new React project to get started. Optionally add TypeScript - I'll be using it in this example.
10+
11+
```sh
12+
yarn create react-app cypress-test-react --template typescript
13+
```
14+
15+
## Configuring Cypress Component Testing
16+
17+
Once you've got a React project, you'll also need to install Cypress and the Webpack Dev Server and React adapters.
18+
19+
Create React App projects are Webpack based; that's why we are installing the relevant Webpack adapter. You also need `@cypress/react`, which is the primary way to mount and interact with components (similar to `mount` in Enzyme or `render` in Testing Library).
20+
21+
```sh
22+
yarn add cypress @cypress/react @cypress/webpack-dev-server --dev
23+
```
24+
25+
Next, create a `cypress.json` with some basic configuration:
26+
27+
```json
28+
{
29+
"component": {
30+
"testFiles": "**/*.test.{js,ts,jsx,tsx}",
31+
"componentFolder": "src"
32+
}
33+
}
34+
```
35+
36+
Here we are adding some Component Testing specific options, hence the `"component"` key. `"componentFolder"` is where all the components and tests are located, and `"testFiles"` is the pattern to search for test files.
37+
38+
The last thing we need to is tell Cypress to use `@cypress/webpack-dev-server` for component tests. Plugins are explained in detail in the [Cypress documentation](https://docs.cypress.io/guides/tooling/plugins-guide#Installing-plugins). By default plugins are loaded from `cypress/plugins/index.js`. Create that file and add:
39+
40+
```js
41+
const injectDevServer = require("@cypress/react/plugins/react-scripts")
42+
43+
module.exports = (on, config) => {
44+
injectDevServer(on, config)
45+
return config
46+
}
47+
```
48+
49+
50+
This will configure the Cypress Webpack Dev Server to use the same Webpack configuration as Create React App uses.
51+
52+
If you are using a different template, like Next.js, we have some other [adapters available](https://github.com/cypress-io/cypress/tree/develop/npm/react/plugins). It's also possible to create your own adapter.
53+
54+
## Writing Some Tests
55+
56+
Let's migrate `src/App.test.tsx`, which comes with the Create React App template, to use Cypress. It's a simple migration:
57+
58+
```tsx
59+
import React from 'react';
60+
import { mount } from '@cypress/react';
61+
import App from './App';
62+
63+
it('renders learn react link', () => {
64+
mount(<App />);
65+
cy.get('a').contains('Learn React');
66+
});
67+
```
68+
69+
Most tests will start with `mount` from `@cypress/react`. This is similar to `render` in Testing Library. Once you've mounted your component, you can use Cypress' extensive [query and assertion APIs](https://docs.cypress.io/api/table-of-contents) to ensure everything behaves correctly. This example asserts an anchor tag with the text "Learn React" is rendered.
70+
71+
Open the component testing runner with:
72+
73+
```sh
74+
yarn cypress open-ct
75+
```
76+
77+
And select the spec to run.
78+
79+
![](https://raw.githubusercontent.com/lmiller1990/cypress-react-template/master/cy-react.png)
80+
81+
Try making a change - the tests will re-run instantly. You not only immediately know if the test passed or failed, but be able to visually inspect and debug any changes.
82+
83+
You can run all the specs with `yarn cypress run-ct`. This is useful for executing all the specs in a CI environment, or one last check before you commit and push your code!
84+
85+
## Discussion
86+
87+
Cypress Component Testing is an alternative to a jsdom based testing environment, such as Jest and Vue Test Utils. Cypress Component Testing offers many benefits:
88+
89+
- Runs in a real browser. This means your tests are closer to what your users will be experiencing.
90+
- Visual. You can see exactly what is rendered. No more scrolling through a cryptic terminal log to figure out what is rendered or to debug - just open the devtools and browse the DOM.
91+
- Powered by Cypress - the most popular and reliable E2E testing tool out there.
92+
93+
It also doubles as a *design environment*. You can see the component as you develop it, and hot reload give you a near instance feedback loop. It can potentially take the place of not only your Jest based test infrastructure, but your Storybook based design infrastructure as well.
94+
95+
Cypress Component Testing is still in alpha but the product is quickly evolving and promises to change the landscape of Component Testing.
96+
97+
## Conclusion
98+
99+
Cypress Component Testing brings everything that is great about Cypress to Component Testing. Since the underlying adapters are built on libraries like Webpack, you don't need to throw away your entire test suite - incremental migration is more than possible.
100+
101+
The visual aspect united testing and design in a single tool. My days of grepping a messy console output to figure out what the user will see are over - I can see exactly what the component will look like as my tests run.
102+
103+
You can get the source code for the blog post [here](https://github.com/lmiller1990/cypress-react-template).
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
{
2+
"video": true,
3+
"projectId": "jq5xpp",
4+
"component": {
5+
"testFiles": "**/*.spec.{js,ts,jsx,tsx}",
6+
"componentFolder": "src"
7+
},
8+
"env": {
9+
"cypress-react-selector": {
10+
"root": "#__cy_root"
11+
}
12+
}
13+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
{
2+
"name": "Using fixtures to represent data",
3+
"email": "hello@cypress.io",
4+
"body": "Fixtures are a great way to mock data for responses to routes"
5+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
const findReactScriptsWebpackConfig = require('@cypress/react/plugins/react-scripts/findReactScriptsWebpackConfig')
2+
const { startDevServer } = require('@cypress/webpack-dev-server')
3+
const _ = require('lodash')
4+
5+
module.exports = (on, config) => {
6+
const map = _.map([4, 8], (n) => n * 2)
7+
8+
console.log(map)
9+
require('@cypress/code-coverage/task')(on, config)
10+
const webpackConfig = findReactScriptsWebpackConfig(config)
11+
12+
const rules = webpackConfig.module.rules.find((rule) => !!rule.oneOf).oneOf
13+
const babelRule = rules.find((rule) => /babel-loader/.test(rule.loader))
14+
15+
babelRule.options.plugins.push(require.resolve('babel-plugin-istanbul'))
16+
17+
on('dev-server:start', (options) => {
18+
return startDevServer({ options, webpackConfig })
19+
})
20+
21+
// IMPORTANT to return the config object
22+
// with the any changed environment variables
23+
return config
24+
}

npm/react/examples/find-webpack/cypress/support/index.ts

Whitespace-only changes.
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
{
2+
"name": "find-webpack",
3+
"version": "1.0.0",
4+
"private": true,
5+
"scripts": {
6+
"cy:open": "node ../../../../scripts/cypress open-ct",
7+
"test": "node ../../../../scripts/cypress run-ct"
8+
},
9+
"dependencies": {
10+
"@types/react": "^17.0.0",
11+
"@types/react-dom": "^17.0.0",
12+
"lodash": "^4.17.21",
13+
"react": "^17.0.2",
14+
"react-dom": "^17.0.2",
15+
"react-scripts": "4.0.3",
16+
"typescript": "^4.2.4"
17+
},
18+
"devDependencies": {
19+
"@cypress/react": "file:../../dist",
20+
"html-webpack-plugin": "4",
21+
"mocha-junit-reporter": "^2.0.0",
22+
"mocha-multi-reporters": "^1.5.1"
23+
}
24+
}
Binary file not shown.
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
1+
<!DOCTYPE html>
2+
<html lang="en">
3+
<head>
4+
<meta charset="utf-8" />
5+
<link rel="icon" href="%PUBLIC_URL%/favicon.ico" />
6+
<meta name="viewport" content="width=device-width, initial-scale=1" />
7+
<meta name="theme-color" content="#000000" />
8+
<meta
9+
name="description"
10+
content="Web site created using create-react-app"
11+
/>
12+
<link rel="apple-touch-icon" href="%PUBLIC_URL%/logo192.png" />
13+
<!--
14+
manifest.json provides metadata used when your web app is installed on a
15+
user's mobile device or desktop. See https://developers.google.com/web/fundamentals/web-app-manifest/
16+
-->
17+
<link rel="manifest" href="%PUBLIC_URL%/manifest.json" />
18+
<!--
19+
Notice the use of %PUBLIC_URL% in the tags above.
20+
It will be replaced with the URL of the `public` folder during the build.
21+
Only files inside the `public` folder can be referenced from the HTML.
22+
23+
Unlike "/favicon.ico" or "favicon.ico", "%PUBLIC_URL%/favicon.ico" will
24+
work correctly both with client-side routing and a non-root public URL.
25+
Learn how to configure a non-root public URL by running `npm run build`.
26+
-->
27+
<title>React App</title>
28+
</head>
29+
<body>
30+
<noscript>You need to enable JavaScript to run this app.</noscript>
31+
<div id="root"></div>
32+
<!--
33+
This HTML file is a template.
34+
If you open it directly in the browser, you will see an empty page.
35+
36+
You can add webfonts, meta tags, or analytics to this file.
37+
The build step will place the bundled scripts into the <body> tag.
38+
39+
To begin the development, run `npm start` or `yarn start`.
40+
To create a production bundle, use `npm run build` or `yarn build`.
41+
-->
42+
</body>
43+
</html>
Loading
Loading
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
{
2+
"short_name": "React App",
3+
"name": "Create React App Sample",
4+
"icons": [
5+
{
6+
"src": "favicon.ico",
7+
"sizes": "64x64 32x32 24x24 16x16",
8+
"type": "image/x-icon"
9+
},
10+
{
11+
"src": "logo192.png",
12+
"type": "image/png",
13+
"sizes": "192x192"
14+
},
15+
{
16+
"src": "logo512.png",
17+
"type": "image/png",
18+
"sizes": "512x512"
19+
}
20+
],
21+
"start_url": ".",
22+
"display": "standalone",
23+
"theme_color": "#000000",
24+
"background_color": "#ffffff"
25+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
# https://www.robotstxt.org/robotstxt.html
2+
User-agent: *
3+
Disallow:
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
.App {
2+
text-align: center;
3+
}
4+
5+
.App-logo {
6+
height: 40vmin;
7+
pointer-events: none;
8+
}
9+
10+
@media (prefers-reduced-motion: no-preference) {
11+
.App-logo {
12+
animation: App-logo-spin infinite 20s linear;
13+
}
14+
}
15+
16+
.App-header {
17+
background-color: #282c34;
18+
min-height: 100vh;
19+
display: flex;
20+
flex-direction: column;
21+
align-items: center;
22+
justify-content: center;
23+
font-size: calc(10px + 2vmin);
24+
color: white;
25+
}
26+
27+
.App-link {
28+
color: #61dafb;
29+
}
30+
31+
@keyframes App-logo-spin {
32+
from {
33+
transform: rotate(0deg);
34+
}
35+
to {
36+
transform: rotate(360deg);
37+
}
38+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
import React from 'react'
2+
import logo from './logo.svg'
3+
import './App.css'
4+
5+
function bar () {
6+
return 'bar'
7+
}
8+
9+
function App () {
10+
return (
11+
<div className="App">
12+
<h1>{bar()}</h1>
13+
<header className="App-header">
14+
<img src={logo} className="App-logo" alt="logo" />
15+
<p>
16+
Edit <code>src/App.tsx</code> and save to reload.
17+
</p>
18+
<a
19+
className="App-link"
20+
href="https://reactjs.org"
21+
target="_blank"
22+
rel="noopener noreferrer"
23+
>
24+
Learn React
25+
</a>
26+
</header>
27+
</div>
28+
)
29+
}
30+
31+
export default App
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
import React from 'react'
2+
import { mount } from '@cypress/react'
3+
import App from './App'
4+
5+
it('renders learn react link', () => {
6+
mount(<App />)
7+
cy.get('a').contains('Learn React')
8+
})
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
body {
2+
margin: 0;
3+
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', 'Roboto', 'Oxygen',
4+
'Ubuntu', 'Cantarell', 'Fira Sans', 'Droid Sans', 'Helvetica Neue',
5+
sans-serif;
6+
-webkit-font-smoothing: antialiased;
7+
-moz-osx-font-smoothing: grayscale;
8+
}
9+
10+
code {
11+
font-family: source-code-pro, Menlo, Monaco, Consolas, 'Courier New',
12+
monospace;
13+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
import React from 'react'
2+
import ReactDOM from 'react-dom'
3+
import './index.css'
4+
import App from './App'
5+
import reportWebVitals from './reportWebVitals'
6+
7+
ReactDOM.render(
8+
<React.StrictMode>
9+
<App />
10+
</React.StrictMode>,
11+
document.getElementById('root'),
12+
)
13+
14+
// If you want to start measuring performance in your app, pass a function
15+
// to log results (for example: reportWebVitals(console.log))
16+
// or send to an analytics endpoint. Learn more: https://bit.ly/CRA-vitals
17+
reportWebVitals()
Loading
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
/// <reference types="react-scripts" />
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
import { ReportHandler } from 'web-vitals'
2+
3+
const reportWebVitals = (onPerfEntry?: ReportHandler) => {
4+
if (onPerfEntry && onPerfEntry instanceof Function) {
5+
import('web-vitals').then(({ getCLS, getFID, getFCP, getLCP, getTTFB }) => {
6+
getCLS(onPerfEntry)
7+
getFID(onPerfEntry)
8+
getFCP(onPerfEntry)
9+
getLCP(onPerfEntry)
10+
getTTFB(onPerfEntry)
11+
})
12+
}
13+
}
14+
15+
export default reportWebVitals
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
// jest-dom adds custom jest matchers for asserting on DOM nodes.
2+
// allows you to do things like:
3+
// expect(element).toHaveTextContent(/react/i)
4+
// learn more: https://github.com/testing-library/jest-dom
5+
import '@testing-library/jest-dom'

0 commit comments

Comments
 (0)