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

Commit fbd07b1

Browse files
committed
feat: support cssFile style
1 parent 796d248 commit fbd07b1

File tree

4 files changed

+87
-48
lines changed

4 files changed

+87
-48
lines changed

README.md

Lines changed: 28 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -4,9 +4,9 @@
44
55
## TLDR
66

7-
* What is this? This package allows you to use [Cypress](https://www.cypress.io/) test runner to unit test your React components with zero effort.
7+
- What is this? This package allows you to use [Cypress](https://www.cypress.io/) test runner to unit test your React components with zero effort.
88

9-
* How is this different from [Enzyme](https://github.com/airbnb/enzyme)? It is similar in functionality BUT runs the component in the real browser with full power of Cypress E2E test runner: [live GUI, full API, screen recording, CI support, cross-platform](https://www.cypress.io/features/).
9+
- How is this different from [Enzyme](https://github.com/airbnb/enzyme)? It is similar in functionality BUT runs the component in the real browser with full power of Cypress E2E test runner: [live GUI, full API, screen recording, CI support, cross-platform](https://www.cypress.io/features/).
1010

1111
## Known problems
1212

@@ -50,8 +50,7 @@ describe('HelloState component', () => {
5050
cy.contains('Hello Spider-man!')
5151
// mounted component can be selected via its name, function, or JSX
5252
// e.g. '@HelloState', HelloState, or <HelloState />
53-
cy.get(HelloState)
54-
.invoke('setState', { name: 'React' })
53+
cy.get(HelloState).invoke('setState', { name: 'React' })
5554
cy.get(HelloState)
5655
.its('state')
5756
.should('deep.equal', { name: 'React' })
@@ -81,9 +80,12 @@ it('can be passed as an option', () => {
8180
color: white;
8281
}
8382
`
84-
cy.mount(<Button name='Orange' orange />, null, { style })
85-
cy.get('.orange button')
86-
.should('have.css', 'background-color', 'rgb(245, 146, 62)')
83+
cy.mount(<Button name="Orange" orange />, { style })
84+
cy.get('.orange button').should(
85+
'have.css',
86+
'background-color',
87+
'rgb(245, 146, 62)',
88+
)
8789
})
8890
```
8991

@@ -98,17 +100,16 @@ import './styles.css'
98100
You can read the CSS file and pass it as `style` option yourself
99101

100102
```js
101-
cy.readFile('cypress/integration/Button.css')
102-
.then(style => {
103-
cy.mount(<Button name='Orange' orange />, null, { style })
104-
})
103+
cy.readFile('cypress/integration/Button.css').then(style => {
104+
cy.mount(<Button name="Orange" orange />, { style })
105+
})
105106
```
106107

107108
You can even let Cypress read the file and inject the style
108109

109110
```js
110111
const cssFile = 'cypress/integration/Button.css'
111-
cy.mount(<Button name='Orange' orange />, null, { cssFile })
112+
cy.mount(<Button name="Orange" orange />, { cssFile })
112113
```
113114

114115
See [cypress/integration/inject-style-spec.js](cypress/integration/inject-style-spec.js) for more examples.
@@ -136,18 +137,18 @@ How can we use features that require transpilation? By using [@cypress/webpack-p
136137

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

139-
* [hello-world-spec.js](cypress/integration/hello-world-spec.js) - testing the simplest React component from [hello-world.jsx](src/hello-world.jsx)
140-
* [hello-x-spec.js](cypress/integration/hello-x-spec.js) - testing React component with props and state [hello-x.jsx](src/hello-x.jsx)
141-
* [counter-spec.js](cypress/integration/counter-spec.js) clicks on the component and confirms the result
142-
* [stateless-spec.js](cypress/integration/stateless-spec.js) shows testing a stateless component from [stateless.jsx](src/stateless.jsx)
143-
* [transpiled-spec.js](cypress/integration/stateless-spec.js) shows testing a component with class properties syntax from [transpiled.jsx](src/stateless.jsx)
144-
* [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)
145-
* [users-spec.js](cypress/integration/users-spec.js) shows how to observe XHR requests, mock server responses for component [users.jsx](src/users.jsx)
146-
* [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)
140+
- [hello-world-spec.js](cypress/integration/hello-world-spec.js) - testing the simplest React component from [hello-world.jsx](src/hello-world.jsx)
141+
- [hello-x-spec.js](cypress/integration/hello-x-spec.js) - testing React component with props and state [hello-x.jsx](src/hello-x.jsx)
142+
- [counter-spec.js](cypress/integration/counter-spec.js) clicks on the component and confirms the result
143+
- [stateless-spec.js](cypress/integration/stateless-spec.js) shows testing a stateless component from [stateless.jsx](src/stateless.jsx)
144+
- [transpiled-spec.js](cypress/integration/stateless-spec.js) shows testing a component with class properties syntax from [transpiled.jsx](src/stateless.jsx)
145+
- [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)
146+
- [users-spec.js](cypress/integration/users-spec.js) shows how to observe XHR requests, mock server responses for component [users.jsx](src/users.jsx)
147+
- [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)
147148

148149
## Large examples
149150

150-
* [bahmutov/calculator](https://github.com/bahmutov/calculator) tests multiple components: calculator App, Button, Display.
151+
- [bahmutov/calculator](https://github.com/bahmutov/calculator) tests multiple components: calculator App, Button, Display.
151152

152153
## Development
153154

@@ -171,12 +172,12 @@ Uses [Percy.io](https://percy.io) visual diffing service as a GitHub pull reques
171172

172173
Same feature for unit testing components from other frameworks using Cypress
173174

174-
* [cypress-vue-unit-test](https://github.com/bahmutov/cypress-vue-unit-test)
175-
* [cypress-cycle-unit-test](https://github.com/bahmutov/cypress-cycle-unit-test)
176-
* [cypress-svelte-unit-test](https://github.com/bahmutov/cypress-svelte-unit-test)
177-
* [cypress-angular-unit-test](https://github.com/bahmutov/cypress-angular-unit-test)
178-
* [cypress-hyperapp-unit-test](https://github.com/bahmutov/cypress-hyperapp-unit-test)
179-
* [cypress-angularjs-unit-test](https://github.com/bahmutov/cypress-angularjs-unit-test)
175+
- [cypress-vue-unit-test](https://github.com/bahmutov/cypress-vue-unit-test)
176+
- [cypress-cycle-unit-test](https://github.com/bahmutov/cypress-cycle-unit-test)
177+
- [cypress-svelte-unit-test](https://github.com/bahmutov/cypress-svelte-unit-test)
178+
- [cypress-angular-unit-test](https://github.com/bahmutov/cypress-angular-unit-test)
179+
- [cypress-hyperapp-unit-test](https://github.com/bahmutov/cypress-hyperapp-unit-test)
180+
- [cypress-angularjs-unit-test](https://github.com/bahmutov/cypress-angularjs-unit-test)
180181

181182
[renovate-badge]: https://img.shields.io/badge/renovate-app-blue.svg
182183
[renovate-app]: https://renovateapp.com/
Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
/// <reference types="cypress" />
2+
import React from 'react'
3+
import { mount } from 'cypress-react-unit-test'
4+
5+
describe('cssFile', () => {
6+
it('is loaded', () => {
7+
const Component = () => <button className="green">Green button</button>
8+
mount(<Component />, {
9+
cssFile: 'cypress/component/component tests/basic/css-file/index.css',
10+
})
11+
12+
cy.get('button')
13+
.should('have.class', 'green')
14+
.and('have.css', 'background-color', 'rgb(0, 255, 0)')
15+
})
16+
})
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
button.green {
2+
background-color: #00ff00;
3+
}

lib/index.ts

Lines changed: 40 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,45 @@ interface MountOptions {
1616
ReactDom?: typeof ReactDOM
1717
stylesheets?: string | string[]
1818
style?: string
19+
cssFile?: string
20+
}
21+
22+
/**
23+
* Inject custom style text or CSS file or 3rd party style resources
24+
*/
25+
const injectStyles = (options: MountOptions) => () => {
26+
const document = cy.state('document')
27+
28+
const el = document.getElementById('cypress-jsdom')
29+
30+
// insert any custom global styles before the component
31+
if (typeof options.stylesheets === 'string') {
32+
options.stylesheets = [options.stylesheets]
33+
}
34+
if (Array.isArray(options.stylesheets)) {
35+
// console.log('adding stylesheets')
36+
options.stylesheets.forEach(href => {
37+
const link = document.createElement('link')
38+
link.type = 'text/css'
39+
link.rel = 'stylesheet'
40+
link.href = href
41+
document.body.insertBefore(link, el)
42+
})
43+
}
44+
45+
if (options.style) {
46+
const style = document.createElement('style')
47+
style.appendChild(document.createTextNode(options.style))
48+
document.body.insertBefore(style, el)
49+
}
50+
51+
if (options.cssFile) {
52+
return cy.readFile(options.cssFile).then(css => {
53+
const style = document.createElement('style')
54+
style.appendChild(document.createTextNode(css))
55+
document.body.appendChild(style)
56+
})
57+
}
1958
}
2059

2160
/**
@@ -55,33 +94,13 @@ export const mount = (jsx: React.ReactElement, options: MountOptions = {}) => {
5594
},
5695
})
5796
})
97+
.then(injectStyles(options))
5898
.then(() => {
5999
const document = cy.state('document')
60100
const reactDomToUse = options.ReactDom || ReactDOM
61101

62102
const el = document.getElementById('cypress-jsdom')
63103

64-
// insert any custom global styles before the component
65-
if (typeof options.stylesheets === 'string') {
66-
options.stylesheets = [options.stylesheets]
67-
}
68-
if (Array.isArray(options.stylesheets)) {
69-
console.log('adding stylesheets')
70-
options.stylesheets.forEach(href => {
71-
const link = document.createElement('link')
72-
link.type = 'text/css'
73-
link.rel = 'stylesheet'
74-
link.href = href
75-
document.body.insertBefore(link, el)
76-
})
77-
}
78-
79-
if (options.style) {
80-
const style = document.createElement('style')
81-
style.appendChild(document.createTextNode(options.style))
82-
document.body.insertBefore(style, el)
83-
}
84-
85104
const props = {
86105
// @ts-ignore provide unique key to the the wrapped component to make sure we are rerendering between tests
87106
key: Cypress?.mocha?.getRunner()?.test?.title || Math.random(),

0 commit comments

Comments
 (0)