-
-
Notifications
You must be signed in to change notification settings - Fork 1.1k
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Add toHaveStyleRule to jest-emotion (#662)
* Support react-test-renderer and enzyme shallow render methods * extract utils * support enzyme.mount * support enzyme.render * support styled-components in all enzyme methods * tidy and document * add more tests * Add flow to a file and add chalk as a dep of jest-emotion
- Loading branch information
Showing
8 changed files
with
327 additions
and
38 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,84 @@ | ||
// @flow | ||
import chalk from 'chalk' | ||
import * as css from 'css' | ||
import { getClassNamesFromNodes } from './utils' | ||
import type { Emotion } from 'create-emotion' | ||
|
||
/* | ||
* Taken from | ||
* https://github.com/facebook/jest/blob/be4bec387d90ac8d6a7596be88bf8e4994bc3ed9/packages/expect/src/jasmine_utils.js#L234 | ||
*/ | ||
function isA(typeName, value) { | ||
return Object.prototype.toString.apply(value) === `[object ${typeName}]` | ||
} | ||
|
||
/* | ||
* Taken from | ||
* https://github.com/facebook/jest/blob/be4bec387d90ac8d6a7596be88bf8e4994bc3ed9/packages/expect/src/jasmine_utils.js#L36 | ||
*/ | ||
function isAsymmetric(obj) { | ||
return obj && isA('Function', obj.asymmetricMatch) | ||
} | ||
|
||
function valueMatches(declaration, value) { | ||
if (value instanceof RegExp) { | ||
return value.test(declaration.value) | ||
} | ||
|
||
if (isAsymmetric(value)) { | ||
return value.asymmetricMatch(declaration.value) | ||
} | ||
|
||
return value === declaration.value | ||
} | ||
|
||
function getStylesFromClassNames(classNames: Array<string>, emotion) { | ||
return Object.keys(emotion.caches.registered).reduce((styles, className) => { | ||
let indexOfClassName = classNames.indexOf(className) | ||
if (indexOfClassName !== -1) { | ||
let nameWithoutKey = classNames[indexOfClassName].substring( | ||
emotion.caches.key.length + 1 | ||
) | ||
// $FlowFixMe | ||
styles += emotion.caches.inserted[nameWithoutKey] | ||
} | ||
return styles | ||
}, '') | ||
} | ||
|
||
export function createMatchers(emotion: Emotion) { | ||
function toHaveStyleRule(received: *, property: *, value: *) { | ||
const selectors = getClassNamesFromNodes([received]) | ||
const cssString = getStylesFromClassNames(selectors, emotion) | ||
const styles = css.parse(cssString) | ||
|
||
const declaration = styles.stylesheet.rules | ||
.reduce((decs, rule) => Object.assign([], decs, rule.declarations), []) | ||
.filter(dec => dec.type === 'declaration' && dec.property === property) | ||
.pop() | ||
|
||
if (!declaration) { | ||
return { | ||
pass: false, | ||
message: () => `Property not found: ${property}` | ||
} | ||
} | ||
|
||
const pass = valueMatches(declaration, value) | ||
|
||
const message = () => | ||
`Expected ${property}${pass ? ' not ' : ' '}to match:\n` + | ||
` ${chalk.green(value)}\n` + | ||
'Received:\n' + | ||
` ${chalk.red(declaration.value)}` | ||
|
||
return { | ||
pass, | ||
message | ||
} | ||
} | ||
|
||
return { | ||
toHaveStyleRule | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,73 @@ | ||
// @flow | ||
|
||
function getClassNames(selectors, classes) { | ||
return classes ? selectors.concat(classes.split(' ')) : selectors | ||
} | ||
|
||
function getClassNamesFromTestRenderer(selectors, node) { | ||
const props = node.props | ||
return getClassNames(selectors, props.className || props.class) | ||
} | ||
|
||
function shouldDive(node) { | ||
return typeof node.dive === 'function' && typeof node.type() !== 'string' | ||
} | ||
|
||
function isTagWithClassName(node) { | ||
return node.prop('className') && typeof node.type() === 'string' | ||
} | ||
|
||
function getClassNamesFromEnzyme(selectors, node) { | ||
// We need to dive if we have selected a styled child from a shallow render | ||
const actualComponent = shouldDive(node) ? node.dive() : node | ||
// Find the first node with a className prop | ||
const components = actualComponent.findWhere(isTagWithClassName) | ||
const classes = components.length && components.first().prop('className') | ||
|
||
return getClassNames(selectors, classes) | ||
} | ||
|
||
function getClassNamesFromCheerio(selectors, node) { | ||
const classes = node.attr('class') | ||
return getClassNames(selectors, classes) | ||
} | ||
|
||
function getClassNamesFromDOMElement(selectors, node: any) { | ||
return getClassNames(selectors, node.getAttribute('class')) | ||
} | ||
|
||
export function isReactElement(val: any): boolean { | ||
return val.$$typeof === Symbol.for('react.test.json') | ||
} | ||
|
||
const domElementPattern = /^((HTML|SVG)\w*)?Element$/ | ||
|
||
export function isDOMElement(val: any): boolean { | ||
return ( | ||
val.nodeType === 1 && | ||
val.constructor && | ||
val.constructor.name && | ||
domElementPattern.test(val.constructor.name) | ||
) | ||
} | ||
|
||
function isEnzymeElement(val: any): boolean { | ||
return typeof val.findWhere === 'function' | ||
} | ||
|
||
function isCheerioElement(val: any): boolean { | ||
return val.cheerio === '[cheerio object]' | ||
} | ||
|
||
export function getClassNamesFromNodes(nodes: Array<any>) { | ||
return nodes.reduce((selectors, node) => { | ||
if (isReactElement(node)) { | ||
return getClassNamesFromTestRenderer(selectors, node) | ||
} else if (isEnzymeElement(node)) { | ||
return getClassNamesFromEnzyme(selectors, node) | ||
} else if (isCheerioElement(node)) { | ||
return getClassNamesFromCheerio(selectors, node) | ||
} | ||
return getClassNamesFromDOMElement(selectors, node) | ||
}, []) | ||
} |
15 changes: 15 additions & 0 deletions
15
packages/jest-emotion/test/__snapshots__/matchers.test.js.snap
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,15 @@ | ||
// Jest Snapshot v1, https://goo.gl/fbAQLP | ||
|
||
exports[`toHaveStyleRule returns a message explaining the failure 1`] = ` | ||
"Expected color to match: | ||
[32mblue[39m | ||
Received: | ||
[31mred[39m" | ||
`; | ||
|
||
exports[`toHaveStyleRule returns a message explaining the failure 2`] = ` | ||
"Expected color not to match: | ||
[32mred[39m | ||
Received: | ||
[31mred[39m" | ||
`; |
Oops, something went wrong.