├── .github ├── FUNDING.yml ├── dependabot.yml └── workflows │ ├── dependabot-dedupe.yml │ └── ci.yml ├── .eslintignore ├── test ├── .eslintignore ├── test │ ├── _helpers │ │ ├── adapter.js │ │ ├── realArrowFunction.js │ │ ├── selectors.js │ │ ├── untranspiledSloppyReturnThis.js │ │ ├── untranspiledArrowFunction.js │ │ ├── version.js │ │ ├── getLoadedLazyComponent.js │ │ ├── withDom.js │ │ ├── setupAdapters.js │ │ ├── describeHooks.js │ │ ├── describeMethods.js │ │ ├── describeLifecycles.js │ │ ├── react-compat.js │ │ └── index.jsx │ ├── shared │ │ ├── methods │ │ │ ├── getNodes.jsx │ │ │ ├── getNode.jsx │ │ │ ├── childAt.jsx │ │ │ ├── last.jsx │ │ │ ├── first.jsx │ │ │ ├── tap.jsx │ │ │ ├── every.jsx │ │ │ ├── getElements.jsx │ │ │ ├── someWhere.jsx │ │ │ ├── everyWhere.jsx │ │ │ ├── not.jsx │ │ │ ├── key.jsx │ │ │ ├── _method.template │ │ │ ├── some.jsx │ │ │ ├── forEach.jsx │ │ │ ├── isEmpty.jsx │ │ │ ├── flatMap.jsx │ │ │ ├── unmount.jsx │ │ │ ├── at.jsx │ │ │ ├── deprecatedInstanceProperties.jsx │ │ │ ├── filter.jsx │ │ │ ├── root.jsx │ │ │ ├── get.jsx │ │ │ ├── map.jsx │ │ │ ├── filterWhere.jsx │ │ │ ├── single.jsx │ │ │ ├── wrap.jsx │ │ │ ├── @@iterator.jsx │ │ │ ├── exists.jsx │ │ │ ├── render.jsx │ │ │ ├── closest.jsx │ │ │ ├── slice.jsx │ │ │ ├── instance.jsx │ │ │ ├── reduce.jsx │ │ │ ├── reduceRight.jsx │ │ │ ├── parent.jsx │ │ │ ├── getElement.jsx │ │ │ ├── prop.jsx │ │ │ ├── is.jsx │ │ │ ├── matchesElement.jsx │ │ │ ├── equals.jsx │ │ │ ├── invoke.jsx │ │ │ ├── html.jsx │ │ │ ├── hostNodes.jsx │ │ │ ├── context.jsx │ │ │ ├── containsAnyMatchingElements.jsx │ │ │ ├── contains.jsx │ │ │ ├── name.jsx │ │ │ ├── simulateError.jsx │ │ │ ├── containsAllMatchingElements.jsx │ │ │ ├── parents.jsx │ │ │ ├── setContext.jsx │ │ │ ├── state.jsx │ │ │ ├── props.jsx │ │ │ ├── isEmptyRender.jsx │ │ │ └── renderProp.jsx │ │ ├── lifecycles │ │ │ ├── componentWillUnmount.jsx │ │ │ ├── componentDidMount.jsx │ │ │ └── getSnapshotBeforeUpdate.jsx │ │ └── hooks │ │ │ ├── _hook.template │ │ │ ├── useRef.jsx │ │ │ ├── useImperativeHandle.jsx │ │ │ ├── useDebugValue.jsx │ │ │ ├── useLayoutEffect.jsx │ │ │ ├── useMemo.jsx │ │ │ ├── useCallback.jsx │ │ │ ├── useReducer.jsx │ │ │ ├── useContext.jsx │ │ │ ├── useState.jsx │ │ │ └── custom.jsx │ ├── enzyme-shallow-equal.spec.js │ └── staticRender.spec.jsx ├── vitest.config.ts ├── vitest.setup.ts ├── .eslintrc.json └── package.json ├── .prettierignore ├── src ├── index.js ├── detectFiberTags.js └── findCurrentFiberUsingSlowPath.js ├── .babelrc ├── .husky └── pre-commit ├── .prettierrc.json ├── .eslintrc.json ├── .vscode ├── extensions.json └── settings.json ├── index.d.ts ├── .yarn └── plugins │ ├── plugin-remove-postinstall.cjs │ └── plugin-remove-workspaces-field.cjs ├── .gitignore ├── .yarnrc.yml ├── README.md ├── LICENSE └── package.json /.github/FUNDING.yml: -------------------------------------------------------------------------------- 1 | github: wojtekmaj 2 | -------------------------------------------------------------------------------- /.eslintignore: -------------------------------------------------------------------------------- 1 | build/ 2 | node_modules/ 3 | **/node_modules/ 4 | -------------------------------------------------------------------------------- /test/.eslintignore: -------------------------------------------------------------------------------- 1 | build/ 2 | node_modules/ 3 | **/node_modules/ 4 | -------------------------------------------------------------------------------- /.prettierignore: -------------------------------------------------------------------------------- 1 | .cache 2 | .yarn 3 | build 4 | coverage 5 | *.yml 6 | -------------------------------------------------------------------------------- /src/index.js: -------------------------------------------------------------------------------- 1 | module.exports = require('./ReactSeventeenAdapter'); 2 | -------------------------------------------------------------------------------- /.babelrc: -------------------------------------------------------------------------------- 1 | { 2 | "presets": ["@babel/env", "@babel/react"], 3 | "sourceMaps": "both" 4 | } 5 | -------------------------------------------------------------------------------- /test/test/_helpers/adapter.js: -------------------------------------------------------------------------------- 1 | module.exports = require('@wojtekmaj/enzyme-adapter-react-17'); 2 | -------------------------------------------------------------------------------- /.husky/pre-commit: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | . "$(dirname "$0")/_/husky.sh" 3 | 4 | yarn pretty-quick --staged 5 | -------------------------------------------------------------------------------- /.prettierrc.json: -------------------------------------------------------------------------------- 1 | { 2 | "printWidth": 100, 3 | "singleQuote": true, 4 | "trailingComma": "all" 5 | } 6 | -------------------------------------------------------------------------------- /.eslintrc.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "wojtekmaj/node", 3 | "rules": { 4 | "import/default": "off" 5 | } 6 | } 7 | -------------------------------------------------------------------------------- /test/test/_helpers/realArrowFunction.js: -------------------------------------------------------------------------------- 1 | try { 2 | module.exports = require('./untranspiledArrowFunction'); 3 | } catch (e) { 4 | module.exports = (x) => () => x; 5 | } 6 | -------------------------------------------------------------------------------- /test/test/_helpers/selectors.js: -------------------------------------------------------------------------------- 1 | export const getElementPropSelector = (prop) => (x) => x.props[prop]; 2 | 3 | export const getWrapperPropSelector = (prop) => (x) => x.prop(prop); 4 | -------------------------------------------------------------------------------- /.vscode/extensions.json: -------------------------------------------------------------------------------- 1 | { 2 | "recommendations": ["dbaeumer.vscode-eslint", "eamodio.gitlens", "esbenp.prettier-vscode"], 3 | "unwantedRecommendations": ["dbaeumer.jshint"] 4 | } 5 | -------------------------------------------------------------------------------- /.vscode/settings.json: -------------------------------------------------------------------------------- 1 | { 2 | "editor.defaultFormatter": "esbenp.prettier-vscode", 3 | "editor.formatOnSave": true, 4 | "search.exclude": { 5 | "**/.yarn": true 6 | } 7 | } 8 | -------------------------------------------------------------------------------- /.github/dependabot.yml: -------------------------------------------------------------------------------- 1 | version: 2 2 | updates: 3 | - package-ecosystem: "npm" 4 | directory: "/" 5 | schedule: 6 | interval: "weekly" 7 | open-pull-requests-limit: 10 8 | -------------------------------------------------------------------------------- /test/vitest.config.ts: -------------------------------------------------------------------------------- 1 | import { defineConfig } from 'vitest/config'; 2 | 3 | export default defineConfig({ 4 | test: { 5 | environment: 'jsdom', 6 | setupFiles: 'vitest.setup.ts', 7 | }, 8 | }); 9 | -------------------------------------------------------------------------------- /index.d.ts: -------------------------------------------------------------------------------- 1 | import { EnzymeAdapter } from 'enzyme'; 2 | 3 | declare class ReactSeventeenAdapter extends EnzymeAdapter {} 4 | 5 | declare namespace ReactSeventeenAdapter {} 6 | 7 | export = ReactSeventeenAdapter; 8 | -------------------------------------------------------------------------------- /test/test/_helpers/untranspiledSloppyReturnThis.js: -------------------------------------------------------------------------------- 1 | module.exports = function (fn) { 2 | return function () { 3 | return fn.apply(null, [this].concat(Array.prototype.slice.call(arguments))); 4 | }; 5 | }; 6 | -------------------------------------------------------------------------------- /test/test/_helpers/untranspiledArrowFunction.js: -------------------------------------------------------------------------------- 1 | // this file is ignored in babelrc, specifically so that tests will be able to run 2 | // on a real arrow function that lacks a .prototype 3 | module.exports = (x) => () => x; 4 | -------------------------------------------------------------------------------- /test/vitest.setup.ts: -------------------------------------------------------------------------------- 1 | import { afterEach, beforeEach, describe, it } from 'vitest'; 2 | 3 | // Temporary workaround to make mocha-wrap work 4 | global.afterEach = afterEach; 5 | global.beforeEach = beforeEach; 6 | global.describe = describe; 7 | global.it = it; 8 | -------------------------------------------------------------------------------- /.yarn/plugins/plugin-remove-postinstall.cjs: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | name: 'plugin-remove-postinstall', 3 | factory: () => ({ 4 | hooks: { 5 | beforeWorkspacePacking(workspace, rawManifest) { 6 | delete rawManifest.scripts.postinstall; 7 | }, 8 | }, 9 | }), 10 | }; 11 | -------------------------------------------------------------------------------- /.yarn/plugins/plugin-remove-workspaces-field.cjs: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | name: 'plugin-remove-workspaces-field', 3 | factory: () => ({ 4 | hooks: { 5 | beforeWorkspacePacking(workspace, rawManifest) { 6 | delete rawManifest.workspaces; 7 | }, 8 | }, 9 | }), 10 | }; 11 | -------------------------------------------------------------------------------- /test/test/_helpers/version.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | 3 | export const VERSION = React.version; 4 | 5 | // The shallow renderer in React 17 does not yet support batched updates. When it does, 6 | // we should be able to go un-skip all of the tests that are skipped with this flag. 7 | export const BATCHING = false; 8 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Cache and IDE-related files 2 | .cache 3 | .idea 4 | 5 | # Yarn-related files 6 | **/.yarn/* 7 | !**/.yarn/releases 8 | !**/.yarn/plugins 9 | !**/.yarn/sdks 10 | yarn-error.log 11 | 12 | # Project-generated directories 13 | build 14 | coverage 15 | node_modules 16 | 17 | # Other 18 | **/.DS_Store 19 | **/.env 20 | -------------------------------------------------------------------------------- /.yarnrc.yml: -------------------------------------------------------------------------------- 1 | logFilters: 2 | - code: YN0076 3 | level: discard 4 | 5 | nodeLinker: node-modules 6 | 7 | plugins: 8 | - path: .yarn/plugins/plugin-remove-postinstall.cjs 9 | - path: .yarn/plugins/plugin-remove-workspaces-field.cjs 10 | - path: .yarn/plugins/@yarnpkg/plugin-interactive-tools.cjs 11 | spec: "@yarnpkg/plugin-interactive-tools" 12 | -------------------------------------------------------------------------------- /test/test/_helpers/getLoadedLazyComponent.js: -------------------------------------------------------------------------------- 1 | import { lazy } from './react-compat'; 2 | 3 | function fakeSyncThenable(result) { 4 | return { 5 | then(resolve) { 6 | return resolve({ default: result }); 7 | }, 8 | }; 9 | } 10 | 11 | export default function getLoadedLazyComponent(wrappedComponent) { 12 | return lazy(() => fakeSyncThenable(wrappedComponent)); 13 | } 14 | -------------------------------------------------------------------------------- /test/test/shared/methods/getNodes.jsx: -------------------------------------------------------------------------------- 1 | import { describe, expect, it } from 'vitest'; 2 | import React from 'react'; 3 | 4 | export default function describeGetNodes({ Wrap, WrapperName }) { 5 | describe('.getNodes()', () => { 6 | it('throws', () => { 7 | const wrapper = Wrap(
); 8 | expect(() => wrapper.getNodes()).to.throw( 9 | `${WrapperName}::getNodes() is no longer supported.`, 10 | ); 11 | }); 12 | }); 13 | } 14 | -------------------------------------------------------------------------------- /test/test/shared/methods/getNode.jsx: -------------------------------------------------------------------------------- 1 | import { describe, expect, it } from 'vitest'; 2 | import React from 'react'; 3 | 4 | export default function describeGetNode({ Wrap, WrapperName, isShallow }) { 5 | describe('.getNode()', () => { 6 | it('throws', () => { 7 | const wrapper = Wrap(
); 8 | expect(() => wrapper.getNode()).to.throw( 9 | `${WrapperName}::getNode() is no longer supported. Use ${WrapperName}::${ 10 | isShallow ? 'getElement' : 'instance' 11 | }() instead`, 12 | ); 13 | }); 14 | }); 15 | } 16 | -------------------------------------------------------------------------------- /test/test/shared/methods/childAt.jsx: -------------------------------------------------------------------------------- 1 | import { describe, expect, it } from 'vitest'; 2 | import React from 'react'; 3 | 4 | export default function describeChildAt({ Wrap }) { 5 | describe('.childAt(index)', () => { 6 | it('gets a wrapped node at the specified index', () => { 7 | const wrapper = Wrap( 8 |
9 |
10 |
11 |
, 12 | ); 13 | 14 | expect(wrapper.childAt(0).hasClass('bar')).to.equal(true); 15 | expect(wrapper.childAt(1).hasClass('baz')).to.equal(true); 16 | }); 17 | }); 18 | } 19 | -------------------------------------------------------------------------------- /test/test/shared/methods/last.jsx: -------------------------------------------------------------------------------- 1 | import { describe, expect, it } from 'vitest'; 2 | import React from 'react'; 3 | 4 | export default function describeLast({ Wrap }) { 5 | describe('.last()', () => { 6 | it('returns the last node in the current set', () => { 7 | const wrapper = Wrap( 8 |
9 |
10 |
11 |
12 |
13 |
, 14 | ); 15 | expect(wrapper.find('.bar').last().hasClass('baz')).to.equal(true); 16 | }); 17 | }); 18 | } 19 | -------------------------------------------------------------------------------- /test/test/shared/methods/first.jsx: -------------------------------------------------------------------------------- 1 | import { describe, expect, it } from 'vitest'; 2 | import React from 'react'; 3 | 4 | export default function describeFirst({ Wrap }) { 5 | describe('.first()', () => { 6 | it('returns the first node in the current set', () => { 7 | const wrapper = Wrap( 8 |
9 |
10 |
11 |
12 |
13 |
, 14 | ); 15 | expect(wrapper.find('.bar').first().hasClass('baz')).to.equal(true); 16 | }); 17 | }); 18 | } 19 | -------------------------------------------------------------------------------- /test/test/_helpers/withDom.js: -------------------------------------------------------------------------------- 1 | require('raf/polyfill'); 2 | 3 | const { JSDOM } = require('jsdom'); 4 | 5 | const jsdom = new JSDOM('', { url: 'http://localhost' }); 6 | 7 | global.window = jsdom.window; 8 | global.document = jsdom.window.document; 9 | global.HTMLElement = window.HTMLElement; 10 | global.HTMLButtonElement = window.HTMLButtonElement; 11 | Object.keys(global.document.defaultView).forEach((property) => { 12 | if (typeof global[property] === 'undefined') { 13 | global[property] = global.document.defaultView[property]; 14 | } 15 | }); 16 | 17 | global.navigator = { 18 | userAgent: 'node.js', 19 | }; 20 | -------------------------------------------------------------------------------- /test/test/shared/methods/tap.jsx: -------------------------------------------------------------------------------- 1 | import { describe, expect, it, vi } from 'vitest'; 2 | import React from 'react'; 3 | 4 | export default function describeTap({ Wrap }) { 5 | describe('.tap()', () => { 6 | it('calls the passed function with current Wrapper and returns itself', () => { 7 | const spy = vi.fn(); 8 | const wrapper = Wrap( 9 |
    10 |
  • xxx
  • 11 |
  • yyy
  • 12 |
  • zzz
  • 13 |
, 14 | ).find('li'); 15 | const result = wrapper.tap(spy); 16 | expect(spy).toHaveBeenCalledWith(wrapper); 17 | expect(result).to.equal(wrapper); 18 | }); 19 | }); 20 | } 21 | -------------------------------------------------------------------------------- /test/.eslintrc.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "wojtekmaj/react-no-automatic-runtime", 3 | "rules": { 4 | "jsx-a11y/anchor-has-content": "off", 5 | "jsx-a11y/anchor-is-valid": "off", 6 | "jsx-a11y/click-events-have-key-events": "off", 7 | "jsx-a11y/label-has-associated-control": "off", 8 | "jsx-a11y/no-static-element-interactions": "off", 9 | "react/display-name": "off", 10 | "react/jsx-key": "off", 11 | "react/no-direct-mutation-state": "off", 12 | "react/no-danger": "off", 13 | "react/no-deprecated": "off", 14 | "react/no-string-refs": "off", 15 | "react/prop-types": "off", 16 | "react-hooks/exhaustive-deps": "off", 17 | "react-hooks/rules-of-hooks": "off" 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /test/test/shared/methods/every.jsx: -------------------------------------------------------------------------------- 1 | import { describe, expect, it } from 'vitest'; 2 | import React from 'react'; 3 | 4 | export default function describeEvery({ Wrap }) { 5 | describe('.every(selector)', () => { 6 | it('returns if every node matches a selector', () => { 7 | const wrapper = Wrap( 8 |
9 |
10 |
11 |
12 |
, 13 | ); 14 | expect(wrapper.find('.foo').every('.foo')).to.equal(true); 15 | expect(wrapper.find('.foo').every('.qoo')).to.equal(false); 16 | expect(wrapper.find('.foo').every('.bar')).to.equal(false); 17 | }); 18 | }); 19 | } 20 | -------------------------------------------------------------------------------- /test/test/shared/methods/getElements.jsx: -------------------------------------------------------------------------------- 1 | import { describe, expect, it } from 'vitest'; 2 | import React from 'react'; 3 | 4 | export default function describeGetElements({ Wrap }) { 5 | describe('.getElements()', () => { 6 | it('returns the wrapped elements', () => { 7 | const one = ; 8 | const two = ; 9 | 10 | class Test extends React.Component { 11 | render() { 12 | return ( 13 |
14 | {one} 15 | {two} 16 |
17 | ); 18 | } 19 | } 20 | 21 | const wrapper = Wrap(); 22 | expect(wrapper.find('span').getElements()).to.deep.equal([one, two]); 23 | }); 24 | }); 25 | } 26 | -------------------------------------------------------------------------------- /test/test/shared/lifecycles/componentWillUnmount.jsx: -------------------------------------------------------------------------------- 1 | import { describe, expect, it, vi } from 'vitest'; 2 | import React from 'react'; 3 | 4 | export default function describeCWU({ Wrap }) { 5 | describe('componentWillUnmount', () => { 6 | it('calls componentWillUnmount', () => { 7 | const spy = vi.fn(); 8 | class Foo extends React.Component { 9 | componentWillUnmount() { 10 | spy('componentWillUnmount'); 11 | } 12 | 13 | render() { 14 | spy('render'); 15 | return
foo
; 16 | } 17 | } 18 | const wrapper = Wrap(); 19 | wrapper.unmount(); 20 | expect(spy.mock.calls).to.deep.equal([['render'], ['componentWillUnmount']]); 21 | }); 22 | }); 23 | } 24 | -------------------------------------------------------------------------------- /test/test/shared/methods/someWhere.jsx: -------------------------------------------------------------------------------- 1 | import { describe, expect, it } from 'vitest'; 2 | import React from 'react'; 3 | 4 | export default function describeSomeWhere({ Wrap }) { 5 | describe('.someWhere(predicate)', () => { 6 | it('returns if a node matches a predicate', () => { 7 | const wrapper = Wrap( 8 |
9 |
10 |
11 |
12 |
, 13 | ); 14 | const foo = wrapper.find('.foo'); 15 | expect(foo.someWhere((n) => n.hasClass('qoo'))).to.equal(true); 16 | expect(foo.someWhere((n) => n.hasClass('foo'))).to.equal(true); 17 | expect(foo.someWhere((n) => n.hasClass('bar'))).to.equal(false); 18 | }); 19 | }); 20 | } 21 | -------------------------------------------------------------------------------- /test/test/shared/hooks/_hook.template: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import PropTypes from 'prop-types'; 3 | import wrap from 'mocha-wrap'; 4 | import { Portal } from 'react-is'; 5 | 6 | import { render } from 'enzyme'; 7 | import getAdapter from 'enzyme/build/getAdapter'; 8 | 9 | import { 10 | describeIf, 11 | itIf, 12 | } from '../../_helpers'; 13 | 14 | import { 15 | useCallback, 16 | useContext, 17 | useEffect, 18 | useLayoutEffect, 19 | useMemo, 20 | useReducer, 21 | useState, 22 | act, 23 | } from '../../_helpers/react-compat'; 24 | 25 | export default function describe$Hook({ 26 | Wrap, 27 | WrapRendered, 28 | Wrapper, 29 | WrapperName, 30 | isShallow, 31 | isMount, 32 | makeDOMElement, 33 | }) { 34 | describe('hooks: $Hook', () => { 35 | }); 36 | } 37 | -------------------------------------------------------------------------------- /test/test/shared/methods/everyWhere.jsx: -------------------------------------------------------------------------------- 1 | import { describe, expect, it } from 'vitest'; 2 | import React from 'react'; 3 | 4 | export default function describeEveryWhere({ Wrap }) { 5 | describe('.everyWhere(predicate)', () => { 6 | it('returns if every node matches a predicate', () => { 7 | const wrapper = Wrap( 8 |
9 |
10 |
11 |
12 |
, 13 | ); 14 | const foo = wrapper.find('.foo'); 15 | expect(foo.everyWhere((n) => n.hasClass('foo'))).to.equal(true); 16 | expect(foo.everyWhere((n) => n.hasClass('qoo'))).to.equal(false); 17 | expect(foo.everyWhere((n) => n.hasClass('bar'))).to.equal(false); 18 | }); 19 | }); 20 | } 21 | -------------------------------------------------------------------------------- /test/test/shared/methods/not.jsx: -------------------------------------------------------------------------------- 1 | import { describe, expect, it } from 'vitest'; 2 | import React from 'react'; 3 | 4 | export default function describeNot({ Wrap }) { 5 | describe('.not(selector)', () => { 6 | it('filters to things not matching a selector', () => { 7 | const wrapper = Wrap( 8 |
9 |
10 |
11 |
12 |
13 |
14 |
, 15 | ); 16 | 17 | expect(wrapper.find('.foo').not('.bar')).to.have.lengthOf(1); 18 | expect(wrapper.find('.baz').not('.foo')).to.have.lengthOf(2); 19 | expect(wrapper.find('.foo').not('div')).to.have.lengthOf(0); 20 | }); 21 | }); 22 | } 23 | -------------------------------------------------------------------------------- /test/test/shared/hooks/useRef.jsx: -------------------------------------------------------------------------------- 1 | import { describe, expect, it } from 'vitest'; 2 | import React from 'react'; 3 | 4 | import { useRef } from '../../_helpers/react-compat'; 5 | 6 | export default function describeUseRef({ Wrap }) { 7 | describe('hooks: useRef', () => { 8 | function ComponentUsingRef() { 9 | const id = useRef(Math.floor(100 * Math.random())); 10 | return
{id.current}
; 11 | } 12 | 13 | it('`current` should be the same between two renders', () => { 14 | const wrapper = Wrap(); 15 | 16 | const childBefore = wrapper.find('div').prop('children'); 17 | wrapper.setProps({ foo: 'bar' }); 18 | const childAfter = wrapper.find('div').prop('children'); 19 | 20 | expect(childBefore).to.equal(childAfter); 21 | }); 22 | }); 23 | } 24 | -------------------------------------------------------------------------------- /test/test/shared/methods/key.jsx: -------------------------------------------------------------------------------- 1 | import { describe, expect, it } from 'vitest'; 2 | import React from 'react'; 3 | 4 | export default function describeKey({ Wrap }) { 5 | describe('.key()', () => { 6 | it('returns the key of the node', () => { 7 | const wrapper = Wrap( 8 |
    9 | {['foo', 'bar', ''].map((s) => ( 10 |
  • {s}
  • 11 | ))} 12 |
, 13 | ).find('li'); 14 | expect(wrapper.at(0).key()).to.equal('foo'); 15 | expect(wrapper.at(1).key()).to.equal('bar'); 16 | expect(wrapper.at(2).key()).to.equal(''); 17 | }); 18 | 19 | it('returns null when no key is specified', () => { 20 | const wrapper = Wrap( 21 |
    22 |
  • foo
  • 23 |
, 24 | ).find('li'); 25 | expect(wrapper.key()).to.equal(null); 26 | }); 27 | }); 28 | } 29 | -------------------------------------------------------------------------------- /test/test/shared/methods/_method.template: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import PropTypes from 'prop-types'; 3 | import wrap from 'mocha-wrap'; 4 | import { Portal } from 'react-is'; 5 | 6 | import { render } from 'enzyme'; 7 | import getAdapter from 'enzyme/build/getAdapter'; 8 | import { 9 | ITERATOR_SYMBOL, 10 | sym, 11 | } from 'enzyme/build/Utils'; 12 | 13 | import { 14 | describeIf, 15 | itIf, 16 | } from '../../_helpers'; 17 | import realArrowFunction from '../../_helpers/realArrowFunction'; 18 | import { getElementPropSelector, getWrapperPropSelector } from '../../_helpers/selectors'; 19 | 20 | import { 21 | createClass, 22 | createPortal, 23 | createRef, 24 | Fragment, 25 | } from '../../_helpers/react-compat'; 26 | 27 | export default function describe$Method({ 28 | Wrap, 29 | WrapRendered, 30 | Wrapper, 31 | WrapperName, 32 | isShallow, 33 | isMount, 34 | makeDOMElement, 35 | }) { 36 | } 37 | -------------------------------------------------------------------------------- /test/test/shared/methods/some.jsx: -------------------------------------------------------------------------------- 1 | import { describe, expect, it } from 'vitest'; 2 | import React from 'react'; 3 | 4 | export default function describeSome({ Wrap, WrapperName }) { 5 | describe('.some(selector)', () => { 6 | it('returns if a node matches a selector', () => { 7 | const wrapper = Wrap( 8 |
9 |
10 |
11 |
12 |
, 13 | ); 14 | const foo = wrapper.find('.foo'); 15 | expect(foo.some('.qoo')).to.equal(true); 16 | expect(foo.some('.foo')).to.equal(true); 17 | expect(foo.some('.bar')).to.equal(false); 18 | }); 19 | 20 | it('throws if called on root', () => { 21 | const wrapper = Wrap( 22 |
23 |
24 |
, 25 | ); 26 | expect(() => wrapper.some('.foo')).to.throw( 27 | Error, 28 | `${WrapperName}::some() can not be called on the root`, 29 | ); 30 | }); 31 | }); 32 | } 33 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | [![npm](https://img.shields.io/npm/v/@wojtekmaj/enzyme-adapter-react-17.svg)](https://www.npmjs.com/package/@wojtekmaj/enzyme-adapter-react-17) ![downloads](https://img.shields.io/npm/dt/@wojtekmaj/enzyme-adapter-react-17.svg) [![CI](https://github.com/wojtekmaj/enzyme-adapter-react-17/workflows/CI/badge.svg)](https://github.com/wojtekmaj/enzyme-adapter-react-17/actions) 2 | 3 | # @wojtekmaj/enzyme-adapter-react-17 4 | 5 | Unofficial adapter for React 17 for [Enzyme](https://enzymejs.github.io/enzyme/). 6 | 7 | ## Installation 8 | 9 | ``` 10 | npm install --save-dev @wojtekmaj/enzyme-adapter-react-17 11 | ``` 12 | 13 | or, if you're using Yarn: 14 | 15 | ``` 16 | yarn add --dev @wojtekmaj/enzyme-adapter-react-17 17 | ``` 18 | 19 | ## Configuration 20 | 21 | Finally, you need to configure enzyme to use the adapter you want it to use. To do this, you can use the top level `configure(...)` API. 22 | 23 | ```js 24 | import Enzyme from 'enzyme'; 25 | import Adapter from '@wojtekmaj/enzyme-adapter-react-17'; 26 | 27 | Enzyme.configure({ adapter: new Adapter() }); 28 | ``` 29 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2015-2023 Airbnb, Inc. and contributors 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /test/test/_helpers/setupAdapters.js: -------------------------------------------------------------------------------- 1 | const util = require('util'); 2 | const Enzyme = require('enzyme'); 3 | const wrap = require('mocha-wrap'); 4 | const resetWarningCache = require('prop-types').checkPropTypes.resetWarningCache; 5 | 6 | const Adapter = require('./adapter'); 7 | 8 | Enzyme.configure({ adapter: new Adapter() }); 9 | 10 | // eslint-disable-next-line no-console 11 | const origWarn = console.warn; 12 | // eslint-disable-next-line no-console 13 | const origError = console.error; 14 | wrap.register(function withConsoleThrows() { 15 | return this.withOverrides( 16 | () => console, 17 | () => ({ 18 | error(msg) { 19 | origError.apply(console, arguments); 20 | throw new EvalError(arguments.length > 1 ? util.format.apply(util, arguments) : msg); 21 | }, 22 | warn(msg) { 23 | origWarn.apply(console, arguments); 24 | throw new EvalError(arguments.length > 1 ? util.format.apply(util, arguments) : msg); 25 | }, 26 | }), 27 | ).extend('with console throws', { 28 | beforeEach() { 29 | resetWarningCache(); 30 | }, 31 | afterEach() { 32 | resetWarningCache(); 33 | }, 34 | }); 35 | }); 36 | -------------------------------------------------------------------------------- /test/test/shared/methods/forEach.jsx: -------------------------------------------------------------------------------- 1 | import { describe, expect, it, vi } from 'vitest'; 2 | import React from 'react'; 3 | 4 | export default function describeForEach({ Wrap, Wrapper }) { 5 | describe('.forEach(fn)', () => { 6 | it('calls a function for each node in the wrapper', () => { 7 | const wrapper = Wrap( 8 |
9 |
10 |
11 |
12 |
, 13 | ); 14 | const spy = vi.fn(); 15 | 16 | wrapper.find('.foo').forEach(spy); 17 | 18 | expect(spy).to.have.property('callCount', 3); 19 | expect(spy.mock.calls[0][0]).to.be.instanceOf(Wrapper); 20 | expect(spy.mock.calls[0][0].hasClass('bax')).to.equal(true); 21 | expect(spy.mock.calls[0][1]).to.equal(0); 22 | expect(spy.mock.calls[1][0]).to.be.instanceOf(Wrapper); 23 | expect(spy.mock.calls[1][0].hasClass('bar')).to.equal(true); 24 | expect(spy.mock.calls[1][1]).to.equal(1); 25 | expect(spy.mock.calls[2][0]).to.be.instanceOf(Wrapper); 26 | expect(spy.mock.calls[2][0].hasClass('baz')).to.equal(true); 27 | expect(spy.mock.calls[2][1]).to.equal(2); 28 | }); 29 | }); 30 | } 31 | -------------------------------------------------------------------------------- /test/test/shared/methods/isEmpty.jsx: -------------------------------------------------------------------------------- 1 | import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest'; 2 | import React from 'react'; 3 | 4 | export default function describeLast({ Wrap }) { 5 | describe('.isEmpty()', () => { 6 | let warningStub; 7 | let fooNode; 8 | let missingNode; 9 | 10 | beforeEach(() => { 11 | warningStub = vi.spyOn(console, 'warn'); 12 | const wrapper = Wrap(
); 13 | fooNode = wrapper.find('.foo'); 14 | missingNode = wrapper.find('.missing'); 15 | }); 16 | afterEach(() => { 17 | warningStub.restore(); 18 | }); 19 | 20 | it('displays a deprecation warning', () => { 21 | fooNode.isEmpty(); 22 | expect(warningStub).toHaveBeenCalledWith( 23 | 'Enzyme::Deprecated method isEmpty() called, use exists() instead.', 24 | ); 25 | }); 26 | 27 | it('calls exists() instead', () => { 28 | const existsSpy = vi.fn(); 29 | fooNode.exists = existsSpy; 30 | expect(fooNode.isEmpty()).to.equal(true); 31 | expect(existsSpy).to.have.property('called', true); 32 | }); 33 | 34 | it('returns true if wrapper is empty', () => { 35 | expect(fooNode.isEmpty()).to.equal(false); 36 | expect(missingNode.isEmpty()).to.equal(true); 37 | }); 38 | }); 39 | } 40 | -------------------------------------------------------------------------------- /test/test/shared/methods/flatMap.jsx: -------------------------------------------------------------------------------- 1 | import { describe, expect, it } from 'vitest'; 2 | import React from 'react'; 3 | 4 | export default function describeFlatMap({ Wrap }) { 5 | describe('.flatMap(fn)', () => { 6 | it('returns a wrapper with the mapped and flattened nodes', () => { 7 | const wrapper = Wrap( 8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
, 22 | ); 23 | 24 | const nodes = wrapper.find('.foo').flatMap((w) => w.children().getElements()); 25 | 26 | expect(nodes).to.have.lengthOf(6); 27 | expect(nodes.at(0).hasClass('bar')).to.equal(true); 28 | expect(nodes.at(1).hasClass('bar')).to.equal(true); 29 | expect(nodes.at(2).hasClass('baz')).to.equal(true); 30 | expect(nodes.at(3).hasClass('baz')).to.equal(true); 31 | expect(nodes.at(4).hasClass('bax')).to.equal(true); 32 | expect(nodes.at(5).hasClass('bax')).to.equal(true); 33 | }); 34 | }); 35 | } 36 | -------------------------------------------------------------------------------- /test/test/shared/methods/unmount.jsx: -------------------------------------------------------------------------------- 1 | import { describe, expect, it, vi } from 'vitest'; 2 | import React from 'react'; 3 | 4 | import { itIf } from '../../_helpers'; 5 | 6 | export default function describeUnmount({ isShallow, Wrap, WrapRendered }) { 7 | describe('.unmount()', () => { 8 | class WillUnmount extends React.Component { 9 | componentWillUnmount() {} 10 | 11 | render() { 12 | const { id } = this.props; 13 | return ( 14 |
15 | {id} 16 |
17 | ); 18 | } 19 | } 20 | 21 | it('calls componentWillUnmount()', () => { 22 | const spy = vi.spyOn(WillUnmount.prototype, 'componentWillUnmount'); 23 | const wrapper = Wrap(); 24 | expect(spy).to.have.property('callCount', 0); 25 | 26 | wrapper.unmount(); 27 | 28 | expect(spy).to.have.property('callCount', 1); 29 | const [args] = spy.mock.calls; 30 | expect(args).to.eql([]); 31 | }); 32 | 33 | itIf(!isShallow, 'throws on non-root', () => { 34 | const wrapper = WrapRendered(); 35 | const span = wrapper.find('span'); 36 | expect(span).to.have.lengthOf(1); 37 | expect(() => span.unmount()).to.throw(Error); 38 | }); 39 | }); 40 | } 41 | -------------------------------------------------------------------------------- /test/test/shared/lifecycles/componentDidMount.jsx: -------------------------------------------------------------------------------- 1 | import { describe, expect, it, vi } from 'vitest'; 2 | import React from 'react'; 3 | 4 | export default function describeCDM({ Wrap }) { 5 | describe('componentDidUpdate()', () => { 6 | it('does not call `componentDidMount` twice when a child component is created', () => { 7 | const spy = vi.fn(); 8 | 9 | class Foo extends React.Component { 10 | constructor(props) { 11 | super(props); 12 | this.state = { 13 | foo: 'init', 14 | }; 15 | } 16 | 17 | componentDidMount() { 18 | spy('componentDidMount'); 19 | } 20 | 21 | render() { 22 | spy('render'); 23 | 24 | const { foo } = this.state; 25 | return ( 26 |
27 | 30 | {foo} 31 |
32 | ); 33 | } 34 | } 35 | 36 | const wrapper = Wrap(); 37 | expect(spy.mock.calls).to.eql([['render'], ['componentDidMount']]); 38 | spy.mockReset(); 39 | 40 | wrapper.find('button').prop('onClick')(); 41 | expect(spy).to.have.property('callCount', 1); 42 | }); 43 | }); 44 | } 45 | -------------------------------------------------------------------------------- /test/test/shared/methods/at.jsx: -------------------------------------------------------------------------------- 1 | import { describe, expect, it } from 'vitest'; 2 | 3 | import React from 'react'; 4 | 5 | export default function describeAt({ Wrap }) { 6 | describe('.at(index)', () => { 7 | it('gets a wrapper of the node at the specified index', () => { 8 | const wrapper = Wrap( 9 |
10 |
11 |
12 |
13 |
14 |
, 15 | ); 16 | const bar = wrapper.find('.bar'); 17 | expect(bar.at(0).hasClass('foo')).to.equal(true); 18 | expect(bar.at(1).hasClass('bax')).to.equal(true); 19 | expect(bar.at(2).hasClass('bux')).to.equal(true); 20 | expect(bar.at(3).hasClass('baz')).to.equal(true); 21 | }); 22 | 23 | it('`.at()` does not affect the results of `.exists()`', () => { 24 | const wrapper = Wrap( 25 |
26 |
27 |
, 28 | ); 29 | const bar = wrapper.find('.bar'); 30 | expect(bar.exists()).to.equal(false); 31 | expect(bar.at(0).exists()).to.equal(false); 32 | 33 | const foo = wrapper.find('.foo'); 34 | expect(foo.exists()).to.equal(true); 35 | expect(foo.at(0).exists()).to.equal(true); 36 | }); 37 | }); 38 | } 39 | -------------------------------------------------------------------------------- /test/test/shared/methods/deprecatedInstanceProperties.jsx: -------------------------------------------------------------------------------- 1 | import { beforeEach, describe, expect, it } from 'vitest'; 2 | import React from 'react'; 3 | 4 | export default function describeDeprecatedInstanceProperties({ Wrap, isShallow }) { 5 | class Foo extends React.Component { 6 | render() { 7 | return ( 8 |
9 | 10 |
13 | ); 14 | } 15 | } 16 | 17 | describe('deprecated instance properties', () => { 18 | let wrapper; 19 | beforeEach(() => { 20 | wrapper = Wrap(); 21 | }); 22 | 23 | const tuples = [ 24 | ['node', 'Consider using the getElement() method instead.'], 25 | ['nodes', 'Consider using the getElements() method instead.'], 26 | ['renderer', ''], 27 | ['options', ''], 28 | ['complexSelector', ''], 29 | ]; 30 | 31 | tuples.forEach(([prop, extra]) => { 32 | it(`warns on \`${prop}\``, () => { 33 | expect(() => wrapper[prop]).to.throw( 34 | Error, 35 | new RegExp(`Attempted to access ${isShallow ? 'Shallow' : 'React'}Wrapper::${prop}`), 36 | ); 37 | expect(() => wrapper[prop]).to.throw( 38 | Error, 39 | new RegExp(`${extra.replace(/([(){}.\\])/g, '\\$1')}`), 40 | ); 41 | }); 42 | }); 43 | }); 44 | } 45 | -------------------------------------------------------------------------------- /test/test/shared/hooks/useImperativeHandle.jsx: -------------------------------------------------------------------------------- 1 | import { describe, expect, it, vi } from 'vitest'; 2 | import React from 'react'; 3 | 4 | import { useImperativeHandle, useRef, forwardRef } from '../../_helpers/react-compat'; 5 | 6 | export default function describeUseImperativeHandle({ Wrap, isShallow }) { 7 | describe('hooks: useImperativeHandle', () => { 8 | function Computer({ compute }, ref) { 9 | const computerRef = useRef({ compute }); 10 | useImperativeHandle(ref, () => ({ 11 | compute: () => { 12 | computerRef.current.compute(); 13 | }, 14 | })); 15 | return
; 16 | } 17 | 18 | const FancyComputer = forwardRef(Computer); 19 | 20 | class ParentComputer extends React.Component { 21 | componentDidMount() { 22 | if (this.ref) { 23 | this.ref.compute(); 24 | } 25 | } 26 | 27 | render() { 28 | return ( 29 | { 31 | this.ref = ref; 32 | }} 33 | {...this.props} 34 | /> 35 | ); 36 | } 37 | } 38 | 39 | it('able to call method with imperative handle', () => { 40 | const compute = vi.fn(); 41 | Wrap(); 42 | 43 | expect(compute).to.have.property('callCount', isShallow ? 0 : 1); 44 | }); 45 | }); 46 | } 47 | -------------------------------------------------------------------------------- /test/test/shared/hooks/useDebugValue.jsx: -------------------------------------------------------------------------------- 1 | import { describe, expect, it, vi } from 'vitest'; 2 | import React from 'react'; 3 | 4 | import { useDebugValue } from '../../_helpers/react-compat'; 5 | 6 | export default function describeUseDebugValue({ Wrap }) { 7 | // TODO: `useDebugValue`: test using react debug tools, verify it actually works, and try to add it to `.debug` 8 | describe('hooks: useDebugValue', () => { 9 | function ComponentUsingDebugValue({ value }) { 10 | useDebugValue(`debug value: ${value}`); 11 | 12 | return
{value}
; 13 | } 14 | 15 | function ComponentUsingDebugValueAndCallback({ value, fn = (x) => `debug value: ${x}` }) { 16 | useDebugValue(value, fn); 17 | 18 | return
{value}
; 19 | } 20 | 21 | it('can render component using useDebugValue', () => { 22 | const value = 'foo'; 23 | const wrapper = Wrap(); 24 | expect(wrapper.find('div').prop('children')).to.equal(value); 25 | }); 26 | 27 | it('can render component using useDebugValue and callback', () => { 28 | const value = 'foo'; 29 | const spy = vi.fn(); 30 | const wrapper = Wrap(); 31 | expect(wrapper.find('div').prop('children')).to.equal(value); 32 | expect(spy).to.have.property('callCount', 0); 33 | }); 34 | }); 35 | } 36 | -------------------------------------------------------------------------------- /test/test/shared/methods/filter.jsx: -------------------------------------------------------------------------------- 1 | import { describe, expect, it } from 'vitest'; 2 | import React from 'react'; 3 | 4 | export default function describeFilter({ Wrap }) { 5 | describe('.filter(selector)', () => { 6 | it('returns a new wrapper of just the nodes that matched the selector', () => { 7 | const wrapper = Wrap( 8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
, 18 | ); 19 | 20 | expect(wrapper.find('.foo').filter('.bar')).to.have.lengthOf(3); 21 | expect(wrapper.find('.bar').filter('.foo')).to.have.lengthOf(3); 22 | expect(wrapper.find('.bar').filter('.bax')).to.have.lengthOf(0); 23 | expect(wrapper.find('.foo').filter('.baz.bar')).to.have.lengthOf(2); 24 | }); 25 | 26 | it('only looks in the current wrappers nodes, not their children', () => { 27 | const wrapper = Wrap( 28 |
29 |
30 |
31 |
32 |
33 |
, 34 | ); 35 | 36 | expect(wrapper.find('.foo').filter('.bar')).to.have.lengthOf(1); 37 | }); 38 | }); 39 | } 40 | -------------------------------------------------------------------------------- /test/test/shared/methods/root.jsx: -------------------------------------------------------------------------------- 1 | import { describe, expect, it } from 'vitest'; 2 | import React from 'react'; 3 | 4 | import { itIf } from '../../_helpers'; 5 | 6 | export default function describeRoot({ Wrap, isMount }) { 7 | describe('.root()', () => { 8 | class Fixture extends React.Component { 9 | render() { 10 | return ( 11 |
12 | 13 | 14 |
15 | ); 16 | } 17 | } 18 | 19 | itIf(isMount, 'returns the root component instance', () => { 20 | const wrapper = Wrap(); 21 | const root = wrapper.root(); 22 | expect(root.is(Fixture)).to.equal(true); 23 | expect(root.childAt(0).children().debug()).to.equal('\n\n\n'); 24 | 25 | expect(wrapper.find('span').root()).to.equal(root); 26 | }); 27 | 28 | itIf(!isMount, 'returns the root rendered node', () => { 29 | const wrapper = Wrap(); 30 | const root = wrapper.root(); 31 | expect(root.is('div')).to.equal(true); 32 | expect(root.children().debug()).to.equal('\n\n\n'); 33 | 34 | expect(wrapper.find('span').root()).to.equal(root); 35 | }); 36 | 37 | it('returns the root wrapper from a child', () => { 38 | const wrapper = Wrap(); 39 | const root = wrapper.root(); 40 | expect(wrapper.find('span').root()).to.equal(root); 41 | }); 42 | }); 43 | } 44 | -------------------------------------------------------------------------------- /test/test/shared/methods/get.jsx: -------------------------------------------------------------------------------- 1 | import { describe, expect, it } from 'vitest'; 2 | import React from 'react'; 3 | 4 | export default function describeGet({ Wrap }) { 5 | describe('.get(index)', () => { 6 | it('gets the node at the specified index', () => { 7 | const wrapper = Wrap( 8 |
9 |
10 |
11 |
12 |
13 |
, 14 | ); 15 | const bar = wrapper.find('.bar'); 16 | expect(bar.get(0)).to.deep.equal(wrapper.find('.foo').getElement()); 17 | expect(bar.get(1)).to.deep.equal(wrapper.find('.bax').getElement()); 18 | expect(bar.get(2)).to.deep.equal(wrapper.find('.bux').getElement()); 19 | expect(bar.get(3)).to.deep.equal(wrapper.find('.baz').getElement()); 20 | }); 21 | 22 | it('does not add a "null" key to elements with a ref and no key', () => { 23 | class Foo extends React.Component { 24 | constructor(props) { 25 | super(props); 26 | this.setRef = this.setRef.bind(this); 27 | } 28 | 29 | setRef(node) { 30 | this.node = node; 31 | } 32 | 33 | render() { 34 | return
; 35 | } 36 | } 37 | const wrapper = Wrap(); 38 | expect(wrapper.get(0)).to.have.property('key', null); 39 | }); 40 | }); 41 | } 42 | -------------------------------------------------------------------------------- /test/test/_helpers/describeHooks.js: -------------------------------------------------------------------------------- 1 | import fs from 'node:fs/promises'; 2 | import path from 'node:path'; 3 | import { fileURLToPath } from 'node:url'; 4 | import { asyncForEach } from '@wojtekmaj/async-array-utils'; 5 | 6 | const dirname = path.dirname(fileURLToPath(import.meta.url)); 7 | const workingDirectory = path.join(dirname, '..', 'shared', 'hooks'); 8 | const filenames = await fs.readdir(workingDirectory); 9 | const map = new Map(); 10 | await asyncForEach(filenames, async (methodFilename) => { 11 | const methodName = path.basename(methodFilename, '.jsx'); 12 | map.set(methodName, (await import(path.join(workingDirectory, methodFilename))).default); 13 | }); 14 | 15 | export default function describeHooks({ Wrap, Wrapper }, ...hooks) { 16 | const WrapperName = Wrapper.name; 17 | const isShallow = WrapperName === 'ShallowWrapper'; 18 | const isMount = WrapperName === 'ReactWrapper'; 19 | const hasDOM = isMount; 20 | const makeDOMElement = () => (hasDOM ? global.document.createElement('div') : { nodeType: 1 }); 21 | 22 | hooks.forEach((hook) => { 23 | if (!map.has(hook)) { 24 | throw new Error(`Hook ${hook} does not exist`); 25 | } 26 | 27 | const hookModule = map.get(hook); 28 | 29 | hookModule({ 30 | Wrap, 31 | WrapRendered: isShallow ? Wrap : (...args) => Wrap(...args).children(), 32 | Wrapper, 33 | WrapperName, 34 | isShallow, 35 | isMount, 36 | hasDOM, 37 | makeDOMElement, 38 | }); 39 | }); 40 | } 41 | -------------------------------------------------------------------------------- /test/test/_helpers/describeMethods.js: -------------------------------------------------------------------------------- 1 | import fs from 'node:fs/promises'; 2 | import path from 'node:path'; 3 | import { fileURLToPath } from 'node:url'; 4 | import { asyncForEach } from '@wojtekmaj/async-array-utils'; 5 | 6 | const dirname = path.dirname(fileURLToPath(import.meta.url)); 7 | const workingDirectory = path.join(dirname, '..', 'shared', 'methods'); 8 | const filenames = await fs.readdir(workingDirectory); 9 | const map = new Map(); 10 | await asyncForEach(filenames, async (methodFilename) => { 11 | const methodName = path.basename(methodFilename, '.jsx'); 12 | map.set(methodName, (await import(path.join(workingDirectory, methodFilename))).default); 13 | }); 14 | 15 | export default function describeMethods({ Wrap, Wrapper }, ...methodNames) { 16 | const WrapperName = Wrapper.name; 17 | const isShallow = WrapperName === 'ShallowWrapper'; 18 | const isMount = WrapperName === 'ReactWrapper'; 19 | const hasDOM = isMount; 20 | const makeDOMElement = () => (hasDOM ? global.document.createElement('div') : { nodeType: 1 }); 21 | 22 | methodNames.forEach((methodName) => { 23 | if (!map.has(methodName)) { 24 | throw new Error(`Method ${methodName} does not exist`); 25 | } 26 | 27 | const methodModule = map.get(methodName); 28 | 29 | methodModule({ 30 | Wrap, 31 | WrapRendered: isShallow ? Wrap : (...args) => Wrap(...args).children(), 32 | Wrapper, 33 | WrapperName, 34 | isShallow, 35 | isMount, 36 | hasDOM, 37 | makeDOMElement, 38 | }); 39 | }); 40 | } 41 | -------------------------------------------------------------------------------- /test/test/_helpers/describeLifecycles.js: -------------------------------------------------------------------------------- 1 | import fs from 'node:fs/promises'; 2 | import path from 'node:path'; 3 | import { fileURLToPath } from 'node:url'; 4 | import { asyncForEach } from '@wojtekmaj/async-array-utils'; 5 | 6 | const dirname = path.dirname(fileURLToPath(import.meta.url)); 7 | const workingDirectory = path.join(dirname, '..', 'shared', 'lifecycles'); 8 | const filenames = await fs.readdir(workingDirectory); 9 | const map = new Map(); 10 | await asyncForEach(filenames, async (methodFilename) => { 11 | const methodName = path.basename(methodFilename, '.jsx'); 12 | map.set(methodName, (await import(path.join(workingDirectory, methodFilename))).default); 13 | }); 14 | 15 | export default function describeLifecycles({ Wrap, Wrapper }, ...lifecycles) { 16 | const WrapperName = Wrapper.name; 17 | const isShallow = WrapperName === 'ShallowWrapper'; 18 | const isMount = WrapperName === 'ReactWrapper'; 19 | const hasDOM = isMount; 20 | const makeDOMElement = () => (hasDOM ? global.document.createElement('div') : { nodeType: 1 }); 21 | 22 | lifecycles.forEach((lifecycle) => { 23 | if (!map.has(lifecycle)) { 24 | throw new Error(`Lifecycle ${lifecycle} does not exist`); 25 | } 26 | 27 | const lifecycleModule = map.get(lifecycle); 28 | 29 | lifecycleModule({ 30 | Wrap, 31 | WrapRendered: isShallow ? Wrap : (...args) => Wrap(...args).children(), 32 | Wrapper, 33 | WrapperName, 34 | isShallow, 35 | isMount, 36 | hasDOM, 37 | makeDOMElement, 38 | }); 39 | }); 40 | } 41 | -------------------------------------------------------------------------------- /test/test/shared/hooks/useLayoutEffect.jsx: -------------------------------------------------------------------------------- 1 | import { describe, expect } from 'vitest'; 2 | import React from 'react'; 3 | 4 | import { itIf } from '../../_helpers'; 5 | 6 | import { useLayoutEffect, useState } from '../../_helpers/react-compat'; 7 | 8 | export default function describeUseLayoutEffect({ Wrap, isShallow }) { 9 | describe('hooks: useLayoutEffect', () => { 10 | function ComponentUsingLayoutEffectHook() { 11 | const [ctr, setCtr] = useState(0); 12 | useLayoutEffect(() => { 13 | setCtr(1); 14 | setTimeout(() => { 15 | setCtr(2); 16 | }, 100); 17 | }, []); 18 | return
{ctr}
; 19 | } 20 | 21 | // TODO: enable when https://github.com/facebook/react/issues/15275 is fixed 22 | itIf(!isShallow, 'works with `useLayoutEffect`', () => { 23 | return new Promise((resolve) => { 24 | const wrapper = Wrap(); 25 | 26 | expect(wrapper.debug()).to.equal( 27 | isShallow 28 | ? `
29 | 1 30 |
` 31 | : ` 32 |
33 | 1 34 |
35 |
`, 36 | ); 37 | 38 | setTimeout(() => { 39 | wrapper.update(); 40 | expect(wrapper.debug()).to.equal( 41 | isShallow 42 | ? `
43 | 2 44 |
` 45 | : ` 46 |
47 | 2 48 |
49 |
`, 50 | ); 51 | resolve(); 52 | }, 100); 53 | }); 54 | }); 55 | }); 56 | } 57 | -------------------------------------------------------------------------------- /test/test/shared/methods/map.jsx: -------------------------------------------------------------------------------- 1 | import { describe, expect, it, vi } from 'vitest'; 2 | import React from 'react'; 3 | 4 | export default function describeMap({ Wrap, Wrapper }) { 5 | describe('.map(fn)', () => { 6 | it('calls a function with a wrapper for each node in the wrapper', () => { 7 | const wrapper = Wrap( 8 |
9 |
10 |
11 |
12 |
, 13 | ); 14 | const spy = vi.fn(); 15 | 16 | wrapper.find('.foo').map(spy); 17 | 18 | expect(spy).to.have.property('callCount', 3); 19 | expect(spy.mock.calls[0][0]).to.be.instanceOf(Wrapper); 20 | expect(spy.mock.calls[0][0].hasClass('bax')).to.equal(true); 21 | expect(spy.mock.calls[0][1]).to.equal(0); 22 | expect(spy.mock.calls[1][0]).to.be.instanceOf(Wrapper); 23 | expect(spy.mock.calls[1][0].hasClass('bar')).to.equal(true); 24 | expect(spy.mock.calls[1][1]).to.equal(1); 25 | expect(spy.mock.calls[2][0]).to.be.instanceOf(Wrapper); 26 | expect(spy.mock.calls[2][0].hasClass('baz')).to.equal(true); 27 | expect(spy.mock.calls[2][1]).to.equal(2); 28 | }); 29 | 30 | it('returns an array with the mapped values', () => { 31 | const wrapper = Wrap( 32 |
33 |
34 |
35 |
36 |
, 37 | ); 38 | const result = wrapper.find('.foo').map((w) => w.props().className); 39 | 40 | expect(result).to.eql(['foo bax', 'foo bar', 'foo baz']); 41 | }); 42 | }); 43 | } 44 | -------------------------------------------------------------------------------- /test/test/shared/methods/filterWhere.jsx: -------------------------------------------------------------------------------- 1 | import { describe, expect, it, vi } from 'vitest'; 2 | import React from 'react'; 3 | 4 | export default function describeFilterWhere({ Wrap, Wrapper }) { 5 | describe('.filterWhere(predicate)', () => { 6 | it('filters only the nodes of the wrapper', () => { 7 | const wrapper = Wrap( 8 |
9 |
10 |
11 |
12 |
, 13 | ); 14 | 15 | const stub = vi.fn(); 16 | stub.mockReturnValueOnce(false); 17 | stub.mockReturnValueOnce(true); 18 | stub.mockReturnValueOnce(false); 19 | 20 | const baz = wrapper.find('.foo').filterWhere(stub); 21 | expect(baz).to.have.lengthOf(1); 22 | expect(baz.hasClass('baz')).to.equal(true); 23 | }); 24 | 25 | it('calls the predicate with the wrapped node as the first argument', () => { 26 | const wrapper = Wrap( 27 |
28 |
29 |
30 |
31 |
, 32 | ); 33 | 34 | const spy = vi.fn(); 35 | spy.mockReturnValue(true); 36 | wrapper.find('.foo').filterWhere(spy); 37 | expect(spy).to.have.property('callCount', 3); 38 | expect(spy.mock.calls[0][0]).to.be.instanceOf(Wrapper); 39 | expect(spy.mock.calls[1][0]).to.be.instanceOf(Wrapper); 40 | expect(spy.mock.calls[2][0]).to.be.instanceOf(Wrapper); 41 | expect(spy.mock.calls[0][0].hasClass('bar')).to.equal(true); 42 | expect(spy.mock.calls[1][0].hasClass('baz')).to.equal(true); 43 | expect(spy.mock.calls[2][0].hasClass('bux')).to.equal(true); 44 | }); 45 | }); 46 | } 47 | -------------------------------------------------------------------------------- /test/test/shared/hooks/useMemo.jsx: -------------------------------------------------------------------------------- 1 | import { describe, expect, it } from 'vitest'; 2 | import React from 'react'; 3 | 4 | import { useMemo } from '../../_helpers/react-compat'; 5 | 6 | export default function describeUseMemo({ Wrap }) { 7 | describe('hooks: useMemo', () => { 8 | function RendersNull() { 9 | return null; 10 | } 11 | 12 | it('get same value when using `useMemo` and rerender with same prop in dependencies', () => { 13 | function ComponentUsingMemoHook(props) { 14 | const { count } = props; 15 | const memoized = useMemo(() => ({ result: count * 2 }), [count]); 16 | return ; 17 | } 18 | const wrapper = Wrap(); 19 | const initialValue = wrapper.find(RendersNull).prop('memoized'); 20 | wrapper.setProps({ unRelatedProp: '123' }); 21 | const nextValue = wrapper.find(RendersNull).prop('memoized'); 22 | expect(initialValue).to.equal(nextValue); 23 | }); 24 | 25 | it('get different value when using `useMemo` and rerender with different prop in dependencies', () => { 26 | function ComponentUsingMemoHook(props) { 27 | const { count, relatedProp } = props; 28 | const memoized = useMemo(() => ({ result: count * 2 }), [count, relatedProp]); 29 | return ; 30 | } 31 | const wrapper = Wrap(); 32 | const initialValue = wrapper.find(RendersNull).prop('memoized'); 33 | wrapper.setProps({ relatedProp: '123' }); 34 | const nextValue = wrapper.find(RendersNull).prop('memoized'); 35 | expect(initialValue).not.to.equal(nextValue); 36 | }); 37 | }); 38 | } 39 | -------------------------------------------------------------------------------- /test/test/shared/methods/single.jsx: -------------------------------------------------------------------------------- 1 | import { describe, expect, it } from 'vitest'; 2 | import React from 'react'; 3 | 4 | import { sym } from 'enzyme/build/Utils'; 5 | 6 | export default function describeSingle({ Wrap }) { 7 | describe('#single()', () => { 8 | it('throws if run on multiple nodes', () => { 9 | const wrapper = Wrap( 10 |
11 | 12 | 13 |
, 14 | ).children(); 15 | expect(wrapper).to.have.lengthOf(2); 16 | expect(() => wrapper.single('name!')).to.throw( 17 | Error, 18 | 'Method “name!” is meant to be run on 1 node. 2 found instead.', 19 | ); 20 | }); 21 | 22 | it('throws if run on zero nodes', () => { 23 | const wrapper = Wrap(
).children(); 24 | expect(wrapper).to.have.lengthOf(0); 25 | expect(() => wrapper.single('name!')).to.throw( 26 | Error, 27 | 'Method “name!” is meant to be run on 1 node. 0 found instead.', 28 | ); 29 | }); 30 | 31 | it('throws if run on zero nodes', () => { 32 | const wrapper = Wrap(
).children(); 33 | expect(wrapper).to.have.lengthOf(0); 34 | expect(() => wrapper.single('name!')).to.throw( 35 | Error, 36 | 'Method “name!” is meant to be run on 1 node. 0 found instead.', 37 | ); 38 | }); 39 | 40 | it('works with a name', () => { 41 | const wrapper = Wrap(
); 42 | wrapper.single('foo', (node) => { 43 | expect(node).to.equal(wrapper[sym('__node__')]); 44 | }); 45 | }); 46 | 47 | it('works without a name', () => { 48 | const wrapper = Wrap(
); 49 | wrapper.single((node) => { 50 | expect(node).to.equal(wrapper[sym('__node__')]); 51 | }); 52 | }); 53 | }); 54 | } 55 | -------------------------------------------------------------------------------- /test/test/shared/methods/wrap.jsx: -------------------------------------------------------------------------------- 1 | import { describe, expect, it } from 'vitest'; 2 | import React from 'react'; 3 | 4 | import { itIf } from '../../_helpers'; 5 | 6 | export default function describeWrap({ Wrap, Wrapper, isShallow, isMount }) { 7 | describe('.wrap()', () => { 8 | class Foo extends React.Component { 9 | render() { 10 | return ( 11 |
12 | Hello 13 | Hello 14 |
15 | ); 16 | } 17 | } 18 | 19 | it('returns itself when it is already a Wrapper', () => { 20 | const wrapperDiv = Wrap(
); 21 | const wrapperFoo = Wrap(); 22 | 23 | expect(wrapperDiv.wrap(wrapperFoo)).to.equal(wrapperFoo); 24 | expect(wrapperFoo.wrap(wrapperDiv)).to.equal(wrapperDiv); 25 | }); 26 | 27 | itIf(isShallow, 'wraps when it is not already a Wrapper', () => { 28 | const wrapper = Wrap(); 29 | const el = wrapper.find('a').at(1); 30 | const wrappedEl = wrapper.wrap(el.getElement()); 31 | expect(wrappedEl).to.be.instanceOf(Wrapper); 32 | expect(wrappedEl.props()).to.eql(el.props()); 33 | 34 | expect(wrappedEl.shallow().debug()).to.equal(el.debug()); 35 | }); 36 | 37 | itIf(isMount, 'wraps when it is not already a Wrapper', () => { 38 | const wrapper = Wrap(); 39 | const el = wrapper.find('a').at(1); 40 | const wrappedEl = wrapper.wrap(el.getElement()); 41 | expect(wrappedEl).to.be.instanceOf(Wrapper); 42 | expect(wrappedEl.props()).to.eql(el.props()); 43 | 44 | // FIXME: enable this instead of that: 45 | // expect(wrappedEl.mount().debug()).to.equal(el.debug()); 46 | expect(wrappedEl.debug()).to.equal(''); 47 | }); 48 | }); 49 | } 50 | -------------------------------------------------------------------------------- /test/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "enzyme-test-suite", 3 | "private": true, 4 | "version": "1.0.0", 5 | "description": "JavaScript Testing utilities for React", 6 | "homepage": "https://enzymejs.github.io/enzyme/", 7 | "scripts": { 8 | "lint": "eslint . --ext .js,.jsx", 9 | "prettier": "prettier --check . --cache", 10 | "run": "vitest run test", 11 | "test": "yarn lint && yarn prettier" 12 | }, 13 | "keywords": [ 14 | "javascript", 15 | "shallow rendering", 16 | "shallowRender", 17 | "test", 18 | "reactjs", 19 | "react", 20 | "flux", 21 | "testing", 22 | "test utils", 23 | "assertion helpers", 24 | "tdd", 25 | "mocha" 26 | ], 27 | "author": "Jordan Harband ", 28 | "contributors": [ 29 | "Wojciech Maj " 30 | ], 31 | "license": "MIT", 32 | "dependencies": { 33 | "@wojtekmaj/async-array-utils": "^1.6.1", 34 | "@wojtekmaj/enzyme-adapter-react-17": "portal:../", 35 | "@wojtekmaj/enzyme-adapter-utils": "^0.2.0", 36 | "create-react-class": "^15.7.0", 37 | "enzyme": "^3.11.0", 38 | "enzyme-shallow-equal": "^1.0.0", 39 | "html-element-map": "^1.3.0", 40 | "jsdom": "~15.1.0", 41 | "lodash.isequal": "^4.5.0", 42 | "mocha-wrap": "^2.1.0", 43 | "object-inspect": "^1.11.0", 44 | "prop-types": "^15.7.0", 45 | "raf": "^3.4.1", 46 | "react": "^17.0.0", 47 | "react-dom": "^17.0.0", 48 | "react-is": "^17.0.0", 49 | "vitest": "^0.30.1" 50 | }, 51 | "devDependencies": { 52 | "eslint": "^8.26.0", 53 | "eslint-config-wojtekmaj": "^0.8.3", 54 | "prettier": "^2.7.0" 55 | }, 56 | "repository": { 57 | "type": "git", 58 | "url": "https://github.com/wojtekmaj/enzyme-adapter-react-17.git", 59 | "directory": "test" 60 | } 61 | } 62 | -------------------------------------------------------------------------------- /.github/workflows/dependabot-dedupe.yml: -------------------------------------------------------------------------------- 1 | name: Dedupe Dependabot PRs 2 | 3 | on: 4 | push: 5 | branches: ['dependabot/**'] 6 | 7 | permissions: 8 | contents: write 9 | pull-requests: write 10 | repository-projects: write 11 | 12 | jobs: 13 | dedupe: 14 | name: Dedupe Dependabot PRs 15 | runs-on: ubuntu-latest 16 | 17 | steps: 18 | - name: Checkout 19 | uses: actions/checkout@v3 20 | 21 | - name: Cache .yarn/cache 22 | uses: actions/cache@v3 23 | env: 24 | cache-name: yarn-cache 25 | with: 26 | path: .yarn/cache 27 | key: ${{ runner.os }}-${{ env.cache-name }}-${{ hashFiles('**/yarn.lock') }} 28 | restore-keys: | 29 | ${{ runner.os }}-${{ env.cache-name }} 30 | 31 | - name: Use Node.js 32 | uses: actions/setup-node@v3 33 | with: 34 | node-version: '18' 35 | 36 | - name: Enable Corepack 37 | run: corepack enable 38 | 39 | - name: Configure Git 40 | run: | 41 | git config user.name 'github-actions[bot]' 42 | git config user.email 'github-actions[bot]@users.noreply.github.com' 43 | 44 | - name: Detect working directory 45 | run: | 46 | echo "WORKING_DIRECTORY=$(git log -1 --pretty=%B | sed -n 's/.* in \/\(.*\)$/\1/p')" >> $GITHUB_ENV 47 | 48 | - name: Dedupe dependencies 49 | run: yarn dedupe 50 | working-directory: ${{ env.WORKING_DIRECTORY }} 51 | env: 52 | HUSKY: 0 53 | 54 | - name: Commit changes 55 | run: | 56 | git add . 57 | git commit -m '[dependabot skip] Dedupe dependencies' || true 58 | working-directory: ${{ env.WORKING_DIRECTORY }} 59 | 60 | - name: Push changes 61 | run: git push 62 | working-directory: ${{ env.WORKING_DIRECTORY }} 63 | -------------------------------------------------------------------------------- /test/test/shared/methods/@@iterator.jsx: -------------------------------------------------------------------------------- 1 | import { expect, it } from 'vitest'; 2 | import React from 'react'; 3 | 4 | import { ITERATOR_SYMBOL } from 'enzyme/build/Utils'; 5 | 6 | import { describeIf } from '../../_helpers'; 7 | 8 | export default function describeIterator({ Wrap }) { 9 | describeIf(!!ITERATOR_SYMBOL, '@@iterator', () => { 10 | it('is iterable', () => { 11 | class Foo extends React.Component { 12 | render() { 13 | return ( 14 |
15 | Hello 16 | Hello 17 | Hello 18 | Hello 19 |
20 | ); 21 | } 22 | } 23 | const wrapper = Wrap(); 24 | const [a, b, c, d, ...e] = wrapper.find('a'); 25 | const a1 = wrapper.find('a').get(0); 26 | const b1 = wrapper.find('a').get(1); 27 | const c1 = wrapper.find('a').get(2); 28 | const d1 = wrapper.find('a').get(3); 29 | expect(a1).to.deep.equal(a); 30 | expect(b1).to.deep.equal(b); 31 | expect(c1).to.deep.equal(c); 32 | expect(d1).to.deep.equal(d); 33 | expect(e).to.eql([]); 34 | }); 35 | 36 | it('returns an iterable iterator', () => { 37 | class Foo extends React.Component { 38 | render() { 39 | return ( 40 |
41 | Hello 42 | Hello 43 | Hello 44 | Hello 45 |
46 | ); 47 | } 48 | } 49 | const wrapper = Wrap(); 50 | 51 | const iter = wrapper[ITERATOR_SYMBOL](); 52 | expect(iter).to.have.property(ITERATOR_SYMBOL).and.be.a('function'); 53 | expect(iter[ITERATOR_SYMBOL]()).to.equal(iter); 54 | }); 55 | }); 56 | } 57 | -------------------------------------------------------------------------------- /test/test/shared/methods/exists.jsx: -------------------------------------------------------------------------------- 1 | import { describe, expect, it, vi } from 'vitest'; 2 | import React from 'react'; 3 | 4 | export default function describeExists({ Wrap, Wrapper }) { 5 | describe('.exists()', () => { 6 | it('has no required arguments', () => { 7 | expect(Wrapper.prototype.exists).to.have.lengthOf(0); 8 | }); 9 | 10 | describe('without argument', () => { 11 | it('returns true if node exists in wrapper', () => { 12 | const wrapper = Wrap(
); 13 | expect(wrapper.find('.bar').exists()).to.equal(false); 14 | expect(wrapper.find('.foo').exists()).to.equal(true); 15 | }); 16 | }); 17 | describe('with argument', () => { 18 | it('throws on invalid EnzymeSelector', () => { 19 | const wrapper = Wrap(
); 20 | 21 | expect(() => wrapper.exists(null)).to.throw(TypeError); 22 | expect(() => wrapper.exists(undefined)).to.throw(TypeError); 23 | expect(() => wrapper.exists(45)).to.throw(TypeError); 24 | expect(() => wrapper.exists({})).to.throw(TypeError); 25 | }); 26 | 27 | it('returns .find(arg).exists() instead', () => { 28 | const wrapper = Wrap(
); 29 | const fakeFindExistsReturnVal = { 30 | toString() { 31 | return 'fake .find(arg).exists() return value'; 32 | }, 33 | }; 34 | const fakeSelector = '.someClass'; 35 | wrapper.find = vi.fn().mockReturnValue({ 36 | exists() { 37 | return fakeFindExistsReturnVal; 38 | }, 39 | }); 40 | const existsResult = wrapper.exists(fakeSelector); 41 | expect(wrapper.find).to.have.property('callCount', 1); 42 | expect(wrapper.find.mock.calls[0][0]).to.equal(fakeSelector); 43 | expect(existsResult).to.equal(fakeFindExistsReturnVal); 44 | }); 45 | }); 46 | }); 47 | } 48 | -------------------------------------------------------------------------------- /test/test/shared/methods/render.jsx: -------------------------------------------------------------------------------- 1 | import { describe, expect, it } from 'vitest'; 2 | import React from 'react'; 3 | 4 | export default function describeRender({ Wrap }) { 5 | describe('.render()', () => { 6 | it('returns a cheerio wrapper around the current node', () => { 7 | class Foo extends React.Component { 8 | render() { 9 | return
; 10 | } 11 | } 12 | 13 | class Bar extends React.Component { 14 | render() { 15 | return ( 16 |
17 | 18 |
19 | ); 20 | } 21 | } 22 | 23 | const wrapper = Wrap(); 24 | 25 | expect(wrapper.render().find('.in-foo')).to.have.lengthOf(1); 26 | 27 | const rendered = wrapper.render(); 28 | expect(rendered.is('.in-bar')).to.equal(true); 29 | expect(rendered).to.have.lengthOf(1); 30 | 31 | const renderedFoo = wrapper.find(Foo).render(); 32 | expect(renderedFoo.is('.in-foo')).to.equal(true); 33 | expect(renderedFoo.is('.in-bar')).to.equal(false); 34 | expect(renderedFoo.find('.in-bar')).to.have.lengthOf(0); 35 | }); 36 | 37 | describe('stateless function components (SFCs)', () => { 38 | it('returns a cheerio wrapper around the current node', () => { 39 | const Foo = () =>
; 40 | 41 | const Bar = () => ( 42 |
43 | 44 |
45 | ); 46 | 47 | const wrapper = Wrap(); 48 | 49 | expect(wrapper.render().find('.in-foo')).to.have.lengthOf(1); 50 | expect(wrapper.render().is('.in-bar')).to.equal(true); 51 | 52 | const renderedFoo = wrapper.find(Foo).render(); 53 | expect(renderedFoo.is('.in-foo')).to.equal(true); 54 | expect(renderedFoo.is('.in-bar')).to.equal(false); 55 | expect(renderedFoo.find('.in-bar')).to.have.lengthOf(0); 56 | }); 57 | }); 58 | }); 59 | } 60 | -------------------------------------------------------------------------------- /test/test/shared/methods/closest.jsx: -------------------------------------------------------------------------------- 1 | import { describe, expect, it } from 'vitest'; 2 | import React from 'react'; 3 | 4 | export default function describeClosest({ Wrap }) { 5 | describe('.closest(selector)', () => { 6 | it('returns the closest ancestor for a given selector', () => { 7 | const wrapper = Wrap( 8 |
9 |
10 |
11 |
12 |
13 |
14 |
, 15 | ); 16 | 17 | const closestFoo = wrapper.find('.bar').closest('.foo'); 18 | expect(closestFoo).to.have.lengthOf(1); 19 | expect(closestFoo.hasClass('baz')).to.equal(true); 20 | }); 21 | 22 | it('only ever returns a wrapper of a single node', () => { 23 | const wrapper = Wrap( 24 |
25 |
26 |
27 |
28 |
29 |
30 |
, 31 | ); 32 | 33 | expect(wrapper.find('.baz').parent().hasClass('bar')).to.equal(true); 34 | }); 35 | 36 | it('returns itself if matching', () => { 37 | const wrapper = Wrap( 38 |
39 |
40 |
41 |
42 |
43 |
44 |
, 45 | ); 46 | 47 | expect(wrapper.find('.bux').closest('.baz').hasClass('bux')).to.equal(true); 48 | }); 49 | 50 | it('does not find a nonexistent match', () => { 51 | const wrapper = Wrap( 52 |
53 |
54 |
, 55 | ); 56 | 57 | expect(wrapper.find('.fooooo')).to.have.lengthOf(0); 58 | 59 | const bar = wrapper.find('.bar'); 60 | expect(bar).to.have.lengthOf(1); 61 | 62 | expect(bar.closest('.fooooo')).to.have.lengthOf(0); 63 | }); 64 | }); 65 | } 66 | -------------------------------------------------------------------------------- /test/test/shared/hooks/useCallback.jsx: -------------------------------------------------------------------------------- 1 | import { describe, expect, it } from 'vitest'; 2 | import React from 'react'; 3 | 4 | import { itIf } from '../../_helpers'; 5 | 6 | import { useCallback } from '../../_helpers/react-compat'; 7 | 8 | export default function describeUseCallback({ Wrap, isShallow }) { 9 | describe('hooks: useCallback', () => { 10 | function RendersNull() { 11 | return null; 12 | } 13 | 14 | function ComponentUsingCallbackHook({ onChange }) { 15 | const callback = useCallback((value) => onChange(value), [onChange]); 16 | return ; 17 | } 18 | 19 | function ComponentUsingCallbackHookWithRelatedProp({ onChange, relatedProp }) { 20 | const callback = useCallback((value) => onChange(value), [onChange, relatedProp]); 21 | return ; 22 | } 23 | 24 | // TODO: fix pending https://github.com/facebook/react/issues/15774 25 | itIf( 26 | !isShallow, 27 | 'get same callback when using `useCallback` and rerender with same prop in dependencies', 28 | () => { 29 | const onChange = () => {}; 30 | const wrapper = Wrap(); 31 | const initialCallback = wrapper.find(RendersNull).prop('callback'); 32 | 33 | wrapper.setProps({ unRelatedProp: '123' }); 34 | 35 | const nextCallback = wrapper.find(RendersNull).prop('callback'); 36 | expect(initialCallback).to.equal(nextCallback); 37 | }, 38 | ); 39 | 40 | it('get different callback when using `useCallback` and rerender with different prop in dependencies', () => { 41 | const onChange = () => {}; 42 | const wrapper = Wrap( 43 | , 44 | ); 45 | const initialCallback = wrapper.find(RendersNull).prop('callback'); 46 | 47 | wrapper.setProps({ relatedProp: '123' }); 48 | 49 | const nextCallback = wrapper.find(RendersNull).prop('callback'); 50 | expect(initialCallback).not.to.equal(nextCallback); 51 | }); 52 | }); 53 | } 54 | -------------------------------------------------------------------------------- /test/test/_helpers/react-compat.js: -------------------------------------------------------------------------------- 1 | const { createFactory } = require('react'); 2 | 3 | let createClass; 4 | let renderToString; 5 | let createPortal; 6 | let createContext; 7 | let createRef; 8 | let forwardRef; 9 | let Fragment; 10 | let StrictMode; 11 | let AsyncMode; 12 | let ConcurrentMode; 13 | let createRoot; 14 | let Profiler; 15 | let PureComponent; 16 | let Suspense; 17 | let lazy; 18 | let memo; 19 | let useCallback; 20 | let useContext; 21 | let useDebugValue; 22 | let useEffect; 23 | let useImperativeHandle; 24 | let useLayoutEffect; 25 | let useMemo; 26 | let useReducer; 27 | let useRef; 28 | let useState; 29 | let act; 30 | 31 | createClass = require('create-react-class'); 32 | 33 | ({ renderToString } = require('react-dom/server')); 34 | 35 | ({ createPortal } = require('react-dom')); 36 | 37 | ({ PureComponent } = require('react')); 38 | 39 | ({ Fragment } = require('react')); 40 | 41 | ({ 42 | createContext, 43 | createRef, 44 | forwardRef, 45 | StrictMode, 46 | unstable_AsyncMode: AsyncMode, 47 | } = require('react')); 48 | 49 | ({ Profiler } = require('react')); 50 | 51 | ({ Suspense, lazy, memo } = require('react')); 52 | 53 | ({ unstable_ConcurrentMode: ConcurrentMode } = require('react')); 54 | 55 | ({ unstable_createRoot: createRoot } = require('react')); 56 | 57 | ({ 58 | useCallback, 59 | useContext, 60 | useDebugValue, 61 | useEffect, 62 | useImperativeHandle, 63 | useLayoutEffect, 64 | useMemo, 65 | useReducer, 66 | useRef, 67 | useState, 68 | } = require('react')); 69 | 70 | ({ act } = require('react-dom/test-utils')); 71 | 72 | export { 73 | createClass, 74 | createFactory, 75 | renderToString, 76 | createPortal, 77 | createContext, 78 | createRef, 79 | createRoot, 80 | forwardRef, 81 | Fragment, 82 | StrictMode, 83 | AsyncMode, 84 | ConcurrentMode, 85 | Profiler, 86 | PureComponent, 87 | Suspense, 88 | lazy, 89 | memo, 90 | useCallback, 91 | useContext, 92 | useDebugValue, 93 | useEffect, 94 | useImperativeHandle, 95 | useLayoutEffect, 96 | useMemo, 97 | useReducer, 98 | useRef, 99 | useState, 100 | act, 101 | }; 102 | -------------------------------------------------------------------------------- /test/test/enzyme-shallow-equal.spec.js: -------------------------------------------------------------------------------- 1 | import { describe, expect, it } from 'vitest'; 2 | 3 | import shallowEqual from 'enzyme-shallow-equal'; 4 | 5 | describe('shallowEqual', () => { 6 | it('returns true for things that are SameValue', () => { 7 | [{}, [], NaN, 42, 'foo', '', 0, Infinity, () => {}, /a/g, true, false, null, undefined].forEach( 8 | (x) => { 9 | expect(shallowEqual(x, x)).to.equal(true); 10 | }, 11 | ); 12 | }); 13 | 14 | it('returns false if one is falsy and one is truthy', () => { 15 | [null, undefined, false, 0, NaN].forEach((x) => { 16 | expect(shallowEqual(true, x)).to.equal(false); 17 | }); 18 | }); 19 | 20 | it('returns true if both have zero keys', () => { 21 | expect(shallowEqual({}, {})).to.equal(true); 22 | }); 23 | 24 | it('returns false if they have different numbers of keys', () => { 25 | expect(shallowEqual({ a: 1 }, {})).to.equal(false); 26 | expect(shallowEqual({}, { a: 1 })).to.equal(false); 27 | }); 28 | 29 | it('returns false if they have the same number, but differently named, keys', () => { 30 | expect(shallowEqual({ a: 1 }, { b: 1 })).to.equal(false); 31 | }); 32 | 33 | it('returns false if they have the same keys, with different values', () => { 34 | [{}, [], NaN, 42, 'foo', '', 0, Infinity, () => {}, /a/g, true, false, null, undefined].forEach( 35 | (x) => { 36 | expect(shallowEqual({ a: x }, { a: {} })).to.equal(false); 37 | expect(shallowEqual({ a: {} }, { a: x })).to.equal(false); 38 | }, 39 | ); 40 | }); 41 | 42 | it('returns false if an undefined key in one is absent in the other', () => { 43 | expect(shallowEqual({ a: undefined, b: true }, { b: true, c: undefined })).to.equal(false); 44 | expect(shallowEqual({ c: undefined, b: true }, { b: true, a: undefined })).to.equal(false); 45 | }); 46 | 47 | it('returns true if they have the same keys, with the same values', () => { 48 | [{}, [], NaN, 42, 'foo', '', 0, Infinity, () => {}, /a/g, true, false, null, undefined].forEach( 49 | (x) => { 50 | expect(shallowEqual({ a: x }, { a: x })).to.equal(true); 51 | expect(shallowEqual({ a: x }, { a: x })).to.equal(true); 52 | }, 53 | ); 54 | }); 55 | }); 56 | -------------------------------------------------------------------------------- /test/test/shared/methods/slice.jsx: -------------------------------------------------------------------------------- 1 | import { describe, expect, it } from 'vitest'; 2 | import React from 'react'; 3 | 4 | export default function describeSlice({ Wrap }) { 5 | describe('.slice([begin[, end]])', () => { 6 | it('returns an identical wrapper if no params are set', () => { 7 | const wrapper = Wrap( 8 |
9 |
10 |
11 |
12 |
, 13 | ); 14 | const slice = wrapper.find('.foo').slice(); 15 | expect(slice).to.have.lengthOf(3); 16 | expect(slice.at(0).hasClass('bax')).to.equal(true); 17 | expect(slice.at(1).hasClass('bar')).to.equal(true); 18 | expect(slice.at(2).hasClass('baz')).to.equal(true); 19 | }); 20 | 21 | it('returns a new wrapper if begin is set', () => { 22 | const wrapper = Wrap( 23 |
24 |
25 |
26 |
27 |
, 28 | ); 29 | const slice = wrapper.find('.foo').slice(1); 30 | expect(slice).to.have.lengthOf(2); 31 | expect(slice.at(0).hasClass('bar')).to.equal(true); 32 | expect(slice.at(1).hasClass('baz')).to.equal(true); 33 | }); 34 | 35 | it('returns a new wrapper if begin and end are set', () => { 36 | const wrapper = Wrap( 37 |
38 |
39 |
40 |
41 |
, 42 | ); 43 | const slice = wrapper.find('.foo').slice(1, 2); 44 | expect(slice).to.have.lengthOf(1); 45 | expect(slice.at(0).hasClass('bar')).to.equal(true); 46 | }); 47 | 48 | it('returns a new wrapper if begin and end are set (negative)', () => { 49 | const wrapper = Wrap( 50 |
51 |
52 |
53 |
54 |
, 55 | ); 56 | const slice = wrapper.find('.foo').slice(-2, -1); 57 | expect(slice).to.have.lengthOf(1); 58 | expect(slice.at(0).hasClass('bar')).to.equal(true); 59 | }); 60 | }); 61 | } 62 | -------------------------------------------------------------------------------- /test/test/shared/methods/instance.jsx: -------------------------------------------------------------------------------- 1 | import { describe, expect, it } from 'vitest'; 2 | import React from 'react'; 3 | 4 | import { itIf } from '../../_helpers'; 5 | 6 | export default function describeInstance({ Wrap, WrapperName, isShallow }) { 7 | describe('.instance()', () => { 8 | it('returns the component instance', () => { 9 | class Foo extends React.Component { 10 | render() { 11 | return
; 12 | } 13 | } 14 | 15 | const wrapper = Wrap(); 16 | expect(wrapper.instance()).to.be.instanceof(Foo); 17 | expect(wrapper.instance().render).to.equal(Foo.prototype.render); 18 | }); 19 | 20 | describe('stateless function components (SFCs)', () => { 21 | function SFC() { 22 | return
; 23 | } 24 | 25 | it('has no instance', () => { 26 | const wrapper = Wrap(); 27 | expect(wrapper.instance()).to.equal(null); 28 | }); 29 | 30 | itIf(false, 'has an instance', () => { 31 | const wrapper = Wrap(); 32 | expect(wrapper.instance()).not.to.equal(null); 33 | }); 34 | }); 35 | 36 | itIf(!isShallow, 'throws when wrapping multiple elements', () => { 37 | class Test extends React.Component { 38 | render() { 39 | return ( 40 |
41 | 42 | 43 |
44 | ); 45 | } 46 | } 47 | 48 | const wrapper = Wrap().find('span'); 49 | expect(() => wrapper.instance()).to.throw( 50 | Error, 51 | 'Method “instance” is meant to be run on 1 node. 2 found instead.', 52 | ); 53 | }); 54 | 55 | itIf(isShallow, 'throws if called on something other than the root node', () => { 56 | class Foo extends React.Component { 57 | render() { 58 | return ( 59 |
60 | 61 |
62 | ); 63 | } 64 | } 65 | 66 | const wrapper = Wrap(); 67 | const div = wrapper.find('div'); 68 | 69 | expect(() => div.instance()).to.throw( 70 | Error, 71 | `${WrapperName}::instance() can only be called on the root`, 72 | ); 73 | }); 74 | }); 75 | } 76 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@wojtekmaj/enzyme-adapter-react-17", 3 | "version": "0.8.0", 4 | "description": "JavaScript Testing utilities for React", 5 | "homepage": "https://enzymejs.github.io/enzyme/", 6 | "main": "build", 7 | "workspaces": [ 8 | "test" 9 | ], 10 | "scripts": { 11 | "build": "babel --source-maps=both src -d build", 12 | "clean": "rimraf build", 13 | "lint": "eslint src test --ext .js,.jsx", 14 | "postinstall": "husky install", 15 | "prepack": "yarn clean && yarn build", 16 | "prettier": "prettier --check . --cache", 17 | "test": "yarn lint && yarn prettier" 18 | }, 19 | "keywords": [ 20 | "javascript", 21 | "shallow rendering", 22 | "shallowRender", 23 | "test", 24 | "reactjs", 25 | "react", 26 | "flux", 27 | "testing", 28 | "test utils", 29 | "assertion helpers", 30 | "tdd", 31 | "mocha" 32 | ], 33 | "author": "Jordan Harband ", 34 | "contributors": [ 35 | "Wojciech Maj " 36 | ], 37 | "license": "MIT", 38 | "dependencies": { 39 | "@wojtekmaj/enzyme-adapter-utils": "^0.2.0", 40 | "enzyme-shallow-equal": "^1.0.0", 41 | "has": "^1.0.0", 42 | "prop-types": "^15.7.0", 43 | "react-is": "^17.0.0", 44 | "react-test-renderer": "^17.0.0" 45 | }, 46 | "devDependencies": { 47 | "@babel/cli": "^7.15.0", 48 | "@babel/core": "^7.15.0", 49 | "@babel/preset-env": "^7.15.0", 50 | "@babel/preset-react": "^7.15.0", 51 | "enzyme": "^3.0.0", 52 | "eslint": "^8.26.0", 53 | "eslint-config-wojtekmaj": "^0.8.3", 54 | "husky": "^8.0.0", 55 | "prettier": "^2.7.0", 56 | "pretty-quick": "^3.1.0", 57 | "react": "^17.0.0", 58 | "react-dom": "^17.0.0", 59 | "rimraf": "^3.0.0" 60 | }, 61 | "peerDependencies": { 62 | "enzyme": "^3.0.0", 63 | "react": "^17.0.0-0", 64 | "react-dom": "^17.0.0-0" 65 | }, 66 | "publishConfig": { 67 | "access": "public" 68 | }, 69 | "files": [ 70 | "build", 71 | "src", 72 | "index.d.ts" 73 | ], 74 | "repository": { 75 | "type": "git", 76 | "url": "https://github.com/wojtekmaj/enzyme-adapter-react-17.git" 77 | }, 78 | "funding": "https://github.com/wojtekmaj/enzyme-adapter-react-17?sponsor=1", 79 | "packageManager": "yarn@3.1.0" 80 | } 81 | -------------------------------------------------------------------------------- /test/test/shared/lifecycles/getSnapshotBeforeUpdate.jsx: -------------------------------------------------------------------------------- 1 | import { describe, it } from 'vitest'; 2 | import React from 'react'; 3 | 4 | import { argSpy, expectArgs } from '../../_helpers'; 5 | 6 | export default function describeGSBU({ Wrap }) { 7 | describe('getSnapshotBeforeUpdate()', () => { 8 | it('calls getSnapshotBeforeUpdate and pass snapshot to componentDidUpdate', () => { 9 | const spy = argSpy(); 10 | 11 | class Foo extends React.Component { 12 | constructor(props) { 13 | super(props); 14 | this.state = { 15 | foo: 'bar', 16 | }; 17 | } 18 | 19 | componentDidUpdate(prevProps, prevState, snapshot) { 20 | spy('componentDidUpdate', prevProps, this.props, prevState, this.state, snapshot); 21 | } 22 | 23 | getSnapshotBeforeUpdate(prevProps, prevState) { 24 | spy('getSnapshotBeforeUpdate', prevProps, this.props, prevState, this.state); 25 | return { snapshot: 'ok' }; 26 | } 27 | 28 | render() { 29 | spy('render'); 30 | return
foo
; 31 | } 32 | } 33 | const wrapper = Wrap(); 34 | expectArgs(spy, 1, [['render']]); 35 | 36 | wrapper.setProps({ name: 'bar' }); 37 | expectArgs(spy, 2, [ 38 | ['render'], 39 | [ 40 | 'getSnapshotBeforeUpdate', 41 | { name: 'foo' }, 42 | { name: 'bar' }, 43 | { foo: 'bar' }, 44 | { foo: 'bar' }, 45 | ], 46 | [ 47 | 'componentDidUpdate', 48 | { name: 'foo' }, 49 | { name: 'bar' }, 50 | { foo: 'bar' }, 51 | { foo: 'bar' }, 52 | { snapshot: 'ok' }, 53 | ], 54 | ]); 55 | 56 | wrapper.setState({ foo: 'baz' }); 57 | expectArgs(spy, 3, [ 58 | ['render'], 59 | [ 60 | 'getSnapshotBeforeUpdate', 61 | { name: 'bar' }, 62 | { name: 'bar' }, 63 | { foo: 'bar' }, 64 | { foo: 'baz' }, 65 | ], 66 | [ 67 | 'componentDidUpdate', 68 | { name: 'bar' }, 69 | { name: 'bar' }, 70 | { foo: 'bar' }, 71 | { foo: 'baz' }, 72 | { snapshot: 'ok' }, 73 | ], 74 | ]); 75 | }); 76 | }); 77 | } 78 | -------------------------------------------------------------------------------- /test/test/shared/hooks/useReducer.jsx: -------------------------------------------------------------------------------- 1 | import { describe, expect, it } from 'vitest'; 2 | import React from 'react'; 3 | 4 | import { useReducer } from '../../_helpers/react-compat'; 5 | 6 | export default function describeUseReducer({ Wrap }) { 7 | describe('hooks: useReducer', () => { 8 | describe('with custom dispatch', () => { 9 | const initialState = []; 10 | 11 | function Child({ dispatch, text }) { 12 | function fire() { 13 | dispatch({ 14 | type: 'ADD_TEXT', 15 | payload: text, 16 | }); 17 | } 18 | 19 | return ( 20 | 24 | ); 25 | } 26 | 27 | function reducer(state, action) { 28 | switch (action.type) { 29 | case 'ADD_TEXT': 30 | return [...state, action.payload]; 31 | default: 32 | throw new Error(); 33 | } 34 | } 35 | 36 | function FooBarTextList() { 37 | const [state, dispatch] = useReducer(reducer, initialState); 38 | 39 | return ( 40 |
41 | 42 | 43 | {state.map((text) => ( 44 |

{text}

45 | ))} 46 |
47 | ); 48 | } 49 | 50 | it('render with initial state from useReducer', () => { 51 | const wrapper = Wrap(); 52 | expect(wrapper.find('p')).to.have.lengthOf(0); 53 | }); 54 | 55 | it('Test with Add Foo & Bar tex', () => { 56 | const wrapper = Wrap(); 57 | expect(wrapper.find('p')).to.have.lengthOf(0); 58 | wrapper.find('Child').at(0).props().dispatch({ 59 | type: 'ADD_TEXT', 60 | payload: 'foo', 61 | }); 62 | wrapper.update(); 63 | 64 | expect(wrapper.find('p')).to.have.lengthOf(1); 65 | expect(wrapper.find('p').at(0).text()).to.equal('foo'); 66 | 67 | wrapper.find('Child').at(1).props().dispatch({ 68 | type: 'ADD_TEXT', 69 | payload: 'bar', 70 | }); 71 | wrapper.update(); 72 | expect(wrapper.find('p')).to.have.length(2); 73 | expect(wrapper.find('p').at(0).text()).to.equal('foo'); 74 | expect(wrapper.find('p').at(1).text()).to.equal('bar'); 75 | }); 76 | }); 77 | }); 78 | } 79 | -------------------------------------------------------------------------------- /test/test/shared/methods/reduce.jsx: -------------------------------------------------------------------------------- 1 | import { describe, expect, it, vi } from 'vitest'; 2 | import React from 'react'; 3 | 4 | import { getElementPropSelector, getWrapperPropSelector } from '../../_helpers/selectors'; 5 | 6 | export default function describeReduce({ Wrap, Wrapper }) { 7 | describe('.reduce(fn[, initialValue])', () => { 8 | it('has the right length', () => { 9 | expect(Wrapper.prototype.reduce).to.have.lengthOf(1); 10 | }); 11 | 12 | it('calls a function with a wrapper for each node in the wrapper', () => { 13 | const wrapper = Wrap( 14 |
15 |
16 |
17 |
18 |
, 19 | ); 20 | const spy = vi.fn((n) => n + 1); 21 | 22 | wrapper.find('.foo').reduce(spy, 0); 23 | 24 | expect(spy).to.have.property('callCount', 3); 25 | expect(spy.mock.calls[0][1]).to.be.instanceOf(Wrapper); 26 | expect(spy.mock.calls[0][1].hasClass('bax')).to.equal(true); 27 | expect(spy.mock.calls[1][1]).to.be.instanceOf(Wrapper); 28 | expect(spy.mock.calls[1][1].hasClass('bar')).to.equal(true); 29 | expect(spy.mock.calls[2][1]).to.be.instanceOf(Wrapper); 30 | expect(spy.mock.calls[2][1].hasClass('baz')).to.equal(true); 31 | }); 32 | 33 | it('accumulates a value', () => { 34 | const wrapper = Wrap( 35 |
36 |
37 |
38 |
39 |
, 40 | ); 41 | const result = wrapper.find('.foo').reduce((obj, n) => { 42 | obj[n.prop('id')] = n.prop('className'); 43 | return obj; 44 | }, {}); 45 | 46 | expect(result).to.eql({ 47 | bax: 'foo qoo', 48 | bar: 'foo boo', 49 | baz: 'foo hoo', 50 | }); 51 | }); 52 | 53 | it('allows the initialValue to be omitted', () => { 54 | const one =
; 55 | const two =
; 56 | const three =
; 57 | const wrapper = Wrap( 58 |
59 | {one} 60 | {two} 61 | {three} 62 |
, 63 | ); 64 | const counter =