diff --git a/README.md b/README.md index 3814609c..cc704f15 100644 --- a/README.md +++ b/README.md @@ -46,7 +46,6 @@ clear to read and to maintain. - - [Installation](#installation) - [Usage](#usage) - [Custom matchers](#custom-matchers) @@ -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 @@ -652,7 +651,10 @@ expected properties applied, not just some of them. #### Examples ```html - ``` @@ -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 @@ -928,6 +939,7 @@ Thanks goes to these people ([emoji key][emojis]): + This project follows the [all-contributors][all-contributors] specification. diff --git a/src/__tests__/to-have-style.js b/src/__tests__/to-have-style.js index 0771bd48..0c313550 100644 --- a/src/__tests__/to-have-style.js +++ b/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(` @@ -144,4 +145,27 @@ describe('.toHaveStyle', () => { 'whatever: anything', ) }) + + test('handles styles as object', () => { + const {container} = render(` +
+ Hello World +
+ `) + + 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', + }) + }) }) diff --git a/src/__tests__/utils.js b/src/__tests__/utils.js index 6410808b..5eae7f20 100644 --- a/src/__tests__/utils.js +++ b/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', () => { @@ -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('') + }) + }) +}) diff --git a/src/to-have-style.js b/src/to-have-style.js index e81303ea..3e8a155b 100644 --- a/src/to-have-style.js +++ b/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 = {} @@ -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, + ) ) } @@ -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) diff --git a/src/utils.js b/src/utils.js index 1647fd29..c07a3037 100644 --- a/src/utils.js +++ b/src/utils.js @@ -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, @@ -203,4 +209,5 @@ export { getTag, getSingleElementValue, compareArraysAsSet, + parseJStoCSS, }