diff --git a/packages/eui/src/services/accessibility/html_id_generator.test.tsx b/packages/eui/src/services/accessibility/html_id_generator.test.tsx index 56dfe712a8b..ca57d17aa8d 100644 --- a/packages/eui/src/services/accessibility/html_id_generator.test.tsx +++ b/packages/eui/src/services/accessibility/html_id_generator.test.tsx @@ -7,12 +7,11 @@ */ import React, { FunctionComponent } from 'react'; -import { shallow } from 'enzyme'; import * as uuid from 'uuid'; -import { render } from '../../test/rtl'; +import { render, renderHook } from '../../test/rtl'; +import { testOnReactVersion } from '../../test/internal'; import { htmlIdGenerator, useGeneratedHtmlId } from './html_id_generator'; -import { testOnReactVersion } from '../../test/internal'; const originalUuid = jest.requireActual('uuid'); jest.mock('uuid'); @@ -65,41 +64,38 @@ describe('htmlIdGenerator', () => { describe('useGeneratedHtmlId', () => { it('does not change when a component updates', () => { - const MockComponent: React.FC = (props) => ( + const MockComponent: React.FC<{ className?: string }> = (props) => (
); - const component = shallow(); - const initialId = component.find('div').prop('id'); + const { container, rerender } = render(); + const initialId = container.firstElementChild!.id; - component.setProps({ className: 'test' }); - const rerenderedId = component.find('div').prop('id'); + rerender(); + const rerenderedId = container.firstElementChild!.id; expect(initialId).toEqual(rerenderedId); }); it('passes prefixes and suffixes to htmlIdGenerator', () => { - const MockComponent: React.FC = () => ( -
+ const { result } = renderHook(() => + useGeneratedHtmlId({ prefix: 'hello', suffix: 'world' }) ); - const component = shallow(); - const id = component.find('div').prop('id'); - expect(id!.startsWith('hello')).toBeTruthy(); - expect(id!.endsWith('world')).toBeTruthy(); + expect(result.current.startsWith('hello')).toBeTruthy(); + expect(result.current.endsWith('world')).toBeTruthy(); }); it('allows overriding generated IDs with conditional IDs (typically from props)', () => { - const MockComponent: React.FC<{ id?: string }> = ({ id, ...props }) => ( -
- ); - const component = shallow(); - expect(component.find('div').prop('id')).toEqual('hello'); + const { result, rerender } = renderHook(useGeneratedHtmlId, { + initialProps: { conditionalId: 'hello' }, + }); + expect(result.current).toEqual('hello'); - component.setProps({ id: 'world' }); - expect(component.find('div').prop('id')).toEqual('world'); + rerender({ conditionalId: 'world' }); + expect(result.current).toEqual('world'); - component.setProps({ id: undefined }); - expect(component.find('div').prop('id')).toBeTruthy(); // Should fall back to a generated ID + rerender({ conditionalId: undefined }); + expect(result.current).toBeTruthy(); // Should fall back to a generated ID }); describe('version-specific tests', () => { @@ -119,7 +115,7 @@ describe('useGeneratedHtmlId', () => { testOnReactVersion('18')('[React 18] generates correct IDs', () => { const { getByTestSubject } = render(); - expect(getByTestSubject('el')).toHaveAttribute('id', 'prefix:r0:suffix'); + expect(getByTestSubject('el')).toHaveAttribute('id', 'prefix:r3:suffix'); }); testOnReactVersion(['16', '17'])( diff --git a/packages/eui/src/services/hooks/useDependentState.test.tsx b/packages/eui/src/services/hooks/useDependentState.test.tsx index 1e8ed9b7109..8782068f93f 100644 --- a/packages/eui/src/services/hooks/useDependentState.test.tsx +++ b/packages/eui/src/services/hooks/useDependentState.test.tsx @@ -6,8 +6,8 @@ * Side Public License, v 1. */ -import React from 'react'; -import { mount } from 'enzyme'; +import { renderHook } from '../../test/rtl'; + import { useDependentState } from './useDependentState'; describe('useDependentState', () => { @@ -19,25 +19,20 @@ describe('useDependentState', () => { return sourceValue * 2; }); - function Foo() { - const [value] = useDependentState(doubler, [sourceValue]); - - return
{value}
; - } - - // mount the component verify the state function was called with no previous state value - const component = mount(); + const { result, rerender } = renderHook(() => + useDependentState(doubler, [sourceValue]) + ); expect(doubler).toHaveBeenCalledTimes(1); expect(doubler).toHaveBeenCalledWith(); - expect(component.text()).toBe('4'); // 2 * 2 + expect(result.current[0]).toEqual(4); // 2 * 2 doubler.mockClear(); // update the source value, force a re-render, and run checks sourceValue = 4; - component.setProps({}); + rerender(); expect(doubler).toHaveBeenCalledTimes(1); expect(doubler).toHaveBeenCalledWith(4); // check previous state value - expect(component.text()).toBe('8'); // new value should be 4 * 2 + expect(result.current[0]).toEqual(8); // new value should be 4 * 2 }); }); diff --git a/packages/eui/src/services/hooks/useForceRender.test.tsx b/packages/eui/src/services/hooks/useForceRender.test.tsx index 1f6b10e56a6..1b8257f9efb 100644 --- a/packages/eui/src/services/hooks/useForceRender.test.tsx +++ b/packages/eui/src/services/hooks/useForceRender.test.tsx @@ -7,8 +7,8 @@ */ import React, { useImperativeHandle, createRef, forwardRef } from 'react'; -import { act } from '@testing-library/react'; -import { mount } from 'enzyme'; +import { render, act } from '@testing-library/react'; + import { useForceRender } from './useForceRender'; interface MockRefShape { @@ -36,7 +36,7 @@ describe('useForceRender', () => { it('causes the component to re-render', () => { const ref = createRef(); - mount(); + render(); expect(renderTracker).toHaveBeenCalledTimes(1); act(() => { diff --git a/packages/eui/src/services/hooks/useLatest.test.tsx b/packages/eui/src/services/hooks/useLatest.test.tsx index d4d5b62b87d..2a2e0c4216e 100644 --- a/packages/eui/src/services/hooks/useLatest.test.tsx +++ b/packages/eui/src/services/hooks/useLatest.test.tsx @@ -6,8 +6,9 @@ * Side Public License, v 1. */ -import { mount } from 'enzyme'; import React, { MutableRefObject, useEffect } from 'react'; +import { render } from '@testing-library/react'; + import { useLatest } from './useLatest'; describe('useLatest', () => { @@ -22,21 +23,16 @@ describe('useLatest', () => { it('updates the ref value but not the ref identity on render', () => { const onRefChange = jest.fn(); const onRefCurrentValueChange = jest.fn(); + const props = { onRefChange, onRefCurrentValueChange }; - const wrapper = mount( - - ); + const { rerender } = render(); expect(onRefChange).toHaveBeenCalledTimes(1); expect(onRefChange).toHaveBeenLastCalledWith({ current: 'first' }); expect(onRefCurrentValueChange).toHaveBeenCalledTimes(1); expect(onRefCurrentValueChange).toHaveBeenLastCalledWith('first'); - wrapper.setProps({ value: 'second' }); + rerender(); expect(onRefChange).toHaveBeenCalledTimes(1); // the ref's identity has not changed expect(onRefCurrentValueChange).toHaveBeenCalledTimes(2); diff --git a/packages/eui/src/services/hooks/useUpdateEffect.test.tsx b/packages/eui/src/services/hooks/useUpdateEffect.test.tsx index 2599918629c..b415cfca174 100644 --- a/packages/eui/src/services/hooks/useUpdateEffect.test.tsx +++ b/packages/eui/src/services/hooks/useUpdateEffect.test.tsx @@ -7,7 +7,8 @@ */ import React from 'react'; -import { mount } from 'enzyme'; +import { render } from '@testing-library/react'; + import { useUpdateEffect } from './useUpdateEffect'; describe('useUpdateEffect', () => { @@ -28,31 +29,31 @@ describe('useUpdateEffect', () => { }); it('does not invoke the passed effect on initial mount', () => { - mount(); + render(); expect(mockEffect).not.toHaveBeenCalled(); }); it('invokes the passed effect on each component update/rerender', () => { - const component = mount(); + const { rerender } = render(); - component.setProps({ test: true }); + rerender(); expect(mockEffect).toHaveBeenCalledTimes(1); - component.setProps({ test: false }); + rerender(); expect(mockEffect).toHaveBeenCalledTimes(2); - component.setProps({ test: true }); + rerender(); expect(mockEffect).toHaveBeenCalledTimes(3); }); it('invokes returned cleanup, same as useEffect', () => { - const component = mount(); + const { rerender, unmount } = render(); - component.setProps({ test: true }); // Trigger first update/call + rerender(); // Trigger first update/call expect(mockCleanup).not.toHaveBeenCalled(); - component.unmount(); // Trigger cleanup + unmount(); // Trigger cleanup expect(mockCleanup).toHaveBeenCalled(); }); }); diff --git a/packages/eui/src/services/window_event/window_event.test.tsx b/packages/eui/src/services/window_event/window_event.test.tsx index 8eef6f17cb5..41e74d637a4 100644 --- a/packages/eui/src/services/window_event/window_event.test.tsx +++ b/packages/eui/src/services/window_event/window_event.test.tsx @@ -7,61 +7,81 @@ */ import React from 'react'; -import { shallow } from 'enzyme'; +import { render } from '@testing-library/react'; + import { EuiWindowEvent } from './window_event'; describe('EuiWindowEvent', () => { + let windowAddCount = 0; + let windowRemoveCount = 0; + + beforeAll(() => { + // React 16 and 17 register a bunch of error listeners which we don't need to capture + window.addEventListener = jest.fn((event: string) => { + if (event !== 'error') windowAddCount++; + }); + window.removeEventListener = jest.fn((event: string) => { + if (event !== 'error') windowRemoveCount++; + }); + }); + beforeEach(() => { - window.addEventListener = jest.fn(); - window.removeEventListener = jest.fn(); + // Reset counts + windowAddCount = 0; + windowRemoveCount = 0; }); - afterEach(() => { + afterAll(() => { jest.restoreAllMocks(); }); test('attaches handler to window event on mount', () => { const handler = () => null; - shallow(); - expect(window.addEventListener).toHaveBeenCalledTimes(1); + render(); expect(window.addEventListener).toHaveBeenCalledWith('click', handler); + expect(windowAddCount).toEqual(1); }); test('removes handler on unmount', () => { const handler = () => null; - const wrapper = shallow(); - wrapper.unmount(); - expect(window.removeEventListener).toHaveBeenLastCalledWith( - 'click', - handler + const { unmount } = render( + ); + unmount(); + expect(window.removeEventListener).toHaveBeenCalledWith('click', handler); + expect(windowRemoveCount).toEqual(1); }); test('removes and re-attaches handler to window event on update', () => { const handler1 = () => null; const handler2 = () => null; - const wrapper = shallow( + const { rerender } = render( ); - expect(window.addEventListener).toHaveBeenLastCalledWith('click', handler1); + expect(window.addEventListener).toHaveBeenCalledWith('click', handler1); - wrapper.setProps({ event: 'hover', handler: handler2 }); + rerender(); - expect(window.removeEventListener).toHaveBeenLastCalledWith( - 'click', - handler1 - ); - expect(window.addEventListener).toHaveBeenLastCalledWith('hover', handler2); + expect(window.removeEventListener).toHaveBeenCalledWith('click', handler1); + expect(window.addEventListener).toHaveBeenCalledWith('keydown', handler2); }); test('does not remove or re-attach handler if update is irrelevant', () => { const handler = () => null; - const wrapper = shallow(); - expect(window.addEventListener).toHaveBeenCalledTimes(1); + const { rerender } = render( + + ); + expect(windowAddCount).toEqual(1); - wrapper.setProps({ whatever: 'ugh' }); - expect(window.addEventListener).toHaveBeenCalledTimes(1); - expect(window.removeEventListener).not.toHaveBeenCalled(); + rerender( + + ); + expect(windowAddCount).toEqual(1); + expect(windowRemoveCount).toEqual(0); }); });