Skip to content
This repository was archived by the owner on Oct 1, 2020. It is now read-only.

Commit 2d29c87

Browse files
authored
Test react scripts (#74)
* add example React app * delete webpack optimization * use module to clean options * fix lint * disable video in e2e test, update snapshot * split workflow into 3 jobs * add react app test job * add no-op install * add public folder to Git * add README to react-app example * link example from root readme * add timeout, fix warning * remove wait-on * do not use Node 12 executor for the app * try to use default executor for install * hmm back to Node 12 * try using react-scripts 3.2 * ignore build folder * add example of working dynamic import
1 parent a6baf1f commit 2d29c87

25 files changed

+7108
-245
lines changed

.eslintignore

+5
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,9 @@
11
test/_test-output
22
cypress/tests/e2e/compile-error.js
33
test/fixtures
4+
examples/react-app/src
5+
examples/react-app/node_modules
6+
examples/react-app/build
7+
examples/react-app/package.json
8+
examples/react-app/cypress/integration/dynamic-import-spec.js
49
*.ts

.gitignore

+1
Original file line numberDiff line numberDiff line change
@@ -4,3 +4,4 @@ node_modules/
44
_test-output
55
cypress/videos
66
cypress/screenshots
7+
examples/react-app/build/

README.md

+6-2
Original file line numberDiff line numberDiff line change
@@ -35,13 +35,17 @@ This plugin (and all Cypress plugins) run in Cypress's own version of Node. If y
3535
In your project's [plugins file](https://on.cypress.io/guides/tooling/plugins-guide.html):
3636

3737
```javascript
38-
const webpack = require('@cypress/webpack-preprocessor')
38+
const webpackPreprocessor = require('@cypress/webpack-preprocessor')
3939

4040
module.exports = (on) => {
41-
on('file:preprocessor', webpack())
41+
on('file:preprocessor', webpackPreprocessor())
4242
}
4343
```
4444

45+
## Examples
46+
47+
- [React app](examples/react-app)
48+
4549
## Options
4650

4751
Pass in options as the second argument to `webpack`:

__snapshots__/e2e.spec.js

+1-9
Original file line numberDiff line numberDiff line change
@@ -33,10 +33,8 @@ SyntaxError: /[cwd]/cypress/tests/e2e/compile-error.js: Unexpected token, expect
3333
| ^
3434
15 | })
3535
16 |
36-
3736
@ multi ./cypress/tests/e2e/compile-error.js main[0]
3837
39-
4038
This occurred while Cypress was compiling and bundling your test code. This is usually caused by:
4139
4240
- A missing file or dependency
@@ -53,18 +51,12 @@ Fix the error in your code and re-run your tests.
5351
│ Pending: 0 │
5452
│ Skipped: 0 │
5553
│ Screenshots: 0 │
56-
│ Video: true
54+
│ Video: false
5755
│ Duration: X seconds │
5856
│ Spec Ran: e2e/compile-error.js │
5957
└────────────────────────────────────────────────────────────────────────────────────────────────┘
6058
6159
62-
(Video)
63-
64-
- Started processing: Compressing to 32 CRF
65-
- Finished processing: /[cwd]/cypress/videos/e2e/compile-error.js.mp4 (X second)
66-
67-
6860
====================================================================================================
6961
7062
(Run Finished)

circle.yml

+43-4
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,29 @@
1+
# based on https://github.com/cypress-io/circleci-orb/blob/master/docs/recipes.md
12
version: 2.1
23
orbs:
34
cypress: cypress-io/cypress@1
45
workflows:
56
build:
67
jobs:
7-
- cypress/run:
8+
# first, run a single job to install dependencies and Cypress
9+
# and perform a few more small steps like linting
10+
- cypress/install:
11+
name: Install
812
executor: cypress/base-12-14-0
913
yarn: true
14+
post-steps:
15+
- run:
16+
name: Show info 📺
17+
command: npx cypress info
18+
19+
- cypress/run:
20+
name: Tests
21+
requires:
22+
- Install
23+
executor: cypress/base-12-14-0
24+
install-command: echo 'Nothing else to install in this job'
1025
command: npm test
26+
no-workspace: true
1127
post-steps:
1228
- run:
1329
name: Lint code 🧹
@@ -18,6 +34,29 @@ workflows:
1834
- run:
1935
name: Run tests w/ webpack@latest 🧪
2036
command: npm test
21-
- run:
22-
name: Semantic release 🚀
23-
command: npm run semantic-release
37+
38+
- cypress/run:
39+
name: Test React App
40+
requires:
41+
- Install
42+
executor: cypress/base-12-14-0
43+
install-command: echo 'Nothing else to install in this job'
44+
timeout: '1m'
45+
start: npm start
46+
no-workspace: true
47+
working_directory: examples/react-app
48+
49+
# wait for all jobs to finish and possible run NPM release
50+
- cypress/run:
51+
name: NPM release
52+
requires:
53+
- Install
54+
- Tests
55+
- Test React App
56+
executor: cypress/base-12-14-0
57+
# nothing to install - cypress/install job does it
58+
# and nothing to pass to the next job
59+
install-command: echo 'Nothing else to install in this job'
60+
no-workspace: true
61+
# instead of "cypress run" do NPM release 😁
62+
command: npm run semantic-release

examples/react-app/.env

+2
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
SKIP_PREFLIGHT_CHECK=true
2+
BROWSER=none

examples/react-app/README.md

+11
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
This is an example React application clone from [codesandbox.io](https://codesandbox.io/s/oj3qm2zq06) and blog post [How To Build a React To-Do App with React Hooks](https://www.digitalocean.com/community/tutorials/how-to-build-a-react-to-do-app-with-react-hooks).
2+
3+
Because the application is using `react-scripts`, we can point our Cypress Webpack preprocessor at the Webpack config included with `react-scripts` module. See [cypress/plugins/index.js](cypress/plugins/index.js) file.
4+
5+
**Tip:** you can use [find-webpack](https://github.com/bahmutov/find-webpack) utility to find the Webpack config options provided by common tools: `react-scripts`, Vue CLI.
6+
7+
## Chunks and dynamic imports
8+
9+
Normally, a code that uses [dynamic imports](https://github.com/tc39/proposal-dynamic-import) is bundled by Webpack into a separate JavaScript bundle (chunk) to be loaded on demand. Cypress needs all chunks to be in the same bundled file, since it cannot be serving additional files, besides the spec itself. Thus we need to force all chunks into the single bundle by changing the Webpack plugins' options.
10+
11+
See [cypress/integration/dynamic-import-spec.js](cypress/integration/dynamic-import-spec.js) and the [cypress/plugins/index.js](cypress/plugins/index.js) file - it uses [find-webpack cleanForCypress](https://github.com/bahmutov/find-webpack) utility to force dynamic imports into the same spec bundle.

examples/react-app/cypress.json

+6
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
{
2+
"baseUrl": "http://localhost:3000",
3+
"fixturesFolder": false,
4+
"supportFile": false,
5+
"viewportWidth": 600
6+
}
+10
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
{
2+
"env": {
3+
"cypress/globals": true
4+
},
5+
"extends": [
6+
"eslint:recommended",
7+
"plugin:cypress/recommended"
8+
],
9+
"plugins": ["cypress"]
10+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
/// <reference types="cypress" />
2+
describe('Dynamic import', () => {
3+
it('works', () => {
4+
// dynamically import module
5+
// and then invoke an exported method "reverse"
6+
cy.wrap(import('../utils'))
7+
.invoke('reverse', 'Hello').should('equal', 'olleH')
8+
})
9+
})
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
/// <reference types="cypress" />
2+
// this spec file will be bundled using the same Webpack options
3+
// as the React App itself. See ../plugins/index.js
4+
5+
describe('Todo app', () => {
6+
it('works', () => {
7+
cy.visit('/')
8+
cy.get('.todo').should('have.length', 3)
9+
cy.get('input').type('Write Cypress tests{enter}')
10+
cy.get('.todo').should('have.length', 4)
11+
})
12+
})
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
const findWebpack = require('find-webpack')
2+
const webpackPreprocessor = require('../../../..')
3+
4+
/**
5+
* @type {Cypress.PluginConfig}
6+
*/
7+
module.exports = (on) => {
8+
// find the Webpack config used by react-scripts
9+
const webpackOptions = findWebpack.getWebpackOptions()
10+
11+
if (!webpackOptions) {
12+
throw new Error('Could not find Webpack in this project 😢')
13+
}
14+
15+
// if we just pass webpackOptions to the preprocessor
16+
// it won't work - because react-scripts by default
17+
// includes plugins that split specs into chunks, etc.
18+
// https://github.com/cypress-io/cypress-webpack-preprocessor/issues/31
19+
20+
// solution 1
21+
// blunt: delete entire optimization object
22+
// delete webpackOptions.optimization
23+
24+
// solution 2
25+
// use a module that carefully removes only plugins
26+
// that we found to be breaking the bundling
27+
// https://github.com/bahmutov/find-webpack
28+
const cleanOptions = {
29+
reactScripts: true,
30+
}
31+
32+
findWebpack.cleanForCypress(cleanOptions, webpackOptions)
33+
34+
const options = {
35+
webpackOptions,
36+
watchOptions: {},
37+
}
38+
39+
on('file:preprocessor', webpackPreprocessor(options))
40+
}

examples/react-app/cypress/utils.js

+1
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
export const reverse = (s) => s.split('').reverse().join('')

examples/react-app/package.json

+28
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
{
2+
"name": "react-app",
3+
"version": "1.0.0",
4+
"description": "Transpiling specs using Webpack options from react-app",
5+
"private": true,
6+
"main": "index.js",
7+
"scripts": {
8+
"start": "../../node_modules/.bin/react-scripts start",
9+
"test": "../../node_modules/.bin/cypress run",
10+
"dev": "../../node_modules/.bin/start-test 3000 cy:open",
11+
"cy:open": "../../node_modules/.bin/cypress open"
12+
},
13+
"license": "ISC",
14+
"author": "",
15+
"keywords": [],
16+
"browserslist": {
17+
"production": [
18+
">0.2%",
19+
"not dead",
20+
"not op_mini all"
21+
],
22+
"development": [
23+
"last 1 chrome version",
24+
"last 1 firefox version",
25+
"last 1 safari version"
26+
]
27+
}
28+
}

examples/react-app/public/favicon.ico

3.78 KB
Binary file not shown.

examples/react-app/public/index.html

+47
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,47 @@
1+
<!DOCTYPE html>
2+
<html lang="en">
3+
<head>
4+
<meta charset="utf-8" />
5+
<link rel="shortcut icon" href="%PUBLIC_URL%/favicon.ico" />
6+
<meta
7+
name="viewport"
8+
content="width=device-width, initial-scale=1, shrink-to-fit=no"
9+
/>
10+
<meta name="theme-color" content="#000000" />
11+
<!--
12+
manifest.json provides metadata used when your web app is added to the
13+
homescreen on Android. See https://developers.google.com/web/fundamentals/web-app-manifest/
14+
-->
15+
<link rel="manifest" href="%PUBLIC_URL%/manifest.json" />
16+
<!--
17+
Notice the use of %PUBLIC_URL% in the tags above.
18+
It will be replaced with the URL of the `public` folder during the build.
19+
Only files inside the `public` folder can be referenced from the HTML.
20+
21+
Unlike "/favicon.ico" or "favicon.ico", "%PUBLIC_URL%/favicon.ico" will
22+
work correctly both with client-side routing and a non-root public URL.
23+
Learn how to configure a non-root public URL by running `npm run build`.
24+
-->
25+
<title>React App</title>
26+
27+
<link
28+
rel="stylesheet"
29+
href="https://cdnjs.cloudflare.com/ajax/libs/bulma/0.7.2/css/bulma.css"
30+
/>
31+
</head>
32+
33+
<body>
34+
<noscript> You need to enable JavaScript to run this app. </noscript>
35+
<div id="root"></div>
36+
<!--
37+
This HTML file is a template.
38+
If you open it directly in the browser, you will see an empty page.
39+
40+
You can add webfonts, meta tags, or analytics to this file.
41+
The build step will place the bundled scripts into the <body> tag.
42+
43+
To begin the development, run `npm start` or `yarn start`.
44+
To create a production bundle, use `npm run build` or `yarn build`.
45+
-->
46+
</body>
47+
</html>
+15
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
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+
"start_url": ".",
12+
"display": "standalone",
13+
"theme_color": "#000000",
14+
"background_color": "#ffffff"
15+
}

examples/react-app/src/App.css

+24
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
.app {
2+
background: #209cee;
3+
padding: 30px;
4+
height: 100vh;
5+
}
6+
7+
.todo-list {
8+
background: #e8e8e8;
9+
border-radius: 4px;
10+
padding: 5px;
11+
max-width: 400px;
12+
}
13+
14+
.todo {
15+
background: #fff;
16+
box-shadow: 1px 1px 1px rgba(0, 0, 0, 0.15);
17+
padding: 3px 10px;
18+
font-size: 12px;
19+
margin-bottom: 6px;
20+
border-radius: 3px;
21+
display: flex;
22+
align-items: center;
23+
justify-content: space-between;
24+
}

0 commit comments

Comments
 (0)