Skip to content

Commit

Permalink
Merge branch 'main' into update-icon-elastic-stack
Browse files Browse the repository at this point in the history
  • Loading branch information
formgeist authored Jun 17, 2024
2 parents dc69f15 + 60d17b9 commit 10474da
Show file tree
Hide file tree
Showing 6 changed files with 90 additions and 82 deletions.
44 changes: 20 additions & 24 deletions packages/eui/src/services/accessibility/html_id_generator.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -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');
Expand Down Expand Up @@ -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) => (
<div id={useGeneratedHtmlId()} {...props} />
);
const component = shallow(<MockComponent />);
const initialId = component.find('div').prop('id');
const { container, rerender } = render(<MockComponent />);
const initialId = container.firstElementChild!.id;

component.setProps({ className: 'test' });
const rerenderedId = component.find('div').prop('id');
rerender(<MockComponent className="test" />);
const rerenderedId = container.firstElementChild!.id;

expect(initialId).toEqual(rerenderedId);
});

it('passes prefixes and suffixes to htmlIdGenerator', () => {
const MockComponent: React.FC = () => (
<div id={useGeneratedHtmlId({ prefix: 'hello', suffix: 'world' })} />
const { result } = renderHook(() =>
useGeneratedHtmlId({ prefix: 'hello', suffix: 'world' })
);
const component = shallow(<MockComponent />);
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 }) => (
<div id={useGeneratedHtmlId({ conditionalId: id })} {...props} />
);
const component = shallow(<MockComponent id="hello" />);
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', () => {
Expand All @@ -119,7 +115,7 @@ describe('useGeneratedHtmlId', () => {

testOnReactVersion('18')('[React 18] generates correct IDs', () => {
const { getByTestSubject } = render(<MockComponent />);
expect(getByTestSubject('el')).toHaveAttribute('id', 'prefix:r0:suffix');
expect(getByTestSubject('el')).toHaveAttribute('id', 'prefix:r3:suffix');
});

testOnReactVersion(['16', '17'])(
Expand Down
21 changes: 8 additions & 13 deletions packages/eui/src/services/hooks/useDependentState.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -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', () => {
Expand All @@ -19,25 +19,20 @@ describe('useDependentState', () => {
return sourceValue * 2;
});

function Foo() {
const [value] = useDependentState(doubler, [sourceValue]);

return <div>{value}</div>;
}

// mount the component verify the state function was called with no previous state value
const component = mount(<Foo />);
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
});
});
6 changes: 3 additions & 3 deletions packages/eui/src/services/hooks/useForceRender.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -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 {
Expand Down Expand Up @@ -36,7 +36,7 @@ describe('useForceRender', () => {

it('causes the component to re-render', () => {
const ref = createRef<MockRefShape>();
mount(<MockComponent ref={ref} />);
render(<MockComponent ref={ref} />);

expect(renderTracker).toHaveBeenCalledTimes(1);
act(() => {
Expand Down
14 changes: 5 additions & 9 deletions packages/eui/src/services/hooks/useLatest.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -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', () => {
Expand All @@ -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(
<MockComponent
onRefChange={onRefChange}
onRefCurrentValueChange={onRefCurrentValueChange}
value="first"
/>
);
const { rerender } = render(<MockComponent {...props} value="first" />);

expect(onRefChange).toHaveBeenCalledTimes(1);
expect(onRefChange).toHaveBeenLastCalledWith({ current: 'first' });
expect(onRefCurrentValueChange).toHaveBeenCalledTimes(1);
expect(onRefCurrentValueChange).toHaveBeenLastCalledWith('first');

wrapper.setProps({ value: 'second' });
rerender(<MockComponent {...props} value="second" />);

expect(onRefChange).toHaveBeenCalledTimes(1); // the ref's identity has not changed
expect(onRefCurrentValueChange).toHaveBeenCalledTimes(2);
Expand Down
19 changes: 10 additions & 9 deletions packages/eui/src/services/hooks/useUpdateEffect.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,8 @@
*/

import React from 'react';
import { mount } from 'enzyme';
import { render } from '@testing-library/react';

import { useUpdateEffect } from './useUpdateEffect';

describe('useUpdateEffect', () => {
Expand All @@ -28,31 +29,31 @@ describe('useUpdateEffect', () => {
});

it('does not invoke the passed effect on initial mount', () => {
mount(<MockComponent />);
render(<MockComponent />);

expect(mockEffect).not.toHaveBeenCalled();
});

it('invokes the passed effect on each component update/rerender', () => {
const component = mount(<MockComponent />);
const { rerender } = render(<MockComponent />);

component.setProps({ test: true });
rerender(<MockComponent test={true} />);
expect(mockEffect).toHaveBeenCalledTimes(1);

component.setProps({ test: false });
rerender(<MockComponent test={false} />);
expect(mockEffect).toHaveBeenCalledTimes(2);

component.setProps({ test: true });
rerender(<MockComponent test={true} />);
expect(mockEffect).toHaveBeenCalledTimes(3);
});

it('invokes returned cleanup, same as useEffect', () => {
const component = mount(<MockComponent />);
const { rerender, unmount } = render(<MockComponent />);

component.setProps({ test: true }); // Trigger first update/call
rerender(<MockComponent test={true} />); // Trigger first update/call
expect(mockCleanup).not.toHaveBeenCalled();

component.unmount(); // Trigger cleanup
unmount(); // Trigger cleanup
expect(mockCleanup).toHaveBeenCalled();
});
});
68 changes: 44 additions & 24 deletions packages/eui/src/services/window_event/window_event.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -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(<EuiWindowEvent event="click" handler={handler} />);
expect(window.addEventListener).toHaveBeenCalledTimes(1);
render(<EuiWindowEvent event="click" handler={handler} />);
expect(window.addEventListener).toHaveBeenCalledWith('click', handler);
expect(windowAddCount).toEqual(1);
});

test('removes handler on unmount', () => {
const handler = () => null;
const wrapper = shallow(<EuiWindowEvent event="click" handler={handler} />);
wrapper.unmount();
expect(window.removeEventListener).toHaveBeenLastCalledWith(
'click',
handler
const { unmount } = render(
<EuiWindowEvent event="click" handler={handler} />
);
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(
<EuiWindowEvent event="click" handler={handler1} />
);

expect(window.addEventListener).toHaveBeenLastCalledWith('click', handler1);
expect(window.addEventListener).toHaveBeenCalledWith('click', handler1);

wrapper.setProps({ event: 'hover', handler: handler2 });
rerender(<EuiWindowEvent event="keydown" handler={handler2} />);

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(<EuiWindowEvent event="click" handler={handler} />);
expect(window.addEventListener).toHaveBeenCalledTimes(1);
const { rerender } = render(
<EuiWindowEvent event="click" handler={handler} />
);
expect(windowAddCount).toEqual(1);

wrapper.setProps({ whatever: 'ugh' });
expect(window.addEventListener).toHaveBeenCalledTimes(1);
expect(window.removeEventListener).not.toHaveBeenCalled();
rerender(
<EuiWindowEvent
event="click"
handler={handler}
data-test-subj="whatever"
/>
);
expect(windowAddCount).toEqual(1);
expect(windowRemoveCount).toEqual(0);
});
});

0 comments on commit 10474da

Please sign in to comment.