jest-emotion
Advanced tools
Comparing version 9.1.3 to 9.2.0
105
lib/index.js
'use strict'; | ||
exports.__esModule = true; | ||
exports.createMatchers = undefined; | ||
var _matchers = require('./matchers'); | ||
Object.defineProperty(exports, 'createMatchers', { | ||
enumerable: true, | ||
get: function get() { | ||
return _matchers.createMatchers; | ||
} | ||
}); | ||
exports.getStyles = getStyles; | ||
@@ -13,2 +23,4 @@ exports.createSerializer = createSerializer; | ||
var _utils = require('./utils'); | ||
function _interopRequireWildcard(obj) { if (obj && obj.__esModule) { return obj; } else { var newObj = {}; if (obj != null) { for (var key in obj) { if (Object.prototype.hasOwnProperty.call(obj, key)) newObj[key] = obj[key]; } } newObj.default = obj; return newObj; } } | ||
@@ -45,29 +57,2 @@ | ||
function getSelectorsFromClasses(selectors, classes) { | ||
return classes ? selectors.concat(classes.split(' ').map(function (c) { | ||
return '.' + c; | ||
})) : selectors; | ||
} | ||
function getSelectorsFromProps(selectors, props) { | ||
return getSelectorsFromClasses(selectors, props.className || props.class); | ||
} | ||
function getSelectorsForDOMElement(selectors, node) { | ||
return getSelectorsFromClasses(selectors, node.getAttribute('class')); | ||
} | ||
function getSelectors(nodes) { | ||
return nodes.reduce(function (selectors, node) { | ||
return isReactElement(node) ? getSelectorsFromProps(selectors, node.props) : getSelectorsForDOMElement(selectors, node); | ||
}, []); | ||
} | ||
function filterChildSelector(baseSelector) { | ||
if (baseSelector.slice(-1) === '>') { | ||
return baseSelector.slice(0, -1); | ||
} | ||
return baseSelector; | ||
} | ||
function getStyles(emotion) { | ||
@@ -82,12 +67,2 @@ return Object.keys(emotion.caches.inserted).reduce(function (style, current) { | ||
function isReactElement(val) { | ||
return val.$$typeof === Symbol.for('react.test.json'); | ||
} | ||
var domElementPattern = /^((HTML|SVG)\w*)?Element$/; | ||
function isDOMElement(val) { | ||
return val.nodeType === 1 && val.constructor && val.constructor.name && domElementPattern.test(val.constructor.name); | ||
} | ||
function createSerializer(emotion) { | ||
@@ -102,10 +77,10 @@ var _ref2 = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {}, | ||
markNodes(nodes); | ||
var selectors = getSelectors(nodes); | ||
var styles = getStylesFromSelectors(selectors); | ||
var classNames = (0, _utils.getClassNamesFromNodes)(nodes); | ||
var styles = getStylesFromClassNames(classNames); | ||
var printedVal = printer(val); | ||
return (0, _replaceClassNames.replaceClassNames)(selectors, styles, printedVal, emotion.caches.key, classNameReplacer); | ||
return (0, _replaceClassNames.replaceClassNames)(classNames, styles, printedVal, emotion.caches.key, classNameReplacer); | ||
} | ||
function test(val) { | ||
return val && !val.withEmotionStyles && (DOMElements ? isReactElement(val) || isDOMElement(val) : isReactElement(val)); | ||
return val && !val.withEmotionStyles && (DOMElements ? (0, _utils.isReactElement)(val) || (0, _utils.isDOMElement)(val) : (0, _utils.isReactElement)(val)); | ||
} | ||
@@ -119,37 +94,23 @@ | ||
function getStylesFromSelectors(nodeSelectors) { | ||
var styles = getStyles(emotion); | ||
var ast = void 0; | ||
function getStylesFromClassNames(classNames) { | ||
var 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(function (className) { | ||
var indexOfClassName = classNames.indexOf(className); | ||
if (indexOfClassName !== -1) { | ||
var nameWithoutKey = classNames[indexOfClassName].substring(emotion.caches.key.length + 1); | ||
// $FlowFixMe | ||
styles += emotion.caches.inserted[nameWithoutKey]; | ||
} | ||
}); | ||
var prettyStyles = void 0; | ||
try { | ||
ast = css.parse(styles); | ||
prettyStyles = css.stringify(css.parse(styles)); | ||
} catch (e) { | ||
console.error(e); | ||
throw new Error('There was an error parsing css in jest-emotion-react: "' + styles + '"'); | ||
throw new Error('There was an error parsing css in jest-emotion: "' + styles + '"'); | ||
} | ||
ast.stylesheet.rules = ast.stylesheet.rules.reduce(reduceRules, []); | ||
var ret = css.stringify(ast); | ||
return ret; | ||
function reduceRules(rules, rule) { | ||
var shouldIncludeRule = false; | ||
if (rule.type === 'rule') { | ||
shouldIncludeRule = rule.selectors.some(function (selector) { | ||
var baseSelector = filterChildSelector(selector.split(/:| |\./).filter(function (s) { | ||
return !!s; | ||
})[0]); | ||
return nodeSelectors.some(function (sel) { | ||
return sel === baseSelector || sel === '.' + baseSelector; | ||
}); | ||
}); | ||
} | ||
if (rule.type === 'media' || rule.type === 'supports') { | ||
rule.rules = rule.rules.reduce(reduceRules, []); | ||
if (rule.rules.length) { | ||
shouldIncludeRule = true; | ||
} | ||
} | ||
return shouldIncludeRule ? rules.concat(rule) : rules; | ||
} | ||
return prettyStyles; | ||
} | ||
@@ -156,0 +117,0 @@ |
@@ -8,13 +8,12 @@ 'use strict'; | ||
var componentSelectorClassNamePattern = /\.e[a-zA-Z0-9-]+[0-9]+/; | ||
var componentSelectorClassNamePattern = /e[a-zA-Z0-9-]+[0-9]+/; | ||
var replaceClassNames = exports.replaceClassNames = function replaceClassNames(selectors, styles, code, key) { | ||
var replaceClassNames = exports.replaceClassNames = function replaceClassNames(classNames, styles, code, key) { | ||
var replacer = arguments.length > 4 && arguments[4] !== undefined ? arguments[4] : defaultClassNameReplacer; | ||
var index = 0; | ||
var classRegex = new RegExp('^\\.' + key + '-([a-zA-Z0-9-]+)'); | ||
return selectors.reduce(function (acc, className) { | ||
if (classRegex.test(className) || componentSelectorClassNamePattern.test(className)) { | ||
var escapedRegex = new RegExp(className.replace('.', '').replace(/[-[\]{}()*+?.,\\^$|#\s]/g, '\\$&'), 'g'); | ||
return classNames.reduce(function (acc, className) { | ||
if (className.indexOf(key + '-') === 0 || componentSelectorClassNamePattern.test(className)) { | ||
var escapedRegex = new RegExp(className.replace(/[-[\]{}()*+?.,\\^$|#\s]/g, '\\$&'), 'g'); | ||
return acc.replace(escapedRegex, replacer(className, index++)); | ||
@@ -21,0 +20,0 @@ } |
{ | ||
"name": "jest-emotion", | ||
"version": "9.1.3", | ||
"version": "9.2.0", | ||
"description": "Jest utilities for emotion", | ||
@@ -17,2 +17,3 @@ "main": "lib/index.js", | ||
"dependencies": { | ||
"chalk": "^2.4.1", | ||
"css": "^2.2.1" | ||
@@ -19,0 +20,0 @@ }, |
@@ -92,4 +92,32 @@ # jest-emotion | ||
# Custom matchers | ||
## toHaveStyleRule | ||
To make more explicit assertions when testing your styled components you can use the `toHaveStyleRule` matcher. | ||
```jsx | ||
import React from 'react' | ||
import renderer from 'react-test-renderer' | ||
import { createMatchers } from 'jest-emotion' | ||
import * as emotion from 'emotion' | ||
import styled from 'react-emotion' | ||
// Add the custom matchers provided by 'jest-emotion' | ||
expect.extend(createMatchers(emotion)) | ||
test('renders with correct styles', () => { | ||
const H1 = styled.h1` | ||
float: left; | ||
` | ||
const tree = renderer.create(<H1>hello world</H1>).toJSON() | ||
expect(tree).toHaveStyleRule('float', 'left') | ||
expect(tree).not.toHaveStyleRule('color', 'hotpink') | ||
}) | ||
``` | ||
## Thanks | ||
Thanks to [Kent C. Dodds](https://twitter.com/kentcdodds) who wrote [jest-glamor-react](https://github.com/kentcdodds/jest-glamor-react) which this library is largely based on. |
105
src/index.js
@@ -7,4 +7,7 @@ // @flow | ||
} from './replace-class-names' | ||
import { getClassNamesFromNodes, isReactElement, isDOMElement } from './utils' | ||
import type { Emotion } from 'create-emotion' | ||
export { createMatchers } from './matchers' | ||
type Options = { | ||
@@ -29,33 +32,2 @@ classNameReplacer: ClassNameReplacer, | ||
function getSelectorsFromClasses(selectors, classes) { | ||
return classes | ||
? selectors.concat(classes.split(' ').map(c => `.${c}`)) | ||
: selectors | ||
} | ||
function getSelectorsFromProps(selectors, props) { | ||
return getSelectorsFromClasses(selectors, props.className || props.class) | ||
} | ||
function getSelectorsForDOMElement(selectors, node) { | ||
return getSelectorsFromClasses(selectors, node.getAttribute('class')) | ||
} | ||
function getSelectors(nodes) { | ||
return nodes.reduce( | ||
(selectors, node) => | ||
isReactElement(node) | ||
? getSelectorsFromProps(selectors, node.props) | ||
: getSelectorsForDOMElement(selectors, node), | ||
[] | ||
) | ||
} | ||
function filterChildSelector(baseSelector) { | ||
if (baseSelector.slice(-1) === '>') { | ||
return baseSelector.slice(0, -1) | ||
} | ||
return baseSelector | ||
} | ||
export function getStyles(emotion: Emotion) { | ||
@@ -70,17 +42,2 @@ return Object.keys(emotion.caches.inserted).reduce((style, current) => { | ||
function isReactElement(val) { | ||
return val.$$typeof === Symbol.for('react.test.json') | ||
} | ||
const domElementPattern = /^((HTML|SVG)\w*)?Element$/ | ||
function isDOMElement(val) { | ||
return ( | ||
val.nodeType === 1 && | ||
val.constructor && | ||
val.constructor.name && | ||
domElementPattern.test(val.constructor.name) | ||
) | ||
} | ||
export function createSerializer( | ||
@@ -93,7 +50,7 @@ emotion: Emotion, | ||
markNodes(nodes) | ||
const selectors = getSelectors(nodes) | ||
const styles = getStylesFromSelectors(selectors) | ||
const classNames = getClassNamesFromNodes(nodes) | ||
const styles = getStylesFromClassNames(classNames) | ||
const printedVal = printer(val) | ||
return replaceClassNames( | ||
selectors, | ||
classNames, | ||
styles, | ||
@@ -122,39 +79,27 @@ printedVal, | ||
function getStylesFromSelectors(nodeSelectors) { | ||
const styles = getStyles(emotion) | ||
let ast | ||
function getStylesFromClassNames(classNames: Array<string>) { | ||
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 => { | ||
let indexOfClassName = classNames.indexOf(className) | ||
if (indexOfClassName !== -1) { | ||
let nameWithoutKey = classNames[indexOfClassName].substring( | ||
emotion.caches.key.length + 1 | ||
) | ||
// $FlowFixMe | ||
styles += emotion.caches.inserted[nameWithoutKey] | ||
} | ||
}) | ||
let prettyStyles | ||
try { | ||
ast = css.parse(styles) | ||
prettyStyles = css.stringify(css.parse(styles)) | ||
} catch (e) { | ||
console.error(e) | ||
throw new Error( | ||
`There was an error parsing css in jest-emotion-react: "${styles}"` | ||
`There was an error parsing css in jest-emotion: "${styles}"` | ||
) | ||
} | ||
ast.stylesheet.rules = ast.stylesheet.rules.reduce(reduceRules, []) | ||
const ret = css.stringify(ast) | ||
return ret | ||
function reduceRules(rules, rule) { | ||
let shouldIncludeRule = false | ||
if (rule.type === 'rule') { | ||
shouldIncludeRule = rule.selectors.some(selector => { | ||
const baseSelector = filterChildSelector( | ||
selector.split(/:| |\./).filter(s => !!s)[0] | ||
) | ||
return nodeSelectors.some( | ||
sel => sel === baseSelector || sel === `.${baseSelector}` | ||
) | ||
}) | ||
} | ||
if (rule.type === 'media' || rule.type === 'supports') { | ||
rule.rules = rule.rules.reduce(reduceRules, []) | ||
if (rule.rules.length) { | ||
shouldIncludeRule = true | ||
} | ||
} | ||
return shouldIncludeRule ? rules.concat(rule) : rules | ||
} | ||
return prettyStyles | ||
} | ||
@@ -161,0 +106,0 @@ |
@@ -8,6 +8,6 @@ // @flow | ||
const componentSelectorClassNamePattern = /\.e[a-zA-Z0-9-]+[0-9]+/ | ||
const componentSelectorClassNamePattern = /e[a-zA-Z0-9-]+[0-9]+/ | ||
export const replaceClassNames = ( | ||
selectors: Array<string>, | ||
classNames: Array<string>, | ||
styles: string, | ||
@@ -19,11 +19,10 @@ code: string, | ||
let index = 0 | ||
const classRegex = new RegExp(`^\\.${key}-([a-zA-Z0-9-]+)`) | ||
return selectors.reduce((acc, className) => { | ||
return classNames.reduce((acc, className) => { | ||
if ( | ||
classRegex.test(className) || | ||
className.indexOf(`${key}-`) === 0 || | ||
componentSelectorClassNamePattern.test(className) | ||
) { | ||
const escapedRegex = new RegExp( | ||
className.replace('.', '').replace(/[-[\]{}()*+?.,\\^$|#\s]/g, '\\$&'), | ||
className.replace(/[-[\]{}()*+?.,\\^$|#\s]/g, '\\$&'), | ||
'g' | ||
@@ -30,0 +29,0 @@ ) |
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
Major refactor
Supply chain riskPackage has recently undergone a major refactor. It may be unstable or indicate significant internal changes. Use caution when updating to versions that include significant changes.
Found 1 instance in 1 package
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
21774
10
488
123
2
1
+ Addedchalk@^2.4.1
+ Addedansi-styles@3.2.1(transitive)
+ Addedchalk@2.4.2(transitive)
+ Addedcolor-convert@1.9.3(transitive)
+ Addedcolor-name@1.1.3(transitive)
+ Addedescape-string-regexp@1.0.5(transitive)
+ Addedhas-flag@3.0.0(transitive)
+ Addedsupports-color@5.5.0(transitive)