Skip to content

Commit

Permalink
feat: Enhance toHaveStyle to accept JS as css (#196)
Browse files Browse the repository at this point in the history
  • Loading branch information
lourenci committed Jan 31, 2020
1 parent 3b3a3d3 commit 7921e4a
Show file tree
Hide file tree
Showing 5 changed files with 101 additions and 11 deletions.
22 changes: 17 additions & 5 deletions README.md
Expand Up @@ -46,7 +46,6 @@ clear to read and to maintain.
<!-- START doctoc generated TOC please keep comment here to allow auto update -->
<!-- DON'T EDIT THIS SECTION, INSTEAD RE-RUN doctoc TO UPDATE -->


- [Installation](#installation)
- [Usage](#usage)
- [Custom matchers](#custom-matchers)
Expand Down Expand Up @@ -642,7 +641,7 @@ expect(getByTestId('login-form')).toHaveFormValues({
### `toHaveStyle`

```typescript
toHaveStyle(css: string)
toHaveStyle(css: string | object)
```

This allows you to check if a certain element has some specific css properties
Expand All @@ -652,7 +651,10 @@ expected properties applied, not just some of them.
#### Examples

```html
<button data-testid="delete-button" style="display: none; color: red">
<button
data-testid="delete-button"
style="display: none; background-color: red"
>
Delete item
</button>
```
Expand All @@ -661,14 +663,23 @@ expected properties applied, not just some of them.
const button = getByTestId('delete-button')

expect(button).toHaveStyle('display: none')
expect(button).toHaveStyle({display: 'none'})
expect(button).toHaveStyle(`
color: red;
background-color: red;
display: none;
`)
expect(button).toHaveStyle({
backgroundColor: 'red',
display: 'none',
})
expect(button).not.toHaveStyle(`
color: blue;
background-color: blue;
display: none;
`)
expect(button).not.toHaveStyle({
backgroundColor: 'blue',
display: 'none',
})
```

This also works with rules that are applied to the element via a class name for
Expand Down Expand Up @@ -928,6 +939,7 @@ Thanks goes to these people ([emoji key][emojis]):

<!-- markdownlint-enable -->
<!-- prettier-ignore-end -->

<!-- ALL-CONTRIBUTORS-LIST:END -->

This project follows the [all-contributors][all-contributors] specification.
Expand Down
24 changes: 24 additions & 0 deletions src/__tests__/to-have-style.js
@@ -1,6 +1,7 @@
import {render} from './helpers/test-utils'
import document from './helpers/document'

// eslint-disable-next-line max-lines-per-function
describe('.toHaveStyle', () => {
test('handles positive test cases', () => {
const {container} = render(`
Expand Down Expand Up @@ -144,4 +145,27 @@ describe('.toHaveStyle', () => {
'whatever: anything',
)
})

test('handles styles as object', () => {
const {container} = render(`
<div class="label" style="background-color: blue; height: 100%">
Hello World
</div>
`)

expect(container.querySelector('.label')).toHaveStyle({
backgroundColor: 'blue',
})
expect(container.querySelector('.label')).toHaveStyle({
backgroundColor: 'blue',
height: '100%',
})
expect(container.querySelector('.label')).not.toHaveStyle({
backgroundColor: 'red',
height: '100%',
})
expect(container.querySelector('.label')).not.toHaveStyle({
whatever: 'anything',
})
})
})
41 changes: 40 additions & 1 deletion src/__tests__/utils.js
@@ -1,4 +1,9 @@
import {deprecate, checkHtmlElement, HtmlElementTypeError} from '../utils'
import {
deprecate,
checkHtmlElement,
HtmlElementTypeError,
parseJStoCSS,
} from '../utils'
import document from './helpers/document'

test('deprecate', () => {
Expand Down Expand Up @@ -77,3 +82,37 @@ describe('checkHtmlElement', () => {
}).toThrow(HtmlElementTypeError)
})
})

describe('parseJStoCSS', () => {
describe('when all the styles are valid', () => {
it('returns the JS parsed as CSS text', () => {
expect(
parseJStoCSS(document, {
backgroundColor: 'blue',
height: '100%',
}),
).toBe('background-color: blue; height: 100%;')
})
})

describe('when some style is invalid', () => {
it('returns the JS parsed as CSS text without the invalid style', () => {
expect(
parseJStoCSS(document, {
backgroundColor: 'blue',
whatever: 'anything',
}),
).toBe('background-color: blue;')
})
})

describe('when all the styles are invalid', () => {
it('returns an empty string', () => {
expect(
parseJStoCSS(document, {
whatever: 'anything',
}),
).toBe('')
})
})
})
18 changes: 13 additions & 5 deletions src/to-have-style.js
@@ -1,7 +1,7 @@
import {matcherHint} from 'jest-matcher-utils'
import jestDiff from 'jest-diff'
import chalk from 'chalk'
import {checkHtmlElement, parseCSS} from './utils'
import {checkHtmlElement, parseCSS, parseJStoCSS} from './utils'

function getStyleDeclaration(document, css) {
const styles = {}
Expand All @@ -17,9 +17,12 @@ function getStyleDeclaration(document, css) {
}

function isSubset(styles, computedStyle) {
return Object.entries(styles).every(
([prop, value]) =>
computedStyle.getPropertyValue(prop.toLowerCase()) === value,
return (
!!Object.keys(styles).length &&
Object.entries(styles).every(
([prop, value]) =>
computedStyle.getPropertyValue(prop.toLowerCase()) === value,
)
)
}

Expand Down Expand Up @@ -48,9 +51,14 @@ function expectedDiff(expected, computedStyles) {
return diffOutput.replace(`${chalk.red('+ Received')}\n`, '')
}

function getCSStoParse(document, css) {
return typeof css === 'object' ? parseJStoCSS(document, css) : css
}

export function toHaveStyle(htmlElement, css) {
checkHtmlElement(htmlElement, toHaveStyle, this)
const parsedCSS = parseCSS(css, toHaveStyle, this)
const cssToParse = getCSStoParse(htmlElement.ownerDocument, css)
const parsedCSS = parseCSS(cssToParse, toHaveStyle, this)
const {getComputedStyle} = htmlElement.ownerDocument.defaultView

const expected = getStyleDeclaration(htmlElement.ownerDocument, parsedCSS)
Expand Down
7 changes: 7 additions & 0 deletions src/utils.js
Expand Up @@ -192,6 +192,12 @@ function compareArraysAsSet(a, b) {
return undefined
}

function parseJStoCSS(document, css) {
const sandboxElement = document.createElement('div')
Object.assign(sandboxElement.style, css)
return sandboxElement.style.cssText
}

export {
HtmlElementTypeError,
checkHtmlElement,
Expand All @@ -203,4 +209,5 @@ export {
getTag,
getSingleElementValue,
compareArraysAsSet,
parseJStoCSS,
}

0 comments on commit 7921e4a

Please sign in to comment.