diff --git a/.changeset/great-bears-sing.md b/.changeset/great-bears-sing.md deleted file mode 100644 index 5b0db5bda..000000000 --- a/.changeset/great-bears-sing.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -'jest-emotion': patch ---- - -Fix printing names of nested shallow-rendered components. diff --git a/.changeset/thin-suits-pull.md b/.changeset/thin-suits-pull.md new file mode 100644 index 000000000..88f48a53b --- /dev/null +++ b/.changeset/thin-suits-pull.md @@ -0,0 +1,5 @@ +--- +'jest-emotion': patch +--- + +Revert improved support for Enzyme's shallow rendering - its release was an unforseen breaking change. diff --git a/packages/jest-emotion/src/index.js b/packages/jest-emotion/src/index.js index 165c74328..cbfaefa98 100644 --- a/packages/jest-emotion/src/index.js +++ b/packages/jest-emotion/src/index.js @@ -9,8 +9,7 @@ import { isDOMElement, getStylesFromClassNames, getStyleElements, - getKeys, - flatMap + getKeys } from './utils' export { matchers } from './matchers' @@ -70,54 +69,14 @@ 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)) { - 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(' ') - let emotionType = val.props.__EMOTION_TYPE_PLEASE_DO_NOT_USE__ - // emotionType will be a string for DOM elements - let type = - typeof emotionType === 'string' ? emotionType : emotionType.name - return printer({ - ...val, - props: filterEmotionProps({ - ...val.props, - className - }), - type - }) - } else { - return val.children.map(printer).join('\n') - } + return val.children.map(printer).join('\n') } if (isEmotionCssPropElementType(val)) { return printer({ @@ -128,10 +87,12 @@ 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, diff --git a/packages/jest-emotion/src/utils.js b/packages/jest-emotion/src/utils.js index 374c10f79..37ec2be08 100644 --- a/packages/jest-emotion/src/utils.js +++ b/packages/jest-emotion/src/utils.js @@ -1,6 +1,6 @@ // @flow -export function flatMap(arr: T[], iteratee: (arg: T) => S[] | S): S[] { +function flatMap(arr, iteratee) { return [].concat(...arr.map(iteratee)) } @@ -25,23 +25,14 @@ function isTagWithClassName(node) { return node.prop('className') && typeof node.type() === 'string' } -function findNodeWithClassName(node) { +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 found = node.findWhere(isTagWithClassName) - return found.length ? found.first() : null -} - -function getClassNameProp(node) { - return (node && node.prop('className')) || '' -} + const components = actualComponent.findWhere(isTagWithClassName) + const classes = components.length && components.first().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().dive() : node - ) - return getClassNames(selectors, getClassNameProp(nodeWithClassName)) + return getClassNames(selectors, classes) } function getClassNamesFromCheerio(selectors, node) { @@ -142,7 +133,7 @@ export function getStylesFromClassNames( let keyframes = {} let styles = '' - flatMap(elements, getElementRules).forEach((rule: string) => { + flatMap(elements, getElementRules).forEach(rule => { if (selectorPattern.test(rule)) { styles += rule } diff --git a/packages/jest-emotion/test/__snapshots__/react-enzyme.test.js.snap b/packages/jest-emotion/test/__snapshots__/react-enzyme.test.js.snap index bae6a9548..bedfd8a7c 100644 --- a/packages/jest-emotion/test/__snapshots__/react-enzyme.test.js.snap +++ b/packages/jest-emotion/test/__snapshots__/react-enzyme.test.js.snap @@ -1,6 +1,6 @@ // Jest Snapshot v1, https://goo.gl/fbAQLP -exports[`enzyme mount basic 1`] = ` +exports[`enzyme mount test 1`] = ` .emotion-0 { background-color: red; } @@ -18,29 +18,7 @@ exports[`enzyme mount basic 1`] = ` `; -exports[`enzyme mount nested 1`] = ` -.emotion-0 { - background-color: red; -} - -.emotion-0 { - background-color: red; -} - -
- -
- Hello -
-
-
-`; - -exports[`enzyme mount with prop containing css element 1`] = ` +exports[`enzyme test with prop containing css element 1`] = ` .emotion-0 { background-color: blue; } @@ -70,7 +48,28 @@ exports[`enzyme mount with prop containing css element 1`] = ` `; -exports[`enzyme mount with prop containing css element not at the top level 1`] = ` +exports[`enzyme test with prop containing css element in fragment 1`] = ` +.emotion-0 { + background-color: blue; +} + +.emotion-0 { + background-color: blue; +} + +
+ Array [ + "x", +
+ y +
, + ] +
+`; + +exports[`enzyme test with prop containing css element not at the top level 1`] = ` .emotion-0 { background-color: blue; } @@ -104,7 +103,7 @@ exports[`enzyme mount with prop containing css element not at the top level 1`] `; -exports[`enzyme mount with prop containing css element with other label 1`] = ` +exports[`enzyme test with prop containing css element with other label 1`] = ` .emotion-0 { background-color: blue; } @@ -142,7 +141,7 @@ exports[`enzyme mount with prop containing css element with other label 1`] = ` `; -exports[`enzyme mount with prop containing css element with other props 1`] = ` +exports[`enzyme test with prop containing css element with other props 1`] = ` .emotion-0 { background-color: blue; } @@ -173,108 +172,3 @@ exports[`enzyme mount with prop containing css element with other props 1`] = ` `; - -exports[`enzyme shallow basic 1`] = ` -
- hello -
-`; - -exports[`enzyme shallow nested 1`] = ` -
- - Hello - -
-`; - -exports[`enzyme shallow with prop containing css element 1`] = ` -
-

- Hello -

- - World! -
-`; - -exports[`enzyme shallow with prop containing css element not at the top level 1`] = ` -
- - Hello -

- } - > - World! -
-
-`; - -exports[`enzyme shallow with prop containing css element with other label 1`] = ` - - } -> -

- Hello -

- - World! -
-`; - -exports[`enzyme shallow with prop containing css element with other props 1`] = ` -
-

- Hello -

- - World! -
-`; - -exports[`enzyme with prop containing css element in fragment 1`] = ` -.emotion-0 { - background-color: blue; -} - -.emotion-0 { - background-color: blue; -} - -
- Array [ - "x", -
- y -
, - ] -
-`; diff --git a/packages/jest-emotion/test/matchers.test.js b/packages/jest-emotion/test/matchers.test.js index f27512be5..e888fc58f 100644 --- a/packages/jest-emotion/test/matchers.test.js +++ b/packages/jest-emotion/test/matchers.test.js @@ -19,6 +19,8 @@ describe('toHaveStyleRule', () => { width: 100%; ` + const enzymeMethods = ['mount', 'render'] + it('matches styles on the top-most node passed in', () => { const tree = renderer .create( @@ -54,49 +56,21 @@ describe('toHaveStyleRule', () => { expect(svgNode).toHaveStyleRule('width', expect.stringMatching(/.*%$/)) }) - it('supports enzyme `mount` method', () => { + it('supports enzyme render methods', () => { const Component = () => (
) - const wrapper = enzyme.mount() - 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 = () => ( -
- -
- ) - - const wrapper = enzyme.render() - 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 = () => ( -
- -
- ) - - const wrapper = enzyme.shallow() - 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') + enzymeMethods.forEach(method => { + const wrapper = enzyme[method]() + 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') + }) }) // i think this isn't working because of forwardRef @@ -107,7 +81,8 @@ describe('toHaveStyleRule', () => { const Svg = styled('svg')` width: 100%; ` - ;['mount', 'render', 'shallow'].forEach(method => { + + enzymeMethods.forEach(method => { const wrapper = enzyme[method](
diff --git a/packages/jest-emotion/test/react-enzyme.test.js b/packages/jest-emotion/test/react-enzyme.test.js index 58b670fb8..07514554a 100644 --- a/packages/jest-emotion/test/react-enzyme.test.js +++ b/packages/jest-emotion/test/react-enzyme.test.js @@ -1,151 +1,130 @@ import 'test-utils/legacy-env' /** @jsx jsx */ -import jestInCase from 'jest-in-case' import * as enzyme from 'enzyme' import { jsx } from '@emotion/core' +import { createSerializer as createEnzymeSerializer } from 'enzyme-to-json' import { createSerializer } from 'jest-emotion' +import { toMatchSnapshot } from 'jest-snapshot' import React from 'react' -import toJson from 'enzyme-to-json' + +const createEnzymeSnapshotMatcher = serializerOptions => { + const serializer = createEnzymeSerializer(serializerOptions) + const identityPrinter = v => v + + return function(val) { + return toMatchSnapshot.call(this, serializer.print(val, identityPrinter)) + } +} expect.addSnapshotSerializer(createSerializer()) -const cases = { - basic: { - render() { - const Greeting = ({ children }) => ( -
{children}
- ) - return hello - } - }, - nested: { - render() { - const Greeting = ({ children, className }) => ( -
{children}
- ) - return ( -
- Hello -
- ) - } - }, - 'with prop containing css element': { - render() { - const Greeting = ({ children, content }) => ( -
- {content} {children} -
- ) - return ( - Hello

}> - World! -
- ) - } - }, - 'with prop containing css element not at the top level': { - render() { - const Greeting = ({ children, content }) => ( -
- {content} {children} -
- ) +expect.extend({ + toMatchShallowSnapshot: createEnzymeSnapshotMatcher(), + toMatchDeepSnapshot: createEnzymeSnapshotMatcher({ mode: 'deep' }) +}) + +test('enzyme mount test', () => { + const Greeting = ({ children }) => ( +
{children}
+ ) + const tree = enzyme.mount(hello) + expect(tree).toMatchShallowSnapshot() +}) - return ( -
- - Hello -

- } - > - World! -
-
- ) - } - }, - 'with prop containing css element with other props': { - render() { - const Greeting = ({ children, content }) => ( -
- {content} {children} -
- ) +test('enzyme test with prop containing css element', () => { + const Greeting = ({ children, content }) => ( +
+ {content} {children} +
+ ) - return ( - - Hello -

- } - > - World! -
- ) - } - }, - 'with prop containing css element with other label': { - render() { - const Thing = ({ content, children }) => { - return children - } - const Greeting = ({ children, content }) => ( - }> - {content} {children} - - ) + const tree = enzyme.mount( + Hello

}> + World! +
+ ) + expect(tree).toMatchShallowSnapshot() +}) - return ( - - Hello -

- } - > - World! -
- ) - } - } -} +test('enzyme test with prop containing css element not at the top level', () => { + const Greeting = ({ children, content }) => ( +
+ {content} {children} +
+ ) + + const tree = enzyme.mount( +
+ + Hello +

+ } + > + World! +
+
+ ) + expect(tree).toMatchShallowSnapshot() +}) -describe('enzyme', () => { - jestInCase( - 'shallow', - ({ render }) => { - const wrapper = enzyme.shallow(render()) - expect(toJson(wrapper)).toMatchSnapshot() - }, - cases +test('enzyme test with prop containing css element with other props', () => { + const Greeting = ({ children, content }) => ( +
+ {content} {children} +
+ ) + + const tree = enzyme.mount( + + Hello +

+ } + > + World! +
) + expect(tree).toMatchShallowSnapshot() +}) - jestInCase( - 'mount', - ({ render }) => { - const wrapper = enzyme.mount(render()) - expect(toJson(wrapper)).toMatchSnapshot() - }, - cases +test('enzyme test with prop containing css element with other label', () => { + const Thing = ({ content, children }) => { + return children + } + const Greeting = ({ children, content }) => ( + }> + {content} {children} + ) - test('with prop containing css element in fragment', () => { - const FragmentComponent = () => ( - - x
y
-
- ) + const tree = enzyme.mount( + + Hello +

+ } + > + World! +
+ ) + expect(tree).toMatchShallowSnapshot() +}) - const wrapper = enzyme.mount( -
- -
- ) +test('enzyme test with prop containing css element in fragment', () => { + const FragmentComponent = () => ( + + x
y
+
+ ) - expect(toJson(wrapper, { mode: 'deep' })).toMatchSnapshot() - }) + const tree = enzyme.mount( +
+ +
+ ) + expect(tree).toMatchDeepSnapshot() })