diff --git a/webapp/STYLE_GUIDE.md b/webapp/STYLE_GUIDE.md index 53cf04e667d..d38274c8579 100644 --- a/webapp/STYLE_GUIDE.md +++ b/webapp/STYLE_GUIDE.md @@ -87,7 +87,15 @@ The following guidelines should be applied to both new and existing code. Howeve - **Selectors**: Prefer using accessible RTL selectors to help ensure that components are accessible, roughly in this order: `getByRole` > `getByText`/`getByPlaceholderText` > `getByLabelText`/`getByAltText`/`getByTitle` > `getByTestId`. Usage of `getByTestId` should be rare. - **User Interactions**: Prefer `userEvent` over `fireEvent` for user interactions, and don't directly call methods on DOM elements to simulate events. RTL's `userEvent` simulates events the most realistically, and it ensures that component changes are properly wrapped in `act`. - **Async Interactions**: Always wait for all methods of `userEvent` as those methods are all asynchronous (e.g. `await userEvent.click(...)`). -- **Usage of act**: `act` should only be used when performing any action that causes React to update and when that action does not alreadu go through a helper provided by RTL such as `userEvent`. Typically, most tests can be written without using `act` explicitly. + - **When fireEvent is acceptable**: Use `fireEvent` only in these specific cases where `userEvent` cannot be used: + - **Focus/Blur events**: `userEvent` doesn't have direct focus/blur methods. Use `fireEvent.focus()` and `fireEvent.blur()`. + - **Scroll events**: `userEvent` doesn't support scroll events. Use `fireEvent.scroll()`. + - **Image loading events**: `userEvent` doesn't support image loading events. Use `fireEvent.load()` and `fireEvent.error()`. + - **Document-level keyboard events**: `userEvent.keyboard()` requires element focus. Use `fireEvent.keyDown(document, ...)` for global keyboard shortcuts. + - **Fake timers**: `userEvent` doesn't work well with `jest.useFakeTimers()` and causes timeouts. Use `fireEvent.click()` when tests use fake timers. + - **Disabled elements**: `userEvent` respects CSS `pointer-events: none` on disabled elements. Use `fireEvent.click()` when testing that disabled element handlers are properly guarded. + - **MouseMove events**: `userEvent.hover()` only triggers mouseEnter/mouseOver, not mouseMove. Use `fireEvent.mouseMove()` when testing mouseMove handlers specifically. +- **Usage of act**: `act` should only be used when performing any action that causes React to update and when that action does not already go through a helper provided by RTL such as `userEvent`. Typically, most tests can be written without using `act` explicitly. ### Dependencies & Packages diff --git a/webapp/channels/src/actions/burn_on_read_deletion.test.ts b/webapp/channels/src/actions/burn_on_read_deletion.test.ts index 979592c678c..3ba5b604503 100644 --- a/webapp/channels/src/actions/burn_on_read_deletion.test.ts +++ b/webapp/channels/src/actions/burn_on_read_deletion.test.ts @@ -13,7 +13,6 @@ describe('burn_on_read_deletion actions', () => { beforeEach(() => { mockDispatch = jest.fn(); - jest.clearAllMocks(); }); describe('burnPostNow', () => { diff --git a/webapp/channels/src/actions/burn_on_read_posts.test.ts b/webapp/channels/src/actions/burn_on_read_posts.test.ts index 5d2b7e969c1..7f5cbf263d1 100644 --- a/webapp/channels/src/actions/burn_on_read_posts.test.ts +++ b/webapp/channels/src/actions/burn_on_read_posts.test.ts @@ -28,10 +28,6 @@ describe('burn_on_read_posts actions', () => { ...mockPost, }; - beforeEach(() => { - jest.clearAllMocks(); - }); - describe('revealBurnOnReadPost', () => { const mockState = { entities: { diff --git a/webapp/channels/src/actions/integration_actions.test.ts b/webapp/channels/src/actions/integration_actions.test.ts index 51e3d15679e..474ea01bbdb 100644 --- a/webapp/channels/src/actions/integration_actions.test.ts +++ b/webapp/channels/src/actions/integration_actions.test.ts @@ -253,10 +253,6 @@ describe('actions/integration_actions', () => { describe('lookupInteractiveDialog', () => { const {getDialogArguments} = require('mattermost-redux/selectors/entities/integrations'); - beforeEach(() => { - jest.clearAllMocks(); - }); - test('lookupInteractiveDialog with current channel', async () => { const testState = { ...initialState, @@ -341,10 +337,6 @@ describe('actions/integration_actions', () => { }); describe('loadIncomingHooksAndProfilesForTeam', () => { - beforeEach(() => { - jest.clearAllMocks(); - }); - test('should load hooks and profiles', async () => { const testStore = mockStore(initialState); await testStore.dispatch(Actions.loadIncomingHooksAndProfilesForTeam('team_id1', 0, 50, false)); @@ -368,10 +360,6 @@ describe('actions/integration_actions', () => { }); describe('loadOutgoingHooksAndProfilesForTeam', () => { - beforeEach(() => { - jest.clearAllMocks(); - }); - test('should load outgoing hooks and profiles', async () => { const testStore = mockStore(initialState); await testStore.dispatch(Actions.loadOutgoingHooksAndProfilesForTeam('team_id1', 1, 25)); @@ -388,10 +376,6 @@ describe('actions/integration_actions', () => { }); describe('loadCommandsAndProfilesForTeam', () => { - beforeEach(() => { - jest.clearAllMocks(); - }); - test('should load commands and profiles', async () => { const testStore = mockStore(initialState); await testStore.dispatch(Actions.loadCommandsAndProfilesForTeam('team_id1')); @@ -408,10 +392,6 @@ describe('actions/integration_actions', () => { }); describe('loadOAuthAppsAndProfiles', () => { - beforeEach(() => { - jest.clearAllMocks(); - }); - test('should load OAuth apps with custom parameters', async () => { const testStore = mockStore(initialState); await testStore.dispatch(Actions.loadOAuthAppsAndProfiles(2, 30)); @@ -435,10 +415,6 @@ describe('actions/integration_actions', () => { }); describe('loadOutgoingOAuthConnectionsAndProfiles', () => { - beforeEach(() => { - jest.clearAllMocks(); - }); - test('should load outgoing OAuth connections', async () => { const testStore = mockStore(initialState); await testStore.dispatch(Actions.loadOutgoingOAuthConnectionsAndProfiles('team_id1', 1, 50)); diff --git a/webapp/channels/src/components/access_history_modal/access_history_modal.test.tsx b/webapp/channels/src/components/access_history_modal/access_history_modal.test.tsx index 8ddf6234cae..cd7ad6cc7a8 100644 --- a/webapp/channels/src/components/access_history_modal/access_history_modal.test.tsx +++ b/webapp/channels/src/components/access_history_modal/access_history_modal.test.tsx @@ -1,12 +1,11 @@ // Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved. // See LICENSE.txt for license information. -import {fireEvent, screen, waitFor} from '@testing-library/react'; import React from 'react'; import AccessHistoryModal from 'components/access_history_modal/access_history_modal'; -import {renderWithContext} from 'tests/react_testing_utils'; +import {renderWithContext, screen, userEvent, waitFor} from 'tests/react_testing_utils'; jest.mock('components/audit_table', () => { return jest.fn().mockImplementation(() => { @@ -85,7 +84,7 @@ describe('components/AccessHistoryModal', () => { ); await waitFor(() => screen.getByText('Access History')); - fireEvent.click(screen.getByLabelText('Close')); + await userEvent.click(screen.getByLabelText('Close')); expect(onHide).toHaveBeenCalledTimes(1); }); diff --git a/webapp/channels/src/components/activity_log_modal/activity_log_modal.test.tsx b/webapp/channels/src/components/activity_log_modal/activity_log_modal.test.tsx index 567561b9636..733fc11ab57 100644 --- a/webapp/channels/src/components/activity_log_modal/activity_log_modal.test.tsx +++ b/webapp/channels/src/components/activity_log_modal/activity_log_modal.test.tsx @@ -1,7 +1,6 @@ // Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved. // See LICENSE.txt for license information. -import {fireEvent, screen, waitFor} from '@testing-library/react'; import React from 'react'; import type {MouseEvent} from 'react'; @@ -9,7 +8,7 @@ import {General} from 'mattermost-redux/constants'; import ActivityLogModal from 'components/activity_log_modal/activity_log_modal'; -import {renderWithContext} from 'tests/react_testing_utils'; +import {renderWithContext, screen, userEvent, waitFor} from 'tests/react_testing_utils'; jest.mock('components/activity_log_modal/components/activity_log', () => { return jest.fn().mockImplementation(({submitRevoke, currentSession}) => { @@ -115,7 +114,7 @@ describe('components/ActivityLogModal', () => { />, ); - fireEvent.click(screen.getByTestId('activity-log')); + await userEvent.click(screen.getByTestId('activity-log')); expect(revokeSession).toHaveBeenCalledTimes(1); expect(revokeSession).toHaveBeenCalledWith('user1', 'session1'); @@ -137,7 +136,7 @@ describe('components/ActivityLogModal', () => { ); await waitFor(() => screen.getByText('Active Sessions')); - fireEvent.click(screen.getByLabelText('Close')); + await userEvent.click(screen.getByLabelText('Close')); expect(onHide).toHaveBeenCalledTimes(1); }); diff --git a/webapp/channels/src/components/admin_console/access_control/modals/policy_test/test_modal.test.tsx b/webapp/channels/src/components/admin_console/access_control/modals/policy_test/test_modal.test.tsx index 5cd555dd2d3..ee606f92273 100644 --- a/webapp/channels/src/components/admin_console/access_control/modals/policy_test/test_modal.test.tsx +++ b/webapp/channels/src/components/admin_console/access_control/modals/policy_test/test_modal.test.tsx @@ -1,12 +1,11 @@ // Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved. // See LICENSE.txt for license information. -import {screen, waitFor, fireEvent} from '@testing-library/react'; import React from 'react'; import type {UserProfile} from '@mattermost/types/users'; -import {renderWithContext} from 'tests/react_testing_utils'; +import {renderWithContext, screen, userEvent, waitFor} from 'tests/react_testing_utils'; import {TestHelper} from 'utils/test_helper'; import TestResultsModal from './test_modal'; @@ -86,8 +85,6 @@ describe('TestResultsModal', () => { }; beforeEach(() => { - jest.clearAllMocks(); - // Mock Redux thunk that returns successful user search response mockSearchUsers.mockReturnValue(() => Promise.resolve({ data: { @@ -97,10 +94,6 @@ describe('TestResultsModal', () => { })); }); - afterEach(() => { - jest.clearAllMocks(); - }); - it('should render modal with proper title and structure', async () => { renderWithContext(); @@ -147,7 +140,8 @@ describe('TestResultsModal', () => { // Perform search const searchInput = screen.getByTestId('search-input'); - fireEvent.change(searchInput, {target: {value: 'test search'}}); + await userEvent.clear(searchInput); + await userEvent.type(searchInput, 'test search'); await waitFor(() => { expect(mockSearchUsers).toHaveBeenCalledWith('test search', '', 50); @@ -172,7 +166,7 @@ describe('TestResultsModal', () => { // Click next page - this should trigger pagination with page=2 which translates to 20 users per page const nextPageButton = screen.getByTestId('next-page-button'); - fireEvent.click(nextPageButton); + await userEvent.click(nextPageButton); // The nextPage function gets called with page 1 (second page), but since it's above USERS_PER_PAGE (10) // but less than USERS_TO_FETCH (50), it should use the cursor logic and call with the last user's ID @@ -199,7 +193,7 @@ describe('TestResultsModal', () => { // Click next page while loading const nextPageButton = screen.getByTestId('next-page-button'); - fireEvent.click(nextPageButton); + await userEvent.click(nextPageButton); // Should not make additional call while loading expect(mockSearchUsers).toHaveBeenCalledTimes(1); @@ -241,7 +235,7 @@ describe('TestResultsModal', () => { // Find and click the close button const closeButton = screen.getByLabelText('Close'); - fireEvent.click(closeButton); + await userEvent.click(closeButton); await waitFor(() => { expect(mockOnExited).toHaveBeenCalled(); @@ -302,15 +296,16 @@ describe('TestResultsModal', () => { // Perform first search const searchInput = screen.getByTestId('search-input'); - fireEvent.change(searchInput, {target: {value: 'search1'}}); + await userEvent.clear(searchInput); + await userEvent.type(searchInput, 'search1'); await waitFor(() => { expect(mockSearchUsers).toHaveBeenCalledWith('search1', '', 50); }); // Perform second search - fireEvent.change(searchInput, {target: {value: ''}}); - fireEvent.change(searchInput, {target: {value: 'search2'}}); + await userEvent.clear(searchInput); + await userEvent.type(searchInput, 'search2'); await waitFor(() => { expect(mockSearchUsers).toHaveBeenCalledWith('search2', '', 50); diff --git a/webapp/channels/src/components/admin_console/blockable_link/blockable_link.test.tsx b/webapp/channels/src/components/admin_console/blockable_link/blockable_link.test.tsx index b426704f2be..ed39ec65370 100644 --- a/webapp/channels/src/components/admin_console/blockable_link/blockable_link.test.tsx +++ b/webapp/channels/src/components/admin_console/blockable_link/blockable_link.test.tsx @@ -1,10 +1,11 @@ // Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved. // See LICENSE.txt for license information. -import {fireEvent, render, screen} from '@testing-library/react'; import React from 'react'; import {MemoryRouter} from 'react-router-dom'; +import {render, screen, userEvent} from 'tests/react_testing_utils'; + import BlockableLink from './blockable_link'; jest.mock('utils/browser_history', () => ({ @@ -34,18 +35,18 @@ describe('components/admin_console/blockable_link/BlockableLink', () => { expect(screen.getByRole('link')).toHaveAttribute('href', '/admin_console/test'); }); - test('should navigate directly when not blocked', () => { + test('should navigate directly when not blocked', async () => { render( , ); - fireEvent.click(screen.getByText('Link Text')); + await userEvent.click(screen.getByText('Link Text')); expect(defaultProps.actions.deferNavigation).not.toHaveBeenCalled(); }); - test('should defer navigation when blocked', () => { + test('should defer navigation when blocked', async () => { const blockedProps = { ...defaultProps, blocked: true, @@ -57,11 +58,11 @@ describe('components/admin_console/blockable_link/BlockableLink', () => { , ); - fireEvent.click(screen.getByText('Link Text')); + await userEvent.click(screen.getByText('Link Text')); expect(blockedProps.actions.deferNavigation).toHaveBeenCalled(); }); - test('should call custom onClick handler if provided', () => { + test('should call custom onClick handler if provided', async () => { const onClickProps = { ...defaultProps, onClick: jest.fn(), @@ -73,7 +74,7 @@ describe('components/admin_console/blockable_link/BlockableLink', () => { , ); - fireEvent.click(screen.getByText('Link Text')); + await userEvent.click(screen.getByText('Link Text')); expect(onClickProps.onClick).toHaveBeenCalled(); }); diff --git a/webapp/channels/src/components/admin_console/checkbox_setting.test.tsx b/webapp/channels/src/components/admin_console/checkbox_setting.test.tsx index 4ebd8aea280..a91b2e0703f 100644 --- a/webapp/channels/src/components/admin_console/checkbox_setting.test.tsx +++ b/webapp/channels/src/components/admin_console/checkbox_setting.test.tsx @@ -3,7 +3,7 @@ import React from 'react'; -import {renderWithContext, screen, fireEvent} from 'tests/react_testing_utils'; +import {renderWithContext, screen, userEvent} from 'tests/react_testing_utils'; import CheckboxSetting from './checkbox_setting'; @@ -26,7 +26,7 @@ describe('components/admin_console/CheckboxSetting', () => { expect(container).toMatchSnapshot(); }); - test('onChange', () => { + test('onChange', async () => { const onChange = jest.fn(); renderWithContext( { const checkbox: HTMLInputElement = screen.getByRole('checkbox'); expect(checkbox).not.toBeChecked(); - fireEvent.click(checkbox); + await userEvent.click(checkbox); expect(onChange).toHaveBeenCalledTimes(1); expect(onChange).toHaveBeenCalledWith('string.id', true); diff --git a/webapp/channels/src/components/admin_console/content_flagging/additional_settings/additional_settings.test.tsx b/webapp/channels/src/components/admin_console/content_flagging/additional_settings/additional_settings.test.tsx index d4366553fae..e14ae0da0ab 100644 --- a/webapp/channels/src/components/admin_console/content_flagging/additional_settings/additional_settings.test.tsx +++ b/webapp/channels/src/components/admin_console/content_flagging/additional_settings/additional_settings.test.tsx @@ -1,12 +1,11 @@ // Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved. // See LICENSE.txt for license information. -import {screen, fireEvent} from '@testing-library/react'; import React from 'react'; import type {ContentFlaggingAdditionalSettings} from '@mattermost/types/config'; -import {renderWithContext} from 'tests/react_testing_utils'; +import {renderWithContext, screen, userEvent} from 'tests/react_testing_utils'; import ContentFlaggingAdditionalSettingsSection from './additional_settings'; @@ -22,10 +21,6 @@ describe('ContentFlaggingAdditionalSettingsSection', () => { } as ContentFlaggingAdditionalSettings, }; - beforeEach(() => { - jest.clearAllMocks(); - }); - test('should render with initial values', () => { renderWithContext(); @@ -60,10 +55,10 @@ describe('ContentFlaggingAdditionalSettingsSection', () => { expect(screen.getByTestId('hideFlaggedPosts_true')).not.toBeChecked(); }); - test('should call onChange when reporter comment requirement changes', () => { + test('should call onChange when reporter comment requirement changes', async () => { renderWithContext(); - fireEvent.click(screen.getByTestId('requireReporterComment_true')); + await userEvent.click(screen.getByTestId('requireReporterComment_true')); expect(defaultProps.onChange).toHaveBeenCalledWith('ContentFlaggingAdditionalSettings', { ...(defaultProps.value as ContentFlaggingAdditionalSettings), @@ -71,10 +66,10 @@ describe('ContentFlaggingAdditionalSettingsSection', () => { }); }); - test('should call onChange when reviewer comment requirement changes', () => { + test('should call onChange when reviewer comment requirement changes', async () => { renderWithContext(); - fireEvent.click(screen.getByTestId('requireReviewerComment_false')); + await userEvent.click(screen.getByTestId('requireReviewerComment_false')); expect(defaultProps.onChange).toHaveBeenCalledWith('ContentFlaggingAdditionalSettings', { ...(defaultProps.value as ContentFlaggingAdditionalSettings), @@ -82,10 +77,10 @@ describe('ContentFlaggingAdditionalSettingsSection', () => { }); }); - test('should call onChange when hide flagged content setting changes', () => { + test('should call onChange when hide flagged content setting changes', async () => { renderWithContext(); - fireEvent.click(screen.getByTestId('hideFlaggedPosts_true')); + await userEvent.click(screen.getByTestId('hideFlaggedPosts_true')); expect(defaultProps.onChange).toHaveBeenCalledWith('ContentFlaggingAdditionalSettings', { ...(defaultProps.value as ContentFlaggingAdditionalSettings), @@ -153,18 +148,18 @@ describe('ContentFlaggingAdditionalSettingsSection', () => { expect(selectInput).toHaveAttribute('id', 'contentFlaggingReasons'); }); - test('should maintain state consistency across multiple changes', () => { + test('should maintain state consistency across multiple changes', async () => { renderWithContext(); // Change reporter comment requirement - fireEvent.click(screen.getByTestId('requireReporterComment_true')); + await userEvent.click(screen.getByTestId('requireReporterComment_true')); expect(defaultProps.onChange).toHaveBeenLastCalledWith('ContentFlaggingAdditionalSettings', { ...(defaultProps.value as ContentFlaggingAdditionalSettings), ReporterCommentRequired: true, }); // Change hide flagged content - fireEvent.click(screen.getByTestId('hideFlaggedPosts_true')); + await userEvent.click(screen.getByTestId('hideFlaggedPosts_true')); expect(defaultProps.onChange).toHaveBeenLastCalledWith('ContentFlaggingAdditionalSettings', { ...(defaultProps.value as ContentFlaggingAdditionalSettings), ReporterCommentRequired: true, diff --git a/webapp/channels/src/components/admin_console/content_flagging/additional_settings/reason_option.test.tsx b/webapp/channels/src/components/admin_console/content_flagging/additional_settings/reason_option.test.tsx index 8556425a9b4..864c3faea21 100644 --- a/webapp/channels/src/components/admin_console/content_flagging/additional_settings/reason_option.test.tsx +++ b/webapp/channels/src/components/admin_console/content_flagging/additional_settings/reason_option.test.tsx @@ -1,11 +1,10 @@ // Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved. // See LICENSE.txt for license information. -import {screen, fireEvent} from '@testing-library/react'; import React from 'react'; import type {MultiValueProps} from 'react-select/dist/declarations/src/components/MultiValue'; -import {renderWithContext} from 'tests/react_testing_utils'; +import {renderWithContext, screen, userEvent} from 'tests/react_testing_utils'; import {ReasonOption} from './reason_option'; @@ -32,10 +31,6 @@ describe('ReasonOption', () => { theme: {} as any, } as unknown as MultiValueProps<{label: string; value: string}, true>; - beforeEach(() => { - jest.clearAllMocks(); - }); - test('should render the reason option with correct label', () => { renderWithContext(); @@ -55,11 +50,11 @@ describe('ReasonOption', () => { expect(removeButton).toBeInTheDocument(); }); - test('should call onClick when remove button is clicked', () => { + test('should call onClick when remove button is clicked', async () => { const {container} = renderWithContext(); const removeButton = container.querySelector('.Remove'); - fireEvent.click(removeButton!); + await userEvent.click(removeButton!); expect(mockProps.removeProps.onClick).toHaveBeenCalledTimes(1); }); diff --git a/webapp/channels/src/components/admin_console/content_flagging/content_reviewers/content_reviewers.test.tsx b/webapp/channels/src/components/admin_console/content_flagging/content_reviewers/content_reviewers.test.tsx index f48fac4c4ba..14c1ffa5669 100644 --- a/webapp/channels/src/components/admin_console/content_flagging/content_reviewers/content_reviewers.test.tsx +++ b/webapp/channels/src/components/admin_console/content_flagging/content_reviewers/content_reviewers.test.tsx @@ -1,12 +1,11 @@ // Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved. // See LICENSE.txt for license information. -import {screen, fireEvent} from '@testing-library/react'; import React from 'react'; import type {ContentFlaggingReviewerSetting, TeamReviewerSetting} from '@mattermost/types/config'; -import {renderWithContext} from 'tests/react_testing_utils'; +import {renderWithContext, screen, userEvent} from 'tests/react_testing_utils'; import ContentFlaggingContentReviewers from './content_reviewers'; @@ -57,10 +56,6 @@ describe('ContentFlaggingContentReviewers', () => { setByEnv: false, }; - beforeEach(() => { - jest.clearAllMocks(); - }); - it('renders the component with correct title and description', () => { renderWithContext(); @@ -108,7 +103,7 @@ describe('ContentFlaggingContentReviewers', () => { expect(screen.getByText('Team Administrators')).toBeInTheDocument(); }); - it('handles same reviewers for all teams radio button change to true', () => { + it('handles same reviewers for all teams radio button change to true', async () => { const props = { ...defaultProps, value: { @@ -120,7 +115,7 @@ describe('ContentFlaggingContentReviewers', () => { renderWithContext(); const trueRadio = screen.getByTestId('sameReviewersForAllTeams_true'); - fireEvent.click(trueRadio); + await userEvent.click(trueRadio); expect(defaultProps.onChange).toHaveBeenCalledWith('content_reviewers', { ...props.value, @@ -128,11 +123,11 @@ describe('ContentFlaggingContentReviewers', () => { }); }); - it('handles same reviewers for all teams radio button change to false', () => { + it('handles same reviewers for all teams radio button change to false', async () => { renderWithContext(); const falseRadio = screen.getByTestId('sameReviewersForAllTeams_false'); - fireEvent.click(falseRadio); + await userEvent.click(falseRadio); expect(defaultProps.onChange).toHaveBeenCalledWith('content_reviewers', { ...(defaultProps.value as ContentFlaggingReviewerSetting), @@ -140,11 +135,11 @@ describe('ContentFlaggingContentReviewers', () => { }); }); - it('handles system admin reviewer checkbox change', () => { + it('handles system admin reviewer checkbox change', async () => { renderWithContext(); const systemAdminCheckbox = screen.getByRole('checkbox', {name: /system administrators/i}); - fireEvent.click(systemAdminCheckbox); + await userEvent.click(systemAdminCheckbox); expect(defaultProps.onChange).toHaveBeenCalledWith('content_reviewers', { ...(defaultProps.value as ContentFlaggingReviewerSetting), @@ -152,11 +147,11 @@ describe('ContentFlaggingContentReviewers', () => { }); }); - it('handles team admin reviewer checkbox change', () => { + it('handles team admin reviewer checkbox change', async () => { renderWithContext(); const teamAdminCheckbox = screen.getByRole('checkbox', {name: /team administrators/i}); - fireEvent.click(teamAdminCheckbox); + await userEvent.click(teamAdminCheckbox); expect(defaultProps.onChange).toHaveBeenCalledWith('content_reviewers', { ...(defaultProps.value as ContentFlaggingReviewerSetting), @@ -164,11 +159,11 @@ describe('ContentFlaggingContentReviewers', () => { }); }); - it('handles common reviewers change', () => { + it('handles common reviewers change', async () => { renderWithContext(); const changeUsersButton = screen.getByTestId('content_reviewers_common_reviewers-change-users'); - fireEvent.click(changeUsersButton); + await userEvent.click(changeUsersButton); expect(defaultProps.onChange).toHaveBeenCalledWith('content_reviewers', { ...(defaultProps.value as ContentFlaggingReviewerSetting), @@ -176,7 +171,7 @@ describe('ContentFlaggingContentReviewers', () => { }); }); - it('handles team reviewer settings change', () => { + it('handles team reviewer settings change', async () => { const props = { ...defaultProps, value: { @@ -188,7 +183,7 @@ describe('ContentFlaggingContentReviewers', () => { renderWithContext(); const changeTeamReviewersButton = screen.getByTestId('team-reviewers-change'); - fireEvent.click(changeTeamReviewersButton); + await userEvent.click(changeTeamReviewersButton); expect(defaultProps.onChange).toHaveBeenCalledWith('content_reviewers', { ...props.value, diff --git a/webapp/channels/src/components/admin_console/content_flagging/content_reviewers/team_option.test.tsx b/webapp/channels/src/components/admin_console/content_flagging/content_reviewers/team_option.test.tsx index bf0090eebfe..66de25bfdee 100644 --- a/webapp/channels/src/components/admin_console/content_flagging/content_reviewers/team_option.test.tsx +++ b/webapp/channels/src/components/admin_console/content_flagging/content_reviewers/team_option.test.tsx @@ -81,7 +81,6 @@ describe('TeamOptionComponent', () => { } as unknown as OptionProps, true>; beforeEach(() => { - jest.clearAllMocks(); (Utils.imageURLForTeam as jest.Mock).mockReturnValue('http://example.com/team-icon.png'); }); diff --git a/webapp/channels/src/components/admin_console/content_flagging/content_reviewers/team_reviewers_section/team_reviewers_section.test.tsx b/webapp/channels/src/components/admin_console/content_flagging/content_reviewers/team_reviewers_section/team_reviewers_section.test.tsx index 80b1633b6bc..b70c2957a8d 100644 --- a/webapp/channels/src/components/admin_console/content_flagging/content_reviewers/team_reviewers_section/team_reviewers_section.test.tsx +++ b/webapp/channels/src/components/admin_console/content_flagging/content_reviewers/team_reviewers_section/team_reviewers_section.test.tsx @@ -1,7 +1,6 @@ // Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved. // See LICENSE.txt for license information. -import {screen, fireEvent, waitFor} from '@testing-library/react'; import React from 'react'; import type {TeamReviewerSetting} from '@mattermost/types/config'; @@ -9,7 +8,7 @@ import type {Team} from '@mattermost/types/teams'; import {searchTeams} from 'mattermost-redux/actions/teams'; -import {renderWithContext} from 'tests/react_testing_utils'; +import {renderWithContext, screen, userEvent, waitFor} from 'tests/react_testing_utils'; import {TestHelper} from 'utils/test_helper'; import TeamReviewersSection from './team_reviewers_section'; @@ -40,8 +39,6 @@ describe('TeamReviewersSection', () => { }; beforeEach(() => { - jest.clearAllMocks(); - mockSearchTeams.mockReturnValue(async () => ({ data: { teams: mockTeams, @@ -88,7 +85,8 @@ describe('TeamReviewersSection', () => { }); const searchInput = screen.getByRole('textbox'); - fireEvent.change(searchInput, {target: {value: 'search term'}}); + await userEvent.clear(searchInput); + await userEvent.type(searchInput, 'search term'); await waitFor(() => { expect(mockSearchTeams).toHaveBeenCalledWith('search term', {page: 0, per_page: 10}); @@ -120,7 +118,7 @@ describe('TeamReviewersSection', () => { }); const nextButton = screen.getByRole('button', {name: /next/i}); - fireEvent.click(nextButton); + await userEvent.click(nextButton); await waitFor(() => { expect(mockSearchTeams).toHaveBeenCalledWith('', {page: 1, per_page: 10}); @@ -153,7 +151,7 @@ describe('TeamReviewersSection', () => { }); const nextButton = screen.getByRole('button', {name: /Next page/i}); - fireEvent.click(nextButton); + await userEvent.click(nextButton); await waitFor(() => { expect(mockSearchTeams).toHaveBeenCalledWith('', {page: 1, per_page: 10}); @@ -161,7 +159,7 @@ describe('TeamReviewersSection', () => { // Then go back to previous page const prevButton = screen.getByRole('button', {name: /Previous page/i}); - fireEvent.click(prevButton); + await userEvent.click(prevButton); await waitFor(() => { expect(mockSearchTeams).toHaveBeenCalledWith('', {page: 0, per_page: 10}); @@ -192,7 +190,7 @@ describe('TeamReviewersSection', () => { }); const toggle = screen.getAllByRole('button', {name: /enable or disable content reviewers for this team/i})[0]; - fireEvent.click(toggle); + await userEvent.click(toggle); expect(onChange).toHaveBeenCalledWith({ team1: { @@ -233,7 +231,7 @@ describe('TeamReviewersSection', () => { }); const toggle = screen.getAllByRole('button', {name: /enable or disable content reviewers for this team/i})[0]; - fireEvent.click(toggle); + await userEvent.click(toggle); expect(onChange).toHaveBeenCalledWith({ team1: { @@ -327,7 +325,7 @@ describe('TeamReviewersSection', () => { // Go to next page const nextButton = screen.getByRole('button', {name: /next/i}); - fireEvent.click(nextButton); + await userEvent.click(nextButton); await waitFor(() => { expect(mockSearchTeams).toHaveBeenCalledWith('', {page: 1, per_page: 10}); @@ -335,7 +333,8 @@ describe('TeamReviewersSection', () => { // Search - should reset to page 0 const searchInput = screen.getByRole('textbox'); - fireEvent.change(searchInput, {target: {value: 'search'}}); + await userEvent.clear(searchInput); + await userEvent.type(searchInput, 'search'); await waitFor(() => { expect(mockSearchTeams).toHaveBeenCalledWith('search', {page: 0, per_page: 10}); @@ -388,7 +387,7 @@ describe('TeamReviewersSection', () => { const toggle = screen.getAllByRole('button', {name: /enable or disable content reviewers for this team/i})[0]; // First click - enable - fireEvent.click(toggle); + await userEvent.click(toggle); expect(onChange).toHaveBeenCalledWith({ team1: { Enabled: true, @@ -416,7 +415,7 @@ describe('TeamReviewersSection', () => { const updatedToggle = screen.getAllByRole('button', {name: /enable or disable content reviewers for this team/i})[0]; // Second click - disable - fireEvent.click(updatedToggle); + await userEvent.click(updatedToggle); expect(onChange).toHaveBeenCalledWith({ team1: { Enabled: true, @@ -454,7 +453,7 @@ describe('TeamReviewersSection', () => { }); const disableForAllButton = screen.getByTestId('disableForAllTeamsButton'); - fireEvent.click(disableForAllButton); + await userEvent.click(disableForAllButton); expect(onChange).toHaveBeenCalledWith({ team1: { diff --git a/webapp/channels/src/components/admin_console/content_flagging/notificatin_settings/notification_settings.test.tsx b/webapp/channels/src/components/admin_console/content_flagging/notificatin_settings/notification_settings.test.tsx index f183574d272..f9419a04752 100644 --- a/webapp/channels/src/components/admin_console/content_flagging/notificatin_settings/notification_settings.test.tsx +++ b/webapp/channels/src/components/admin_console/content_flagging/notificatin_settings/notification_settings.test.tsx @@ -1,12 +1,13 @@ // Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved. // See LICENSE.txt for license information. -import {render, screen, fireEvent} from '@testing-library/react'; import React from 'react'; import {IntlProvider} from 'react-intl'; import type {ContentFlaggingNotificationSettings} from '@mattermost/types/config'; +import {fireEvent, render, screen, userEvent} from 'tests/react_testing_utils'; + import ContentFlaggingNotificationSettingsSection from './notification_settings'; const renderWithContext = (component: React.ReactElement) => { @@ -31,10 +32,6 @@ describe('ContentFlaggingNotificationSettingsSection', () => { onChange: jest.fn(), }; - beforeEach(() => { - jest.clearAllMocks(); - }); - test('should render section title and description', () => { renderWithContext(); @@ -80,11 +77,11 @@ describe('ContentFlaggingNotificationSettingsSection', () => { expect(screen.getByTestId('dismissed_reporter')).not.toBeChecked(); }); - test('should handle checkbox change when adding a target', () => { + test('should handle checkbox change when adding a target', async () => { renderWithContext(); const flaggedAuthorsCheckbox = screen.getByTestId('flagged_author'); - fireEvent.click(flaggedAuthorsCheckbox); + await userEvent.click(flaggedAuthorsCheckbox); expect(defaultProps.onChange).toHaveBeenCalledWith('test-id', { EventTargetMapping: { @@ -96,11 +93,11 @@ describe('ContentFlaggingNotificationSettingsSection', () => { }); }); - test('should handle checkbox change when removing a target', () => { + test('should handle checkbox change when removing a target', async () => { renderWithContext(); const removedAuthorCheckbox = screen.getByTestId('removed_author'); - fireEvent.click(removedAuthorCheckbox); + await userEvent.click(removedAuthorCheckbox); expect(defaultProps.onChange).toHaveBeenCalledWith('test-id', { EventTargetMapping: { @@ -140,6 +137,7 @@ describe('ContentFlaggingNotificationSettingsSection', () => { renderWithContext(); + // Use fireEvent.click to test defensive coding in handler (checkbox is disabled) const flaggedReviewersCheckbox = screen.getByTestId('flagged_reviewers'); fireEvent.click(flaggedReviewersCheckbox); @@ -153,7 +151,7 @@ describe('ContentFlaggingNotificationSettingsSection', () => { }); }); - test('should initialize action array if not present', () => { + test('should initialize action array if not present', async () => { const propsWithPartialMapping = { ...defaultProps, value: { @@ -168,7 +166,7 @@ describe('ContentFlaggingNotificationSettingsSection', () => { renderWithContext(); const assignedReviewersCheckbox = screen.getByTestId('assigned_reviewers'); - fireEvent.click(assignedReviewersCheckbox); + await userEvent.click(assignedReviewersCheckbox); expect(defaultProps.onChange).toHaveBeenCalledWith('test-id', { EventTargetMapping: { @@ -181,6 +179,7 @@ describe('ContentFlaggingNotificationSettingsSection', () => { test('should not add duplicate targets', () => { renderWithContext(); + // Use fireEvent.click to test defensive coding in handler (checkbox is disabled) // Try to add 'reviewers' to flagged again (it's already there) const flaggedReviewersCheckbox = screen.getByTestId('flagged_reviewers'); fireEvent.click(flaggedReviewersCheckbox); @@ -196,16 +195,16 @@ describe('ContentFlaggingNotificationSettingsSection', () => { }); }); - test('should handle multiple checkbox changes correctly', () => { + test('should handle multiple checkbox changes correctly', async () => { renderWithContext(); // First change: add author to flagged const flaggedAuthorsCheckbox = screen.getByTestId('flagged_author'); - fireEvent.click(flaggedAuthorsCheckbox); + await userEvent.click(flaggedAuthorsCheckbox); // Second change: add reporter to removed const removedReporterCheckbox = screen.getByTestId('removed_reporter'); - fireEvent.click(removedReporterCheckbox); + await userEvent.click(removedReporterCheckbox); expect(defaultProps.onChange).toHaveBeenCalledTimes(2); @@ -228,13 +227,13 @@ describe('ContentFlaggingNotificationSettingsSection', () => { }); }); - test('should handle unchecking and rechecking the same checkbox', () => { + test('should handle unchecking and rechecking the same checkbox', async () => { renderWithContext(); const removedAuthorCheckbox = screen.getByTestId('removed_author'); // First click: uncheck (remove author from removed) - fireEvent.click(removedAuthorCheckbox); + await userEvent.click(removedAuthorCheckbox); expect(defaultProps.onChange).toHaveBeenNthCalledWith(1, 'test-id', { EventTargetMapping: { flagged: ['reviewers'], @@ -245,7 +244,7 @@ describe('ContentFlaggingNotificationSettingsSection', () => { }); // Second click: check again (add author back to removed) - fireEvent.click(removedAuthorCheckbox); + await userEvent.click(removedAuthorCheckbox); expect(defaultProps.onChange).toHaveBeenNthCalledWith(2, 'test-id', { EventTargetMapping: { flagged: ['reviewers'], diff --git a/webapp/channels/src/components/admin_console/content_flagging/user_multiselector/user_profile_pill.test.tsx b/webapp/channels/src/components/admin_console/content_flagging/user_multiselector/user_profile_pill.test.tsx index 9a14a7f7ec0..c3cbbf26ec2 100644 --- a/webapp/channels/src/components/admin_console/content_flagging/user_multiselector/user_profile_pill.test.tsx +++ b/webapp/channels/src/components/admin_console/content_flagging/user_multiselector/user_profile_pill.test.tsx @@ -8,7 +8,7 @@ import type {Group} from '@mattermost/types/groups'; import type {Team} from '@mattermost/types/teams'; import type {UserProfile} from '@mattermost/types/users'; -import {fireEvent, renderWithContext} from 'tests/react_testing_utils'; +import {renderWithContext, userEvent} from 'tests/react_testing_utils'; import {TestHelper} from 'utils/test_helper'; import type {AutocompleteOptionType} from './user_multiselector'; @@ -66,10 +66,6 @@ describe('components/admin_console/content_flagging/user_multiselector/UserProfi }, }; - beforeEach(() => { - jest.clearAllMocks(); - }); - test('should render user profile pill with avatar and display name', () => { const {container} = renderWithContext( , @@ -110,7 +106,7 @@ describe('components/admin_console/content_flagging/user_multiselector/UserProfi expect(removeComponent).toBeInTheDocument(); }); - test('should call onClick when remove button is clicked', () => { + test('should call onClick when remove button is clicked', async () => { const mockOnClick = jest.fn(); const propsWithClick = { ...baseProps, @@ -128,7 +124,7 @@ describe('components/admin_console/content_flagging/user_multiselector/UserProfi expect(removeComponent).toBeInTheDocument(); expect(removeComponent).toBeDefined(); - fireEvent.click(removeComponent!); + await userEvent.click(removeComponent!); expect(mockOnClick).toHaveBeenCalledTimes(1); }); diff --git a/webapp/channels/src/components/admin_console/custom_enable_disable_guest_accounts_setting.test.tsx b/webapp/channels/src/components/admin_console/custom_enable_disable_guest_accounts_setting.test.tsx index 4225627a7d0..4fcc255ab3d 100644 --- a/webapp/channels/src/components/admin_console/custom_enable_disable_guest_accounts_setting.test.tsx +++ b/webapp/channels/src/components/admin_console/custom_enable_disable_guest_accounts_setting.test.tsx @@ -1,11 +1,10 @@ // Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved. // See LICENSE.txt for license information. -import {fireEvent, screen} from '@testing-library/react'; import React from 'react'; import {FormattedMessage} from 'react-intl'; -import {renderWithContext} from 'tests/react_testing_utils'; +import {renderWithContext, screen, userEvent} from 'tests/react_testing_utils'; import CustomEnableDisableGuestAccountsSetting from './custom_enable_disable_guest_accounts_setting'; @@ -50,7 +49,7 @@ describe('components/AdminConsole/CustomEnableDisableGuestAccountsSetting', () = }); describe('handleChange', () => { - test('should enable without show confirmation modal or warning', () => { + test('should enable without show confirmation modal or warning', async () => { const props = { showConfirm: true, onChange: jest.fn(), @@ -64,12 +63,12 @@ describe('components/AdminConsole/CustomEnableDisableGuestAccountsSetting', () = ); const trueRadio = screen.getByTestId('MySettingtrue'); - fireEvent.click(trueRadio); + await userEvent.click(trueRadio); expect(props.onChange).toHaveBeenCalledWith(baseProps.id, true, false, false, ''); }); - test('should show confirmation modal and warning when disabling', () => { + test('should show confirmation modal and warning when disabling', async () => { const props = { value: true, showConfirm: true, @@ -84,12 +83,12 @@ describe('components/AdminConsole/CustomEnableDisableGuestAccountsSetting', () = ); const falseRadio = screen.getByTestId('MySettingfalse'); - fireEvent.click(falseRadio); + await userEvent.click(falseRadio); expect(props.onChange).toHaveBeenCalledWith(baseProps.id, false, true, false, warningMessage); }); - test('should call onChange with doSubmit = true when confirm is true', () => { + test('should call onChange with doSubmit = true when confirm is true', async () => { const props = { ...baseProps, onChange: jest.fn(), @@ -103,10 +102,10 @@ describe('components/AdminConsole/CustomEnableDisableGuestAccountsSetting', () = ); const falseRadio = screen.getByTestId('MySettingfalse'); - fireEvent.click(falseRadio); + await userEvent.click(falseRadio); const confirmButton = screen.getByText('Save and Disable Guest Access'); - fireEvent.click(confirmButton); + await userEvent.click(confirmButton); expect(props.onChange).toHaveBeenCalledWith(baseProps.id, false, true, true, warningMessage); }); diff --git a/webapp/channels/src/components/admin_console/custom_profile_attributes/custom_profile_attributes.test.tsx b/webapp/channels/src/components/admin_console/custom_profile_attributes/custom_profile_attributes.test.tsx index fae291d43a4..d6217a1a581 100644 --- a/webapp/channels/src/components/admin_console/custom_profile_attributes/custom_profile_attributes.test.tsx +++ b/webapp/channels/src/components/admin_console/custom_profile_attributes/custom_profile_attributes.test.tsx @@ -1,14 +1,13 @@ // Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved. // See LICENSE.txt for license information. -import {screen, fireEvent} from '@testing-library/react'; import React from 'react'; import type {UserPropertyField, UserPropertyFieldGroupID, UserPropertyFieldType} from '@mattermost/types/properties'; import {Client4} from 'mattermost-redux/client'; -import {act, renderWithContext} from 'tests/react_testing_utils'; +import {act, renderWithContext, screen, userEvent} from 'tests/react_testing_utils'; import CustomProfileAttributes from './custom_profile_attributes'; @@ -56,10 +55,6 @@ describe('components/admin_console/custom_profile_attributes/CustomProfileAttrib const initialState = createInitialState({attr1, attr2}); - beforeEach(() => { - jest.clearAllMocks(); - }); - test('should not render anything when no attributes exist', () => { const {container} = renderWithContext( , @@ -100,9 +95,11 @@ describe('components/admin_console/custom_profile_attributes/CustomProfileAttrib ); const input = await screen.findByDisplayValue('department'); - fireEvent.change(input, {target: {value: 'new-department'}}); + await userEvent.clear(input); + await userEvent.type(input, 'new-department'); - const saveAction = baseProps.registerSaveAction.mock.calls[1][0]; + const calls = baseProps.registerSaveAction.mock.calls; + const saveAction = calls[calls.length - 1][0]; await act(async () => { await saveAction(); }); @@ -158,9 +155,11 @@ describe('components/admin_console/custom_profile_attributes/CustomProfileAttrib ); const input = await screen.findByDisplayValue('title'); - fireEvent.change(input, {target: {value: 'new-title'}}); + await userEvent.clear(input); + await userEvent.type(input, 'new-title'); - const saveAction = baseProps.registerSaveAction.mock.calls[1][0]; + const calls = baseProps.registerSaveAction.mock.calls; + const saveAction = calls[calls.length - 1][0]; await act(async () => { await saveAction(); }); @@ -199,9 +198,11 @@ describe('components/admin_console/custom_profile_attributes/CustomProfileAttrib ); const input = await screen.findByDisplayValue('department'); - fireEvent.change(input, {target: {value: 'new-department'}}); + await userEvent.clear(input); + await userEvent.type(input, 'new-department'); - const saveAction = baseProps.registerSaveAction.mock.calls[1][0]; + const calls = baseProps.registerSaveAction.mock.calls; + const saveAction = calls[calls.length - 1][0]; // Verify the save action catches and returns the error await expect(saveAction()).resolves.toEqual( diff --git a/webapp/channels/src/components/admin_console/ip_filtering/add_edit_ip_filter_modal.test.tsx b/webapp/channels/src/components/admin_console/ip_filtering/add_edit_ip_filter_modal.test.tsx index 4ee51e6bb65..5706017646f 100644 --- a/webapp/channels/src/components/admin_console/ip_filtering/add_edit_ip_filter_modal.test.tsx +++ b/webapp/channels/src/components/admin_console/ip_filtering/add_edit_ip_filter_modal.test.tsx @@ -1,12 +1,11 @@ // Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved. // See LICENSE.txt for license information. -import {fireEvent, waitFor} from '@testing-library/react'; import React from 'react'; import type {AllowedIPRange} from '@mattermost/types/config'; -import {renderWithContext} from 'tests/react_testing_utils'; +import {fireEvent, renderWithContext, userEvent, waitFor} from 'tests/react_testing_utils'; import IPFilteringAddOrEditModal from './add_edit_ip_filter_modal'; @@ -73,9 +72,11 @@ describe('IPFilteringAddOrEditModal', () => { />, ); - fireEvent.change(getByLabelText('Enter a name for this rule'), {target: {value: 'Test IP Filter 2'}}); - fireEvent.change(getByLabelText('Enter IP Range'), {target: {value: '10.0.0.0/8'}}); - fireEvent.click(getByTestId('save-add-edit-button')); + await userEvent.clear(getByLabelText('Enter a name for this rule')); + await userEvent.type(getByLabelText('Enter a name for this rule'), 'Test IP Filter 2'); + await userEvent.clear(getByLabelText('Enter IP Range')); + await userEvent.type(getByLabelText('Enter IP Range'), '10.0.0.0/8'); + await userEvent.click(getByTestId('save-add-edit-button')); await waitFor(() => { expect(onSave).toHaveBeenCalledWith({ @@ -96,9 +97,11 @@ describe('IPFilteringAddOrEditModal', () => { />, ); - fireEvent.change(getByLabelText('Enter a name for this rule'), {target: {value: 'Test IP Filter 2'}}); - fireEvent.change(getByLabelText('Enter IP Range'), {target: {value: '10.0.0.0/8'}}); - fireEvent.click(getByTestId('save-add-edit-button')); + await userEvent.clear(getByLabelText('Enter a name for this rule')); + await userEvent.type(getByLabelText('Enter a name for this rule'), 'Test IP Filter 2'); + await userEvent.clear(getByLabelText('Enter IP Range')); + await userEvent.type(getByLabelText('Enter IP Range'), '10.0.0.0/8'); + await userEvent.click(getByTestId('save-add-edit-button')); await waitFor(() => { expect(onSave).toHaveBeenCalledWith({ @@ -118,9 +121,12 @@ describe('IPFilteringAddOrEditModal', () => { />, ); - fireEvent.change(getByLabelText('Enter IP Range'), {target: {value: 'invalid-cidr'}}); + await userEvent.clear(getByLabelText('Enter IP Range')); + await userEvent.type(getByLabelText('Enter IP Range'), 'invalid-cidr'); + + // Trigger validation on blur - fireEvent used because userEvent doesn't have direct focus/blur methods fireEvent.blur(getByLabelText('Enter IP Range')); - fireEvent.click(getByTestId('save-add-edit-button')); + await userEvent.click(getByTestId('save-add-edit-button')); await waitFor(() => { expect(getByText('Invalid CIDR address range')).toBeInTheDocument(); @@ -129,14 +135,16 @@ describe('IPFilteringAddOrEditModal', () => { }); }); - test('disables the Save button when an invalid CIDR is entered', () => { + test('disables the Save button when an invalid CIDR is entered', async () => { const {getByLabelText, getByTestId} = renderWithContext( , ); - fireEvent.change(getByLabelText('Enter IP Range'), {target: {value: 'invalid-cidr'}}); + await userEvent.clear(getByLabelText('Enter IP Range')); + await userEvent.type(getByLabelText('Enter IP Range'), 'invalid-cidr'); + fireEvent.blur(getByLabelText('Enter IP Range')); expect(getByTestId('save-add-edit-button')).toBeDisabled(); diff --git a/webapp/channels/src/components/admin_console/ip_filtering/delete_confirmation.test.tsx b/webapp/channels/src/components/admin_console/ip_filtering/delete_confirmation.test.tsx index ee1fe18c9a3..44888f54099 100644 --- a/webapp/channels/src/components/admin_console/ip_filtering/delete_confirmation.test.tsx +++ b/webapp/channels/src/components/admin_console/ip_filtering/delete_confirmation.test.tsx @@ -1,9 +1,10 @@ // Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved. // See LICENSE.txt for license information. -import {render, fireEvent, waitFor} from '@testing-library/react'; import React from 'react'; +import {render, userEvent, waitFor} from 'tests/react_testing_utils'; + import DeleteConfirmationModal from './delete_confirmation'; describe('DeleteConfirmationModal', () => { @@ -41,14 +42,14 @@ describe('DeleteConfirmationModal', () => { expect(getByText('Test IP Filter')).toBeInTheDocument(); }); - test('calls the onClose function when the Cancel button is clicked', () => { + test('calls the onClose function when the Cancel button is clicked', async () => { const {getByText} = render( , ); - fireEvent.click(getByText('Cancel')); + await userEvent.click(getByText('Cancel')); expect(onExited).toHaveBeenCalled(); expect(onConfirm).not.toHaveBeenCalled(); @@ -61,7 +62,7 @@ describe('DeleteConfirmationModal', () => { />, ); - fireEvent.click(getByText('Delete filter')); + await userEvent.click(getByText('Delete filter')); await waitFor(() => { expect(onConfirm).toHaveBeenCalledWith(filterToDelete); diff --git a/webapp/channels/src/components/admin_console/ip_filtering/edit_section/edit_section.test.tsx b/webapp/channels/src/components/admin_console/ip_filtering/edit_section/edit_section.test.tsx index eabd7af23a5..3218183db15 100644 --- a/webapp/channels/src/components/admin_console/ip_filtering/edit_section/edit_section.test.tsx +++ b/webapp/channels/src/components/admin_console/ip_filtering/edit_section/edit_section.test.tsx @@ -1,12 +1,11 @@ // Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved. // See LICENSE.txt for license information. -import {screen, fireEvent} from '@testing-library/react'; import React from 'react'; import type {AllowedIPRange} from '@mattermost/types/config'; -import {renderWithContext} from 'tests/react_testing_utils'; +import {renderWithContext, screen, userEvent} from 'tests/react_testing_utils'; import EditSection from './'; @@ -49,28 +48,28 @@ describe('EditSection', () => { expect(screen.getByText('192.168.0.0/24')).toBeInTheDocument(); }); - test('clicking the Add Filter button calls setShowAddModal', () => { + test('clicking the Add Filter button calls setShowAddModal', async () => { renderWithContext( , ); - fireEvent.click(screen.getByText('Add Filter')); + await userEvent.click(screen.getByText('Add Filter')); expect(setShowAddModal).toHaveBeenCalledTimes(1); expect(setShowAddModal).toHaveBeenCalledWith(true); }); - test('clicking the Edit button calls setEditFilter', () => { + test('clicking the Edit button calls setEditFilter', async () => { renderWithContext( , ); - fireEvent.mouseEnter(screen.getByText('Test Filter')); - fireEvent.click(screen.getByRole('button', { + await userEvent.hover(screen.getByText('Test Filter')); + await userEvent.click(screen.getByRole('button', { name: /Edit/i, })); @@ -78,15 +77,15 @@ describe('EditSection', () => { expect(setEditFilter).toHaveBeenCalledWith(ipFilters[0]); }); - test('clicking the Delete button calls handleConfirmDeleteFilter', () => { + test('clicking the Delete button calls handleConfirmDeleteFilter', async () => { renderWithContext( , ); - fireEvent.mouseEnter(screen.getByText('Test Filter')); - fireEvent.click(screen.getByRole('button', { + await userEvent.hover(screen.getByText('Test Filter')); + await userEvent.click(screen.getByRole('button', { name: /Delete/i, })); diff --git a/webapp/channels/src/components/admin_console/ip_filtering/enable_section.test.tsx b/webapp/channels/src/components/admin_console/ip_filtering/enable_section.test.tsx index 8c68e0e16fb..7e30072751c 100644 --- a/webapp/channels/src/components/admin_console/ip_filtering/enable_section.test.tsx +++ b/webapp/channels/src/components/admin_console/ip_filtering/enable_section.test.tsx @@ -1,10 +1,9 @@ // Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved. // See LICENSE.txt for license information. -import {screen, fireEvent} from '@testing-library/react'; import React from 'react'; -import {renderWithContext} from 'tests/react_testing_utils'; +import {renderWithContext, screen, userEvent} from 'tests/react_testing_utils'; import EnableSectionContent from './enable_section'; jest.mock('components/external_link', () => { @@ -35,14 +34,14 @@ describe('EnableSectionContent', () => { expect(screen.getByRole('button', {pressed: true})).toBeInTheDocument(); }); - test('clicking the toggle calls setFilterToggle', () => { + test('clicking the toggle calls setFilterToggle', async () => { renderWithContext( , ); - fireEvent.click(screen.getByTestId('filterToggle-button')); + await userEvent.click(screen.getByTestId('filterToggle-button')); expect(setFilterToggle).toHaveBeenCalledTimes(1); expect(setFilterToggle).toHaveBeenCalledWith(false); diff --git a/webapp/channels/src/components/admin_console/ip_filtering/ip_filtering.test.tsx b/webapp/channels/src/components/admin_console/ip_filtering/ip_filtering.test.tsx index e33c26bddb3..d29ab5530a5 100644 --- a/webapp/channels/src/components/admin_console/ip_filtering/ip_filtering.test.tsx +++ b/webapp/channels/src/components/admin_console/ip_filtering/ip_filtering.test.tsx @@ -1,7 +1,6 @@ // Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved. // See LICENSE.txt for license information. -import {render, fireEvent, waitFor, screen} from '@testing-library/react'; import React from 'react'; import {IntlProvider} from 'react-intl'; import {Provider} from 'react-redux'; @@ -16,6 +15,8 @@ import configureStore from 'store'; import ModalController from 'components/modal_controller'; +import {fireEvent, render, screen, userEvent, waitFor} from 'tests/react_testing_utils'; + import IPFiltering from './index'; jest.mock('mattermost-redux/client'); @@ -99,7 +100,7 @@ describe('IPFiltering', () => { expect(screen.getByRole('button', {pressed: true})).toBeInTheDocument(); }); - fireEvent.click(screen.getByTestId('filterToggle-button')); + await userEvent.click(screen.getByTestId('filterToggle-button')); await waitFor(() => { expect(screen.getByRole('button', {pressed: false})).toBeInTheDocument(); @@ -113,15 +114,17 @@ describe('IPFiltering', () => { expect(getByText('Add Filter')).toBeInTheDocument(); }); - fireEvent.click(getByText('Add Filter')); + await userEvent.click(getByText('Add Filter')); const descriptionInput = getByLabelText('Enter a name for this rule'); const cidrInput = getByLabelText('Enter IP Range'); const saveButton = screen.getByTestId('save-add-edit-button'); - fireEvent.change(cidrInput, {target: {value: '192.168.0.0/16'}}); - fireEvent.change(descriptionInput, {target: {value: 'Test IP Filter 2'}}); - fireEvent.click(saveButton); + await userEvent.clear(cidrInput); + await userEvent.type(cidrInput, '192.168.0.0/16'); + await userEvent.clear(descriptionInput); + await userEvent.type(descriptionInput, 'Test IP Filter 2'); + await userEvent.click(saveButton); await waitFor(() => { expect(getByText('Test IP Filter 2')).toBeInTheDocument(); @@ -136,8 +139,8 @@ describe('IPFiltering', () => { expect(getByText('Test IP Filter')).toBeInTheDocument(); }); - fireEvent.mouseEnter(screen.getByText('Test IP Filter')); - fireEvent.click(screen.getByRole('button', { + await userEvent.hover(screen.getByText('Test IP Filter')); + await userEvent.click(screen.getByRole('button', { name: /Edit/i, })); @@ -145,9 +148,11 @@ describe('IPFiltering', () => { const cidrInput = getByLabelText('Enter IP Range'); const saveButton = screen.getByTestId('save-add-edit-button'); - fireEvent.change(cidrInput, {target: {value: '192.168.0.0/16'}}); - fireEvent.change(descriptionInput, {target: {value: 'zzzzzfilter'}}); - fireEvent.click(saveButton); + await userEvent.clear(cidrInput); + await userEvent.type(cidrInput, '192.168.0.0/16'); + await userEvent.clear(descriptionInput); + await userEvent.type(descriptionInput, 'zzzzzfilter'); + await userEvent.click(saveButton); await waitFor(() => { expect(getByText('zzzzzfilter')).toBeInTheDocument(); @@ -165,14 +170,14 @@ describe('IPFiltering', () => { expect(getByText('Test IP Filter')).toBeInTheDocument(); }); - fireEvent.mouseEnter(screen.getByText('Test IP Filter')); - fireEvent.click(screen.getByRole('button', { + await userEvent.hover(screen.getByText('Test IP Filter')); + await userEvent.click(screen.getByRole('button', { name: /Delete/i, })); const confirmButton = getByText('Delete filter'); - fireEvent.click(confirmButton); + await userEvent.click(confirmButton); await waitFor(() => { expect(queryByText('Test IP Filter')).not.toBeInTheDocument(); @@ -187,7 +192,7 @@ describe('IPFiltering', () => { expect(screen.getByRole('button', {pressed: true})).toBeInTheDocument(); }); - fireEvent.click(screen.getByTestId('filterToggle-button')); + await userEvent.click(screen.getByTestId('filterToggle-button')); await waitFor(() => { expect(screen.getByRole('button', {pressed: false})).toBeInTheDocument(); @@ -197,8 +202,8 @@ describe('IPFiltering', () => { expect(queryByText('Test IP Filter')).not.toBeInTheDocument(); }); - fireEvent.click(getByText('Save')); - fireEvent.click(screen.getByTestId('save-confirmation-button')); + await userEvent.click(getByText('Save')); + await userEvent.click(screen.getByTestId('save-confirmation-button')); await waitFor(() => { expect(applyIPFiltersMock).toHaveBeenCalledTimes(1); @@ -212,8 +217,8 @@ describe('IPFiltering', () => { expect(getByText('Test IP Filter')).toBeInTheDocument(); }); - fireEvent.mouseEnter(screen.getByText('Test IP Filter')); - fireEvent.click(screen.getByRole('button', { + await userEvent.hover(screen.getByText('Test IP Filter')); + await userEvent.click(screen.getByRole('button', { name: /Edit/i, })); @@ -221,9 +226,11 @@ describe('IPFiltering', () => { const cidrInput = getByLabelText('Enter IP Range'); const saveButton = screen.getByTestId('save-add-edit-button'); - fireEvent.change(cidrInput, {target: {value: '192.168.0.0/16'}}); - fireEvent.change(descriptionInput, {target: {value: 'zzzzzfilter'}}); - fireEvent.click(saveButton); + await userEvent.clear(cidrInput); + await userEvent.type(cidrInput, '192.168.0.0/16'); + await userEvent.clear(descriptionInput); + await userEvent.type(descriptionInput, 'zzzzzfilter'); + await userEvent.click(saveButton); await waitFor(() => { expect(getByText('zzzzzfilter')).toBeInTheDocument(); @@ -247,6 +254,7 @@ describe('IPFiltering', () => { expect(screen.getByRole('button', {pressed: true})).toBeInTheDocument(); }); + // Use fireEvent.click here because userEvent doesn't work well with fake timers fireEvent.click(screen.getByTestId('filterToggle-button')); await waitFor(() => { diff --git a/webapp/channels/src/components/admin_console/ip_filtering/save_confirmation_modal.test.tsx b/webapp/channels/src/components/admin_console/ip_filtering/save_confirmation_modal.test.tsx index 4ba1c1ae966..056cea56a7f 100644 --- a/webapp/channels/src/components/admin_console/ip_filtering/save_confirmation_modal.test.tsx +++ b/webapp/channels/src/components/admin_console/ip_filtering/save_confirmation_modal.test.tsx @@ -1,10 +1,9 @@ // Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved. // See LICENSE.txt for license information. -import {fireEvent} from '@testing-library/react'; import React from 'react'; -import {renderWithContext} from 'tests/react_testing_utils'; +import {renderWithContext, userEvent} from 'tests/react_testing_utils'; import SaveConfirmationModal from './save_confirmation_modal'; @@ -51,26 +50,26 @@ describe('SaveConfirmationModal', () => { expect(getByText('Using the Customer Portal to restore access')).toBeInTheDocument(); }); - test('calls onClose when the cancel button is clicked', () => { + test('calls onClose when the cancel button is clicked', async () => { const {getByText} = renderWithContext( , ); - fireEvent.click(getByText('Cancel')); + await userEvent.click(getByText('Cancel')); expect(onExitedMock).toHaveBeenCalledTimes(1); }); - test('calls onConfirm when the confirm button is clicked', () => { + test('calls onConfirm when the confirm button is clicked', async () => { const {getByText} = renderWithContext( , ); - fireEvent.click(getByText(buttonText)); + await userEvent.click(getByText(buttonText)); expect(onConfirmMock).toHaveBeenCalledTimes(1); }); diff --git a/webapp/channels/src/components/admin_console/license_settings/renew_license_card/renew_license_card.test.tsx b/webapp/channels/src/components/admin_console/license_settings/renew_license_card/renew_license_card.test.tsx index 78d0313c69b..6a265c0ad6e 100644 --- a/webapp/channels/src/components/admin_console/license_settings/renew_license_card/renew_license_card.test.tsx +++ b/webapp/channels/src/components/admin_console/license_settings/renew_license_card/renew_license_card.test.tsx @@ -54,10 +54,6 @@ const actImmediate = (wrapper: ReactWrapper) => ); describe('components/RenewalLicenseCard', () => { - afterEach(() => { - jest.clearAllMocks(); - }); - const props = { license: { id: 'license_id', diff --git a/webapp/channels/src/components/admin_console/permission_schemes_settings/guest_permissions_tree/guest_permissions_tree.test.tsx b/webapp/channels/src/components/admin_console/permission_schemes_settings/guest_permissions_tree/guest_permissions_tree.test.tsx index 4b4f712778e..3b75fc3dc1b 100644 --- a/webapp/channels/src/components/admin_console/permission_schemes_settings/guest_permissions_tree/guest_permissions_tree.test.tsx +++ b/webapp/channels/src/components/admin_console/permission_schemes_settings/guest_permissions_tree/guest_permissions_tree.test.tsx @@ -34,10 +34,6 @@ describe('components/admin_console/permission_schemes_settings/guest_permissions }, }; - beforeEach(() => { - jest.clearAllMocks(); - }); - test('should render guest permissions tree with headers', () => { const {container} = renderWithContext(); diff --git a/webapp/channels/src/components/admin_console/reset_email_modal/reset_email_modal.test.tsx b/webapp/channels/src/components/admin_console/reset_email_modal/reset_email_modal.test.tsx index 0b3d4203bce..cad93647490 100644 --- a/webapp/channels/src/components/admin_console/reset_email_modal/reset_email_modal.test.tsx +++ b/webapp/channels/src/components/admin_console/reset_email_modal/reset_email_modal.test.tsx @@ -26,10 +26,6 @@ describe('components/admin_console/reset_email_modal/reset_email_modal.tsx', () onExited: jest.fn(), }; - beforeEach(() => { - jest.clearAllMocks(); - }); - test('should render modal with user name in title', () => { renderWithContext(); diff --git a/webapp/channels/src/components/admin_console/reset_password_modal/reset_password_modal.test.tsx b/webapp/channels/src/components/admin_console/reset_password_modal/reset_password_modal.test.tsx index 935994b57b7..87bd861aed4 100644 --- a/webapp/channels/src/components/admin_console/reset_password_modal/reset_password_modal.test.tsx +++ b/webapp/channels/src/components/admin_console/reset_password_modal/reset_password_modal.test.tsx @@ -49,10 +49,6 @@ describe('components/admin_console/reset_password_modal/reset_password_modal.tsx }, }; - beforeEach(() => { - jest.clearAllMocks(); - }); - test('should render modal with user name in title', () => { renderWithContext(); diff --git a/webapp/channels/src/components/admin_console/schema_admin_settings.test.tsx b/webapp/channels/src/components/admin_console/schema_admin_settings.test.tsx index 6746d2461a0..88003c0208d 100644 --- a/webapp/channels/src/components/admin_console/schema_admin_settings.test.tsx +++ b/webapp/channels/src/components/admin_console/schema_admin_settings.test.tsx @@ -1,14 +1,13 @@ // Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved. // See LICENSE.txt for license information. -import userEvent from '@testing-library/user-event'; import React from 'react'; import type {CloudState} from '@mattermost/types/cloud'; import type {AdminConfig, EnvironmentConfig} from '@mattermost/types/config'; import {defaultIntl} from 'tests/helpers/intl-test-helper'; -import {renderWithContext, screen, waitFor} from 'tests/react_testing_utils'; +import {renderWithContext, screen, userEvent, waitFor} from 'tests/react_testing_utils'; import SchemaAdminSettings, {SchemaAdminSettings as SchemaAdminSettingsClass} from './schema_admin_settings'; import type {ConsoleAccess, AdminDefinitionSubSectionSchema, AdminDefinitionSettingInput} from './types'; diff --git a/webapp/channels/src/components/admin_console/system_properties/user_properties_delete_modal.test.tsx b/webapp/channels/src/components/admin_console/system_properties/user_properties_delete_modal.test.tsx index b3af17e010b..dabccfb4d7c 100644 --- a/webapp/channels/src/components/admin_console/system_properties/user_properties_delete_modal.test.tsx +++ b/webapp/channels/src/components/admin_console/system_properties/user_properties_delete_modal.test.tsx @@ -1,14 +1,13 @@ // Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved. // See LICENSE.txt for license information. -import {screen, fireEvent} from '@testing-library/react'; import React from 'react'; import type {UserPropertyField} from '@mattermost/types/properties'; import {openModal} from 'actions/views/modals'; -import {renderWithContext, renderHookWithContext} from 'tests/react_testing_utils'; +import {renderHookWithContext, renderWithContext, screen, userEvent} from 'tests/react_testing_utils'; import {ModalIdentifiers} from 'utils/constants'; import RemoveUserPropertyFieldModal, {useUserPropertyFieldDelete} from './user_properties_delete_modal'; @@ -22,10 +21,6 @@ describe('RemoveUserPropertyFieldModal', () => { const onCancel = jest.fn(); const onExited = jest.fn(); - beforeEach(() => { - jest.clearAllMocks(); - }); - it('renders with the correct field name', () => { renderWithContext( { expect(screen.getByText('Delete')).toBeInTheDocument(); }); - it('calls onConfirm when confirm button is clicked', () => { + it('calls onConfirm when confirm button is clicked', async () => { renderWithContext( { />, ); - fireEvent.click(screen.getByText('Delete')); + await userEvent.click(screen.getByText('Delete')); expect(onConfirm).toHaveBeenCalledTimes(1); }); - it('calls onCancel when cancel button is clicked', () => { + it('calls onCancel when cancel button is clicked', async () => { renderWithContext( { />, ); - fireEvent.click(screen.getByText('Cancel')); + await userEvent.click(screen.getByText('Cancel')); expect(onCancel).toHaveBeenCalledTimes(1); }); }); diff --git a/webapp/channels/src/components/admin_console/system_properties/user_properties_dot_menu.test.tsx b/webapp/channels/src/components/admin_console/system_properties/user_properties_dot_menu.test.tsx index 4da526bbf6e..16802efb26a 100644 --- a/webapp/channels/src/components/admin_console/system_properties/user_properties_dot_menu.test.tsx +++ b/webapp/channels/src/components/admin_console/system_properties/user_properties_dot_menu.test.tsx @@ -1,15 +1,14 @@ // Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved. // See LICENSE.txt for license information. -import {fireEvent, screen, waitFor} from '@testing-library/react'; -import React from 'react'; import type {ComponentProps} from 'react'; +import React from 'react'; import type {UserPropertyField} from '@mattermost/types/properties'; import ModalController from 'components/modal_controller'; -import {renderWithContext} from 'tests/react_testing_utils'; +import {renderWithContext, screen, userEvent, waitFor} from 'tests/react_testing_utils'; import DotMenu from './user_properties_dot_menu'; @@ -33,10 +32,6 @@ describe('UserPropertyDotMenu', () => { const deleteField = jest.fn(); const createField = jest.fn(); - beforeEach(() => { - jest.clearAllMocks(); - }); - const renderComponent = (field: UserPropertyField = baseField, dotMenuProps?: Partial>) => { return renderWithContext( ( @@ -79,7 +74,7 @@ describe('UserPropertyDotMenu', () => { // Open the menu const menuButton = screen.getByTestId(`user-property-field_dotmenu-${baseField.id}`); - fireEvent.click(menuButton); + await userEvent.click(menuButton); // Verify the current visibility option is shown expect(screen.getByText('Hide when empty')).toBeInTheDocument(); @@ -90,15 +85,15 @@ describe('UserPropertyDotMenu', () => { // Open the menu const menuButton = screen.getByTestId(`user-property-field_dotmenu-${baseField.id}`); - fireEvent.click(menuButton); + await userEvent.click(menuButton); // Open the visibility submenu const visibilityMenuItem = screen.getByRole('menuitem', {name: /Visibility/}); - fireEvent.mouseOver(visibilityMenuItem); + await userEvent.hover(visibilityMenuItem); // Click "Always show" option const alwaysShowOption = screen.getByRole('menuitemradio', {name: /Always show/}); - fireEvent.click(alwaysShowOption); + await userEvent.click(alwaysShowOption); // Verify the field was updated with the new visibility expect(updateField).toHaveBeenCalledWith({ @@ -115,7 +110,7 @@ describe('UserPropertyDotMenu', () => { // Open the menu const menuButton = screen.getByTestId(`user-property-field_dotmenu-${baseField.id}`); - fireEvent.click(menuButton); + await userEvent.click(menuButton); // Verify both link options are shown expect(screen.getByText('Link attribute to AD/LDAP')).toBeInTheDocument(); @@ -132,7 +127,7 @@ describe('UserPropertyDotMenu', () => { // Open the menu const menuButton = screen.getByTestId(`user-property-field_dotmenu-${pendingField.id}`); - fireEvent.click(menuButton); + await userEvent.click(menuButton); // Verify both link options are not shown expect(screen.queryByText('Link attribute to AD/LDAP')).not.toBeInTheDocument(); @@ -152,7 +147,7 @@ describe('UserPropertyDotMenu', () => { // Open the menu const menuButton = screen.getByTestId(`user-property-field_dotmenu-${linkedField.id}`); - fireEvent.click(menuButton); + await userEvent.click(menuButton); // Verify the LDAP link text shows the edit option expect(screen.getByText('Edit LDAP link')).toBeInTheDocument(); @@ -171,7 +166,7 @@ describe('UserPropertyDotMenu', () => { // Open the menu const menuButton = screen.getByTestId(`user-property-field_dotmenu-${linkedField.id}`); - fireEvent.click(menuButton); + await userEvent.click(menuButton); // Verify the SAML link text shows the edit option expect(screen.getByText('Edit SAML link')).toBeInTheDocument(); @@ -182,10 +177,10 @@ describe('UserPropertyDotMenu', () => { // Open the menu const menuButton = screen.getByTestId(`user-property-field_dotmenu-${baseField.id}`); - fireEvent.click(menuButton); + await userEvent.click(menuButton); // Click the duplicate option - fireEvent.click(screen.getByText(/Duplicate attribute/)); + await userEvent.click(screen.getByText(/Duplicate attribute/)); // Wait for createField to be called await waitFor(() => { @@ -202,7 +197,7 @@ describe('UserPropertyDotMenu', () => { // Open the menu const menuButton = screen.getByTestId(`user-property-field_dotmenu-${baseField.id}`); - fireEvent.click(menuButton); + await userEvent.click(menuButton); // Verify duplicate option is not shown expect(screen.queryByText(/Duplicate attribute/)).not.toBeInTheDocument(); @@ -213,11 +208,11 @@ describe('UserPropertyDotMenu', () => { // Open the menu const menuButton = screen.getByTestId(`user-property-field_dotmenu-${baseField.id}`); - fireEvent.click(menuButton); + await userEvent.click(menuButton); // Click delete option const deleteOption = screen.getByRole('menuitem', {name: /Delete attribute/}); - fireEvent.click(deleteOption); + await userEvent.click(deleteOption); await waitFor(() => { // Verify the delete modal is shown @@ -226,7 +221,7 @@ describe('UserPropertyDotMenu', () => { // click delete confirm button const deleteConfirmButton = screen.getByRole('button', {name: /Delete/}); - fireEvent.click(deleteConfirmButton); + await userEvent.click(deleteConfirmButton); await waitFor(() => { // Verify deleteField was called @@ -245,11 +240,11 @@ describe('UserPropertyDotMenu', () => { // Open the menu const menuButton = screen.getByTestId(`user-property-field_dotmenu-${pendingField.id}`); - fireEvent.click(menuButton); + await userEvent.click(menuButton); // Click delete option const deleteOption = screen.getByRole('menuitem', {name: /Delete attribute/}); - fireEvent.click(deleteOption); + await userEvent.click(deleteOption); await waitFor(() => { // Verify deleteField was called diff --git a/webapp/channels/src/components/admin_console/system_properties/user_properties_table.test.tsx b/webapp/channels/src/components/admin_console/system_properties/user_properties_table.test.tsx index c1c6185484f..907d1008260 100644 --- a/webapp/channels/src/components/admin_console/system_properties/user_properties_table.test.tsx +++ b/webapp/channels/src/components/admin_console/system_properties/user_properties_table.test.tsx @@ -1,13 +1,12 @@ // Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved. // See LICENSE.txt for license information. -import {fireEvent, screen, waitFor} from '@testing-library/react'; import React from 'react'; import type {UserPropertyField} from '@mattermost/types/properties'; import {collectionFromArray} from '@mattermost/types/utilities'; -import {renderWithContext} from 'tests/react_testing_utils'; +import {fireEvent, renderWithContext, screen, userEvent, waitFor} from 'tests/react_testing_utils'; import {UserPropertiesTable} from './user_properties_table'; @@ -58,10 +57,6 @@ describe('UserPropertiesTable', () => { const deleteField = jest.fn(); const reorderField = jest.fn(); - beforeEach(() => { - jest.clearAllMocks(); - }); - const renderComponent = (fields = baseFields) => { const collection = collectionFromArray(fields); @@ -93,11 +88,14 @@ describe('UserPropertiesTable', () => { expect(screen.getByText('Select')).toBeInTheDocument(); }); - it('allows editing field names', () => { + it('allows editing field names', async () => { renderComponent(); const field1Input = screen.getByDisplayValue('Field 1'); - fireEvent.change(field1Input, {target: {value: 'Edited Field 1'}}); + await userEvent.clear(field1Input); + await userEvent.type(field1Input, 'Edited Field 1'); + + // Trigger blur to save the edited field name - fireEvent used because userEvent doesn't have direct focus/blur methods fireEvent.blur(field1Input); expect(updateField).toHaveBeenCalledWith({ diff --git a/webapp/channels/src/components/admin_console/system_properties/user_properties_type_menu.test.tsx b/webapp/channels/src/components/admin_console/system_properties/user_properties_type_menu.test.tsx index 7d929adf4ec..0d26cf43b72 100644 --- a/webapp/channels/src/components/admin_console/system_properties/user_properties_type_menu.test.tsx +++ b/webapp/channels/src/components/admin_console/system_properties/user_properties_type_menu.test.tsx @@ -1,12 +1,11 @@ // Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved. // See LICENSE.txt for license information. -import {fireEvent, screen} from '@testing-library/react'; import React from 'react'; import type {UserPropertyField} from '@mattermost/types/properties'; -import {renderWithContext} from 'tests/react_testing_utils'; +import {renderWithContext, screen, userEvent} from 'tests/react_testing_utils'; import SelectType from './user_properties_type_menu'; @@ -37,10 +36,6 @@ describe('UserPropertyTypeMenu', () => { ); }; - beforeEach(() => { - jest.clearAllMocks(); - }); - it('renders with correct current type', () => { renderComponent(); @@ -76,14 +71,14 @@ describe('UserPropertyTypeMenu', () => { expect(menuButton).toBeDisabled(); }); - it('changes field type when a new type is selected', () => { + it('changes field type when a new type is selected', async () => { renderComponent(); // Open the menu - fireEvent.click(screen.getByText('Text')); + await userEvent.click(screen.getByText('Text')); // Click to select Phone type - fireEvent.click(screen.getByText('Phone')); + await userEvent.click(screen.getByText('Phone')); // Verify the field was updated with the new type expect(updateField).toHaveBeenCalledWith({ @@ -96,26 +91,27 @@ describe('UserPropertyTypeMenu', () => { }); }); - it('filters options when searching', () => { + it('filters options when searching', async () => { renderComponent(); // Open the menu - fireEvent.click(screen.getByText('Text')); + await userEvent.click(screen.getByText('Text')); // Type in the filter input const filterInput = screen.getByRole('textbox', {name: 'Attribute type'}); - fireEvent.change(filterInput, {target: {value: 'multi'}}); + await userEvent.clear(filterInput); + await userEvent.type(filterInput, 'multi'); // Should only see Multi-select now expect(screen.getByText('Multi-select')).toBeInTheDocument(); expect(screen.getAllByRole('menuitemradio')).toHaveLength(1); }); - it('disables non-supported options when ldap-linked', () => { + it('disables non-supported options when ldap-linked', async () => { renderComponent({...baseField, attrs: {...baseField.attrs, ldap: 'ldapPropName'}}); // Open the menu - fireEvent.click(screen.getByText('Text')); + await userEvent.click(screen.getByText('Text')); // Non-text should be disabled expect(screen.getByRole('menuitemradio', {name: 'Phone'})).toHaveAttribute('aria-disabled', 'true'); @@ -125,11 +121,11 @@ describe('UserPropertyTypeMenu', () => { expect(screen.getByRole('menuitemradio', {name: 'Select'})).toHaveAttribute('aria-disabled', 'true'); }); - it('disables non-supported options when saml-linked', () => { + it('disables non-supported options when saml-linked', async () => { renderComponent({...baseField, attrs: {...baseField.attrs, saml: 'samlPropName'}}); // Open the menu - fireEvent.click(screen.getByText('Text')); + await userEvent.click(screen.getByText('Text')); // Non-text should be disabled expect(screen.getByRole('menuitemradio', {name: 'Phone'})).toHaveAttribute('aria-disabled', 'true'); @@ -139,7 +135,7 @@ describe('UserPropertyTypeMenu', () => { expect(screen.getByRole('menuitemradio', {name: 'Select'})).toHaveAttribute('aria-disabled', 'true'); }); - it('shows check icon for current type', () => { + it('shows check icon for current type', async () => { const selectField = { ...baseField, type: 'select' as const, @@ -152,7 +148,7 @@ describe('UserPropertyTypeMenu', () => { renderComponent(selectField); // Open the menu - fireEvent.click(screen.getByText('Select')); + await userEvent.click(screen.getByText('Select')); // All options should be visible, but Select should have a check expect(screen.getByRole('menuitemradio', {name: 'Select'})).toHaveAttribute('aria-checked', 'true'); diff --git a/webapp/channels/src/components/admin_console/system_properties/user_properties_values.test.tsx b/webapp/channels/src/components/admin_console/system_properties/user_properties_values.test.tsx index 5dc2b367752..6d8c5613133 100644 --- a/webapp/channels/src/components/admin_console/system_properties/user_properties_values.test.tsx +++ b/webapp/channels/src/components/admin_console/system_properties/user_properties_values.test.tsx @@ -1,12 +1,11 @@ // Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved. // See LICENSE.txt for license information. -import {screen, fireEvent} from '@testing-library/react'; import React from 'react'; import type {UserPropertyField} from '@mattermost/types/properties'; -import {renderWithContext} from 'tests/react_testing_utils'; +import {fireEvent, renderWithContext, screen, userEvent} from 'tests/react_testing_utils'; import UserPropertyValues from './user_properties_values'; @@ -42,10 +41,6 @@ describe('UserPropertyValues', () => { ); }; - beforeEach(() => { - jest.clearAllMocks(); - }); - it('renders correctly for select/multiselect field types', () => { renderComponent(); @@ -69,8 +64,9 @@ describe('UserPropertyValues', () => { renderComponent(); const input = screen.getByRole('combobox'); - fireEvent.change(input, {target: {value: 'New Option'}}); - fireEvent.keyDown(input, {key: 'Enter'}); + await userEvent.clear(input); + await userEvent.type(input, 'New Option'); + await userEvent.keyboard('{Enter}'); expect(updateField).toHaveBeenCalledWith({ ...baseField, @@ -88,7 +84,10 @@ describe('UserPropertyValues', () => { renderComponent(); const input = screen.getByRole('combobox'); - fireEvent.change(input, {target: {value: 'New Option'}}); + await userEvent.clear(input); + await userEvent.type(input, 'New Option'); + + // Trigger blur to save the new option value - fireEvent used because userEvent doesn't have direct focus/blur methods fireEvent.blur(input); expect(updateField).toHaveBeenCalledWith({ @@ -108,7 +107,7 @@ describe('UserPropertyValues', () => { // Find and click the first remove button (x) const removeButtons = screen.getAllByRole('button'); - fireEvent.click(removeButtons[0]); + await userEvent.click(removeButtons[0]); expect(updateField).toHaveBeenCalledWith({ ...baseField, @@ -123,13 +122,14 @@ describe('UserPropertyValues', () => { renderComponent(); const input = screen.getByRole('combobox'); - fireEvent.change(input, {target: {value: 'Option 1'}}); // This already exists + await userEvent.clear(input); + await userEvent.type(input, 'Option 1'); // This already exists // Error message should appear expect(screen.getByText('Values must be unique.')).toBeInTheDocument(); // Pressing Enter shouldn't add the duplicate - fireEvent.keyDown(input, {key: 'Enter'}); + await userEvent.keyboard('{Enter}'); expect(updateField).not.toHaveBeenCalled(); }); diff --git a/webapp/channels/src/components/admin_console/team_channel_settings/channel/details/channel_level_access_rules.test.tsx b/webapp/channels/src/components/admin_console/team_channel_settings/channel/details/channel_level_access_rules.test.tsx index 410c5dcb217..4c063169f73 100644 --- a/webapp/channels/src/components/admin_console/team_channel_settings/channel/details/channel_level_access_rules.test.tsx +++ b/webapp/channels/src/components/admin_console/team_channel_settings/channel/details/channel_level_access_rules.test.tsx @@ -117,10 +117,6 @@ describe('ChannelLevelAccessRules', () => { isDisabled: false, }; - beforeEach(() => { - jest.clearAllMocks(); - }); - it('should render the component with correct title and subtitle', () => { renderWithContext(); diff --git a/webapp/channels/src/components/admin_console/team_channel_settings/team/list/team_list.test.tsx b/webapp/channels/src/components/admin_console/team_channel_settings/team/list/team_list.test.tsx index f6c58353a43..377e110cf9d 100644 --- a/webapp/channels/src/components/admin_console/team_channel_settings/team/list/team_list.test.tsx +++ b/webapp/channels/src/components/admin_console/team_channel_settings/team/list/team_list.test.tsx @@ -1,12 +1,11 @@ // Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved. // See LICENSE.txt for license information. -import userEvent from '@testing-library/user-event'; import React from 'react'; import {PAGE_SIZE} from 'components/admin_console/team_channel_settings/abstract_list'; -import {renderWithContext, screen, waitFor} from 'tests/react_testing_utils'; +import {renderWithContext, screen, userEvent, waitFor} from 'tests/react_testing_utils'; import {TestHelper} from 'utils/test_helper'; import TeamList from './team_list'; diff --git a/webapp/channels/src/components/advanced_text_editor/rewrite_menu.test.tsx b/webapp/channels/src/components/advanced_text_editor/rewrite_menu.test.tsx index f5a4f979e1a..e318c4ca678 100644 --- a/webapp/channels/src/components/advanced_text_editor/rewrite_menu.test.tsx +++ b/webapp/channels/src/components/advanced_text_editor/rewrite_menu.test.tsx @@ -5,7 +5,7 @@ import React from 'react'; import type {Agent} from '@mattermost/types/agents'; -import {renderWithContext, screen, fireEvent} from 'tests/react_testing_utils'; +import {renderWithContext, screen, userEvent} from 'tests/react_testing_utils'; import {RewriteAction} from './rewrite_action'; import type {RewriteMenuProps} from './rewrite_menu'; @@ -115,10 +115,6 @@ describe('RewriteMenu', () => { customPromptRef: React.createRef(), }; - beforeEach(() => { - jest.clearAllMocks(); - }); - test('should not render agent dropdown when no agents', () => { renderWithContext( { expect(screen.getByText('Stop generating')).toBeInTheDocument(); }); - test('should call onCancelProcessing when stop generating button is clicked', () => { + test('should call onCancelProcessing when stop generating button is clicked', async () => { const onCancelProcessing = jest.fn(); renderWithContext( { onCancelProcessing={onCancelProcessing} />, ); - fireEvent.click(screen.getByText('Stop generating')); + await userEvent.click(screen.getByText('Stop generating')); expect(onCancelProcessing).toHaveBeenCalled(); }); @@ -217,7 +213,7 @@ describe('RewriteMenu', () => { expect(screen.queryByTestId('menu-footer')).not.toBeInTheDocument(); }); - test('should call onUndoMessage when discard button is clicked', () => { + test('should call onUndoMessage when discard button is clicked', async () => { const onUndoMessage = jest.fn(); renderWithContext( { onUndoMessage={onUndoMessage} />, ); - fireEvent.click(screen.getByText('Discard')); + await userEvent.click(screen.getByText('Discard')); expect(onUndoMessage).toHaveBeenCalled(); }); - test('should call onRegenerateMessage when regenerate button is clicked', () => { + test('should call onRegenerateMessage when regenerate button is clicked', async () => { const onRegenerateMessage = jest.fn(); renderWithContext( { onRegenerateMessage={onRegenerateMessage} />, ); - fireEvent.click(screen.getByText('Regenerate')); + await userEvent.click(screen.getByText('Regenerate')); expect(onRegenerateMessage).toHaveBeenCalled(); }); - test('should call onMenuAction when menu item is clicked', () => { + test('should call onMenuAction when menu item is clicked', async () => { const onMenuAction = jest.fn(() => () => {}); renderWithContext( { />, ); const menuItems = screen.getAllByTestId('menu-item'); - fireEvent.click(menuItems[0]); + await userEvent.click(menuItems[0]); expect(onMenuAction).toHaveBeenCalled(); }); - test('should call setPrompt when prompt input changes', () => { + test('should call setPrompt when prompt input changes', async () => { const setPrompt = jest.fn(); renderWithContext( { />, ); const input = screen.getByTestId('prompt-input-field'); - fireEvent.change(input, {target: {value: 'New prompt'}}); + await userEvent.clear(input); + await userEvent.type(input, 'New prompt'); expect(setPrompt).toHaveBeenCalled(); }); - test('should call onCustomPromptKeyDown when key is pressed in prompt input', () => { + test('should call onCustomPromptKeyDown when key is pressed in prompt input', async () => { const onCustomPromptKeyDown = jest.fn(); renderWithContext( { />, ); const input = screen.getByTestId('prompt-input-field'); - fireEvent.keyDown(input, {key: 'Enter'}); + input.focus(); + await userEvent.keyboard('{Enter}'); expect(onCustomPromptKeyDown).toHaveBeenCalled(); }); @@ -337,7 +335,7 @@ describe('RewriteMenu', () => { expect(screen.queryByTestId('agent-dropdown')).not.toBeInTheDocument(); }); - test('should call setSelectedAgentId when agent is selected', () => { + test('should call setSelectedAgentId when agent is selected', async () => { const setSelectedAgentId = jest.fn(); renderWithContext( { />, ); const select = screen.getByTestId('agent-select'); - fireEvent.change(select, {target: {value: 'agent2'}}); + await userEvent.selectOptions(select, 'agent2'); expect(setSelectedAgentId).toHaveBeenCalledWith('agent2'); }); }); diff --git a/webapp/channels/src/components/advanced_text_editor/send_button/send_post_options/core_menu_options.test.tsx b/webapp/channels/src/components/advanced_text_editor/send_button/send_post_options/core_menu_options.test.tsx index 63658edd521..9e202ef1d37 100644 --- a/webapp/channels/src/components/advanced_text_editor/send_button/send_post_options/core_menu_options.test.tsx +++ b/webapp/channels/src/components/advanced_text_editor/send_button/send_post_options/core_menu_options.test.tsx @@ -7,7 +7,7 @@ import React from 'react'; import useTimePostBoxIndicator from 'components/advanced_text_editor/use_post_box_indicator'; import {WithTestMenuContext} from 'components/menu/menu_context_test'; -import {renderWithContext, fireEvent, screen} from 'tests/react_testing_utils'; +import {fireEvent, renderWithContext, screen} from 'tests/react_testing_utils'; import CoreMenuOptions from './core_menu_options'; @@ -48,7 +48,6 @@ describe('CoreMenuOptions Component', () => { const handleOnSelect = jest.fn(); beforeEach(() => { - jest.clearAllMocks(); handleOnSelect.mockReset(); mockedUseTimePostBoxIndicator.mockReturnValue({ ...defaultUseTimePostBoxIndicatorReturnValue, @@ -137,6 +136,8 @@ describe('CoreMenuOptions Component', () => { renderComponent(); const tomorrowOption = screen.getByText(/Tomorrow at/); + + // Use fireEvent.click here because userEvent doesn't work well with fake timers fireEvent.click(tomorrowOption); const expectedTimestamp = DateTime.now(). diff --git a/webapp/channels/src/components/advanced_text_editor/send_button/send_post_options/recent_used_custom_date.test.tsx b/webapp/channels/src/components/advanced_text_editor/send_button/send_post_options/recent_used_custom_date.test.tsx index e84b670772a..2427ed6d980 100644 --- a/webapp/channels/src/components/advanced_text_editor/send_button/send_post_options/recent_used_custom_date.test.tsx +++ b/webapp/channels/src/components/advanced_text_editor/send_button/send_post_options/recent_used_custom_date.test.tsx @@ -6,7 +6,7 @@ import React from 'react'; import {getPreferenceKey} from 'mattermost-redux/utils/preference_utils'; -import {renderWithContext, fireEvent, screen} from 'tests/react_testing_utils'; +import {fireEvent, renderWithContext, screen} from 'tests/react_testing_utils'; import {scheduledPosts} from 'utils/constants'; import RecentUsedCustomDate from './recent_used_custom_date'; @@ -51,7 +51,6 @@ describe('CoreMenuOptions Component', () => { let nextMonday: number; beforeEach(() => { - jest.clearAllMocks(); handleOnSelect.mockReset(); now = DateTime.fromISO('2024-11-01T10:00:00', {zone: userCurrentTimezone}); jest.useFakeTimers(); @@ -134,6 +133,8 @@ describe('CoreMenuOptions Component', () => { renderComponent(state, handleOnSelectMock); const recentCustomOption = screen.getByText(recentUsedCustomDateString); + + // Use fireEvent.click here because userEvent doesn't work well with fake timers fireEvent.click(recentCustomOption); expect(handleOnSelectMock).toHaveBeenCalledWith(expect.anything(), recentTimestamp); diff --git a/webapp/channels/src/components/advanced_text_editor/show_formatting/show_formatting.test.tsx b/webapp/channels/src/components/advanced_text_editor/show_formatting/show_formatting.test.tsx index 4ed2392835f..a78824fa5b4 100644 --- a/webapp/channels/src/components/advanced_text_editor/show_formatting/show_formatting.test.tsx +++ b/webapp/channels/src/components/advanced_text_editor/show_formatting/show_formatting.test.tsx @@ -3,7 +3,7 @@ import React from 'react'; -import {renderWithContext, fireEvent, screen} from 'tests/react_testing_utils'; +import {renderWithContext, screen, userEvent} from 'tests/react_testing_utils'; import ShowFormatting from './show_formatting'; @@ -23,7 +23,7 @@ describe('ShowFormatting Component', () => { expect(screen.getByLabelText('Eye Icon')).toBeInTheDocument(); }); - it('should call onClick handler when clicked', () => { + it('should call onClick handler when clicked', async () => { const onClick = jest.fn(); renderWithContext( { />, ); - fireEvent.click(screen.getByLabelText('Eye Icon')); + await userEvent.click(screen.getByLabelText('Eye Icon')); expect(onClick).toHaveBeenCalledTimes(1); }); diff --git a/webapp/channels/src/components/advanced_text_editor/toggle_formatting_bar.test.tsx b/webapp/channels/src/components/advanced_text_editor/toggle_formatting_bar.test.tsx index 203f6115667..84b14f1e966 100644 --- a/webapp/channels/src/components/advanced_text_editor/toggle_formatting_bar.test.tsx +++ b/webapp/channels/src/components/advanced_text_editor/toggle_formatting_bar.test.tsx @@ -3,7 +3,7 @@ import React from 'react'; -import {renderWithContext, fireEvent, screen} from 'tests/react_testing_utils'; +import {fireEvent, renderWithContext, screen, userEvent} from 'tests/react_testing_utils'; import ToggleFormattingBar from './toggle_formatting_bar'; @@ -24,7 +24,7 @@ describe('ToggleFormattingBar Component', () => { expect(screen.getAllByLabelText('Format letter Case Icon')[0]).toBeInTheDocument(); }); - it('should call onClick handler when clicked', () => { + it('should call onClick handler when clicked', async () => { const onClick = jest.fn(); renderWithContext( { />, ); - fireEvent.click(screen.getByLabelText('formatting')); + await userEvent.click(screen.getByLabelText('formatting')); expect(onClick).toHaveBeenCalledTimes(1); }); @@ -48,6 +48,7 @@ describe('ToggleFormattingBar Component', () => { />, ); + // Use fireEvent.click to test disabled behavior (userEvent respects pointer-events: none) fireEvent.click(screen.getByLabelText('formatting')); expect(onClick).not.toHaveBeenCalled(); }); diff --git a/webapp/channels/src/components/advanced_text_editor/use_rewrite.test.tsx b/webapp/channels/src/components/advanced_text_editor/use_rewrite.test.tsx index 4281d4a6ec0..105ad466808 100644 --- a/webapp/channels/src/components/advanced_text_editor/use_rewrite.test.tsx +++ b/webapp/channels/src/components/advanced_text_editor/use_rewrite.test.tsx @@ -85,7 +85,6 @@ describe('useRewrite', () => { }; beforeEach(() => { - jest.clearAllMocks(); MockedRewriteMenu.mockClear(); document.body.innerHTML = ''; try { diff --git a/webapp/channels/src/components/announcement_bar/overage_users_banner/overage_users_banner.test.tsx b/webapp/channels/src/components/announcement_bar/overage_users_banner/overage_users_banner.test.tsx index 941df93c7e6..7e8020531ab 100644 --- a/webapp/channels/src/components/announcement_bar/overage_users_banner/overage_users_banner.test.tsx +++ b/webapp/channels/src/components/announcement_bar/overage_users_banner/overage_users_banner.test.tsx @@ -8,7 +8,7 @@ import type {DeepPartial} from '@mattermost/types/utilities'; import {savePreferences} from 'mattermost-redux/actions/preferences'; import {General} from 'mattermost-redux/constants'; -import {fireEvent, renderWithContext, screen} from 'tests/react_testing_utils'; +import {renderWithContext, screen, userEvent} from 'tests/react_testing_utils'; import {OverActiveUserLimits, Preferences, SelfHostedProducts, StatTypes} from 'utils/constants'; import {TestHelper} from 'utils/test_helper'; import {generateId} from 'utils/utils'; @@ -201,7 +201,7 @@ describe('components/overage_users_banner', () => { expect(screen.getByText(contactSalesTextLink)).toBeInTheDocument(); }); - it('should track if the admin click Contact Sales CTA in a 10% overage state', () => { + it('should track if the admin click Contact Sales CTA in a 10% overage state', async () => { const store = JSON.parse(JSON.stringify(initialState)); store.entities.cloud = { @@ -217,7 +217,7 @@ describe('components/overage_users_banner', () => { renderWithContext(, store); - fireEvent.click(screen.getByText(contactSalesTextLink)); + await userEvent.click(screen.getByText(contactSalesTextLink)); expect(windowSpy).toHaveBeenCalledTimes(1); // only the email is encoded and other params are empty. See logic for useOpenSalesLink hook @@ -255,7 +255,7 @@ describe('components/overage_users_banner', () => { expect(screen.getByText(contactSalesTextLink)).toBeInTheDocument(); }); - it('should save the preferences for 5% banner if admin click on close', () => { + it('should save the preferences for 5% banner if admin click on close', async () => { const store = JSON.parse(JSON.stringify(initialState)); store.entities.admin = { @@ -267,7 +267,7 @@ describe('components/overage_users_banner', () => { renderWithContext(, store); - fireEvent.click(screen.getByRole('link')); + await userEvent.click(screen.getByRole('link')); expect(savePreferences).toHaveBeenCalledTimes(1); expect(savePreferences).toHaveBeenCalledWith(store.entities.users.profiles.current_user.id, [{ @@ -298,7 +298,7 @@ describe('components/overage_users_banner', () => { expect(screen.getByText(contactSalesTextLink)).toBeInTheDocument(); }); - it('should track if the admin click Contact Sales CTA in a 10% overage state', () => { + it('should track if the admin click Contact Sales CTA in a 10% overage state', async () => { const store = JSON.parse(JSON.stringify(initialState)); store.entities.cloud = { @@ -314,7 +314,7 @@ describe('components/overage_users_banner', () => { renderWithContext(, store); - fireEvent.click(screen.getByText(contactSalesTextLink)); + await userEvent.click(screen.getByText(contactSalesTextLink)); expect(windowSpy).toHaveBeenCalledTimes(1); // only the email is encoded and other params are empty. See logic for useOpenSalesLink hook diff --git a/webapp/channels/src/components/announcement_bar/post_history_limit_banner/post_history_limit_banner.test.tsx b/webapp/channels/src/components/announcement_bar/post_history_limit_banner/post_history_limit_banner.test.tsx index 8e594a4358b..acaa5ea9709 100644 --- a/webapp/channels/src/components/announcement_bar/post_history_limit_banner/post_history_limit_banner.test.tsx +++ b/webapp/channels/src/components/announcement_bar/post_history_limit_banner/post_history_limit_banner.test.tsx @@ -8,7 +8,7 @@ import type {DeepPartial} from '@mattermost/types/utilities'; import {savePreferences} from 'mattermost-redux/actions/preferences'; import {General} from 'mattermost-redux/constants'; -import {fireEvent, renderWithContext, screen} from 'tests/react_testing_utils'; +import {renderWithContext, screen, userEvent} from 'tests/react_testing_utils'; import {Preferences} from 'utils/constants'; import {TestHelper} from 'utils/test_helper'; import {generateId} from 'utils/utils'; @@ -54,8 +54,6 @@ describe('components/announcement_bar/PostHistoryLimitBanner', () => { let mockToLocaleDateString: jest.SpyInstance; beforeEach(() => { - jest.clearAllMocks(); - mockOpenPricingModal = jest.fn(); mockUseOpenPricingModal.mockReturnValue({openPricingModal: mockOpenPricingModal}); @@ -259,19 +257,19 @@ describe('components/announcement_bar/PostHistoryLimitBanner', () => { describe('User Interactions', () => { const preferenceName = 'post_history_limit_banner'; - it('should call openPricingModal when upgrade button is clicked', () => { + it('should call openPricingModal when upgrade button is clicked', async () => { setupServerLimits(true); const state = createInitialState(true, []); renderWithContext(, state); const upgradeButton = screen.getByText('Restore Access'); - fireEvent.click(upgradeButton); + await userEvent.click(upgradeButton); expect(mockOpenPricingModal).toHaveBeenCalled(); }); - it('should save dismissal timestamp when close button is clicked', () => { + it('should save dismissal timestamp when close button is clicked', async () => { setupServerLimits(true); const state = createInitialState(true, []); @@ -281,7 +279,7 @@ describe('components/announcement_bar/PostHistoryLimitBanner', () => { renderWithContext(, state); const closeButton = screen.getByRole('link', {name: '×'}); - fireEvent.click(closeButton); + await userEvent.click(closeButton); expect(mockDispatch).toHaveBeenCalledWith( mockSavePreferences(currentUserId, [{ diff --git a/webapp/channels/src/components/announcement_bar/renewal_link/renewal_link.test.tsx b/webapp/channels/src/components/announcement_bar/renewal_link/renewal_link.test.tsx index 06a2cc6242d..e478fbd025a 100644 --- a/webapp/channels/src/components/announcement_bar/renewal_link/renewal_link.test.tsx +++ b/webapp/channels/src/components/announcement_bar/renewal_link/renewal_link.test.tsx @@ -54,10 +54,6 @@ const actImmediate = (wrapper: ReactWrapper) => ); describe('components/RenewalLink', () => { - afterEach(() => { - jest.clearAllMocks(); - }); - test('should show Contact sales button', async () => { const store = mockStore(initialState); const wrapper = mountWithIntl(); diff --git a/webapp/channels/src/components/apps_form/apps_form_component.test.tsx b/webapp/channels/src/components/apps_form/apps_form_component.test.tsx index 3222b23b849..ac49c2d9a6c 100644 --- a/webapp/channels/src/components/apps_form/apps_form_component.test.tsx +++ b/webapp/channels/src/components/apps_form/apps_form_component.test.tsx @@ -1,13 +1,11 @@ // Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved. // See LICENSE.txt for license information. -import {screen, waitFor} from '@testing-library/react'; -import userEvent from '@testing-library/user-event'; import React from 'react'; import {AppCallResponseTypes} from 'mattermost-redux/constants/apps'; -import {renderWithContext} from 'tests/react_testing_utils'; +import {renderWithContext, screen, waitFor, userEvent} from 'tests/react_testing_utils'; import {AppsForm} from './apps_form_component'; import type {Props} from './apps_form_component'; @@ -71,10 +69,6 @@ describe('AppsFormComponent', () => { }, }; - afterEach(() => { - jest.clearAllMocks(); - }); - test('should render form with title, header, fields and initial values', () => { renderWithContext( { }; beforeEach(() => { - jest.clearAllMocks(); - // Mock current time to avoid timezone-dependent tests jest.useFakeTimers(); jest.setSystemTime(new Date('2025-01-15T10:00:00.000Z')); @@ -90,6 +87,8 @@ describe('AppsFormDateField', () => { it('should handle input click to open date picker', () => { renderComponent(); const button = screen.getByRole('button'); + + // Use fireEvent.click here because userEvent doesn't work well with fake timers fireEvent.click(button); // DatePicker opening is handled by the DatePicker component itself @@ -101,6 +100,7 @@ describe('AppsFormDateField', () => { renderComponent(); const button = screen.getByRole('button'); + // Simulate keyboard Enter key - fireEvent used because userEvent doesn't work well with fake timers fireEvent.keyDown(button, {key: 'Enter'}); expect(button).toBeInTheDocument(); diff --git a/webapp/channels/src/components/apps_form/apps_form_datetime_field/apps_form_datetime_field.test.tsx b/webapp/channels/src/components/apps_form/apps_form_datetime_field/apps_form_datetime_field.test.tsx index a3574ca75a3..fd440d690a3 100644 --- a/webapp/channels/src/components/apps_form/apps_form_datetime_field/apps_form_datetime_field.test.tsx +++ b/webapp/channels/src/components/apps_form/apps_form_datetime_field/apps_form_datetime_field.test.tsx @@ -44,8 +44,6 @@ describe('AppsFormDateTimeField', () => { }; beforeEach(() => { - jest.clearAllMocks(); - // Mock current time to avoid timezone-dependent tests jest.useFakeTimers(); jest.setSystemTime(new Date('2025-01-15T10:00:00.000Z')); diff --git a/webapp/channels/src/components/burn_on_read/burn_on_read_button.test.tsx b/webapp/channels/src/components/burn_on_read/burn_on_read_button.test.tsx index 4f4b594b051..0fad31acdf5 100644 --- a/webapp/channels/src/components/burn_on_read/burn_on_read_button.test.tsx +++ b/webapp/channels/src/components/burn_on_read/burn_on_read_button.test.tsx @@ -3,7 +3,7 @@ import React from 'react'; -import {renderWithContext, fireEvent, screen} from 'tests/react_testing_utils'; +import {renderWithContext, userEvent, screen} from 'tests/react_testing_utils'; import BurnOnReadButton from './burn_on_read_button'; @@ -19,10 +19,6 @@ describe('BurnOnReadButton', () => { durationMinutes: 10, }; - beforeEach(() => { - jest.clearAllMocks(); - }); - it('should render correctly when disabled', () => { renderWithContext( , @@ -46,7 +42,7 @@ describe('BurnOnReadButton', () => { expect(button).toHaveClass('control'); }); - it('should call onToggle with true when clicked while disabled', () => { + it('should call onToggle with true when clicked while disabled', async () => { const onToggle = jest.fn(); renderWithContext( { ); const button = screen.getByRole('button'); - fireEvent.click(button); + await userEvent.click(button); expect(onToggle).toHaveBeenCalledTimes(1); expect(onToggle).toHaveBeenCalledWith(true); }); - it('should call onToggle with false when clicked while enabled', () => { + it('should call onToggle with false when clicked while enabled', async () => { const onToggle = jest.fn(); renderWithContext( { ); const button = screen.getByRole('button'); - fireEvent.click(button); + await userEvent.click(button); expect(onToggle).toHaveBeenCalledTimes(1); expect(onToggle).toHaveBeenCalledWith(false); diff --git a/webapp/channels/src/components/burn_on_read/burn_on_read_label.test.tsx b/webapp/channels/src/components/burn_on_read/burn_on_read_label.test.tsx index ee81b684a4b..0e42f0def18 100644 --- a/webapp/channels/src/components/burn_on_read/burn_on_read_label.test.tsx +++ b/webapp/channels/src/components/burn_on_read/burn_on_read_label.test.tsx @@ -3,7 +3,7 @@ import React from 'react'; -import {renderWithContext, fireEvent, screen} from 'tests/react_testing_utils'; +import {renderWithContext, userEvent, screen} from 'tests/react_testing_utils'; import BurnOnReadLabel from './burn_on_read_label'; @@ -14,10 +14,6 @@ describe('BurnOnReadLabel', () => { durationMinutes: 10, }; - beforeEach(() => { - jest.clearAllMocks(); - }); - it('should render correctly with duration', () => { renderWithContext( , @@ -62,7 +58,7 @@ describe('BurnOnReadLabel', () => { expect(closeButton).not.toBeInTheDocument(); }); - it('should call onRemove when close button is clicked', () => { + it('should call onRemove when close button is clicked', async () => { const onRemove = jest.fn(); renderWithContext( { ); const closeButton = screen.getByRole('button'); - fireEvent.click(closeButton); + await userEvent.click(closeButton); expect(onRemove).toHaveBeenCalledTimes(1); }); diff --git a/webapp/channels/src/components/burn_on_read_confirmation_modal/burn_on_read_confirmation_modal.test.tsx b/webapp/channels/src/components/burn_on_read_confirmation_modal/burn_on_read_confirmation_modal.test.tsx index 2fad5e4cae9..c5c7ee3e8b4 100644 --- a/webapp/channels/src/components/burn_on_read_confirmation_modal/burn_on_read_confirmation_modal.test.tsx +++ b/webapp/channels/src/components/burn_on_read_confirmation_modal/burn_on_read_confirmation_modal.test.tsx @@ -1,10 +1,9 @@ // Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved. // See LICENSE.txt for license information. -import {fireEvent} from '@testing-library/react'; import React from 'react'; -import {renderWithContext, screen} from 'tests/react_testing_utils'; +import {renderWithContext, screen, userEvent} from 'tests/react_testing_utils'; import BurnOnReadConfirmationModal from './burn_on_read_confirmation_modal'; @@ -15,10 +14,6 @@ describe('BurnOnReadConfirmationModal', () => { onCancel: jest.fn(), }; - beforeEach(() => { - jest.clearAllMocks(); - }); - it('should render receiver delete message when show is true and isSenderDelete is false', () => { renderWithContext(); @@ -49,7 +44,7 @@ describe('BurnOnReadConfirmationModal', () => { expect(screen.queryByText('Delete Message Now?')).not.toBeInTheDocument(); }); - it('should call onCancel when Cancel button is clicked', () => { + it('should call onCancel when Cancel button is clicked', async () => { const onCancel = jest.fn(); renderWithContext( { ); const cancelButton = screen.getByText('Cancel'); - fireEvent.click(cancelButton); + await userEvent.click(cancelButton); expect(onCancel).toHaveBeenCalledTimes(1); }); - it('should call onConfirm with false when Delete Now button is clicked without checkbox', () => { + it('should call onConfirm with false when Delete Now button is clicked without checkbox', async () => { const onConfirm = jest.fn(); renderWithContext( { ); const confirmButton = screen.getByText('Delete Now'); - fireEvent.click(confirmButton); + await userEvent.click(confirmButton); expect(onConfirm).toHaveBeenCalledWith(false); }); @@ -103,7 +98,7 @@ describe('BurnOnReadConfirmationModal', () => { expect(screen.queryByRole('checkbox')).not.toBeInTheDocument(); }); - it('should call onConfirm with true when checkbox is checked', () => { + it('should call onConfirm with true when checkbox is checked', async () => { const onConfirm = jest.fn(); renderWithContext( { ); const checkbox = screen.getByRole('checkbox'); - fireEvent.click(checkbox); + await userEvent.click(checkbox); const confirmButton = screen.getByText('Delete Now'); - fireEvent.click(confirmButton); + await userEvent.click(confirmButton); expect(onConfirm).toHaveBeenCalledWith(true); }); diff --git a/webapp/channels/src/components/channel_header/channel_header_title.test.tsx b/webapp/channels/src/components/channel_header/channel_header_title.test.tsx index 3766a91614f..e5e17a3855d 100644 --- a/webapp/channels/src/components/channel_header/channel_header_title.test.tsx +++ b/webapp/channels/src/components/channel_header/channel_header_title.test.tsx @@ -88,10 +88,6 @@ jest.mock('mattermost-redux/actions/shared_channels', () => ({ describe('components/channel_header/ChannelHeaderTitle', () => { const mockStore = configureStore(); - afterEach(() => { - jest.clearAllMocks(); - }); - test('should not fetch shared channels for non-shared channels', () => { // Mock non-shared channel const channel = { diff --git a/webapp/channels/src/components/channel_header/channel_header_title_favorite.test.tsx b/webapp/channels/src/components/channel_header/channel_header_title_favorite.test.tsx index 4bb7b35f7f3..fbd8dd8b1dc 100644 --- a/webapp/channels/src/components/channel_header/channel_header_title_favorite.test.tsx +++ b/webapp/channels/src/components/channel_header/channel_header_title_favorite.test.tsx @@ -1,7 +1,6 @@ // Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved. // See LICENSE.txt for license information. -import {screen, fireEvent, act, waitFor} from '@testing-library/react'; import React from 'react'; import type {Channel} from '@mattermost/types/channels'; @@ -9,7 +8,7 @@ import type {Channel} from '@mattermost/types/channels'; import {favoriteChannel, unfavoriteChannel} from 'mattermost-redux/actions/channels'; import * as channelsSelectors from 'mattermost-redux/selectors/entities/channels'; -import {renderWithContext} from 'tests/react_testing_utils'; +import {act, fireEvent, renderWithContext, screen, userEvent, waitFor} from 'tests/react_testing_utils'; import type {A11yFocusEventDetail} from 'utils/constants'; import {A11yCustomEventTypes} from 'utils/constants'; @@ -47,8 +46,6 @@ describe('ChannelHeaderTitleFavorite Component', () => { } as Channel; beforeEach(() => { - jest.clearAllMocks(); - // Spy on selectors isCurrentChannelFavoriteMock = jest.spyOn(channelsSelectors, 'isCurrentChannelFavorite'); getCurrentChannelMock = jest.spyOn(channelsSelectors, 'getCurrentChannel'); @@ -66,7 +63,7 @@ describe('ChannelHeaderTitleFavorite Component', () => { return renderWithContext(); } - it('should dispatch favoriteChannel when "Add to Favorites" button is clicked', () => { + it('should dispatch favoriteChannel when "Add to Favorites" button is clicked', async () => { isCurrentChannelFavoriteMock.mockReturnValue(false); getCurrentChannelMock.mockReturnValue(activeChannel); @@ -79,7 +76,7 @@ describe('ChannelHeaderTitleFavorite Component', () => { renderComponent(); const button = screen.getByRole('button', {name: ADD_TO_FAVORITES_REGEX}); - fireEvent.click(button); + await userEvent.click(button); expect(dispatchMock).toHaveBeenCalledTimes(1); expect(dispatchMock).toHaveBeenCalledWith({ @@ -88,7 +85,7 @@ describe('ChannelHeaderTitleFavorite Component', () => { }); }); - it('should dispatch unfavoriteChannel when "Remove from Favorites" button is clicked', () => { + it('should dispatch unfavoriteChannel when "Remove from Favorites" button is clicked', async () => { isCurrentChannelFavoriteMock.mockReturnValue(true); getCurrentChannelMock.mockReturnValue(activeChannel); @@ -101,7 +98,7 @@ describe('ChannelHeaderTitleFavorite Component', () => { renderComponent(); const button = screen.getByRole('button', {name: REMOVE_FROM_FAVORITES_REGEX}); - fireEvent.click(button); + await userEvent.click(button); expect(dispatchMock).toHaveBeenCalledTimes(1); expect(dispatchMock).toHaveBeenCalledWith({ @@ -205,11 +202,11 @@ describe('ChannelHeaderTitleFavorite Component', () => { const button = screen.getByRole('button', {name: ADD_TO_FAVORITES_REGEX}); - // Ensure the ref is set by triggering a focus event + // Trigger focus to ensure the button ref is set before clicking - fireEvent used because userEvent doesn't have direct focus/blur methods fireEvent.focus(button); - act(() => { - fireEvent.click(button); + await act(async () => { + await userEvent.click(button); }); expect(dispatchMock).toHaveBeenCalledWith({ diff --git a/webapp/channels/src/components/channel_header_menu/menu_items/add_channel_members.test.tsx b/webapp/channels/src/components/channel_header_menu/menu_items/add_channel_members.test.tsx index 4e5c358b91d..99fa678eb08 100644 --- a/webapp/channels/src/components/channel_header_menu/menu_items/add_channel_members.test.tsx +++ b/webapp/channels/src/components/channel_header_menu/menu_items/add_channel_members.test.tsx @@ -9,7 +9,7 @@ import * as modalActions from 'actions/views/modals'; import ChannelInviteModal from 'components/channel_invite_modal'; import {WithTestMenuContext} from 'components/menu/menu_context_test'; -import {renderWithContext, screen, fireEvent} from 'tests/react_testing_utils'; +import {renderWithContext, screen, userEvent} from 'tests/react_testing_utils'; import {ModalIdentifiers} from 'utils/constants'; import {TestHelper} from 'utils/test_helper'; @@ -40,7 +40,7 @@ describe('components/ChannelHeaderMenu/MenuItems/AddChannelMembers', () => { expect(menuItem).toBeInTheDocument(); // Check if text "Add Members" renders }); - test('dispatches openModal action on click', () => { + test('dispatches openModal action on click', async () => { renderWithContext( { const menuItem = screen.getByText('Add Members'); expect(menuItem).toBeInTheDocument(); // Check if text "Add Members" renders - fireEvent.click(menuItem); // Simulate click on the menu item + await userEvent.click(menuItem); // Simulate click on the menu item expect(useDispatch).toHaveBeenCalledTimes(1); // Ensure dispatch was called expect(modalActions.openModal).toHaveBeenCalledTimes(1); diff --git a/webapp/channels/src/components/channel_header_menu/menu_items/add_group_members.test.tsx b/webapp/channels/src/components/channel_header_menu/menu_items/add_group_members.test.tsx index cc28c3fdb19..d031ccf3ae5 100644 --- a/webapp/channels/src/components/channel_header_menu/menu_items/add_group_members.test.tsx +++ b/webapp/channels/src/components/channel_header_menu/menu_items/add_group_members.test.tsx @@ -9,7 +9,7 @@ import * as modalActions from 'actions/views/modals'; import {WithTestMenuContext} from 'components/menu/menu_context_test'; import MoreDirectChannels from 'components/more_direct_channels'; -import {renderWithContext, screen, fireEvent} from 'tests/react_testing_utils'; +import {renderWithContext, screen, userEvent} from 'tests/react_testing_utils'; import {ModalIdentifiers} from 'utils/constants'; import AddGroupMembers from './add_group_members'; @@ -35,7 +35,7 @@ describe('components/ChannelHeaderMenu/MenuItems/AddGroupMembers', () => { expect(menuItem).toBeInTheDocument(); // Check if text "Add Members" renders }); - test('dispatches openModal action on click', () => { + test('dispatches openModal action on click', async () => { renderWithContext( @@ -44,7 +44,7 @@ describe('components/ChannelHeaderMenu/MenuItems/AddGroupMembers', () => { const menuItem = screen.getByText('Add Members'); expect(menuItem).toBeInTheDocument(); // Check if text "Add Members" renders - fireEvent.click(menuItem); // Simulate click on the menu item + await userEvent.click(menuItem); // Simulate click on the menu item expect(useDispatch).toHaveBeenCalledTimes(1); // Ensure dispatch was called expect(modalActions.openModal).toHaveBeenCalledTimes(1); diff --git a/webapp/channels/src/components/channel_header_menu/menu_items/archive_channel.test.tsx b/webapp/channels/src/components/channel_header_menu/menu_items/archive_channel.test.tsx index dc8a757ac16..1b0dbef87b2 100644 --- a/webapp/channels/src/components/channel_header_menu/menu_items/archive_channel.test.tsx +++ b/webapp/channels/src/components/channel_header_menu/menu_items/archive_channel.test.tsx @@ -10,7 +10,7 @@ import LocalStorageStore from 'stores/local_storage_store'; import DeleteChannelModal from 'components/delete_channel_modal'; import {WithTestMenuContext} from 'components/menu/menu_context_test'; -import {renderWithContext, screen, fireEvent} from 'tests/react_testing_utils'; +import {renderWithContext, screen, userEvent} from 'tests/react_testing_utils'; import {ModalIdentifiers} from 'utils/constants'; import {TestHelper} from 'utils/test_helper'; @@ -66,10 +66,6 @@ describe('components/ChannelHeaderMenu/MenuItems/ArchiveChannel', () => { jest.spyOn(require('react-redux'), 'useDispatch'); }); - afterEach(() => { - jest.clearAllMocks(); - }); - test('renders the component correctly', () => { renderWithContext( , initialState, @@ -79,7 +75,7 @@ describe('components/ChannelHeaderMenu/MenuItems/ArchiveChannel', () => { expect(menuItem).toBeInTheDocument(); // Check if text "Add Members" renders }); - test('dispatches openModal action on click with default channel', () => { + test('dispatches openModal action on click with default channel', async () => { renderWithContext( @@ -88,7 +84,7 @@ describe('components/ChannelHeaderMenu/MenuItems/ArchiveChannel', () => { const menuItem = screen.getByText('Archive Channel'); expect(menuItem).toBeInTheDocument(); // Check if text "Add Members" renders - fireEvent.click(menuItem); // Simulate click on the menu item + await userEvent.click(menuItem); // Simulate click on the menu item expect(useDispatch).toHaveBeenCalledTimes(1); // Ensure dispatch was called expect(modalActions.openModal).toHaveBeenCalledTimes(1); diff --git a/webapp/channels/src/components/channel_header_menu/menu_items/close_channel.test.tsx b/webapp/channels/src/components/channel_header_menu/menu_items/close_channel.test.tsx index f89ecfaa74c..f7cab404a50 100644 --- a/webapp/channels/src/components/channel_header_menu/menu_items/close_channel.test.tsx +++ b/webapp/channels/src/components/channel_header_menu/menu_items/close_channel.test.tsx @@ -7,7 +7,7 @@ import * as channelActions from 'actions/views/channel'; import {WithTestMenuContext} from 'components/menu/menu_context_test'; -import {renderWithContext, screen, fireEvent} from 'tests/react_testing_utils'; +import {renderWithContext, screen, userEvent} from 'tests/react_testing_utils'; import CloseChannel from './close_channel'; @@ -20,7 +20,7 @@ describe('components/ChannelHeaderMenu/MenuItems/CloseChannel', () => { jest.clearAllMocks(); }); - test('renders the component correctly, handle click event', () => { + test('renders the component correctly, handle click event', async () => { renderWithContext( @@ -30,7 +30,7 @@ describe('components/ChannelHeaderMenu/MenuItems/CloseChannel', () => { const menuItem = screen.getByText('Close Channel'); expect(menuItem).toBeInTheDocument(); - fireEvent.click(menuItem); // Simulate click on the menu item + await userEvent.click(menuItem); // Simulate click on the menu item expect(channelActions.goToLastViewedChannel).toHaveBeenCalledTimes(1); // Ensure dispatch was called }); }); diff --git a/webapp/channels/src/components/channel_header_menu/menu_items/close_message.test.tsx b/webapp/channels/src/components/channel_header_menu/menu_items/close_message.test.tsx index 1ab8cd5b4bc..8f65fe616c4 100644 --- a/webapp/channels/src/components/channel_header_menu/menu_items/close_message.test.tsx +++ b/webapp/channels/src/components/channel_header_menu/menu_items/close_message.test.tsx @@ -11,7 +11,7 @@ import * as channelActions from 'actions/views/channel'; import {WithTestMenuContext} from 'components/menu/menu_context_test'; -import {renderWithContext, screen, fireEvent} from 'tests/react_testing_utils'; +import {renderWithContext, screen, userEvent} from 'tests/react_testing_utils'; import {Constants} from 'utils/constants'; import {TestHelper} from 'utils/test_helper'; @@ -80,7 +80,7 @@ describe('components/ChannelHeaderMenu/MenuItems/CloseMessage', () => { jest.clearAllMocks(); }); - test('renders the component correctly for group channel', () => { + test('renders the component correctly for group channel', async () => { renderWithContext( { const menuItem = screen.getByText('Close Group Message'); expect(menuItem).toBeInTheDocument(); - fireEvent.click(menuItem); // Simulate click on the menu item + await userEvent.click(menuItem); // Simulate click on the menu item expect(channelActions.leaveDirectChannel).toHaveBeenCalledTimes(1); // Ensure dispatch was called expect(channelActions.leaveDirectChannel).toHaveBeenCalledWith(groupChannel.name); @@ -104,7 +104,7 @@ describe('components/ChannelHeaderMenu/MenuItems/CloseMessage', () => { ); }); - test('renders the component correctly for direct channel', () => { + test('renders the component correctly for direct channel', async () => { renderWithContext( { const menuItem = screen.getByText('Close Direct Message'); expect(menuItem).toBeInTheDocument(); - fireEvent.click(menuItem); // Simulate click on the menu item + await userEvent.click(menuItem); // Simulate click on the menu item expect(channelActions.leaveDirectChannel).toHaveBeenCalledTimes(1); // Ensure dispatch was called expect(channelActions.leaveDirectChannel).toHaveBeenCalledWith(directChannel.name); diff --git a/webapp/channels/src/components/channel_header_menu/menu_items/convert_gm_to_private.test.tsx b/webapp/channels/src/components/channel_header_menu/menu_items/convert_gm_to_private.test.tsx index 45e042698ba..6835da74956 100644 --- a/webapp/channels/src/components/channel_header_menu/menu_items/convert_gm_to_private.test.tsx +++ b/webapp/channels/src/components/channel_header_menu/menu_items/convert_gm_to_private.test.tsx @@ -9,7 +9,7 @@ import * as modalActions from 'actions/views/modals'; import ConvertGmToChannelModal from 'components/convert_gm_to_channel_modal'; import {WithTestMenuContext} from 'components/menu/menu_context_test'; -import {renderWithContext, screen, fireEvent} from 'tests/react_testing_utils'; +import {renderWithContext, screen, userEvent} from 'tests/react_testing_utils'; import {ModalIdentifiers} from 'utils/constants'; import {TestHelper} from 'utils/test_helper'; @@ -28,7 +28,7 @@ describe('components/ChannelHeaderMenu/MenuItems/ConvertGMtoPrivate', () => { }); const channel = TestHelper.getChannelMock(); - test('renders the component correctly, handle click event', () => { + test('renders the component correctly, handle click event', async () => { renderWithContext( @@ -38,7 +38,7 @@ describe('components/ChannelHeaderMenu/MenuItems/ConvertGMtoPrivate', () => { const menuItem = screen.getByText('Convert to Private Channel'); expect(menuItem).toBeInTheDocument(); - fireEvent.click(menuItem); // Simulate click on the menu item + await userEvent.click(menuItem); // Simulate click on the menu item expect(useDispatch).toHaveBeenCalledTimes(1); // Ensure dispatch was called expect(modalActions.openModal).toHaveBeenCalledTimes(1); expect(modalActions.openModal).toHaveBeenCalledWith({ diff --git a/webapp/channels/src/components/channel_header_menu/menu_items/edit_conversation_header.test.tsx b/webapp/channels/src/components/channel_header_menu/menu_items/edit_conversation_header.test.tsx index b4c5d64eba0..55425b94394 100644 --- a/webapp/channels/src/components/channel_header_menu/menu_items/edit_conversation_header.test.tsx +++ b/webapp/channels/src/components/channel_header_menu/menu_items/edit_conversation_header.test.tsx @@ -9,7 +9,7 @@ import * as modalActions from 'actions/views/modals'; import EditChannelHeaderModal from 'components/edit_channel_header_modal'; import {WithTestMenuContext} from 'components/menu/menu_context_test'; -import {renderWithContext, screen, fireEvent} from 'tests/react_testing_utils'; +import {renderWithContext, screen, userEvent} from 'tests/react_testing_utils'; import {ModalIdentifiers} from 'utils/constants'; import {TestHelper} from 'utils/test_helper'; @@ -23,12 +23,9 @@ describe('components/ChannelHeaderMenu/MenuItems/EditConversationHeader', () => jest.spyOn(require('react-redux'), 'useDispatch'); }); - afterEach(() => { - jest.clearAllMocks(); - }); const channel = TestHelper.getChannelMock(); - test('renders the component correctly, handle click event', () => { + test('renders the component correctly, handle click event', async () => { renderWithContext( @@ -38,7 +35,7 @@ describe('components/ChannelHeaderMenu/MenuItems/EditConversationHeader', () => const menuItem = screen.getByText('Edit Header'); expect(menuItem).toBeInTheDocument(); - fireEvent.click(menuItem); // Simulate click on the menu item + await userEvent.click(menuItem); // Simulate click on the menu item expect(useDispatch).toHaveBeenCalledTimes(1); // Ensure dispatch was called expect(modalActions.openModal).toHaveBeenCalledTimes(1); expect(modalActions.openModal).toHaveBeenCalledWith({ diff --git a/webapp/channels/src/components/channel_header_menu/menu_items/groups.test.tsx b/webapp/channels/src/components/channel_header_menu/menu_items/groups.test.tsx index a0699880f0e..11023b33a1f 100644 --- a/webapp/channels/src/components/channel_header_menu/menu_items/groups.test.tsx +++ b/webapp/channels/src/components/channel_header_menu/menu_items/groups.test.tsx @@ -10,7 +10,7 @@ import AddGroupsToChannelModal from 'components/add_groups_to_channel_modal'; import ChannelGroupsManageModal from 'components/channel_groups_manage_modal'; import {WithTestMenuContext} from 'components/menu/menu_context_test'; -import {renderWithContext, screen, fireEvent} from 'tests/react_testing_utils'; +import {renderWithContext, screen, userEvent} from 'tests/react_testing_utils'; import {ModalIdentifiers} from 'utils/constants'; import {TestHelper} from 'utils/test_helper'; @@ -24,12 +24,9 @@ describe('components/ChannelHeaderMenu/MenuItems/Groups', () => { jest.spyOn(require('react-redux'), 'useDispatch'); }); - afterEach(() => { - jest.clearAllMocks(); - }); const channel = TestHelper.getChannelMock(); - test('renders the component correctly, handle click event for add groups', () => { + test('renders the component correctly, handle click event for add groups', async () => { renderWithContext( @@ -39,7 +36,7 @@ describe('components/ChannelHeaderMenu/MenuItems/Groups', () => { const menuItem = screen.getByText('Add Groups'); expect(menuItem).toBeInTheDocument(); - fireEvent.click(menuItem); // Simulate click on the menu item + await userEvent.click(menuItem); // Simulate click on the menu item expect(useDispatch).toHaveBeenCalledTimes(1); // Ensure dispatch was called expect(modalActions.openModal).toHaveBeenCalledTimes(1); expect(modalActions.openModal).toHaveBeenCalledWith({ @@ -48,7 +45,7 @@ describe('components/ChannelHeaderMenu/MenuItems/Groups', () => { }); }); - test('renders the component correctly, handle click event for manage groups', () => { + test('renders the component correctly, handle click event for manage groups', async () => { renderWithContext( @@ -58,7 +55,7 @@ describe('components/ChannelHeaderMenu/MenuItems/Groups', () => { const menuItemMG = screen.getByText('Manage Groups'); expect(menuItemMG).toBeInTheDocument(); - fireEvent.click(menuItemMG); // Simulate click on the menu item + await userEvent.click(menuItemMG); // Simulate click on the menu item expect(useDispatch).toHaveBeenCalledTimes(1); // Ensure dispatch was called expect(modalActions.openModal).toHaveBeenCalledTimes(1); expect(modalActions.openModal).toHaveBeenCalledWith({ diff --git a/webapp/channels/src/components/channel_header_menu/menu_items/leave_channel.test.tsx b/webapp/channels/src/components/channel_header_menu/menu_items/leave_channel.test.tsx index b3f031ed822..7f14137c08e 100644 --- a/webapp/channels/src/components/channel_header_menu/menu_items/leave_channel.test.tsx +++ b/webapp/channels/src/components/channel_header_menu/menu_items/leave_channel.test.tsx @@ -10,7 +10,7 @@ import * as modalActions from 'actions/views/modals'; import LeaveChannelModal from 'components/leave_channel_modal'; import {WithTestMenuContext} from 'components/menu/menu_context_test'; -import {renderWithContext, screen, fireEvent} from 'tests/react_testing_utils'; +import {renderWithContext, screen, userEvent} from 'tests/react_testing_utils'; import {ModalIdentifiers} from 'utils/constants'; import {TestHelper} from 'utils/test_helper'; @@ -25,11 +25,7 @@ describe('components/ChannelHeaderMenu/MenuItems/LeaveChannelTest', () => { jest.spyOn(require('react-redux'), 'useDispatch'); }); - afterEach(() => { - jest.clearAllMocks(); - }); - - test('renders the component correctly, handle click event correctly for Public Channel', () => { + test('renders the component correctly, handle click event correctly for Public Channel', async () => { const channel = TestHelper.getChannelMock(); renderWithContext( @@ -41,13 +37,13 @@ describe('components/ChannelHeaderMenu/MenuItems/LeaveChannelTest', () => { const menuItem = screen.getByText('Leave Channel'); expect(menuItem).toBeInTheDocument(); - fireEvent.click(menuItem); // Simulate click on the menu item + await userEvent.click(menuItem); // Simulate click on the menu item expect(useDispatch).toHaveBeenCalledTimes(1); // Ensure dispatch was called expect(channelActions.leaveChannel).toHaveBeenCalledTimes(1); // Ensure dispatch was called expect(channelActions.leaveChannel).toHaveBeenCalledWith(channel.id); }); - test('renders the component correctly, handle click event for manage groups', () => { + test('renders the component correctly, handle click event for manage groups', async () => { const channel = TestHelper.getChannelMock({type: 'P'}); renderWithContext( @@ -59,7 +55,7 @@ describe('components/ChannelHeaderMenu/MenuItems/LeaveChannelTest', () => { const menuItemMG = screen.getByText('Leave Channel'); expect(menuItemMG).toBeInTheDocument(); - fireEvent.click(menuItemMG); // Simulate click on the menu item + await userEvent.click(menuItemMG); // Simulate click on the menu item expect(useDispatch).toHaveBeenCalledTimes(1); // Ensure dispatch was called expect(modalActions.openModal).toHaveBeenCalledTimes(1); expect(modalActions.openModal).toHaveBeenCalledWith({ diff --git a/webapp/channels/src/components/channel_header_menu/menu_items/mobile_channel_header_plugins.test.tsx b/webapp/channels/src/components/channel_header_menu/menu_items/mobile_channel_header_plugins.test.tsx index a95781414b7..f1d074d8399 100644 --- a/webapp/channels/src/components/channel_header_menu/menu_items/mobile_channel_header_plugins.test.tsx +++ b/webapp/channels/src/components/channel_header_menu/menu_items/mobile_channel_header_plugins.test.tsx @@ -13,7 +13,7 @@ import * as modalActions from 'actions/views/modals'; import {WithTestMenuContext} from 'components/menu/menu_context_test'; -import {renderWithContext, screen, fireEvent, waitFor} from 'tests/react_testing_utils'; +import {renderWithContext, screen, userEvent, waitFor} from 'tests/react_testing_utils'; import {TestHelper} from 'utils/test_helper'; import MobileChannelHeaderPlugins from './mobile_channel_header_plugins'; @@ -104,7 +104,7 @@ describe('components/ChannelHeaderMenu/MenuItems/MobileChannelHeaderPlugins, wit expect(container.firstChild).toBeNull(); }); - test('renders the component correctly, with one extended component, and handle click event', () => { + test('renders the component correctly, with one extended component, and handle click event', async () => { renderWithContext( { expect(useDispatch).toHaveBeenCalledTimes(1); // Ensure dispatch was called expect(appsActions.handleBindingClick).toHaveBeenCalledTimes(1); @@ -239,7 +239,7 @@ describe('components/ChannelHeaderMenu/MenuItems/MobileChannelHeaderPlugins, wit const menuItem = screen.getByText('App 1 Channel Header'); expect(menuItem).toBeInTheDocument(); - fireEvent.click(menuItem); + await userEvent.click(menuItem); await waitFor(() => { // expect(useDispatch).toHaveBeenCalledTimes(1); // Ensure dispatch was called expect(appsActions.handleBindingClick).toHaveBeenCalledTimes(1); @@ -269,7 +269,7 @@ describe('components/ChannelHeaderMenu/MenuItems/MobileChannelHeaderPlugins, wit const menuItem = screen.getByText('App 1 Channel Header'); expect(menuItem).toBeInTheDocument(); - fireEvent.click(menuItem); + await userEvent.click(menuItem); await waitFor(() => { // expect(useDispatch).toHaveBeenCalledTimes(1); // Ensure dispatch was called expect(appsActions.handleBindingClick).toHaveBeenCalledTimes(1); @@ -277,7 +277,7 @@ describe('components/ChannelHeaderMenu/MenuItems/MobileChannelHeaderPlugins, wit }); }); - test('renders the component correctly, with one extended component, isDropDown false', () => { + test('renders the component correctly, with one extended component, isDropDown false', async () => { const action = jest.fn(); const pluginState = { plugins: { @@ -305,7 +305,7 @@ describe('components/ChannelHeaderMenu/MenuItems/MobileChannelHeaderPlugins, wit ); const button = screen.getByRole('button'); expect(button).toBeInTheDocument(); - fireEvent.click(button); + await userEvent.click(button); expect(action).toHaveBeenCalledTimes(1); }); @@ -328,7 +328,7 @@ describe('components/ChannelHeaderMenu/MenuItems/MobileChannelHeaderPlugins, wit ); const button = screen.getByRole('button'); expect(button).toBeInTheDocument(); - fireEvent.click(button); + await userEvent.click(button); await waitFor(() => { expect(appsActions.handleBindingClick).toHaveBeenCalledTimes(1); }); diff --git a/webapp/channels/src/components/channel_header_menu/menu_items/notification.test.tsx b/webapp/channels/src/components/channel_header_menu/menu_items/notification.test.tsx index 3b2f20d7c29..15f1b3a2654 100644 --- a/webapp/channels/src/components/channel_header_menu/menu_items/notification.test.tsx +++ b/webapp/channels/src/components/channel_header_menu/menu_items/notification.test.tsx @@ -9,7 +9,7 @@ import * as modalActions from 'actions/views/modals'; import ChannelNotificationsModal from 'components/channel_notifications_modal'; import {WithTestMenuContext} from 'components/menu/menu_context_test'; -import {renderWithContext, screen, fireEvent} from 'tests/react_testing_utils'; +import {renderWithContext, screen, userEvent} from 'tests/react_testing_utils'; import {ModalIdentifiers} from 'utils/constants'; import {TestHelper} from 'utils/test_helper'; @@ -23,11 +23,7 @@ describe('components/ChannelHeaderMenu/MenuItems/Notification', () => { jest.spyOn(require('react-redux'), 'useDispatch'); }); - afterEach(() => { - jest.clearAllMocks(); - }); - - test('renders the component correctly, handle click event', () => { + test('renders the component correctly, handle click event', async () => { const channel = TestHelper.getChannelMock(); const user = TestHelper.getUserMock(); @@ -43,7 +39,7 @@ describe('components/ChannelHeaderMenu/MenuItems/Notification', () => { const menuItemMG = screen.getByText('Notification Preferences'); expect(menuItemMG).toBeInTheDocument(); - fireEvent.click(menuItemMG); // Simulate click on the menu item + await userEvent.click(menuItemMG); // Simulate click on the menu item expect(useDispatch).toHaveBeenCalledTimes(1); // Ensure dispatch was called expect(modalActions.openModal).toHaveBeenCalledTimes(1); expect(modalActions.openModal).toHaveBeenCalledWith({ diff --git a/webapp/channels/src/components/channel_header_menu/menu_items/open_member_rhs.test.tsx b/webapp/channels/src/components/channel_header_menu/menu_items/open_member_rhs.test.tsx index 433d84fef7b..c475b8669a8 100644 --- a/webapp/channels/src/components/channel_header_menu/menu_items/open_member_rhs.test.tsx +++ b/webapp/channels/src/components/channel_header_menu/menu_items/open_member_rhs.test.tsx @@ -9,7 +9,7 @@ import * as rhsActions from 'actions/views/rhs'; import {WithTestMenuContext} from 'components/menu/menu_context_test'; -import {renderWithContext, screen, fireEvent} from 'tests/react_testing_utils'; +import {renderWithContext, screen, userEvent} from 'tests/react_testing_utils'; import {RHSStates} from 'utils/constants'; import {TestHelper} from 'utils/test_helper'; @@ -23,11 +23,7 @@ describe('components/ChannelHeaderMenu/MenuItems/OpenMembersRHS', () => { jest.spyOn(require('react-redux'), 'useDispatch'); }); - afterEach(() => { - jest.clearAllMocks(); - }); - - test('renders the component correctly, handles click event, rhs closed', () => { + test('renders the component correctly, handles click event, rhs closed', async () => { const state = { views: { rhs: { @@ -55,13 +51,13 @@ describe('components/ChannelHeaderMenu/MenuItems/OpenMembersRHS', () => { const menuItem = screen.getByText('View Members'); expect(menuItem).toBeInTheDocument(); - fireEvent.click(menuItem); // Simulate click on the menu item + await userEvent.click(menuItem); // Simulate click on the menu item expect(useDispatch).toHaveBeenCalledTimes(1); // Ensure dispatch was called expect(rhsActions.showChannelMembers).toHaveBeenCalledTimes(1); expect(rhsActions.showChannelMembers).toHaveBeenCalledWith(channel.id, false); }); - test('renders the component correctly, handles correct click event, rhs open', () => { + test('renders the component correctly, handles correct click event, rhs open', async () => { const state = { views: { rhs: { @@ -91,7 +87,7 @@ describe('components/ChannelHeaderMenu/MenuItems/OpenMembersRHS', () => { const menuItem = screen.getByText('View Members'); expect(menuItem).toBeInTheDocument(); - fireEvent.click(menuItem); // Simulate click on the menu + await userEvent.click(menuItem); // Simulate click on the menu expect(rhsActions.showChannelMembers).not.toHaveBeenCalled(); }); }); diff --git a/webapp/channels/src/components/channel_header_menu/menu_items/toggle_favorite_channel.test.tsx b/webapp/channels/src/components/channel_header_menu/menu_items/toggle_favorite_channel.test.tsx index 8d2e4b9bdcd..6b89546fbe7 100644 --- a/webapp/channels/src/components/channel_header_menu/menu_items/toggle_favorite_channel.test.tsx +++ b/webapp/channels/src/components/channel_header_menu/menu_items/toggle_favorite_channel.test.tsx @@ -8,7 +8,7 @@ import * as channelActions from 'mattermost-redux/actions/channels'; import {WithTestMenuContext} from 'components/menu/menu_context_test'; -import {renderWithContext, screen, fireEvent} from 'tests/react_testing_utils'; +import {renderWithContext, screen, userEvent} from 'tests/react_testing_utils'; import {TestHelper} from 'utils/test_helper'; import ToggleFavoriteChannel from './toggle_favorite_channel'; @@ -22,11 +22,7 @@ describe('components/ChannelHeaderMenu/MenuItems/ToggleFavoriteChannel', () => { jest.spyOn(require('react-redux'), 'useDispatch'); }); - afterEach(() => { - jest.clearAllMocks(); - }); - - test('renders the component correctly, handles correct click event, is favorite false', () => { + test('renders the component correctly, handles correct click event, is favorite false', async () => { renderWithContext( { const menuItem = screen.getByText('Add to Favorites'); expect(menuItem).toBeInTheDocument(); - fireEvent.click(menuItem); // Simulate click on the menu item + await userEvent.click(menuItem); // Simulate click on the menu item // expect(useDispatch).toHaveBeenCalledTimes(1); // Ensure dispatch was called expect(channelActions.favoriteChannel).toHaveBeenCalledTimes(1); expect(channelActions.favoriteChannel).toHaveBeenCalledWith(channel.id); }); - test('renders the component correctly, handles correct click event, is favorite true', () => { + test('renders the component correctly, handles correct click event, is favorite true', async () => { renderWithContext( { const menuItem = screen.getByText('Remove from Favorites'); expect(menuItem).toBeInTheDocument(); - fireEvent.click(menuItem); // Simulate click on the menu item + await userEvent.click(menuItem); // Simulate click on the menu item expect(useDispatch).toHaveBeenCalledTimes(1); // Ensure dispatch was called expect(channelActions.unfavoriteChannel).toHaveBeenCalledTimes(1); }); diff --git a/webapp/channels/src/components/channel_header_menu/menu_items/toggle_info.test.tsx b/webapp/channels/src/components/channel_header_menu/menu_items/toggle_info.test.tsx index cf09faa08fc..1ec63ad17cb 100644 --- a/webapp/channels/src/components/channel_header_menu/menu_items/toggle_info.test.tsx +++ b/webapp/channels/src/components/channel_header_menu/menu_items/toggle_info.test.tsx @@ -8,7 +8,7 @@ import * as rhsActions from 'actions/views/rhs'; import {WithTestMenuContext} from 'components/menu/menu_context_test'; -import {renderWithContext, screen, fireEvent} from 'tests/react_testing_utils'; +import {renderWithContext, screen, userEvent} from 'tests/react_testing_utils'; import {RHSStates} from 'utils/constants'; import {TestHelper} from 'utils/test_helper'; @@ -22,11 +22,7 @@ describe('components/ChannelHeaderMenu/MenuItems/ToggleInfo', () => { jest.spyOn(require('react-redux'), 'useDispatch'); }); - afterEach(() => { - jest.clearAllMocks(); - }); - - test('renders the component correctly, handles click event, rhs closed', () => { + test('renders the component correctly, handles click event, rhs closed', async () => { const state = { views: { rhs: { @@ -45,13 +41,13 @@ describe('components/ChannelHeaderMenu/MenuItems/ToggleInfo', () => { const menuItem = screen.getByText('View Info'); expect(menuItem).toBeInTheDocument(); - fireEvent.click(menuItem); // Simulate click on the menu item + await userEvent.click(menuItem); // Simulate click on the menu item // expect(useDispatch).toHaveBeenCalledTimes(1); // Ensure dispatch was called expect(rhsActions.showChannelInfo).toHaveBeenCalledTimes(1); expect(rhsActions.showChannelInfo).toHaveBeenCalledWith(channel.id); }); - test('renders the component correctly, handles correct click event, rhs open', () => { + test('renders the component correctly, handles correct click event, rhs open', async () => { const state = { views: { rhs: { @@ -72,7 +68,7 @@ describe('components/ChannelHeaderMenu/MenuItems/ToggleInfo', () => { const menuItem = screen.getByText('Close Info'); expect(menuItem).toBeInTheDocument(); - fireEvent.click(menuItem); // Simulate click on the menu item + await userEvent.click(menuItem); // Simulate click on the menu item expect(useDispatch).toHaveBeenCalledTimes(1); // Ensure dispatch was called expect(rhsActions.closeRightHandSide).toHaveBeenCalledTimes(1); }); diff --git a/webapp/channels/src/components/channel_header_menu/menu_items/toggle_mute_channel.test.tsx b/webapp/channels/src/components/channel_header_menu/menu_items/toggle_mute_channel.test.tsx index c37c33a9168..4b1585bf814 100644 --- a/webapp/channels/src/components/channel_header_menu/menu_items/toggle_mute_channel.test.tsx +++ b/webapp/channels/src/components/channel_header_menu/menu_items/toggle_mute_channel.test.tsx @@ -8,7 +8,7 @@ import * as channelActions from 'mattermost-redux/actions/channels'; import {WithTestMenuContext} from 'components/menu/menu_context_test'; -import {renderWithContext, screen, fireEvent} from 'tests/react_testing_utils'; +import {renderWithContext, screen, userEvent} from 'tests/react_testing_utils'; import {NotificationLevels} from 'utils/constants'; import {TestHelper} from 'utils/test_helper'; @@ -23,11 +23,7 @@ describe('components/ChannelHeaderMenu/MenuItems/ToggleMuteChannel', () => { jest.spyOn(require('react-redux'), 'useDispatch'); }); - afterEach(() => { - jest.clearAllMocks(); - }); - - test('renders the component correctly, public channel, not muted', () => { + test('renders the component correctly, public channel, not muted', async () => { renderWithContext( { const menuItem = screen.getByText('Mute Channel'); expect(menuItem).toBeInTheDocument(); - fireEvent.click(menuItem); // Simulate click on the menu item + await userEvent.click(menuItem); // Simulate click on the menu item expect(useDispatch).toHaveBeenCalledTimes(1); // Ensure dispatch was called expect(channelActions.updateChannelNotifyProps).toHaveBeenCalledTimes(1); expect(channelActions.updateChannelNotifyProps).toHaveBeenCalledWith( @@ -51,7 +47,7 @@ describe('components/ChannelHeaderMenu/MenuItems/ToggleMuteChannel', () => { ); }); - test('renders the component correctly, public channel, muted', () => { + test('renders the component correctly, public channel, muted', async () => { renderWithContext( { const menuItem = screen.getByText('Unmute Channel'); expect(menuItem).toBeInTheDocument(); - fireEvent.click(menuItem); // Simulate click on the menu item + await userEvent.click(menuItem); // Simulate click on the menu item expect(useDispatch).toHaveBeenCalledTimes(1); // Ensure dispatch was called expect(channelActions.updateChannelNotifyProps).toHaveBeenCalledTimes(1); expect(channelActions.updateChannelNotifyProps).toHaveBeenCalledWith( @@ -75,7 +71,7 @@ describe('components/ChannelHeaderMenu/MenuItems/ToggleMuteChannel', () => { ); }); - test('renders the component correctly, dm channel, not muted', () => { + test('renders the component correctly, dm channel, not muted', async () => { const channel = TestHelper.getChannelMock({type: 'D'}); renderWithContext( @@ -90,7 +86,7 @@ describe('components/ChannelHeaderMenu/MenuItems/ToggleMuteChannel', () => { const menuItem = screen.getByText('Mute'); expect(menuItem).toBeInTheDocument(); - fireEvent.click(menuItem); // Simulate click on the menu item + await userEvent.click(menuItem); // Simulate click on the menu item expect(useDispatch).toHaveBeenCalledTimes(1); // Ensure dispatch was called expect(channelActions.updateChannelNotifyProps).toHaveBeenCalledTimes(1); expect(channelActions.updateChannelNotifyProps).toHaveBeenCalledWith( @@ -99,7 +95,7 @@ describe('components/ChannelHeaderMenu/MenuItems/ToggleMuteChannel', () => { {mark_unread: NotificationLevels.MENTION}, ); }); - test('renders the component correctly, dm channel, muted', () => { + test('renders the component correctly, dm channel, muted', async () => { const channel = TestHelper.getChannelMock({type: 'D'}); renderWithContext( @@ -114,7 +110,7 @@ describe('components/ChannelHeaderMenu/MenuItems/ToggleMuteChannel', () => { const menuItem = screen.getByText('Unmute'); expect(menuItem).toBeInTheDocument(); - fireEvent.click(menuItem); // Simulate click on the menu item + await userEvent.click(menuItem); // Simulate click on the menu item expect(useDispatch).toHaveBeenCalledTimes(1); // Ensure dispatch was called expect(channelActions.updateChannelNotifyProps).toHaveBeenCalledTimes(1); expect(channelActions.updateChannelNotifyProps).toHaveBeenCalledWith( diff --git a/webapp/channels/src/components/channel_header_menu/menu_items/unarchive_channel.test.tsx b/webapp/channels/src/components/channel_header_menu/menu_items/unarchive_channel.test.tsx index 172405d03a3..a657a86ba77 100644 --- a/webapp/channels/src/components/channel_header_menu/menu_items/unarchive_channel.test.tsx +++ b/webapp/channels/src/components/channel_header_menu/menu_items/unarchive_channel.test.tsx @@ -9,7 +9,7 @@ import * as modalActions from 'actions/views/modals'; import {WithTestMenuContext} from 'components/menu/menu_context_test'; import UnarchiveChannelModal from 'components/unarchive_channel_modal'; -import {renderWithContext, screen, fireEvent} from 'tests/react_testing_utils'; +import {renderWithContext, screen, userEvent} from 'tests/react_testing_utils'; import {ModalIdentifiers} from 'utils/constants'; import {TestHelper} from 'utils/test_helper'; @@ -21,11 +21,7 @@ describe('components/ChannelHeaderMenu/MenuItems/UnarchiveChannel', () => { jest.spyOn(require('react-redux'), 'useDispatch'); }); - afterEach(() => { - jest.clearAllMocks(); - }); - - test('renders the component correctly, handle click event', () => { + test('renders the component correctly, handle click event', async () => { const channel = TestHelper.getChannelMock(); renderWithContext( @@ -37,7 +33,7 @@ describe('components/ChannelHeaderMenu/MenuItems/UnarchiveChannel', () => { const menuItem = screen.getByText('Unarchive Channel'); expect(menuItem).toBeInTheDocument(); - fireEvent.click(menuItem); // Simulate click on the menu item + await userEvent.click(menuItem); // Simulate click on the menu item expect(useDispatch).toHaveBeenCalledTimes(1); // Ensure dispatch was called expect(modalActions.openModal).toHaveBeenCalledTimes(1); expect(modalActions.openModal).toHaveBeenCalledWith({ diff --git a/webapp/channels/src/components/channel_header_menu/menu_items/view_pinned_posts.test.tsx b/webapp/channels/src/components/channel_header_menu/menu_items/view_pinned_posts.test.tsx index 2e205ba9393..1903b4834cf 100644 --- a/webapp/channels/src/components/channel_header_menu/menu_items/view_pinned_posts.test.tsx +++ b/webapp/channels/src/components/channel_header_menu/menu_items/view_pinned_posts.test.tsx @@ -8,7 +8,7 @@ import * as rhsActions from 'actions/views/rhs'; import {WithTestMenuContext} from 'components/menu/menu_context_test'; -import {renderWithContext, screen, fireEvent} from 'tests/react_testing_utils'; +import {renderWithContext, screen, userEvent} from 'tests/react_testing_utils'; import {RHSStates} from 'utils/constants'; import {TestHelper} from 'utils/test_helper'; @@ -22,11 +22,7 @@ describe('components/ChannelHeaderMenu/MenuItems/ViewPinnedPosts', () => { jest.spyOn(require('react-redux'), 'useDispatch'); }); - afterEach(() => { - jest.clearAllMocks(); - }); - - test('renders the component correctly, handles correct click event', () => { + test('renders the component correctly, handles correct click event', async () => { const state = { views: { rhs: { @@ -45,13 +41,13 @@ describe('components/ChannelHeaderMenu/MenuItems/ViewPinnedPosts', () => { const menuItem = screen.getByText('View Pinned Posts'); expect(menuItem).toBeInTheDocument(); - fireEvent.click(menuItem); // Simulate click on the menu item + await userEvent.click(menuItem); // Simulate click on the menu item expect(useDispatch).toHaveBeenCalledTimes(1); // Ensure dispatch was called expect(rhsActions.showPinnedPosts).toHaveBeenCalledTimes(1); expect(rhsActions.showPinnedPosts).toHaveBeenCalledWith(channel.id); }); - test('renders the component correctly, handles correct click event', () => { + test('renders the component correctly, handles correct click event', async () => { const state = { views: { rhs: { @@ -70,7 +66,7 @@ describe('components/ChannelHeaderMenu/MenuItems/ViewPinnedPosts', () => { const menuItem = screen.getByText('View Pinned Posts'); expect(menuItem).toBeInTheDocument(); - fireEvent.click(menuItem); // Simulate click on the menu item + await userEvent.click(menuItem); // Simulate click on the menu item expect(useDispatch).toHaveBeenCalledTimes(1); // Ensure dispatch was called expect(rhsActions.closeRightHandSide).toHaveBeenCalledTimes(1); }); diff --git a/webapp/channels/src/components/channel_info_rhs/components/editable_area.test.tsx b/webapp/channels/src/components/channel_info_rhs/components/editable_area.test.tsx index cb2fbead5cd..891faad9114 100644 --- a/webapp/channels/src/components/channel_info_rhs/components/editable_area.test.tsx +++ b/webapp/channels/src/components/channel_info_rhs/components/editable_area.test.tsx @@ -3,7 +3,7 @@ import React from 'react'; -import {fireEvent, renderWithContext, screen} from 'tests/react_testing_utils'; +import {renderWithContext, screen, userEvent} from 'tests/react_testing_utils'; import EditableArea from './editable_area'; @@ -33,7 +33,7 @@ describe('channel_info_rhs/components/editable_area', () => { ); expect(screen.getByLabelText('Edit')).toBeInTheDocument(); - fireEvent.click(screen.getByLabelText('Edit')); + await userEvent.click(screen.getByLabelText('Edit')); expect(mockOnEdit).toHaveBeenCalled(); }); @@ -64,10 +64,10 @@ describe('channel_info_rhs/components/editable_area', () => { expect(screen.getByText('No content')).toBeInTheDocument(); // We should be able to click on the text... - fireEvent.click(screen.getByText('No content')); + await userEvent.click(screen.getByText('No content')); // ... or the Edit icon - fireEvent.click(screen.getByLabelText('Edit')); + await userEvent.click(screen.getByLabelText('Edit')); expect(mockOnEdit).toHaveBeenCalledTimes(2); }); }); diff --git a/webapp/channels/src/components/channel_info_rhs/header.test.tsx b/webapp/channels/src/components/channel_info_rhs/header.test.tsx index c041023b11a..542770a9785 100644 --- a/webapp/channels/src/components/channel_info_rhs/header.test.tsx +++ b/webapp/channels/src/components/channel_info_rhs/header.test.tsx @@ -5,7 +5,7 @@ import React from 'react'; import type {Channel} from '@mattermost/types/channels'; -import {fireEvent, renderWithContext, screen} from 'tests/react_testing_utils'; +import {renderWithContext, screen, userEvent} from 'tests/react_testing_utils'; import Header from './header'; @@ -22,7 +22,7 @@ describe('channel_info_rhs/header', () => { expect(screen.getByText('my channel title')).toBeInTheDocument(); }); - test('should call onClose when clicking on the close icon', () => { + test('should call onClose when clicking on the close icon', async () => { const onClose = jest.fn(); renderWithContext( @@ -34,11 +34,11 @@ describe('channel_info_rhs/header', () => { />, ); - fireEvent.click(screen.getByLabelText('Close Sidebar Icon')); + await userEvent.click(screen.getByLabelText('Close Sidebar Icon')); expect(onClose).toHaveBeenCalled(); }); - test('should call onClose when clicking on the back icon', () => { + test('should call onClose when clicking on the back icon', async () => { const onClose = jest.fn(); renderWithContext( @@ -50,7 +50,7 @@ describe('channel_info_rhs/header', () => { />, ); - fireEvent.click(screen.getByLabelText('Back Icon')); + await userEvent.click(screen.getByLabelText('Back Icon')); expect(onClose).toHaveBeenCalled(); }); diff --git a/webapp/channels/src/components/channel_info_rhs/menu.test.tsx b/webapp/channels/src/components/channel_info_rhs/menu.test.tsx index 293bb271b77..73e1007978d 100644 --- a/webapp/channels/src/components/channel_info_rhs/menu.test.tsx +++ b/webapp/channels/src/components/channel_info_rhs/menu.test.tsx @@ -7,9 +7,9 @@ import type {Channel, ChannelStats} from '@mattermost/types/channels'; import { act, - fireEvent, renderWithContext, screen, + userEvent, } from 'tests/react_testing_utils'; import Constants from 'utils/constants'; @@ -54,7 +54,7 @@ describe('channel_info_rhs/menu', () => { }); expect(screen.getByText('Notification Preferences')).toBeInTheDocument(); - fireEvent.click(screen.getByText('Notification Preferences')); + await userEvent.click(screen.getByText('Notification Preferences')); expect(props.actions.openNotificationSettings).toHaveBeenCalled(); }); @@ -115,7 +115,7 @@ describe('channel_info_rhs/menu', () => { expect(fileItem).toBeInTheDocument(); expect(fileItem.parentElement).toHaveTextContent('3'); - fireEvent.click(fileItem); + await userEvent.click(fileItem); expect(props.actions.showChannelFiles).toHaveBeenCalled(); }); @@ -137,7 +137,7 @@ describe('channel_info_rhs/menu', () => { expect(fileItem).toBeInTheDocument(); expect(fileItem.parentElement).toHaveTextContent('12'); - fireEvent.click(fileItem); + await userEvent.click(fileItem); expect(props.actions.showPinnedPosts).toHaveBeenCalled(); }); @@ -159,7 +159,7 @@ describe('channel_info_rhs/menu', () => { expect(membersItem).toBeInTheDocument(); expect(membersItem.parentElement).toHaveTextContent('32'); - fireEvent.click(membersItem); + await userEvent.click(membersItem); expect(props.actions.showChannelMembers).toHaveBeenCalled(); }); diff --git a/webapp/channels/src/components/channel_info_rhs/top_buttons.test.tsx b/webapp/channels/src/components/channel_info_rhs/top_buttons.test.tsx index 1cc0ca76695..0bb41731b3b 100644 --- a/webapp/channels/src/components/channel_info_rhs/top_buttons.test.tsx +++ b/webapp/channels/src/components/channel_info_rhs/top_buttons.test.tsx @@ -3,7 +3,7 @@ import React from 'react'; -import {fireEvent, renderWithContext, screen} from 'tests/react_testing_utils'; +import {renderWithContext, screen, userEvent} from 'tests/react_testing_utils'; import Constants from 'utils/constants'; import TopButtons from './top_buttons'; @@ -35,7 +35,7 @@ describe('channel_info_rhs/top_buttons', () => { }, }; - test('should display and toggle Favorite', () => { + test('should display and toggle Favorite', async () => { const toggleFavorite = jest.fn(); // Favorite to Favorited @@ -54,7 +54,7 @@ describe('channel_info_rhs/top_buttons', () => { ); expect(screen.getByText('Favorite')).toBeInTheDocument(); - fireEvent.click(screen.getByText('Favorite')); + await userEvent.click(screen.getByText('Favorite')); expect(toggleFavorite).toHaveBeenCalled(); // Favorited to Favorite @@ -67,11 +67,11 @@ describe('channel_info_rhs/top_buttons', () => { ); expect(screen.getByText('Favorited')).toBeInTheDocument(); - fireEvent.click(screen.getByText('Favorited')); + await userEvent.click(screen.getByText('Favorited')); expect(toggleFavorite).toHaveBeenCalled(); }); - test('should display and toggle Mute', () => { + test('should display and toggle Mute', async () => { const toggleMute = jest.fn(); // Mute to Muted @@ -90,7 +90,7 @@ describe('channel_info_rhs/top_buttons', () => { ); expect(screen.getByText('Mute')).toBeInTheDocument(); - fireEvent.click(screen.getByText('Mute')); + await userEvent.click(screen.getByText('Mute')); expect(toggleMute).toHaveBeenCalled(); // Muted to Mute @@ -103,11 +103,11 @@ describe('channel_info_rhs/top_buttons', () => { ); expect(screen.getByText('Muted')).toBeInTheDocument(); - fireEvent.click(screen.getByText('Muted')); + await userEvent.click(screen.getByText('Muted')); expect(toggleMute).toHaveBeenCalled(); }); - test('should display and active call Add People', () => { + test('should display and active call Add People', async () => { const addPeople = jest.fn(); const testProps = { @@ -125,7 +125,7 @@ describe('channel_info_rhs/top_buttons', () => { ); expect(screen.getByText('Add People')).toBeInTheDocument(); - fireEvent.click(screen.getByText('Add People')); + await userEvent.click(screen.getByText('Add People')); expect(addPeople).toHaveBeenCalled(); }); test('should not Add People in DM', () => { @@ -158,7 +158,7 @@ describe('channel_info_rhs/top_buttons', () => { expect(screen.queryByText('Add People')).not.toBeInTheDocument(); }); - test('can copy link', () => { + test('can copy link', async () => { renderWithContext( { ); expect(screen.getByText('Copy Link')).toBeInTheDocument(); - fireEvent.click(screen.getByText('Copy Link')); + await userEvent.click(screen.getByText('Copy Link')); expect(mockOnCopyTextClick).toHaveBeenCalled(); }); diff --git a/webapp/channels/src/components/channel_invite_modal/channel_invite_modal.test.tsx b/webapp/channels/src/components/channel_invite_modal/channel_invite_modal.test.tsx index c512bcc4227..5932707578a 100644 --- a/webapp/channels/src/components/channel_invite_modal/channel_invite_modal.test.tsx +++ b/webapp/channels/src/components/channel_invite_modal/channel_invite_modal.test.tsx @@ -1,8 +1,6 @@ // Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved. // See LICENSE.txt for license information. -import {fireEvent, screen, waitFor} from '@testing-library/react'; -import userEvent from '@testing-library/user-event'; import React from 'react'; import {GenericModal} from '@mattermost/components'; @@ -17,7 +15,7 @@ import ChannelInviteModal from 'components/channel_invite_modal/channel_invite_m import type {Value} from 'components/multiselect/multiselect'; import {shallowWithIntl} from 'tests/helpers/intl-test-helper'; -import {act, renderWithContext} from 'tests/react_testing_utils'; +import {act, renderWithContext, screen, userEvent, waitFor} from 'tests/react_testing_utils'; type UserProfileValue = Value & UserProfile; @@ -430,9 +428,8 @@ describe('components/channel_invite_modal', () => { const input = screen.getByRole('combobox', {name: /search for people/i}); // Directly trigger the change event with a value that has spaces - act(() => { - fireEvent.change(input, {target: {value: ' something '}}); - }); + await userEvent.clear(input); + await userEvent.type(input, ' something '); // Verify the search was called with the trimmed term await waitFor(() => { diff --git a/webapp/channels/src/components/channel_members_rhs/action_bar.test.tsx b/webapp/channels/src/components/channel_members_rhs/action_bar.test.tsx index a6a9977beb2..6c75c7b3b8c 100644 --- a/webapp/channels/src/components/channel_members_rhs/action_bar.test.tsx +++ b/webapp/channels/src/components/channel_members_rhs/action_bar.test.tsx @@ -3,7 +3,7 @@ import React from 'react'; -import {fireEvent, renderWithContext, screen} from 'tests/react_testing_utils'; +import {renderWithContext, screen, userEvent} from 'tests/react_testing_utils'; import Constants from 'utils/constants'; import ActionBar from './action_bar'; @@ -42,7 +42,7 @@ describe('channel_members_rhs/action_bar', () => { expect(screen.getByText(`${testProps.membersCount} members`)).toBeInTheDocument(); }); - test('should display Add button', () => { + test('should display Add button', async () => { const testProps: Props = {...actionBarDefaultProps}; renderWithContext( @@ -52,7 +52,7 @@ describe('channel_members_rhs/action_bar', () => { ); expect(screen.getByText('Add')).toBeInTheDocument(); - fireEvent.click(screen.getByText('Add')); + await userEvent.click(screen.getByText('Add')); expect(testProps.actions.inviteMembers).toHaveBeenCalled(); }); @@ -68,7 +68,7 @@ describe('channel_members_rhs/action_bar', () => { expect(screen.queryByText('Add')).not.toBeInTheDocument(); }); - test('should display Manage', () => { + test('should display Manage', async () => { const testProps: Props = {...actionBarDefaultProps}; renderWithContext( @@ -78,11 +78,11 @@ describe('channel_members_rhs/action_bar', () => { ); expect(screen.getByText('Manage')).toBeInTheDocument(); - fireEvent.click(screen.getByText('Manage')); + await userEvent.click(screen.getByText('Manage')); expect(testProps.actions.startEditing).toHaveBeenCalled(); }); - test('should display Done', () => { + test('should display Done', async () => { const testProps: Props = { ...actionBarDefaultProps, editing: true, @@ -95,7 +95,7 @@ describe('channel_members_rhs/action_bar', () => { ); expect(screen.getByText('Done')).toBeInTheDocument(); - fireEvent.click(screen.getByText('Done')); + await userEvent.click(screen.getByText('Done')); expect(testProps.actions.stopEditing).toHaveBeenCalled(); }); diff --git a/webapp/channels/src/components/channel_members_rhs/member.test.tsx b/webapp/channels/src/components/channel_members_rhs/member.test.tsx index 5963495f9bf..ccca8699b7f 100644 --- a/webapp/channels/src/components/channel_members_rhs/member.test.tsx +++ b/webapp/channels/src/components/channel_members_rhs/member.test.tsx @@ -1,13 +1,12 @@ // Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved. // See LICENSE.txt for license information. -import userEvent from '@testing-library/user-event'; import React from 'react'; import type {ChannelType} from '@mattermost/types/channels'; import type {UserProfile} from '@mattermost/types/users'; -import {renderWithContext, screen, waitFor} from 'tests/react_testing_utils'; +import {renderWithContext, screen, waitFor, userEvent} from 'tests/react_testing_utils'; import Member from './member'; import type {ChannelMember} from './member_list'; @@ -256,6 +255,5 @@ describe('components/channel_members_rhs/Member', () => { afterEach(() => { jest.clearAllTimers(); jest.useRealTimers(); - jest.clearAllMocks(); }); }); diff --git a/webapp/channels/src/components/channel_notifications_modal/channel_notifications_modal.test.tsx b/webapp/channels/src/components/channel_notifications_modal/channel_notifications_modal.test.tsx index 581a55e7e5b..4810304a2f5 100644 --- a/webapp/channels/src/components/channel_notifications_modal/channel_notifications_modal.test.tsx +++ b/webapp/channels/src/components/channel_notifications_modal/channel_notifications_modal.test.tsx @@ -1,8 +1,6 @@ // Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved. // See LICENSE.txt for license information. -import {screen, waitFor} from '@testing-library/react'; -import userEvent from '@testing-library/user-event'; import React from 'react'; import type {ChannelMembership} from '@mattermost/types/channels'; @@ -11,7 +9,7 @@ import type {UserNotifyProps} from '@mattermost/types/users'; import ChannelNotificationsModal, {createChannelNotifyPropsFromSelectedSettings, getInitialValuesOfChannelNotifyProps, areDesktopAndMobileSettingsDifferent} from 'components/channel_notifications_modal/channel_notifications_modal'; import type {Props} from 'components/channel_notifications_modal/channel_notifications_modal'; -import {renderWithContext} from 'tests/react_testing_utils'; +import {renderWithContext, screen, waitFor, userEvent} from 'tests/react_testing_utils'; import {DesktopSound, IgnoreChannelMentions, NotificationLevels} from 'utils/constants'; import {DesktopNotificationSounds, convertDesktopSoundNotifyPropFromUserToDesktop} from 'utils/notification_sounds'; import {TestHelper} from 'utils/test_helper'; diff --git a/webapp/channels/src/components/channel_settings_modal/channel_activity_warning_modal.test.tsx b/webapp/channels/src/components/channel_settings_modal/channel_activity_warning_modal.test.tsx index 52ad2e369a6..91cfdd33c54 100644 --- a/webapp/channels/src/components/channel_settings_modal/channel_activity_warning_modal.test.tsx +++ b/webapp/channels/src/components/channel_settings_modal/channel_activity_warning_modal.test.tsx @@ -1,11 +1,9 @@ // Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved. // See LICENSE.txt for license information. -import {screen} from '@testing-library/react'; -import userEvent from '@testing-library/user-event'; import React from 'react'; -import {renderWithContext} from 'tests/react_testing_utils'; +import {renderWithContext, screen, userEvent} from 'tests/react_testing_utils'; import ChannelActivityWarningModal from './channel_activity_warning_modal'; @@ -17,10 +15,6 @@ describe('ChannelActivityWarningModal', () => { channelName: 'test-channel', }; - beforeEach(() => { - jest.clearAllMocks(); - }); - test('should render modal when isOpen is true', () => { renderWithContext( , diff --git a/webapp/channels/src/components/channel_settings_modal/channel_settings_access_rules_activity_warning.test.tsx b/webapp/channels/src/components/channel_settings_modal/channel_settings_access_rules_activity_warning.test.tsx index edf2ce6aad7..cca7eb51f37 100644 --- a/webapp/channels/src/components/channel_settings_modal/channel_settings_access_rules_activity_warning.test.tsx +++ b/webapp/channels/src/components/channel_settings_modal/channel_settings_access_rules_activity_warning.test.tsx @@ -185,8 +185,6 @@ describe('ChannelSettingsAccessRulesTab - Activity Warning Integration', () => { }; beforeEach(() => { - jest.clearAllMocks(); - // Set up default mock implementations mockUseChannelAccessControlActions.mockReturnValue(mockActions); mockUseChannelSystemPolicies.mockReturnValue({ diff --git a/webapp/channels/src/components/channel_settings_modal/channel_settings_archive_tab.test.tsx b/webapp/channels/src/components/channel_settings_modal/channel_settings_archive_tab.test.tsx index 0672f56d27c..f5a268350dc 100644 --- a/webapp/channels/src/components/channel_settings_modal/channel_settings_archive_tab.test.tsx +++ b/webapp/channels/src/components/channel_settings_modal/channel_settings_archive_tab.test.tsx @@ -1,8 +1,6 @@ // Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved. // See LICENSE.txt for license information. -import {screen, waitFor} from '@testing-library/react'; -import userEvent from '@testing-library/user-event'; import React from 'react'; import type {Team} from '@mattermost/types/teams'; @@ -11,7 +9,7 @@ import * as teams from 'mattermost-redux/selectors/entities/teams'; import * as channelActions from 'actions/views/channel'; -import {renderWithContext} from 'tests/react_testing_utils'; +import {renderWithContext, screen, userEvent, waitFor} from 'tests/react_testing_utils'; import {TestHelper} from 'utils/test_helper'; import ChannelSettingsArchiveTab from './channel_settings_archive_tab'; @@ -49,8 +47,6 @@ const baseProps = { describe('ChannelSettingsArchiveTab', () => { const {getHistory} = require('utils/browser_history'); beforeEach(() => { - jest.clearAllMocks(); - jest.spyOn(teams, 'getCurrentTeam').mockReturnValue({ id: 'team1', name: 'team-name', diff --git a/webapp/channels/src/components/channel_settings_modal/channel_settings_configuration_tab.test.tsx b/webapp/channels/src/components/channel_settings_modal/channel_settings_configuration_tab.test.tsx index 0af933044fb..0fee7c555d6 100644 --- a/webapp/channels/src/components/channel_settings_modal/channel_settings_configuration_tab.test.tsx +++ b/webapp/channels/src/components/channel_settings_modal/channel_settings_configuration_tab.test.tsx @@ -1,11 +1,9 @@ // Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved. // See LICENSE.txt for license information. -import {screen} from '@testing-library/react'; -import userEvent from '@testing-library/user-event'; import React from 'react'; -import {renderWithContext} from 'tests/react_testing_utils'; +import {renderWithContext, screen, userEvent} from 'tests/react_testing_utils'; import {TestHelper} from 'utils/test_helper'; import ChannelSettingsConfigurationTab from './channel_settings_configuration_tab'; @@ -66,10 +64,6 @@ const baseProps = { }; describe('ChannelSettingsConfigurationTab', () => { - beforeEach(() => { - jest.clearAllMocks(); - }); - it('should render with the correct initial values when banner is disabled', () => { renderWithContext(); diff --git a/webapp/channels/src/components/channel_settings_modal/channel_settings_info_tab.test.tsx b/webapp/channels/src/components/channel_settings_modal/channel_settings_info_tab.test.tsx index 4b7adf94648..6ab2e224606 100644 --- a/webapp/channels/src/components/channel_settings_modal/channel_settings_info_tab.test.tsx +++ b/webapp/channels/src/components/channel_settings_modal/channel_settings_info_tab.test.tsx @@ -1,13 +1,11 @@ // Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved. // See LICENSE.txt for license information. -import {act, screen} from '@testing-library/react'; -import userEvent from '@testing-library/user-event'; import React from 'react'; import type {ChannelType} from '@mattermost/types/channels'; -import {renderWithContext} from 'tests/react_testing_utils'; +import {act, renderWithContext, screen, userEvent} from 'tests/react_testing_utils'; import {TestHelper} from 'utils/test_helper'; import ChannelSettingsInfoTab from './channel_settings_info_tab'; @@ -128,7 +126,6 @@ const baseProps = { describe('ChannelSettingsInfoTab', () => { beforeEach(() => { - jest.clearAllMocks(); mockChannelPropertiesPermission = true; mockConvertToPublicPermission = true; mockConvertToPrivatePermission = true; diff --git a/webapp/channels/src/components/channel_settings_modal/channel_settings_modal.test.tsx b/webapp/channels/src/components/channel_settings_modal/channel_settings_modal.test.tsx index 89c2071b1af..9aa9e1a1c95 100644 --- a/webapp/channels/src/components/channel_settings_modal/channel_settings_modal.test.tsx +++ b/webapp/channels/src/components/channel_settings_modal/channel_settings_modal.test.tsx @@ -1,13 +1,11 @@ // Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved. // See LICENSE.txt for license information. -import {screen, waitFor} from '@testing-library/react'; -import userEvent from '@testing-library/user-event'; import React from 'react'; import {General} from 'mattermost-redux/constants'; -import {renderWithContext} from 'tests/react_testing_utils'; +import {renderWithContext, screen, waitFor, userEvent} from 'tests/react_testing_utils'; import {TestHelper} from 'utils/test_helper'; import ChannelSettingsModal from './channel_settings_modal'; @@ -166,7 +164,6 @@ describe('ChannelSettingsModal', () => { } beforeEach(() => { - jest.clearAllMocks(); mockPrivateChannelPermission = true; mockPublicChannelPermission = true; mockManageChannelAccessRulesPermission = false; // Default to no access rules permission @@ -518,10 +515,6 @@ describe('ChannelSettingsModal', () => { }); describe('warn-once modal closing behavior', () => { - beforeEach(() => { - jest.clearAllMocks(); - }); - it('should close immediately when no unsaved changes exist', async () => { renderWithContext(, makeTestState()); diff --git a/webapp/channels/src/components/cloud_preview_modal/cloud_preview_modal_controller.test.tsx b/webapp/channels/src/components/cloud_preview_modal/cloud_preview_modal_controller.test.tsx index 2d5fb1fc232..91045358872 100644 --- a/webapp/channels/src/components/cloud_preview_modal/cloud_preview_modal_controller.test.tsx +++ b/webapp/channels/src/components/cloud_preview_modal/cloud_preview_modal_controller.test.tsx @@ -1,14 +1,13 @@ // Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved. // See LICENSE.txt for license information. -import {fireEvent, screen, waitFor} from '@testing-library/react'; import React from 'react'; import * as reactRedux from 'react-redux'; import type {Subscription, PreviewModalContentData} from '@mattermost/types/cloud'; import type {TeamType} from '@mattermost/types/teams'; -import {renderWithContext} from 'tests/react_testing_utils'; +import {renderWithContext, screen, userEvent, waitFor} from 'tests/react_testing_utils'; import CloudPreviewModal from './cloud_preview_modal_controller'; import {modalContent} from './preview_modal_content_data'; @@ -258,13 +257,13 @@ describe('CloudPreviewModal', () => { // Click close button const closeButton = screen.getByText('Close'); - fireEvent.click(closeButton); + await userEvent.click(closeButton); // Check that dispatch was called (savePreferences action) expect(dummyDispatch).toHaveBeenCalled(); }); - it('should reset preference and reopen modal when FAB is clicked', () => { + it('should reset preference and reopen modal when FAB is clicked', async () => { const dummyDispatch = jest.fn(); useDispatchMock.mockReturnValue(dummyDispatch); @@ -280,7 +279,7 @@ describe('CloudPreviewModal', () => { // Click the FAB button const button = fabButton.querySelector('button'); expect(button).toBeInTheDocument(); - fireEvent.click(button!); + await userEvent.click(button!); // Check that dispatch was called to reset the preference expect(dummyDispatch).toHaveBeenCalled(); diff --git a/webapp/channels/src/components/cloud_preview_modal/preview_modal_content.test.tsx b/webapp/channels/src/components/cloud_preview_modal/preview_modal_content.test.tsx index 7a592f0439d..1700ebb4ab0 100644 --- a/webapp/channels/src/components/cloud_preview_modal/preview_modal_content.test.tsx +++ b/webapp/channels/src/components/cloud_preview_modal/preview_modal_content.test.tsx @@ -1,10 +1,9 @@ // Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved. // See LICENSE.txt for license information. -import {screen, fireEvent} from '@testing-library/react'; import React from 'react'; -import {renderWithContext} from 'tests/react_testing_utils'; +import {renderWithContext, screen, userEvent} from 'tests/react_testing_utils'; import PreviewModalContent from './preview_modal_content'; import type {PreviewModalContentData} from './preview_modal_content_data'; @@ -129,7 +128,7 @@ describe('PreviewModalContent', () => { expect(playButton).toHaveClass('custom-play-button'); }); - it('should hide play button and show controls when play button is clicked', () => { + it('should hide play button and show controls when play button is clicked', async () => { const content = { ...baseContent, videoUrl: 'https://example.com/video.mp4', @@ -142,7 +141,7 @@ describe('PreviewModalContent', () => { renderComponent(content); const playButton = screen.getByLabelText('Play video'); - fireEvent.click(playButton); + await userEvent.click(playButton); expect(mockPlay).toHaveBeenCalled(); expect(screen.queryByLabelText('Play video')).not.toBeInTheDocument(); diff --git a/webapp/channels/src/components/cloud_preview_modal/preview_modal_content_controller.test.tsx b/webapp/channels/src/components/cloud_preview_modal/preview_modal_content_controller.test.tsx index d83a43cdda2..121b8762767 100644 --- a/webapp/channels/src/components/cloud_preview_modal/preview_modal_content_controller.test.tsx +++ b/webapp/channels/src/components/cloud_preview_modal/preview_modal_content_controller.test.tsx @@ -1,10 +1,9 @@ // Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved. // See LICENSE.txt for license information. -import {screen, fireEvent} from '@testing-library/react'; import React from 'react'; -import {renderWithContext} from 'tests/react_testing_utils'; +import {renderWithContext, screen, userEvent} from 'tests/react_testing_utils'; import PreviewModalController from './preview_modal_content_controller'; import type {PreviewModalContentData} from './preview_modal_content_data'; @@ -121,43 +120,43 @@ describe('PreviewModalController', () => { expect(screen.getByText('1/3')).toBeInTheDocument(); }); - it('should update page counter when navigating', () => { + it('should update page counter when navigating', async () => { renderComponent(); expect(screen.getByText('1/3')).toBeInTheDocument(); const nextButton = screen.getByText('Next'); - fireEvent.click(nextButton); + await userEvent.click(nextButton); expect(screen.getByText('2/3')).toBeInTheDocument(); }); - it('should navigate to next slide when Next is clicked', () => { + it('should navigate to next slide when Next is clicked', async () => { renderComponent(); const nextButton = screen.getByText('Next'); - fireEvent.click(nextButton); + await userEvent.click(nextButton); expect(screen.getByText('Second Slide')).toBeInTheDocument(); expect(screen.queryByText('First Slide')).not.toBeInTheDocument(); }); - it('should navigate to previous slide when Previous is clicked', () => { + it('should navigate to previous slide when Previous is clicked', async () => { renderComponent(); // Go to second slide first const nextButton = screen.getByText('Next'); - fireEvent.click(nextButton); + await userEvent.click(nextButton); // Now go back const previousButton = screen.getByText('Previous'); - fireEvent.click(previousButton); + await userEvent.click(previousButton); expect(screen.getByText('First Slide')).toBeInTheDocument(); expect(screen.queryByText('Second Slide')).not.toBeInTheDocument(); }); - it('should show Skip for now button only on first slide', () => { + it('should show Skip for now button only on first slide', async () => { renderComponent(); // First slide should have Skip button @@ -165,7 +164,7 @@ describe('PreviewModalController', () => { // Go to second slide const nextButton = screen.getByText('Next'); - fireEvent.click(nextButton); + await userEvent.click(nextButton); // Second slide should not have Skip button expect(screen.queryByText('Skip for now')).not.toBeInTheDocument(); @@ -176,47 +175,47 @@ describe('PreviewModalController', () => { expect(screen.queryByText('Previous')).not.toBeInTheDocument(); }); - it('should show Done button on last slide', () => { + it('should show Done button on last slide', async () => { renderComponent(); // Navigate to last slide const nextButton = screen.getByText('Next'); - fireEvent.click(nextButton); - fireEvent.click(nextButton); + await userEvent.click(nextButton); + await userEvent.click(nextButton); expect(screen.getByText('Finish')).toBeInTheDocument(); expect(screen.queryByText('Next')).not.toBeInTheDocument(); }); - it('should call onClose when Skip is clicked', () => { + it('should call onClose when Skip is clicked', async () => { renderComponent(); const skipButton = screen.getByText('Skip for now'); - fireEvent.click(skipButton); + await userEvent.click(skipButton); expect(mockOnClose).toHaveBeenCalledTimes(1); }); - it('should call onClose when Finish is clicked on last slide', () => { + it('should call onClose when Finish is clicked on last slide', async () => { renderComponent(); // Navigate to last slide const nextButton = screen.getByText('Next'); - fireEvent.click(nextButton); - fireEvent.click(nextButton); + await userEvent.click(nextButton); + await userEvent.click(nextButton); const finishButton = screen.getByText('Finish'); - fireEvent.click(finishButton); + await userEvent.click(finishButton); expect(mockOnClose).toHaveBeenCalledTimes(1); }); - it('should call onClose when close button is clicked', () => { + it('should call onClose when close button is clicked', async () => { renderComponent(); // Look for the actual close button from GenericModal const closeButton = screen.getByLabelText('Close'); - fireEvent.click(closeButton); + await userEvent.click(closeButton); expect(mockOnClose).toHaveBeenCalledTimes(1); }); diff --git a/webapp/channels/src/components/color_input.test.tsx b/webapp/channels/src/components/color_input.test.tsx index cad3b93f2fa..85e16ca2dd7 100644 --- a/webapp/channels/src/components/color_input.test.tsx +++ b/webapp/channels/src/components/color_input.test.tsx @@ -74,7 +74,7 @@ describe('components/ColorInput', () => { expect(container.querySelector('.color-popover')).toBeInTheDocument(); }); - test('should keep what the user types in the textbox until blur', () => { + test('should keep what the user types in the textbox until blur', async () => { let currentValue = '#ffffff'; const onChange = jest.fn((value: string) => { currentValue = value; @@ -91,9 +91,11 @@ describe('components/ColorInput', () => { const input = screen.getByRole('textbox'); const colorIcon = container.querySelector('.color-icon') as HTMLElement; + // Simulate focus on input - fireEvent used because userEvent doesn't have direct focus/blur methods fireEvent.focus(input); - fireEvent.change(input, {target: {value: '#abc'}}); + await userEvent.clear(input); + await userEvent.type(input, '#abc'); expect(onChange).toHaveBeenLastCalledWith('#aabbcc'); expect(input).toHaveValue('#abc'); expect(colorIcon.style.backgroundColor).toBe('rgb(170, 187, 204)'); diff --git a/webapp/channels/src/components/common/agents/agent_dropdown.test.tsx b/webapp/channels/src/components/common/agents/agent_dropdown.test.tsx index f4e90d60646..3fc30b9ba9e 100644 --- a/webapp/channels/src/components/common/agents/agent_dropdown.test.tsx +++ b/webapp/channels/src/components/common/agents/agent_dropdown.test.tsx @@ -41,10 +41,6 @@ describe('AgentDropdown', () => { defaultBotId: 'bot1', }; - beforeEach(() => { - jest.clearAllMocks(); - }); - test('should render with selected bot name', () => { renderWithContext(); diff --git a/webapp/channels/src/components/common/hooks/useAccessControlAttributes.test.tsx b/webapp/channels/src/components/common/hooks/useAccessControlAttributes.test.tsx index 07d8d8c97de..35c4113e578 100644 --- a/webapp/channels/src/components/common/hooks/useAccessControlAttributes.test.tsx +++ b/webapp/channels/src/components/common/hooks/useAccessControlAttributes.test.tsx @@ -59,10 +59,6 @@ describe('useAccessControlAttributes', () => { ); - beforeEach(() => { - jest.clearAllMocks(); - }); - test('should return initial state', () => { const {result} = renderHook(() => useAccessControlAttributes(EntityType.Channel, undefined, undefined), {wrapper}); diff --git a/webapp/channels/src/components/common/infinite_scroll.test.tsx b/webapp/channels/src/components/common/infinite_scroll.test.tsx index 7237085f0c1..ec0f0838129 100644 --- a/webapp/channels/src/components/common/infinite_scroll.test.tsx +++ b/webapp/channels/src/components/common/infinite_scroll.test.tsx @@ -61,6 +61,7 @@ describe('/components/common/InfiniteScroll', () => { Object.defineProperty(scrollContainer, 'scrollTop', {value: 500, configurable: true}); await act(async () => { + // Simulate scroll event - fireEvent used because userEvent doesn't support scroll events fireEvent.scroll(scrollContainer); // Wait for debounce (200ms) plus some buffer diff --git a/webapp/channels/src/components/common/scrollbars.test.tsx b/webapp/channels/src/components/common/scrollbars.test.tsx index 1baaf7ca48b..0f234d3b0db 100644 --- a/webapp/channels/src/components/common/scrollbars.test.tsx +++ b/webapp/channels/src/components/common/scrollbars.test.tsx @@ -32,7 +32,7 @@ describe('Scrollbars', () => { , ); - // Ideally, we'd actually scroll the content of the element, but jsdom doesn't implement scroll events + // Simulate scrolling to verify the onScroll handler is attached correctly - fireEvent used because userEvent doesn't support scroll events fireEvent.scroll(document.querySelector('.simplebar-content-wrapper')!); expect(onScroll).toHaveBeenCalled(); diff --git a/webapp/channels/src/components/convert_channel_modal/convert_channel_modal.test.tsx b/webapp/channels/src/components/convert_channel_modal/convert_channel_modal.test.tsx index a8d416eabba..eca6893c9b9 100644 --- a/webapp/channels/src/components/convert_channel_modal/convert_channel_modal.test.tsx +++ b/webapp/channels/src/components/convert_channel_modal/convert_channel_modal.test.tsx @@ -1,7 +1,7 @@ // Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved. // See LICENSE.txt for license information. -import {fireEvent, waitForElementToBeRemoved} from '@testing-library/react'; +import {waitForElementToBeRemoved} from '@testing-library/react'; import React from 'react'; import {General} from 'mattermost-redux/constants'; @@ -39,7 +39,7 @@ describe('component/ConvertChannelModal', () => { test('should not call updateChannelPrivacy when Close button is clicked', async () => { renderWithContext(); - fireEvent.click(screen.getByRole('button', {name: 'Close'})); + await userEvent.click(screen.getByRole('button', {name: 'Close'})); await waitForElementToBeRemoved(() => screen.getByText('Convert Channel Display Name to a Private Channel?')); expect(updateChannelPrivacy).not.toHaveBeenCalled(); @@ -47,7 +47,7 @@ describe('component/ConvertChannelModal', () => { test('should not call updateChannelPrivacy when other Cancel is clicked', async () => { renderWithContext(); - fireEvent.click(screen.getByRole('button', {name: 'No, cancel'})); + await userEvent.click(screen.getByRole('button', {name: 'No, cancel'})); await waitForElementToBeRemoved(() => screen.getByText('Convert Channel Display Name to a Private Channel?')); expect(updateChannelPrivacy).not.toHaveBeenCalled(); diff --git a/webapp/channels/src/components/convert_gm_to_channel_modal/convert_gm_to_channel_modal.test.tsx b/webapp/channels/src/components/convert_gm_to_channel_modal/convert_gm_to_channel_modal.test.tsx index e0f7a9390d6..744f6437ac9 100644 --- a/webapp/channels/src/components/convert_gm_to_channel_modal/convert_gm_to_channel_modal.test.tsx +++ b/webapp/channels/src/components/convert_gm_to_channel_modal/convert_gm_to_channel_modal.test.tsx @@ -1,7 +1,6 @@ // Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved. // See LICENSE.txt for license information. -import {fireEvent, waitFor} from '@testing-library/react'; import nock from 'nock'; import React from 'react'; @@ -16,7 +15,7 @@ import {Preferences} from 'mattermost-redux/constants'; import ConvertGmToChannelModal from 'components/convert_gm_to_channel_modal/convert_gm_to_channel_modal'; import TestHelper from 'packages/mattermost-redux/test/test_helper'; -import {act, renderWithContext, screen} from 'tests/react_testing_utils'; +import {fireEvent, renderWithContext, screen, userEvent, waitFor} from 'tests/react_testing_utils'; import type {GlobalState} from 'types/store'; @@ -161,18 +160,17 @@ describe('component/ConvertGmToChannelModal', () => { const team1Option = screen.queryByText('Team 1'); expect(team1Option).toBeInTheDocument(); - fireEvent.click(team1Option!); + await userEvent.click(team1Option!); const channelNameInput = screen.queryByPlaceholderText('Channel name'); expect(channelNameInput).toBeInTheDocument(); - fireEvent.change(channelNameInput!, {target: {value: 'Channel name set by me'}}); + await userEvent.clear(channelNameInput!); + await userEvent.type(channelNameInput!, 'Channel name set by me'); const confirmButton = screen.queryByText('Convert to private channel'); expect(channelNameInput).toBeInTheDocument(); - await act(async () => { - fireEvent.click(confirmButton!); - }); + await userEvent.click(confirmButton!); }); test('duplicate channel names should npt be allowed', async () => { @@ -202,14 +200,13 @@ describe('component/ConvertGmToChannelModal', () => { const channelNameInput = screen.queryByPlaceholderText('Channel name'); expect(channelNameInput).toBeInTheDocument(); - fireEvent.change(channelNameInput!, {target: {value: 'Channel'}}); + await userEvent.clear(channelNameInput!); + await userEvent.type(channelNameInput!, 'Channel'); const confirmButton = screen.queryByText('Convert to private channel'); expect(channelNameInput).toBeInTheDocument(); - await act(async () => { - fireEvent.click(confirmButton!); - }); + await userEvent.click(confirmButton!); expect(screen.queryByText('A channel with that URL already exists')).toBeInTheDocument(); }); diff --git a/webapp/channels/src/components/create_recap_modal/channel_selector.test.tsx b/webapp/channels/src/components/create_recap_modal/channel_selector.test.tsx index 831e719af6b..5b4ca75a3cc 100644 --- a/webapp/channels/src/components/create_recap_modal/channel_selector.test.tsx +++ b/webapp/channels/src/components/create_recap_modal/channel_selector.test.tsx @@ -66,10 +66,6 @@ describe('ChannelSelector', () => { unreadChannels: mockUnreadChannels, }; - beforeEach(() => { - jest.clearAllMocks(); - }); - describe('Rendering', () => { it('should render the component with label', () => { renderWithContext(); diff --git a/webapp/channels/src/components/create_recap_modal/create_recap_modal.test.tsx b/webapp/channels/src/components/create_recap_modal/create_recap_modal.test.tsx index fdbeea861c7..8d3d675cd63 100644 --- a/webapp/channels/src/components/create_recap_modal/create_recap_modal.test.tsx +++ b/webapp/channels/src/components/create_recap_modal/create_recap_modal.test.tsx @@ -115,10 +115,6 @@ describe('CreateRecapModal', () => { }, }; - beforeEach(() => { - jest.clearAllMocks(); - }); - test('should render modal with header including AI agent dropdown', () => { renderWithContext(, initialState); diff --git a/webapp/channels/src/components/create_recap_modal/recap_configuration.test.tsx b/webapp/channels/src/components/create_recap_modal/recap_configuration.test.tsx index 72ab574229d..a7bdb94c236 100644 --- a/webapp/channels/src/components/create_recap_modal/recap_configuration.test.tsx +++ b/webapp/channels/src/components/create_recap_modal/recap_configuration.test.tsx @@ -33,10 +33,6 @@ describe('RecapConfiguration', () => { unreadChannels: mockUnreadChannels, }; - beforeEach(() => { - jest.clearAllMocks(); - }); - describe('Recap Name Input', () => { it('should render name input field', () => { renderWithContext(); diff --git a/webapp/channels/src/components/create_team/components/display_name.test.tsx b/webapp/channels/src/components/create_team/components/display_name.test.tsx index 02b886ae463..076374a7691 100644 --- a/webapp/channels/src/components/create_team/components/display_name.test.tsx +++ b/webapp/channels/src/components/create_team/components/display_name.test.tsx @@ -5,7 +5,7 @@ import React from 'react'; import DisplayName from 'components/create_team/components/display_name'; -import {renderWithContext, screen, fireEvent, userEvent} from 'tests/react_testing_utils'; +import {renderWithContext, screen, userEvent} from 'tests/react_testing_utils'; import {cleanUpUrlable} from 'utils/url'; jest.mock('images/logo.png', () => 'logo.png'); @@ -55,7 +55,8 @@ describe('/components/create_team/components/display_name', () => { }; const input = screen.getByRole('textbox'); - fireEvent.change(input, {target: {value: teamDisplayName}}); + await userEvent.clear(input); + await userEvent.type(input, teamDisplayName); await userEvent.click(screen.getByRole('button', {name: /next/i})); diff --git a/webapp/channels/src/components/datetime_input/datetime_input.test.tsx b/webapp/channels/src/components/datetime_input/datetime_input.test.tsx index b0e5af5a550..fd8022d70a8 100644 --- a/webapp/channels/src/components/datetime_input/datetime_input.test.tsx +++ b/webapp/channels/src/components/datetime_input/datetime_input.test.tsx @@ -26,7 +26,6 @@ describe('components/datetime_input/DateTimeInput', () => { }; beforeEach(() => { - jest.clearAllMocks(); mockGetCurrentMomentForTimezone.mockReturnValue(moment('2025-06-08T10:00:00Z')); mockIsBeforeTime.mockReturnValue(false); }); diff --git a/webapp/channels/src/components/dialog_router/dialog_router.test.tsx b/webapp/channels/src/components/dialog_router/dialog_router.test.tsx index 0a4df2c3526..2bb23c6bb02 100644 --- a/webapp/channels/src/components/dialog_router/dialog_router.test.tsx +++ b/webapp/channels/src/components/dialog_router/dialog_router.test.tsx @@ -55,7 +55,6 @@ describe('components/dialog_router/DialogRouter', () => { }; beforeEach(() => { - jest.clearAllMocks(); jest.spyOn(console, 'error').mockImplementation(() => {}); }); diff --git a/webapp/channels/src/components/dot_menu/dot_menu.test.tsx b/webapp/channels/src/components/dot_menu/dot_menu.test.tsx index 0d42d511ae4..82f16b90585 100644 --- a/webapp/channels/src/components/dot_menu/dot_menu.test.tsx +++ b/webapp/channels/src/components/dot_menu/dot_menu.test.tsx @@ -1,15 +1,13 @@ // Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved. // See LICENSE.txt for license information. -import {fireEvent, screen} from '@testing-library/react'; -import userEvent from '@testing-library/user-event'; import React from 'react'; import type {ChannelType} from '@mattermost/types/channels'; import type {Post, PostType} from '@mattermost/types/posts'; import type {DeepPartial} from '@mattermost/types/utilities'; -import {renderWithContext} from 'tests/react_testing_utils'; +import {renderWithContext, screen, userEvent} from 'tests/react_testing_utils'; import {Locations} from 'utils/constants'; import {TestHelper} from 'utils/test_helper'; @@ -335,7 +333,8 @@ describe('components/dot_menu/DotMenu', () => { // Simulate pressing 'W' key while menu is open const menu = screen.getByRole('menu'); - fireEvent.keyDown(menu, {key: 'W', code: 'KeyW', keyCode: 87}); + menu.focus(); + await userEvent.keyboard('W'); // Move thread should not be triggered (menu item doesn't exist, so no way to verify the action wasn't called) // The fact that canMove is false means the menu item is hidden and keyboard shortcut is blocked @@ -454,7 +453,7 @@ describe('components/dot_menu/DotMenu', () => { }); }); - test('should show flag post menu option when allowed', () => { + test('should show flag post menu option when allowed', async () => { const props = { ...baseProps, post: post1, @@ -465,13 +464,13 @@ describe('components/dot_menu/DotMenu', () => { ); const button = screen.getByTestId(`PostDotMenu-Button-${post1.id}`); - fireEvent.click(button); + await userEvent.click(button); const flagPostOption = screen.getByTestId(`flag_post_${post1.id}`); expect(flagPostOption).toBeVisible(); }); - test('should not show flag post menu option for DM channel even when enabled', () => { + test('should not show flag post menu option for DM channel even when enabled', async () => { const props = { ...baseProps, post: dmPost, @@ -482,13 +481,13 @@ describe('components/dot_menu/DotMenu', () => { ); const button = screen.getByTestId(`PostDotMenu-Button-${dmPost.id}`); - fireEvent.click(button); + await userEvent.click(button); const flagPostOption = screen.queryByTestId(`flag_post_${dmPost.id}`); expect(flagPostOption).toBeNull(); }); - test('should not show flag post menu option for GM channel even when enabled', () => { + test('should not show flag post menu option for GM channel even when enabled', async () => { const props = { ...baseProps, post: gmPost, @@ -499,13 +498,13 @@ describe('components/dot_menu/DotMenu', () => { ); const button = screen.getByTestId(`PostDotMenu-Button-${gmPost.id}`); - fireEvent.click(button); + await userEvent.click(button); const flagPostOption = screen.queryByTestId(`flag_post_${gmPost.id}`); expect(flagPostOption).toBeNull(); }); - test('should not show flag post menu option for burn on read posts', () => { + test('should not show flag post menu option for burn on read posts', async () => { const burnOnReadPost: Post = { ...post1, type: 'burn_on_read', @@ -517,13 +516,13 @@ describe('components/dot_menu/DotMenu', () => { renderWithContext(, initialState); const button = screen.getByTestId(`PostDotMenu-Button-${post1.id}`); - fireEvent.click(button); + await userEvent.click(button); const flagPostOption = screen.queryByTestId(`flag_post_${post1.id}`); expect(flagPostOption).toBeNull(); }); - test('should show copy link for burn-on-read post sender', () => { + test('should show copy link for burn-on-read post sender', async () => { const borPost = { ...post1, type: 'burn_on_read' as PostType, @@ -552,13 +551,13 @@ describe('components/dot_menu/DotMenu', () => { ); const button = screen.getByTestId(`PostDotMenu-Button-${borPost.id}`); - fireEvent.click(button); + await userEvent.click(button); const copyLinkOption = screen.queryByTestId(`permalink_${borPost.id}`); expect(copyLinkOption).not.toBeNull(); }); - test('should not show copy link for burn-on-read post receiver', () => { + test('should not show copy link for burn-on-read post receiver', async () => { const borPost = { ...post1, type: 'burn_on_read' as PostType, @@ -587,13 +586,13 @@ describe('components/dot_menu/DotMenu', () => { ); const button = screen.getByTestId(`PostDotMenu-Button-${borPost.id}`); - fireEvent.click(button); + await userEvent.click(button); const copyLinkOption = screen.queryByTestId(`permalink_${borPost.id}`); expect(copyLinkOption).toBeNull(); }); - test('should not trigger reply when pressing R key on burn-on-read post', () => { + test('should not trigger reply when pressing R key on burn-on-read post', async () => { const borPost = { ...post1, type: 'burn_on_read' as PostType, @@ -621,7 +620,7 @@ describe('components/dot_menu/DotMenu', () => { renderWithContext(, stateWithBorPost); const button = screen.getByTestId(`PostDotMenu-Button-${borPost.id}`); - fireEvent.click(button); + await userEvent.click(button); // Reply option should not be visible for BoR posts const replyOption = screen.queryByTestId(`reply_to_post_${borPost.id}`); @@ -629,7 +628,8 @@ describe('components/dot_menu/DotMenu', () => { // Simulate pressing 'R' key while menu is open - should do nothing const menu = screen.getByRole('menu'); - fireEvent.keyDown(menu, {key: 'R', code: 'KeyR', keyCode: 82}); + menu.focus(); + await userEvent.keyboard('R'); // Since reply option doesn't exist, keyboard shortcut should be blocked (no way to verify action wasn't called, but menu item being hidden confirms it) expect(replyOption).toBeNull(); diff --git a/webapp/channels/src/components/drafts/draft_actions/schedule_post_actions/scheduled_post_actions.test.tsx b/webapp/channels/src/components/drafts/draft_actions/schedule_post_actions/scheduled_post_actions.test.tsx index 021c9dee814..c6b739e2843 100644 --- a/webapp/channels/src/components/drafts/draft_actions/schedule_post_actions/scheduled_post_actions.test.tsx +++ b/webapp/channels/src/components/drafts/draft_actions/schedule_post_actions/scheduled_post_actions.test.tsx @@ -82,12 +82,6 @@ describe('ScheduledPostActions Component', () => { let getMyChannelMembershipsnMock: jest.SpyInstance; beforeEach(() => { - jest.clearAllMocks(); - }); - - beforeEach(() => { - jest.clearAllMocks(); - isCurrentUserSystemAdminMock = jest.spyOn(usersSelectors, 'isCurrentUserSystemAdmin'); getMyChannelMembershipsnMock = jest.spyOn(commonSelectors, 'getMyChannelMemberships'); diff --git a/webapp/channels/src/components/dynamic_virtualized_list/list_item.test.tsx b/webapp/channels/src/components/dynamic_virtualized_list/list_item.test.tsx index e64d4ae07eb..8d2de193937 100644 --- a/webapp/channels/src/components/dynamic_virtualized_list/list_item.test.tsx +++ b/webapp/channels/src/components/dynamic_virtualized_list/list_item.test.tsx @@ -40,10 +40,6 @@ describe('ListItem', () => { onUnmount: jest.fn(), }; - beforeEach(() => { - jest.clearAllMocks(); - }); - test('renders the item content correctly', () => { render(); diff --git a/webapp/channels/src/components/file_attachment/file_attachment.test.tsx b/webapp/channels/src/components/file_attachment/file_attachment.test.tsx index d706013e4e3..3fffcb2deee 100644 --- a/webapp/channels/src/components/file_attachment/file_attachment.test.tsx +++ b/webapp/channels/src/components/file_attachment/file_attachment.test.tsx @@ -1,13 +1,13 @@ // Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved. // See LICENSE.txt for license information. -import {screen, fireEvent} from '@testing-library/react'; +import {screen} from '@testing-library/react'; import React from 'react'; import type {GlobalState} from '@mattermost/types/store'; import type {DeepPartial} from '@mattermost/types/utilities'; -import {renderWithContext} from 'tests/react_testing_utils'; +import {renderWithContext, userEvent} from 'tests/react_testing_utils'; import {TestHelper} from 'utils/test_helper'; import FileAttachment from './file_attachment'; @@ -168,13 +168,13 @@ describe('FileAttachment', () => { expect(container).toMatchSnapshot(); }); - test('should blur file attachment link after click', () => { + test('should blur file attachment link after click', async () => { const props = {...baseProps, compactDisplay: true}; renderWithContext(); const link = screen.getByText(baseProps.fileInfo.name); const blur = jest.spyOn(link, 'blur'); - fireEvent.click(link); + await userEvent.click(link); expect(blur).toHaveBeenCalled(); }); diff --git a/webapp/channels/src/components/flag_message_modal/flag_post_modal.test.tsx b/webapp/channels/src/components/flag_message_modal/flag_post_modal.test.tsx index d5a576b280d..3d5c898ac2f 100644 --- a/webapp/channels/src/components/flag_message_modal/flag_post_modal.test.tsx +++ b/webapp/channels/src/components/flag_message_modal/flag_post_modal.test.tsx @@ -1,15 +1,13 @@ // Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved. // See LICENSE.txt for license information. -import {screen, waitFor} from '@testing-library/react'; -import userEvent from '@testing-library/user-event'; import React from 'react'; import type {DeepPartial} from '@mattermost/types/utilities'; import {Client4} from 'mattermost-redux/client'; -import {renderWithContext} from 'tests/react_testing_utils'; +import {renderWithContext, screen, userEvent, waitFor} from 'tests/react_testing_utils'; import {TestHelper} from 'utils/test_helper'; import type {GlobalState} from 'types/store'; diff --git a/webapp/channels/src/components/info_toast/info_toast.test.tsx b/webapp/channels/src/components/info_toast/info_toast.test.tsx index 3ae6decc0eb..44c1793c679 100644 --- a/webapp/channels/src/components/info_toast/info_toast.test.tsx +++ b/webapp/channels/src/components/info_toast/info_toast.test.tsx @@ -1,12 +1,13 @@ // Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved. // See LICENSE.txt for license information. -import {render, screen, fireEvent} from '@testing-library/react'; import React from 'react'; import type {ComponentProps} from 'react'; import {CheckIcon} from '@mattermost/compass-icons/components'; +import {render, screen, userEvent} from 'tests/react_testing_utils'; + import InfoToast from './info_toast'; describe('components/InfoToast', () => { @@ -25,18 +26,18 @@ describe('components/InfoToast', () => { expect(container).toMatchSnapshot(); }); - test('should close the toast on undo', () => { + test('should close the toast on undo', async () => { render(); - fireEvent.click(screen.getByText(/undo/i)); + await userEvent.click(screen.getByText(/undo/i)); expect(baseProps.content.undo).toHaveBeenCalled(); expect(baseProps.onExited).toHaveBeenCalled(); }); - test('should close the toast on close button click', () => { + test('should close the toast on close button click', async () => { render(); - fireEvent.click(screen.getByRole('button', {name: /close/i})); + await userEvent.click(screen.getByRole('button', {name: /close/i})); expect(baseProps.onExited).toHaveBeenCalled(); }); }); diff --git a/webapp/channels/src/components/invitation_modal/overage_users_banner_notice/overage_users_banner_notice.test.tsx b/webapp/channels/src/components/invitation_modal/overage_users_banner_notice/overage_users_banner_notice.test.tsx index 8bd8b703e93..b6b11897ae6 100644 --- a/webapp/channels/src/components/invitation_modal/overage_users_banner_notice/overage_users_banner_notice.test.tsx +++ b/webapp/channels/src/components/invitation_modal/overage_users_banner_notice/overage_users_banner_notice.test.tsx @@ -9,9 +9,9 @@ import {savePreferences} from 'mattermost-redux/actions/preferences'; import {General} from 'mattermost-redux/constants'; import { - fireEvent, renderWithContext, screen, + userEvent, } from 'tests/react_testing_utils'; import {LicenseLinks, OverActiveUserLimits, Preferences, SelfHostedProducts, StatTypes} from 'utils/constants'; import {TestHelper} from 'utils/test_helper'; @@ -199,7 +199,7 @@ describe('components/invitation_modal/overage_users_banner_notice', () => { expect(screen.getByText(notifyText, {exact: false})).toBeInTheDocument(); }); - it('should track if the admin click Contact Sales CTA in a 5% overage state', () => { + it('should track if the admin click Contact Sales CTA in a 5% overage state', async () => { const store: GlobalState = JSON.parse(JSON.stringify(initialState)); store.entities.admin = { @@ -218,7 +218,7 @@ describe('components/invitation_modal/overage_users_banner_notice', () => { store, ); - fireEvent.click(screen.getByText(contactSalesTextLink)); + await userEvent.click(screen.getByText(contactSalesTextLink)); expect(screen.getByRole('link')).toHaveAttribute( 'href', LicenseLinks.CONTACT_SALES + @@ -255,7 +255,7 @@ describe('components/invitation_modal/overage_users_banner_notice', () => { expect(screen.getByText(notifyText, {exact: false})).toBeInTheDocument(); }); - it('should save the preferences for 5% banner if admin click on close', () => { + it('should save the preferences for 5% banner if admin click on close', async () => { const store: GlobalState = JSON.parse(JSON.stringify(initialState)); store.entities.admin = { @@ -270,7 +270,7 @@ describe('components/invitation_modal/overage_users_banner_notice', () => { store, ); - fireEvent.click(screen.getByRole('button')); + await userEvent.click(screen.getByRole('button')); expect(savePreferences).toHaveBeenCalledTimes(1); expect(savePreferences).toHaveBeenCalledWith(store.entities.users.profiles.current_user.id, [{ @@ -300,7 +300,7 @@ describe('components/invitation_modal/overage_users_banner_notice', () => { expect(screen.getByText(notifyText, {exact: false})).toBeInTheDocument(); }); - it('should track if the admin click Contact Sales CTA in a 10% overage state', () => { + it('should track if the admin click Contact Sales CTA in a 10% overage state', async () => { const store: GlobalState = JSON.parse(JSON.stringify(initialState)); store.entities.admin = { @@ -319,7 +319,7 @@ describe('components/invitation_modal/overage_users_banner_notice', () => { store, ); - fireEvent.click(screen.getByText(contactSalesTextLink)); + await userEvent.click(screen.getByText(contactSalesTextLink)); expect(screen.getByRole('link')).toHaveAttribute( 'href', LicenseLinks.CONTACT_SALES + @@ -385,7 +385,7 @@ describe('components/invitation_modal/overage_users_banner_notice', () => { expect(screen.queryByText(notifyText, {exact: false})).not.toBeInTheDocument(); }); - it('should save preferences for the banner because we are over 10% and we don\'t have preferences', () => { + it('should save preferences for the banner because we are over 10% and we don\'t have preferences', async () => { const store: GlobalState = JSON.parse(JSON.stringify(initialState)); store.entities.admin = { @@ -400,7 +400,7 @@ describe('components/invitation_modal/overage_users_banner_notice', () => { store, ); - fireEvent.click(screen.getByRole('button')); + await userEvent.click(screen.getByRole('button')); expect(savePreferences).toHaveBeenCalledTimes(1); expect(savePreferences).toHaveBeenCalledWith(store.entities.users.profiles.current_user.id, [{ diff --git a/webapp/channels/src/components/login/login.test.tsx b/webapp/channels/src/components/login/login.test.tsx index c8b8bba03af..2f901fb0890 100644 --- a/webapp/channels/src/components/login/login.test.tsx +++ b/webapp/channels/src/components/login/login.test.tsx @@ -112,10 +112,6 @@ describe('components/login/Login', () => { LocalStorageStore.setWasLoggedIn(false); }); - afterEach(() => { - jest.clearAllMocks(); - }); - it('should match snapshot', () => { renderWithContext( , diff --git a/webapp/channels/src/components/markdown_image/markdown_image.test.tsx b/webapp/channels/src/components/markdown_image/markdown_image.test.tsx index de88f9ec8f9..ad062e508dd 100644 --- a/webapp/channels/src/components/markdown_image/markdown_image.test.tsx +++ b/webapp/channels/src/components/markdown_image/markdown_image.test.tsx @@ -77,7 +77,7 @@ describe('components/MarkdownImage', () => { expect(img).toBeInTheDocument(); expect(img).not.toHaveClass('broken-image'); - // Simulate load failure + // Simulate image error event - fireEvent used because userEvent doesn't support image loading events fireEvent.error(img!); // After load failure, image should have broken-image class @@ -101,7 +101,6 @@ describe('components/MarkdownImage', () => { const img = container.querySelector('img'); - // Simulate load failure fireEvent.error(img!); expect(container.querySelector('img')).toHaveClass('broken-image'); @@ -161,7 +160,7 @@ describe('components/MarkdownImage', () => { Object.defineProperty(img, 'naturalHeight', {value: 90, configurable: true}); Object.defineProperty(img, 'naturalWidth', {value: 1041, configurable: true}); - // Simulate successful load + // Simulate image load event - fireEvent used because userEvent doesn't support image loading events fireEvent.load(img!); // After load, should not have loading class and onImageLoaded should be called @@ -186,7 +185,6 @@ describe('components/MarkdownImage', () => { Object.defineProperty(img, 'naturalHeight', {value: 90, configurable: true}); Object.defineProperty(img, 'naturalWidth', {value: 100, configurable: true}); - // Simulate load fireEvent.load(img!); expect(container).toMatchSnapshot(); @@ -204,7 +202,6 @@ describe('components/MarkdownImage', () => { Object.defineProperty(img, 'naturalHeight', {value: 90, configurable: true}); Object.defineProperty(img, 'naturalWidth', {value: 1041, configurable: true}); - // Simulate load fireEvent.load(img!); // After load, should have hover and cursor classes for clickable preview @@ -226,7 +223,6 @@ describe('components/MarkdownImage', () => { Object.defineProperty(img, 'naturalHeight', {value: 90, configurable: true}); Object.defineProperty(img, 'naturalWidth', {value: 1041, configurable: true}); - // Simulate load fireEvent.load(img!); // When imageIsLink, should have no-border class and not cursor--pointer @@ -248,7 +244,6 @@ describe('components/MarkdownImage', () => { Object.defineProperty(img, 'naturalHeight', {value: 90, configurable: true}); Object.defineProperty(img, 'naturalWidth', {value: 1041, configurable: true}); - // Simulate load fireEvent.load(img!); // Click the image to trigger showModal @@ -269,7 +264,6 @@ describe('components/MarkdownImage', () => { Object.defineProperty(img, 'naturalHeight', {value: 90, configurable: true}); Object.defineProperty(img, 'naturalWidth', {value: 1041, configurable: true}); - // Simulate load fireEvent.load(img!); // After load, should have scaled-down class @@ -300,7 +294,6 @@ describe('components/MarkdownImage', () => { Object.defineProperty(img, 'naturalHeight', {value: 76, configurable: true}); Object.defineProperty(img, 'naturalWidth', {value: 50, configurable: true}); - // Simulate load to get final state fireEvent.load(img!); expect(img).toHaveAttribute('width', '50'); @@ -331,7 +324,6 @@ describe('components/MarkdownImage', () => { Object.defineProperty(img, 'naturalHeight', {value: 250, configurable: true}); Object.defineProperty(img, 'naturalWidth', {value: 50, configurable: true}); - // Simulate load fireEvent.load(img!); expect(container).toMatchSnapshot(); @@ -360,7 +352,6 @@ describe('components/MarkdownImage', () => { Object.defineProperty(img, 'naturalHeight', {value: 250, configurable: true}); Object.defineProperty(img, 'naturalWidth', {value: 50, configurable: true}); - // Simulate load fireEvent.load(img!); expect(container).toMatchSnapshot(); diff --git a/webapp/channels/src/components/more_direct_channels/list_item/user_details/user_details.test.tsx b/webapp/channels/src/components/more_direct_channels/list_item/user_details/user_details.test.tsx index 40c0bf94e80..b0b251b15e8 100644 --- a/webapp/channels/src/components/more_direct_channels/list_item/user_details/user_details.test.tsx +++ b/webapp/channels/src/components/more_direct_channels/list_item/user_details/user_details.test.tsx @@ -1,12 +1,11 @@ // Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved. // See LICENSE.txt for license information. -import userEvent from '@testing-library/user-event'; import React from 'react'; import type {UserProfile} from '@mattermost/types/users'; -import {renderWithContext, screen, waitFor} from 'tests/react_testing_utils'; +import {renderWithContext, screen, waitFor, userEvent} from 'tests/react_testing_utils'; import UserDetails from './user_details'; @@ -246,6 +245,5 @@ describe('components/more_direct_channels/list_item/user_details/UserDetails', ( afterEach(() => { jest.clearAllTimers(); jest.useRealTimers(); - jest.clearAllMocks(); }); }); diff --git a/webapp/channels/src/components/new_channel_modal/new_channel_modal.test.tsx b/webapp/channels/src/components/new_channel_modal/new_channel_modal.test.tsx index 59eb1bcaeb6..b0cc6bd1399 100644 --- a/webapp/channels/src/components/new_channel_modal/new_channel_modal.test.tsx +++ b/webapp/channels/src/components/new_channel_modal/new_channel_modal.test.tsx @@ -267,9 +267,11 @@ describe('components/new_channel_modal', () => { const ChannelPurposeTextArea = screen.getByLabelText('Channel Purpose'); expect(ChannelPurposeTextArea).toBeInTheDocument(); + // Simulate user interaction with purpose field including focus/blur for validation - fireEvent used because userEvent doesn't have direct focus/blur methods await act(async () => { fireEvent.focus(ChannelPurposeTextArea); - fireEvent.change(ChannelPurposeTextArea, {target: {value}}); + await userEvent.clear(ChannelPurposeTextArea); + await userEvent.type(ChannelPurposeTextArea, value); fireEvent.blur(ChannelPurposeTextArea); }); diff --git a/webapp/channels/src/components/new_search/new_search.test.tsx b/webapp/channels/src/components/new_search/new_search.test.tsx index 1cd8336f130..4d5a2f6a0cd 100644 --- a/webapp/channels/src/components/new_search/new_search.test.tsx +++ b/webapp/channels/src/components/new_search/new_search.test.tsx @@ -96,6 +96,7 @@ describe('components/new_search/NewSearch', () => { expect(screen.queryByText('Messages')).not.toBeInTheDocument(); + // Trigger global keyboard shortcut (Ctrl+Shift+F) to open search from any element - fireEvent used because userEvent.keyboard requires element focus await act(() => fireEvent.keyDown( screen.getByText('Outside'), {key: 'f', code: 'KeyF', keyCode: 70, charCode: 70, ctrlKey: true, shiftKey: true}, diff --git a/webapp/channels/src/components/new_search/search_box.test.tsx b/webapp/channels/src/components/new_search/search_box.test.tsx index 2027f4fa392..f90477686dd 100644 --- a/webapp/channels/src/components/new_search/search_box.test.tsx +++ b/webapp/channels/src/components/new_search/search_box.test.tsx @@ -8,7 +8,6 @@ import type {Team} from '@mattermost/types/teams'; import { renderWithContext, screen, - fireEvent, userEvent, } from 'tests/react_testing_utils'; @@ -51,28 +50,32 @@ describe('components/new_search/SearchBox', () => { expect(screen.getByText('Ext:')).toBeInTheDocument(); }); - test('should call close on esc keydown', () => { + test('should call close on esc keydown', async () => { renderWithContext(); - fireEvent.keyDown(screen.getByPlaceholderText('Search messages'), {key: 'Escape', code: 'Escape'}); + const input = screen.getByPlaceholderText('Search messages'); + input.focus(); + await userEvent.keyboard('{Escape}'); expect(baseProps.onClose).toHaveBeenCalledTimes(1); }); - test('should call search on enter keydown', () => { + test('should call search on enter keydown', async () => { renderWithContext(); - fireEvent.keyDown(screen.getByPlaceholderText('Search messages'), {key: 'Enter', code: 'Enter'}); + const input = screen.getByPlaceholderText('Search messages'); + input.focus(); + await userEvent.keyboard('{Enter}'); expect(baseProps.onSearch).toHaveBeenCalledTimes(1); }); test('should be able to select with the up and down arrows', async () => { renderWithContext(); await userEvent.click(screen.getByText('Files')); - fireEvent.change(screen.getByPlaceholderText('Search files'), {target: {value: 'ext:'}}); + await userEvent.type(screen.getByPlaceholderText('Search files'), 'ext:'); expect(screen.getByText('Text file')).toHaveClass('selected'); expect(screen.getByText('Word Document')).not.toHaveClass('selected'); - fireEvent.keyDown(screen.getByPlaceholderText('Search files'), {key: 'ArrowDown', code: 'ArrowDown'}); + await userEvent.keyboard('{ArrowDown}'); expect(screen.getByText('Text file')).not.toHaveClass('selected'); expect(screen.getByText('Word Document')).toHaveClass('selected'); - fireEvent.keyDown(screen.getByPlaceholderText('Search files'), {key: 'ArrowUp', code: 'ArrowUp'}); + await userEvent.keyboard('{ArrowUp}'); expect(screen.getByText('Text file')).toHaveClass('selected'); expect(screen.getByText('Word Document')).not.toHaveClass('selected'); }); diff --git a/webapp/channels/src/components/new_search/search_box_hints.test.tsx b/webapp/channels/src/components/new_search/search_box_hints.test.tsx index 3627f3acfe7..d5745253406 100644 --- a/webapp/channels/src/components/new_search/search_box_hints.test.tsx +++ b/webapp/channels/src/components/new_search/search_box_hints.test.tsx @@ -6,7 +6,7 @@ import React from 'react'; import { renderWithContext, screen, - fireEvent, + userEvent, } from 'tests/react_testing_utils'; import SearchBoxHints from './search_box_hints'; @@ -56,9 +56,9 @@ describe('components/new_search/SearchBoxHints', () => { expect(screen.getByText('From:')).toBeInTheDocument(); }); - test('should change the search term and focus on click', () => { + test('should change the search term and focus on click', async () => { renderWithContext(); - fireEvent.click(screen.getByText('From:')); + await userEvent.click(screen.getByText('From:')); expect(baseProps.setSearchTerms).toHaveBeenCalledWith('From:'); expect(baseProps.focus).toHaveBeenCalledWith(5); }); diff --git a/webapp/channels/src/components/new_search/search_box_input.test.tsx b/webapp/channels/src/components/new_search/search_box_input.test.tsx index 75ba798d09b..df8881a2bac 100644 --- a/webapp/channels/src/components/new_search/search_box_input.test.tsx +++ b/webapp/channels/src/components/new_search/search_box_input.test.tsx @@ -6,7 +6,7 @@ import React from 'react'; import { renderWithContext, screen, - fireEvent, + userEvent, } from 'tests/react_testing_utils'; import SearchBoxInput from './search_box_input'; @@ -44,31 +44,37 @@ describe('components/new_search/SearchBoxInput', () => { expect(screen.getByPlaceholderText('Search messages')).toHaveValue('test-value'); }); - test('should call on key down when there is a key down event on the input field', () => { + test('should call on key down when there is a key down event on the input field', async () => { const props = {...baseProps}; renderWithContext(); - fireEvent.keyDown(screen.getByPlaceholderText('Search messages'), {key: 'Enter'}); + const input = screen.getByPlaceholderText('Search messages'); + input.focus(); + await userEvent.keyboard('{Enter}'); expect(props.onKeyDown).toHaveBeenCalledTimes(1); }); - test('should call on key down when there is a key down event on the input field', () => { + test('should call on key down when there is a key down event on the input field with Escape', async () => { const props = {...baseProps}; renderWithContext(); - fireEvent.keyDown(screen.getByPlaceholderText('Search messages'), {key: 'Enter'}); + const input = screen.getByPlaceholderText('Search messages'); + input.focus(); + await userEvent.keyboard('{Escape}'); expect(props.onKeyDown).toHaveBeenCalledTimes(1); }); - test('should update the search term on change', () => { + test('should update the search term on change', async () => { const props = {...baseProps}; renderWithContext(); - fireEvent.change(screen.getByPlaceholderText('Search messages'), {target: {value: 'new-value'}}); + const input = screen.getByPlaceholderText('Search messages'); + await userEvent.clear(input); + await userEvent.type(input, 'new-value'); expect(props.setSearchTerms).toHaveBeenCalledWith('new-value'); }); - test('should clear the terms and focus in the input whenever click the clear button', () => { + test('should clear the terms and focus in the input whenever click the clear button', async () => { const props = {...baseProps, searchTerms: 'test-value'}; renderWithContext(); - fireEvent.click(screen.getByText('Clear')); + await userEvent.click(screen.getByText('Clear')); expect(props.setSearchTerms).toHaveBeenCalledWith(''); expect(props.focus).toHaveBeenCalledWith(0); }); diff --git a/webapp/channels/src/components/new_search/search_box_suggestions.test.tsx b/webapp/channels/src/components/new_search/search_box_suggestions.test.tsx index ed901479d86..012ea7439ed 100644 --- a/webapp/channels/src/components/new_search/search_box_suggestions.test.tsx +++ b/webapp/channels/src/components/new_search/search_box_suggestions.test.tsx @@ -7,6 +7,7 @@ import { renderWithContext, screen, fireEvent, + userEvent, } from 'tests/react_testing_utils'; import SearchBoxSuggestions from './search_box_suggestions'; @@ -62,24 +63,27 @@ describe('components/new_search/SearchBoxSuggestions', () => { expect(screen.getByText('user2')).toBeInTheDocument(); }); - test('should call the onSuggestionSelected on click', () => { + test('should call the onSuggestionSelected on click', async () => { renderWithContext(); - fireEvent.click(screen.getByText('test-username1')); + await userEvent.click(screen.getByText('test-username1')); expect(baseProps.onSuggestionSelected).toHaveBeenCalledWith('test-username1', ''); }); - test('should call the onSuggestionSelected on click with matchedPretext and previous text', () => { + test('should call the onSuggestionSelected on click with matchedPretext and previous text', async () => { const props = {...baseProps, searchTerms: 'something from:test-user', results: {...baseProps.results, matchedPretext: 'test-user'}}; renderWithContext(); - fireEvent.click(screen.getByText('test-username1')); + await userEvent.click(screen.getByText('test-username1')); expect(baseProps.onSuggestionSelected).toHaveBeenCalledWith('test-username1', 'test-user'); }); test('should change the selected option on mousemove', () => { const props = {...baseProps}; renderWithContext(); + + // Simulate mouse move to change selection - fireEvent used because userEvent.hover only triggers mouseEnter/mouseOver, not mouseMove fireEvent.mouseMove(screen.getByText('test-username2')); expect(baseProps.setSelectedTerm).toHaveBeenCalledWith('user2'); + fireEvent.mouseMove(screen.getByText('test-username1')); expect(baseProps.setSelectedTerm).toHaveBeenCalledWith('user1'); }); diff --git a/webapp/channels/src/components/popout_button.test.tsx b/webapp/channels/src/components/popout_button.test.tsx index 1f2178d3e40..007d2359330 100644 --- a/webapp/channels/src/components/popout_button.test.tsx +++ b/webapp/channels/src/components/popout_button.test.tsx @@ -1,11 +1,9 @@ // Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved. // See LICENSE.txt for license information. -import {screen, waitFor} from '@testing-library/react'; -import userEvent from '@testing-library/user-event'; import React from 'react'; -import {renderWithContext} from 'tests/react_testing_utils'; +import {renderWithContext, screen, userEvent, waitFor} from 'tests/react_testing_utils'; import PopoutButton from './popout_button'; @@ -28,8 +26,6 @@ describe('PopoutButton', () => { }; beforeEach(() => { - jest.clearAllMocks(); - mockCanPopout = true; }); diff --git a/webapp/channels/src/components/popout_controller/popout_controller.test.tsx b/webapp/channels/src/components/popout_controller/popout_controller.test.tsx index 963d223d447..47b95033062 100644 --- a/webapp/channels/src/components/popout_controller/popout_controller.test.tsx +++ b/webapp/channels/src/components/popout_controller/popout_controller.test.tsx @@ -89,8 +89,6 @@ const baseRouteProps: RouteComponentProps = { describe('PopoutController', () => { beforeEach(() => { - jest.clearAllMocks(); - // Reset document.body classes document.body.className = ''; diff --git a/webapp/channels/src/components/post_edit_history/edited_post_item/edited_post_item.test.tsx b/webapp/channels/src/components/post_edit_history/edited_post_item/edited_post_item.test.tsx index a74520c0665..026aefeba7d 100644 --- a/webapp/channels/src/components/post_edit_history/edited_post_item/edited_post_item.test.tsx +++ b/webapp/channels/src/components/post_edit_history/edited_post_item/edited_post_item.test.tsx @@ -1,11 +1,11 @@ // Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved. // See LICENSE.txt for license information. -import {screen, fireEvent} from '@testing-library/react'; +import {screen} from '@testing-library/react'; import React from 'react'; import type {ComponentProps} from 'react'; -import {renderWithContext} from 'tests/react_testing_utils'; +import {renderWithContext, userEvent} from 'tests/react_testing_utils'; import {ModalIdentifiers} from 'utils/constants'; import {TestHelper} from 'utils/test_helper'; @@ -45,12 +45,12 @@ describe('components/post_edit_history/edited_post_item', () => { expect(container).toMatchSnapshot(); }); - test('clicking on the restore button should call openRestorePostModal', () => { + test('clicking on the restore button should call openRestorePostModal', async () => { renderWithContext(); // find the button with restore icon and click it const restoreButton = screen.getByRole('button', {name: /restore/i}); - fireEvent.click(restoreButton); + await userEvent.click(restoreButton); expect(baseProps.actions.openModal).toHaveBeenCalledWith( expect.objectContaining({ diff --git a/webapp/channels/src/components/post_view/burn_on_read_badge/burn_on_read_badge.test.tsx b/webapp/channels/src/components/post_view/burn_on_read_badge/burn_on_read_badge.test.tsx index e777e2a6eb4..085e8b78c87 100644 --- a/webapp/channels/src/components/post_view/burn_on_read_badge/burn_on_read_badge.test.tsx +++ b/webapp/channels/src/components/post_view/burn_on_read_badge/burn_on_read_badge.test.tsx @@ -1,10 +1,9 @@ // Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved. // See LICENSE.txt for license information. -import {fireEvent} from '@testing-library/react'; import React from 'react'; -import {renderWithContext, screen} from 'tests/react_testing_utils'; +import {renderWithContext, screen, userEvent} from 'tests/react_testing_utils'; import {TestHelper} from 'utils/test_helper'; import BurnOnReadBadge from './burn_on_read_badge'; @@ -105,7 +104,7 @@ describe('BurnOnReadBadge', () => { expect(badge).toHaveClass('BurnOnReadBadge'); }); - it('should call onSenderDelete when sender clicks badge', () => { + it('should call onSenderDelete when sender clicks badge', async () => { const onSenderDelete = jest.fn(); renderWithContext( { ); const badge = screen.getByTestId('burn-on-read-badge-post123'); - fireEvent.click(badge); + await userEvent.click(badge); expect(onSenderDelete).toHaveBeenCalledTimes(1); }); - it('should not call onSenderDelete when recipient clicks badge', () => { + it('should not call onSenderDelete when recipient clicks badge', async () => { const onSenderDelete = jest.fn(); const onReveal = jest.fn(); renderWithContext( @@ -135,7 +134,7 @@ describe('BurnOnReadBadge', () => { ); const badge = screen.getByTestId('burn-on-read-badge-post123'); - fireEvent.click(badge); + await userEvent.click(badge); expect(onSenderDelete).not.toHaveBeenCalled(); expect(onReveal).toHaveBeenCalledWith('post123'); diff --git a/webapp/channels/src/components/post_view/burn_on_read_concealed_placeholder/burn_on_read_concealed_placeholder.test.tsx b/webapp/channels/src/components/post_view/burn_on_read_concealed_placeholder/burn_on_read_concealed_placeholder.test.tsx index f5ef9308355..b84212e1d16 100644 --- a/webapp/channels/src/components/post_view/burn_on_read_concealed_placeholder/burn_on_read_concealed_placeholder.test.tsx +++ b/webapp/channels/src/components/post_view/burn_on_read_concealed_placeholder/burn_on_read_concealed_placeholder.test.tsx @@ -3,7 +3,7 @@ import React from 'react'; -import {renderWithContext, screen, fireEvent} from 'tests/react_testing_utils'; +import {renderWithContext, screen, userEvent} from 'tests/react_testing_utils'; import BurnOnReadConcealedPlaceholder from './burn_on_read_concealed_placeholder'; @@ -14,10 +14,6 @@ describe('BurnOnReadConcealedPlaceholder', () => { onReveal: jest.fn(), }; - beforeEach(() => { - jest.clearAllMocks(); - }); - it('should render with concealed placeholder text', () => { renderWithContext( , @@ -28,47 +24,50 @@ describe('BurnOnReadConcealedPlaceholder', () => { expect(screen.getByRole('button')).toHaveClass('BurnOnReadConcealedPlaceholder'); }); - it('should call onReveal when clicked', () => { + it('should call onReveal when clicked', async () => { renderWithContext( , ); const placeholder = screen.getByRole('button'); - fireEvent.click(placeholder); + await userEvent.click(placeholder); expect(baseProps.onReveal).toHaveBeenCalledWith('post123'); expect(baseProps.onReveal).toHaveBeenCalledTimes(1); }); - it('should call onReveal when Enter key is pressed', () => { + it('should call onReveal when Enter key is pressed', async () => { renderWithContext( , ); const placeholder = screen.getByRole('button'); - fireEvent.keyDown(placeholder, {key: 'Enter'}); + placeholder.focus(); + await userEvent.keyboard('{Enter}'); expect(baseProps.onReveal).toHaveBeenCalledWith('post123'); }); - it('should call onReveal when Space key is pressed', () => { + it('should call onReveal when Space key is pressed', async () => { renderWithContext( , ); const placeholder = screen.getByRole('button'); - fireEvent.keyDown(placeholder, {key: ' '}); + placeholder.focus(); + await userEvent.keyboard(' '); expect(baseProps.onReveal).toHaveBeenCalledWith('post123'); }); - it('should not call onReveal when other keys are pressed', () => { + it('should not call onReveal when other keys are pressed', async () => { renderWithContext( , ); const placeholder = screen.getByRole('button'); - fireEvent.keyDown(placeholder, {key: 'a'}); + placeholder.focus(); + await userEvent.keyboard('a'); expect(baseProps.onReveal).not.toHaveBeenCalled(); }); @@ -85,7 +84,7 @@ describe('BurnOnReadConcealedPlaceholder', () => { expect(screen.queryByText('Click to Reveal')).not.toBeInTheDocument(); }); - it('should not trigger onReveal when loading', () => { + it('should not trigger onReveal when loading', async () => { renderWithContext( { ); const placeholder = screen.getByRole('button'); - fireEvent.click(placeholder); + await userEvent.click(placeholder); expect(baseProps.onReveal).not.toHaveBeenCalled(); }); diff --git a/webapp/channels/src/components/post_view/burn_on_read_timer_chip/burn_on_read_timer_chip.test.tsx b/webapp/channels/src/components/post_view/burn_on_read_timer_chip/burn_on_read_timer_chip.test.tsx index 1f3a309401a..4fe5359568f 100644 --- a/webapp/channels/src/components/post_view/burn_on_read_timer_chip/burn_on_read_timer_chip.test.tsx +++ b/webapp/channels/src/components/post_view/burn_on_read_timer_chip/burn_on_read_timer_chip.test.tsx @@ -1,10 +1,9 @@ // Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved. // See LICENSE.txt for license information. -import {fireEvent} from '@testing-library/react'; import React from 'react'; -import {renderWithContext, screen} from 'tests/react_testing_utils'; +import {renderWithContext, screen, fireEvent} from 'tests/react_testing_utils'; import BurnOnReadTimerChip from './burn_on_read_timer_chip'; @@ -16,7 +15,6 @@ describe('BurnOnReadTimerChip', () => { beforeEach(() => { jest.useFakeTimers(); - jest.clearAllMocks(); }); afterEach(() => { @@ -40,6 +38,8 @@ describe('BurnOnReadTimerChip', () => { ); const chip = screen.getByRole('button'); + + // Click the timer chip to verify onClick handler is called - fireEvent used because userEvent doesn't work well with fake timers fireEvent.click(chip); expect(onClick).toHaveBeenCalledTimes(1); @@ -55,6 +55,8 @@ describe('BurnOnReadTimerChip', () => { ); const chip = screen.getByRole('button'); + + // Press Enter key to verify keyboard accessibility for the timer chip - fireEvent used because userEvent doesn't work well with fake timers fireEvent.keyDown(chip, {key: 'Enter', code: 'Enter'}); expect(onClick).toHaveBeenCalled(); diff --git a/webapp/channels/src/components/post_view/post_time/post_time.test.tsx b/webapp/channels/src/components/post_view/post_time/post_time.test.tsx index facfc91af5e..5af9d696a0b 100644 --- a/webapp/channels/src/components/post_view/post_time/post_time.test.tsx +++ b/webapp/channels/src/components/post_view/post_time/post_time.test.tsx @@ -1,10 +1,9 @@ // Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved. // See LICENSE.txt for license information. -import userEvent from '@testing-library/user-event'; import React from 'react'; -import {renderWithContext, screen, waitFor, act} from 'tests/react_testing_utils'; +import {renderWithContext, screen, waitFor, act, userEvent} from 'tests/react_testing_utils'; import PostTime from './post_time'; diff --git a/webapp/channels/src/components/preparing_workspace/invite_members.test.tsx b/webapp/channels/src/components/preparing_workspace/invite_members.test.tsx index 1bde6290135..b1b1288b1f3 100644 --- a/webapp/channels/src/components/preparing_workspace/invite_members.test.tsx +++ b/webapp/channels/src/components/preparing_workspace/invite_members.test.tsx @@ -5,7 +5,7 @@ import React from 'react'; import type {ComponentProps} from 'react'; import {withIntl} from 'tests/helpers/intl-test-helper'; -import {fireEvent, render, screen} from 'tests/react_testing_utils'; +import {render, screen, userEvent} from 'tests/react_testing_utils'; import InviteMembers from './invite_members'; @@ -78,11 +78,11 @@ describe('InviteMembers component', () => { expect(button).toBeDisabled(); }); - it('invokes next prop on button click', () => { + it('invokes next prop on button click', async () => { const component = withIntl(); render(component); const button = screen.getByRole('button', {name: 'Finish setup'}); - fireEvent.click(button); + await userEvent.click(button); expect(defaultProps.next).toHaveBeenCalled(); }); diff --git a/webapp/channels/src/components/preparing_workspace/invite_members_link.test.tsx b/webapp/channels/src/components/preparing_workspace/invite_members_link.test.tsx index 5e9d479b366..b02d5d9e99b 100644 --- a/webapp/channels/src/components/preparing_workspace/invite_members_link.test.tsx +++ b/webapp/channels/src/components/preparing_workspace/invite_members_link.test.tsx @@ -4,7 +4,7 @@ import React from 'react'; import {withIntl} from 'tests/helpers/intl-test-helper'; -import {fireEvent, render, screen} from 'tests/react_testing_utils'; +import {render, screen, userEvent} from 'tests/react_testing_utils'; import InviteMembersLink from './invite_members_link'; @@ -63,7 +63,7 @@ describe('components/preparing-workspace/invite_members_link', () => { expect(button).toBeInTheDocument(); }); - it('changes the button text to "Link Copied" when the URL is copied', () => { + it('changes the button text to "Link Copied" when the URL is copied', async () => { const component = withIntl(); render(component); const button = screen.getByRole('button', {name: /copy link/i}); @@ -71,7 +71,7 @@ describe('components/preparing-workspace/invite_members_link', () => { const linkCopiedText = 'Link Copied'; expect(button).toHaveTextContent(originalText); - fireEvent.click(button); + await userEvent.click(button); expect(button).toHaveTextContent(linkCopiedText); }); diff --git a/webapp/channels/src/components/profile_popover/profile_popover_email.test.tsx b/webapp/channels/src/components/profile_popover/profile_popover_email.test.tsx index 05674f3eeb7..c593d49b5f5 100644 --- a/webapp/channels/src/components/profile_popover/profile_popover_email.test.tsx +++ b/webapp/channels/src/components/profile_popover/profile_popover_email.test.tsx @@ -1,10 +1,9 @@ // Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved. // See LICENSE.txt for license information. -import {screen, fireEvent} from '@testing-library/react'; import React from 'react'; -import {renderWithContext} from 'tests/react_testing_utils'; +import {renderWithContext, screen, userEvent} from 'tests/react_testing_utils'; import ProfilePopoverEmail from './profile_popover_email'; @@ -48,26 +47,25 @@ describe('components/ProfilePopoverEmail', () => { expect(emailLink.tagName).toBe('A'); }); - test('should open email client when clicked', () => { + test('should open email client when clicked', async () => { renderWithContext(); const email = 'test@example.com'; const emailLink = screen.getByText(email); - fireEvent.click(emailLink); + await userEvent.click(emailLink); expect(mockWindowOpen).toHaveBeenCalledWith('mailto:test@example.com'); }); - test('should prevent default click behavior', () => { + test('should prevent default click behavior', async () => { renderWithContext(); const email = 'test@example.com'; const emailLink = screen.getByText(email); - const clickEvent = fireEvent.click(emailLink); + await userEvent.click(emailLink); - expect(clickEvent).toBe(false); // fireEvent.click returns false when preventDefault was called expect(mockWindowOpen).toHaveBeenCalledWith('mailto:test@example.com'); }); }); diff --git a/webapp/channels/src/components/profile_popover/profile_popover_phone.test.tsx b/webapp/channels/src/components/profile_popover/profile_popover_phone.test.tsx index ee7a5b1be13..97c8d0c0b14 100644 --- a/webapp/channels/src/components/profile_popover/profile_popover_phone.test.tsx +++ b/webapp/channels/src/components/profile_popover/profile_popover_phone.test.tsx @@ -1,12 +1,11 @@ // Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved. // See LICENSE.txt for license information. -import {screen, fireEvent} from '@testing-library/react'; import React from 'react'; import type {UserPropertyField} from '@mattermost/types/properties'; -import {renderWithContext} from 'tests/react_testing_utils'; +import {renderWithContext, screen, userEvent} from 'tests/react_testing_utils'; import ProfilePopoverPhone from './profile_popover_phone'; @@ -104,26 +103,25 @@ describe('components/ProfilePopoverPhone', () => { expect(phoneLink.tagName).toBe('A'); }); - test('should open phone dialer when clicked', () => { + test('should open phone dialer when clicked', async () => { renderWithContext(); const phone = '+1 (555) 123-4567'; const phoneLink = screen.getByText(phone); - fireEvent.click(phoneLink); + await userEvent.click(phoneLink); expect(mockWindowOpen).toHaveBeenCalledWith('tel:+1 (555) 123-4567'); }); - test('should prevent default click behavior', () => { + test('should prevent default click behavior', async () => { renderWithContext(); const phone = '+1 (555) 123-4567'; const phoneLink = screen.getByText(phone); - const clickEvent = fireEvent.click(phoneLink); + await userEvent.click(phoneLink); - expect(clickEvent).toBe(false); // fireEvent.click returns false when preventDefault was called expect(mockWindowOpen).toHaveBeenCalledWith('tel:+1 (555) 123-4567'); }); }); diff --git a/webapp/channels/src/components/properties_card_view/propertyValueRenderer/channel_property_renderer/channel_property_renderer.test.tsx b/webapp/channels/src/components/properties_card_view/propertyValueRenderer/channel_property_renderer/channel_property_renderer.test.tsx index 57fcea8b43d..2ddf48fd2d4 100644 --- a/webapp/channels/src/components/properties_card_view/propertyValueRenderer/channel_property_renderer/channel_property_renderer.test.tsx +++ b/webapp/channels/src/components/properties_card_view/propertyValueRenderer/channel_property_renderer/channel_property_renderer.test.tsx @@ -29,10 +29,6 @@ describe('ChannelPropertyRenderer', () => { value: 'channel-id-123', } as PropertyValue; - beforeEach(() => { - jest.clearAllMocks(); - }); - it('should render channel name and icon when channel exists', () => { mockUseChannel.mockReturnValue(mockChannel); diff --git a/webapp/channels/src/components/recaps/recap_channel_card.test.tsx b/webapp/channels/src/components/recaps/recap_channel_card.test.tsx index 5539ba864ff..ae10a525c41 100644 --- a/webapp/channels/src/components/recaps/recap_channel_card.test.tsx +++ b/webapp/channels/src/components/recaps/recap_channel_card.test.tsx @@ -92,10 +92,6 @@ describe('RecapChannelCard', () => { create_at: 1000, }; - beforeEach(() => { - jest.clearAllMocks(); - }); - test('should render channel name', () => { renderWithContext( , diff --git a/webapp/channels/src/components/recaps/recap_menu.test.tsx b/webapp/channels/src/components/recaps/recap_menu.test.tsx index 1f396a364e0..a4a222520f1 100644 --- a/webapp/channels/src/components/recaps/recap_menu.test.tsx +++ b/webapp/channels/src/components/recaps/recap_menu.test.tsx @@ -57,10 +57,6 @@ describe('RecapMenu', () => { }, ]; - beforeEach(() => { - jest.clearAllMocks(); - }); - test('should render menu button', () => { renderWithContext(); diff --git a/webapp/channels/src/components/recaps/recaps_list.test.tsx b/webapp/channels/src/components/recaps/recaps_list.test.tsx index 2aab378860a..7ab66f4b9f8 100644 --- a/webapp/channels/src/components/recaps/recaps_list.test.tsx +++ b/webapp/channels/src/components/recaps/recaps_list.test.tsx @@ -44,10 +44,6 @@ describe('RecapsList', () => { }, ]; - beforeEach(() => { - jest.clearAllMocks(); - }); - test('should render empty state when no recaps', () => { renderWithContext(); diff --git a/webapp/channels/src/components/rhs_plugin_popout/rhs_plugin_popout.test.tsx b/webapp/channels/src/components/rhs_plugin_popout/rhs_plugin_popout.test.tsx index bbdaf79c814..7cea07e54c1 100644 --- a/webapp/channels/src/components/rhs_plugin_popout/rhs_plugin_popout.test.tsx +++ b/webapp/channels/src/components/rhs_plugin_popout/rhs_plugin_popout.test.tsx @@ -66,7 +66,6 @@ const baseState = { describe('RhsPluginPopout', () => { beforeEach(() => { mockUseParams.mockReturnValue({pluginId}); - jest.clearAllMocks(); }); it('should render LoadingScreen when plugin is not found', () => { diff --git a/webapp/channels/src/components/rhs_popout/rhs_popout.test.tsx b/webapp/channels/src/components/rhs_popout/rhs_popout.test.tsx index 089185f696c..f05ee0784ed 100644 --- a/webapp/channels/src/components/rhs_popout/rhs_popout.test.tsx +++ b/webapp/channels/src/components/rhs_popout/rhs_popout.test.tsx @@ -68,7 +68,6 @@ describe('RhsPopout', () => { const channel1 = TestHelper.getChannelMock({id: 'channel1', name: 'channel1'}); beforeEach(() => { - jest.clearAllMocks(); mockDispatch.mockReturnValue({type: 'MOCK_ACTION'}); mockUseDispatch.mockReturnValue(mockDispatch); mockUseParams.mockReturnValue({team: 'team1', identifier: 'channel1'}); diff --git a/webapp/channels/src/components/section_notice/index.test.tsx b/webapp/channels/src/components/section_notice/index.test.tsx index 35c2a735a97..ab0ccad2d7c 100644 --- a/webapp/channels/src/components/section_notice/index.test.tsx +++ b/webapp/channels/src/components/section_notice/index.test.tsx @@ -1,11 +1,10 @@ // Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved. // See LICENSE.txt for license information. -import {fireEvent, screen} from '@testing-library/react'; import type {ComponentProps} from 'react'; import React from 'react'; -import {renderWithContext} from 'tests/react_testing_utils'; +import {renderWithContext, screen, userEvent} from 'tests/react_testing_utils'; import SectionNotice from '.'; @@ -38,7 +37,7 @@ function getBaseProps(): Props { } describe('PluginAction', () => { - it('does show the correct information', () => { + it('does show the correct information', async () => { const props = getBaseProps(); renderWithContext(); const primaryButton = screen.getByText(props.primaryButton!.text); @@ -54,15 +53,15 @@ describe('PluginAction', () => { expect(closeButton).toBeInTheDocument(); expect(screen.queryByText(props.text as string)).toBeInTheDocument(); expect(screen.queryByText(props.title as string)).toBeInTheDocument(); - fireEvent.click(primaryButton); + await userEvent.click(primaryButton); expect(props.primaryButton?.onClick).toHaveBeenCalledTimes(1); - fireEvent.click(secondaryButton); + await userEvent.click(secondaryButton); expect(props.secondaryButton?.onClick).toHaveBeenCalledTimes(1); - fireEvent.click(tertiaryButton); + await userEvent.click(tertiaryButton); expect(props.tertiaryButton?.onClick).toHaveBeenCalledTimes(1); - fireEvent.click(linkButton); + await userEvent.click(linkButton); expect(props.linkButton?.onClick).toHaveBeenCalledTimes(1); - fireEvent.click(closeButton); + await userEvent.click(closeButton); expect(props.onDismissClick).toHaveBeenCalledTimes(1); }); diff --git a/webapp/channels/src/components/setting_picture.test.tsx b/webapp/channels/src/components/setting_picture.test.tsx index 2ed4570211a..48440b1282f 100644 --- a/webapp/channels/src/components/setting_picture.test.tsx +++ b/webapp/channels/src/components/setting_picture.test.tsx @@ -7,7 +7,7 @@ import {FormattedMessage} from 'react-intl'; import SettingPicture from 'components/setting_picture'; -import {renderWithContext, screen, fireEvent, userEvent} from 'tests/react_testing_utils'; +import {renderWithContext, screen, userEvent} from 'tests/react_testing_utils'; const helpText: ReactNode = ( { // Trigger file change const fileInput = screen.getByTestId('uploadPicture'); - fireEvent.change(fileInput, {target: {files: [mockFile]}}); + await userEvent.upload(fileInput, mockFile); expect(props.onFileChange).toHaveBeenCalledTimes(1); diff --git a/webapp/channels/src/components/shared_channel_indicator.test.tsx b/webapp/channels/src/components/shared_channel_indicator.test.tsx index 811888b1fb8..cb3c590ff89 100644 --- a/webapp/channels/src/components/shared_channel_indicator.test.tsx +++ b/webapp/channels/src/components/shared_channel_indicator.test.tsx @@ -1,10 +1,9 @@ // Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved. // See LICENSE.txt for license information. -import userEvent from '@testing-library/user-event'; import React from 'react'; -import {renderWithContext, screen, waitFor} from 'tests/react_testing_utils'; +import {renderWithContext, screen, userEvent, waitFor} from 'tests/react_testing_utils'; import SharedChannelIndicator from './shared_channel_indicator'; diff --git a/webapp/channels/src/components/shared_user_indicator.test.tsx b/webapp/channels/src/components/shared_user_indicator.test.tsx index 14dbf1776c3..d37d8f28ab1 100644 --- a/webapp/channels/src/components/shared_user_indicator.test.tsx +++ b/webapp/channels/src/components/shared_user_indicator.test.tsx @@ -1,11 +1,10 @@ // Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved. // See LICENSE.txt for license information. -import userEvent from '@testing-library/user-event'; import React from 'react'; import * as reactIntl from 'react-intl'; -import {renderWithContext, screen, waitFor} from 'tests/react_testing_utils'; +import {renderWithContext, screen, userEvent, waitFor} from 'tests/react_testing_utils'; import SharedUserIndicator from './shared_user_indicator'; diff --git a/webapp/channels/src/components/sidebar/sidebar.test.tsx b/webapp/channels/src/components/sidebar/sidebar.test.tsx index 2810fda21e2..69c2f9ca300 100644 --- a/webapp/channels/src/components/sidebar/sidebar.test.tsx +++ b/webapp/channels/src/components/sidebar/sidebar.test.tsx @@ -301,6 +301,7 @@ describe('components/sidebar', () => { expect(document.getElementById('SidebarContainer')).toBeInTheDocument(); // Test with backslash key (should not trigger the modal) + // fireEvent on document used because userEvent.keyboard requires element focus fireEvent.keyDown(document, { key: '\\', code: 'Backslash', @@ -310,6 +311,7 @@ describe('components/sidebar', () => { expect(openModalSpy).not.toHaveBeenCalled(); // Test with 'ù' key but with forward slash keyCode (should trigger the modal) + // fireEvent on document used because userEvent.keyboard requires element focus fireEvent.keyDown(document, { key: 'ù', code: 'Slash', @@ -324,6 +326,7 @@ describe('components/sidebar', () => { openModalSpy.mockClear(); // Test with '/' key but with seven keyCode (should trigger the modal) + // fireEvent on document used because userEvent.keyboard requires element focus fireEvent.keyDown(document, { key: '/', code: 'Digit7', @@ -338,6 +341,7 @@ describe('components/sidebar', () => { openModalSpy.mockClear(); // Test with forward slash key (should trigger the modal) + // fireEvent on document used because userEvent.keyboard requires element focus fireEvent.keyDown(document, { key: '/', code: 'Slash', @@ -371,6 +375,7 @@ describe('components/sidebar', () => { expect(document.getElementById('SidebarContainer')).toBeInTheDocument(); // Test with forward slash key (should close the modal since it's already open) + // fireEvent on document used because userEvent.keyboard requires element focus fireEvent.keyDown(document, { key: '/', code: 'Slash', diff --git a/webapp/channels/src/components/sidebar/sidebar_header/sidebar_team_menu.test.tsx b/webapp/channels/src/components/sidebar/sidebar_header/sidebar_team_menu.test.tsx index cfe11b1d07c..45f8bddb2ab 100644 --- a/webapp/channels/src/components/sidebar/sidebar_header/sidebar_team_menu.test.tsx +++ b/webapp/channels/src/components/sidebar/sidebar_header/sidebar_team_menu.test.tsx @@ -102,10 +102,6 @@ describe('components/sidebar/sidebar_header/sidebar_team_menu', () => { currentTeam, }; - beforeEach(() => { - jest.clearAllMocks(); - }); - test('should open team menu when clicked', async () => { renderWithContext( , diff --git a/webapp/channels/src/components/single_image_view/single_image_view.test.tsx b/webapp/channels/src/components/single_image_view/single_image_view.test.tsx index 3363e71b27b..50b9b08778d 100644 --- a/webapp/channels/src/components/single_image_view/single_image_view.test.tsx +++ b/webapp/channels/src/components/single_image_view/single_image_view.test.tsx @@ -34,6 +34,8 @@ describe('components/SingleImageView', () => { expect(img).toBeInTheDocument(); Object.defineProperty(img, 'naturalHeight', {value: 100, configurable: true}); Object.defineProperty(img, 'naturalWidth', {value: 100, configurable: true}); + + // Simulate image load event - fireEvent used because userEvent doesn't support image loading events fireEvent.load(img!); expect(container).toMatchSnapshot(); }); @@ -56,6 +58,7 @@ describe('components/SingleImageView', () => { expect(img).toBeInTheDocument(); Object.defineProperty(img, 'naturalHeight', {value: 100, configurable: true}); Object.defineProperty(img, 'naturalWidth', {value: 100, configurable: true}); + fireEvent.load(img!); expect(container).toMatchSnapshot(); }); @@ -71,6 +74,7 @@ describe('components/SingleImageView', () => { // Simulate loaded state Object.defineProperty(img, 'naturalHeight', {value: 100, configurable: true}); Object.defineProperty(img, 'naturalWidth', {value: 100, configurable: true}); + fireEvent.load(img!); // Click the image @@ -110,6 +114,7 @@ describe('components/SingleImageView', () => { expect(img).toBeInTheDocument(); Object.defineProperty(img, 'naturalHeight', {value: 100, configurable: true}); Object.defineProperty(img, 'naturalWidth', {value: 100, configurable: true}); + fireEvent.load(img!); // After load, should have image-fade-in class (loaded = true) diff --git a/webapp/channels/src/components/system_policy_indicator/system_policy_indicator.test.tsx b/webapp/channels/src/components/system_policy_indicator/system_policy_indicator.test.tsx index f6cf8347144..676dc9eec50 100644 --- a/webapp/channels/src/components/system_policy_indicator/system_policy_indicator.test.tsx +++ b/webapp/channels/src/components/system_policy_indicator/system_policy_indicator.test.tsx @@ -5,7 +5,7 @@ import React from 'react'; import type {AccessControlPolicy} from '@mattermost/types/access_control'; -import {renderWithContext, screen, fireEvent} from 'tests/react_testing_utils'; +import {renderWithContext, screen, userEvent} from 'tests/react_testing_utils'; import {TestHelper} from 'utils/test_helper'; import SystemPolicyIndicator from './system_policy_indicator'; @@ -224,7 +224,7 @@ describe('components/system_policy_indicator/SystemPolicyIndicator', () => { }); // Test more button functionality - test('should call onMorePoliciesClick when more button is clicked', () => { + test('should call onMorePoliciesClick when more button is clicked', async () => { const mockPolicy3: AccessControlPolicy = { id: 'policy3', name: 'Test Policy 3', @@ -248,12 +248,12 @@ describe('components/system_policy_indicator/SystemPolicyIndicator', () => { ); const moreButton = screen.getByText('1 more'); - fireEvent.click(moreButton); + await userEvent.click(moreButton); expect(onMorePoliciesClick).toHaveBeenCalledTimes(1); }); - test('should call onMorePoliciesClick when more button is activated with Enter key', () => { + test('should call onMorePoliciesClick when more button is activated with Enter key', async () => { const mockPolicy3: AccessControlPolicy = { id: 'policy3', name: 'Test Policy 3', @@ -277,7 +277,8 @@ describe('components/system_policy_indicator/SystemPolicyIndicator', () => { ); const moreButton = screen.getByText('1 more'); - fireEvent.keyDown(moreButton, {key: 'Enter', code: 'Enter'}); + moreButton.focus(); + await userEvent.keyboard('{Enter}'); expect(onMorePoliciesClick).toHaveBeenCalledTimes(1); }); @@ -315,7 +316,7 @@ describe('components/system_policy_indicator/SystemPolicyIndicator', () => { expect(screen.getByText('policy1')).toBeInTheDocument(); }); - test('should prevent default action and stop propagation on click', () => { + test('should prevent default action and stop propagation on click', async () => { const mockPolicy3: AccessControlPolicy = { id: 'policy3', name: 'Test Policy 3', @@ -329,8 +330,6 @@ describe('components/system_policy_indicator/SystemPolicyIndicator', () => { }; const onMorePoliciesClick = jest.fn(); - const mockPreventDefault = jest.fn(); - const mockStopPropagation = jest.fn(); renderWithContext( { const moreButton = screen.getByText('1 more'); - fireEvent.click(moreButton, { - preventDefault: mockPreventDefault, - stopPropagation: mockStopPropagation, - }); + await userEvent.click(moreButton); expect(onMorePoliciesClick).toHaveBeenCalledTimes(1); }); diff --git a/webapp/channels/src/components/team_settings/team_access_tab/open_invite.test.tsx b/webapp/channels/src/components/team_settings/team_access_tab/open_invite.test.tsx index 183a6ac5856..f30fe407fd1 100644 --- a/webapp/channels/src/components/team_settings/team_access_tab/open_invite.test.tsx +++ b/webapp/channels/src/components/team_settings/team_access_tab/open_invite.test.tsx @@ -1,10 +1,9 @@ // Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved. // See LICENSE.txt for license information. -import {fireEvent, screen} from '@testing-library/react'; import React, {type ComponentProps} from 'react'; -import {renderWithContext} from 'tests/react_testing_utils'; +import {renderWithContext, screen, userEvent} from 'tests/react_testing_utils'; import OpenInvite from './open_invite'; @@ -41,12 +40,12 @@ describe('components/TeamSettings/OpenInvite', () => { expect(checkbox).toBeChecked(); }); - test('should call setAllowOpenInvite when the checkbox is clicked', () => { + test('should call setAllowOpenInvite when the checkbox is clicked', async () => { renderWithContext(); const checkbox = screen.getByRole('checkbox'); expect(checkbox).toBeInTheDocument(); expect(checkbox).not.toBeChecked(); - fireEvent.click(checkbox); + await userEvent.click(checkbox); expect(setAllowOpenInvite).toHaveBeenCalledTimes(1); expect(setAllowOpenInvite).toHaveBeenCalledWith(true); }); diff --git a/webapp/channels/src/components/thread_popout/thread_popout.test.tsx b/webapp/channels/src/components/thread_popout/thread_popout.test.tsx index 7524eb96a92..1bed4cb378f 100644 --- a/webapp/channels/src/components/thread_popout/thread_popout.test.tsx +++ b/webapp/channels/src/components/thread_popout/thread_popout.test.tsx @@ -127,7 +127,6 @@ describe('ThreadPopout', () => { }; beforeEach(() => { - jest.clearAllMocks(); Object.defineProperty(window, 'isActive', { writable: true, value: false, diff --git a/webapp/channels/src/components/three_days_left_trial_modal/three_days_left_trial_modal.test.tsx b/webapp/channels/src/components/three_days_left_trial_modal/three_days_left_trial_modal.test.tsx index 49ee385fcce..1f3d24ee3a1 100644 --- a/webapp/channels/src/components/three_days_left_trial_modal/three_days_left_trial_modal.test.tsx +++ b/webapp/channels/src/components/three_days_left_trial_modal/three_days_left_trial_modal.test.tsx @@ -72,10 +72,6 @@ describe('components/three_days_left_trial_modal/three_days_left_trial_modal', ( limitsOverpassed: false, }; - beforeEach(() => { - jest.clearAllMocks(); - }); - test('should render the modal with header, subtitle, feature cards, and view plans button', () => { renderWithContext( , diff --git a/webapp/channels/src/components/toast_wrapper/toast_wrapper.test.tsx b/webapp/channels/src/components/toast_wrapper/toast_wrapper.test.tsx index 3a0973cd42c..c4275191850 100644 --- a/webapp/channels/src/components/toast_wrapper/toast_wrapper.test.tsx +++ b/webapp/channels/src/components/toast_wrapper/toast_wrapper.test.tsx @@ -1,7 +1,6 @@ // Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved. // See LICENSE.txt for license information. -import {fireEvent, screen} from '@testing-library/react'; import React from 'react'; import type {ComponentProps} from 'react'; @@ -12,7 +11,7 @@ import {HINT_TOAST_TESTID} from 'components/hint-toast/hint_toast'; import {SCROLL_TO_BOTTOM_DISMISS_BUTTON_TESTID, SCROLL_TO_BOTTOM_TOAST_TESTID} from 'components/scroll_to_bottom_toast/scroll_to_bottom_toast'; import {shallowWithIntl} from 'tests/helpers/intl-test-helper'; -import {renderWithContext} from 'tests/react_testing_utils'; +import {renderWithContext, screen, userEvent} from 'tests/react_testing_utils'; import {getHistory} from 'utils/browser_history'; import {PostListRowListIds} from 'utils/constants'; @@ -692,7 +691,7 @@ describe('components/ToastWrapper', () => { expect(hintToast).toBeInTheDocument(); }); - test('should call scrollToLatestMessages on click, and hide this toast (do not call dismiss function)', () => { + test('should call scrollToLatestMessages on click, and hide this toast (do not call dismiss function)', async () => { const props = { ...baseProps, showScrollToBottomToast: true, @@ -700,7 +699,7 @@ describe('components/ToastWrapper', () => { renderWithContext(); const scrollToBottomToast = screen.getByTestId(SCROLL_TO_BOTTOM_TOAST_TESTID); - fireEvent.click(scrollToBottomToast); + await userEvent.click(scrollToBottomToast); expect(baseProps.scrollToLatestMessages).toHaveBeenCalledTimes(1); @@ -709,7 +708,7 @@ describe('components/ToastWrapper', () => { expect(baseProps.hideScrollToBottomToast).toHaveBeenCalledTimes(1); }); - test('should call the dismiss callback', () => { + test('should call the dismiss callback', async () => { const props = { ...baseProps, showScrollToBottomToast: true, @@ -717,7 +716,7 @@ describe('components/ToastWrapper', () => { renderWithContext(); const scrollToBottomToastDismiss = screen.getByTestId(SCROLL_TO_BOTTOM_DISMISS_BUTTON_TESTID); - fireEvent.click(scrollToBottomToastDismiss); + await userEvent.click(scrollToBottomToastDismiss); expect(baseProps.onScrollToBottomToastDismiss).toHaveBeenCalledTimes(1); }); diff --git a/webapp/channels/src/components/user_settings/general/user_settings_general.test.tsx b/webapp/channels/src/components/user_settings/general/user_settings_general.test.tsx index b8b7caca9a4..998e7d38375 100644 --- a/webapp/channels/src/components/user_settings/general/user_settings_general.test.tsx +++ b/webapp/channels/src/components/user_settings/general/user_settings_general.test.tsx @@ -771,7 +771,7 @@ describe('components/user_settings/general/UserSettingsGeneral', () => { // Type the invalid value await userEvent.type(input, 'ftp://invalid-scheme'); - // Focus and blur explicitly to trigger validation without relatedTarget + // Trigger validation - fireEvent used because userEvent doesn't have direct focus/blur methods await act(async () => { fireEvent.focus(input); fireEvent.blur(input, {relatedTarget: null}); @@ -815,7 +815,6 @@ describe('components/user_settings/general/UserSettingsGeneral', () => { // Type the invalid value await userEvent.type(input, 'invalid-email'); - // Focus and blur explicitly to trigger validation without relatedTarget await act(async () => { fireEvent.focus(input); fireEvent.blur(input, {relatedTarget: null}); diff --git a/webapp/channels/src/components/user_settings/headers/setting_mobile_header.test.tsx b/webapp/channels/src/components/user_settings/headers/setting_mobile_header.test.tsx index b6e7f0441c5..3f9617a9549 100644 --- a/webapp/channels/src/components/user_settings/headers/setting_mobile_header.test.tsx +++ b/webapp/channels/src/components/user_settings/headers/setting_mobile_header.test.tsx @@ -1,11 +1,11 @@ // Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved. // See LICENSE.txt for license information. -import {screen, fireEvent} from '@testing-library/react'; +import {screen} from '@testing-library/react'; import type {ComponentProps} from 'react'; import React from 'react'; -import {renderWithContext} from 'tests/react_testing_utils'; +import {renderWithContext, userEvent} from 'tests/react_testing_utils'; import SettingMobileHeader from './setting_mobile_header'; @@ -18,15 +18,15 @@ const baseProps: Props = { }; describe('plugin tab', () => { - it('calls closeModal on hitting close', () => { + it('calls closeModal on hitting close', async () => { renderWithContext(); - fireEvent.click(screen.getByText('×')); + await userEvent.click(screen.getByText('×')); expect(baseProps.closeModal).toHaveBeenCalled(); }); - it('calls collapseModal on hitting back', () => { + it('calls collapseModal on hitting back', async () => { renderWithContext(); - fireEvent.click(screen.getByLabelText('Collapse Icon')); + await userEvent.click(screen.getByLabelText('Collapse Icon')); expect(baseProps.collapseModal).toHaveBeenCalled(); }); diff --git a/webapp/channels/src/components/user_settings/plugin/plugin_action.test.tsx b/webapp/channels/src/components/user_settings/plugin/plugin_action.test.tsx index 0ef5103534e..ea1d697ae3b 100644 --- a/webapp/channels/src/components/user_settings/plugin/plugin_action.test.tsx +++ b/webapp/channels/src/components/user_settings/plugin/plugin_action.test.tsx @@ -1,11 +1,10 @@ // Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved. // See LICENSE.txt for license information. -import {fireEvent, render, screen} from '@testing-library/react'; import type {ComponentProps} from 'react'; import React from 'react'; -import {renderWithContext} from 'tests/react_testing_utils'; +import {render, renderWithContext, screen, userEvent} from 'tests/react_testing_utils'; import PluginAction from './plugin_action'; @@ -28,14 +27,14 @@ describe('PluginAction', () => { expect(container.firstChild).toBeNull(); }); - it('does show the correct information', () => { + it('does show the correct information', async () => { const props = getBaseProps(); renderWithContext(); const button = screen.getByText(props.action!.buttonText); expect(button).toBeInTheDocument(); expect(screen.queryByText(props.action!.text)).toBeInTheDocument(); expect(screen.queryByText(props.action!.title)).toBeInTheDocument(); - fireEvent.click(button); + await userEvent.click(button); expect(props.action?.onClick).toHaveBeenCalled(); }); }); diff --git a/webapp/channels/src/components/user_settings/plugin/plugin_setting.test.tsx b/webapp/channels/src/components/user_settings/plugin/plugin_setting.test.tsx index f99917f4f44..447aad00981 100644 --- a/webapp/channels/src/components/user_settings/plugin/plugin_setting.test.tsx +++ b/webapp/channels/src/components/user_settings/plugin/plugin_setting.test.tsx @@ -1,7 +1,7 @@ // Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved. // See LICENSE.txt for license information. -import {fireEvent, screen} from '@testing-library/react'; +import {screen} from '@testing-library/react'; import type {ComponentProps} from 'react'; import React from 'react'; @@ -78,13 +78,13 @@ describe('plugin setting', () => { expect(screen.queryByText(OPTION_1_TEXT)).toBeInTheDocument(); }); - it('isDisabled is respected', () => { + it('isDisabled is respected', async () => { const props = getBaseProps(); props.section.disabled = true; renderWithContext(); expect(screen.queryByText('Edit')).not.toBeInTheDocument(); expect(screen.queryByText(SECTION_TITLE)).toBeInTheDocument(); - fireEvent.click(screen.getByText(SECTION_TITLE)); + await userEvent.click(screen.getByText(SECTION_TITLE)); expect(screen.queryByText(OPTION_1_TEXT)).not.toBeInTheDocument(); }); @@ -149,39 +149,39 @@ describe('plugin setting', () => { }, ]); }); - it('does not update anything if nothing has changed', () => { + it('does not update anything if nothing has changed', async () => { const mockSavePreferences = jest.spyOn(preferencesActions, 'savePreferences'); const props = getBaseProps(); props.activeSection = SECTION_TITLE; renderWithContext(); - fireEvent.click(screen.getByText(SAVE_TEXT)); + await userEvent.click(screen.getByText(SAVE_TEXT)); expect(props.section.onSubmit).not.toHaveBeenCalled(); expect(props.updateSection).toHaveBeenCalledWith(''); expect(mockSavePreferences).not.toHaveBeenCalled(); }); - it('does not consider anything changed after moving back and forth between sections', () => { + it('does not consider anything changed after moving back and forth between sections', async () => { const mockSavePreferences = jest.spyOn(preferencesActions, 'savePreferences'); const props = getBaseProps(); props.activeSection = SECTION_TITLE; const {rerender} = renderWithContext(); - fireEvent.click(screen.getByText(OPTION_1_TEXT)); + await userEvent.click(screen.getByText(OPTION_1_TEXT)); props.activeSection = ''; rerender(); props.activeSection = SECTION_TITLE; rerender(); - fireEvent.click(screen.getByText(SAVE_TEXT)); + await userEvent.click(screen.getByText(SAVE_TEXT)); expect(props.section.onSubmit).not.toHaveBeenCalled(); expect(props.updateSection).toHaveBeenCalledWith(''); expect(mockSavePreferences).not.toHaveBeenCalled(); - fireEvent.click(screen.getByText(OPTION_1_TEXT)); + await userEvent.click(screen.getByText(OPTION_1_TEXT)); props.activeSection = 'other section'; rerender(); props.activeSection = SECTION_TITLE; rerender(); - fireEvent.click(screen.getByText(SAVE_TEXT)); + await userEvent.click(screen.getByText(SAVE_TEXT)); expect(props.section.onSubmit).not.toHaveBeenCalled(); expect(props.updateSection).toHaveBeenCalledWith(''); expect(mockSavePreferences).not.toHaveBeenCalled(); diff --git a/webapp/channels/src/components/user_settings/plugin/radio.test.tsx b/webapp/channels/src/components/user_settings/plugin/radio.test.tsx index 9450e6c02ec..69084bf255d 100644 --- a/webapp/channels/src/components/user_settings/plugin/radio.test.tsx +++ b/webapp/channels/src/components/user_settings/plugin/radio.test.tsx @@ -1,7 +1,6 @@ // Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved. // See LICENSE.txt for license information. -import {screen, fireEvent} from '@testing-library/react'; import type {ComponentProps} from 'react'; import React from 'react'; @@ -9,7 +8,7 @@ import type {DeepPartial} from '@mattermost/types/utilities'; import {getPreferenceKey} from 'mattermost-redux/utils/preference_utils'; -import {renderWithContext} from 'tests/react_testing_utils'; +import {renderWithContext, screen, userEvent} from 'tests/react_testing_utils'; import {getPluginPreferenceKey} from 'utils/plugins/preferences'; import type {GlobalState} from 'types/store'; @@ -57,11 +56,11 @@ describe('radio', () => { expect(screen.queryByText(props.setting.title!)).toBeInTheDocument(); }); - it('inform change is called', () => { + it('inform change is called', async () => { const props = getBaseProps(); renderWithContext(); - fireEvent.click(screen.getByText(OPTION_1_TEXT)); + await userEvent.click(screen.getByText(OPTION_1_TEXT)); expect(props.informChange).toHaveBeenCalledWith(SETTING_NAME, '1'); }); @@ -122,7 +121,7 @@ describe('radio', () => { expect((option1Radio as HTMLInputElement).checked).toBeTruthy(); }); - it('properly persist changes', () => { + it('properly persist changes', async () => { renderWithContext(); const option0Radio = screen.getByText(OPTION_0_TEXT).children[0]; @@ -132,7 +131,7 @@ describe('radio', () => { expect((option0Radio as HTMLInputElement).checked).toBeTruthy(); expect((option1Radio as HTMLInputElement).checked).toBeFalsy(); - fireEvent.click(option1Radio); + await userEvent.click(option1Radio); expect((option0Radio as HTMLInputElement).checked).toBeFalsy(); expect((option1Radio as HTMLInputElement).checked).toBeTruthy(); }); diff --git a/webapp/channels/src/components/user_settings/plugin/radio_option.test.tsx b/webapp/channels/src/components/user_settings/plugin/radio_option.test.tsx index d60fdb3f5ed..924a701b98f 100644 --- a/webapp/channels/src/components/user_settings/plugin/radio_option.test.tsx +++ b/webapp/channels/src/components/user_settings/plugin/radio_option.test.tsx @@ -1,11 +1,10 @@ // Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved. // See LICENSE.txt for license information. -import {screen, fireEvent} from '@testing-library/react'; import type {ComponentProps} from 'react'; import React from 'react'; -import {renderWithContext} from 'tests/react_testing_utils'; +import {renderWithContext, screen, userEvent} from 'tests/react_testing_utils'; import RadioOption from './radio_option'; @@ -33,11 +32,11 @@ describe('radio option', () => { expect(screen.queryByText(props.option.helpText!)).toBeInTheDocument(); }); - it('onSelected is properly called', () => { + it('onSelected is properly called', async () => { const props = getBaseProps(); renderWithContext(); - fireEvent.click(screen.getByText(props.option.text)); + await userEvent.click(screen.getByText(props.option.text)); expect(props.onSelected).toHaveBeenCalledWith(props.option.value); }); diff --git a/webapp/channels/src/components/widgets/admin_console/admin_panel.test.tsx b/webapp/channels/src/components/widgets/admin_console/admin_panel.test.tsx index e9636e96c50..83a7c661cfb 100644 --- a/webapp/channels/src/components/widgets/admin_console/admin_panel.test.tsx +++ b/webapp/channels/src/components/widgets/admin_console/admin_panel.test.tsx @@ -1,10 +1,9 @@ // Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved. // See LICENSE.txt for license information. -import userEvent from '@testing-library/user-event'; import React from 'react'; -import {renderWithContext, screen} from 'tests/react_testing_utils'; +import {renderWithContext, screen, userEvent} from 'tests/react_testing_utils'; import AdminPanel from './admin_panel'; diff --git a/webapp/channels/src/components/widgets/admin_console/admin_panel_with_button.test.tsx b/webapp/channels/src/components/widgets/admin_console/admin_panel_with_button.test.tsx index d13103e714d..cc22f186376 100644 --- a/webapp/channels/src/components/widgets/admin_console/admin_panel_with_button.test.tsx +++ b/webapp/channels/src/components/widgets/admin_console/admin_panel_with_button.test.tsx @@ -1,10 +1,9 @@ // Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved. // See LICENSE.txt for license information. -import userEvent from '@testing-library/user-event'; import React from 'react'; -import {renderWithContext, screen} from 'tests/react_testing_utils'; +import {renderWithContext, screen, userEvent} from 'tests/react_testing_utils'; import AdminPanelWithButton from './admin_panel_with_button'; diff --git a/webapp/channels/src/components/widgets/admin_console/admin_panel_with_link.test.tsx b/webapp/channels/src/components/widgets/admin_console/admin_panel_with_link.test.tsx index 5e951c3dca3..335e31662f6 100644 --- a/webapp/channels/src/components/widgets/admin_console/admin_panel_with_link.test.tsx +++ b/webapp/channels/src/components/widgets/admin_console/admin_panel_with_link.test.tsx @@ -1,10 +1,9 @@ // Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved. // See LICENSE.txt for license information. -import userEvent from '@testing-library/user-event'; import React from 'react'; -import {renderWithContext, screen} from 'tests/react_testing_utils'; +import {renderWithContext, screen, userEvent} from 'tests/react_testing_utils'; import AdminPanelWithLink from './admin_panel_with_link'; diff --git a/webapp/channels/src/components/widgets/advanced_textbox/advanced_textbox.test.tsx b/webapp/channels/src/components/widgets/advanced_textbox/advanced_textbox.test.tsx index 0689081dcef..1506dcc599f 100644 --- a/webapp/channels/src/components/widgets/advanced_textbox/advanced_textbox.test.tsx +++ b/webapp/channels/src/components/widgets/advanced_textbox/advanced_textbox.test.tsx @@ -1,11 +1,11 @@ // Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved. // See LICENSE.txt for license information. -import {render, screen, fireEvent} from '@testing-library/react'; -import userEvent from '@testing-library/user-event'; import React from 'react'; import type {ComponentProps} from 'react'; +import {fireEvent, render, screen, userEvent} from 'tests/react_testing_utils'; + import AdvancedTextbox from './advanced_textbox'; // Mock dependencies @@ -52,10 +52,6 @@ describe('AdvancedTextbox', () => { descriptionMessage: 'This is a description', }; - beforeEach(() => { - jest.clearAllMocks(); - }); - test('renders correctly with all props', () => { render(); @@ -274,6 +270,8 @@ describe('AdvancedTextbox', () => { // Focus the textbox const textbox = screen.getByTestId('mock-textbox'); + + // Simulate focus event - fireEvent used because userEvent doesn't have direct focus/blur methods fireEvent.focus(textbox); // Label should now have active class @@ -286,13 +284,14 @@ describe('AdvancedTextbox', () => { // Focus the textbox const textbox = screen.getByTestId('mock-textbox'); + fireEvent.focus(textbox); // Label should have active class let label = document.querySelector('.AdvancedTextbox__label'); expect(label).toHaveClass('AdvancedTextbox__label--active'); - // Blur the textbox + // Simulate blur event - fireEvent used because userEvent doesn't have direct focus/blur methods fireEvent.blur(textbox); // Label should not have active class @@ -305,13 +304,13 @@ describe('AdvancedTextbox', () => { // Focus the textbox const textbox = screen.getByTestId('mock-textbox'); + fireEvent.focus(textbox); // Label should have active class let label = document.querySelector('.AdvancedTextbox__label'); expect(label).toHaveClass('AdvancedTextbox__label--active'); - // Blur the textbox fireEvent.blur(textbox); // Label should still have active class because there's a value diff --git a/webapp/channels/src/components/widgets/inputs/input/input.test.tsx b/webapp/channels/src/components/widgets/inputs/input/input.test.tsx index d21158bb629..8b3e01e4fd7 100644 --- a/webapp/channels/src/components/widgets/inputs/input/input.test.tsx +++ b/webapp/channels/src/components/widgets/inputs/input/input.test.tsx @@ -1,11 +1,9 @@ // Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved. // See LICENSE.txt for license information. -import {act, screen, fireEvent} from '@testing-library/react'; -import userEvent from '@testing-library/user-event'; import React from 'react'; -import {renderWithContext} from 'tests/react_testing_utils'; +import {act, fireEvent, renderWithContext, screen, userEvent} from 'tests/react_testing_utils'; import Input from './input'; @@ -74,6 +72,7 @@ describe('components/widgets/inputs/Input', () => { const input = container.querySelector('input') as HTMLInputElement; + // Trigger validation on blur - fireEvent used because userEvent doesn't have direct focus/blur methods await act(async () => { fireEvent.focus(input); fireEvent.blur(input); @@ -119,7 +118,7 @@ describe('components/widgets/inputs/Input', () => { // Click the button await act(async () => { - fireEvent.click(button); + await userEvent.click(button); }); // Should validate after click diff --git a/webapp/channels/src/components/widgets/menu/menu.test.tsx b/webapp/channels/src/components/widgets/menu/menu.test.tsx index 65f38503bad..9e51c99e8a6 100644 --- a/webapp/channels/src/components/widgets/menu/menu.test.tsx +++ b/webapp/channels/src/components/widgets/menu/menu.test.tsx @@ -3,7 +3,7 @@ import React from 'react'; -import {fireEvent, render, screen} from 'tests/react_testing_utils'; +import {render, screen, userEvent} from 'tests/react_testing_utils'; import Menu from './menu'; @@ -239,7 +239,7 @@ describe('components/Menu', () => { expect(dividers[0]).toHaveStyle({display: 'block'}); }); - test('should keep menu open when clicking empty space but allow closing from menu items', () => { + test('should keep menu open when clicking empty space but allow closing from menu items', async () => { const TestComponent = () => { const [isMenuOpen, setIsMenuOpen] = React.useState(true); @@ -273,14 +273,14 @@ describe('components/Menu', () => { expect(screen.queryByTestId('menu-closed')).not.toBeInTheDocument(); // Clicking empty space in menu should NOT close it - fireEvent.click(menu); + await userEvent.click(menu); menu = screen.getByRole('menu'); expect(menu).toBeInTheDocument(); expect(screen.queryByTestId('menu-closed')).not.toBeInTheDocument(); // But clicking a menu item SHOULD close it (event bubbles normally) const menuItem = screen.getByRole('button', {name: 'Close Menu'}); - fireEvent.click(menuItem); + await userEvent.click(menuItem); expect(screen.queryByRole('menu')).not.toBeInTheDocument(); expect(screen.getByTestId('menu-closed')).toBeInTheDocument(); }); diff --git a/webapp/channels/src/components/widgets/menu/menu_group.test.tsx b/webapp/channels/src/components/widgets/menu/menu_group.test.tsx index 0d8bfa1c0da..e9adccedfe7 100644 --- a/webapp/channels/src/components/widgets/menu/menu_group.test.tsx +++ b/webapp/channels/src/components/widgets/menu/menu_group.test.tsx @@ -3,7 +3,7 @@ import React from 'react'; -import {fireEvent, render, screen} from 'tests/react_testing_utils'; +import {render, screen, userEvent} from 'tests/react_testing_utils'; import MenuGroup from './menu_group'; @@ -29,13 +29,13 @@ describe('components/MenuItem', () => { expect(screen.queryByRole('separator')).not.toBeInTheDocument(); }); - test('should prevent default and stop propagation when divider is clicked', () => { + test('should prevent default and stop propagation when divider is clicked', async () => { render({'text'}); const separator = screen.getByRole('separator'); // Fire click event on separator - fireEvent.click(separator); + await userEvent.click(separator); // Verify the element exists and is clickable expect(separator).toBeInTheDocument(); diff --git a/webapp/channels/src/components/widgets/menu/menu_modals/submenu_modal/submenu_modal.test.tsx b/webapp/channels/src/components/widgets/menu/menu_modals/submenu_modal/submenu_modal.test.tsx index b7ad233a87c..470c4587346 100644 --- a/webapp/channels/src/components/widgets/menu/menu_modals/submenu_modal/submenu_modal.test.tsx +++ b/webapp/channels/src/components/widgets/menu/menu_modals/submenu_modal/submenu_modal.test.tsx @@ -1,7 +1,7 @@ // Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved. // See LICENSE.txt for license information. -import {fireEvent, waitForElementToBeRemoved} from '@testing-library/react'; +import {waitForElementToBeRemoved} from '@testing-library/react'; import {shallow} from 'enzyme'; import React from 'react'; import {Modal} from 'react-bootstrap'; @@ -65,7 +65,7 @@ describe('components/submenu_modal', () => { screen.getByText('Text B'); screen.getByText('Text C'); - fireEvent.click(view.getByTestId('SubMenuModalBody')); + await userEvent.click(view.getByTestId('SubMenuModalBody')); await waitForElementToBeRemoved(() => screen.getByText('Text A')); expect(screen.queryAllByText('Text B').length).toBe(0); diff --git a/webapp/channels/src/components/widgets/menu/menu_wrapper.test.tsx b/webapp/channels/src/components/widgets/menu/menu_wrapper.test.tsx index 40a1ccc8195..16580bccbe9 100644 --- a/webapp/channels/src/components/widgets/menu/menu_wrapper.test.tsx +++ b/webapp/channels/src/components/widgets/menu/menu_wrapper.test.tsx @@ -1,10 +1,9 @@ // Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved. // See LICENSE.txt for license information. -import userEvent from '@testing-library/user-event'; import React from 'react'; -import {fireEvent, render} from 'tests/react_testing_utils'; +import {fireEvent, render, userEvent} from 'tests/react_testing_utils'; import MenuWrapper from './menu_wrapper'; @@ -160,6 +159,7 @@ describe('components/MenuWrapper', () => { expect(wrapper).toHaveClass('MenuWrapper--open'); // Press ESC key + // fireEvent on document used because userEvent.keyboard requires element focus fireEvent.keyUp(document, {key: 'Escape', code: 'Escape'}); // Menu should be closed @@ -190,6 +190,8 @@ describe('components/MenuWrapper', () => { // Simulate TAB key to an element outside the menu const outsideButton = container.querySelectorAll('button')[2]; + + // fireEvent on document used because userEvent.keyboard requires element focus fireEvent.keyUp(outsideButton, {key: 'Tab', code: 'Tab'}); // Menu should be closed @@ -217,6 +219,8 @@ describe('components/MenuWrapper', () => { // Simulate TAB key within the menu const menuButton = container.querySelectorAll('button')[1]; + + // fireEvent on document used because userEvent.keyboard requires element focus fireEvent.keyUp(menuButton, {key: 'Tab', code: 'Tab'}); // Menu should still be open @@ -244,6 +248,7 @@ describe('components/MenuWrapper', () => { onToggle.mockClear(); // Press ESC key + // fireEvent on document used because userEvent.keyboard requires element focus fireEvent.keyUp(document, {key: 'Escape', code: 'Escape'}); // onToggle should be called with false @@ -262,6 +267,7 @@ describe('components/MenuWrapper', () => { const wrapper = container.querySelector('.MenuWrapper'); // Menu is closed by default, press ESC + // fireEvent on document used because userEvent.keyboard requires element focus fireEvent.keyUp(document, {key: 'Escape', code: 'Escape'}); // onToggle should not be called since menu was already closed diff --git a/webapp/channels/src/components/widgets/modals/components/react_select_item.test.tsx b/webapp/channels/src/components/widgets/modals/components/react_select_item.test.tsx index 408390a6f3c..d5a1d84840c 100644 --- a/webapp/channels/src/components/widgets/modals/components/react_select_item.test.tsx +++ b/webapp/channels/src/components/widgets/modals/components/react_select_item.test.tsx @@ -16,10 +16,6 @@ const mockIntl = { }; describe('getOptionLabel', () => { - beforeEach(() => { - jest.clearAllMocks(); - }); - test('should return string label as-is', () => { const option: SelectOption = { value: 'test', diff --git a/webapp/channels/src/components/widgets/settings/bool_setting.test.tsx b/webapp/channels/src/components/widgets/settings/bool_setting.test.tsx index 4ac234fd310..32cc0337abe 100644 --- a/webapp/channels/src/components/widgets/settings/bool_setting.test.tsx +++ b/webapp/channels/src/components/widgets/settings/bool_setting.test.tsx @@ -1,10 +1,9 @@ // Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved. // See LICENSE.txt for license information. -import userEvent from '@testing-library/user-event'; import React from 'react'; -import {render, screen} from 'tests/react_testing_utils'; +import {render, screen, userEvent} from 'tests/react_testing_utils'; import BoolSetting from './bool_setting'; diff --git a/webapp/channels/src/components/widgets/settings/radio_setting.test.tsx b/webapp/channels/src/components/widgets/settings/radio_setting.test.tsx index dcf0b0aabdc..99ab8ee69fb 100644 --- a/webapp/channels/src/components/widgets/settings/radio_setting.test.tsx +++ b/webapp/channels/src/components/widgets/settings/radio_setting.test.tsx @@ -1,10 +1,9 @@ // Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved. // See LICENSE.txt for license information. -import userEvent from '@testing-library/user-event'; import React from 'react'; -import {render, screen} from 'tests/react_testing_utils'; +import {render, screen, userEvent} from 'tests/react_testing_utils'; import RadioSetting from './radio_setting'; diff --git a/webapp/channels/src/components/widgets/settings/text_setting.test.tsx b/webapp/channels/src/components/widgets/settings/text_setting.test.tsx index 7f370b7f2f9..7d6e74acd26 100644 --- a/webapp/channels/src/components/widgets/settings/text_setting.test.tsx +++ b/webapp/channels/src/components/widgets/settings/text_setting.test.tsx @@ -1,10 +1,9 @@ // Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved. // See LICENSE.txt for license information. -import userEvent from '@testing-library/user-event'; import React from 'react'; -import {render, screen} from 'tests/react_testing_utils'; +import {render, screen, userEvent} from 'tests/react_testing_utils'; import TextSetting from './text_setting'; diff --git a/webapp/channels/src/components/widgets/tag/alert_tag.test.tsx b/webapp/channels/src/components/widgets/tag/alert_tag.test.tsx index 97a5cd6004a..63e8767f818 100644 --- a/webapp/channels/src/components/widgets/tag/alert_tag.test.tsx +++ b/webapp/channels/src/components/widgets/tag/alert_tag.test.tsx @@ -1,10 +1,10 @@ // Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved. // See LICENSE.txt for license information. -import {screen, render} from '@testing-library/react'; -import userEvent from '@testing-library/user-event'; import React from 'react'; +import {render, screen, userEvent} from 'tests/react_testing_utils'; + import AlertTag from './alert_tag'; describe('components/widgets/tag/AlertTag', () => { diff --git a/webapp/channels/src/components/widgets/tag/tag.test.tsx b/webapp/channels/src/components/widgets/tag/tag.test.tsx index cf3ed9bcca4..56b1a7d5651 100644 --- a/webapp/channels/src/components/widgets/tag/tag.test.tsx +++ b/webapp/channels/src/components/widgets/tag/tag.test.tsx @@ -1,10 +1,10 @@ // Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved. // See LICENSE.txt for license information. -import {render, screen} from '@testing-library/react'; -import userEvent from '@testing-library/user-event'; import React from 'react'; +import {render, screen, userEvent} from 'tests/react_testing_utils'; + import Tag from './tag'; describe('components/widgets/tag/Tag', () => { diff --git a/webapp/channels/src/components/widgets/users/avatar/avatar.test.tsx b/webapp/channels/src/components/widgets/users/avatar/avatar.test.tsx index 458e2cacbe2..fcf86d81b79 100644 --- a/webapp/channels/src/components/widgets/users/avatar/avatar.test.tsx +++ b/webapp/channels/src/components/widgets/users/avatar/avatar.test.tsx @@ -86,7 +86,7 @@ describe('components/widgets/users/Avatar', () => { const avatar = screen.getByRole('img') as HTMLImageElement; expect(avatar.src).toContain('userid123'); - // Simulate image load error + // Simulate image error event - fireEvent used because userEvent doesn't support image loading events fireEvent.error(avatar); // Should fall back to default user avatar (replace ?_= with /default) @@ -103,7 +103,6 @@ describe('components/widgets/users/Avatar', () => { const avatar = screen.getByRole('img') as HTMLImageElement; const initialSrc = avatar.src; - // Simulate image load error fireEvent.error(avatar); // Should change from the initial URL (bot icon set, even if empty in test environment) @@ -124,7 +123,6 @@ describe('components/widgets/users/Avatar', () => { const avatar = screen.getByRole('img') as HTMLImageElement; const initialSrc = avatar.src; - // Simulate image load error fireEvent.error(avatar); // Should not change src if it's already the fallback diff --git a/webapp/channels/src/components/with_tooltip/index.test.tsx b/webapp/channels/src/components/with_tooltip/index.test.tsx index e7d5dcedad0..1d51a96a11b 100644 --- a/webapp/channels/src/components/with_tooltip/index.test.tsx +++ b/webapp/channels/src/components/with_tooltip/index.test.tsx @@ -1,10 +1,9 @@ // Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved. // See LICENSE.txt for license information. -import userEvent from '@testing-library/user-event'; import React from 'react'; -import {renderWithContext, screen, waitFor} from 'tests/react_testing_utils'; +import {renderWithContext, screen, userEvent, waitFor} from 'tests/react_testing_utils'; import WithTooltip from './index'; diff --git a/webapp/channels/src/components/youtube_video/youtube_video.test.tsx b/webapp/channels/src/components/youtube_video/youtube_video.test.tsx index 2206a6da905..4a7e3d92ebd 100644 --- a/webapp/channels/src/components/youtube_video/youtube_video.test.tsx +++ b/webapp/channels/src/components/youtube_video/youtube_video.test.tsx @@ -130,7 +130,7 @@ describe('YoutubeVideo', () => { 'https://img.youtube.com/vi/xqCoNej8Zxo/maxresdefault.jpg', ); - // Simulate an image error + // Simulate thumbnail loading failure to test fallback behavior - fireEvent used because userEvent doesn't support image loading events fireEvent.error(thumbnail!); // Verify that hqdefault is used after error (useMaxResThumbnail is now false) diff --git a/webapp/channels/src/hooks/useChannelAccessControlActions.test.ts b/webapp/channels/src/hooks/useChannelAccessControlActions.test.ts index 7161bb3bdf8..97dae465321 100644 --- a/webapp/channels/src/hooks/useChannelAccessControlActions.test.ts +++ b/webapp/channels/src/hooks/useChannelAccessControlActions.test.ts @@ -25,10 +25,6 @@ const mockGetVisualAST = getVisualAST as jest.MockedFunction; describe('useChannelAccessControlActions', () => { - beforeEach(() => { - jest.clearAllMocks(); - }); - test('should have correct action imports', () => { // Test that the required actions are imported and mocked correctly expect(getAccessControlFields).toBeDefined(); diff --git a/webapp/channels/src/utils/popouts/popout_windows.test.ts b/webapp/channels/src/utils/popouts/popout_windows.test.ts index a9948aab5e0..ebf26ef9896 100644 --- a/webapp/channels/src/utils/popouts/popout_windows.test.ts +++ b/webapp/channels/src/utils/popouts/popout_windows.test.ts @@ -46,7 +46,6 @@ const getMockSetupBrowserPopout = () => { describe('popout_windows', () => { beforeEach(() => { - jest.clearAllMocks(); getMockSetupBrowserPopout().mockClear(); // Default: no subpath