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

Commit

Permalink
feat: add style option
Browse files Browse the repository at this point in the history
  • Loading branch information
bahmutov committed Apr 2, 2020
1 parent a7916c3 commit 72799e2
Show file tree
Hide file tree
Showing 4 changed files with 134 additions and 2 deletions.
41 changes: 41 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -63,6 +63,47 @@ describe('HelloState component', () => {

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

### styles

You can add individual style to the mounted component by passing its text as an option

```js
it('can be passed as an option', () => {
const style = `
.component-button {
display: inline-flex;
width: 25%;
flex: 1 0 auto;
}
.component-button.orange button {
background-color: #F5923E;
color: white;
}
`
cy.mount(<Button name='Orange' orange />, null, { style })
cy.get('.orange button')
.should('have.css', 'background-color', 'rgb(245, 146, 62)')
})
```

Often your component rely on global CSS style imported from the root `index.js` or `app.js` file

```js
// index.js
import './styles.css'
// bootstrap application
```

You can read the CSS file and pass it as `style` option yourself

```js
cy.readFile('cypress/integration/Button.css')
.then(style => {
cy.mount(<Button name='Orange' orange />, null, { style })
})
```

## Configuration

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:
Expand Down
78 changes: 78 additions & 0 deletions cypress/integration/inject-style-spec.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,78 @@
/// <reference types="cypress" />
import React from 'react'

class Button extends React.Component {
handleClick () {
this.props.clickHandler(this.props.name)
}

render () {
const className = [
'component-button',
this.props.orange ? 'orange' : '',
this.props.wide ? 'wide' : ''
]

return (
<div className={className.join(' ').trim()}>
<button onClick={this.handleClick.bind(this)}>{this.props.name}</button>
</div>
)
}
}

describe('Injecting style', () => {
it('can be passed as an option', () => {
const style = `
.component-button {
display: inline-flex;
width: 25%;
flex: 1 0 auto;
}
.component-button.orange button {
background-color: #F5923E;
color: white;
}
`
cy.mount(<Button name='Orange' orange />, null, { style })
cy.get('.orange button')
.should('have.css', 'background-color', 'rgb(245, 146, 62)')
})

it('read CSS file and pass as style', () => {
cy.readFile('cypress/integration/Button.css')
.then(style => {
cy.mount(<Button name='Orange' orange />, null, { style })
})

cy.get('.component-button')
.should('have.class', 'orange')
.find('button')
.should('have.css', 'background-color', 'rgb(245, 146, 62)')
})

context('read CSS file once', () => {
before(() => {
// .as('style') will save the loaded CSS text
// in the text context property "style"
cy.readFile('cypress/integration/Button.css').as('style')
})

it('is orange', function () {
// notice we use "function () {}" callback
// to get the test context "this" to be able to use "this.style"
cy.mount(<Button name='Orange' orange />, null, { style: this.style })

cy.get('.orange button')
.should('have.css', 'background-color', 'rgb(245, 146, 62)')
})

it('is orange again', function () {
cy.mount(<Button name='Orange' orange />, null, { style: this.style })

cy.get('.orange button')
.should('have.css', 'background-color', 'rgb(245, 146, 62)')
})
})
})
6 changes: 5 additions & 1 deletion lib/index.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,10 @@ interface JSXElement {
props: object
}

interface MountOptions {
style: string
}

declare namespace Cypress {
interface Cypress {
stylesCache: any
Expand Down Expand Up @@ -58,7 +62,7 @@ declare namespace Cypress {
cy.get(Hello)
```
**/
mount: (component: JSXElement, alias?: string) => Chainable<void>
mount: (component: JSXElement, alias?: string, options?: Partial<MountOptions>) => Chainable<void>
get<S = any>(alias: string | symbol | Function, options?: Partial<Loggable & Timeoutable>): Chainable<any>
}
}
11 changes: 10 additions & 1 deletion lib/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -87,6 +87,14 @@ Cypress.Commands.add('copyComponentStyles', component => {
})
})

const injectStyle = (options?: Partial<MountOptions>) => (w: Window) => {
if (options && options.style) {
const style = w.document.createElement('style')
style.appendChild(document.createTextNode(options.style))
w.document.body.appendChild(style)
}
}

/**
* Mount a React component in a blank document; register it as an alias
* To access: use an alias or original component reference
Expand All @@ -106,7 +114,7 @@ Cypress.Commands.add('copyComponentStyles', component => {
cy.get(Hello)
```
**/
export const mount = (jsx: JSXElement, alias?: string) => {
export const mount = (jsx: JSXElement, alias?: string, options?: Partial<MountOptions>) => {
// Get the display name property via the component constructor
const jsxType = typeof jsx.type === 'string' ? jsx as unknown as JSX : jsx.type
const displayname = getDisplayName(jsxType, alias)
Expand All @@ -127,6 +135,7 @@ export const mount = (jsx: JSXElement, alias?: string) => {
}
})
})
.then(injectStyle(options))
.then(setXMLHttpRequest)
.then(setAlert)
.then(win => {
Expand Down

0 comments on commit 72799e2

Please sign in to comment.