Skip to content

Commit

Permalink
Merge branch 'master' into next
Browse files Browse the repository at this point in the history
# Conflicts:
#	packages/jest-emotion/CHANGELOG.md
#	packages/jest-emotion/package.json
#	packages/jest-emotion/src/utils.js
  • Loading branch information
Andarist committed Nov 29, 2019
2 parents c6d2b80 + 1eda973 commit c5e91b0
Show file tree
Hide file tree
Showing 6 changed files with 316 additions and 161 deletions.
6 changes: 6 additions & 0 deletions packages/jest-emotion/CHANGELOG.md
Expand Up @@ -47,6 +47,12 @@
- @emotion/core@11.0.0-next.0
- emotion@11.0.0-next.0

## 10.0.25

### Patch Changes

- [`858c6e70`](https://github.com/emotion-js/emotion/commit/858c6e70e2aa83d159dba00af16f1e34a6d93fd0) [#1648](https://github.com/emotion-js/emotion/pull/1648) Thanks [@ajs139](https://github.com/ajs139)! - Improve support for Enzyme's shallow rendering.

## 10.0.17

### Patch Changes
Expand Down
43 changes: 39 additions & 4 deletions packages/jest-emotion/src/index.js
Expand Up @@ -9,7 +9,8 @@ import {
isDOMElement,
getStylesFromClassNames,
getStyleElements,
getKeys
getKeys,
flatMap
} from './utils'

export { matchers } from './matchers'
Expand Down Expand Up @@ -69,14 +70,50 @@ function filterEmotionProps(props = {}) {
return rest
}

function hasIntersection(left: any[], right: any[]) {
return left.some(value => right.includes(value))
}

function isShallowEnzymeElement(element, classNames) {
const delimiter = ' '
let childClassNames = flatMap(element.children || [], ({ props = {} }) =>
(props.className || '').split(delimiter)
).filter(Boolean)

return !hasIntersection(classNames, childClassNames)
}

export function createSerializer({
classNameReplacer,
DOMElements = true
}: Options = {}) {
let cache = new WeakSet()
function print(val: *, printer: Function) {
let elements = getStyleElements()
let keys = getKeys(elements)
if (isEmotionCssPropEnzymeElement(val)) {
return val.children.map(printer).join('\n')
let cssClassNames = (val.props.css.name || '').split(' ')
let expectedClassNames = flatMap(cssClassNames, cssClassName =>
keys.map(key => `${key}-${cssClassName}`)
)
// if this is a shallow element, we need to manufacture the className
// since the underlying component is not rendered.
if (isShallowEnzymeElement(val, expectedClassNames)) {
let className = [val.props.className]
.concat(expectedClassNames)
.filter(Boolean)
.join(' ')
return printer({
...val,
props: filterEmotionProps({
...val.props,
className
}),
type: val.props.__EMOTION_TYPE_PLEASE_DO_NOT_USE__
})
} else {
return val.children.map(printer).join('\n')
}
}
if (isEmotionCssPropElementType(val)) {
return printer({
Expand All @@ -87,12 +124,10 @@ export function createSerializer({
}
const nodes = getNodes(val)
const classNames = getClassNamesFromNodes(nodes)
let elements = getStyleElements()
const styles = getPrettyStylesFromClassNames(classNames, elements)
nodes.forEach(cache.add, cache)
const printedVal = printer(val)
nodes.forEach(cache.delete, cache)
let keys = getKeys(elements)
return replaceClassNames(
classNames,
styles,
Expand Down
23 changes: 15 additions & 8 deletions packages/jest-emotion/src/utils.js
Expand Up @@ -4,7 +4,7 @@ function last(arr) {
return arr.length > 0 ? arr[arr.length - 1] : undefined
}

function flatMap(arr, iteratee) {
export function flatMap<T, S>(arr: T[], iteratee: (arg: T) => S[] | S): S[] {
return [].concat(...arr.map(iteratee))
}

Expand Down Expand Up @@ -37,14 +37,21 @@ 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
function findNodeWithClassName(node) {
// Find the first node with a className prop
const components = actualComponent.findWhere(isTagWithClassName)
const classes = components.length && components.first().prop('className')
const found = node.findWhere(isTagWithClassName)
return found.length ? found.first() : null
}

return getClassNames(selectors, classes)
function getClassNameProp(node) {
return (node && node.prop('className')) || ''
}

function getClassNamesFromEnzyme(selectors, node) {
// We need to dive in to get the className if we have a styled element from a shallow render
let isShallow = shouldDive(node)
let nodeWithClassName = findNodeWithClassName(isShallow ? node.dive() : node)
return getClassNames(selectors, getClassNameProp(nodeWithClassName))
}

function getClassNamesFromCheerio(selectors, node) {
Expand Down Expand Up @@ -152,7 +159,7 @@ export function getStylesFromClassNames(
let keyframes = {}
let styles = ''

flatMap(elements, getElementRules).forEach(rule => {
flatMap(elements, getElementRules).forEach((rule: string) => {
if (selectorPattern.test(rule)) {
styles += rule
}
Expand Down
125 changes: 99 additions & 26 deletions packages/jest-emotion/test/__snapshots__/react-enzyme.test.js.snap
@@ -1,6 +1,6 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP

exports[`enzyme mount test 1`] = `
exports[`enzyme mount basic 1`] = `
.emotion-0 {
background-color: red;
}
Expand All @@ -18,7 +18,7 @@ exports[`enzyme mount test 1`] = `
</Greeting>
`;

exports[`enzyme test with prop containing css element 1`] = `
exports[`enzyme mount with prop containing css element 1`] = `
.emotion-0 {
background-color: blue;
}
Expand Down Expand Up @@ -48,28 +48,7 @@ exports[`enzyme test with prop containing css element 1`] = `
</Greeting>
`;

exports[`enzyme test with prop containing css element in fragment 1`] = `
.emotion-0 {
background-color: blue;
}
.emotion-0 {
background-color: blue;
}
<div>
Array [
"x",
<div
className="emotion-0"
>
y
</div>,
]
</div>
`;

exports[`enzyme test with prop containing css element not at the top level 1`] = `
exports[`enzyme mount with prop containing css element not at the top level 1`] = `
.emotion-0 {
background-color: blue;
}
Expand Down Expand Up @@ -103,7 +82,7 @@ exports[`enzyme test with prop containing css element not at the top level 1`] =
</div>
`;

exports[`enzyme test with prop containing css element with other label 1`] = `
exports[`enzyme mount with prop containing css element with other label 1`] = `
.emotion-0 {
background-color: blue;
}
Expand Down Expand Up @@ -141,7 +120,7 @@ exports[`enzyme test with prop containing css element with other label 1`] = `
</Greeting>
`;

exports[`enzyme test with prop containing css element with other props 1`] = `
exports[`enzyme mount with prop containing css element with other props 1`] = `
.emotion-0 {
background-color: blue;
}
Expand Down Expand Up @@ -172,3 +151,97 @@ exports[`enzyme test with prop containing css element with other props 1`] = `
</div>
</Greeting>
`;

exports[`enzyme shallow basic 1`] = `
<div
className=""
css="unknown styles"
>
hello
</div>
`;

exports[`enzyme shallow with prop containing css element 1`] = `
<div>
<p
className=""
css="unknown styles"
>
Hello
</p>
World!
</div>
`;

exports[`enzyme shallow with prop containing css element not at the top level 1`] = `
<div>
<Greeting
content={
<p
css="unknown styles"
id="something"
>
Hello
</p>
}
>
World!
</Greeting>
</div>
`;

exports[`enzyme shallow with prop containing css element with other label 1`] = `
<Thing
content={
<div
css="unknown styles"
/>
}
>
<p
className=""
css="unknown styles"
id="something"
>
Hello
</p>
World!
</Thing>
`;

exports[`enzyme shallow with prop containing css element with other props 1`] = `
<div>
<p
className=""
css="unknown styles"
id="something"
>
Hello
</p>
World!
</div>
`;

exports[`enzyme with prop containing css element in fragment 1`] = `
.emotion-0 {
background-color: blue;
}
.emotion-0 {
background-color: blue;
}
<div>
Array [
"x",
<div
className="emotion-0"
>
y
</div>,
]
</div>
`;
51 changes: 38 additions & 13 deletions packages/jest-emotion/test/matchers.test.js
Expand Up @@ -19,8 +19,6 @@ describe('toHaveStyleRule', () => {
width: 100%;
`

const enzymeMethods = ['mount', 'render']

it('matches styles on the top-most node passed in', () => {
const tree = renderer
.create(
Expand Down Expand Up @@ -56,21 +54,49 @@ describe('toHaveStyleRule', () => {
expect(svgNode).toHaveStyleRule('width', expect.stringMatching(/.*%$/))
})

it('supports enzyme render methods', () => {
it('supports enzyme `mount` method', () => {
const Component = () => (
<div css={divStyle}>
<svg css={svgStyle} />
</div>
)

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')
})
const wrapper = enzyme.mount(<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')
})

it('supports enzyme `render` method', () => {
const Component = () => (
<div css={divStyle}>
<svg css={svgStyle} />
</div>
)

const wrapper = enzyme.render(<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')
})

it('supports enzyme `shallow` method', () => {
const Component = () => (
<div css={divStyle}>
<svg css={svgStyle} />
</div>
)

const wrapper = enzyme.shallow(<Component />)
expect(wrapper).toHaveStyleRule('color', 'red')
expect(wrapper).not.toHaveStyleRule('width', '100%')
const svgNode = wrapper.childAt(0)
expect(svgNode).toHaveStyleRule('width', '100%')
expect(svgNode).not.toHaveStyleRule('color', 'red')
})

// i think this isn't working because of forwardRef
Expand All @@ -81,8 +107,7 @@ describe('toHaveStyleRule', () => {
const Svg = styled('svg')`
width: 100%;
`

enzymeMethods.forEach(method => {
;['mount', 'render', 'shallow'].forEach(method => {
const wrapper = enzyme[method](
<Div>
<Svg />
Expand Down

0 comments on commit c5e91b0

Please sign in to comment.