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

Commit b8ecd2f

Browse files
Ryan Kellerbahmutov
authored andcommitted
break: use Cypress commands and aliases, version bumps and module loading tweaks (#37)
BREAKING CHANGE: how components are mounted and accessed * Fixed users spec - had race condition * Drafted cypress commands * Lib functions as commands. Use node_modules for loading React * Switch over to using cy.mount. Docs updated * Version bumping incl. babel@7 * Added error boundaries - good example component * Line about err boundary component
1 parent b9b61f4 commit b8ecd2f

File tree

16 files changed

+1749
-277
lines changed

16 files changed

+1749
-277
lines changed

.babelrc

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,9 @@
11
{
2-
"presets" : ["es2015", "react"]
2+
"presets": [
3+
"@babel/preset-env",
4+
"@babel/preset-react"
5+
],
6+
"plugins": [
7+
"@babel/plugin-proposal-class-properties"
8+
],
39
}

README.md

Lines changed: 8 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -28,18 +28,18 @@ npm install --save-dev cypress cypress-react-unit-test
2828
// import the component you want to test
2929
import { HelloState } from '../../src/hello-x.jsx'
3030
import React from 'react'
31-
import { mount } from 'cypress-react-unit-test'
3231
describe('HelloState component', () => {
3332
it('works', () => {
3433
// mount the component under test
35-
mount(<HelloState />)
34+
cy.mount(<HelloState />)
3635
// start testing!
3736
cy.contains('Hello Spider-man!')
38-
// mounted component is returned from Cypress.component()
39-
Cypress.component().invoke('setState', {name: 'React'})
40-
Cypress.component().its('state').should('deep.equal', {
41-
name: 'React'
42-
})
37+
// mounted component is aliased as @Component
38+
cy.get('@Component')
39+
.invoke('setState', { name: 'React' })
40+
cy.get('@Component')
41+
.its('state')
42+
.should('deep.equal', { name: 'React' })
4343
// check if GUI has rerendered
4444
cy.contains('Hello React!')
4545
})
@@ -115,6 +115,7 @@ All components are in [src](src) folder. All tests are in [cypress/integration](
115115
* [counter-spec.js](cypress/integration/counter-spec.js) clicks on the component and confirms the result
116116
* [stateless-spec.js](cypress/integration/stateless-spec.js) shows testing a stateless component from [stateless.jsx](src/stateless.jsx)
117117
* [transpiled-spec.js](cypress/integration/stateless-spec.js) shows testing a component with class properties syntax from [transpiled.jsx](src/stateless.jsx)
118+
* [error-boundary-spec.js](cypress/integration/error-boundary-spec.js) shows testing a component acting as an error boundary from [error-boundary.jsx](src/error-boundary.jsx)
118119
* [users-spec.js](cypress/integration/users-spec.js) shows how to observe XHR requests, mock server responses for component [users.jsx](src/users.jsx)
119120
* [alert-spec.js](cypress/integration/alert-spec.js) shows how to spy on `window.alert` calls from your component [stateless-alert.jsx](src/stateless-alert.jsx)
120121

cypress/integration/alert-spec.js

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,12 @@
11
import HelloWorld from '../../src/stateless-alert.jsx'
22
import React from 'react'
3-
import { mount } from '../../lib'
43

54
/* eslint-env mocha */
65
describe('Stateless alert', () => {
76
beforeEach(() => {
87
const spy = cy.spy().as('alert')
98
cy.on('window:alert', spy)
10-
mount(<HelloWorld name='Alert' />)
9+
cy.mount(<HelloWorld name='Alert' />)
1110
})
1211

1312
it('shows link', () => {

cypress/integration/counter-spec.js

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,10 @@
11
import { Counter } from '../../src/counter.jsx'
22
import React from 'react'
3-
import { mount } from '../../lib'
43

54
/* eslint-env mocha */
65
describe('Counter', () => {
76
it('counts clicks', () => {
8-
mount(<Counter />)
7+
cy.mount(<Counter />)
98
cy.contains('count: 0')
109
.click()
1110
.contains('count: 1')
@@ -14,7 +13,7 @@ describe('Counter', () => {
1413
})
1514

1615
it('counts clicks 2', () => {
17-
mount(<Counter />)
16+
cy.mount(<Counter />)
1817
cy.contains('count: 0')
1918
.click()
2019
.contains('count: 1')
@@ -23,9 +22,9 @@ describe('Counter', () => {
2322
})
2423
})
2524

26-
describe('Counter mounted before each test', () => {
25+
describe('Counter cy.mounted before each test', () => {
2726
beforeEach(() => {
28-
mount(<Counter />)
27+
cy.mount(<Counter />)
2928
})
3029

3130
it('goes to 3', () => {
@@ -41,7 +40,8 @@ describe('Counter mounted before each test', () => {
4140
.click()
4241
.click()
4342
.click()
44-
Cypress.component().its('state')
43+
cy.get('@Component')
44+
.its('state')
4545
.should('deep.equal', {count: 3})
4646
})
4747
})
Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
1+
import { ErrorBoundary } from '../../src/error-boundary.jsx'
2+
import React from 'react'
3+
4+
/* eslint-env mocha */
5+
describe('Error Boundary', () => {
6+
const errorMessage = 'I crashed!'
7+
const ChildWithoutError = () => <h1>Normal Child</h1>
8+
const ChildWithError = () => {
9+
throw new Error(errorMessage)
10+
return null
11+
}
12+
13+
it('renders normal children', () => {
14+
cy.mount(
15+
<ErrorBoundary>
16+
<ChildWithoutError />
17+
</ErrorBoundary>
18+
)
19+
cy.get('h1')
20+
.should('have.text', 'Normal Child')
21+
cy.get('@Component')
22+
.its('state.error')
23+
.should('not.exist')
24+
})
25+
26+
it('on error, display fallback UI', () => {
27+
cy.mount(
28+
<ErrorBoundary>
29+
<ChildWithError />
30+
</ErrorBoundary>
31+
)
32+
cy.get('header h1')
33+
.should('contain', 'Something went wrong.')
34+
cy.get('header h3')
35+
.should('contain', 'failed to load')
36+
cy.get('@Component')
37+
.its('state.error.message')
38+
.should('equal', errorMessage)
39+
cy.get('@Component')
40+
.its('state.error.stack')
41+
.should('contain', 'ChildWithError')
42+
})
43+
})
Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,10 @@
11
import { HelloWorld } from '../../src/hello-world.jsx'
22
import React from 'react'
3-
import { mount } from '../../lib'
43

54
/* eslint-env mocha */
65
describe('HelloWorld component', () => {
76
it('works', () => {
8-
mount(<HelloWorld />)
7+
cy.mount(<HelloWorld />)
98
cy.contains('Hello World!')
109
})
1110
})
Lines changed: 8 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,23 +1,24 @@
11
import { HelloX, HelloState } from '../../src/hello-x.jsx'
22
import React from 'react'
3-
import { mount } from '../../lib'
43

54
/* eslint-env mocha */
65
describe('HelloX component', () => {
76
it('works', () => {
8-
mount(<HelloX name="SuperMan" />)
7+
cy.mount(<HelloX name="SuperMan" />)
98
cy.contains('Hello SuperMan!')
109
})
1110
})
1211

1312
describe('HelloState component', () => {
1413
it('changes state', () => {
15-
mount(<HelloState />)
14+
cy.mount(<HelloState />)
1615
cy.contains('Hello Spider-man!')
17-
Cypress.component().invoke('setState', {name: 'React'})
18-
Cypress.component().its('state').should('deep.equal', {
19-
name: 'React'
20-
})
16+
const stateToSet = { name: 'React' }
17+
cy.get('@Component')
18+
.invoke('setState', stateToSet)
19+
cy.get('@Component')
20+
.its('state')
21+
.should('deep.equal', stateToSet)
2122
cy.contains('Hello React!')
2223
})
2324
})

cypress/integration/stateless-spec.js

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,13 @@
11
import HelloWorld from '../../src/stateless.jsx'
22
import React from 'react'
3-
import { mount } from '../../lib'
43

54
/* eslint-env mocha */
65
describe('Stateless component', () => {
76
beforeEach(() => {
87
// pass spy and save it under an alias
98
// so we can easily get it later with cy.get('@greeting')
109
const spy = cy.spy().as('greeting')
11-
mount(<HelloWorld name="Test Aficionado" click={spy} />)
10+
cy.mount(<HelloWorld name="Test Aficionado" click={spy} />)
1211
})
1312

1413
it('shows link', () => {

cypress/integration/transpiled-spec.js

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,10 @@
11
import { Transpiled } from '../../src/transpiled.jsx'
22
import React from 'react'
3-
import { mount } from '../../lib'
43

54
/* eslint-env mocha */
65
describe('Transpiled', () => {
76
it('counts clicks', () => {
8-
mount(<Transpiled />)
7+
cy.mount(<Transpiled />)
98
cy.contains('count: 0')
109
.click()
1110
.contains('count: 1')
@@ -14,7 +13,7 @@ describe('Transpiled', () => {
1413
})
1514

1615
it('counts clicks 2', () => {
17-
mount(<Transpiled />)
16+
cy.mount(<Transpiled />)
1817
cy.contains('count: 0')
1918
.click()
2019
.contains('count: 1')
@@ -23,9 +22,9 @@ describe('Transpiled', () => {
2322
})
2423
})
2524

26-
describe('Counter mounted before each test', () => {
25+
describe('Counter cy.mounted before each test', () => {
2726
beforeEach(() => {
28-
mount(<Transpiled />)
27+
cy.mount(<Transpiled />)
2928
})
3029

3130
it('goes to 3', () => {
@@ -41,7 +40,8 @@ describe('Counter mounted before each test', () => {
4140
.click()
4241
.click()
4342
.click()
44-
Cypress.component().its('state')
43+
cy.get('@Component')
44+
.its('state')
4545
.should('deep.equal', {count: 3})
4646
})
4747
})

cypress/integration/users-spec.js

Lines changed: 44 additions & 37 deletions
Original file line numberDiff line numberDiff line change
@@ -1,49 +1,56 @@
11
import { Users } from '../../src/users.jsx'
22
import React from 'react'
3-
import { mount } from '../../lib'
43

54
/* eslint-env mocha */
6-
describe('Users', () => {
7-
beforeEach(() => {
8-
mount(<Users />)
5+
context('Users', () => {
6+
describe('Component', () => {
7+
it('fetches 3 users from remote API', () => {
8+
cy.mount(<Users />)
9+
cy.get('li').should('have.length', 3)
10+
})
911
})
1012

11-
it('fetches 3 users from remote API', () => {
12-
cy.get('li').should('have.length', 3)
13-
})
13+
describe('Network State', () => {
14+
beforeEach(() => {
15+
cy.server()
16+
// cy.mount the component after defining routes in tests
17+
// preventing race conditions where you wait on untouched routes
18+
})
1419

15-
it('can inspect real data in XHR', () => {
16-
cy.server()
17-
cy.route('/users?_limit=3').as('users')
18-
cy.wait('@users').its('response.body').should('have.length', 3)
19-
})
2020

21-
it('can display mock XHR response', () => {
22-
cy.server()
23-
const users = [{id: 1, name: 'foo'}]
24-
cy.route('GET', '/users?_limit=3', users).as('users')
25-
cy.get('li').should('have.length', 1)
26-
.first().contains('foo')
27-
})
21+
it('can inspect real data in XHR', () => {
22+
cy.route('/users?_limit=3').as('users')
23+
cy.mount(<Users />)
24+
cy.wait('@users').its('response.body').should('have.length', 3)
25+
})
2826

29-
it('can inspect mocked XHR', () => {
30-
cy.server()
31-
const users = [{id: 1, name: 'foo'}]
32-
cy.route('GET', '/users?_limit=3', users).as('users')
33-
cy.wait('@users').its('response.body').should('deep.equal', users)
34-
})
27+
it('can display mock XHR response', () => {
28+
const users = [{id: 1, name: 'foo'}]
29+
cy.route('GET', '/users?_limit=3', users).as('users')
30+
cy.mount(<Users />)
31+
cy.get('li').should('have.length', 1)
32+
.first().contains('foo')
33+
})
34+
35+
it('can inspect mocked XHR', () => {
36+
const users = [{id: 1, name: 'foo'}]
37+
cy.route('GET', '/users?_limit=3', users).as('users')
38+
cy.mount(<Users />)
39+
cy.wait('@users').its('response.body').should('deep.equal', users)
40+
})
3541

36-
it('can delay and wait on XHR', () => {
37-
cy.server()
38-
const users = [{id: 1, name: 'foo'}]
39-
cy.route({
40-
method: 'GET',
41-
url: '/users?_limit=3',
42-
response: users,
43-
delay: 1000
44-
}).as('users')
45-
cy.get('li').should('have.length', 0)
46-
cy.wait('@users')
47-
cy.get('li').should('have.length', 1)
42+
it('can delay and wait on XHR', () => {
43+
const users = [{id: 1, name: 'foo'}]
44+
cy.route({
45+
method: 'GET',
46+
url: '/users?_limit=3',
47+
response: users,
48+
delay: 1000
49+
}).as('users')
50+
cy.mount(<Users />)
51+
cy.get('li').should('have.length', 0)
52+
cy.wait('@users')
53+
cy.get('li').should('have.length', 1)
54+
})
4855
})
4956
})

0 commit comments

Comments
 (0)