Skip to content

Commit 7921e4a

Browse files
authored
feat: Enhance toHaveStyle to accept JS as css (#196)
1 parent 3b3a3d3 commit 7921e4a

File tree

5 files changed

+101
-11
lines changed

5 files changed

+101
-11
lines changed

README.md

Lines changed: 17 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -46,7 +46,6 @@ clear to read and to maintain.
4646
<!-- START doctoc generated TOC please keep comment here to allow auto update -->
4747
<!-- DON'T EDIT THIS SECTION, INSTEAD RE-RUN doctoc TO UPDATE -->
4848

49-
5049
- [Installation](#installation)
5150
- [Usage](#usage)
5251
- [Custom matchers](#custom-matchers)
@@ -642,7 +641,7 @@ expect(getByTestId('login-form')).toHaveFormValues({
642641
### `toHaveStyle`
643642

644643
```typescript
645-
toHaveStyle(css: string)
644+
toHaveStyle(css: string | object)
646645
```
647646

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

654653
```html
655-
<button data-testid="delete-button" style="display: none; color: red">
654+
<button
655+
data-testid="delete-button"
656+
style="display: none; background-color: red"
657+
>
656658
Delete item
657659
</button>
658660
```
@@ -661,14 +663,23 @@ expected properties applied, not just some of them.
661663
const button = getByTestId('delete-button')
662664

663665
expect(button).toHaveStyle('display: none')
666+
expect(button).toHaveStyle({display: 'none'})
664667
expect(button).toHaveStyle(`
665-
color: red;
668+
background-color: red;
666669
display: none;
667670
`)
671+
expect(button).toHaveStyle({
672+
backgroundColor: 'red',
673+
display: 'none',
674+
})
668675
expect(button).not.toHaveStyle(`
669-
color: blue;
676+
background-color: blue;
670677
display: none;
671678
`)
679+
expect(button).not.toHaveStyle({
680+
backgroundColor: 'blue',
681+
display: 'none',
682+
})
672683
```
673684

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

929940
<!-- markdownlint-enable -->
930941
<!-- prettier-ignore-end -->
942+
931943
<!-- ALL-CONTRIBUTORS-LIST:END -->
932944

933945
This project follows the [all-contributors][all-contributors] specification.

src/__tests__/to-have-style.js

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
import {render} from './helpers/test-utils'
22
import document from './helpers/document'
33

4+
// eslint-disable-next-line max-lines-per-function
45
describe('.toHaveStyle', () => {
56
test('handles positive test cases', () => {
67
const {container} = render(`
@@ -144,4 +145,27 @@ describe('.toHaveStyle', () => {
144145
'whatever: anything',
145146
)
146147
})
148+
149+
test('handles styles as object', () => {
150+
const {container} = render(`
151+
<div class="label" style="background-color: blue; height: 100%">
152+
Hello World
153+
</div>
154+
`)
155+
156+
expect(container.querySelector('.label')).toHaveStyle({
157+
backgroundColor: 'blue',
158+
})
159+
expect(container.querySelector('.label')).toHaveStyle({
160+
backgroundColor: 'blue',
161+
height: '100%',
162+
})
163+
expect(container.querySelector('.label')).not.toHaveStyle({
164+
backgroundColor: 'red',
165+
height: '100%',
166+
})
167+
expect(container.querySelector('.label')).not.toHaveStyle({
168+
whatever: 'anything',
169+
})
170+
})
147171
})

src/__tests__/utils.js

Lines changed: 40 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,9 @@
1-
import {deprecate, checkHtmlElement, HtmlElementTypeError} from '../utils'
1+
import {
2+
deprecate,
3+
checkHtmlElement,
4+
HtmlElementTypeError,
5+
parseJStoCSS,
6+
} from '../utils'
27
import document from './helpers/document'
38

49
test('deprecate', () => {
@@ -77,3 +82,37 @@ describe('checkHtmlElement', () => {
7782
}).toThrow(HtmlElementTypeError)
7883
})
7984
})
85+
86+
describe('parseJStoCSS', () => {
87+
describe('when all the styles are valid', () => {
88+
it('returns the JS parsed as CSS text', () => {
89+
expect(
90+
parseJStoCSS(document, {
91+
backgroundColor: 'blue',
92+
height: '100%',
93+
}),
94+
).toBe('background-color: blue; height: 100%;')
95+
})
96+
})
97+
98+
describe('when some style is invalid', () => {
99+
it('returns the JS parsed as CSS text without the invalid style', () => {
100+
expect(
101+
parseJStoCSS(document, {
102+
backgroundColor: 'blue',
103+
whatever: 'anything',
104+
}),
105+
).toBe('background-color: blue;')
106+
})
107+
})
108+
109+
describe('when all the styles are invalid', () => {
110+
it('returns an empty string', () => {
111+
expect(
112+
parseJStoCSS(document, {
113+
whatever: 'anything',
114+
}),
115+
).toBe('')
116+
})
117+
})
118+
})

src/to-have-style.js

Lines changed: 13 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
import {matcherHint} from 'jest-matcher-utils'
22
import jestDiff from 'jest-diff'
33
import chalk from 'chalk'
4-
import {checkHtmlElement, parseCSS} from './utils'
4+
import {checkHtmlElement, parseCSS, parseJStoCSS} from './utils'
55

66
function getStyleDeclaration(document, css) {
77
const styles = {}
@@ -17,9 +17,12 @@ function getStyleDeclaration(document, css) {
1717
}
1818

1919
function isSubset(styles, computedStyle) {
20-
return Object.entries(styles).every(
21-
([prop, value]) =>
22-
computedStyle.getPropertyValue(prop.toLowerCase()) === value,
20+
return (
21+
!!Object.keys(styles).length &&
22+
Object.entries(styles).every(
23+
([prop, value]) =>
24+
computedStyle.getPropertyValue(prop.toLowerCase()) === value,
25+
)
2326
)
2427
}
2528

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

54+
function getCSStoParse(document, css) {
55+
return typeof css === 'object' ? parseJStoCSS(document, css) : css
56+
}
57+
5158
export function toHaveStyle(htmlElement, css) {
5259
checkHtmlElement(htmlElement, toHaveStyle, this)
53-
const parsedCSS = parseCSS(css, toHaveStyle, this)
60+
const cssToParse = getCSStoParse(htmlElement.ownerDocument, css)
61+
const parsedCSS = parseCSS(cssToParse, toHaveStyle, this)
5462
const {getComputedStyle} = htmlElement.ownerDocument.defaultView
5563

5664
const expected = getStyleDeclaration(htmlElement.ownerDocument, parsedCSS)

src/utils.js

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -192,6 +192,12 @@ function compareArraysAsSet(a, b) {
192192
return undefined
193193
}
194194

195+
function parseJStoCSS(document, css) {
196+
const sandboxElement = document.createElement('div')
197+
Object.assign(sandboxElement.style, css)
198+
return sandboxElement.style.cssText
199+
}
200+
195201
export {
196202
HtmlElementTypeError,
197203
checkHtmlElement,
@@ -203,4 +209,5 @@ export {
203209
getTag,
204210
getSingleElementValue,
205211
compareArraysAsSet,
212+
parseJStoCSS,
206213
}

0 commit comments

Comments
 (0)