jest-styled-components
Advanced tools
Comparing version 5.0.1 to 6.0.0-0
@@ -7,2 +7,13 @@ # Changelog | ||
## [5.0.1](https://github.com/styled-components/jest-styled-components/compare/v5.0.0...v5.0.1) - 2018-04-01 | ||
### Fixed | ||
- [toHaveStyleRule] Allow spaces or no spaces in media queries (see | ||
[#128](https://github.com/styled-components/jest-styled-components/pull/128)). | ||
### Changed | ||
- Improve README (see | ||
[#127](https://github.com/styled-components/jest-styled-components/pull/127) | ||
[#131](https://github.com/styled-components/jest-styled-components/pull/131) | ||
[#132](https://github.com/styled-components/jest-styled-components/pull/132)). | ||
## [5.0.0](https://github.com/styled-components/jest-styled-components/compare/v4.10.0...v5.0.0) - 2018-02-24 | ||
@@ -9,0 +20,0 @@ ### Changed |
{ | ||
"name": "jest-styled-components", | ||
"version": "5.0.1", | ||
"version": "6.0.0-0", | ||
"description": "Jest utilities for Styled Components", | ||
@@ -5,0 +5,0 @@ "main": "./src/index.js", |
@@ -309,12 +309,30 @@ [![NPM version](https://img.shields.io/npm/v/jest-styled-components.svg)](https://www.npmjs.com/package/jest-styled-components) | ||
The `toHaveStyleRule` matcher is useful to test if a given rule is applied to a component. | ||
The first argument is the expected property, the second is the expected value (string or RegExp). | ||
The first argument is the expected property, the second is the expected value which can be a String, RegExp, Jest asymmetric matcher or `undefined`. | ||
```js | ||
test('it works', () => { | ||
const Button = styled.button` | ||
color: red; | ||
border: 0.05em solid ${props => props.transparent ? 'transparent' : 'black'}; | ||
cursor: ${props => !props.disabled && 'pointer'}; | ||
opacity: ${props => props.disabled && '.65'}; | ||
` | ||
test('it applies default styles', () => { | ||
const tree = renderer.create(<Button />).toJSON() | ||
expect(tree).toHaveStyleRule('color', 'red') | ||
expect(tree).toHaveStyleRule('border', '0.05em solid black') | ||
expect(tree).toHaveStyleRule('cursor', 'pointer') | ||
expect(tree).toHaveStyleRule('opacity', undefined) // equivalent of the following | ||
expect(tree).not.toHaveStyleRule('opacity', expect.any(String)) | ||
}) | ||
test('it applies styles according to passed props', () => { | ||
const tree = renderer.create(<Button disabled transparent />).toJSON() | ||
expect(tree).toHaveStyleRule('border', expect.stringContaining('transparent')) | ||
expect(tree).toHaveStyleRule('cursor', undefined) | ||
expect(tree).toHaveStyleRule('opacity', '.65') | ||
}) | ||
``` | ||
The matcher supports a third `options` parameter which makes it possible to search for rules nested within an [At-rule](https://developer.mozilla.org/en/docs/Web/CSS/At-rule) ([media](https://developer.mozilla.org/en-US/docs/Web/CSS/@media)) or to add modifiers to the class selector. This feature is supported in React only, and more options are coming soon. | ||
The matcher supports an optional third `options` parameter which makes it possible to search for rules nested within an [At-rule](https://developer.mozilla.org/en/docs/Web/CSS/At-rule) ([media](https://developer.mozilla.org/en-US/docs/Web/CSS/@media)) or to add modifiers to the class selector. This feature is supported in React only, and more options are coming soon. | ||
@@ -321,0 +339,0 @@ ```js |
@@ -0,11 +1,19 @@ | ||
interface AsymmetricMatcher { | ||
$$typeof: Symbol; | ||
sample?: string | RegExp | object | Array<any>, Function; | ||
} | ||
type Value = string | RegExp | AsymmetricMatcher | undefined | ||
interface Options { | ||
media?: string; | ||
modifier?: string; | ||
supports?: string; | ||
} | ||
declare namespace jest { | ||
interface Options { | ||
media?: string; | ||
modifier?: string; | ||
supports?: string; | ||
} | ||
interface Matchers<R> { | ||
toHaveStyleRule(property: string, value: string | RegExp, options?: Options): R; | ||
toHaveStyleRule(property: string, value: Value, options?: Options): R; | ||
} | ||
} |
@@ -1,2 +0,4 @@ | ||
function toHaveStyleRule(component, name, expected) { | ||
const { matcherTest, buildReturnMessage } = require('../utils') | ||
function toHaveStyleRule(component, property, expected) { | ||
const styles = component.props.style.filter(x => x) | ||
@@ -7,3 +9,3 @@ | ||
*/ | ||
const camelCasedName = name.replace(/-(\w)/, (_, match) => | ||
const camelCasedProperty = property.replace(/-(\w)/, (_, match) => | ||
match.toUpperCase() | ||
@@ -17,35 +19,11 @@ ) | ||
const mergedStyles = styles.reduce((acc, item) => ({ ...acc, ...item }), {}) | ||
const received = mergedStyles[camelCasedName] | ||
const pass = received === expected | ||
const received = mergedStyles[camelCasedProperty] | ||
const pass = matcherTest(received, expected) | ||
if (!received) { | ||
const error = `${name} isn't in the style rules` | ||
return { | ||
message: () => | ||
`${this.utils.matcherHint('.toHaveStyleRule')}\n\n` + | ||
`Expected ${component.type} to have a style rule:\n` + | ||
` ${this.utils.printExpected(`${name}: ${expected}`)}\n` + | ||
'Received:\n' + | ||
` ${this.utils.printReceived(error)}`, | ||
pass: false, | ||
} | ||
return { | ||
pass, | ||
message: buildReturnMessage(this.utils, pass, property, received, expected), | ||
} | ||
const diff = | ||
'' + | ||
` ${this.utils.printExpected(`${name}: ${expected}`)}\n` + | ||
'Received:\n' + | ||
` ${this.utils.printReceived(`${name}: ${received}`)}` | ||
const message = pass | ||
? () => | ||
`${this.utils.matcherHint('.not.toHaveStyleRule')}\n\n` + | ||
`Expected ${component.type} not to contain:\n${diff}` | ||
: () => | ||
`${this.utils.matcherHint('.toHaveStyleRule')}\n\n` + | ||
`Expected ${component.type} to have a style rule:\n${diff}` | ||
return { message, pass } | ||
} | ||
module.exports = toHaveStyleRule |
@@ -1,2 +0,2 @@ | ||
const { getCSS } = require('./utils') | ||
const { getCSS, matcherTest, buildReturnMessage } = require('./utils') | ||
@@ -52,3 +52,3 @@ const shouldDive = node => | ||
if (modifier.includes('&')) { | ||
modifier = modifier.replace('&', classNameSelector) | ||
modifier = modifier.replace(/&/g, classNameSelector) | ||
} else { | ||
@@ -80,5 +80,10 @@ prefix += classNameSelector | ||
const die = (utils, property) => ({ | ||
const handleMissingRules = options => ({ | ||
pass: false, | ||
message: () => `Property not found: ${utils.printReceived(property)}`, | ||
message: () => | ||
`No style rules found on passed Component${ | ||
Object.keys(options).length | ||
? ` using options:\n${JSON.stringify(options)}` | ||
: '' | ||
}`, | ||
}) | ||
@@ -97,37 +102,27 @@ | ||
const normalizeModifierOption = modifier => | ||
Array.isArray(modifier) ? modifier.join('') : modifier | ||
/* eslint-disable prettier/prettier */ | ||
const normalizeOptions = ({ modifier, ...options }) => | ||
modifier | ||
? Object.assign(options, { | ||
modifier: Array.isArray(modifier) ? modifier.join('') : modifier, | ||
}) | ||
: options | ||
/* eslint-enable prettier/prettier */ | ||
function toHaveStyleRule(received, property, value, options = {}) { | ||
const classNames = getClassNames(received) | ||
function toHaveStyleRule(component, property, expected, options = {}) { | ||
const ast = getCSS() | ||
options.modifier = normalizeModifierOption(options.modifier) | ||
const rules = getRules(ast, classNames, options) | ||
const classNames = getClassNames(component) | ||
const normalizedOptions = normalizeOptions(options) | ||
const rules = getRules(ast, classNames, normalizedOptions) | ||
if (!rules.length) { | ||
return die(this.utils, property) | ||
} | ||
if (!rules.length) return handleMissingRules(normalizedOptions) | ||
const declarations = getDeclarations(rules, property) | ||
const declaration = declarations.pop() || {} | ||
const received = declaration.value | ||
const pass = matcherTest(received, expected) | ||
if (!declarations.length) { | ||
return die(this.utils, property) | ||
} | ||
const declaration = declarations.pop() | ||
const pass = | ||
value instanceof RegExp | ||
? value.test(declaration.value) | ||
: value === declaration.value | ||
const message = () => | ||
`Expected ${property}${pass ? ' not ' : ' '}to match:\n` + | ||
` ${this.utils.printExpected(value)}\n` + | ||
'Received:\n' + | ||
` ${this.utils.printReceived(declaration.value)}` | ||
return { | ||
pass, | ||
message, | ||
message: buildReturnMessage(this.utils, pass, property, received, expected), | ||
} | ||
@@ -134,0 +129,0 @@ } |
@@ -61,2 +61,28 @@ const css = require('css') | ||
const buildReturnMessage = (utils, pass, property, received, expected) => () => | ||
`${utils.printReceived( | ||
!received && !pass | ||
? `Property '${property}' not found in style rules` | ||
: `Value mismatch for property '${property}'` | ||
)}\n\n` + | ||
'Expected\n' + | ||
` ${utils.printExpected(`${property}: ${expected}`)}\n` + | ||
'Received:\n' + | ||
` ${utils.printReceived(`${property}: ${received}`)}` | ||
const matcherTest = (received, expected) => { | ||
try { | ||
const matcher = | ||
expected === undefined || | ||
expected.$$typeof === Symbol.for('jest.asymmetricMatcher') | ||
? expected | ||
: expect.stringMatching(expected) | ||
expect(received).toEqual(matcher) | ||
return true | ||
} catch (error) { | ||
return false | ||
} | ||
} | ||
module.exports = { | ||
@@ -66,2 +92,4 @@ resetStyleSheet, | ||
getHashes, | ||
buildReturnMessage, | ||
matcherTest, | ||
} |
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
No v1
QualityPackage is not semver >=1. This means it is not stable and does not support ^ ranges.
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
42671
316
415
2