Skip to content

Commit c77f0db

Browse files
[Management][Es Ui Shared][Kbn Management] Migrate Enzyme to RTL (elastic#238764)
## Summary This PR migrates the first batch of test files from Enzyme to React Testing Library (RTL) as part of the effort to remove Enzyme dependencies from the Kibana codebase. Addresses elastic#22260 ### Migration Details **Enzyme → RTL API Changes:** - Replaced `shallow()` / `mount()` with RTL's `render()` - Replaced `shallowWithIntl` / `mountWithIntl` with `renderWithI18n` - Replaced `.find()` with `screen.getByTestId()` / `screen.getByLabelText()` - Replaced `.simulate()` with `userEvent` interactions - Removed `wrapper.update()` calls (unnecessary in RTL) - Removed Enzyme type imports (`ShallowWrapper`, `ReactWrapper`) **Modern Testing Patterns:** - Added Jest modern fake timers setup (`jest.useFakeTimers()`) - Configured `userEvent.setup({ advanceTimers: jest.advanceTimersByTime })` - Replaced `waitFor()` polling with direct assertions using timer control - Implemented "Start → Flush → Await" pattern for async operations with internal timers - Added proper `act()` wrapping for state-changing operations - All tests now use async/await for user interactions **TypeScript Fixes:** - Fixed context provider type issues in `landing.test.tsx` - Ensured all required `AppDependencies` props are provided - Removed unused imports and fixed linting issues **Snapshot Updates:** - Updated all snapshots to reflect RTL's rendering output format ## Testing - ✅ All migrated tests pass locally - ✅ Zero console warnings (verified with `NODE_ENV=development`) - ✅ TypeScript compilation successful - ✅ ESLint passes with no errors - ✅ Snapshots updated and validated ## Performance Improvements Tests are now faster and more deterministic: - No polling with `waitFor()` - direct assertions with timer control - Fake timers eliminate real-time waiting - More reliable in CI environments ## Checklist - [x] Removed all Enzyme dependencies from test files - [x] Added modern fake timer setup - [x] Configured userEvent with timer advancement - [x] Replaced waitFor() with timer control - [x] All tests pass with zero warnings - [x] TypeScript compiles without errors - [x] ESLint passes - [x] Snapshots updated - [x] Helper files migrated where needed ## Next Steps This is the first batch. Subsequent batches will continue migrating the remaining test files listed in issue elastic#222608. --------- Co-authored-by: kibanamachine <42973632+kibanamachine@users.noreply.github.com>
1 parent 002cae9 commit c77f0db

19 files changed

Lines changed: 1145 additions & 1102 deletions

File tree

src/platform/packages/shared/kbn-management/settings/application/__snapshots__/query_input.test.tsx.snap

Lines changed: 78 additions & 55 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

src/platform/packages/shared/kbn-management/settings/application/query_input.test.tsx

Lines changed: 33 additions & 36 deletions
Original file line numberDiff line numberDiff line change
@@ -9,10 +9,10 @@
99

1010
import React from 'react';
1111
import { Query } from '@elastic/eui';
12-
import { findTestSubject } from '@elastic/eui/lib/test';
13-
import { act, waitFor } from '@testing-library/react';
12+
import { screen } from '@testing-library/react';
13+
import userEvent from '@testing-library/user-event';
1414

15-
import { shallowWithI18nProvider, mountWithI18nProvider } from '@kbn/test-jest-helpers';
15+
import { renderWithI18n } from '@kbn/test-jest-helpers';
1616
import { getSettingsMock } from '@kbn/management-settings-utilities/mocks/settings.mock';
1717

1818
import { QueryInput } from './query_input';
@@ -27,57 +27,54 @@ const categories = Object.keys(
2727
)
2828
);
2929

30+
beforeAll(() => {
31+
jest.useFakeTimers();
32+
});
33+
34+
afterAll(() => {
35+
jest.useRealTimers();
36+
});
37+
38+
beforeEach(() => {
39+
jest.clearAllMocks();
40+
});
41+
42+
const user = userEvent.setup({ advanceTimers: jest.advanceTimersByTime });
43+
3044
describe('Search', () => {
3145
it('should render normally', async () => {
3246
const onQueryChange = () => {};
33-
const component = shallowWithI18nProvider(
34-
<QueryInput {...{ categories, query, onQueryChange }} />
35-
);
47+
const { container } = renderWithI18n(<QueryInput {...{ categories, query, onQueryChange }} />);
3648

37-
expect(component).toMatchSnapshot();
49+
expect(container).toMatchSnapshot();
3850
});
3951

4052
it('should call parent function when query is changed', async () => {
41-
// This test is brittle as it knows about implementation details
42-
// (EuiFieldSearch uses onKeyup instead of onChange to handle input)
4353
const onQueryChange = jest.fn();
44-
const component = mountWithI18nProvider(
45-
<QueryInput {...{ categories, query, onQueryChange }} />
46-
);
47-
findTestSubject(component, 'settingsSearchBar').simulate('keyup', {
48-
target: { value: 'new filter' },
49-
});
50-
expect(onQueryChange).toHaveBeenCalledTimes(1);
54+
renderWithI18n(<QueryInput {...{ categories, query, onQueryChange }} />);
55+
56+
const searchBar = screen.getByTestId('settingsSearchBar');
57+
await user.type(searchBar, 'new filter');
58+
59+
expect(onQueryChange).toHaveBeenCalled();
5160
});
5261

5362
it('should handle query parse error', async () => {
5463
const onQueryChange = jest.fn();
55-
const component = mountWithI18nProvider(
56-
<QueryInput {...{ categories, query }} onQueryChange={onQueryChange} />
57-
);
64+
renderWithI18n(<QueryInput {...{ categories, query }} onQueryChange={onQueryChange} />);
5865

59-
const searchBar = findTestSubject(component, 'settingsSearchBar');
66+
const searchBar = screen.getByTestId('settingsSearchBar');
6067

6168
// Send invalid query
62-
act(() => {
63-
searchBar.simulate('keyup', { target: { value: '?' } });
64-
});
69+
await user.type(searchBar, '?');
6570

66-
expect(onQueryChange).toHaveBeenCalledTimes(1);
67-
68-
waitFor(() => {
69-
expect(component.contains('Unable to parse query')).toBe(true);
70-
});
71+
expect(onQueryChange).toHaveBeenCalled();
72+
expect(screen.getByText(/Unable to parse query/)).toBeInTheDocument();
7173

7274
// Send valid query to ensure component can recover from invalid query
73-
act(() => {
74-
searchBar.simulate('keyup', { target: { value: 'dateFormat' } });
75-
});
76-
77-
expect(onQueryChange).toHaveBeenCalledTimes(2);
75+
await user.clear(searchBar);
76+
await user.type(searchBar, 'dateFormat');
7877

79-
waitFor(() => {
80-
expect(component.contains('Unable to parse query')).toBe(false);
81-
});
78+
expect(screen.queryByText(/Unable to parse query/)).not.toBeInTheDocument();
8279
});
8380
});

0 commit comments

Comments
 (0)