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

Commit 95feb07

Browse files
committed
merge master into this branch
2 parents ba6b20c + d29901e commit 95feb07

File tree

12 files changed

+15611
-10859
lines changed

12 files changed

+15611
-10859
lines changed

.travis.yml

Lines changed: 0 additions & 31 deletions
This file was deleted.

README.md

Lines changed: 51 additions & 55 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
# cypress-react-unit-test [![Build Status](https://travis-ci.org/bahmutov/cypress-react-unit-test.svg?branch=master)](https://travis-ci.org/bahmutov/cypress-react-unit-test) [![Cypress.io tests](https://img.shields.io/badge/cypress.io-tests-green.svg?style=flat-square)](https://dashboard.cypress.io/#/projects/z9dxah) [![renovate-app badge][renovate-badge]][renovate-app] [![This project is using Percy.io for visual regression testing.](https://percy.io/static/images/percy-badge.svg)](https://percy.io/bahmutov/cypress-react-unit-test)
1+
# cypress-react-unit-test [![CircleCI](https://circleci.com/gh/bahmutov/cypress-react-unit-test/tree/master.svg?style=svg)](https://circleci.com/gh/bahmutov/cypress-react-unit-test/tree/master) [![Cypress.io tests](https://img.shields.io/badge/cypress.io-tests-green.svg?style=flat-square)](https://dashboard.cypress.io/#/projects/z9dxah) [![renovate-app badge][renovate-badge]][renovate-app] [![This project is using Percy.io for visual regression testing.](https://percy.io/static/images/percy-badge.svg)](https://percy.io/bahmutov/cypress-react-unit-test)
22

33
> A little helper to unit test React components in the open source [Cypress.io](https://www.cypress.io/) E2E test runner **ALPHA**
44
@@ -16,7 +16,7 @@
1616

1717
## Install
1818

19-
Requires [Node](https://nodejs.org/en/) version 6 or above.
19+
Requires [Node](https://nodejs.org/en/) version 8 or above.
2020

2121
```sh
2222
npm install --save-dev cypress cypress-react-unit-test
@@ -63,79 +63,75 @@ describe('HelloState component', () => {
6363

6464
![Unit testing React components](images/demo.png)
6565

66-
## Configuration
66+
### styles
6767

68-
If your React and React DOM libraries are installed in non-standard paths (think monorepo scenario), you can tell this plugin where to find them. In `cypress.json` specify paths like this:
68+
You can add individual style to the mounted component by passing its text as an option
6969

70-
```json
71-
{
72-
"env": {
73-
"cypress-react-unit-test": {
74-
"react": "node_modules/react/umd/react.development.js",
75-
"react-dom": "node_modules/react-dom/umd/react-dom.development.js"
70+
```js
71+
it('can be passed as an option', () => {
72+
const style = `
73+
.component-button {
74+
display: inline-flex;
75+
width: 25%;
76+
flex: 1 0 auto;
7677
}
77-
}
78-
}
79-
```
80-
81-
## Transpilation
8278
83-
How can we use features that require transpilation? Using [@cypress/webpack-preprocessor](https://github.com/cypress-io/cypress-webpack-preprocessor#readme). You can use [cypress/plugins/index.js](cypress/plugins/index.js) to configure any transpilation plugins you need.
79+
.component-button.orange button {
80+
background-color: #F5923E;
81+
color: white;
82+
}
83+
`
84+
cy.mount(<Button name='Orange' orange />, null, { style })
85+
cy.get('.orange button')
86+
.should('have.css', 'background-color', 'rgb(245, 146, 62)')
87+
})
88+
```
8489

85-
For example, to enable class properties:
90+
Often your component rely on global CSS style imported from the root `index.js` or `app.js` file
8691

8792
```js
88-
// cypress/plugins/index.js
89-
const webpack = require('@cypress/webpack-preprocessor')
90-
const webpackOptions = {
91-
module: {
92-
rules: [
93-
{
94-
test: /\.(js|jsx|mjs)$/,
95-
loader: 'babel-loader',
96-
options: {
97-
presets: ['@babel/preset-env', '@babel/preset-react'],
98-
plugins: ['@babel/plugin-proposal-class-properties'],
99-
},
100-
}
101-
]
102-
}
103-
}
93+
// index.js
94+
import './styles.css'
95+
// bootstrap application
96+
```
10497

105-
const options = {
106-
// send in the options from your webpack.config.js, so it works the same
107-
// as your app's code
108-
webpackOptions,
109-
watchOptions: {}
110-
}
98+
You can read the CSS file and pass it as `style` option yourself
11199

112-
module.exports = on => {
113-
on('file:preprocessor', webpack(options))
114-
}
100+
```js
101+
cy.readFile('cypress/integration/Button.css')
102+
.then(style => {
103+
cy.mount(<Button name='Orange' orange />, null, { style })
104+
})
115105
```
116106

117-
Install dev dependencies
107+
You can even let Cypress read the file and inject the style
118108

119-
```shell
120-
npm i -D @cypress/webpack-preprocessor \
121-
babel-loader @babel/preset-env @babel/preset-react \
122-
@babel/plugin-proposal-class-properties
109+
```js
110+
const cssFile = 'cypress/integration/Button.css'
111+
cy.mount(<Button name='Orange' orange />, null, { cssFile })
123112
```
124113

125-
And write a component using class properties
114+
See [cypress/integration/inject-style-spec.js](cypress/integration/inject-style-spec.js) for more examples.
126115

127-
```js
128-
import React from 'react'
116+
## Configuration
129117

130-
export class Transpiled extends React.Component {
131-
state = {
132-
count: 0
133-
}
118+
If your React and React DOM libraries are installed in non-standard paths (think monorepo scenario), you can tell this plugin where to find them. In `cypress.json` specify paths like this:
134119

135-
// ...
120+
```json
121+
{
122+
"env": {
123+
"cypress-react-unit-test": {
124+
"react": "node_modules/react/umd/react.development.js",
125+
"react-dom": "node_modules/react-dom/umd/react-dom.development.js"
126+
}
127+
}
136128
}
137129
```
138130

131+
## Transpilation
132+
133+
How can we use features that require transpilation? By using [@cypress/webpack-preprocessor](https://github.com/cypress-io/cypress-webpack-preprocessor#readme) - see the plugin configuration in [cypress/plugins/index.js](cypress/plugins/index.js)
134+
139135
## Examples
140136

141137
All components are in [src](src) folder. All tests are in [cypress/integration](cypress/integration) folder.

circle.yml

Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,47 @@
1+
version: 2.1
2+
orbs:
3+
cypress: cypress-io/cypress@1
4+
workflows:
5+
build:
6+
jobs:
7+
# install and cache dependencies in this job
8+
# AND build the library once
9+
# then the workspace will be passed to other jobs
10+
- cypress/install:
11+
name: Install
12+
build: npm run transpile
13+
post-steps:
14+
- run:
15+
name: Show info 📺
16+
command: npx cypress info
17+
- run:
18+
name: Linting code 🧹
19+
command: npm run lint
20+
21+
# the test job automatically attaches the workspace
22+
# created by the install job, so it is ready to test
23+
- cypress/run:
24+
name: Test
25+
requires:
26+
- Install
27+
# notice a trick to avoid re-installing dependencies
28+
# in this job - a do-nothing "install-command" parameter
29+
install-command: echo 'Nothing to install in this job'
30+
# we are not going to use results from this job anywhere else
31+
no-workspace: true
32+
record: false
33+
34+
# this job attaches the workspace left by the install job
35+
# so it is ready to run Cypress tests
36+
# only we will run semantic release script instead
37+
- cypress/run:
38+
name: NPM release
39+
# we need newer Node for semantic release
40+
executor: cypress/base-12-6-0
41+
requires:
42+
- Install
43+
- Test
44+
install-command: echo 'Nothing to install in this job'
45+
no-workspace: true
46+
# instead of "cypress run" do NPM release 😁
47+
command: npm run semantic-release

cypress/component/component tests/basic/network/users-spec.js

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,9 @@ context('Users', () => {
2222
// preventing race conditions where you wait on untouched routes
2323
})
2424

25-
it('can inspect real data in XHR', () => {
25+
// Cypress v4 is failing on this test
26+
// but this test passes in new mount mode
27+
it.skip('can inspect real data in XHR', () => {
2628
cy.route('/users?_limit=3').as('users')
2729
mount(<Users />)
2830
cy.wait('@users')

cypress/integration/empty-spec.js

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
/// <reference types="cypress" />
2+
/// <reference types="../../lib" />
3+
import React from 'react'
4+
5+
/* eslint-env mocha */
6+
describe('Empty div jsx', () => {
7+
it('works with El', () => {
8+
const El = () => <div>foo</div>
9+
cy.mount(<El></El>)
10+
})
11+
12+
it('works with div', () => {
13+
cy.mount(<div>I am a div</div>)
14+
cy.contains('I am a div').should('be.visible')
15+
})
16+
})
Lines changed: 97 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,97 @@
1+
// <reference types="cypress" />
2+
/// <reference types="../../lib" />
3+
import React from 'react'
4+
5+
class Button extends React.Component {
6+
handleClick() {
7+
this.props.clickHandler(this.props.name)
8+
}
9+
10+
render() {
11+
const className = [
12+
'component-button',
13+
this.props.orange ? 'orange' : '',
14+
this.props.wide ? 'wide' : '',
15+
]
16+
17+
return (
18+
<div className={className.join(' ').trim()}>
19+
<button onClick={this.handleClick.bind(this)}>{this.props.name}</button>
20+
</div>
21+
)
22+
}
23+
}
24+
25+
describe('Injecting style', () => {
26+
it('can be passed as an option', () => {
27+
const style = `
28+
.component-button {
29+
display: inline-flex;
30+
width: 25%;
31+
flex: 1 0 auto;
32+
}
33+
34+
.component-button.orange button {
35+
background-color: #F5923E;
36+
color: white;
37+
}
38+
`
39+
cy.mount(<Button name="Orange" orange />, null, { style })
40+
cy.get('.orange button').should(
41+
'have.css',
42+
'background-color',
43+
'rgb(245, 146, 62)',
44+
)
45+
})
46+
47+
it('read CSS file and pass as style', () => {
48+
cy.readFile('cypress/integration/Button.css').then(style => {
49+
cy.mount(<Button name="Orange" orange />, null, { style })
50+
})
51+
52+
cy.get('.component-button')
53+
.should('have.class', 'orange')
54+
.find('button')
55+
.should('have.css', 'background-color', 'rgb(245, 146, 62)')
56+
})
57+
58+
it('can be read automatically', () => {
59+
const cssFile = 'cypress/integration/Button.css'
60+
cy.mount(<Button name="Orange" orange />, null, { cssFile })
61+
cy.get('.orange button').should(
62+
'have.css',
63+
'background-color',
64+
'rgb(245, 146, 62)',
65+
)
66+
})
67+
68+
context('read CSS file once', () => {
69+
before(() => {
70+
// .as('style') will save the loaded CSS text
71+
// in the text context property "style"
72+
cy.readFile('cypress/integration/Button.css').as('style')
73+
})
74+
75+
it('is orange', function() {
76+
// notice we use "function () {}" callback
77+
// to get the test context "this" to be able to use "this.style"
78+
cy.mount(<Button name="Orange" orange />, null, { style: this.style })
79+
80+
cy.get('.orange button').should(
81+
'have.css',
82+
'background-color',
83+
'rgb(245, 146, 62)',
84+
)
85+
})
86+
87+
it('is orange again', function() {
88+
cy.mount(<Button name="Orange" orange />, null, { style: this.style })
89+
90+
cy.get('.orange button').should(
91+
'have.css',
92+
'background-color',
93+
'rgb(245, 146, 62)',
94+
)
95+
})
96+
})
97+
})

cypress/plugins/index.js

Lines changed: 0 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -5,12 +5,6 @@ const { initPlugin } = require('cypress-plugin-snapshots/plugin')
55

66
// should we just reuse root webpack config?
77
const webpackOptions = {
8-
// https://webpack.js.org/configuration/node/
9-
// avoid winston logger problem
10-
// https://github.com/percy/percy-cypress/issues/58
11-
node: {
12-
fs: 'empty',
13-
},
148
resolve: {
159
alias: {
1610
react: path.resolve('./node_modules/react'),

lib/getDisplayName.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@ export default function getDisplayName(
1818
return nameFromCache
1919
}
2020

21-
let displayName: string
21+
let displayName: string | null = null;
2222

2323
// The displayName property is not guaranteed to be a string.
2424
// It's only safe to use for our purposes if it's a string.

0 commit comments

Comments
 (0)