From b72dd97c636cc03a05545f95e7e21989d629c219 Mon Sep 17 00:00:00 2001 From: Nicholas Boll Date: Fri, 14 Jan 2022 14:11:36 -0700 Subject: [PATCH] test: Remove enzyme (#1418) Enzyme is not being supported long term. The React 17 adapter is unofficial and there may not be an adapter for future versions of React. [category:Test] --- .github/workflows/pull-request.yml | 12 +- jest/setupTests.ts | 3 - .../header/spec/GlobalHeader.spec.tsx | 103 +-- .../labs-react/header/spec/Header.spec.tsx | 132 +--- modules/preview-react/menu/lib/MenuItem.tsx | 2 +- modules/preview-react/menu/spec/Menu.spec.tsx | 696 +++++++----------- .../react/action-bar/spec/ActionBar.spec.tsx | 21 +- modules/react/banner/spec/Banner.spec.tsx | 15 +- .../react/common/spec/InputProvider.spec.tsx | 17 +- modules/react/common/spec/useTheme.spec.tsx | 35 +- .../cookie-banner/spec/CookieBanner.spec.tsx | 54 +- modules/react/icon/spec/AccentIcon.spec.tsx | 15 +- modules/react/icon/spec/AppletIcon.spec.tsx | 15 +- modules/react/icon/spec/Graphic.spec.tsx | 25 +- modules/react/icon/spec/Svg.spec.tsx | 15 +- modules/react/icon/spec/SystemIcon.spec.tsx | 16 +- modules/react/layout/spec/Layout.spec.tsx | 21 +- .../spec/LoadingDots.spec.tsx | 12 +- .../page-header/spec/PageHeader.spec.tsx | 63 +- .../react/side-panel/spec/SidePanel.spec.tsx | 57 +- .../spec/StatusIndicator.spec.tsx | 13 +- modules/react/table/spec/Table.spec.tsx | 20 +- modules/react/table/spec/TableRow.spec.tsx | 20 +- .../spec/InputIconContainer.spec.tsx | 14 +- package.json | 4 - utils/check-dependencies-exist.js | 1 - yarn.lock | 281 +------ 27 files changed, 510 insertions(+), 1172 deletions(-) diff --git a/.github/workflows/pull-request.yml b/.github/workflows/pull-request.yml index df8b239f70..a0be59f16d 100644 --- a/.github/workflows/pull-request.yml +++ b/.github/workflows/pull-request.yml @@ -5,6 +5,8 @@ on: pull_request jobs: install: runs-on: ubuntu-latest + outputs: + cypress-version: ${{ steps.cypress-version.outputs.version }} steps: - uses: actions/checkout@v2 @@ -51,7 +53,7 @@ jobs: - uses: actions/cache/@v2 with: path: .cache/cypress - key: ${{ runner.os }}-cypress-cache-version-${{ steps.cypress-version.outputs.version }} + key: ${{ runner.os }}-cypress-cache-version-${{ needs.install.outputs.cypress-version }} - uses: actions/cache@v2 with: @@ -84,7 +86,7 @@ jobs: - uses: actions/cache/@v2 with: path: .cache/cypress - key: ${{ runner.os }}-cypress-cache-version-${{ steps.cypress-version.outputs.version }} + key: ${{ runner.os }}-cypress-cache-version-${{ needs.install.outputs.cypress-version }} - uses: actions/cache@v2 with: @@ -116,7 +118,7 @@ jobs: - uses: actions/cache/@v2 with: path: .cache/cypress - key: ${{ runner.os }}-cypress-cache-version-${{ steps.cypress-version.outputs.version }} + key: ${{ runner.os }}-cypress-cache-version-${{ needs.install.outputs.cypress-version }} - uses: actions/cache@v2 with: @@ -139,7 +141,7 @@ jobs: integration-test: runs-on: ubuntu-latest - needs: 'build' + needs: ['install', 'build'] strategy: fail-fast: false matrix: @@ -156,7 +158,7 @@ jobs: - uses: actions/cache/@v2 with: path: .cache/cypress - key: ${{ runner.os }}-cypress-cache-version-${{ steps.cypress-version.outputs.version }} + key: ${{ runner.os }}-cypress-cache-version-${{ needs.install.outputs.cypress-version }} - uses: actions/cache@v2 with: diff --git a/jest/setupTests.ts b/jest/setupTests.ts index 8b9de0a1b3..8280fafd0d 100644 --- a/jest/setupTests.ts +++ b/jest/setupTests.ts @@ -1,5 +1,3 @@ -import Adapter from 'enzyme-adapter-react-16'; -import {configure} from 'enzyme'; import {toHaveNoViolations} from 'jest-axe'; import {matchers} from 'jest-emotion'; import '@testing-library/jest-dom/extend-expect'; @@ -10,7 +8,6 @@ import {setUniqueSeed, resetUniqueIdCount} from '@workday/canvas-kit-react/commo expect.extend(toHaveNoViolations); expect.extend(matchers); -configure({adapter: new Adapter()}); // add convenience variables to the global context (global as any).verifyComponent = verifyComponent; diff --git a/modules/labs-react/header/spec/GlobalHeader.spec.tsx b/modules/labs-react/header/spec/GlobalHeader.spec.tsx index 835d3ac7a2..72557afb5b 100644 --- a/modules/labs-react/header/spec/GlobalHeader.spec.tsx +++ b/modules/labs-react/header/spec/GlobalHeader.spec.tsx @@ -1,111 +1,26 @@ import * as React from 'react'; import DeprecatedGlobalHeader from '../lib/GlobalHeader'; -import {shallow} from 'enzyme'; -import DeprecatedHeader from '../lib/Header'; -import {SearchForm} from '@workday/canvas-kit-labs-react/search-form'; -import {DeprecatedDubLogoTitle} from '../lib/parts'; -import {DeprecatedHeaderTheme, DeprecatedHeaderVariant} from '../lib/shared/types'; -declare global { - interface Window { - resizeBy: (x: number, y: number) => void; - } -} -const map: {[key: string]: any} = {}; -window.addEventListener = jest.fn((event, callback) => { - map[event] = callback; -}); -window.removeEventListener = jest.fn((event, callback) => { - if (map[event] && map[event] === callback) { - delete map[event]; - } -}); -window.dispatchEvent = (event: Event) => { - if (map[event.type]) { - map[event.type](); - } - - // TODO: not totally accurate - return false; -}; - -window.resizeBy = (x: number, y: number) => { - // @ts-ignore - window.innerWidth = x; - // @ts-ignore - window.innerHeight = y; - window.dispatchEvent(new Event('resize')); -}; - -// @ts-ignore -window.requestAnimationFrame = cbFn => cbFn(); +import {render} from '@testing-library/react'; describe('DeprecatedGlobalHeader', () => { - const cb = jest.fn(); - beforeEach(() => { - window.resizeBy(1280, 1024); - }); - - afterEach(() => { - cb.mockReset(); - }); - describe('How DeprecatedGlobalHeader children render', () => { beforeEach(() => { window.resizeBy(1280, 1024); }); - test('Renders non-React child elements as is', () => { - const text = 'not a react element'; - const wrapper = shallow( - {text} - ); + it('should render non-element children as is', () => { + const text = 'not an element'; + const {container} = render({text}); - expect(wrapper.contains(text)); + expect(container).toContainHTML(text); }); - test('Renders a div element as is', () => { - const wrapper = shallow( - -
Test
-
- ); - expect( - wrapper - .find('div') - .first() - .contains('Test') - ).toBeTruthy(); - }); - - test('Passes props to Header correctly', () => { - const propsHeader1 = { - brand:
Brand
, - menuToggle:
MenuToggle
, - onMenuClick: jest.fn(), - leftSlot: , - isCollapsed: true, - }; - const propsHeader2 = { - menuToggle: 'abcde', - isCollapsed: false, - themeColor: DeprecatedHeaderTheme.White, - }; - const defaultProps = { - brand: , - variant: DeprecatedHeaderVariant.Global, - children: undefined, - }; - - const childPropsHeader1 = shallow() - .find(DeprecatedHeader) - .props(); - const childPropsHeader2 = shallow() - .find(DeprecatedHeader) - .props(); + it('should render a div element as is', () => { + const html =
Test
; + const {container} = render({html}); - expect(childPropsHeader1).toEqual({...defaultProps, ...propsHeader1}); - expect(childPropsHeader2).toEqual({...defaultProps, ...propsHeader2}); + expect(container).toContainHTML('
Test
'); }); }); }); diff --git a/modules/labs-react/header/spec/Header.spec.tsx b/modules/labs-react/header/spec/Header.spec.tsx index 818e742111..1b0f03ce7b 100644 --- a/modules/labs-react/header/spec/Header.spec.tsx +++ b/modules/labs-react/header/spec/Header.spec.tsx @@ -1,120 +1,44 @@ import * as React from 'react'; import Header from '../lib/Header'; -import {shallow, mount} from 'enzyme'; import {IconButton} from '@workday/canvas-kit-react/button'; -import {SystemIcon} from '@workday/canvas-kit-react/icon'; -import {activityStreamIcon, justifyIcon} from '@workday/canvas-system-icons-web'; - -declare global { - interface Window { - resizeBy: (x: number, y: number) => void; - } -} -const map: {[key: string]: any} = {}; -window.addEventListener = jest.fn((event, callback) => { - map[event] = callback; -}); -window.removeEventListener = jest.fn((event, callback) => { - if (map[event] && map[event] === callback) { - delete map[event]; - } -}); -window.dispatchEvent = (event: Event) => { - if (map[event.type]) { - map[event.type](); - } - - // TODO: not totally accurate - return false; -}; - -window.resizeBy = (x: number, y: number) => { - // @ts-ignore - window.innerWidth = x; - // @ts-ignore - window.innerHeight = y; - window.dispatchEvent(new Event('resize')); -}; - -// @ts-ignore -window.requestAnimationFrame = cbFn => cbFn(); +import {activityStreamIcon} from '@workday/canvas-system-icons-web'; -describe('Header', () => { - const cb = jest.fn(); - beforeEach(() => { - window.resizeBy(1280, 1024); - }); +import {screen, render, fireEvent} from '@testing-library/react'; - afterEach(() => { - cb.mockReset(); - }); +describe('Header', () => { + it('should spread extra props to containing element', () => { + render(
); - test('Header should spread extra props', () => { - const component = mount(
); - const container = component.at(0).getDOMNode(); - expect(container.getAttribute('data-propspread')).toBe('test'); - component.unmount(); + expect(screen.getByTestId('test')).toHaveAttribute('data-propspread', 'test'); }); describe('How Header children render', () => { - beforeEach(() => { - window.resizeBy(1280, 1024); - }); + it('should render non-element children as is', () => { + const text = 'not an element'; - test('Renders non-React child elements as is', () => { - const text = 'not a react element'; - const wrapper = shallow
(
{text}
); + const {container} = render(
{text}
); - expect(wrapper.contains(text)); + expect(container).toContainHTML(text); }); - test('Renders a div element as is', () => { - const wrapper = shallow
( -
-
Test
-
- ); - expect( - wrapper - .find('div') - .first() - .contains('Test') - ).toBeTruthy(); - }); + it('should render children as is', () => { + const children =
Test
; + + const {container} = render(
{children}
); - test('Converts SystemIcons into IconButtons matching theme', () => { - const theme = Header.Theme.Blue; - - const wrapper = mount
( -
- - - -
- ); - const renderedIcon = wrapper.find(IconButton).first(); - - expect(wrapper.find(IconButton)).toHaveLength(1); - expect(renderedIcon.props().icon).toBe(activityStreamIcon); - expect(renderedIcon.props().variant).toBe('inverse'); - expect(renderedIcon.props().onClick).toBeTruthy(); + expect(container).toContainHTML('
Test
'); }); - test('Renders a child hamburger menu (IconButton) when isCollapsed is true', () => { - const wrapper = mount
( -
- -
- ); - const renderedIcon = wrapper.find(IconButton).first(); + it('should render a hamburger menu with a justify icon when "isCollapsed" is true', () => { + render(
); - expect(renderedIcon.props().icon).toBe(justifyIcon); + expect(screen.getByRole('button', {name: 'Open Menu'})).toContainHTML('wd-icon-justify'); }); - describe('When rendered in collapsed mode', () => { - test('Calls onMenuClick when the menuToggle does not have an onClick prop', () => { + describe('when rendered in collapsed mode', () => { + it('should call "onMenuClick" when the "menuToggle" component does not have an "onClick" prop', () => { const onMenuClick = jest.fn(); - const wrapper = mount
( + render(
{ /> ); - wrapper.find('button').simulate('click'); + fireEvent.click(screen.getByRole('button', {name: 'Activity Stream'})); expect(onMenuClick).toHaveBeenCalled(); }); - test('Does not overwrite the menuToggle onClick prop when onMenuClick is defined', () => { + it('should not call "onMenuClick" when the "menuToggle" component has an "onClick" prop', () => { const onMenuClick = jest.fn(); const onIconClick = jest.fn(); - const wrapper = mount
( + render(
{ /> ); - wrapper.find('button').simulate('click'); + fireEvent.click(screen.getByRole('button', {name: 'Activity Stream'})); expect(onMenuClick).not.toHaveBeenCalled(); expect(onIconClick).toHaveBeenCalled(); }); - test('Does not overwrite the menuToggle onClick prop when onMenuClick is undefined', () => { + it('should not overwrite the "menuToggle" onClick when "onMenuClick" is not provided', () => { const onIconClick = jest.fn(); - const wrapper = mount
( + render(
{ /> ); - wrapper.find('button').simulate('click'); + fireEvent.click(screen.getByRole('button', {name: 'Activity Stream'})); expect(onIconClick).toHaveBeenCalled(); }); diff --git a/modules/preview-react/menu/lib/MenuItem.tsx b/modules/preview-react/menu/lib/MenuItem.tsx index c49b4d3d97..3ee83177bf 100644 --- a/modules/preview-react/menu/lib/MenuItem.tsx +++ b/modules/preview-react/menu/lib/MenuItem.tsx @@ -274,7 +274,7 @@ class MenuItem extends React.Component { id={id} role={role} onClick={this.handleClick} - aria-disabled={!!isDisabled} + aria-disabled={isDisabled ? true : undefined} isDisabled={!!isDisabled} isFocused={!!isFocused} {...elemProps} diff --git a/modules/preview-react/menu/spec/Menu.spec.tsx b/modules/preview-react/menu/spec/Menu.spec.tsx index cc2556a8f9..2457783a2e 100644 --- a/modules/preview-react/menu/spec/Menu.spec.tsx +++ b/modules/preview-react/menu/spec/Menu.spec.tsx @@ -1,265 +1,175 @@ import * as React from 'react'; import Menu, {MenuItem} from '../index'; -import {mount} from 'enzyme'; +import {screen, render, fireEvent} from '@testing-library/react'; describe('Menu', () => { const cb = jest.fn(); - afterEach(() => { - cb.mockReset(); - }); - test('should call a callback function when item is clicked', () => { - const component = mount( + it('should call the "onClick" event when an item is clicked', () => { + render( - + Option ); - const item = component.find('li'); - item.simulate('click'); - expect(cb.mock.calls.length).toBe(1); - component.unmount(); + + fireEvent.click(screen.getByRole('menuitem', {name: 'Option'})); + + expect(cb).toBeCalledTimes(1); }); - test('should call on select function when item is clicked', () => { - const component = mount( + it('should call the "onSelect" event when an item is clicked', () => { + render( - + Option ); - const item = component.find('li'); - item.simulate('click'); - expect(cb.mock.calls.length).toBe(1); - component.unmount(); + + fireEvent.click(screen.getByRole('menuitem', {name: 'Option'})); + + expect(cb).toBeCalledTimes(1); }); - test('should call on close function when item is clicked', () => { - const component = mount( + it('should call the "onClose" event when an item is clicked', () => { + render( - + Option ); - const item = component.find('li'); - item.simulate('click'); - expect(cb.mock.calls.length).toBe(1); - component.unmount(); + + fireEvent.click(screen.getByRole('menuitem', {name: 'Option'})); + + expect(cb).toBeCalledTimes(1); }); - test('should call on close function when item is clicked with shouldClose enabled', () => { - const component = mount( + it('should call the "onClose" event when an item is clicked and "shouldClose" is true', () => { + render( - + Option ); - const item = component.find('li'); - item.simulate('click'); - expect(cb.mock.calls.length).toBe(0); - component.unmount(); + + fireEvent.click(screen.getByRole('menuitem', {name: 'Option'})); + + expect(cb).toBeCalledTimes(1); }); - test('should not call on close function when item is clicked with shouldClose disabled', () => { - const component = mount( + it('should not call the "onClose" event when an item is clicked and "shouldClose" is false', () => { + render( - + Option ); - const item = component.find('li'); - item.simulate('click'); - expect(cb.mock.calls.length).toBe(0); - component.unmount(); + + fireEvent.click(screen.getByRole('menuitem', {name: 'Option'})); + + expect(cb).not.toHaveBeenCalled(); }); - test('should not call a callback function when disabled', () => { - const component = mount( - - + it('should not call the "onClose" event when an item is clicked and disabled', () => { + render( + + Option ); - const item = component.find('li'); - item.simulate('click'); - expect(cb.mock.calls.length).toBe(0); - component.unmount(); - }); - test('should not call a callback function when none is passed in', () => { - const component = mount(); - const item = component.find('li'); - item.simulate('click'); - expect(cb.mock.calls.length).toBe(0); - component.unmount(); + fireEvent.click(screen.getByRole('menuitem', {name: 'Option'})); + + expect(cb).not.toHaveBeenCalled(); }); - test('should not call on select function when disabled', () => { - const component = mount( + it('should not call the "onSelect" event when an item is clicked and disabled', () => { + render( - + Option ); - const item = component.find('li'); - item.simulate('click'); - expect(cb.mock.calls.length).toBe(0); - component.unmount(); - }); - test('should not call on close function when disabled', () => { - const component = mount( - - - - ); - const item = component.find('li'); - item.simulate('click'); - expect(cb.mock.calls.length).toBe(0); - component.unmount(); + fireEvent.click(screen.getByRole('menuitem', {name: 'Option'})); + + expect(cb).not.toHaveBeenCalled(); }); - test('menu item should render children correctly', () => { - const labelText: JSX.Element = Menu Label; - const component = mount({labelText}); - expect(component.find('span').getDOMNode().innerHTML).toEqual( - mount(labelText).getDOMNode().outerHTML + it('should render a menu item with children', () => { + render( + + Option + ); - component.unmount(); + + expect(screen.getByRole('menuitem', {name: 'Option'})).toContainHTML('Option'); }); - test('Menu should spread extra props', () => { - const component = mount(); - const list = component - .find('ul') // TODO: Standardize on prop spread location (see #150) - .getDOMNode(); - expect(list.getAttribute('data-propspread')).toBe('test'); - component.unmount(); + it('should forward extra Menu props to the element', () => { + render(); + + expect(screen.getByRole('menu')).toHaveAttribute('data-propspread', 'test'); }); - test('Menu Item should spread extra props', () => { - const component = mount(); - const container = component.at(0).getDOMNode(); - expect(container.getAttribute('data-propspread')).toBe('test'); - component.unmount(); + it('should forward extra MenuItem props to the element', () => { + render(Option); + + expect(screen.getByRole('menuitem', {name: 'Option'})).toHaveAttribute( + 'data-propspread', + 'test' + ); }); }); describe('Menu Accessibility', () => { // https://www.w3.org/TR/wai-aria-practices-1.1/examples/menu-button/menu-button-actions-active-descendant.html - const cb = jest.fn(); - afterEach(() => { - cb.mockReset(); + + it('should render Menu as [role="menu"]', () => { + render(); + + expect(screen.getByRole('menu')).toBeInTheDocument(); }); - test('menu should be using