Skip to content

Commit

Permalink
fix: #49: update-tohavestyle-to-match-cssstyledeclaration (#57)
Browse files Browse the repository at this point in the history
* #49: Updated toHaveStyle to match CSSStyleDeclaration

* #49: added border test
  • Loading branch information
smacpherson64 committed Sep 18, 2018
1 parent 9c3ea4e commit 042db4d
Show file tree
Hide file tree
Showing 3 changed files with 136 additions and 66 deletions.
121 changes: 80 additions & 41 deletions src/__tests__/to-have-style.js
@@ -1,59 +1,98 @@
import {render} from './helpers/test-utils'

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

const style = document.createElement('style')
style.innerHTML = `
.label {
background-color: black;
color: white;
float: left;
}
`
document.body.appendChild(style)
document.body.appendChild(container)

expect(container.querySelector('.label')).toHaveStyle(`
height: 100%;
color: white;
background-color: blue;
`)

expect(container.querySelector('.label')).toHaveStyle(`
background-color: blue;
color: white;
`)
expect(container.querySelector('.label')).toHaveStyle(
'background-color:blue;color:white',
)

expect(container.querySelector('.label')).not.toHaveStyle(`
color: white;
font-weight: bold;
`)
})

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

const style = document.createElement('style')
style.innerHTML = `
const style = document.createElement('style')
style.innerHTML = `
.label {
background-color: black;
color: white;
float: left;
}
`
document.body.appendChild(style)
document.body.appendChild(container)
document.body.appendChild(style)
document.body.appendChild(container)

expect(container.querySelector('.label')).toHaveStyle(`
height: 100%;
color: white;
background-color: blue;
`)
expect(() =>
expect(container.querySelector('.label')).toHaveStyle(
'font-weight: bold',
),
).toThrowError()
expect(() =>
expect(container.querySelector('.label')).not.toHaveStyle('color: white'),
).toThrowError()

expect(container.querySelector('.label')).toHaveStyle(`
background-color: blue;
color: white;
`)
expect(container.querySelector('.label')).toHaveStyle(
'background-color:blue;color:white',
)
// Make sure the test fails if the css syntax is not valid
expect(() =>
expect(container.querySelector('.label')).not.toHaveStyle(
'font-weight bold',
),
).toThrowError()
expect(() =>
expect(container.querySelector('.label')).toHaveStyle('color white'),
).toThrowError()

expect(container.querySelector('.label')).not.toHaveStyle(`
color: white;
font-weight: bold;
`)
document.body.removeChild(style)
document.body.removeChild(container)
})

expect(() =>
expect(container.querySelector('.label')).toHaveStyle('font-weight: bold'),
).toThrowError()
expect(() =>
expect(container.querySelector('.label')).not.toHaveStyle('color: white'),
).toThrowError()

// Make sure the test fails if the css syntax is not valid
expect(() =>
expect(container.querySelector('.label')).not.toHaveStyle(
'font-weight bold',
),
).toThrowError()
expect(() =>
expect(container.querySelector('.label')).toHaveStyle('color white'),
).toThrowError()

document.body.removeChild(style)
document.body.removeChild(container)
test('properly normalizes colors', () => {
const {queryByTestId} = render(`
<span data-testid="color-example" style="background-color: #123456">Hello World</span>
`)
expect(queryByTestId('color-example')).toHaveStyle(
'background-color: #123456',
)
})

test('properly normalizes colors for border', () => {
const {queryByTestId} = render(`
<span data-testid="color-example" style="border: 1px solid #fff">Hello World</span>
`)
expect(queryByTestId('color-example')).toHaveStyle('border: 1px solid #fff')
})
})
38 changes: 14 additions & 24 deletions src/to-have-style.js
@@ -1,24 +1,17 @@
import {parse} from 'css'
import {matcherHint} from 'jest-matcher-utils'
import jestDiff from 'jest-diff'
import chalk from 'chalk'
import {checkHtmlElement} from './utils'
import {checkHtmlElement, checkValidCSS} from './utils'

function parseCSS(css) {
const ast = parse(`selector { ${css} }`, {silent: true}).stylesheet
if (ast.parsingErrors && ast.parsingErrors.length > 0) {
const {reason, line, column} = ast.parsingErrors[0]
return {
parsingError: `Syntax error parsing expected css: ${reason} in ${line}:${column}`,
}
}
const parsedRules = ast.rules[0].declarations
.filter(d => d.type === 'declaration')
.reduce(
(obj, {property, value}) => Object.assign(obj, {[property]: value}),
{},
)
return {parsedRules}
function getStyleDeclaration(css) {
const copy = document.createElement('div')
copy.style = css
const styles = copy.style

return Array.from(styles).reduce(
(acc, name) => ({...acc, [name]: styles[name]}),
{},
)
}

function isSubset(styles, computedStyle) {
Expand Down Expand Up @@ -55,14 +48,11 @@ function expectedDiff(expected, computedStyles) {

export function toHaveStyle(htmlElement, css) {
checkHtmlElement(htmlElement, toHaveStyle, this)
const {parsedRules: expected, parsingError} = parseCSS(css)
if (parsingError) {
return {
pass: this.isNot, // Fail regardless of the test being positive or negative
message: () => parsingError,
}
}
checkValidCSS(css, toHaveStyle, this)

const expected = getStyleDeclaration(css)
const received = getComputedStyle(htmlElement)

return {
pass: isSubset(expected, received),
message: () => {
Expand Down
43 changes: 42 additions & 1 deletion src/utils.js
Expand Up @@ -7,6 +7,7 @@ import {
printReceived,
stringify,
} from 'jest-matcher-utils'
import {parse} from 'css'

class HtmlElementTypeError extends Error {
constructor(received, matcherFn, context) {
Expand Down Expand Up @@ -40,6 +41,39 @@ function checkHtmlElement(htmlElement, ...args) {
}
}

class InvalidCSSError extends Error {
constructor(received, matcherFn) {
super()

/* istanbul ignore next */
if (Error.captureStackTrace) {
Error.captureStackTrace(this, matcherFn)
}
this.message = [
received.message,
'',
receivedColor(`Failing css:`),
receivedColor(`${received.css}`),
].join('\n')
}
}

function checkValidCSS(css, ...args) {
const ast = parse(`selector { ${css} }`, {silent: true}).stylesheet

if (ast.parsingErrors && ast.parsingErrors.length > 0) {
const {reason, line} = ast.parsingErrors[0]

throw new InvalidCSSError(
{
css,
message: `Syntax error parsing expected css: ${reason} on line: ${line}`,
},
...args,
)
}
}

class InvalidDocumentError extends Error {
constructor(message, matcherFn) {
super()
Expand Down Expand Up @@ -108,4 +142,11 @@ function deprecate(name, replacementText) {
)
}

export {checkDocumentKey, checkHtmlElement, deprecate, getMessage, matches}
export {
checkDocumentKey,
checkHtmlElement,
checkValidCSS,
deprecate,
getMessage,
matches,
}

0 comments on commit 042db4d

Please sign in to comment.