-
-
Notifications
You must be signed in to change notification settings - Fork 1.1k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Add toHaveStyleRule to jest-emotion #662
Changes from 1 commit
f03be82
0dee3a8
ce19a6f
4812c7d
0731c57
3bb956a
6fcb827
b11af13
a82d6df
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,86 @@ | ||
import chalk from 'chalk' | ||
import * as css from 'css' | ||
import { getClassNamesFromNodes } from './index' | ||
|
||
/* | ||
* 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) { | ||
let styles = '' | ||
// This could be done in a more efficient way | ||
// but it would be a breaking change to do so | ||
// because it would change the ordering of styles | ||
Object.keys(emotion.caches.registered).forEach(className => { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. personally I would replace There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I actually copied that block of code from index.js because it did what I needed. Agree it should be refactored 👍 |
||
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) { | ||
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 | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,71 @@ | ||
import React from 'react' | ||
import renderer from 'react-test-renderer' | ||
import * as enzyme from 'enzyme' | ||
import * as emotion from 'emotion' | ||
import { createSerializer, createMatchers } from '../src' | ||
|
||
expect.addSnapshotSerializer(createSerializer(emotion)) | ||
expect.extend(createMatchers(emotion)) | ||
|
||
describe('toHaveStyleRule', () => { | ||
const divStyle = emotion.css` | ||
color: red; | ||
` | ||
|
||
const svgStyle = emotion.css` | ||
width: 100%; | ||
` | ||
|
||
it('matches styles on the top-most node passed in', () => { | ||
const tree = renderer | ||
.create( | ||
<div className={divStyle}> | ||
<svg className={svgStyle} /> | ||
</div> | ||
) | ||
.toJSON() | ||
|
||
expect(tree).toHaveStyleRule('color', 'red') | ||
expect(tree).not.toHaveStyleRule('width', '100%') | ||
|
||
const svgNode = tree.children[0] | ||
|
||
expect(svgNode).toHaveStyleRule('width', '100%') | ||
expect(svgNode).not.toHaveStyleRule('color', 'red') | ||
}) | ||
|
||
it('supports asymmetric matchers', () => { | ||
const tree = renderer | ||
.create( | ||
<div className={divStyle}> | ||
<svg className={svgStyle} /> | ||
</div> | ||
) | ||
.toJSON() | ||
|
||
expect(tree).toHaveStyleRule('color', expect.anything()) | ||
expect(tree).not.toHaveStyleRule('padding', expect.anything()) | ||
|
||
const svgNode = tree.children[0] | ||
|
||
expect(svgNode).toHaveStyleRule('width', expect.stringMatching(/.*%$/)) | ||
}) | ||
|
||
it('supports enzyme render methods', () => { | ||
const Component = () => ( | ||
<div className={divStyle}> | ||
<svg className={svgStyle} /> | ||
</div> | ||
) | ||
const enzymeMethods = ['shallow'] | ||
|
||
enzymeMethods.forEach(method => { | ||
const wrapper = enzyme[method](<Component />) | ||
expect(wrapper).toHaveStyleRule('color', 'red') | ||
expect(wrapper).not.toHaveStyleRule('width', '100%') | ||
const svgNode = wrapper.find('svg') | ||
expect(svgNode).toHaveStyleRule('width', '100%') | ||
expect(svgNode).not.toHaveStyleRule('color', 'red') | ||
}) | ||
}) | ||
}) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Could you move this into a utils file and import it from that file because exporting from here means it’s exported from the main package so people could start using it and if we wanted to change it we would have to do a major version