diff --git a/.claude/skills/gen-rtl-test/SKILL.md b/.claude/skills/gen-rtl-test/SKILL.md
index 8776f3732fa..9cee2186eee 100644
--- a/.claude/skills/gen-rtl-test/SKILL.md
+++ b/.claude/skills/gen-rtl-test/SKILL.md
@@ -59,7 +59,7 @@ RTL emphasizes testing components as users interact with them. Users find button
4. **DRY Helpers** - Use reusable function in frontend/packages/console-shared/src/test-utils directoty and sub-directory if exists else extract repetitive setup into reusable functions
-5. **Async-Aware** - Handle asynchronous updates with `findBy*` and `waitFor`
+5. **Async-Aware** - Prefer `findByText` / `findByRole` / `findAllByRole` when waiting for content to **appear**; use `waitFor` when the assertion is **not** expressible as a query (e.g. button **disabled** after validation, **absence**, class names, mocks). See **Rule 11**.
6. **TypeScript Safety** - Use proper types for props, state, and mock data
@@ -360,7 +360,7 @@ it('should find the heading', () => {
**Query Variants:**
- **`getBy*`** - Element expected to be present synchronously (throws if not found)
- **`queryBy*`** - Only for asserting element is NOT present
-- **`findBy*`** - Element will appear asynchronously (returns Promise)
+- **`findBy*`** - Element will appear asynchronously (returns Promise). Prefer **`findBy*`** over **`waitFor(() => … getBy* …)`** when the goal is to wait for that node to **appear** (see **Rule 11**).
**Anti-pattern:** Avoid `container.querySelector` - it tests implementation details.
@@ -456,7 +456,8 @@ it('should render the Name field', async () => {
// Don't do this - use verifyInputField instead!
expect(screen.getByLabelText('Name')).toBeInTheDocument();
expect(screen.getByLabelText('Name')).toHaveValue('');
- fireEvent.change(screen.getByLabelText('Name'), { target: { value: 'test' } });
+ const user = userEvent.setup();
+ await user.type(screen.getByLabelText('Name'), 'test');
expect(screen.getByLabelText('Name')).toHaveValue('test');
expect(screen.getByText('Unique name for the resource')).toBeInTheDocument();
});
@@ -482,14 +483,15 @@ When testing form components:
### Rule 10: Test Conditional Rendering by Asserting Both States
```typescript
-it('should show content when expanded', () => {
+it('should show content when expanded', async () => {
+ const user = userEvent.setup();
render();
// 1. Assert initial hidden state
expect(screen.queryByText('Hidden content')).not.toBeInTheDocument();
// 2. Simulate user action
- fireEvent.click(screen.getByRole('button', { name: 'Expand' }));
+ await user.click(screen.getByRole('button', { name: 'Expand' }));
// 3. Assert final visible state
expect(screen.getByText('Hidden content')).toBeVisible();
@@ -498,18 +500,54 @@ it('should show content when expanded', () => {
### Rule 11: Handle Asynchronous Behavior
+#### Prefer `findBy*` over `waitFor` + `getBy*` when waiting for content to appear
+
+`findByText`, `findByRole`, `findAllByRole`, etc. are implemented as **`waitFor` + `getBy*`** (they return a Promise and retry until the element is found or the timeout is reached). When the intent is **“wait until this text/role appears in the DOM,”** use **`await screen.findBy…`** instead of wrapping **`getBy…`** in **`waitFor`**.
+
```typescript
-// Use findBy* to wait for an element to appear
-const element = await screen.findByText('Loaded content');
-expect(element).toBeVisible();
+// ✅ GOOD: findBy* expresses “eventually this appears”
+expect(await screen.findByText('Loaded content')).toBeVisible();
+const row = await screen.findByRole('row', { name: /my-resource/i });
+expect(await screen.findAllByRole('button', { name: 'Remove' })).toHaveLength(2);
-// Use waitFor for complex assertions
+// ⚠️ EQUIVALENT but more verbose (prefer findBy* above)
await waitFor(() => {
- expect(screen.getByText('Updated')).toBeInTheDocument();
+ expect(screen.getByText('Loaded content')).toBeInTheDocument();
});
```
-**Avoid Explicit act():** Rarely needed. `render`, `fireEvent`, `findBy*`, and `waitFor` already wrap operations in `act()`.
+#### Keep `waitFor` when `findBy*` cannot express the assertion
+
+**`findBy*` queries wait for presence** of a matching node. They do **not** replace every `waitFor`. Still use **`waitFor`** when you need to wait for something that is **not** “find this text/role in the tree,” for example:
+
+| Situation | Why `findBy*` is not enough | Pattern |
+|-----------|------------------------------|---------|
+| **Control state after async validation** (e.g. Save **disabled** after Formik/yup runs) | Disabled is a **property**, not a new queryable label; there is no `findBy…` for “button became disabled.” | `await waitFor(() => expect(save).toBeDisabled());` — you can still use **`findByText`** / **`findByRole`** in the **same** test for **error messages** that appear as text. |
+| **Eventually absent** (list/link/control **not** in the document) | **`findBy*` waits for presence**, not absence. | `await waitFor(() => expect(screen.queryByRole('list')).not.toBeInTheDocument());` or `waitForElementToBeRemoved` when something was visible first. |
+| **CSS class** or **non-query** DOM state | Not a text/role query. | `await waitFor(() => expect(input).toHaveClass('invalid-tag'));` |
+| **Mock** or **callback** assertions | Not the DOM. | `await waitFor(() => expect(mockFn).toHaveBeenCalledWith(…));` |
+
+```typescript
+// Async validation: message appears → findByText; button disabled → waitFor
+await user.type(cpuRequest, '300');
+await waitFor(() => expect(save).toBeDisabled());
+expect(
+ await screen.findByText('CPU request must be less than or equal to limit.'),
+).toBeVisible();
+```
+
+#### Quick reference
+
+```typescript
+// Prefer findBy* when waiting for content to appear
+const element = await screen.findByText('Loaded content');
+expect(element).toBeVisible();
+
+// Use waitFor for disabled state, absence, classes, mocks (see table above)
+await waitFor(() => expect(save).toBeDisabled());
+```
+
+**Avoid Explicit act():** Rarely needed. `render`, `userEvent` (await its async methods), `findBy*`, and `waitFor` already wrap operations in `act()`.
### Rule 12: Use Lifecycle Hooks for Setup and Cleanup
@@ -553,24 +591,31 @@ expect(userName).toBeVisible();
expect(editButton).toBeVisible();
```
-### Rule 14: Simulate User Events with fireEvent
+### Rule 14: Simulate User Events with userEvent
+
+Use `@testing-library/user-event` for clicks, typing, and other interactions. It simulates full event sequences closer to real browser behavior than low-level `fireEvent`.
```typescript
-import { render, screen, fireEvent } from '@testing-library/react';
+import { render, screen } from '@testing-library/react';
+import userEvent from '@testing-library/user-event';
+const user = userEvent.setup();
render();
const input = screen.getByLabelText(/name/i);
const button = screen.getByRole('button', { name: /submit/i });
-// Simulate typing
-fireEvent.change(input, { target: { value: 'John Doe' } });
+// Simulate typing (prefer over fireEvent.change)
+await user.type(input, 'John Doe');
// Simulate clicking
-fireEvent.click(button);
+await user.click(button);
```
-**Note:** `userEvent` from `@testing-library/user-event` is not supported due to incompatible Jest version (will be updated after Jest upgrade).
+**Conventions:**
+- Call `const user = userEvent.setup()` per test and **await** `user.click`, `user.type`, `user.keyboard`, etc.
+- For text inputs in forms, prefer **`verifyInputField`** (Rule 9) when it applies; use `userEvent` for other controls (selects, checkboxes, buttons, complex widgets).
+- Reserve **`fireEvent`** only for rare cases (e.g., triggering a specific low-level DOM event that `userEvent` does not model).
### Rule 15: Test "Unhappy Paths" and Error States
@@ -648,12 +693,14 @@ Remove any imports that are not used in the test file:
```typescript
// ❌ BAD - Unused imports
-import { render, screen, fireEvent, waitFor, within } from '@testing-library/react';
+import { render, screen, waitFor, within } from '@testing-library/react';
+import userEvent from '@testing-library/user-event';
import { k8sCreate, k8sPatch, k8sUpdate } from '@console/internal/module/k8s';
-// ... but only using render, screen, fireEvent
+// ... but only using render, screen, userEvent
// ✅ GOOD - Only what's needed
-import { render, screen, fireEvent } from '@testing-library/react';
+import { render, screen } from '@testing-library/react';
+import userEvent from '@testing-library/user-event';
import { k8sCreate } from '@console/internal/module/k8s';
```
@@ -717,23 +764,25 @@ Clean up any variables that are declared but never used:
```typescript
// ❌ BAD - Unused variables
-it('should submit form', () => {
+it('should submit form', async () => {
const mockData = { foo: 'bar' };
const unusedSpy = jest.spyOn(console, 'log');
const onSubmit = jest.fn();
+ const user = userEvent.setup();
render(
);
- fireEvent.click(screen.getByRole('button'));
+ await user.click(screen.getByRole('button'));
expect(onSubmit).toHaveBeenCalled();
});
// ✅ GOOD - Only necessary variables
-it('should submit form', () => {
+it('should submit form', async () => {
+ const user = userEvent.setup();
const onSubmit = jest.fn();
render();
- fireEvent.click(screen.getByRole('button'));
+ await user.click(screen.getByRole('button'));
expect(onSubmit).toHaveBeenCalled();
});
@@ -950,43 +999,45 @@ act(() => {
**Strategy 1: Wrap async interactions in waitFor**
```typescript
+import { screen, waitFor } from '@testing-library/react';
+import userEvent from '@testing-library/user-event';
+
+const user = userEvent.setup();
+
// ❌ BAD: Causes act() warning
-fireEvent.click(button);
+await user.click(button);
expect(screen.getByText('Updated')).toBeInTheDocument();
// ✅ GOOD: Use waitFor for async updates
-fireEvent.click(button);
+await user.click(button);
await waitFor(() => {
expect(screen.getByText('Updated')).toBeInTheDocument();
});
```
**Strategy 2: Use findBy* queries (preferred for new elements)**
-```typescript
-// ❌ BAD: Causes act() warning
-fireEvent.click(button);
-expect(screen.getByText('Loaded')).toBeInTheDocument();
-// ✅ GOOD: Use findBy* which waits automatically
-fireEvent.click(button);
-expect(await screen.findByText('Loaded')).toBeInTheDocument();
-```
+When waiting for content to appear after user interactions, prefer `findBy*` queries which automatically wait for elements. See **Rule 11** for comprehensive guidance on `findBy*` vs `waitFor`, including when to use each and a detailed comparison table.
-**Strategy 3: Wrap test in act() when needed**
+**Strategy 3: Prefer userEvent + findBy* for dropdown/select-style interactions**
```typescript
-// Import act from @testing-library/react
-import { render, screen, fireEvent, waitFor, act } from '@testing-library/react';
+import { screen, act } from '@testing-library/react';
+import userEvent from '@testing-library/user-event';
+
+const user = userEvent.setup();
+
+// ❌ BAD: Asserts before async menu content appears
+await user.click(screen.getByText('Select Option'));
+expect(screen.getByText('Option 1')).toBeInTheDocument();
-// ❌ BAD: Causes act() warning for dropdown/select interactions
-const dropdown = screen.getByText('Select Option');
-fireEvent.click(dropdown);
+// ✅ GOOD: Wait for option, then click
+await user.click(screen.getByText('Select Option'));
+await user.click(await screen.findByText('Option 1'));
-// ✅ GOOD: Wrap in act() for complex interactions
+// If a third-party widget still warns, wrap only that interaction in act():
await act(async () => {
- fireEvent.click(dropdown);
+ await user.click(screen.getByRole('button', { name: /toggle menu/i }));
});
-const option = await screen.findByText('Option 1');
-fireEvent.click(option);
```
**Strategy 4: Mock timers or async operations**
@@ -1004,7 +1055,7 @@ await waitFor(() => {
#### Common Causes of act() Warnings
1. **Dropdown/Select interactions** - PatternFly Select/Dropdown components
- - Solution: Wrap in `act()` or use `waitFor` after interaction
+ - Solution: Use `await user.click` / `user.type` with `findBy*` or `waitFor` after opening; wrap in `act()` only if a widget still emits warnings
2. **Async state updates** - useEffect, setTimeout, promises
- Solution: Use `findBy*` or `waitFor`
@@ -1455,11 +1506,13 @@ When generating tests for React components:
4. ✅ Mock factories return simple values (null, strings, children) - NO React.createElement
5. ✅ Cast mocked imports to `jest.Mock` when calling `.mockResolvedValue()` etc.
6. ✅ **Import `verifyInputField` for form components** - Rule 9 strictly enforced
+7. ✅ **Use `userEvent` from `@testing-library/user-event` for clicks, typing, and similar interactions** - Rule 14 (`userEvent.setup()`, then `await user.click` / `await user.type`, etc.)
**Code Generation Pattern:**
```typescript
// ✅ ALWAYS - ES6 imports at file top
-import { render, screen, fireEvent, waitFor } from '@testing-library/react';
+import { render, screen, waitFor } from '@testing-library/react';
+import userEvent from '@testing-library/user-event';
import { k8sCreate } from '@console/internal/module/k8s';
import { history } from '@console/internal/components/utils';
import { verifyInputField } from '@console/shared/src/test-utils/unit-test-utils'; // For form components
@@ -1584,7 +1637,7 @@ If **ANY** `require()` found that is NOT `jest.requireActual` → **IMMEDIATELY
- Fix **EVERY** act() warning using Rule 23 strategies:
- Wrap async interactions in `waitFor`
- Use `findBy*` queries for async elements
- - Add `await waitFor()` after dropdown/select interactions
+ - After `await user.click` on dropdowns/selects, wait for menu content with `findBy*` or `waitFor`
- Ensure async state updates are properly awaited
- Re-run tests after fixing warnings
- **DO NOT** complete until output has ZERO act() warnings
diff --git a/frontend/packages/console-app/src/__tests__/hooks/useCSPViolationDetector.spec.tsx b/frontend/packages/console-app/src/__tests__/hooks/useCSPViolationDetector.spec.tsx
index 28b2b92e8b8..5bb7e0d54bc 100644
--- a/frontend/packages/console-app/src/__tests__/hooks/useCSPViolationDetector.spec.tsx
+++ b/frontend/packages/console-app/src/__tests__/hooks/useCSPViolationDetector.spec.tsx
@@ -1,4 +1,4 @@
-import { act, fireEvent } from '@testing-library/react';
+import { act } from '@testing-library/react';
import { renderWithProviders } from '@console/shared/src/test-utils/unit-test-utils';
import {
newPluginCSPViolationEvent,
@@ -78,7 +78,7 @@ describe('useCSPViolationDetector', () => {
mockCacheEvent.mockReturnValue(true);
renderWithProviders();
act(() => {
- fireEvent(document, testEvent);
+ document.dispatchEvent(testEvent);
});
expect(mockCacheEvent).toHaveBeenCalledWith(testPluginEvent);
expect(mockFireTelemetry).toHaveBeenCalledWith('CSPViolation', testPluginEvent);
@@ -89,7 +89,7 @@ describe('useCSPViolationDetector', () => {
renderWithProviders();
act(() => {
- fireEvent(document, testEvent);
+ document.dispatchEvent(testEvent);
});
expect(mockCacheEvent).toHaveBeenCalledWith(testPluginEvent);
@@ -104,7 +104,7 @@ describe('useCSPViolationDetector', () => {
const expected = newPluginCSPViolationEvent('foo', testEventWithPlugin);
renderWithProviders();
act(() => {
- fireEvent(document, testEventWithPlugin);
+ document.dispatchEvent(testEventWithPlugin);
});
expect(mockCacheEvent).toHaveBeenCalledWith(expected);
expect(mockFireTelemetry).toHaveBeenCalledWith('CSPViolation', expected);
@@ -119,7 +119,7 @@ describe('useCSPViolationDetector', () => {
const expected = newPluginCSPViolationEvent('foo', testEventWithPlugin);
renderWithProviders();
act(() => {
- fireEvent(document, testEventWithPlugin);
+ document.dispatchEvent(testEventWithPlugin);
});
expect(mockCacheEvent).toHaveBeenCalledWith(expected);
expect(mockFireTelemetry).toHaveBeenCalledWith('CSPViolation', expected);
diff --git a/frontend/packages/console-app/src/components/modals/resource-limits/__tests__/ResourceLimitsModal.spec.tsx b/frontend/packages/console-app/src/components/modals/resource-limits/__tests__/ResourceLimitsModal.spec.tsx
index b5737c0208d..58bc7ba8907 100644
--- a/frontend/packages/console-app/src/components/modals/resource-limits/__tests__/ResourceLimitsModal.spec.tsx
+++ b/frontend/packages/console-app/src/components/modals/resource-limits/__tests__/ResourceLimitsModal.spec.tsx
@@ -1,5 +1,6 @@
import type { ComponentProps } from 'react';
-import { screen, fireEvent } from '@testing-library/react';
+import { screen } from '@testing-library/react';
+import userEvent from '@testing-library/user-event';
import type { FormikProps, FormikValues } from 'formik';
import { formikFormProps } from '@console/shared/src/test-utils/formik-props-utils';
import { renderWithProviders } from '@console/shared/src/test-utils/unit-test-utils';
@@ -70,16 +71,18 @@ describe('ResourceLimitsModal Form', () => {
});
it('calls the cancel function when the Cancel button is clicked', async () => {
+ const user = userEvent.setup();
renderWithProviders();
- await fireEvent.click(screen.getByRole('button', { name: 'Cancel' }));
+ await user.click(screen.getByRole('button', { name: 'Cancel' }));
expect(formProps.cancel).toHaveBeenCalledTimes(1);
});
it('calls the handleSubmit function when the form is submitted', async () => {
+ const user = userEvent.setup();
renderWithProviders();
- await fireEvent.submit(screen.getByRole('form'));
+ await user.click(screen.getByRole('button', { name: 'Save' }));
expect(formProps.handleSubmit).toHaveBeenCalledTimes(1);
});
});
diff --git a/frontend/packages/console-shared/src/components/alerts/__tests__/SwitchToYAMLAlert.spec.tsx b/frontend/packages/console-shared/src/components/alerts/__tests__/SwitchToYAMLAlert.spec.tsx
index 68a89e28231..f37fee90cf7 100644
--- a/frontend/packages/console-shared/src/components/alerts/__tests__/SwitchToYAMLAlert.spec.tsx
+++ b/frontend/packages/console-shared/src/components/alerts/__tests__/SwitchToYAMLAlert.spec.tsx
@@ -1,4 +1,5 @@
-import { screen, fireEvent } from '@testing-library/react';
+import { screen } from '@testing-library/react';
+import userEvent from '@testing-library/user-event';
import { renderWithProviders } from '../../../test-utils/unit-test-utils';
import SwitchToYAMLAlert from '../SwitchToYAMLAlert';
@@ -42,12 +43,13 @@ describe('SwitchToYAMLAlert', () => {
expect(closeButton).toBeInTheDocument();
});
- it('should call onClose when close button is clicked', () => {
+ it('should call onClose when close button is clicked', async () => {
+ const user = userEvent.setup();
const mockOnClose = jest.fn();
renderWithProviders();
const closeButton = screen.getByRole('button', { name: /close/i });
- fireEvent.click(closeButton);
+ await user.click(closeButton);
expect(mockOnClose).toHaveBeenCalledTimes(1);
});
diff --git a/frontend/packages/console-shared/src/components/editor/__tests__/CodeEditorToolbar.spec.tsx b/frontend/packages/console-shared/src/components/editor/__tests__/CodeEditorToolbar.spec.tsx
index 585e8b16366..5002909bb55 100644
--- a/frontend/packages/console-shared/src/components/editor/__tests__/CodeEditorToolbar.spec.tsx
+++ b/frontend/packages/console-shared/src/components/editor/__tests__/CodeEditorToolbar.spec.tsx
@@ -1,4 +1,5 @@
-import { render, screen, fireEvent } from '@testing-library/react';
+import { render, screen } from '@testing-library/react';
+import userEvent from '@testing-library/user-event';
import { useTranslation } from 'react-i18next';
import { ActionType } from '@console/internal/reducers/ols';
import { useConsoleDispatch } from '@console/shared/src/hooks/useConsoleDispatch';
@@ -48,11 +49,12 @@ describe('CodeEditorToolbar', () => {
expect(screen.queryByRole('button')).not.toBeInTheDocument();
});
- it('should dispatch OpenOLS action when "Ask OpenShift Lightspeed" button is clicked', () => {
+ it('should dispatch OpenOLS action when "Ask OpenShift Lightspeed" button is clicked', async () => {
+ const user = userEvent.setup();
(useOLSConfig as jest.Mock).mockReturnValue(true);
render();
const button = screen.getByRole('button');
- fireEvent.click(button);
+ await user.click(button);
expect(mockDispatch).toHaveBeenCalledWith({ type: ActionType.OpenOLS });
});
});
diff --git a/frontend/packages/console-shared/src/components/form-utils/__tests__/FlexForm.spec.tsx b/frontend/packages/console-shared/src/components/form-utils/__tests__/FlexForm.spec.tsx
index ea82fa84aef..179fab1ec3a 100644
--- a/frontend/packages/console-shared/src/components/form-utils/__tests__/FlexForm.spec.tsx
+++ b/frontend/packages/console-shared/src/components/form-utils/__tests__/FlexForm.spec.tsx
@@ -1,4 +1,5 @@
-import { screen, fireEvent } from '@testing-library/react';
+import { screen } from '@testing-library/react';
+import userEvent from '@testing-library/user-event';
import { renderWithProviders } from '../../../test-utils/unit-test-utils';
import FlexForm from '../FlexForm';
@@ -22,14 +23,19 @@ describe('FlexForm', () => {
});
});
- it('should preserve form props including onSubmit', () => {
+ it('should preserve form props including onSubmit', async () => {
+ const user = userEvent.setup();
const handleSubmit = jest.fn((e) => e.preventDefault());
- renderWithProviders();
+ renderWithProviders(
+
+
+ ,
+ );
const formElement = screen.getByRole('form', { name: 'flex form' });
expect(formElement).toHaveAttribute('style');
- fireEvent.submit(formElement);
+ await user.click(screen.getByRole('button', { name: 'Submit' }));
expect(handleSubmit).toHaveBeenCalledTimes(1);
});
@@ -45,7 +51,8 @@ describe('FlexForm', () => {
expect(screen.getByRole('button', { name: 'Submit' })).toBeVisible();
});
- it('should handle form submission', () => {
+ it('should handle form submission', async () => {
+ const user = userEvent.setup();
const handleSubmit = jest.fn((e) => e.preventDefault());
renderWithProviders(
@@ -55,7 +62,7 @@ describe('FlexForm', () => {
);
const submitButton = screen.getByRole('button', { name: 'Submit' });
- fireEvent.click(submitButton);
+ await user.click(submitButton);
expect(handleSubmit).toHaveBeenCalledTimes(1);
});
diff --git a/frontend/packages/console-shared/src/components/form-utils/__tests__/FormFooter.spec.tsx b/frontend/packages/console-shared/src/components/form-utils/__tests__/FormFooter.spec.tsx
index 3e5d5f00056..27230e7c3fc 100644
--- a/frontend/packages/console-shared/src/components/form-utils/__tests__/FormFooter.spec.tsx
+++ b/frontend/packages/console-shared/src/components/form-utils/__tests__/FormFooter.spec.tsx
@@ -1,4 +1,5 @@
-import { screen, fireEvent } from '@testing-library/react';
+import { screen } from '@testing-library/react';
+import userEvent from '@testing-library/user-event';
import { renderWithProviders } from '../../../test-utils/unit-test-utils';
import type { FormFooterProps } from '../form-utils-types';
import FormFooter from '../FormFooter';
@@ -81,13 +82,14 @@ describe('FormFooter', () => {
expect(screen.getByRole('button', { name: 'Create' })).toHaveAttribute('type', 'button');
});
- it('should call the handler when a button is clicked', () => {
+ it('should call the handler when a button is clicked', async () => {
+ const user = userEvent.setup();
const handleSubmit = jest.fn();
renderWithProviders();
- fireEvent.click(screen.getByRole('button', { name: 'Create' }));
- fireEvent.click(screen.getByRole('button', { name: 'Reset' }));
- fireEvent.click(screen.getByRole('button', { name: 'Cancel' }));
+ await user.click(screen.getByRole('button', { name: 'Create' }));
+ await user.click(screen.getByRole('button', { name: 'Reset' }));
+ await user.click(screen.getByRole('button', { name: 'Cancel' }));
expect(handleSubmit).toHaveBeenCalledTimes(1);
expect(props.handleReset).toHaveBeenCalledTimes(1);
diff --git a/frontend/packages/console-shared/src/components/formik-fields/__tests__/NumberSpinnerField.spec.tsx b/frontend/packages/console-shared/src/components/formik-fields/__tests__/NumberSpinnerField.spec.tsx
index e66c97001cf..84e277ebae7 100644
--- a/frontend/packages/console-shared/src/components/formik-fields/__tests__/NumberSpinnerField.spec.tsx
+++ b/frontend/packages/console-shared/src/components/formik-fields/__tests__/NumberSpinnerField.spec.tsx
@@ -1,4 +1,5 @@
-import { screen, configure, fireEvent, act, waitFor } from '@testing-library/react';
+import { screen, configure, act, waitFor } from '@testing-library/react';
+import userEvent from '@testing-library/user-event';
import { mockFormikRenderer } from '../../../test-utils/unit-test-utils';
import NumberSpinnerField from '../NumberSpinnerField';
@@ -25,23 +26,23 @@ describe('Number Spinner Field', () => {
expect(getInput().value).toEqual('0');
await act(async () => {
- await fireEvent.change(getInput(), {
- currentTarget: { value: '12' },
- target: { value: '12' },
- });
+ const user = userEvent.setup();
+ await user.clear(getInput());
+ await user.type(getInput(), '12');
});
await waitFor(() => expect(getInput().value).toEqual('12'));
});
- it('should increment or decrement value based on clicked button', () => {
+ it('should increment or decrement value based on clicked button', async () => {
+ const user = userEvent.setup();
mockFormikRenderer(, { spinnerField: '' });
expect(getInput().value).toEqual('0');
- fireEvent.click(screen.getByTestId('Increment'));
- fireEvent.click(screen.getByTestId('Increment'));
+ await user.click(screen.getByTestId('Increment'));
+ await user.click(screen.getByTestId('Increment'));
expect(getInput().value).toEqual('2');
- fireEvent.click(screen.getByTestId('Decrement'));
+ await user.click(screen.getByTestId('Decrement'));
expect(getInput().value).toEqual('1');
});
});
diff --git a/frontend/packages/console-shared/src/components/formik-fields/key-value-file-input-field/__tests__/KeyValueFileInputField.spec.tsx b/frontend/packages/console-shared/src/components/formik-fields/key-value-file-input-field/__tests__/KeyValueFileInputField.spec.tsx
index d5d61180493..d98367db0fb 100644
--- a/frontend/packages/console-shared/src/components/formik-fields/key-value-file-input-field/__tests__/KeyValueFileInputField.spec.tsx
+++ b/frontend/packages/console-shared/src/components/formik-fields/key-value-file-input-field/__tests__/KeyValueFileInputField.spec.tsx
@@ -1,5 +1,6 @@
import type { FC } from 'react';
-import { screen, render, fireEvent, act, waitFor } from '@testing-library/react';
+import { screen, render, act, waitFor } from '@testing-library/react';
+import userEvent from '@testing-library/user-event';
import type { FormikConfig, FormikErrors } from 'formik';
import { Formik } from 'formik';
import KeyValueFileInputField from '../KeyValueFileInputField';
@@ -41,9 +42,10 @@ test('should have validation error given input field is touched and error exists
);
const KeyField = screen.getByTestId('key-0');
- act(() => {
- fireEvent.click(KeyField);
- fireEvent.blur(KeyField);
+ const user = userEvent.setup();
+ await act(async () => {
+ await user.click(KeyField);
+ await user.tab();
});
const validationErrors = await screen.findByText(`Required`);
@@ -110,7 +112,8 @@ test('should add new entry on clicking Add key/value button', async () => {
/>,
);
const addKeyValueButton = screen.getByTestId('add-key-value-button');
- fireEvent.click(addKeyValueButton);
+ const user = userEvent.setup();
+ await user.click(addKeyValueButton);
await waitFor(() => {
const keyValuePair = screen.queryAllByTestId('key-value-pair');
diff --git a/frontend/packages/console-shared/src/components/formik-fields/multi-column-field/__tests__/MultiColumnFieldFooter.spec.tsx b/frontend/packages/console-shared/src/components/formik-fields/multi-column-field/__tests__/MultiColumnFieldFooter.spec.tsx
index 60bec2c552a..7f24e39d2b2 100644
--- a/frontend/packages/console-shared/src/components/formik-fields/multi-column-field/__tests__/MultiColumnFieldFooter.spec.tsx
+++ b/frontend/packages/console-shared/src/components/formik-fields/multi-column-field/__tests__/MultiColumnFieldFooter.spec.tsx
@@ -1,4 +1,5 @@
-import { screen, fireEvent, waitFor } from '@testing-library/react';
+import { screen, waitFor } from '@testing-library/react';
+import userEvent from '@testing-library/user-event';
import { renderWithProviders } from '../../../../test-utils/unit-test-utils';
import MultiColumnFieldFooter from '../MultiColumnFieldFooter';
@@ -13,7 +14,8 @@ describe('MultiColumnFieldFooter', () => {
expect(screen.queryByRole('tooltip')).not.toBeInTheDocument();
});
- it('should render a disabled button when disableAddRow is true', () => {
+ it('should render a disabled button when disableAddRow is true', async () => {
+ const user = userEvent.setup();
const onAdd = jest.fn();
renderWithProviders();
@@ -22,11 +24,12 @@ describe('MultiColumnFieldFooter', () => {
expect(button).toHaveAttribute('aria-disabled', 'true');
// Verify button doesn't trigger callback when disabled
- fireEvent.click(button);
+ await user.click(button);
expect(onAdd).not.toHaveBeenCalled();
});
it('should render a disabled button with tooltipAddRow prop', async () => {
+ const user = userEvent.setup();
const onAdd = jest.fn();
renderWithProviders(
,
@@ -37,11 +40,11 @@ describe('MultiColumnFieldFooter', () => {
expect(button).toHaveAttribute('aria-disabled', 'true');
// Verify button doesn't trigger callback when disabled
- fireEvent.click(button);
+ await user.click(button);
expect(onAdd).not.toHaveBeenCalled();
// Hover over button to show tooltip
- fireEvent.mouseEnter(button);
+ await user.hover(button);
// Verify tooltip text appears
await waitFor(() => {
diff --git a/frontend/packages/console-shared/src/components/getting-started/__tests__/GettingStartedCard.spec.tsx b/frontend/packages/console-shared/src/components/getting-started/__tests__/GettingStartedCard.spec.tsx
index cde12f6c5af..983d9990913 100644
--- a/frontend/packages/console-shared/src/components/getting-started/__tests__/GettingStartedCard.spec.tsx
+++ b/frontend/packages/console-shared/src/components/getting-started/__tests__/GettingStartedCard.spec.tsx
@@ -1,4 +1,5 @@
-import { screen, fireEvent } from '@testing-library/react';
+import { screen } from '@testing-library/react';
+import userEvent from '@testing-library/user-event';
import { renderWithProviders } from '@console/shared/src/test-utils/unit-test-utils';
import type { GettingStartedCardProps } from '../GettingStartedCard';
import { GettingStartedCard } from '../GettingStartedCard';
@@ -41,7 +42,8 @@ describe('GettingStartedCard', () => {
expect(screen.getByTestId('item more-link')).toBeInTheDocument();
});
- it('calls onClick for internal link', () => {
+ it('calls onClick for internal link', async () => {
+ const user = userEvent.setup();
const onClick = jest.fn();
const props = {
...defaultProps,
@@ -55,11 +57,12 @@ describe('GettingStartedCard', () => {
],
};
renderWithProviders();
- fireEvent.click(screen.getByTestId('item link-1'));
+ await user.click(screen.getByTestId('item link-1'));
expect(onClick).toHaveBeenCalled();
});
- it('calls onClick for moreLink', () => {
+ it('calls onClick for moreLink', async () => {
+ const user = userEvent.setup();
const onClick = jest.fn();
const props = {
...defaultProps,
@@ -71,7 +74,7 @@ describe('GettingStartedCard', () => {
},
};
renderWithProviders();
- fireEvent.click(screen.getByTestId('item more-link'));
+ await user.click(screen.getByTestId('item more-link'));
expect(onClick).toHaveBeenCalled();
});
diff --git a/frontend/packages/console-shared/src/components/getting-started/__tests__/RestoreGettingStartedButton.spec.tsx b/frontend/packages/console-shared/src/components/getting-started/__tests__/RestoreGettingStartedButton.spec.tsx
index b734447db97..6b1e8d2c451 100644
--- a/frontend/packages/console-shared/src/components/getting-started/__tests__/RestoreGettingStartedButton.spec.tsx
+++ b/frontend/packages/console-shared/src/components/getting-started/__tests__/RestoreGettingStartedButton.spec.tsx
@@ -1,4 +1,5 @@
-import { screen, fireEvent } from '@testing-library/react';
+import { screen } from '@testing-library/react';
+import userEvent from '@testing-library/user-event';
import { renderWithProviders } from '../../../test-utils/unit-test-utils';
import { RestoreGettingStartedButton } from '../RestoreGettingStartedButton';
import { useGettingStartedShowState, GettingStartedShowState } from '../useGettingStartedShowState';
@@ -29,7 +30,8 @@ describe('RestoreGettingStartedButton', () => {
expect(screen.getByText('Show getting started resources')).toBeVisible();
});
- it('should change user settings to show if button is pressed', () => {
+ it('should change user settings to show if button is pressed', async () => {
+ const user = userEvent.setup();
const setGettingStartedShowState = jest.fn();
useGettingStartedShowStateMock.mockReturnValue([
GettingStartedShowState.HIDE,
@@ -39,12 +41,13 @@ describe('RestoreGettingStartedButton', () => {
renderWithProviders();
- fireEvent.click(screen.getByRole('button', { name: 'Show getting started resources' }));
+ await user.click(screen.getByRole('button', { name: 'Show getting started resources' }));
expect(setGettingStartedShowState).toHaveBeenCalledTimes(1);
expect(setGettingStartedShowState).toHaveBeenLastCalledWith(GettingStartedShowState.SHOW);
});
- it('should change user settings to disappear if close (x) on the button is pressed', () => {
+ it('should change user settings to disappear if close (x) on the button is pressed', async () => {
+ const user = userEvent.setup();
const setGettingStartedShowState = jest.fn();
useGettingStartedShowStateMock.mockReturnValue([
GettingStartedShowState.HIDE,
@@ -54,7 +57,7 @@ describe('RestoreGettingStartedButton', () => {
renderWithProviders();
- fireEvent.click(screen.getByRole('button', { name: 'Close Show getting started resources' }));
+ await user.click(screen.getByRole('button', { name: 'Close Show getting started resources' }));
expect(setGettingStartedShowState).toHaveBeenCalledTimes(1);
expect(setGettingStartedShowState).toHaveBeenLastCalledWith(GettingStartedShowState.DISAPPEAR);
});
diff --git a/frontend/packages/console-shared/src/components/modals/__tests__/FetchProgressModal.spec.tsx b/frontend/packages/console-shared/src/components/modals/__tests__/FetchProgressModal.spec.tsx
index 0d510011282..b41d3f5c1a8 100644
--- a/frontend/packages/console-shared/src/components/modals/__tests__/FetchProgressModal.spec.tsx
+++ b/frontend/packages/console-shared/src/components/modals/__tests__/FetchProgressModal.spec.tsx
@@ -1,4 +1,5 @@
-import { render, screen, fireEvent, waitFor } from '@testing-library/react';
+import { render, screen, waitFor } from '@testing-library/react';
+import userEvent from '@testing-library/user-event';
import { coFetch } from '@console/internal/co-fetch';
import type { FetchProgressModalProps } from '../FetchProgressModal';
import { FetchProgressModal } from '../FetchProgressModal';
@@ -163,6 +164,7 @@ describe('FetchProgressModal', () => {
});
it('should show toast when download is cancelled', async () => {
+ const user = userEvent.setup();
const setIsDownloading = jest.fn();
coFetchMock.mockImplementation(
(_url: string, options: { signal: AbortSignal }) =>
@@ -177,7 +179,7 @@ describe('FetchProgressModal', () => {
,
);
- fireEvent.click(screen.getByTestId('fetch-progress-modal-cancel'));
+ await user.click(screen.getByTestId('fetch-progress-modal-cancel'));
await waitFor(() => {
expect(setIsDownloading).toHaveBeenCalledWith(false);
@@ -194,7 +196,8 @@ describe('FetchProgressModal', () => {
expect(addToastMock).toHaveBeenCalled();
});
- it('should abort the fetch when Cancel button is clicked', () => {
+ it('should abort the fetch when Cancel button is clicked', async () => {
+ const user = userEvent.setup();
const abortSpy = jest.spyOn(AbortController.prototype, 'abort');
const setIsDownloading = jest.fn();
coFetchMock.mockReturnValue(new Promise(() => {}));
@@ -203,7 +206,7 @@ describe('FetchProgressModal', () => {
,
);
- fireEvent.click(screen.getByRole('button', { name: 'Cancel' }));
+ await user.click(screen.getByRole('button', { name: 'Cancel' }));
expect(abortSpy).toHaveBeenCalled();
expect(setIsDownloading).toHaveBeenCalledWith(false);
@@ -312,6 +315,7 @@ describe('FetchProgressModal', () => {
});
it('should close the modal when PF modal close button is clicked', async () => {
+ const user = userEvent.setup();
const setIsDownloading = jest.fn();
const mockReader = createMockReader();
coFetchMock.mockResolvedValue({
@@ -334,7 +338,7 @@ describe('FetchProgressModal', () => {
/>,
);
- fireEvent.click(screen.getByLabelText('Close'));
+ await user.click(screen.getByLabelText('Close'));
await waitFor(() => {
expect(screen.queryByRole('dialog')).not.toBeInTheDocument();
diff --git a/frontend/packages/console-shared/src/components/modals/__tests__/TextInputModal.spec.tsx b/frontend/packages/console-shared/src/components/modals/__tests__/TextInputModal.spec.tsx
index 4adfea963c8..e3b7d8032f1 100644
--- a/frontend/packages/console-shared/src/components/modals/__tests__/TextInputModal.spec.tsx
+++ b/frontend/packages/console-shared/src/components/modals/__tests__/TextInputModal.spec.tsx
@@ -1,4 +1,5 @@
-import { screen, fireEvent, waitFor } from '@testing-library/react';
+import { screen, waitFor } from '@testing-library/react';
+import userEvent from '@testing-library/user-event';
import { renderWithProviders } from '../../../test-utils/unit-test-utils';
import { TextInputModal } from '../TextInputModal';
@@ -33,43 +34,48 @@ describe('TextInputModal', () => {
expect(input.value).toBe('initial-value');
});
- it('should call onSubmit with value when save button is clicked', () => {
+ it('should call onSubmit with value when save button is clicked', async () => {
+ const user = userEvent.setup();
renderWithProviders();
const saveButton = screen.getByRole('button', { name: 'Save' });
- fireEvent.click(saveButton);
+ await user.click(saveButton);
expect(mockOnSubmit).toHaveBeenCalledWith('initial-value');
expect(mockCloseOverlay).toHaveBeenCalled();
});
- it('should update value when input changes', () => {
+ it('should update value when input changes', async () => {
+ const user = userEvent.setup();
renderWithProviders();
const input = screen.getByTestId('input-value') as HTMLInputElement;
- fireEvent.change(input, { target: { value: 'new-value' } });
+ await user.clear(input);
+ await user.type(input, 'new-value');
expect(input.value).toBe('new-value');
});
- it('should call closeOverlay when cancel button is clicked', () => {
+ it('should call closeOverlay when cancel button is clicked', async () => {
+ const user = userEvent.setup();
renderWithProviders();
const cancelButton = screen.getByRole('button', { name: 'Cancel' });
- fireEvent.click(cancelButton);
+ await user.click(cancelButton);
expect(mockCloseOverlay).toHaveBeenCalled();
expect(mockOnSubmit).not.toHaveBeenCalled();
});
it('should show error when submitting empty value and isRequired is true', async () => {
+ const user = userEvent.setup();
renderWithProviders();
const input = screen.getByTestId('input-value') as HTMLInputElement;
- fireEvent.change(input, { target: { value: '' } });
+ await user.clear(input);
const saveButton = screen.getByRole('button', { name: 'Save' });
- fireEvent.click(saveButton);
+ await user.click(saveButton);
await waitFor(() => {
expect(screen.getByText('This field is required')).toBeInTheDocument();
@@ -80,12 +86,13 @@ describe('TextInputModal', () => {
});
it('should call validator and show error when validation fails', async () => {
+ const user = userEvent.setup();
const mockValidator = jest.fn().mockReturnValue('Invalid value');
renderWithProviders();
const saveButton = screen.getByRole('button', { name: 'Save' });
- fireEvent.click(saveButton);
+ await user.click(saveButton);
await waitFor(() => {
expect(screen.getByText('Invalid value')).toBeInTheDocument();
@@ -97,13 +104,14 @@ describe('TextInputModal', () => {
});
it('should allow submission after fixing validation error', async () => {
+ const user = userEvent.setup();
const mockValidator = jest.fn().mockReturnValueOnce('Invalid value').mockReturnValueOnce(null);
renderWithProviders();
// Trigger validation error
const saveButton = screen.getByRole('button', { name: 'Save' });
- fireEvent.click(saveButton);
+ await user.click(saveButton);
await waitFor(() => {
expect(screen.getByText('Invalid value')).toBeInTheDocument();
@@ -111,8 +119,9 @@ describe('TextInputModal', () => {
// Change input and retry
const input = screen.getByTestId('input-value') as HTMLInputElement;
- fireEvent.change(input, { target: { value: 'valid-value' } });
- fireEvent.click(saveButton);
+ await user.clear(input);
+ await user.type(input, 'valid-value');
+ await user.click(saveButton);
expect(mockValidator).toHaveBeenCalledTimes(2);
expect(mockOnSubmit).toHaveBeenCalledWith('valid-value');
@@ -145,11 +154,12 @@ describe('TextInputModal', () => {
expect(input).toHaveAttribute('type', 'email');
});
- it('should allow empty value submission when isRequired is false', () => {
+ it('should allow empty value submission when isRequired is false', async () => {
+ const user = userEvent.setup();
renderWithProviders();
const saveButton = screen.getByRole('button', { name: 'Save' });
- fireEvent.click(saveButton);
+ await user.click(saveButton);
expect(mockOnSubmit).toHaveBeenCalledWith('');
expect(mockCloseOverlay).toHaveBeenCalled();
@@ -163,21 +173,24 @@ describe('TextInputModal', () => {
expect(label.closest('.pf-v6-c-form__label')).toBeInTheDocument();
});
- it('should submit form when Enter is pressed in input field', () => {
+ it('should submit form when Enter is pressed in input field', async () => {
+ const user = userEvent.setup();
renderWithProviders();
const input = screen.getByTestId('input-value') as HTMLInputElement;
- fireEvent.submit(input.closest('form'));
+ await user.click(input);
+ await user.keyboard('{Enter}');
expect(mockOnSubmit).toHaveBeenCalledWith('initial-value');
expect(mockCloseOverlay).toHaveBeenCalled();
});
it('should prevent submission when value is empty and isRequired is true', async () => {
+ const user = userEvent.setup();
renderWithProviders();
const saveButton = screen.getByRole('button', { name: 'Save' });
- fireEvent.click(saveButton);
+ await user.click(saveButton);
// Should show validation error instead of calling onSubmit
await waitFor(() => {
diff --git a/frontend/packages/console-shared/src/components/progressive-list/__tests__/ProgressiveList.spec.tsx b/frontend/packages/console-shared/src/components/progressive-list/__tests__/ProgressiveList.spec.tsx
index 7d02efb2ff6..05e79d4fff9 100644
--- a/frontend/packages/console-shared/src/components/progressive-list/__tests__/ProgressiveList.spec.tsx
+++ b/frontend/packages/console-shared/src/components/progressive-list/__tests__/ProgressiveList.spec.tsx
@@ -1,5 +1,6 @@
import type { FC, ReactNode } from 'react';
-import { screen, fireEvent } from '@testing-library/react';
+import { screen } from '@testing-library/react';
+import userEvent from '@testing-library/user-event';
import { renderWithProviders } from '../../../test-utils/unit-test-utils';
import ProgressiveList from '../ProgressiveList';
import ProgressiveListItem from '../ProgressiveListItem';
@@ -49,7 +50,8 @@ describe('ProgressiveList', () => {
expect(screen.getByRole('button', { name: 'Dummy' })).toBeVisible();
});
- it('clicking on a button should add that component related to it to visibleItems list', () => {
+ it('clicking on a button should add that component related to it to visibleItems list', async () => {
+ const user = userEvent.setup();
const visibleItems: string[] = [];
const callback = jest.fn((item: string) => {
visibleItems.push(item);
@@ -67,7 +69,7 @@ describe('ProgressiveList', () => {
expect(screen.queryByText('Dummy Component')).not.toBeInTheDocument();
expect(visibleItems).toHaveLength(0);
- fireEvent.click(screen.getByRole('button', { name: 'Dummy' }));
+ await user.click(screen.getByRole('button', { name: 'Dummy' }));
expect(callback).toHaveBeenCalledWith('Dummy');
expect(visibleItems).toHaveLength(1);
diff --git a/frontend/packages/console-shared/src/components/toast/__tests__/ToastProvider.spec.tsx b/frontend/packages/console-shared/src/components/toast/__tests__/ToastProvider.spec.tsx
index fc3cac43372..c3ae7960827 100644
--- a/frontend/packages/console-shared/src/components/toast/__tests__/ToastProvider.spec.tsx
+++ b/frontend/packages/console-shared/src/components/toast/__tests__/ToastProvider.spec.tsx
@@ -1,5 +1,6 @@
import { useContext } from 'react';
-import { act, screen, fireEvent, waitFor } from '@testing-library/react';
+import { act, screen, waitFor } from '@testing-library/react';
+import userEvent from '@testing-library/user-event';
import { renderWithProviders } from '../../../test-utils/unit-test-utils';
import type { ToastContextType } from '../ToastContext';
import ToastContext, { ToastVariant } from '../ToastContext';
@@ -70,6 +71,7 @@ describe('ToastProvider', () => {
});
it('should dismiss toast on action', async () => {
+ const user = userEvent.setup();
const actionFn = jest.fn();
renderWithProviders(
@@ -97,7 +99,7 @@ describe('ToastProvider', () => {
});
const actionButton = screen.getByRole('button', { name: /action 1/i });
- fireEvent.click(actionButton);
+ await user.click(actionButton);
expect(actionFn).toHaveBeenCalledTimes(1);
@@ -140,6 +142,7 @@ describe('ToastProvider', () => {
});
it('should dismiss toast on action on anchor click', async () => {
+ const user = userEvent.setup();
const actionFn = jest.fn();
renderWithProviders(
@@ -169,9 +172,8 @@ describe('ToastProvider', () => {
const actionLink = await screen.findByText('action 1');
const anchorElement = actionLink.closest('a');
- if (anchorElement) {
- fireEvent.click(anchorElement);
- }
+ expect(anchorElement).toBeTruthy();
+ await user.click(anchorElement as HTMLElement);
expect(actionFn).toHaveBeenCalledTimes(1);
@@ -181,6 +183,7 @@ describe('ToastProvider', () => {
});
it('should call onToastClose if provided on toast close', async () => {
+ const user = userEvent.setup();
const toastClose = jest.fn();
renderWithProviders(
@@ -203,7 +206,7 @@ describe('ToastProvider', () => {
});
const closeButton = screen.getByRole('button', { name: /close/i });
- fireEvent.click(closeButton);
+ await user.click(closeButton);
expect(toastClose).toHaveBeenCalled();
});
diff --git a/frontend/packages/console-shared/src/test-utils/unit-test-utils.tsx b/frontend/packages/console-shared/src/test-utils/unit-test-utils.tsx
index 1b33fbebe51..093b21ce2f0 100644
--- a/frontend/packages/console-shared/src/test-utils/unit-test-utils.tsx
+++ b/frontend/packages/console-shared/src/test-utils/unit-test-utils.tsx
@@ -3,7 +3,8 @@ import type { PluginStore } from '@openshift/dynamic-plugin-sdk';
import { PluginStoreProvider } from '@openshift/dynamic-plugin-sdk';
import { Form } from '@patternfly/react-core';
import type { RenderOptions, BoundFunctions, Queries } from '@testing-library/react';
-import { render, renderHook, screen, fireEvent, waitFor, within } from '@testing-library/react';
+import { render, renderHook, screen, waitFor, within } from '@testing-library/react';
+import userEvent from '@testing-library/user-event';
import type { FormikValues } from 'formik';
import { Formik } from 'formik';
import { Provider } from 'react-redux';
@@ -182,11 +183,10 @@ export const verifyInputField = async ({
}
// Simulate an input change if a new value is provided
- // TODO: Use the 'userEvent' instead of 'fireEvent' after Jest and React Testing Libraries upgrade
if (testValue !== undefined) {
- await (async () => {
- fireEvent.change(input, { target: { value: testValue } });
- await waitFor(() => expect(input).toHaveValue(testValue));
- });
+ const user = userEvent.setup();
+ await user.clear(input);
+ await user.type(input, testValue);
+ await waitFor(() => expect(input).toHaveValue(testValue));
}
};
diff --git a/frontend/packages/container-security/src/components/__tests__/ImageVulnerabilityToggleGroup.spec.tsx b/frontend/packages/container-security/src/components/__tests__/ImageVulnerabilityToggleGroup.spec.tsx
index ae386ace3b9..716628bc09b 100644
--- a/frontend/packages/container-security/src/components/__tests__/ImageVulnerabilityToggleGroup.spec.tsx
+++ b/frontend/packages/container-security/src/components/__tests__/ImageVulnerabilityToggleGroup.spec.tsx
@@ -1,4 +1,5 @@
-import { screen, fireEvent } from '@testing-library/react';
+import { screen } from '@testing-library/react';
+import userEvent from '@testing-library/user-event';
import { renderWithProviders } from '@console/shared/src/test-utils/unit-test-utils';
import { fakeVulnFor } from '../../../integration-tests/bad-pods';
import { Priority } from '../../const';
@@ -28,11 +29,12 @@ describe('ImageVulnerabilityToggleGroup', () => {
expect(screen.getByText(/Quay Security Scanner has detected.*vulnerabilities/i)).toBeVisible();
});
- it('should render empty state when switching to App dependency with no vulnerabilities', () => {
+ it('should render empty state when switching to App dependency with no vulnerabilities', async () => {
+ const user = userEvent.setup();
renderWithProviders();
const appDependencyButton = screen.getByText('App dependency');
- fireEvent.click(appDependencyButton);
+ await user.click(appDependencyButton);
expect(screen.getByText(/No.*app dependency.*vulnerabilities/i)).toBeVisible();
});
diff --git a/frontend/packages/dev-console/src/components/deployments/__tests__/AdvancedSection.spec.tsx b/frontend/packages/dev-console/src/components/deployments/__tests__/AdvancedSection.spec.tsx
index 7449459fa68..f8df34fbec3 100644
--- a/frontend/packages/dev-console/src/components/deployments/__tests__/AdvancedSection.spec.tsx
+++ b/frontend/packages/dev-console/src/components/deployments/__tests__/AdvancedSection.spec.tsx
@@ -1,4 +1,5 @@
-import { render, screen, fireEvent, cleanup, waitFor } from '@testing-library/react';
+import { render, screen, cleanup, waitFor } from '@testing-library/react';
+import userEvent from '@testing-library/user-event';
import i18n from 'i18next';
import { setI18n } from 'react-i18next';
import { Resources } from '../../import/import-types';
@@ -34,6 +35,7 @@ afterEach(() => cleanup());
describe('AdvancedSection', () => {
it('should show Pause rollouts section on click', async () => {
+ const user = userEvent.setup();
expect(screen.getByTestId('deployment-form-testid').textContent).toEqual(
'Click on the names to access advanced options for Pause rollouts and Scaling.',
);
@@ -45,7 +47,7 @@ describe('AdvancedSection', () => {
expect(screen.queryByTestId('pause-rollouts')).toBeNull();
expect(screen.queryByRole('checkbox')).toBeNull();
- fireEvent.click(pauseRolloutsButton);
+ await user.click(pauseRolloutsButton);
await waitFor(() => {
expect(screen.queryByTestId('pause-rollouts')).not.toBeNull();
@@ -58,6 +60,7 @@ describe('AdvancedSection', () => {
});
it('should show Scaling section on click', async () => {
+ const user = userEvent.setup();
expect(screen.getByTestId('deployment-form-testid').textContent).toEqual(
'Click on the names to access advanced options for Pause rollouts and Scaling.',
);
@@ -69,7 +72,7 @@ describe('AdvancedSection', () => {
expect(screen.queryByTestId('scaling')).toBeNull();
expect(screen.queryByRole('spinbutton', { name: /input/i })).toBeNull();
- fireEvent.click(scalingButton);
+ await user.click(scalingButton);
await waitFor(() => {
expect(screen.queryByTestId('scaling')).not.toBeNull();
@@ -82,6 +85,7 @@ describe('AdvancedSection', () => {
});
it('should not show message when both advanced options are clicked', async () => {
+ const user = userEvent.setup();
expect(screen.getByTestId('deployment-form-testid').textContent).toEqual(
'Click on the names to access advanced options for Pause rollouts and Scaling.',
);
@@ -92,7 +96,7 @@ describe('AdvancedSection', () => {
expect(screen.queryByTestId('pause-rollouts')).toBeNull();
expect(screen.queryByRole('checkbox')).toBeNull();
- fireEvent.click(pauseRolloutsButton);
+ await user.click(pauseRolloutsButton);
await waitFor(() => {
expect(screen.queryByTestId('pause-rollouts')).not.toBeNull();
@@ -110,7 +114,7 @@ describe('AdvancedSection', () => {
expect(screen.queryByTestId('scaling')).toBeNull();
expect(screen.queryByRole('spinbutton', { name: /input/i })).toBeNull();
- fireEvent.click(scalingButton);
+ await user.click(scalingButton);
await waitFor(() => {
expect(screen.queryByTestId('scaling')).not.toBeNull();
diff --git a/frontend/packages/dev-console/src/components/deployments/__tests__/DeploymentForm.spec.tsx b/frontend/packages/dev-console/src/components/deployments/__tests__/DeploymentForm.spec.tsx
index 84e118a56ca..bae9e0dcc22 100644
--- a/frontend/packages/dev-console/src/components/deployments/__tests__/DeploymentForm.spec.tsx
+++ b/frontend/packages/dev-console/src/components/deployments/__tests__/DeploymentForm.spec.tsx
@@ -1,5 +1,6 @@
import type { FC } from 'react';
-import { fireEvent, screen, cleanup, waitFor } from '@testing-library/react';
+import { screen, cleanup, waitFor } from '@testing-library/react';
+import userEvent from '@testing-library/user-event';
import i18n from 'i18next';
import * as _ from 'lodash';
import { setI18n } from 'react-i18next';
@@ -102,19 +103,20 @@ describe('EditDeploymentForm', () => {
});
it('should show all the form sections wrt form/YAML view', async () => {
+ const user = userEvent.setup();
const formButton = screen.getByRole('radio', { name: /form view/i });
const yamlButton = screen.getByRole('radio', {
name: /yaml view/i,
});
- fireEvent.click(yamlButton);
+ await user.click(yamlButton);
await waitFor(() => {
expect(screen.queryByTestId('yaml-editor')).not.toBeNull();
expect(screen.queryByTestId('form-footer')).not.toBeNull();
});
- fireEvent.click(formButton);
+ await user.click(formButton);
await waitFor(() => {
expect(screen.queryByTestId('info-alert')).not.toBeNull();
@@ -127,6 +129,7 @@ describe('EditDeploymentForm', () => {
});
it('should disable save button and show loader on save button click', async () => {
+ const user = userEvent.setup();
const saveButton = screen.getByRole('button', {
name: /save/i,
});
@@ -138,23 +141,25 @@ describe('EditDeploymentForm', () => {
mockValues.formData.isSearchingForImage = true;
mockValues.formData.deploymentStrategy.rollingParams.timeoutSeconds = 500;
- fireEvent.change(timeoutField, { target: { value: 500 } });
+ await user.clear(timeoutField);
+ await user.type(timeoutField, '500');
expect(saveButton.hasAttribute('disabled')).toBeFalsy();
await waitFor(() => {
expect(timeoutField.value).toEqual('500');
});
- fireEvent.click(saveButton);
+ await user.click(saveButton);
- expect(saveButton.hasAttribute('disabled')).toBeTruthy();
- expect(saveButton.querySelector('.pf-v6-c-button__progress')).not.toBeNull();
await waitFor(() => {
+ expect(saveButton.hasAttribute('disabled')).toBeTruthy();
+ expect(saveButton.querySelector('.pf-v6-c-button__progress')).not.toBeNull();
expect(handleSubmit).toHaveBeenCalledTimes(1);
});
});
it('should load the form with current resource values on reload button click', async () => {
+ const user = userEvent.setup();
const reloadButton = screen.getByRole('button', {
name: /reload/i,
});
@@ -162,13 +167,14 @@ describe('EditDeploymentForm', () => {
name: /timeout/i,
}) as HTMLInputElement;
- fireEvent.change(timeoutField, { target: { value: 500 } });
+ await user.clear(timeoutField);
+ await user.type(timeoutField, '500');
await waitFor(() => {
expect(timeoutField.value).toEqual('500');
});
- fireEvent.click(reloadButton);
+ await user.click(reloadButton);
await waitFor(() => {
expect(timeoutField.value).toEqual('600');
@@ -176,11 +182,12 @@ describe('EditDeploymentForm', () => {
});
it('should call handleCancel on Cancel button click ', async () => {
+ const user = userEvent.setup();
const cancelButton = screen.getByRole('button', {
name: /cancel/i,
});
- fireEvent.click(cancelButton);
+ await user.click(cancelButton);
await waitFor(() => expect(handleCancel).toHaveBeenCalledTimes(1));
});
diff --git a/frontend/packages/dev-console/src/components/deployments/__tests__/DeploymentStrategySection.spec.tsx b/frontend/packages/dev-console/src/components/deployments/__tests__/DeploymentStrategySection.spec.tsx
index 50161ea274c..3b95b51d5fd 100644
--- a/frontend/packages/dev-console/src/components/deployments/__tests__/DeploymentStrategySection.spec.tsx
+++ b/frontend/packages/dev-console/src/components/deployments/__tests__/DeploymentStrategySection.spec.tsx
@@ -1,4 +1,5 @@
-import { cleanup, fireEvent, render, screen, waitFor } from '@testing-library/react';
+import { cleanup, render, screen, waitFor } from '@testing-library/react';
+import userEvent from '@testing-library/user-event';
import { Provider } from 'react-redux';
import store from '@console/internal/redux';
import { Resources } from '../../import/import-types';
@@ -18,6 +19,7 @@ afterEach(() => cleanup());
describe('DeploymentStrategySection(DeploymentConfig)', () => {
it('should show strategy fields based on strategy type selected', async () => {
+ const user = userEvent.setup();
render(
{() => (
@@ -39,21 +41,21 @@ describe('DeploymentStrategySection(DeploymentConfig)', () => {
name: /strategy type/i,
});
- fireEvent.click(strategyDropdown);
+ await user.click(strategyDropdown);
const recreateButton = screen.getByRole('option', { name: /recreate/i });
- fireEvent.click(recreateButton);
+ await user.click(recreateButton);
await waitFor(() => {
expect(screen.queryByTestId('recreateParams')).not.toBeNull();
});
- fireEvent.click(strategyDropdown);
+ await user.click(strategyDropdown);
const customButton = screen.getByRole('option', { name: /custom/i });
- fireEvent.click(customButton);
+ await user.click(customButton);
await waitFor(() => {
expect(screen.queryByTestId('customParams')).not.toBeNull();
@@ -61,6 +63,7 @@ describe('DeploymentStrategySection(DeploymentConfig)', () => {
});
it('should render additional fields for Recreate strategy type', async () => {
+ const user = userEvent.setup();
render(
{
name: /strategy type/i,
});
- fireEvent.click(strategyDropdown);
+ await user.click(strategyDropdown);
const recreateButton = screen.getByRole('option', { name: /recreate/i });
- fireEvent.click(recreateButton);
+ await user.click(recreateButton);
await waitFor(() => {
expect(screen.queryByTestId('recreateParams')).not.toBeNull();
@@ -97,7 +100,7 @@ describe('DeploymentStrategySection(DeploymentConfig)', () => {
const advancedSection = screen.getByText('Show additional parameters and lifecycle hooks');
- fireEvent.click(advancedSection);
+ await user.click(advancedSection);
await waitFor(() => {
expect(screen.getByText('Pre Lifecycle Hook')).not.toBeNull();
@@ -107,7 +110,7 @@ describe('DeploymentStrategySection(DeploymentConfig)', () => {
const addMidLifecycleHook = screen.getByText('Add mid lifecycle hook');
- fireEvent.click(addMidLifecycleHook);
+ await user.click(addMidLifecycleHook);
await waitFor(() => {
expect(
@@ -120,7 +123,7 @@ describe('DeploymentStrategySection(DeploymentConfig)', () => {
let action = screen.getByText(
'Runs a command in a new pod using the container from the deployment template. You can add additional environment variables and volumes',
);
- fireEvent.click(action);
+ await user.click(action);
await waitFor(() => {
expect(screen.getByText('Container name')).not.toBeNull();
@@ -130,7 +133,7 @@ describe('DeploymentStrategySection(DeploymentConfig)', () => {
action = screen.getByText(
'Tags the current image as an image stream tag if the deployment succeeds',
);
- fireEvent.click(action);
+ await user.click(action);
await waitFor(() => {
expect(screen.getByText('Container name')).not.toBeNull();
@@ -141,6 +144,7 @@ describe('DeploymentStrategySection(DeploymentConfig)', () => {
describe('DeploymentStrategySection(Deployment)', () => {
it('should show strategy fields based on strategy type selected', async () => {
+ const user = userEvent.setup();
render(
{
expect(screen.queryByTestId('rollingUpdate')).not.toBeNull();
});
- fireEvent.click(strategyDropdown);
+ await user.click(strategyDropdown);
const recreateButton = screen.getByRole('option', { name: /recreate/i });
- fireEvent.click(recreateButton);
+ await user.click(recreateButton);
await waitFor(() => {
expect(screen.queryByTestId('recreateParams')).toBeNull();
});
- fireEvent.click(strategyDropdown);
+ await user.click(strategyDropdown);
await waitFor(() => {
expect(screen.queryByRole('option', { name: /custom/i })).toBeNull();
diff --git a/frontend/packages/dev-console/src/components/deployments/__tests__/EnvironmentVariablesSection.spec.tsx b/frontend/packages/dev-console/src/components/deployments/__tests__/EnvironmentVariablesSection.spec.tsx
index 97eb86ea1d6..0ed1708eb0f 100644
--- a/frontend/packages/dev-console/src/components/deployments/__tests__/EnvironmentVariablesSection.spec.tsx
+++ b/frontend/packages/dev-console/src/components/deployments/__tests__/EnvironmentVariablesSection.spec.tsx
@@ -1,5 +1,6 @@
import type { FC } from 'react';
-import { cleanup, fireEvent, render, screen, waitFor } from '@testing-library/react';
+import { cleanup, render, screen, waitFor } from '@testing-library/react';
+import userEvent from '@testing-library/user-event';
import * as _ from 'lodash';
import { Provider } from 'react-redux';
import store from '@console/internal/redux';
@@ -53,9 +54,10 @@ describe('EnvironmentVariablesSection', () => {
});
it('should add a new row when (+) Add button is clicked', async () => {
+ const user = userEvent.setup();
const addButton = screen.getByRole('button', { name: /add value/i });
- fireEvent.click(addButton);
+ await user.click(addButton);
const names = screen.getAllByPlaceholderText(/name/i).map((ele: HTMLInputElement) => ele.value);
const values = screen
@@ -69,30 +71,32 @@ describe('EnvironmentVariablesSection', () => {
});
it('should add new row with resourse and key dropdowns when (+) Add ConfigMap or Secret button is clicked', async () => {
+ const user = userEvent.setup();
const addCMSButton = screen.getByRole('button', {
name: /add from configmap or secret/i,
});
- fireEvent.click(addCMSButton);
+ await user.click(addCMSButton);
const resourceButton = screen.getByRole('button', { name: /select a resource/i });
const keyButton = screen.getByRole('button', { name: /select a key/i });
- fireEvent.click(resourceButton);
+ await user.click(resourceButton);
const resourceDropdown = screen.queryByPlaceholderText(/configmap or secret/i);
await waitFor(() => expect(resourceDropdown).not.toBeNull());
- fireEvent.click(keyButton);
+ await user.click(keyButton);
const keyDropdown = screen.queryByPlaceholderText(/key/i);
await waitFor(() => expect(keyDropdown).not.toBeNull());
});
it('should remove row when (-) button is clicked', async () => {
+ const user = userEvent.setup();
const deleteButtons = screen.getAllByRole('button', { name: /delete/i });
- fireEvent.click(deleteButtons[0]);
+ await user.click(deleteButtons[0]);
const names = screen.getAllByPlaceholderText(/name/i).map((ele: HTMLInputElement) => ele.value);
const values = screen
diff --git a/frontend/packages/dev-console/src/components/deployments/__tests__/ImagesSection.spec.tsx b/frontend/packages/dev-console/src/components/deployments/__tests__/ImagesSection.spec.tsx
index f7c8e211745..64f0add889d 100644
--- a/frontend/packages/dev-console/src/components/deployments/__tests__/ImagesSection.spec.tsx
+++ b/frontend/packages/dev-console/src/components/deployments/__tests__/ImagesSection.spec.tsx
@@ -1,6 +1,7 @@
import type { FC } from 'react';
import type { RenderResult } from '@testing-library/react';
-import { cleanup, fireEvent, render, screen, waitFor } from '@testing-library/react';
+import { cleanup, render, screen, waitFor } from '@testing-library/react';
+import userEvent from '@testing-library/user-event';
import { Provider } from 'react-redux';
import store from '@console/internal/redux';
import { Resources } from '../../import/import-types';
@@ -41,6 +42,7 @@ afterEach(() => cleanup());
describe('ImagesSection', () => {
it('should have image-stream-tag dropdowns or image-name text field based on fromImageStreamTagCheckbox value', async () => {
+ const user = userEvent.setup();
const fromImageStreamTagCheckbox = screen.getByRole('checkbox', {
name: /deploy image from an image stream tag/i,
});
@@ -48,7 +50,7 @@ describe('ImagesSection', () => {
expect(screen.queryByTestId('image-stream-tag')).not.toBeNull();
expect(screen.queryByTestId('image-name')).toBeNull();
- fireEvent.click(fromImageStreamTagCheckbox);
+ await user.click(fromImageStreamTagCheckbox);
await waitFor(() => {
expect(screen.queryByTestId('image-name')).not.toBeNull();
@@ -57,6 +59,7 @@ describe('ImagesSection', () => {
});
it('should have the required trigger checkbox fields based on fromImageStreamTagCheckbox value', async () => {
+ const user = userEvent.setup();
const fromImageStreamTagCheckbox = screen.getByRole('checkbox', {
name: /deploy image from an image stream tag/i,
});
@@ -72,7 +75,7 @@ describe('ImagesSection', () => {
}),
).not.toBeNull();
- fireEvent.click(fromImageStreamTagCheckbox);
+ await user.click(fromImageStreamTagCheckbox);
await waitFor(() => {
expect(
@@ -89,6 +92,7 @@ describe('ImagesSection', () => {
});
it('should have the required trigger checkbox fields based on resourceType', async () => {
+ const user = userEvent.setup();
renderResults.rerender(
{() => (
@@ -114,7 +118,7 @@ describe('ImagesSection', () => {
}),
).toBeNull();
- fireEvent.click(fromImageStreamTagCheckbox);
+ await user.click(fromImageStreamTagCheckbox);
await waitFor(() => {
expect(
@@ -131,6 +135,7 @@ describe('ImagesSection', () => {
});
it('should have the advanced options expand/collapse button', async () => {
+ const user = userEvent.setup();
const showAdvancedOptions = screen.getByRole('button', {
name: /show advanced image options/i,
});
@@ -146,7 +151,7 @@ describe('ImagesSection', () => {
}),
).toBeNull();
- fireEvent.click(showAdvancedOptions);
+ await user.click(showAdvancedOptions);
await waitFor(() => {
expect(
diff --git a/frontend/packages/dev-console/src/components/deployments/__tests__/PauseRolloutsSection.spec.tsx b/frontend/packages/dev-console/src/components/deployments/__tests__/PauseRolloutsSection.spec.tsx
index 790065d9fbc..7b397ccf24d 100644
--- a/frontend/packages/dev-console/src/components/deployments/__tests__/PauseRolloutsSection.spec.tsx
+++ b/frontend/packages/dev-console/src/components/deployments/__tests__/PauseRolloutsSection.spec.tsx
@@ -1,10 +1,12 @@
-import { render, fireEvent, screen, waitFor } from '@testing-library/react';
+import { render, screen, waitFor } from '@testing-library/react';
+import userEvent from '@testing-library/user-event';
import { Resources } from '../../import/import-types';
import MockForm from '../__mocks__/MockForm';
import PauseRolloutsSection from '../PauseRolloutsSection';
describe('PauseRolloutsSection', () => {
it('checkbox should work correctly', async () => {
+ const user = userEvent.setup();
const handleSubmit = jest.fn();
render(
@@ -13,7 +15,7 @@ describe('PauseRolloutsSection', () => {
);
const checkbox = screen.getByRole('checkbox') as HTMLInputElement;
expect(checkbox.value).toEqual('false');
- fireEvent.click(checkbox);
+ await user.click(checkbox);
await waitFor(() => expect(checkbox.value).toEqual('true'));
});
});
diff --git a/frontend/packages/dev-console/src/components/user-preferences/__tests__/SecureRouteFields.spec.tsx b/frontend/packages/dev-console/src/components/user-preferences/__tests__/SecureRouteFields.spec.tsx
index ea0bf205410..a8c85f237cc 100644
--- a/frontend/packages/dev-console/src/components/user-preferences/__tests__/SecureRouteFields.spec.tsx
+++ b/frontend/packages/dev-console/src/components/user-preferences/__tests__/SecureRouteFields.spec.tsx
@@ -1,4 +1,5 @@
-import { configure, fireEvent, render, screen, waitFor } from '@testing-library/react';
+import { configure, render, screen, waitFor } from '@testing-library/react';
+import userEvent from '@testing-library/user-event';
import SecureRouteFields from '../SecureRouteFields';
import { usePreferredRoutingOptions } from '../usePreferredRoutingOptions';
@@ -35,6 +36,7 @@ describe('SecureRouteFields', () => {
});
it('should not show Allow option in Insecure traffic dropdown if TLS termination is Passthrough', async () => {
+ const user = userEvent.setup();
mockUsePreferredRoutingOptions.mockReturnValue([
{
secure: true,
@@ -45,8 +47,8 @@ describe('SecureRouteFields', () => {
true,
]);
render();
- const inSecureTraffic = screen.queryByTestId('insecure-traffic');
- fireEvent.click(inSecureTraffic);
+ const inSecureTraffic = screen.getByTestId('insecure-traffic');
+ await user.click(inSecureTraffic);
await waitFor(() => {
expect(screen.queryByRole('option', { name: /Allow/i })).toBeNull();
expect(screen.queryByRole('option', { name: /None/i })).not.toBeNull();
@@ -55,6 +57,7 @@ describe('SecureRouteFields', () => {
});
it('should show Allow, None and Redirect options in Insecure traffic dropdown if TLS termination is not Passthrough', async () => {
+ const user = userEvent.setup();
mockUsePreferredRoutingOptions.mockReturnValue([
{
secure: true,
@@ -65,8 +68,8 @@ describe('SecureRouteFields', () => {
true,
]);
render();
- const inSecureTraffic = screen.queryByTestId('insecure-traffic');
- fireEvent.click(inSecureTraffic);
+ const inSecureTraffic = screen.getByTestId('insecure-traffic');
+ await user.click(inSecureTraffic);
await waitFor(() => {
expect(screen.queryByRole('option', { name: /Allow/i })).not.toBeNull();
expect(screen.queryByRole('option', { name: /None/i })).not.toBeNull();
diff --git a/frontend/packages/knative-plugin/src/components/overview/__tests__/RevisionsOverviewList.spec.tsx b/frontend/packages/knative-plugin/src/components/overview/__tests__/RevisionsOverviewList.spec.tsx
index cc4b2ba2537..93ecf952490 100644
--- a/frontend/packages/knative-plugin/src/components/overview/__tests__/RevisionsOverviewList.spec.tsx
+++ b/frontend/packages/knative-plugin/src/components/overview/__tests__/RevisionsOverviewList.spec.tsx
@@ -1,4 +1,5 @@
-import { render, screen, fireEvent } from '@testing-library/react';
+import { render, screen } from '@testing-library/react';
+import userEvent from '@testing-library/user-event';
import * as _ from 'lodash';
import { useAccessReview } from '@console/internal/components/utils';
import { referenceForModel } from '@console/internal/module/k8s';
@@ -129,7 +130,8 @@ describe('RevisionsOverviewList', () => {
expect(button?.outerHTML).not.toContain('isdisabled');
});
- it('should call setTrafficDistributionModal on click', () => {
+ it('should call setTrafficDistributionModal on click', async () => {
+ const user = userEvent.setup();
const trafficSplitModalLauncherMock = jest.fn();
useTrafficSplittingModalLauncherMock.mockImplementation(() => trafficSplitModalLauncherMock);
const { container } = render(
@@ -140,7 +142,7 @@ describe('RevisionsOverviewList', () => {
);
const button = container.querySelector('Button');
expect(button).toBeInTheDocument();
- fireEvent.click(button);
+ await user.click(button as HTMLElement);
expect(trafficSplitModalLauncherMock).toHaveBeenCalled();
});
diff --git a/frontend/packages/knative-plugin/src/components/sink-source/__tests__/SinkSourceModal.spec.tsx b/frontend/packages/knative-plugin/src/components/sink-source/__tests__/SinkSourceModal.spec.tsx
index b055f64e6c6..8723ba1fe3d 100644
--- a/frontend/packages/knative-plugin/src/components/sink-source/__tests__/SinkSourceModal.spec.tsx
+++ b/frontend/packages/knative-plugin/src/components/sink-source/__tests__/SinkSourceModal.spec.tsx
@@ -1,5 +1,6 @@
import type { ComponentProps } from 'react';
-import { render, fireEvent } from '@testing-library/react';
+import { render, screen } from '@testing-library/react';
+import userEvent from '@testing-library/user-event';
import { formikFormProps } from '@console/shared/src/test-utils/formik-props-utils';
import { ServiceModel } from '../../../models';
import SinkSourceModal from '../SinkSourceModal';
@@ -8,7 +9,13 @@ jest.mock('@patternfly/react-core', () => ({
...jest.requireActual('@patternfly/react-core'),
ModalHeader: jest.fn(() => null),
ModalBody: jest.fn(({ children }) =>