Browse Source

Upgrade to React 18 (#1955)

* Upgrade deps

* migration

* Fix specs

* exclude index.tsx from sonar metrics

* Update deps
Oleg Shur 3 years ago
parent
commit
eb47ec012d
76 changed files with 1064 additions and 861 deletions
  1. 396 226
      kafka-ui-react-app/package-lock.json
  2. 21 25
      kafka-ui-react-app/package.json
  3. 1 1
      kafka-ui-react-app/sonar-project.properties
  4. 34 32
      kafka-ui-react-app/src/components/Alerts/__tests__/Alerts.spec.tsx
  5. 1 1
      kafka-ui-react-app/src/components/Brokers/Brokers.tsx
  6. 21 11
      kafka-ui-react-app/src/components/Brokers/__test__/Brokers.spec.tsx
  7. 1 1
      kafka-ui-react-app/src/components/Connect/Details/Actions/Actions.tsx
  8. 1 1
      kafka-ui-react-app/src/components/Connect/Details/Config/Config.tsx
  9. 15 12
      kafka-ui-react-app/src/components/Connect/Edit/__tests__/Edit.spec.tsx
  10. 33 31
      kafka-ui-react-app/src/components/Connect/List/__tests__/List.spec.tsx
  11. 18 12
      kafka-ui-react-app/src/components/Connect/New/__tests__/New.spec.tsx
  12. 1 1
      kafka-ui-react-app/src/components/ConsumerGroups/Details/Details.tsx
  13. 1 1
      kafka-ui-react-app/src/components/ConsumerGroups/Details/ResetOffsets/ResetOffsets.tsx
  14. 8 9
      kafka-ui-react-app/src/components/ConsumerGroups/Details/ResetOffsets/__test__/ResetOffsets.spec.tsx
  15. 1 1
      kafka-ui-react-app/src/components/ConsumerGroups/Details/TopicContents/__test__/TopicContents.spec.tsx
  16. 10 11
      kafka-ui-react-app/src/components/ConsumerGroups/Details/__tests__/Details.spec.tsx
  17. 1 1
      kafka-ui-react-app/src/components/ConsumerGroups/Details/__tests__/ListItem.spec.tsx
  18. 6 6
      kafka-ui-react-app/src/components/ConsumerGroups/__test__/ConsumerGroups.spec.tsx
  19. 1 1
      kafka-ui-react-app/src/components/KsqlDb/List/List.tsx
  20. 1 1
      kafka-ui-react-app/src/components/KsqlDb/List/__test__/List.spec.tsx
  21. 1 1
      kafka-ui-react-app/src/components/KsqlDb/Query/Query.tsx
  22. 35 57
      kafka-ui-react-app/src/components/KsqlDb/Query/QueryForm/__test__/QueryForm.spec.tsx
  23. 16 29
      kafka-ui-react-app/src/components/KsqlDb/Query/__test__/Query.spec.tsx
  24. 1 1
      kafka-ui-react-app/src/components/Nav/ClusterMenu.tsx
  25. 4 2
      kafka-ui-react-app/src/components/Nav/ClusterMenuItem.tsx
  26. 1 1
      kafka-ui-react-app/src/components/Schemas/Details/Details.tsx
  27. 14 7
      kafka-ui-react-app/src/components/Schemas/Details/__test__/Details.spec.tsx
  28. 1 1
      kafka-ui-react-app/src/components/Schemas/Diff/Diff.tsx
  29. 1 1
      kafka-ui-react-app/src/components/Schemas/Edit/Edit.tsx
  30. 12 19
      kafka-ui-react-app/src/components/Schemas/Edit/__tests__/Edit.spec.tsx
  31. 9 4
      kafka-ui-react-app/src/components/Schemas/List/GlobalSchemaSelector/__test__/GlobalSchemaSelector.spec.tsx
  32. 23 16
      kafka-ui-react-app/src/components/Schemas/List/__test__/List.spec.tsx
  33. 1 1
      kafka-ui-react-app/src/components/Schemas/New/New.tsx
  34. 1 1
      kafka-ui-react-app/src/components/Schemas/New/__test__/New.spec.tsx
  35. 1 1
      kafka-ui-react-app/src/components/Schemas/__test__/Schemas.spec.tsx
  36. 1 2
      kafka-ui-react-app/src/components/Topics/List/List.tsx
  37. 1 1
      kafka-ui-react-app/src/components/Topics/List/__tests__/List.spec.tsx
  38. 1 1
      kafka-ui-react-app/src/components/Topics/New/New.tsx
  39. 3 3
      kafka-ui-react-app/src/components/Topics/New/__test__/New.spec.tsx
  40. 1 1
      kafka-ui-react-app/src/components/Topics/Topic/Details/Messages/Filters/Filters.tsx
  41. 37 29
      kafka-ui-react-app/src/components/Topics/Topic/Details/Messages/Filters/__tests__/AddEditFilterContainer.spec.tsx
  42. 54 53
      kafka-ui-react-app/src/components/Topics/Topic/Details/Messages/Filters/__tests__/AddFilter.spec.tsx
  43. 21 9
      kafka-ui-react-app/src/components/Topics/Topic/Details/Messages/Filters/__tests__/EditFilter.spec.tsx
  44. 5 3
      kafka-ui-react-app/src/components/Topics/Topic/Details/Messages/Filters/__tests__/FilterModal.spec.tsx
  45. 38 32
      kafka-ui-react-app/src/components/Topics/Topic/Details/Messages/Filters/__tests__/Filters.spec.tsx
  46. 1 1
      kafka-ui-react-app/src/components/Topics/Topic/Details/Messages/Messages.tsx
  47. 1 1
      kafka-ui-react-app/src/components/Topics/Topic/Details/Messages/MessagesTable.tsx
  48. 1 1
      kafka-ui-react-app/src/components/Topics/Topic/Details/Messages/__test__/MessagesTable.spec.tsx
  49. 35 49
      kafka-ui-react-app/src/components/Topics/Topic/Edit/DangerZone/__test__/DangerZone.spec.tsx
  50. 1 1
      kafka-ui-react-app/src/components/Topics/Topic/Edit/Edit.tsx
  51. 8 12
      kafka-ui-react-app/src/components/Topics/Topic/Edit/__test__/Edit.spec.tsx
  52. 1 1
      kafka-ui-react-app/src/components/Topics/Topic/SendMessage/SendMessage.tsx
  53. 35 39
      kafka-ui-react-app/src/components/Topics/Topic/SendMessage/__test__/SendMessage.spec.tsx
  54. 5 5
      kafka-ui-react-app/src/components/Topics/shared/Form/CustomParams/__test__/CustomParamField.spec.tsx
  55. 15 14
      kafka-ui-react-app/src/components/Topics/shared/Form/CustomParams/__test__/CustomParams.spec.tsx
  56. 2 2
      kafka-ui-react-app/src/components/Topics/shared/Form/__tests__/TimeToRetainBtn.spec.tsx
  57. 2 2
      kafka-ui-react-app/src/components/Topics/shared/Form/__tests__/TimeToRetainBtns.spec.tsx
  58. 2 2
      kafka-ui-react-app/src/components/Topics/shared/Form/__tests__/TopicForm.spec.tsx
  59. 8 4
      kafka-ui-react-app/src/components/__tests__/App.spec.tsx
  60. 4 2
      kafka-ui-react-app/src/components/common/Breadcrumb/Breadcrumb.provider.tsx
  61. 4 2
      kafka-ui-react-app/src/components/common/ConfirmationModal/ConfirmationModal.tsx
  62. 12 2
      kafka-ui-react-app/src/components/common/Dropdown/Dropdown.tsx
  63. 2 2
      kafka-ui-react-app/src/components/common/Dropdown/DropdownItem.tsx
  64. 19 21
      kafka-ui-react-app/src/components/common/Metrics/Indicator.tsx
  65. 7 9
      kafka-ui-react-app/src/components/common/Metrics/Section.tsx
  66. 6 2
      kafka-ui-react-app/src/components/common/PageHeading/PageHeading.tsx
  67. 1 1
      kafka-ui-react-app/src/components/common/Pagination/__tests__/Pagination.spec.tsx
  68. 12 10
      kafka-ui-react-app/src/components/common/SmartTable/TableRow.tsx
  69. 2 2
      kafka-ui-react-app/src/components/common/Tabs/Tabs.tsx
  70. 5 2
      kafka-ui-react-app/src/components/common/heading/Heading.styled.tsx
  71. 4 2
      kafka-ui-react-app/src/components/common/table/TableHeaderCell/TableHeaderCell.tsx
  72. 7 4
      kafka-ui-react-app/src/index.tsx
  73. 1 1
      kafka-ui-react-app/src/lib/hooks/__tests__/useModal.spec.ts
  74. 1 1
      kafka-ui-react-app/src/lib/hooks/usePagination.ts
  75. 1 1
      kafka-ui-react-app/src/lib/hooks/useSearch.ts
  76. 4 2
      kafka-ui-react-app/src/lib/testHelpers.tsx

File diff suppressed because it is too large
+ 396 - 226
kafka-ui-react-app/package-lock.json


+ 21 - 25
kafka-ui-react-app/package.json

@@ -1,15 +1,15 @@
 {
   "name": "kafka-ui",
-  "version": "0.1.0",
+  "version": "0.4.0",
   "homepage": "./",
   "private": true,
   "dependencies": {
     "@fortawesome/fontawesome-free": "^5.15.4",
     "@hookform/error-message": "^2.0.0",
     "@hookform/resolvers": "^2.7.1",
-    "@reduxjs/toolkit": "^1.7.1",
+    "@reduxjs/toolkit": "^1.8.1",
     "@rooks/use-outside-click-ref": "^4.10.1",
-    "@testing-library/react": "^12.0.0",
+    "@testing-library/react": "^13.2.0",
     "@types/eventsource": "^1.1.6",
     "@types/yup": "^0.29.13",
     "ace-builds": "^1.4.12",
@@ -17,22 +17,22 @@
     "bulma": "^0.9.3",
     "classnames": "^2.2.6",
     "dayjs": "^1.10.6",
-    "eslint-import-resolver-node": "^0.3.5",
-    "eslint-import-resolver-typescript": "^2.4.0",
+    "eslint-import-resolver-node": "^0.3.6",
+    "eslint-import-resolver-typescript": "^2.7.1",
     "fetch-mock": "^9.11.0",
     "json-schema-faker": "^0.5.0-rcv.39",
     "lodash": "^4.17.21",
     "node-fetch": "^2.6.1",
     "pretty-ms": "^7.0.1",
-    "react": "^17.0.1",
+    "react": "^18.1.0",
     "react-ace": "^9.4.3",
     "react-datepicker": "^4.2.0",
-    "react-dom": "^17.0.1",
+    "react-dom": "^18.1.0",
     "react-hook-form": "7.6.9",
     "react-multi-select-component": "^4.0.6",
     "react-redux": "^7.2.6",
     "react-router": "^5.2.0",
-    "react-router-dom": "^5.2.0",
+    "react-router-dom": "^5.3.1",
     "redux": "^4.1.1",
     "redux-thunk": "^2.3.0",
     "sass": "^1.43.4",
@@ -79,39 +79,36 @@
   },
   "devDependencies": {
     "@jest/types": "^27.0.6",
-    "@openapitools/openapi-generator-cli": "^2.4.15",
+    "@openapitools/openapi-generator-cli": "^2.5.1",
     "@testing-library/dom": "^8.11.1",
     "@testing-library/jest-dom": "^5.14.1",
-    "@testing-library/react-hooks": "^7.0.2",
     "@testing-library/user-event": "^13.5.0",
     "@types/classnames": "^2.2.11",
     "@types/jest": "^27.0.2",
     "@types/lodash": "^4.14.172",
     "@types/node": "^16.4.13",
     "@types/node-fetch": "^3.0.3",
-    "@types/react": "^17.0.16",
+    "@types/react": "^18.0.9",
     "@types/react-datepicker": "^4.1.4",
-    "@types/react-dom": "^17.0.9",
+    "@types/react-dom": "^18.0.3",
     "@types/react-redux": "^7.1.18",
-    "@types/react-router-dom": "^5.1.8",
-    "@types/react-test-renderer": "^17.0.1",
+    "@types/react-router-dom": "^5.3.3",
     "@types/redux-mock-store": "^1.0.3",
     "@types/styled-components": "^5.1.13",
     "@types/uuid": "^8.3.1",
     "@typescript-eslint/eslint-plugin": "^5.10.0",
     "@typescript-eslint/parser": "^5.10.0",
-    "dotenv": "^15.0.0",
-    "eslint": "^8.7.0",
-    "eslint-config-airbnb": "^19.0.0",
-    "eslint-config-airbnb-typescript": "^16.1.0",
-    "eslint-config-prettier": "^8.3.0",
-    "eslint-plugin-import": "^2.25.4",
+    "dotenv": "^16.0.1",
+    "eslint": "^8.15.0",
+    "eslint-config-airbnb": "^19.0.4",
+    "eslint-config-airbnb-typescript": "^17.0.0",
+    "eslint-config-prettier": "^8.5.0",
+    "eslint-plugin-import": "^2.26.0",
     "eslint-plugin-jest-dom": "^4.0.1",
     "eslint-plugin-jsx-a11y": "^6.5.1",
     "eslint-plugin-prettier": "^4.0.0",
-    "eslint-plugin-react": "^7.28.0",
-    "eslint-plugin-react-hooks": "^4.3.0",
-    "esprint": "^3.1.0",
+    "eslint-plugin-react": "^7.29.4",
+    "eslint-plugin-react-hooks": "^4.5.0",
     "fetch-mock-jest": "^1.5.1",
     "history": "^5.0.0",
     "http-proxy-middleware": "^2.0.1",
@@ -120,8 +117,7 @@
     "jest-styled-components": "^7.0.6",
     "lint-staged": "^12.1.2",
     "prettier": "^2.3.1",
-    "react-scripts": "5.0.0",
-    "react-test-renderer": "^17.0.2",
+    "react-scripts": "5.0.1",
     "redux-mock-store": "^1.5.4",
     "rimraf": "^3.0.2",
     "ts-jest": "^26.5.4",

+ 1 - 1
kafka-ui-react-app/sonar-project.properties

@@ -2,7 +2,7 @@ sonar.projectKey=com.provectus:kafka-ui_frontend
 sonar.organization=provectus
 
 sonar.sources=.
-sonar.exclusions=**/__tests__/**,**/__test__/**,src/serviceWorker.ts,src/setupTests.ts,src/setupProxy.js,**/fixtures.ts,src/lib/testHelpers.tsx
+sonar.exclusions=**/__tests__/**,**/__test__/**,src/serviceWorker.ts,src/setupTests.ts,src/setupProxy.js,**/fixtures.ts,src/lib/testHelpers.tsx,src/index.tsx
 
 sonar.typescript.lcov.reportPaths=./coverage/lcov.info
 sonar.testExecutionReportPaths=./test-report.xml

+ 34 - 32
kafka-ui-react-app/src/components/Alerts/__tests__/Alerts.spec.tsx

@@ -1,48 +1,50 @@
 import React from 'react';
 import { Action, FailurePayload, ServerResponse } from 'redux/interfaces';
-import { screen } from '@testing-library/react';
+import { act, screen } from '@testing-library/react';
 import Alerts from 'components/Alerts/Alerts';
 import { render } from 'lib/testHelpers';
 import { store } from 'redux/store';
 import { UnknownAsyncThunkRejectedWithValueAction } from '@reduxjs/toolkit/dist/matchers';
 import userEvent from '@testing-library/user-event';
 
-describe('Alerts', () => {
-  beforeEach(() => render(<Alerts />, { store }));
+const payload: ServerResponse = {
+  status: 422,
+  statusText: 'Unprocessable Entity',
+  message: 'Unprocessable Entity',
+  url: 'https://test.com/clusters',
+};
+const action: UnknownAsyncThunkRejectedWithValueAction = {
+  type: 'any/action/rejected',
+  payload,
+  meta: {
+    arg: 'test',
+    requestId: 'test-request-id',
+    requestStatus: 'rejected',
+    aborted: false,
+    condition: false,
+    rejectedWithValue: true,
+  },
+  error: { message: 'Rejected' },
+};
+const alert: FailurePayload = {
+  title: '404 - Not Found',
+  message: 'Item is not found',
+  subject: 'subject',
+};
+const legacyAction: Action = {
+  type: 'CLEAR_TOPIC_MESSAGES__FAILURE',
+  payload: { alert },
+};
 
+describe('Alerts', () => {
   it('renders alerts', async () => {
-    const payload: ServerResponse = {
-      status: 422,
-      statusText: 'Unprocessable Entity',
-      message: 'Unprocessable Entity',
-      url: 'https://test.com/clusters',
-    };
-    const action: UnknownAsyncThunkRejectedWithValueAction = {
-      type: 'any/action/rejected',
-      payload,
-      meta: {
-        arg: 'test',
-        requestId: 'test-request-id',
-        requestStatus: 'rejected',
-        aborted: false,
-        condition: false,
-        rejectedWithValue: true,
-      },
-      error: { message: 'Rejected' },
-    };
     store.dispatch(action);
-
-    const alert: FailurePayload = {
-      title: '404 - Not Found',
-      message: 'Item is not found',
-      subject: 'subject',
-    };
-    const legacyAction: Action = {
-      type: 'CLEAR_TOPIC_MESSAGES__FAILURE',
-      payload: { alert },
-    };
     store.dispatch(legacyAction);
 
+    await act(() => {
+      render(<Alerts />, { store });
+    });
+
     expect(screen.getAllByRole('alert').length).toEqual(2);
 
     const dissmissAlertButtons = screen.getAllByRole('button');

+ 1 - 1
kafka-ui-react-app/src/components/Brokers/Brokers.tsx

@@ -2,7 +2,7 @@ import React from 'react';
 import { ClusterName } from 'redux/interfaces';
 import useInterval from 'lib/hooks/useInterval';
 import BytesFormatted from 'components/common/BytesFormatted/BytesFormatted';
-import { useParams } from 'react-router';
+import { useParams } from 'react-router-dom';
 import TableHeaderCell from 'components/common/table/TableHeaderCell/TableHeaderCell';
 import { Table } from 'components/common/table/Table/Table.styled';
 import PageHeading from 'components/common/PageHeading/PageHeading';

+ 21 - 11
kafka-ui-react-app/src/components/Brokers/__test__/Brokers.spec.tsx

@@ -2,10 +2,11 @@ import React from 'react';
 import Brokers from 'components/Brokers/Brokers';
 import { render } from 'lib/testHelpers';
 import { screen, waitFor } from '@testing-library/dom';
-import { Route } from 'react-router';
+import { Route } from 'react-router-dom';
 import { clusterBrokersPath } from 'lib/paths';
 import fetchMock from 'fetch-mock';
 import { clusterStatsPayload } from 'redux/reducers/brokers/__test__/fixtures';
+import { act } from '@testing-library/react';
 
 describe('Brokers Component', () => {
   afterEach(() => fetchMock.reset());
@@ -41,23 +42,26 @@ describe('Brokers Component', () => {
         fetchStatsUrl,
         clusterStatsPayload
       );
-      renderComponent();
-      await waitFor(() => {
-        expect(fetchStatsMock.called()).toBeTruthy();
-      });
-      await waitFor(() => {
-        expect(fetchBrokersMock.called()).toBeTruthy();
+      await act(() => {
+        renderComponent();
       });
+
+      await waitFor(() => expect(fetchStatsMock.called()).toBeTruthy());
+      await waitFor(() => expect(fetchBrokersMock.called()).toBeTruthy());
+
       expect(screen.getByRole('table')).toBeInTheDocument();
       const rows = screen.getAllByRole('row');
       expect(rows.length).toEqual(3);
     });
+
     it('shows warning when offlinePartitionCount > 0', async () => {
       const fetchStatsMock = fetchMock.getOnce(fetchStatsUrl, {
         ...clusterStatsPayload,
         offlinePartitionCount: 1345,
       });
-      renderComponent();
+      await act(() => {
+        renderComponent();
+      });
       await waitFor(() => {
         expect(fetchStatsMock.called()).toBeTruthy();
       });
@@ -76,7 +80,9 @@ describe('Brokers Component', () => {
         inSyncReplicasCount: testInSyncReplicasCount,
         outOfSyncReplicasCount: testOutOfSyncReplicasCount,
       });
-      renderComponent();
+      await act(() => {
+        renderComponent();
+      });
       await waitFor(() => {
         expect(fetchStatsMock.called()).toBeTruthy();
       });
@@ -94,7 +100,9 @@ describe('Brokers Component', () => {
         inSyncReplicasCount: undefined,
         outOfSyncReplicasCount: testOutOfSyncReplicasCount,
       });
-      renderComponent();
+      await act(() => {
+        renderComponent();
+      });
       await waitFor(() => {
         expect(fetchStatsMock.called()).toBeTruthy();
       });
@@ -108,7 +116,9 @@ describe('Brokers Component', () => {
         inSyncReplicasCount: testInSyncReplicasCount,
         outOfSyncReplicasCount: undefined,
       });
-      renderComponent();
+      await act(() => {
+        renderComponent();
+      });
       await waitFor(() => {
         expect(fetchStatsMock.called()).toBeTruthy();
       });

+ 1 - 1
kafka-ui-react-app/src/components/Connect/Details/Actions/Actions.tsx

@@ -85,7 +85,7 @@ const Actions: React.FC<ActionsProps> = ({
   }, [restartConnector, clusterName, connectName, connectorName]);
 
   const restartTasksHandler = React.useCallback(
-    (actionType) => {
+    (actionType: ConnectorAction) => {
       restartTasks({
         clusterName,
         connectName,

+ 1 - 1
kafka-ui-react-app/src/components/Connect/Details/Config/Config.tsx

@@ -1,5 +1,5 @@
 import React from 'react';
-import { useParams } from 'react-router';
+import { useParams } from 'react-router-dom';
 import {
   ClusterName,
   ConnectName,

+ 15 - 12
kafka-ui-react-app/src/components/Connect/Edit/__tests__/Edit.spec.tsx

@@ -6,9 +6,9 @@ import {
 } from 'lib/paths';
 import Edit, { EditProps } from 'components/Connect/Edit/Edit';
 import { connector } from 'redux/reducers/connect/__test__/fixtures';
-import { Route } from 'react-router';
+import { Route } from 'react-router-dom';
 import { waitFor } from '@testing-library/dom';
-import { fireEvent, screen } from '@testing-library/react';
+import { act, fireEvent, screen } from '@testing-library/react';
 
 jest.mock('components/common/PageLoader/PageLoader', () => 'mock-PageLoader');
 
@@ -52,9 +52,9 @@ describe('Edit', () => {
       }
     );
 
-  it('fetches config on mount', () => {
+  it('fetches config on mount', async () => {
     const fetchConfig = jest.fn();
-    renderComponent({ fetchConfig });
+    await waitFor(() => renderComponent({ fetchConfig }));
     expect(fetchConfig).toHaveBeenCalledTimes(1);
     expect(fetchConfig).toHaveBeenCalledWith({
       clusterName,
@@ -65,9 +65,9 @@ describe('Edit', () => {
 
   it('calls updateConfig on form submit', async () => {
     const updateConfig = jest.fn();
-    renderComponent({ updateConfig });
-    await waitFor(() => fireEvent.submit(screen.getByRole('form')));
-    expect(updateConfig).toHaveBeenCalledTimes(1);
+    await waitFor(() => renderComponent({ updateConfig }));
+    fireEvent.submit(screen.getByRole('form'));
+    await waitFor(() => expect(updateConfig).toHaveBeenCalledTimes(1));
     expect(updateConfig).toHaveBeenCalledWith({
       clusterName,
       connectName,
@@ -78,9 +78,10 @@ describe('Edit', () => {
 
   it('redirects to connector config view on successful submit', async () => {
     const updateConfig = jest.fn().mockResolvedValueOnce(connector);
-    renderComponent({ updateConfig });
-    await waitFor(() => fireEvent.submit(screen.getByRole('form')));
-    expect(mockHistoryPush).toHaveBeenCalledTimes(1);
+    await waitFor(() => renderComponent({ updateConfig }));
+    fireEvent.submit(screen.getByRole('form'));
+
+    await waitFor(() => expect(mockHistoryPush).toHaveBeenCalledTimes(1));
     expect(mockHistoryPush).toHaveBeenCalledWith(
       clusterConnectConnectorConfigPath(clusterName, connectName, connectorName)
     );
@@ -88,8 +89,10 @@ describe('Edit', () => {
 
   it('does not redirect to connector config view on unsuccessful submit', async () => {
     const updateConfig = jest.fn().mockResolvedValueOnce(undefined);
-    renderComponent({ updateConfig });
-    await waitFor(() => fireEvent.submit(screen.getByRole('form')));
+    await waitFor(() => renderComponent({ updateConfig }));
+    await act(() => {
+      fireEvent.submit(screen.getByRole('form'));
+    });
     expect(mockHistoryPush).not.toHaveBeenCalled();
   });
 });

+ 33 - 31
kafka-ui-react-app/src/components/Connect/List/__tests__/List.spec.tsx

@@ -6,13 +6,15 @@ import ClusterContext, {
 } from 'components/contexts/ClusterContext';
 import ListContainer from 'components/Connect/List/ListContainer';
 import List, { ListProps } from 'components/Connect/List/List';
-import { screen } from '@testing-library/react';
+import { act, screen } from '@testing-library/react';
 import { render } from 'lib/testHelpers';
 
 describe('Connectors List', () => {
   describe('Container', () => {
-    it('renders view with initial state of storage', () => {
-      render(<ListContainer />);
+    it('renders view with initial state of storage', async () => {
+      await act(() => {
+        render(<ListContainer />);
+      });
       expect(screen.getByRole('heading')).toHaveTextContent('Connectors');
     });
   });
@@ -21,63 +23,63 @@ describe('Connectors List', () => {
     const fetchConnects = jest.fn();
     const fetchConnectors = jest.fn();
     const setConnectorSearch = jest.fn();
-    const setupComponent = (
+    const renderComponent = (
       props: Partial<ListProps> = {},
       contextValue: ContextProps = initialValue
-    ) => (
-      <ClusterContext.Provider value={contextValue}>
-        <List
-          areConnectorsFetching
-          areConnectsFetching
-          connectors={[]}
-          connects={[]}
-          fetchConnects={fetchConnects}
-          fetchConnectors={fetchConnectors}
-          search=""
-          setConnectorSearch={setConnectorSearch}
-          {...props}
-        />
-      </ClusterContext.Provider>
-    );
+    ) => {
+      render(
+        <ClusterContext.Provider value={contextValue}>
+          <List
+            areConnectorsFetching
+            areConnectsFetching
+            connectors={[]}
+            connects={[]}
+            fetchConnects={fetchConnects}
+            fetchConnectors={fetchConnectors}
+            search=""
+            setConnectorSearch={setConnectorSearch}
+            {...props}
+          />
+        </ClusterContext.Provider>
+      );
+    };
 
-    it('renders PageLoader', () => {
-      render(setupComponent({ areConnectorsFetching: true }));
+    it('renders PageLoader', async () => {
+      await act(() => renderComponent({ areConnectorsFetching: true }));
       expect(screen.getByRole('progressbar')).toBeInTheDocument();
       expect(screen.queryByRole('row')).not.toBeInTheDocument();
     });
 
     it('renders table', () => {
-      render(setupComponent({ areConnectorsFetching: false }));
+      renderComponent({ areConnectorsFetching: false });
       expect(screen.queryByRole('progressbar')).not.toBeInTheDocument();
       expect(screen.getByRole('table')).toBeInTheDocument();
     });
 
     it('renders connectors list', () => {
-      render(
-        setupComponent({
-          areConnectorsFetching: false,
-          connectors,
-        })
-      );
+      renderComponent({
+        areConnectorsFetching: false,
+        connectors,
+      });
       expect(screen.queryByRole('progressbar')).not.toBeInTheDocument();
       expect(screen.getByRole('table')).toBeInTheDocument();
       expect(screen.getAllByRole('row').length).toEqual(3);
     });
 
     it('handles fetchConnects and fetchConnectors', () => {
-      render(setupComponent());
+      renderComponent();
       expect(fetchConnects).toHaveBeenCalledTimes(1);
       expect(fetchConnectors).toHaveBeenCalledTimes(1);
     });
 
     it('renders actions if cluster is not readonly', () => {
-      render(setupComponent({}, { ...initialValue, isReadOnly: false }));
+      renderComponent({}, { ...initialValue, isReadOnly: false });
       expect(screen.getByRole('button')).toBeInTheDocument();
     });
 
     describe('readonly cluster', () => {
       it('does not render actions if cluster is readonly', () => {
-        render(setupComponent({}, { ...initialValue, isReadOnly: true }));
+        renderComponent({}, { ...initialValue, isReadOnly: true });
         expect(screen.queryByRole('button')).not.toBeInTheDocument();
       });
     });

+ 18 - 12
kafka-ui-react-app/src/components/Connect/New/__tests__/New.spec.tsx

@@ -6,8 +6,8 @@ import {
 } from 'lib/paths';
 import New, { NewProps } from 'components/Connect/New/New';
 import { connects, connector } from 'redux/reducers/connect/__test__/fixtures';
-import { Route } from 'react-router';
-import { waitFor, fireEvent, screen } from '@testing-library/react';
+import { Route } from 'react-router-dom';
+import { fireEvent, screen, act } from '@testing-library/react';
 import userEvent from '@testing-library/user-event';
 import { ControllerRenderProps } from 'react-hook-form';
 
@@ -30,18 +30,21 @@ jest.mock('react-router-dom', () => ({
 describe('New', () => {
   const clusterName = 'my-cluster';
   const simulateFormSubmit = async () => {
-    userEvent.type(
-      screen.getByPlaceholderText('Connector Name'),
-      'my-connector'
-    );
-    userEvent.type(
-      screen.getByPlaceholderText('json'),
-      '{"class":"MyClass"}'.replace(/[{[]/g, '$&$&')
-    );
+    await act(() => {
+      userEvent.type(
+        screen.getByPlaceholderText('Connector Name'),
+        'my-connector'
+      );
+      userEvent.type(
+        screen.getByPlaceholderText('json'),
+        '{"class":"MyClass"}'.replace(/[{[]/g, '$&$&')
+      );
+    });
+
     expect(screen.getByPlaceholderText('json')).toHaveValue(
       '{"class":"MyClass"}'
     );
-    await waitFor(() => {
+    await act(() => {
       fireEvent.submit(screen.getByRole('form'));
     });
   };
@@ -62,7 +65,9 @@ describe('New', () => {
 
   it('fetches connects on mount', async () => {
     const fetchConnects = jest.fn();
-    await waitFor(() => renderComponent({ fetchConnects }));
+    await act(() => {
+      renderComponent({ fetchConnects });
+    });
     expect(fetchConnects).toHaveBeenCalledTimes(1);
     expect(fetchConnects).toHaveBeenCalledWith(clusterName);
   });
@@ -71,6 +76,7 @@ describe('New', () => {
     const createConnector = jest.fn();
     renderComponent({ createConnector });
     await simulateFormSubmit();
+
     expect(createConnector).toHaveBeenCalledTimes(1);
     expect(createConnector).toHaveBeenCalledWith({
       clusterName,

+ 1 - 1
kafka-ui-react-app/src/components/ConsumerGroups/Details/Details.tsx

@@ -7,7 +7,7 @@ import {
 import { ConsumerGroupID } from 'redux/interfaces/consumerGroup';
 import PageLoader from 'components/common/PageLoader/PageLoader';
 import ConfirmationModal from 'components/common/ConfirmationModal/ConfirmationModal';
-import { useHistory, useParams } from 'react-router';
+import { useHistory, useParams } from 'react-router-dom';
 import ClusterContext from 'components/contexts/ClusterContext';
 import PageHeading from 'components/common/PageHeading/PageHeading';
 import VerticalElipsisIcon from 'components/common/Icons/VerticalElipsisIcon';

+ 1 - 1
kafka-ui-react-app/src/components/ConsumerGroups/Details/ResetOffsets/ResetOffsets.tsx

@@ -15,7 +15,7 @@ import 'react-datepicker/dist/react-datepicker.css';
 import { groupBy } from 'lodash';
 import PageLoader from 'components/common/PageLoader/PageLoader';
 import { ErrorMessage } from '@hookform/error-message';
-import { useHistory, useParams } from 'react-router';
+import { useHistory, useParams } from 'react-router-dom';
 import Select from 'components/common/Select/Select';
 import { InputLabel } from 'components/common/Input/InputLabel.styled';
 import { Button } from 'components/common/Button/Button';

+ 8 - 9
kafka-ui-react-app/src/components/ConsumerGroups/Details/ResetOffsets/__test__/ResetOffsets.spec.tsx

@@ -1,7 +1,7 @@
 import React from 'react';
 import fetchMock from 'fetch-mock';
-import { Route } from 'react-router';
-import { screen, waitFor } from '@testing-library/react';
+import { Route } from 'react-router-dom';
+import { act, screen, waitFor } from '@testing-library/react';
 import userEvent from '@testing-library/user-event';
 import { render } from 'lib/testHelpers';
 import { clusterConsumerGroupResetOffsetsPath } from 'lib/paths';
@@ -77,12 +77,12 @@ describe('ResetOffsets', () => {
     fetchMock.reset();
   });
 
-  it('renders progress bar for initial state', () => {
+  it('renders progress bar for initial state', async () => {
     fetchMock.getOnce(
       `/api/clusters/${clusterName}/consumer-groups/${groupId}`,
       404
     );
-    renderComponent();
+    await waitFor(() => renderComponent());
     expect(screen.getByRole('progressbar')).toBeInTheDocument();
   });
 
@@ -93,11 +93,10 @@ describe('ResetOffsets', () => {
           `/api/clusters/${clusterName}/consumer-groups/${groupId}`,
           consumerGroupPayload
         );
-        renderComponent();
-        await waitFor(() =>
-          expect(fetchConsumerGroupMock.called()).toBeTruthy()
-        );
-        await waitFor(() => screen.queryByRole('form'));
+        await act(() => {
+          renderComponent();
+        });
+        expect(fetchConsumerGroupMock.called()).toBeTruthy();
       });
 
       it('calls resetConsumerGroupOffsets with EARLIEST', async () => {

+ 1 - 1
kafka-ui-react-app/src/components/ConsumerGroups/Details/TopicContents/__test__/TopicContents.spec.tsx

@@ -4,7 +4,7 @@ import { screen } from '@testing-library/react';
 import TopicContents from 'components/ConsumerGroups/Details/TopicContents/TopicContents';
 import { consumerGroupPayload } from 'redux/reducers/consumerGroups/__test__/fixtures';
 import { render } from 'lib/testHelpers';
-import { Route } from 'react-router';
+import { Route } from 'react-router-dom';
 import { ConsumerGroupTopicPartition } from 'generated-sources';
 
 const clusterName = 'cluster1';

+ 10 - 11
kafka-ui-react-app/src/components/ConsumerGroups/Details/__tests__/Details.spec.tsx

@@ -3,7 +3,7 @@ import React from 'react';
 import fetchMock from 'fetch-mock';
 import { createMemoryHistory } from 'history';
 import { render } from 'lib/testHelpers';
-import { Route, Router } from 'react-router';
+import { Route, Router } from 'react-router-dom';
 import {
   clusterConsumerGroupDetailsPath,
   clusterConsumerGroupResetOffsetsPath,
@@ -16,6 +16,7 @@ import {
   waitForElementToBeRemoved,
 } from '@testing-library/dom';
 import userEvent from '@testing-library/user-event';
+import { act } from '@testing-library/react';
 
 const clusterName = 'cluster1';
 const { groupId } = consumerGroupPayload;
@@ -92,20 +93,18 @@ describe('Details component', () => {
 
     it('handles [Delete consumer group] click', async () => {
       expect(screen.queryByRole('dialog')).not.toBeInTheDocument();
-      userEvent.click(screen.getByText('Delete consumer group'));
-
-      await waitFor(() =>
-        expect(screen.queryByRole('dialog')).toBeInTheDocument()
-      );
-
+      await act(() => {
+        userEvent.click(screen.getByText('Delete consumer group'));
+      });
+      expect(screen.queryByRole('dialog')).toBeInTheDocument();
       const deleteConsumerGroupMock = fetchMock.deleteOnce(
         `/api/clusters/${clusterName}/consumer-groups/${groupId}`,
         200
       );
-      userEvent.click(screen.getByText('Submit'));
-      await waitFor(() =>
-        expect(deleteConsumerGroupMock.called()).toBeTruthy()
-      );
+      await act(() => {
+        userEvent.click(screen.getByText('Submit'));
+      });
+      expect(deleteConsumerGroupMock.called()).toBeTruthy();
       expect(screen.queryByRole('dialog')).not.toBeInTheDocument();
       expect(history.location.pathname).toEqual(
         clusterConsumerGroupsPath(clusterName)

+ 1 - 1
kafka-ui-react-app/src/components/ConsumerGroups/Details/__tests__/ListItem.spec.tsx

@@ -5,7 +5,7 @@ import userEvent from '@testing-library/user-event';
 import ListItem from 'components/ConsumerGroups/Details/ListItem';
 import { consumerGroupPayload } from 'redux/reducers/consumerGroups/__test__/fixtures';
 import { render } from 'lib/testHelpers';
-import { Route } from 'react-router';
+import { Route } from 'react-router-dom';
 import { ConsumerGroupTopicPartition } from 'generated-sources';
 
 const clusterName = 'cluster1';

+ 6 - 6
kafka-ui-react-app/src/components/ConsumerGroups/__test__/ConsumerGroups.spec.tsx

@@ -1,6 +1,7 @@
 import React from 'react';
 import { clusterConsumerGroupsPath } from 'lib/paths';
 import {
+  act,
   screen,
   waitFor,
   waitForElementToBeRemoved,
@@ -12,7 +13,7 @@ import {
 } from 'redux/reducers/consumerGroups/__test__/fixtures';
 import { render } from 'lib/testHelpers';
 import fetchMock from 'fetch-mock';
-import { Route, Router } from 'react-router';
+import { Route, Router } from 'react-router-dom';
 import { ConsumerGroupOrdering, SortOrder } from 'generated-sources';
 import { createMemoryHistory } from 'history';
 
@@ -37,7 +38,6 @@ const renderComponent = (history = historyMock) =>
 describe('ConsumerGroups', () => {
   it('renders with initial state', async () => {
     renderComponent();
-
     expect(screen.getByRole('progressbar')).toBeInTheDocument();
   });
 
@@ -54,10 +54,10 @@ describe('ConsumerGroups', () => {
           sortOrder: SortOrder.ASC,
         },
       });
-
-      renderComponent();
-      await waitFor(() => expect(fetchMock.calls().length).toBe(1));
-
+      await act(() => {
+        renderComponent();
+      });
+      expect(fetchMock.calls().length).toBe(1);
       expect(screen.getByRole('table')).toBeInTheDocument();
       expect(screen.getByText('No active consumer groups')).toBeInTheDocument();
     });

+ 1 - 1
kafka-ui-react-app/src/components/KsqlDb/List/List.tsx

@@ -3,7 +3,7 @@ import PageLoader from 'components/common/PageLoader/PageLoader';
 import ListItem from 'components/KsqlDb/List/ListItem';
 import React, { FC, useEffect } from 'react';
 import { useDispatch, useSelector } from 'react-redux';
-import { useParams } from 'react-router';
+import { useParams } from 'react-router-dom';
 import { fetchKsqlDbTables } from 'redux/reducers/ksqlDb/ksqlDbSlice';
 import { getKsqlDbTables } from 'redux/reducers/ksqlDb/selectors';
 import { clusterKsqlDbQueryPath } from 'lib/paths';

+ 1 - 1
kafka-ui-react-app/src/components/KsqlDb/List/__test__/List.spec.tsx

@@ -1,6 +1,6 @@
 import React from 'react';
 import List from 'components/KsqlDb/List/List';
-import { Route, Router } from 'react-router';
+import { Route, Router } from 'react-router-dom';
 import { createMemoryHistory } from 'history';
 import { clusterKsqlDbPath } from 'lib/paths';
 import { render } from 'lib/testHelpers';

+ 1 - 1
kafka-ui-react-app/src/components/KsqlDb/Query/Query.tsx

@@ -1,5 +1,5 @@
 import React, { useCallback, useEffect, FC, useState } from 'react';
-import { useParams } from 'react-router';
+import { useParams } from 'react-router-dom';
 import TableRenderer from 'components/KsqlDb/Query/renderer/TableRenderer/TableRenderer';
 import {
   executeKsql,

+ 35 - 57
kafka-ui-react-app/src/components/KsqlDb/Query/QueryForm/__test__/QueryForm.spec.tsx

@@ -1,8 +1,9 @@
 import { render } from 'lib/testHelpers';
 import React from 'react';
 import QueryForm, { Props } from 'components/KsqlDb/Query/QueryForm/QueryForm';
-import { screen, waitFor, within } from '@testing-library/dom';
+import { screen, within } from '@testing-library/dom';
 import userEvent from '@testing-library/user-event';
+import { act } from '@testing-library/react';
 
 const renderComponent = (props: Props) => render(<QueryForm {...props} />);
 
@@ -65,7 +66,7 @@ describe('QueryForm', () => {
       submitHandler: submitFn,
     });
 
-    await waitFor(() =>
+    await act(() =>
       userEvent.click(screen.getByRole('button', { name: 'Execute' }))
     );
     expect(screen.getByText('ksql is a required field')).toBeInTheDocument();
@@ -81,7 +82,7 @@ describe('QueryForm', () => {
       submitHandler: jest.fn(),
     });
 
-    await waitFor(() =>
+    await act(() => {
       // the use of `paste` is a hack that i found somewhere,
       // `type` won't work
       userEvent.paste(
@@ -89,12 +90,10 @@ describe('QueryForm', () => {
           screen.getByLabelText('Stream properties (JSON format)')
         ).getByRole('textbox'),
         'not-a-JSON-string'
-      )
-    );
+      );
 
-    await waitFor(() =>
-      userEvent.click(screen.getByRole('button', { name: 'Execute' }))
-    );
+      userEvent.click(screen.getByRole('button', { name: 'Execute' }));
+    });
 
     expect(
       screen.getByText('streamsProperties is not JSON object')
@@ -110,19 +109,15 @@ describe('QueryForm', () => {
       submitHandler: jest.fn(),
     });
 
-    await waitFor(() =>
+    await act(() => {
       userEvent.paste(
         within(
           screen.getByLabelText('Stream properties (JSON format)')
         ).getByRole('textbox'),
         '{"totallyJSON": "string"}'
-      )
-    );
-
-    await waitFor(() =>
-      userEvent.click(screen.getByRole('button', { name: 'Execute' }))
-    );
-
+      );
+      userEvent.click(screen.getByRole('button', { name: 'Execute' }));
+    });
     expect(
       screen.queryByText('streamsProperties is not JSON object')
     ).not.toBeInTheDocument();
@@ -138,25 +133,21 @@ describe('QueryForm', () => {
       submitHandler: submitFn,
     });
 
-    await waitFor(() =>
+    await act(() => {
       userEvent.paste(
         within(screen.getByLabelText('KSQL')).getByRole('textbox'),
         'show tables;'
-      )
-    );
+      );
 
-    await waitFor(() =>
       userEvent.paste(
         within(
           screen.getByLabelText('Stream properties (JSON format)')
         ).getByRole('textbox'),
         '{"totallyJSON": "string"}'
-      )
-    );
+      );
 
-    await waitFor(() =>
-      userEvent.click(screen.getByRole('button', { name: 'Execute' }))
-    );
+      userEvent.click(screen.getByRole('button', { name: 'Execute' }));
+    });
 
     expect(
       screen.queryByText('ksql is a required field')
@@ -181,7 +172,7 @@ describe('QueryForm', () => {
 
     expect(screen.getByRole('button', { name: 'Clear results' })).toBeEnabled();
 
-    await waitFor(() =>
+    await act(() =>
       userEvent.click(screen.getByRole('button', { name: 'Clear results' }))
     );
 
@@ -200,7 +191,7 @@ describe('QueryForm', () => {
 
     expect(screen.getByRole('button', { name: 'Stop query' })).toBeEnabled();
 
-    await waitFor(() =>
+    await act(() =>
       userEvent.click(screen.getByRole('button', { name: 'Stop query' }))
     );
 
@@ -217,19 +208,17 @@ describe('QueryForm', () => {
       submitHandler: submitFn,
     });
 
-    await waitFor(() =>
+    await act(() => {
       userEvent.paste(
         within(screen.getByLabelText('KSQL')).getByRole('textbox'),
         'show tables;'
-      )
-    );
+      );
 
-    await waitFor(() =>
       userEvent.type(
         within(screen.getByLabelText('KSQL')).getByRole('textbox'),
         '{ctrl}{enter}'
-      )
-    );
+      );
+    });
 
     expect(submitFn.mock.calls.length).toBe(1);
   });
@@ -244,30 +233,26 @@ describe('QueryForm', () => {
       submitHandler: submitFn,
     });
 
-    await waitFor(() =>
+    await act(() => {
       userEvent.paste(
         within(screen.getByLabelText('KSQL')).getByRole('textbox'),
         'show tables;'
-      )
-    );
+      );
 
-    await waitFor(() =>
       userEvent.paste(
         within(
           screen.getByLabelText('Stream properties (JSON format)')
         ).getByRole('textbox'),
         '{"some":"json"}'
-      )
-    );
+      );
 
-    await waitFor(() =>
       userEvent.type(
         within(
           screen.getByLabelText('Stream properties (JSON format)')
         ).getByRole('textbox'),
         '{ctrl}{enter}'
-      )
-    );
+      );
+    });
 
     expect(submitFn.mock.calls.length).toBe(1);
   });
@@ -281,20 +266,17 @@ describe('QueryForm', () => {
       submitHandler: jest.fn(),
     });
 
-    await waitFor(() =>
+    await act(() => {
       userEvent.paste(
         within(screen.getByLabelText('KSQL')).getByRole('textbox'),
         'show tables;'
-      )
-    );
-
-    await waitFor(() =>
+      );
       userEvent.click(
         within(screen.getByLabelText('KSQL')).getByRole('button', {
           name: 'Clear',
         })
-      )
-    );
+      );
+    });
 
     expect(screen.queryByText('show tables;')).not.toBeInTheDocument();
   });
@@ -308,25 +290,21 @@ describe('QueryForm', () => {
       submitHandler: jest.fn(),
     });
 
-    await waitFor(() =>
+    await act(() => {
       userEvent.paste(
         within(
           screen.getByLabelText('Stream properties (JSON format)')
         ).getByRole('textbox'),
         '{"some":"json"}'
-      )
-    );
-
-    await waitFor(() =>
+      );
       userEvent.click(
         within(
           screen.getByLabelText('Stream properties (JSON format)')
         ).getByRole('button', {
           name: 'Clear',
         })
-      )
-    );
-
+      );
+    });
     expect(screen.queryByText('{"some":"json"}')).not.toBeInTheDocument();
   });
 });

+ 16 - 29
kafka-ui-react-app/src/components/KsqlDb/Query/__test__/Query.spec.tsx

@@ -3,11 +3,12 @@ import React from 'react';
 import Query, {
   getFormattedErrorFromTableData,
 } from 'components/KsqlDb/Query/Query';
-import { screen, waitFor, within } from '@testing-library/dom';
+import { screen, within } from '@testing-library/dom';
 import fetchMock from 'fetch-mock';
 import userEvent from '@testing-library/user-event';
 import { Route } from 'react-router-dom';
 import { clusterKsqlDbQueryPath } from 'lib/paths';
+import { act } from '@testing-library/react';
 
 const clusterName = 'testLocal';
 const renderComponent = () =>
@@ -42,16 +43,14 @@ describe('Query', () => {
       value: EventSourceMock,
     });
 
-    await waitFor(() =>
+    await act(() => {
       userEvent.paste(
         within(screen.getByLabelText('KSQL')).getByRole('textbox'),
         'show tables;'
-      )
-    );
+      );
+      userEvent.click(screen.getByRole('button', { name: 'Execute' }));
+    });
 
-    await waitFor(() =>
-      userEvent.click(screen.getByRole('button', { name: 'Execute' }))
-    );
     expect(mock.calls().length).toBe(1);
   });
 
@@ -66,25 +65,19 @@ describe('Query', () => {
       value: EventSourceMock,
     });
 
-    await waitFor(() =>
+    await act(() => {
       userEvent.paste(
         within(screen.getByLabelText('KSQL')).getByRole('textbox'),
         'show tables;'
-      )
-    );
-
-    await waitFor(() =>
+      );
       userEvent.paste(
         within(
           screen.getByLabelText('Stream properties (JSON format)')
         ).getByRole('textbox'),
         '{"some":"json"}'
-      )
-    );
-
-    await waitFor(() =>
-      userEvent.click(screen.getByRole('button', { name: 'Execute' }))
-    );
+      );
+      userEvent.click(screen.getByRole('button', { name: 'Execute' }));
+    });
     expect(mock.calls().length).toBe(1);
   });
 
@@ -99,25 +92,19 @@ describe('Query', () => {
       value: EventSourceMock,
     });
 
-    await waitFor(() =>
+    await act(() => {
       userEvent.paste(
         within(screen.getByLabelText('KSQL')).getByRole('textbox'),
         'show tables;'
-      )
-    );
-
-    await waitFor(() =>
+      );
       userEvent.paste(
         within(
           screen.getByLabelText('Stream properties (JSON format)')
         ).getByRole('textbox'),
         '{"some":"json"}'
-      )
-    );
-
-    await waitFor(() =>
-      userEvent.click(screen.getByRole('button', { name: 'Execute' }))
-    );
+      );
+      userEvent.click(screen.getByRole('button', { name: 'Execute' }));
+    });
     expect(mock.calls().length).toBe(1);
   });
 });

+ 1 - 1
kafka-ui-react-app/src/components/Nav/ClusterMenu.tsx

@@ -24,7 +24,7 @@ const ClusterMenu: React.FC<Props> = ({
   singleMode,
 }) => {
   const hasFeatureConfigured = React.useCallback(
-    (key) => features?.includes(key),
+    (key: ClusterFeaturesEnum) => features?.includes(key),
     [features]
   );
   const [isOpen, setIsOpen] = React.useState(!!singleMode);

+ 4 - 2
kafka-ui-react-app/src/components/Nav/ClusterMenuItem.tsx

@@ -1,4 +1,4 @@
-import React from 'react';
+import React, { PropsWithChildren } from 'react';
 import { NavLinkProps } from 'react-router-dom';
 
 import * as S from './Nav.styled';
@@ -11,7 +11,9 @@ export interface ClusterMenuItemProps {
   isActive?: NavLinkProps['isActive'];
 }
 
-const ClusterMenuItem: React.FC<ClusterMenuItemProps> = (props) => {
+const ClusterMenuItem: React.FC<PropsWithChildren<ClusterMenuItemProps>> = (
+  props
+) => {
   const { to, title, children, exact, isTopLevel, isActive } = props;
 
   if (to) {

+ 1 - 1
kafka-ui-react-app/src/components/Schemas/Details/Details.tsx

@@ -1,5 +1,5 @@
 import React from 'react';
-import { useHistory, useParams } from 'react-router';
+import { useHistory, useParams } from 'react-router-dom';
 import {
   clusterSchemasPath,
   clusterSchemaSchemaDiffPath,

+ 14 - 7
kafka-ui-react-app/src/components/Schemas/Details/__test__/Details.spec.tsx

@@ -1,7 +1,7 @@
 import React from 'react';
 import Details from 'components/Schemas/Details/Details';
 import { render } from 'lib/testHelpers';
-import { Route } from 'react-router';
+import { Route } from 'react-router-dom';
 import { clusterSchemaPath } from 'lib/paths';
 import { screen, waitFor } from '@testing-library/dom';
 import {
@@ -14,6 +14,7 @@ import ClusterContext, {
   initialValue as contextInitialValue,
 } from 'components/contexts/ClusterContext';
 import { RootState } from 'redux/interfaces';
+import { act } from '@testing-library/react';
 
 import { versionPayload, versionEmptyPayload } from './fixtures';
 
@@ -24,8 +25,8 @@ const schemasAPIVersionsUrl = `/api/clusters/${clusterName}/schemas/${schemaVers
 const renderComponent = (
   initialState: RootState['schemas'] = schemasInitialState,
   context: ContextProps = contextInitialValue
-) => {
-  return render(
+) =>
+  render(
     <Route path={clusterSchemaPath(':clusterName', ':subject')}>
       <ClusterContext.Provider value={context}>
         <Details />
@@ -38,7 +39,6 @@ const renderComponent = (
       },
     }
   );
-};
 
 describe('Details', () => {
   afterEach(() => fetchMock.reset());
@@ -50,7 +50,10 @@ describe('Details', () => {
         schemasAPIVersionsUrl,
         404
       );
-      renderComponent();
+      await act(() => {
+        renderComponent();
+      });
+
       await waitFor(() => {
         expect(schemasAPILatestMock.called()).toBeTruthy();
       });
@@ -78,7 +81,9 @@ describe('Details', () => {
           schemasAPIVersionsUrl,
           versionPayload
         );
-        renderComponent();
+        await act(() => {
+          renderComponent();
+        });
         await waitFor(() => {
           expect(schemasAPILatestMock.called()).toBeTruthy();
         });
@@ -104,7 +109,9 @@ describe('Details', () => {
           schemasAPIVersionsUrl,
           versionEmptyPayload
         );
-        renderComponent();
+        await act(() => {
+          renderComponent();
+        });
         await waitFor(() => {
           expect(schemasAPILatestMock.called()).toBeTruthy();
         });

+ 1 - 1
kafka-ui-react-app/src/components/Schemas/Diff/Diff.tsx

@@ -3,7 +3,7 @@ import { SchemaSubject } from 'generated-sources';
 import { clusterSchemaSchemaDiffPath } from 'lib/paths';
 import PageLoader from 'components/common/PageLoader/PageLoader';
 import DiffViewer from 'components/common/DiffViewer/DiffViewer';
-import { useHistory, useParams, useLocation } from 'react-router';
+import { useHistory, useParams, useLocation } from 'react-router-dom';
 import {
   fetchSchemaVersions,
   SCHEMAS_VERSIONS_FETCH_ACTION,

+ 1 - 1
kafka-ui-react-app/src/components/Schemas/Edit/Edit.tsx

@@ -1,5 +1,5 @@
 import React from 'react';
-import { useHistory, useParams } from 'react-router';
+import { useHistory, useParams } from 'react-router-dom';
 import { useForm, Controller, FormProvider } from 'react-hook-form';
 import {
   CompatibilityLevelCompatibilityEnum,

+ 12 - 19
kafka-ui-react-app/src/components/Schemas/Edit/__tests__/Edit.spec.tsx

@@ -6,7 +6,7 @@ import {
   schemasInitialState,
   schemaVersion,
 } from 'redux/reducers/schemas/__test__/fixtures';
-import { Route } from 'react-router';
+import { Route } from 'react-router-dom';
 import { screen, waitFor } from '@testing-library/dom';
 import ClusterContext, {
   ContextProps,
@@ -14,6 +14,7 @@ import ClusterContext, {
 } from 'components/contexts/ClusterContext';
 import { RootState } from 'redux/interfaces';
 import fetchMock from 'fetch-mock';
+import { act } from '@testing-library/react';
 
 const clusterName = 'testClusterName';
 const schemasAPILatestUrl = `/api/clusters/${clusterName}/schemas/${schemaVersion.subject}/latest`;
@@ -21,8 +22,8 @@ const schemasAPILatestUrl = `/api/clusters/${clusterName}/schemas/${schemaVersio
 const renderComponent = (
   initialState: RootState['schemas'] = schemasInitialState,
   context: ContextProps = contextInitialValue
-) => {
-  return render(
+) =>
+  render(
     <Route path={clusterSchemaEditPath(':clusterName', ':subject')}>
       <ClusterContext.Provider value={context}>
         <Edit />
@@ -35,21 +36,17 @@ const renderComponent = (
       },
     }
   );
-};
 
 describe('Edit', () => {
   afterEach(() => fetchMock.reset());
 
   describe('fetch failed', () => {
-    beforeEach(async () => {
+    it('renders pageloader', async () => {
       const schemasAPILatestMock = fetchMock.getOnce(schemasAPILatestUrl, 404);
-      renderComponent();
-      await waitFor(() => {
-        expect(schemasAPILatestMock.called()).toBeTruthy();
+      await act(() => {
+        renderComponent();
       });
-    });
-
-    it('renders pageloader', () => {
+      await waitFor(() => expect(schemasAPILatestMock.called()).toBeTruthy());
       expect(screen.getByRole('progressbar')).toBeInTheDocument();
       expect(screen.queryByText(schemaVersion.subject)).not.toBeInTheDocument();
       expect(screen.queryByText('Submit')).not.toBeInTheDocument();
@@ -58,19 +55,15 @@ describe('Edit', () => {
 
   describe('fetch success', () => {
     describe('has schema versions', () => {
-      beforeEach(async () => {
+      it('renders component with schema info', async () => {
         const schemasAPILatestMock = fetchMock.getOnce(
           schemasAPILatestUrl,
           schemaVersion
         );
-
-        renderComponent();
-        await waitFor(() => {
-          expect(schemasAPILatestMock.called()).toBeTruthy();
+        await act(() => {
+          renderComponent();
         });
-      });
-
-      it('renders component with schema info', () => {
+        await waitFor(() => expect(schemasAPILatestMock.called()).toBeTruthy());
         expect(screen.getByText('Submit')).toBeInTheDocument();
         expect(screen.queryByRole('progressbar')).not.toBeInTheDocument();
       });

+ 9 - 4
kafka-ui-react-app/src/components/Schemas/List/GlobalSchemaSelector/__test__/GlobalSchemaSelector.spec.tsx

@@ -1,11 +1,11 @@
 import React from 'react';
-import { screen, waitFor, within } from '@testing-library/react';
+import { act, screen, waitFor, within } from '@testing-library/react';
 import { render } from 'lib/testHelpers';
 import { CompatibilityLevelCompatibilityEnum } from 'generated-sources';
 import GlobalSchemaSelector from 'components/Schemas/List/GlobalSchemaSelector/GlobalSchemaSelector';
 import userEvent from '@testing-library/user-event';
 import { clusterSchemasPath } from 'lib/paths';
-import { Route } from 'react-router';
+import { Route } from 'react-router-dom';
 import fetchMock from 'fetch-mock';
 
 const clusterName = 'testClusterName';
@@ -42,7 +42,9 @@ describe('GlobalSchemaSelector', () => {
       `api/clusters/${clusterName}/schemas/compatibility`,
       { compatibility: CompatibilityLevelCompatibilityEnum.FULL }
     );
-    renderComponent();
+    await act(() => {
+      renderComponent();
+    });
     await waitFor(() =>
       expect(fetchGlobalCompatibilityLevelMock.called()).toBeTruthy()
     );
@@ -89,7 +91,10 @@ describe('GlobalSchemaSelector', () => {
     });
     await waitFor(() => expect(putNewCompatibilityMock.called()).toBeTruthy());
     await waitFor(() => expect(getSchemasMock.called()).toBeTruthy());
-    expect(screen.queryByText('Confirm the action')).not.toBeInTheDocument();
+
+    await waitFor(() =>
+      expect(screen.queryByText('Confirm the action')).not.toBeInTheDocument()
+    );
     expectOptionIsSelected(CompatibilityLevelCompatibilityEnum.FORWARD);
   });
 });

+ 23 - 16
kafka-ui-react-app/src/components/Schemas/List/__test__/List.spec.tsx

@@ -1,9 +1,9 @@
 import React from 'react';
 import List from 'components/Schemas/List/List';
 import { render } from 'lib/testHelpers';
-import { Route } from 'react-router';
+import { Route } from 'react-router-dom';
 import { clusterSchemasPath } from 'lib/paths';
-import { screen, waitFor } from '@testing-library/dom';
+import { act, screen } from '@testing-library/react';
 import {
   schemasFulfilledState,
   schemasInitialState,
@@ -52,9 +52,11 @@ describe('List', () => {
         schemasAPICompabilityUrl,
         404
       );
-      renderComponent();
-      await waitFor(() => expect(fetchSchemasMock.called()).toBeTruthy());
-      await waitFor(() => expect(fetchCompabilityMock.called()).toBeTruthy());
+      await act(() => {
+        renderComponent();
+      });
+      expect(fetchSchemasMock.called()).toBeTruthy();
+      expect(fetchCompabilityMock.called()).toBeTruthy();
       expect(screen.getByRole('progressbar')).toBeInTheDocument();
     });
   });
@@ -70,9 +72,11 @@ describe('List', () => {
           schemasAPICompabilityUrl,
           200
         );
-        renderComponent();
-        await waitFor(() => expect(fetchSchemasMock.called()).toBeTruthy());
-        await waitFor(() => expect(fetchCompabilityMock.called()).toBeTruthy());
+        await act(() => {
+          renderComponent();
+        });
+        expect(fetchSchemasMock.called()).toBeTruthy();
+        expect(fetchCompabilityMock.called()).toBeTruthy();
       });
       it('renders empty table', () => {
         expect(screen.getByText('No schemas found')).toBeInTheDocument();
@@ -88,9 +92,11 @@ describe('List', () => {
           schemasAPICompabilityUrl,
           200
         );
-        renderComponent(schemasFulfilledState);
-        await waitFor(() => expect(fetchSchemasMock.called()).toBeTruthy());
-        await waitFor(() => expect(fetchCompabilityMock.called()).toBeTruthy());
+        await act(() => {
+          renderComponent(schemasFulfilledState);
+        });
+        expect(fetchSchemasMock.called()).toBeTruthy();
+        expect(fetchCompabilityMock.called()).toBeTruthy();
       });
       it('renders list', () => {
         expect(screen.getByText(schemaVersion1.subject)).toBeInTheDocument();
@@ -104,12 +110,13 @@ describe('List', () => {
           schemasAPIUrl,
           schemasPayload
         );
-
-        renderComponent(schemasFulfilledState, {
-          ...contextInitialValue,
-          isReadOnly: true,
+        await act(() => {
+          renderComponent(schemasFulfilledState, {
+            ...contextInitialValue,
+            isReadOnly: true,
+          });
         });
-        await waitFor(() => expect(fetchSchemasMock.called()).toBeTruthy());
+        expect(fetchSchemasMock.called()).toBeTruthy();
       });
       it('does not render Create Schema button', () => {
         expect(screen.queryByText('Create Schema')).not.toBeInTheDocument();

+ 1 - 1
kafka-ui-react-app/src/components/Schemas/New/New.tsx

@@ -5,7 +5,7 @@ import { ErrorMessage } from '@hookform/error-message';
 import { clusterSchemaPath } from 'lib/paths';
 import { SchemaType } from 'generated-sources';
 import { SCHEMA_NAME_VALIDATION_PATTERN } from 'lib/constants';
-import { useHistory, useParams } from 'react-router';
+import { useHistory, useParams } from 'react-router-dom';
 import { InputLabel } from 'components/common/Input/InputLabel.styled';
 import Input from 'components/common/Input/Input';
 import { FormError } from 'components/common/Input/Input.styled';

+ 1 - 1
kafka-ui-react-app/src/components/Schemas/New/__test__/New.spec.tsx

@@ -2,7 +2,7 @@ import React from 'react';
 import New from 'components/Schemas/New/New';
 import { render } from 'lib/testHelpers';
 import { clusterSchemaNewPath } from 'lib/paths';
-import { Route } from 'react-router';
+import { Route } from 'react-router-dom';
 import { screen } from '@testing-library/dom';
 
 const clusterName = 'local';

+ 1 - 1
kafka-ui-react-app/src/components/Schemas/__test__/Schemas.spec.tsx

@@ -9,7 +9,7 @@ import {
   clusterSchemasPath,
 } from 'lib/paths';
 import { screen, waitFor } from '@testing-library/dom';
-import { Route } from 'react-router';
+import { Route } from 'react-router-dom';
 import fetchMock from 'fetch-mock';
 import { schemaVersion } from 'redux/reducers/schemas/__test__/fixtures';
 

+ 1 - 2
kafka-ui-react-app/src/components/Topics/List/List.tsx

@@ -1,11 +1,10 @@
 import React from 'react';
-import { useHistory } from 'react-router';
+import { useHistory, useParams } from 'react-router-dom';
 import {
   TopicWithDetailedInfo,
   ClusterName,
   TopicName,
 } from 'redux/interfaces';
-import { useParams } from 'react-router-dom';
 import { clusterTopicCopyPath, clusterTopicNewPath } from 'lib/paths';
 import usePagination from 'lib/hooks/usePagination';
 import useModal from 'lib/hooks/useModal';

+ 1 - 1
kafka-ui-react-app/src/components/Topics/List/__tests__/List.spec.tsx

@@ -1,7 +1,7 @@
 import React from 'react';
 import { render } from 'lib/testHelpers';
 import { screen, waitFor, within } from '@testing-library/react';
-import { Route, Router, StaticRouter } from 'react-router';
+import { Route, Router, StaticRouter } from 'react-router-dom';
 import ClusterContext, {
   ContextProps,
 } from 'components/contexts/ClusterContext';

+ 1 - 1
kafka-ui-react-app/src/components/Topics/New/New.tsx

@@ -10,7 +10,7 @@ import {
 } from 'redux/actions';
 import { useDispatch } from 'react-redux';
 import { getResponse } from 'lib/errorHandling';
-import { useHistory, useLocation, useParams } from 'react-router';
+import { useHistory, useLocation, useParams } from 'react-router-dom';
 import { yupResolver } from '@hookform/resolvers/yup';
 import { topicFormValidationSchema } from 'lib/yupExtended';
 import PageHeading from 'components/common/PageHeading/PageHeading';

+ 3 - 3
kafka-ui-react-app/src/components/Topics/New/__test__/New.spec.tsx

@@ -1,10 +1,10 @@
 import React from 'react';
 import New from 'components/Topics/New/New';
-import { Route, Router } from 'react-router';
+import { Route, Router } from 'react-router-dom';
 import configureStore from 'redux-mock-store';
 import { RootState } from 'redux/interfaces';
 import { Provider } from 'react-redux';
-import { screen, waitFor } from '@testing-library/react';
+import { act, screen, waitFor } from '@testing-library/react';
 import { createMemoryHistory } from 'history';
 import fetchMock from 'fetch-mock-jest';
 import {
@@ -139,7 +139,7 @@ describe('New', () => {
     jest.spyOn(mocked, 'push');
     renderComponent(mocked);
 
-    await waitFor(() => {
+    await act(() => {
       userEvent.type(screen.getByPlaceholderText('Topic Name'), topicName);
       userEvent.click(screen.getByText(/submit/i));
     });

+ 1 - 1
kafka-ui-react-app/src/components/Topics/Topic/Details/Messages/Filters/Filters.tsx

@@ -12,7 +12,7 @@ import {
 } from 'generated-sources';
 import React, { useContext } from 'react';
 import { omitBy } from 'lodash';
-import { useHistory, useLocation } from 'react-router';
+import { useHistory, useLocation } from 'react-router-dom';
 import DatePicker from 'react-datepicker';
 import MultiSelect from 'components/common/MultiSelect/MultiSelect.styled';
 import { Option } from 'react-multi-select-component/dist/lib/interfaces';

+ 37 - 29
kafka-ui-react-app/src/components/Topics/Topic/Details/Messages/Filters/__tests__/AddEditFilterContainer.spec.tsx

@@ -3,7 +3,7 @@ import AddEditFilterContainer, {
   AddEditFilterContainerProps,
 } from 'components/Topics/Topic/Details/Messages/Filters/AddEditFilterContainer';
 import { render } from 'lib/testHelpers';
-import { screen, waitFor } from '@testing-library/react';
+import { act, screen } from '@testing-library/react';
 import userEvent from '@testing-library/user-event';
 import { MessageFilters } from 'components/Topics/Topic/Details/Messages/Filters/Filters';
 
@@ -15,7 +15,9 @@ describe('AddEditFilterContainer component', () => {
     code: 'mockCode',
   };
 
-  const setupComponent = (props: Partial<AddEditFilterContainerProps> = {}) =>
+  const renderComponent = (
+    props: Partial<AddEditFilterContainerProps> = {}
+  ) => {
     render(
       <AddEditFilterContainer
         cancelBtnHandler={jest.fn()}
@@ -23,10 +25,11 @@ describe('AddEditFilterContainer component', () => {
         {...props}
       />
     );
+  };
 
   describe('default Component Parameters', () => {
     beforeEach(async () => {
-      await waitFor(() => setupComponent());
+      await act(() => renderComponent());
     });
 
     it('should check the default Button text', () => {
@@ -40,39 +43,43 @@ describe('AddEditFilterContainer component', () => {
       const inputs = screen.getAllByRole('textbox');
 
       const textAreaElement = inputs[0] as HTMLTextAreaElement;
-      userEvent.paste(textAreaElement, 'Hello World With TextArea');
+      await act(() =>
+        userEvent.paste(textAreaElement, 'Hello World With TextArea')
+      );
 
       const inputNameElement = inputs[1];
-      userEvent.type(inputNameElement, 'Hello World!');
+      await act(() => userEvent.type(inputNameElement, 'Hello World!'));
 
-      await waitFor(() => expect(submitButtonElem).toBeEnabled());
+      expect(submitButtonElem).toBeEnabled();
 
-      userEvent.clear(inputNameElement);
+      await act(() => userEvent.clear(inputNameElement));
 
-      await waitFor(() => expect(submitButtonElem).toBeDisabled());
+      expect(submitButtonElem).toBeDisabled();
     });
 
     it('should view the error message after typing and clearing the input', async () => {
       const inputs = screen.getAllByRole('textbox');
 
       const textAreaElement = inputs[0] as HTMLTextAreaElement;
-      userEvent.paste(textAreaElement, 'Hello World With TextArea');
+      await act(() =>
+        userEvent.paste(textAreaElement, 'Hello World With TextArea')
+      );
 
       const inputNameElement = inputs[1];
-      userEvent.type(inputNameElement, 'Hello World!');
+      await act(() => {
+        userEvent.type(inputNameElement, 'Hello World!');
 
-      userEvent.clear(inputNameElement);
-      userEvent.clear(textAreaElement);
+        userEvent.clear(inputNameElement);
+        userEvent.clear(textAreaElement);
+      });
 
-      await waitFor(() =>
-        expect(screen.getByText(/required field/i)).toBeInTheDocument()
-      );
+      expect(screen.getByText(/required field/i)).toBeInTheDocument();
     });
   });
 
   describe('Custom setup for the component', () => {
     it('should render the input with default data if they are passed', async () => {
-      setupComponent({
+      renderComponent({
         inputDisplayNameDefaultValue: mockData.name,
         inputCodeDefaultValue: mockData.code,
       });
@@ -80,23 +87,24 @@ describe('AddEditFilterContainer component', () => {
       const inputs = screen.getAllByRole('textbox');
       const textAreaElement = inputs[0] as HTMLTextAreaElement;
       const inputNameElement = inputs[1];
-      await waitFor(() => expect(inputNameElement).toHaveValue(mockData.name));
+      expect(inputNameElement).toHaveValue(mockData.name);
       expect(textAreaElement.value).toEqual('');
     });
 
     it('should test whether the cancel callback is being called', async () => {
       const cancelCallback = jest.fn();
-      setupComponent({
+      renderComponent({
         cancelBtnHandler: cancelCallback,
       });
       const cancelBtnElement = screen.getByText(/cancel/i);
-      userEvent.click(cancelBtnElement);
-      await waitFor(() => expect(cancelCallback).toBeCalled());
+
+      await act(() => userEvent.click(cancelBtnElement));
+      expect(cancelCallback).toBeCalled();
     });
 
     it('should test whether the submit Callback is being called', async () => {
       const submitCallback = jest.fn();
-      setupComponent({
+      renderComponent({
         submitCallback,
       });
 
@@ -106,30 +114,30 @@ describe('AddEditFilterContainer component', () => {
       userEvent.paste(textAreaElement, 'Hello World With TextArea');
 
       const inputNameElement = inputs[1];
-      userEvent.type(inputNameElement, 'Hello World!');
+      await act(() => userEvent.type(inputNameElement, 'Hello World!'));
 
       const submitBtnElement = screen.getByText(defaultSubmitBtn);
 
-      await waitFor(() => expect(submitBtnElement).toBeEnabled());
+      expect(submitBtnElement).toBeEnabled();
 
-      userEvent.click(submitBtnElement);
+      await act(() => userEvent.click(submitBtnElement));
 
-      await waitFor(() => expect(submitCallback).toBeCalled());
+      expect(submitCallback).toBeCalled();
     });
 
     it('should display the checkbox if the props is passed and initially check state', async () => {
-      setupComponent({ isAdd: true });
+      renderComponent({ isAdd: true });
       const checkbox = screen.getByRole('checkbox');
       expect(checkbox).toBeInTheDocument();
       expect(checkbox).not.toBeChecked();
-      await waitFor(() => userEvent.click(checkbox));
+      await act(() => userEvent.click(checkbox));
       expect(checkbox).toBeChecked();
     });
 
     it('should pass and render the correct button text', async () => {
       const submitBtnText = 'submitBtnTextTest';
-      await waitFor(() =>
-        setupComponent({
+      await act(() =>
+        renderComponent({
           submitBtnText,
         })
       );

+ 54 - 53
kafka-ui-react-app/src/components/Topics/Topic/Details/Messages/Filters/__tests__/AddFilter.spec.tsx

@@ -4,7 +4,7 @@ import AddFilter, {
 } from 'components/Topics/Topic/Details/Messages/Filters/AddFilter';
 import { render } from 'lib/testHelpers';
 import { MessageFilters } from 'components/Topics/Topic/Details/Messages/Filters/Filters';
-import { screen, waitFor } from '@testing-library/react';
+import { act, screen } from '@testing-library/react';
 import userEvent from '@testing-library/user-event';
 
 const filters: MessageFilters[] = [
@@ -14,7 +14,7 @@ const filters: MessageFilters[] = [
 
 const editFilterMock = jest.fn();
 
-const setupComponent = (props: Partial<FilterModalProps> = {}) =>
+const renderComponent = (props: Partial<FilterModalProps> = {}) =>
   render(
     <AddFilter
       toggleIsOpen={jest.fn()}
@@ -30,20 +30,21 @@ const setupComponent = (props: Partial<FilterModalProps> = {}) =>
 
 describe('AddFilter component', () => {
   it('should test click on Saved Filters redirects to Saved components', () => {
-    setupComponent();
+    renderComponent();
     userEvent.click(screen.getByRole('savedFilterText'));
     expect(screen.getByText('Saved filters')).toBeInTheDocument();
     expect(screen.getAllByRole('savedFilter')).toHaveLength(2);
   });
 
   it('should test click on return to custom filter redirects to Add filters', async () => {
-    setupComponent();
+    renderComponent();
     userEvent.click(screen.getByRole('savedFilterText'));
+
     expect(screen.getByText('Saved filters')).toBeInTheDocument();
     expect(screen.queryByRole('savedFilterText')).not.toBeInTheDocument();
     expect(screen.getAllByRole('savedFilter')).toHaveLength(2);
 
-    await waitFor(() =>
+    await act(() =>
       userEvent.click(screen.getByText(/back to custom filters/i))
     );
     expect(screen.queryByText('Saved filters')).not.toBeInTheDocument();
@@ -52,7 +53,9 @@ describe('AddFilter component', () => {
 
   describe('Add new filter', () => {
     beforeEach(async () => {
-      await waitFor(() => setupComponent());
+      await act(() => {
+        renderComponent();
+      });
     });
 
     it('adding new filter', async () => {
@@ -66,8 +69,12 @@ describe('AddFilter component', () => {
       const addFilterBtn = screen.getByRole('button', { name: /Add filter/i });
       expect(addFilterBtn).toBeDisabled();
       expect(screen.getByPlaceholderText('Enter Name')).toBeInTheDocument();
-      await waitFor(() => userEvent.paste(codeTextBox, codeValue));
-      await waitFor(() => userEvent.type(nameTextBox, nameValue));
+
+      await act(() => {
+        userEvent.paste(codeTextBox, codeValue);
+        userEvent.type(nameTextBox, nameValue);
+      });
+
       expect(addFilterBtn).toBeEnabled();
       expect(codeTextBox.value).toEqual(`${codeValue}\n\n`);
       expect(nameTextBox).toHaveValue(nameValue);
@@ -81,7 +88,7 @@ describe('AddFilter component', () => {
       const addFilterBtn = screen.getByRole('button', { name: /Add filter/i });
       expect(addFilterBtn).toBeDisabled();
       expect(screen.getByPlaceholderText('Enter Name')).toBeInTheDocument();
-      await waitFor(() => userEvent.paste(codeTextBox, code));
+      await act(() => userEvent.paste(codeTextBox, code));
       expect(addFilterBtn).toBeEnabled();
       expect(codeTextBox).toHaveValue(`${code}\n\n`);
     });
@@ -110,13 +117,13 @@ describe('AddFilter component', () => {
     const nameValue = 'filter name';
 
     beforeEach(async () => {
-      await waitFor(() =>
-        setupComponent({
+      await act(() => {
+        renderComponent({
           addFilter: addFilterMock,
           activeFilterHandler: activeFilterHandlerMock,
           toggleIsOpen: toggleModelMock,
-        })
-      );
+        });
+      });
     });
 
     afterEach(() => {
@@ -127,7 +134,7 @@ describe('AddFilter component', () => {
 
     describe('OnSubmit conditions with codeValue and nameValue in fields', () => {
       beforeEach(async () => {
-        await waitFor(() => {
+        await act(() => {
           userEvent.paste(
             screen.getAllByRole('textbox')[0] as HTMLTextAreaElement,
             codeValue
@@ -142,19 +149,20 @@ describe('AddFilter component', () => {
           name: /Add filter/i,
         });
         expect(addFilterBtn).toBeEnabled();
-        userEvent.click(addFilterBtn);
 
-        await waitFor(() => expect(activeFilterHandlerMock).toHaveBeenCalled());
+        await act(() => userEvent.click(addFilterBtn));
+
+        expect(activeFilterHandlerMock).toHaveBeenCalled();
         expect(addFilterMock).not.toHaveBeenCalled();
       });
 
       it('OnSubmit condition with checkbox on functionality', async () => {
-        userEvent.click(screen.getByRole('checkbox'));
+        await act(() => {
+          userEvent.click(screen.getByRole('checkbox'));
+          userEvent.click(screen.getAllByRole('button')[1]);
+        });
 
-        userEvent.click(screen.getAllByRole('button')[1]);
-        await waitFor(() =>
-          expect(activeFilterHandlerMock).not.toHaveBeenCalled()
-        );
+        expect(activeFilterHandlerMock).not.toHaveBeenCalled();
         expect(addFilterMock).toHaveBeenCalled();
         expect(toggleModelMock).not.toHaveBeenCalled();
       });
@@ -169,13 +177,12 @@ describe('AddFilter component', () => {
           name: /Add filter/i,
         });
 
-        userEvent.clear(nameTextBox);
+        await act(() => userEvent.clear(nameTextBox));
+
         expect(nameTextBox).toHaveValue('');
 
-        userEvent.click(addFilterBtn);
-        await waitFor(() =>
-          expect(activeFilterHandlerMock).toHaveBeenCalledTimes(1)
-        );
+        await act(() => userEvent.click(addFilterBtn));
+        expect(activeFilterHandlerMock).toHaveBeenCalledTimes(1);
 
         expect(activeFilterHandlerMock).toHaveBeenCalledWith(
           {
@@ -189,20 +196,18 @@ describe('AddFilter component', () => {
         expect(codeTextBox).toHaveValue(``);
         expect(toggleModelMock).toHaveBeenCalled();
 
-        userEvent.paste(codeTextBox, codeValue);
+        await act(() => userEvent.paste(codeTextBox, codeValue));
         expect(codeTextBox).toHaveValue(`${codeValue}\n\n`);
 
-        userEvent.click(checkbox);
+        await act(() => userEvent.click(checkbox));
         expect(addFilterBtn).toBeDisabled();
 
-        userEvent.type(nameTextBox, nameValue);
+        await act(() => userEvent.type(nameTextBox, nameValue));
         expect(nameTextBox).toHaveValue(nameValue);
-        await waitFor(() => expect(addFilterBtn).toBeEnabled());
-        userEvent.click(addFilterBtn);
+        expect(addFilterBtn).toBeEnabled();
+        await act(() => userEvent.click(addFilterBtn));
 
-        await waitFor(() =>
-          expect(activeFilterHandlerMock).toHaveBeenCalledTimes(1)
-        );
+        expect(activeFilterHandlerMock).toHaveBeenCalledTimes(1);
         expect(addFilterMock).toHaveBeenCalledWith({
           name: nameValue,
           code: codeValue,
@@ -217,34 +222,30 @@ describe('AddFilter component', () => {
       )[0] as HTMLTextAreaElement;
       const nameTextBox = screen.getAllByRole('textbox')[1];
       const addFilterBtn = screen.getByRole('button', { name: /Add filter/i });
-
-      userEvent.clear(nameTextBox);
-      userEvent.clear(codeTextBox);
-
-      await waitFor(() => {
+      await act(() => {
+        userEvent.clear(nameTextBox);
+        userEvent.clear(codeTextBox);
         userEvent.paste(codeTextBox, longCodeValue);
       });
 
       expect(nameTextBox).toHaveValue('');
       expect(codeTextBox).toHaveValue(`${longCodeValue}\n\n`);
 
-      userEvent.click(addFilterBtn);
+      await act(() => userEvent.click(addFilterBtn));
 
       const filterName = `${longCodeValue.slice(0, 16)}...`;
 
-      await waitFor(() => {
-        expect(activeFilterHandlerMock).toHaveBeenCalledTimes(1);
-        expect(activeFilterHandlerMock).toHaveBeenCalledWith(
-          {
-            name: filterName,
-            code: longCodeValue,
-            saveFilter: false,
-          },
-          -1
-        );
-        expect(codeTextBox.value).toEqual('');
-        expect(toggleModelMock).toHaveBeenCalled();
-      });
+      expect(activeFilterHandlerMock).toHaveBeenCalledTimes(1);
+      expect(activeFilterHandlerMock).toHaveBeenCalledWith(
+        {
+          name: filterName,
+          code: longCodeValue,
+          saveFilter: false,
+        },
+        -1
+      );
+      expect(codeTextBox).toHaveValue('');
+      expect(toggleModelMock).toHaveBeenCalled();
     });
   });
 });

+ 21 - 9
kafka-ui-react-app/src/components/Topics/Topic/Details/Messages/Filters/__tests__/EditFilter.spec.tsx

@@ -3,7 +3,7 @@ import EditFilter, {
   EditFilterProps,
 } from 'components/Topics/Topic/Details/Messages/Filters/EditFilter';
 import { render } from 'lib/testHelpers';
-import { screen, waitFor, fireEvent, within } from '@testing-library/react';
+import { screen, fireEvent, within, act } from '@testing-library/react';
 import userEvent from '@testing-library/user-event';
 import { FilterEdit } from 'components/Topics/Topic/Details/Messages/Filters/FilterModal';
 
@@ -12,7 +12,7 @@ const editFilter: FilterEdit = {
   filter: { name: 'name', code: '' },
 };
 
-const setupComponent = (props?: Partial<EditFilterProps>) =>
+const renderComponent = (props?: Partial<EditFilterProps>) =>
   render(
     <EditFilter
       toggleEditModal={jest.fn()}
@@ -24,13 +24,17 @@ const setupComponent = (props?: Partial<EditFilterProps>) =>
 
 describe('EditFilter component', () => {
   it('renders component', async () => {
-    await waitFor(() => setupComponent());
+    await act(() => {
+      renderComponent();
+    });
     expect(screen.getByText(/edit saved filter/i)).toBeInTheDocument();
   });
 
   it('closes editFilter modal', async () => {
     const toggleEditModal = jest.fn();
-    await waitFor(() => setupComponent({ toggleEditModal }));
+    await act(() => {
+      renderComponent({ toggleEditModal });
+    });
     userEvent.click(screen.getByRole('button', { name: /Cancel/i }));
     expect(toggleEditModal).toHaveBeenCalledTimes(1);
   });
@@ -38,19 +42,27 @@ describe('EditFilter component', () => {
   it('save edited fields and close modal', async () => {
     const toggleEditModal = jest.fn();
     const editSavedFilter = jest.fn();
-    await waitFor(() => setupComponent({ toggleEditModal, editSavedFilter }));
+
+    await act(() => {
+      renderComponent({ toggleEditModal, editSavedFilter });
+    });
+
     const inputs = screen.getAllByRole('textbox');
     const textAreaElement = inputs[0] as HTMLTextAreaElement;
     const inputNameElement = inputs[1];
-    userEvent.paste(textAreaElement, 'edited code');
-    userEvent.type(inputNameElement, 'edited name');
-    await waitFor(() => fireEvent.submit(screen.getByRole('form')));
+    await act(() => {
+      userEvent.paste(textAreaElement, 'edited code');
+      userEvent.type(inputNameElement, 'edited name');
+      fireEvent.submit(screen.getByRole('form'));
+    });
     expect(toggleEditModal).toHaveBeenCalledTimes(1);
     expect(editSavedFilter).toHaveBeenCalledTimes(1);
   });
 
   it('checks input values to match', async () => {
-    await waitFor(() => setupComponent());
+    await act(() => {
+      renderComponent();
+    });
     const inputs = screen.getAllByRole('textbox');
     const textAreaElement = inputs[0] as HTMLTextAreaElement;
     const inputNameElement = inputs[1];

+ 5 - 3
kafka-ui-react-app/src/components/Topics/Topic/Details/Messages/Filters/__tests__/FilterModal.spec.tsx

@@ -4,12 +4,12 @@ import FilterModal, {
 } from 'components/Topics/Topic/Details/Messages/Filters/FilterModal';
 import { render } from 'lib/testHelpers';
 import { MessageFilters } from 'components/Topics/Topic/Details/Messages/Filters/Filters';
-import { screen, waitFor } from '@testing-library/react';
+import { screen, act } from '@testing-library/react';
 import userEvent from '@testing-library/user-event';
 
 const filters: MessageFilters[] = [{ name: 'name', code: 'code' }];
 
-const setupWrapper = (props?: Partial<FilterModalProps>) =>
+const renderComponent = (props?: Partial<FilterModalProps>) =>
   render(
     <FilterModal
       toggleIsOpen={jest.fn()}
@@ -23,7 +23,9 @@ const setupWrapper = (props?: Partial<FilterModalProps>) =>
   );
 describe('FilterModal component', () => {
   beforeEach(async () => {
-    await waitFor(() => setupWrapper());
+    await act(() => {
+      renderComponent();
+    });
   });
   it('renders component with add filter modal', () => {
     expect(

+ 38 - 32
kafka-ui-react-app/src/components/Topics/Topic/Details/Messages/Filters/__tests__/Filters.spec.tsx

@@ -5,7 +5,7 @@ import Filters, {
   SeekTypeOptions,
 } from 'components/Topics/Topic/Details/Messages/Filters/Filters';
 import { render } from 'lib/testHelpers';
-import { screen, waitFor, within } from '@testing-library/react';
+import { act, screen, within, waitFor } from '@testing-library/react';
 import userEvent from '@testing-library/user-event';
 import TopicMessagesContext, {
   ContextProps,
@@ -19,7 +19,7 @@ const defaultContextValue: ContextProps = {
   changeSeekDirection: jest.fn(),
 };
 
-const setupWrapper = (
+const renderComponent = (
   props: Partial<FiltersProps> = {},
   ctx: ContextProps = defaultContextValue
 ) => {
@@ -41,52 +41,58 @@ const setupWrapper = (
     </TopicMessagesContext.Provider>
   );
 };
-const getSubmit = () => screen.getByText('Submit');
 
 describe('Filters component', () => {
   it('shows cancel button while fetching', () => {
-    setupWrapper({ isFetching: true });
+    renderComponent({ isFetching: true });
     expect(screen.getByText('Cancel')).toBeInTheDocument();
   });
 
   it('shows submit button while fetching is over', () => {
-    setupWrapper();
-    expect(getSubmit()).toBeInTheDocument();
+    renderComponent();
+    expect(screen.getByText('Submit')).toBeInTheDocument();
   });
 
   describe('Input elements', () => {
     const inputValue = 'Hello World!';
 
-    beforeEach(() => setupWrapper());
+    beforeEach(async () => {
+      await act(() => {
+        renderComponent();
+      });
+    });
 
     it('search input', () => {
-      const SearchInput = screen.getByPlaceholderText('Search');
-      expect(SearchInput).toBeInTheDocument();
-      expect(SearchInput).toHaveValue('');
-      userEvent.type(SearchInput, inputValue);
-      expect(SearchInput).toHaveValue(inputValue);
+      const searchInput = screen.getByPlaceholderText('Search');
+      expect(searchInput).toHaveValue('');
+      userEvent.type(searchInput, inputValue);
+      expect(searchInput).toHaveValue(inputValue);
     });
 
     it('offset input', () => {
-      const OffsetInput = screen.getByPlaceholderText('Offset');
-      expect(OffsetInput).toBeInTheDocument();
-      expect(OffsetInput).toHaveValue('');
-      userEvent.type(OffsetInput, inputValue);
-      expect(OffsetInput).toHaveValue(inputValue);
+      const offsetInput = screen.getByPlaceholderText('Offset');
+      expect(offsetInput).toHaveValue('');
+      userEvent.type(offsetInput, inputValue);
+      expect(offsetInput).toHaveValue(inputValue);
     });
 
     it('timestamp input', async () => {
       const seekTypeSelect = screen.getAllByRole('listbox');
       const option = screen.getAllByRole('option');
 
-      userEvent.click(seekTypeSelect[0]);
-      userEvent.selectOptions(seekTypeSelect[0], ['Timestamp']);
+      await act(() => userEvent.click(seekTypeSelect[0]));
+
+      await act(() => {
+        userEvent.selectOptions(seekTypeSelect[0], ['Timestamp']);
+      });
+
       expect(option[0]).toHaveTextContent('Timestamp');
       const timestampInput = screen.getByPlaceholderText('Select timestamp');
-      expect(timestampInput).toBeInTheDocument();
       expect(timestampInput).toHaveValue('');
-      userEvent.type(timestampInput, inputValue);
-      await waitFor(() => expect(timestampInput).toHaveValue(inputValue));
+
+      await waitFor(() => userEvent.type(timestampInput, inputValue));
+
+      expect(timestampInput).toHaveValue(inputValue);
       expect(screen.getByText('Submit')).toBeInTheDocument();
     });
   });
@@ -101,7 +107,7 @@ describe('Filters component', () => {
     const mockTypeOptionSelectLabel = selectTypeOptionValue.label;
 
     beforeEach(() => {
-      setupWrapper();
+      renderComponent();
       seekTypeSelects = screen.getAllByRole('listbox');
       options = screen.getAllByRole('option');
     });
@@ -124,16 +130,16 @@ describe('Filters component', () => {
   });
 
   it('stop loading when live mode is active', () => {
-    setupWrapper();
+    renderComponent();
     userEvent.click(screen.getByText('Stop loading'));
     const option = screen.getAllByRole('option');
     expect(option[1]).toHaveTextContent('Oldest First');
-    expect(getSubmit()).toBeInTheDocument();
+    expect(screen.getByText('Submit')).toBeInTheDocument();
   });
 
   it('renders addFilter modal', async () => {
-    setupWrapper();
-    await waitFor(() =>
+    renderComponent();
+    await act(() =>
       userEvent.click(
         screen.getByRole('button', {
           name: /add filters/i,
@@ -145,9 +151,9 @@ describe('Filters component', () => {
 
   describe('when there is active smart filter', () => {
     beforeEach(async () => {
-      setupWrapper();
+      renderComponent();
 
-      await waitFor(() =>
+      await act(() =>
         userEvent.click(
           screen.getByRole('button', {
             name: /add filters/i,
@@ -165,7 +171,7 @@ describe('Filters component', () => {
 
       const textAreaElement = textBoxElements[0] as HTMLTextAreaElement;
       const inputNameElement = textBoxElements[1];
-      await waitFor(() => {
+      await act(() => {
         userEvent.paste(textAreaElement, filterName);
         userEvent.type(inputNameElement, filterCode);
       });
@@ -173,7 +179,7 @@ describe('Filters component', () => {
       expect(textAreaElement.value).toEqual(`${filterName}\n\n`);
       expect(inputNameElement).toHaveValue(filterCode);
 
-      await waitFor(() =>
+      await act(() =>
         userEvent.click(
           within(messageFilterModal).getByRole('button', {
             name: /add filter/i,
@@ -191,7 +197,7 @@ describe('Filters component', () => {
       const deleteIcon = within(smartFilterElement).getByTestId(
         'activeSmartFilterCloseIcon'
       );
-      await waitFor(() => userEvent.click(deleteIcon));
+      await act(() => userEvent.click(deleteIcon));
 
       const anotherSmartFilterElement =
         screen.queryByTestId('activeSmartFilter');

+ 1 - 1
kafka-ui-react-app/src/components/Topics/Topic/Details/Messages/Messages.tsx

@@ -1,7 +1,7 @@
 import React, { useCallback, useMemo, useState } from 'react';
 import TopicMessagesContext from 'components/contexts/TopicMessagesContext';
 import { SeekDirection } from 'generated-sources';
-import { useLocation } from 'react-router';
+import { useLocation } from 'react-router-dom';
 
 import FiltersContainer from './Filters/FiltersContainer';
 import MessagesTable from './MessagesTable';

+ 1 - 1
kafka-ui-react-app/src/components/Topics/Topic/Details/Messages/MessagesTable.tsx

@@ -6,7 +6,7 @@ import styled from 'styled-components';
 import { compact, concat, groupBy, map, maxBy, minBy } from 'lodash';
 import React, { useContext } from 'react';
 import { useSelector } from 'react-redux';
-import { useHistory } from 'react-router';
+import { useHistory } from 'react-router-dom';
 import {
   getTopicMessges,
   getIsTopicMessagesFetching,

+ 1 - 1
kafka-ui-react-app/src/components/Topics/Topic/Details/Messages/__test__/MessagesTable.spec.tsx

@@ -2,7 +2,7 @@ import React from 'react';
 import { screen } from '@testing-library/react';
 import { render } from 'lib/testHelpers';
 import MessagesTable from 'components/Topics/Topic/Details/Messages/MessagesTable';
-import { Router } from 'react-router';
+import { Router } from 'react-router-dom';
 import { createMemoryHistory, MemoryHistory } from 'history';
 import { SeekDirection, SeekType, TopicMessage } from 'generated-sources';
 import userEvent from '@testing-library/user-event';

+ 35 - 49
kafka-ui-react-app/src/components/Topics/Topic/Edit/DangerZone/__test__/DangerZone.spec.tsx

@@ -2,7 +2,7 @@ import React from 'react';
 import DangerZone, {
   Props,
 } from 'components/Topics/Topic/Edit/DangerZone/DangerZone';
-import { screen, waitFor, within } from '@testing-library/react';
+import { act, screen, waitFor, within } from '@testing-library/react';
 import userEvent from '@testing-library/user-event';
 import { render } from 'lib/testHelpers';
 import {
@@ -13,8 +13,8 @@ import {
 const defaultPartitions = 3;
 const defaultReplicationFactor = 3;
 
-const renderComponent = (props?: Partial<Props>) => {
-  return render(
+const renderComponent = (props?: Partial<Props>) =>
+  render(
     <DangerZone
       clusterName={clusterName}
       topicName={topicName}
@@ -27,7 +27,6 @@ const renderComponent = (props?: Partial<Props>) => {
       {...props}
     />
   );
-};
 
 const clickOnDialogSubmitButton = () => {
   userEvent.click(
@@ -40,18 +39,14 @@ const clickOnDialogSubmitButton = () => {
 const checkDialogThenPressCancel = async () => {
   const dialog = screen.getByRole('dialog');
   expect(screen.getByRole('dialog')).toBeInTheDocument();
-
-  await waitFor(() => {
-    userEvent.click(within(dialog).getByText(/cancel/i));
-  });
-
+  userEvent.click(within(dialog).getByText(/cancel/i));
   await waitFor(() =>
     expect(screen.queryByRole('dialog')).not.toBeInTheDocument()
   );
 };
 
 describe('DangerZone', () => {
-  it('renders the component', () => {
+  it('renders the component', async () => {
     renderComponent();
 
     const numberOfPartitionsEditForm = screen.getByRole('form', {
@@ -139,27 +134,23 @@ describe('DangerZone', () => {
     const partitionInput = screen.getByPlaceholderText('Number of partitions');
     const partitionInputSubmitBtn = screen.getAllByText(/submit/i)[0];
     const value = (defaultPartitions - 4).toString();
-
     expect(partitionInputSubmitBtn).toBeDisabled();
-    await waitFor(() => {
+    await act(() => {
       userEvent.clear(partitionInput);
       userEvent.type(partitionInput, value);
     });
-
     expect(partitionInput).toHaveValue(+value);
     expect(partitionInputSubmitBtn).toBeEnabled();
-    userEvent.click(partitionInputSubmitBtn);
-
-    await waitFor(() => {
-      expect(
-        screen.getByText(/You can only increase the number of partitions!/i)
-      ).toBeInTheDocument();
-    });
-
-    await waitFor(() => {
-      userEvent.clear(partitionInput);
+    await act(() => {
+      userEvent.click(partitionInputSubmitBtn);
     });
-    expect(screen.getByText(/are required/i)).toBeInTheDocument();
+    expect(
+      screen.getByText(/You can only increase the number of partitions!/i)
+    ).toBeInTheDocument();
+    userEvent.clear(partitionInput);
+    await waitFor(() =>
+      expect(screen.getByText(/are required/i)).toBeInTheDocument()
+    );
   });
 
   it('should view the validation error when Replication Facto value is lower than the default passed or empty', async () => {
@@ -168,31 +159,31 @@ describe('DangerZone', () => {
       screen.getByPlaceholderText('Replication Factor');
     const replicatorFactorInputSubmitBtn = screen.getAllByText(/submit/i)[1];
 
-    await waitFor(() => {
-      userEvent.clear(replicatorFactorInput);
-    });
+    await waitFor(() => userEvent.clear(replicatorFactorInput));
 
     expect(replicatorFactorInputSubmitBtn).toBeEnabled();
-    await waitFor(() => {
-      userEvent.click(replicatorFactorInputSubmitBtn);
-    });
-
-    expect(screen.getByText(/are required/i)).toBeInTheDocument();
-
-    await waitFor(() => {
-      userEvent.type(replicatorFactorInput, '1');
-    });
-    expect(screen.queryByText(/are required/i)).not.toBeInTheDocument();
+    userEvent.click(replicatorFactorInputSubmitBtn);
+    await waitFor(() =>
+      expect(screen.getByText(/are required/i)).toBeInTheDocument()
+    );
+    userEvent.type(replicatorFactorInput, '1');
+    await waitFor(() =>
+      expect(screen.queryByText(/are required/i)).not.toBeInTheDocument()
+    );
   });
 
-  it('should close any popup if the partitionsCount is Increased ', () => {
+  it('should close any popup if the partitionsCount is Increased ', async () => {
     renderComponent({ partitionsCountIncreased: true });
-    expect(screen.queryByRole('dialog')).not.toBeInTheDocument();
+    await waitFor(() =>
+      expect(screen.queryByRole('dialog')).not.toBeInTheDocument()
+    );
   });
 
-  it('should close any popup if the replicationFactor is Updated', () => {
+  it('should close any popup if the replicationFactor is Updated', async () => {
     renderComponent({ replicationFactorUpdated: true });
-    expect(screen.queryByRole('dialog')).not.toBeInTheDocument();
+    await waitFor(() =>
+      expect(screen.queryByRole('dialog')).not.toBeInTheDocument()
+    );
   });
 
   it('should already opened Confirmation popup if partitionsCount is Increased', async () => {
@@ -254,14 +245,12 @@ describe('DangerZone', () => {
 
   it('should close the partitions dialog if he cancel button is pressed', async () => {
     renderComponent();
+
     const partitionInput = screen.getByPlaceholderText('Number of partitions');
     const partitionInputSubmitBtn = screen.getAllByText(/submit/i)[0];
 
-    await waitFor(() => {
+    await act(() => {
       userEvent.type(partitionInput, '5');
-    });
-
-    await waitFor(() => {
       userEvent.click(partitionInputSubmitBtn);
     });
 
@@ -274,11 +263,8 @@ describe('DangerZone', () => {
       screen.getByPlaceholderText('Replication Factor');
     const replicatorFactorInputSubmitBtn = screen.getAllByText(/submit/i)[1];
 
-    await waitFor(() => {
+    await act(() => {
       userEvent.type(replicatorFactorInput, '5');
-    });
-
-    await waitFor(() => {
       userEvent.click(replicatorFactorInputSubmitBtn);
     });
 

+ 1 - 1
kafka-ui-react-app/src/components/Topics/Topic/Edit/Edit.tsx

@@ -10,7 +10,7 @@ import {
 import { useForm, FormProvider } from 'react-hook-form';
 import TopicForm from 'components/Topics/shared/Form/TopicForm';
 import { clusterTopicPath } from 'lib/paths';
-import { useHistory } from 'react-router';
+import { useHistory } from 'react-router-dom';
 import { yupResolver } from '@hookform/resolvers/yup';
 import { topicFormValidationSchema } from 'lib/yupExtended';
 import { TOPIC_CUSTOM_PARAMS_PREFIX, TOPIC_CUSTOM_PARAMS } from 'lib/constants';

+ 8 - 12
kafka-ui-react-app/src/components/Topics/Topic/Edit/__test__/Edit.spec.tsx

@@ -1,6 +1,6 @@
 import React from 'react';
 import Edit, { DEFAULTS, Props } from 'components/Topics/Topic/Edit/Edit';
-import { screen, waitFor } from '@testing-library/react';
+import { act, screen } from '@testing-library/react';
 import { render } from 'lib/testHelpers';
 import userEvent from '@testing-library/user-event';
 import { Router } from 'react-router-dom';
@@ -109,7 +109,7 @@ describe('Edit Component', () => {
       const btn = screen.getAllByText(/submit/i)[0];
       expect(btn).toBeEnabled();
 
-      await waitFor(() => {
+      await act(() => {
         userEvent.type(
           screen.getByPlaceholderText('Min In Sync Replicas'),
           '1'
@@ -117,9 +117,7 @@ describe('Edit Component', () => {
         userEvent.click(btn);
       });
       expect(updateTopicMock).toHaveBeenCalledTimes(1);
-      await waitFor(() => {
-        expect(mocked.push).not.toHaveBeenCalled();
-      });
+      expect(mocked.push).not.toHaveBeenCalled();
     });
 
     it('should check the submit functionality when topic updated is true', async () => {
@@ -135,7 +133,7 @@ describe('Edit Component', () => {
 
       const btn = screen.getAllByText(/submit/i)[0];
 
-      await waitFor(() => {
+      await act(() => {
         userEvent.type(
           screen.getByPlaceholderText('Min In Sync Replicas'),
           '1'
@@ -143,12 +141,10 @@ describe('Edit Component', () => {
         userEvent.click(btn);
       });
       expect(updateTopicMock).toHaveBeenCalledTimes(1);
-      await waitFor(() => {
-        expect(mocked.push).toHaveBeenCalled();
-        expect(mocked.location.pathname).toBe(
-          clusterTopicPath(clusterName, topicName)
-        );
-      });
+      expect(mocked.push).toHaveBeenCalled();
+      expect(mocked.location.pathname).toBe(
+        clusterTopicPath(clusterName, topicName)
+      );
     });
   });
 });

+ 1 - 1
kafka-ui-react-app/src/components/Topics/Topic/SendMessage/SendMessage.tsx

@@ -2,7 +2,7 @@ import Editor from 'components/common/Editor/Editor';
 import PageLoader from 'components/common/PageLoader/PageLoader';
 import React, { useEffect } from 'react';
 import { useForm, Controller } from 'react-hook-form';
-import { useHistory, useParams } from 'react-router';
+import { useHistory, useParams } from 'react-router-dom';
 import { clusterTopicMessagesPath } from 'lib/paths';
 import jsf from 'json-schema-faker';
 import { fetchTopicMessageSchema, messagesApiClient } from 'redux/actions';

+ 35 - 39
kafka-ui-react-app/src/components/Topics/Topic/SendMessage/__test__/SendMessage.spec.tsx

@@ -1,15 +1,11 @@
 import React from 'react';
 import SendMessage from 'components/Topics/Topic/SendMessage/SendMessage';
-import {
-  screen,
-  waitFor,
-  waitForElementToBeRemoved,
-} from '@testing-library/react';
+import { act, screen } from '@testing-library/react';
 import userEvent from '@testing-library/user-event';
 import fetchMock from 'fetch-mock';
 import { createMemoryHistory } from 'history';
 import { render } from 'lib/testHelpers';
-import { Route, Router } from 'react-router';
+import { Route, Router } from 'react-router-dom';
 import {
   clusterTopicMessagesPath,
   clusterTopicSendMessagePath,
@@ -43,31 +39,35 @@ const clusterName = 'testCluster';
 const topicName = externalTopicPayload.name;
 const history = createMemoryHistory();
 
-const renderComponent = () => {
+const renderComponent = async () => {
   history.push(clusterTopicSendMessagePath(clusterName, topicName));
-  render(
-    <>
-      <Router history={history}>
-        <Route path={clusterTopicSendMessagePath(':clusterName', ':topicName')}>
-          <SendMessage />
-        </Route>
-      </Router>
-      <S.AlertsContainer role="toolbar">
-        <Alerts />
-      </S.AlertsContainer>
-    </>,
-    { store }
-  );
+  await act(() => {
+    render(
+      <>
+        <Router history={history}>
+          <Route
+            path={clusterTopicSendMessagePath(':clusterName', ':topicName')}
+          >
+            <SendMessage />
+          </Route>
+        </Router>
+        <S.AlertsContainer role="toolbar">
+          <Alerts />
+        </S.AlertsContainer>
+      </>,
+      { store }
+    );
+  });
 };
 
 const renderAndSubmitData = async (error: string[] = []) => {
-  renderComponent();
-  await waitForElementToBeRemoved(() => screen.getByRole('progressbar'));
-
-  userEvent.selectOptions(screen.getByLabelText('Partition'), '0');
-  const sendBtn = await screen.findByText('Send');
-  (validateMessage as Mock).mockImplementation(() => error);
-  userEvent.click(sendBtn);
+  await renderComponent();
+  expect(screen.queryByRole('progressbar')).not.toBeInTheDocument();
+  await act(() => {
+    userEvent.selectOptions(screen.getByLabelText('Partition'), '0');
+    (validateMessage as Mock).mockImplementation(() => error);
+    userEvent.click(screen.getByText('Send'));
+  });
 };
 
 describe('SendMessage', () => {
@@ -85,12 +85,14 @@ describe('SendMessage', () => {
     fetchMock.reset();
   });
 
-  it('fetches schema on first render', () => {
+  it('fetches schema on first render', async () => {
     const fetchTopicMessageSchemaMock = fetchMock.getOnce(
       `/api/clusters/${clusterName}/topics/${topicName}/messages/schema`,
       testSchema
     );
-    renderComponent();
+    await act(() => {
+      renderComponent();
+    });
     expect(fetchTopicMessageSchemaMock.called()).toBeTruthy();
   });
 
@@ -107,9 +109,7 @@ describe('SendMessage', () => {
     it('calls sendTopicMessage on submit', async () => {
       const sendTopicMessageMock = fetchMock.postOnce(url, 200);
       await renderAndSubmitData();
-      await waitFor(() =>
-        expect(sendTopicMessageMock.called(url)).toBeTruthy()
-      );
+      expect(sendTopicMessageMock.called(url)).toBeTruthy();
       expect(history.location.pathname).toEqual(
         clusterTopicMessagesPath(clusterName, topicName)
       );
@@ -120,12 +120,8 @@ describe('SendMessage', () => {
         throws: 'Error',
       });
       await renderAndSubmitData();
-      await waitFor(() => {
-        expect(sendTopicMessageMock.called(url)).toBeTruthy();
-      });
-      await waitFor(() => {
-        expect(screen.getByRole('alert')).toBeInTheDocument();
-      });
+      expect(sendTopicMessageMock.called(url)).toBeTruthy();
+      expect(screen.getByRole('alert')).toBeInTheDocument();
       expect(history.location.pathname).toEqual(
         clusterTopicMessagesPath(clusterName, topicName)
       );
@@ -134,7 +130,7 @@ describe('SendMessage', () => {
     it('should check and view validation error message when is not valid', async () => {
       const sendTopicMessageMock = fetchMock.postOnce(url, 200);
       await renderAndSubmitData(['error']);
-      await waitFor(() => expect(sendTopicMessageMock.called(url)).toBeFalsy());
+      expect(sendTopicMessageMock.called(url)).toBeFalsy();
       expect(history.location.pathname).not.toEqual(
         clusterTopicMessagesPath(clusterName, topicName)
       );

+ 5 - 5
kafka-ui-react-app/src/components/Topics/shared/Form/CustomParams/__test__/CustomParamField.spec.tsx

@@ -1,5 +1,5 @@
-import React from 'react';
-import { screen, waitFor, within } from '@testing-library/react';
+import React, { PropsWithChildren } from 'react';
+import { act, screen, within } from '@testing-library/react';
 import { render } from 'lib/testHelpers';
 import CustomParamsField, {
   Props,
@@ -16,8 +16,8 @@ const field = { name: 'name', value: 'value', id: 'id' };
 const SPACE_KEY = ' ';
 
 const selectOption = async (listbox: HTMLElement, option: string) => {
-  await waitFor(() => userEvent.click(listbox));
-  await waitFor(() => userEvent.click(screen.getByText(option)));
+  await act(() => userEvent.click(listbox));
+  await act(() => userEvent.click(screen.getByText(option)));
 };
 
 describe('CustomParamsField', () => {
@@ -25,7 +25,7 @@ describe('CustomParamsField', () => {
   const setExistingFields = jest.fn();
 
   const setupComponent = (props: Props) => {
-    const Wrapper: React.FC = ({ children }) => {
+    const Wrapper: React.FC<PropsWithChildren<unknown>> = ({ children }) => {
       const methods = useForm();
       return <FormProvider {...methods}>{children}</FormProvider>;
     };

+ 15 - 14
kafka-ui-react-app/src/components/Topics/shared/Form/CustomParams/__test__/CustomParams.spec.tsx

@@ -1,5 +1,5 @@
-import React from 'react';
-import { screen, waitFor, within } from '@testing-library/react';
+import React, { PropsWithChildren } from 'react';
+import { act, screen, within } from '@testing-library/react';
 import { render } from 'lib/testHelpers';
 import CustomParams, {
   CustomParamsProps,
@@ -11,10 +11,10 @@ import { TOPIC_CUSTOM_PARAMS } from 'lib/constants';
 import { defaultValues } from './fixtures';
 
 const selectOption = async (listbox: HTMLElement, option: string) => {
-  await waitFor(() => {
+  await act(() => {
     userEvent.click(listbox);
-    userEvent.click(screen.getByText(option));
   });
+  userEvent.click(screen.getByText(option));
 };
 
 const expectOptionIsSelected = (listbox: HTMLElement, option: string) => {
@@ -28,7 +28,7 @@ const expectOptionAvailability = async (
   option: string,
   disabled: boolean
 ) => {
-  await waitFor(() => userEvent.click(listbox));
+  await act(() => userEvent.click(listbox));
   const selectedOptions = within(listbox).getAllByText(option).reverse();
   // its either two or one nodes, we only need last one
   const selectedOption = selectedOptions[0];
@@ -43,11 +43,11 @@ const expectOptionAvailability = async (
     'cursor',
     disabled ? 'not-allowed' : 'pointer'
   );
-  await waitFor(() => userEvent.click(listbox));
+  await act(() => userEvent.click(listbox));
 };
 
 const renderComponent = (props: CustomParamsProps, defaults = {}) => {
-  const Wrapper: React.FC = ({ children }) => {
+  const Wrapper: React.FC<PropsWithChildren<unknown>> = ({ children }) => {
     const methods = useForm({ defaultValues: defaults });
     return <FormProvider {...methods}>{children}</FormProvider>;
   };
@@ -81,10 +81,11 @@ describe('CustomParams', () => {
 
   describe('works with user inputs correctly', () => {
     let button: HTMLButtonElement;
+
     beforeEach(async () => {
       renderComponent({ isSubmitting: false });
       button = screen.getByRole('button');
-      await waitFor(() => userEvent.click(button));
+      await act(() => userEvent.click(button));
     });
 
     it('button click creates custom param fieldset', async () => {
@@ -119,8 +120,8 @@ describe('CustomParams', () => {
     });
 
     it('multiple button clicks create multiple fieldsets', async () => {
-      await waitFor(() => userEvent.click(button));
-      await waitFor(() => userEvent.click(button));
+      await act(() => userEvent.click(button));
+      await act(() => userEvent.click(button));
 
       const listboxes = screen.getAllByRole('listbox');
       expect(listboxes.length).toBe(3);
@@ -130,7 +131,7 @@ describe('CustomParams', () => {
     });
 
     it("can't select already selected option", async () => {
-      await waitFor(() => userEvent.click(button));
+      await act(() => userEvent.click(button));
 
       const listboxes = screen.getAllByRole('listbox');
 
@@ -143,8 +144,8 @@ describe('CustomParams', () => {
     });
 
     it('when fieldset with selected custom property type is deleted disabled options update correctly', async () => {
-      await waitFor(() => userEvent.click(button));
-      await waitFor(() => userEvent.click(button));
+      await act(() => userEvent.click(button));
+      await act(() => userEvent.click(button));
 
       const listboxes = screen.getAllByRole('listbox');
 
@@ -171,7 +172,7 @@ describe('CustomParams', () => {
       const deleteSecondFieldsetButton = screen.getByTitle(
         'Delete customParam field 1'
       );
-      await waitFor(() => userEvent.click(deleteSecondFieldsetButton));
+      await act(() => userEvent.click(deleteSecondFieldsetButton));
       expect(secondListbox).not.toBeInTheDocument();
 
       await expectOptionAvailability(

+ 2 - 2
kafka-ui-react-app/src/components/Topics/shared/Form/__tests__/TimeToRetainBtn.spec.tsx

@@ -1,4 +1,4 @@
-import React from 'react';
+import React, { PropsWithChildren } from 'react';
 import { render } from 'lib/testHelpers';
 import { screen } from '@testing-library/react';
 import TimeToRetainBtn, {
@@ -14,7 +14,7 @@ describe('TimeToRetainBtn', () => {
     text: 'defaultPropsText',
     value: 0,
   };
-  const Wrapper: React.FC = ({ children }) => {
+  const Wrapper: React.FC<PropsWithChildren<unknown>> = ({ children }) => {
     const methods = useForm();
     return <FormProvider {...methods}>{children}</FormProvider>;
   };

+ 2 - 2
kafka-ui-react-app/src/components/Topics/shared/Form/__tests__/TimeToRetainBtns.spec.tsx

@@ -1,4 +1,4 @@
-import React from 'react';
+import React, { PropsWithChildren } from 'react';
 import { render } from 'lib/testHelpers';
 import { screen } from '@testing-library/react';
 import TimeToRetainBtns, {
@@ -11,7 +11,7 @@ describe('TimeToRetainBtns', () => {
     name: 'defaultPropsTestingName',
     value: 'defaultPropsValue',
   };
-  const Wrapper: React.FC = ({ children }) => {
+  const Wrapper: React.FC<PropsWithChildren<unknown>> = ({ children }) => {
     const methods = useForm();
     return <FormProvider {...methods}>{children}</FormProvider>;
   };

+ 2 - 2
kafka-ui-react-app/src/components/Topics/shared/Form/__tests__/TopicForm.spec.tsx

@@ -1,4 +1,4 @@
-import React from 'react';
+import React, { PropsWithChildren } from 'react';
 import { render } from 'lib/testHelpers';
 import { screen } from '@testing-library/dom';
 import { FormProvider, useForm } from 'react-hook-form';
@@ -9,7 +9,7 @@ const isSubmitting = false;
 const onSubmit = jest.fn();
 
 const renderComponent = (props: Props = { isSubmitting, onSubmit }) => {
-  const Wrapper: React.FC = ({ children }) => {
+  const Wrapper: React.FC<PropsWithChildren<unknown>> = ({ children }) => {
     const methods = useForm();
     return <FormProvider {...methods}>{children}</FormProvider>;
   };

+ 8 - 4
kafka-ui-react-app/src/components/__tests__/App.spec.tsx

@@ -1,5 +1,5 @@
 import React from 'react';
-import { screen, within, waitFor } from '@testing-library/react';
+import { screen, within, act } from '@testing-library/react';
 import App from 'components/App';
 import { render } from 'lib/testHelpers';
 import { clustersPayload } from 'redux/reducers/clusters/__test__/fixtures';
@@ -43,10 +43,14 @@ describe('App', () => {
   describe('with clusters list fetched', () => {
     it('shows Cluster list', async () => {
       const mock = fetchMock.getOnce('/api/clusters', clustersPayload);
-      render(<App />, {
-        pathname: '/',
+      await act(() => {
+        render(<App />, {
+          pathname: '/',
+        });
       });
-      await waitFor(() => expect(mock.called()).toBeTruthy());
+
+      expect(mock.called()).toBeTruthy();
+
       const menuContainer = screen.getByLabelText('Sidebar Menu');
       expect(menuContainer).toBeInTheDocument();
       expect(within(menuContainer).getByText('Dashboard')).toBeInTheDocument();

+ 4 - 2
kafka-ui-react-app/src/components/common/Breadcrumb/Breadcrumb.provider.tsx

@@ -1,4 +1,4 @@
-import React, { useState } from 'react';
+import React, { PropsWithChildren, useState } from 'react';
 import capitalize from 'lodash/capitalize';
 
 import { BreadcrumbContext, BreadcrumbEntry } from './Breadcrumb.context';
@@ -13,7 +13,9 @@ const mapLocationToPath = (
       : item
   );
 
-export const BreadcrumbProvider: React.FC = ({ children }) => {
+export const BreadcrumbProvider: React.FC<PropsWithChildren<unknown>> = ({
+  children,
+}) => {
   const [state, setState] = useState<BreadcrumbEntry>({
     link: '',
     path: [],

+ 4 - 2
kafka-ui-react-app/src/components/common/ConfirmationModal/ConfirmationModal.tsx

@@ -1,4 +1,4 @@
-import React from 'react';
+import React, { PropsWithChildren } from 'react';
 import { Button } from 'components/common/Button/Button';
 
 import { ConfirmationModalWrapper } from './ConfirmationModal.styled';
@@ -12,7 +12,9 @@ export interface ConfirmationModalProps {
   submitBtnText?: string;
 }
 
-const ConfirmationModal: React.FC<ConfirmationModalProps> = ({
+const ConfirmationModal: React.FC<
+  PropsWithChildren<ConfirmationModalProps>
+> = ({
   isOpen,
   children,
   title = 'Confirm the action',

+ 12 - 2
kafka-ui-react-app/src/components/common/Dropdown/Dropdown.tsx

@@ -1,6 +1,11 @@
 import useOutsideClickRef from '@rooks/use-outside-click-ref';
 import cx from 'classnames';
-import React, { useCallback, useMemo, useState } from 'react';
+import React, {
+  PropsWithChildren,
+  useCallback,
+  useMemo,
+  useState,
+} from 'react';
 
 import * as S from './Dropdown.styled';
 
@@ -10,7 +15,12 @@ export interface DropdownProps {
   up?: boolean;
 }
 
-const Dropdown: React.FC<DropdownProps> = ({ label, right, up, children }) => {
+const Dropdown: React.FC<PropsWithChildren<DropdownProps>> = ({
+  label,
+  right,
+  up,
+  children,
+}) => {
   const [active, setActive] = useState<boolean>(false);
   const [wrapperRef] = useOutsideClickRef(() => setActive(false));
   const onClick = useCallback(() => setActive(!active), [active]);

+ 2 - 2
kafka-ui-react-app/src/components/common/Dropdown/DropdownItem.tsx

@@ -1,4 +1,4 @@
-import React from 'react';
+import React, { PropsWithChildren } from 'react';
 
 import * as S from './Dropdown.styled';
 
@@ -7,7 +7,7 @@ export interface DropdownItemProps {
   danger?: boolean;
 }
 
-const DropdownItem: React.FC<DropdownItemProps> = ({
+const DropdownItem: React.FC<PropsWithChildren<DropdownItemProps>> = ({
   onClick,
   danger,
   children,

+ 19 - 21
kafka-ui-react-app/src/components/common/Metrics/Indicator.tsx

@@ -1,4 +1,4 @@
-import React from 'react';
+import React, { PropsWithChildren } from 'react';
 import { AlertType } from 'redux/interfaces';
 
 import * as S from './Metrics.styled';
@@ -11,31 +11,29 @@ export interface Props {
   alertType?: AlertType;
 }
 
-const Indicator: React.FC<Props> = ({
+const Indicator: React.FC<PropsWithChildren<Props>> = ({
   label,
   title,
   fetching,
   isAlert,
   alertType = 'error',
   children,
-}) => {
-  return (
-    <S.IndicatorWrapper>
-      <div title={title}>
-        <S.IndicatorTitle>
-          {label}{' '}
-          {isAlert && (
-            <S.CircularAlertWrapper>
-              <S.CircularAlert $type={alertType} />
-            </S.CircularAlertWrapper>
-          )}
-        </S.IndicatorTitle>
-        <span>
-          {fetching ? <i className="fas fa-spinner fa-pulse" /> : children}
-        </span>
-      </div>
-    </S.IndicatorWrapper>
-  );
-};
+}) => (
+  <S.IndicatorWrapper>
+    <div title={title}>
+      <S.IndicatorTitle>
+        {label}{' '}
+        {isAlert && (
+          <S.CircularAlertWrapper>
+            <S.CircularAlert $type={alertType} />
+          </S.CircularAlertWrapper>
+        )}
+      </S.IndicatorTitle>
+      <span>
+        {fetching ? <i className="fas fa-spinner fa-pulse" /> : children}
+      </span>
+    </div>
+  </S.IndicatorWrapper>
+);
 
 export default Indicator;

+ 7 - 9
kafka-ui-react-app/src/components/common/Metrics/Section.tsx

@@ -1,4 +1,4 @@
-import React from 'react';
+import React, { PropsWithChildren } from 'react';
 
 import * as S from './Metrics.styled';
 
@@ -6,13 +6,11 @@ interface Props {
   title?: string;
 }
 
-const Section: React.FC<Props> = ({ title, children }) => {
-  return (
-    <div>
-      {title && <S.SectionTitle>{title}</S.SectionTitle>}
-      <S.IndicatorsWrapper>{children}</S.IndicatorsWrapper>
-    </div>
-  );
-};
+const Section: React.FC<PropsWithChildren<Props>> = ({ title, children }) => (
+  <div>
+    {title && <S.SectionTitle>{title}</S.SectionTitle>}
+    <S.IndicatorsWrapper>{children}</S.IndicatorsWrapper>
+  </div>
+);
 
 export default Section;

+ 6 - 2
kafka-ui-react-app/src/components/common/PageHeading/PageHeading.tsx

@@ -1,5 +1,5 @@
 import styled from 'styled-components';
-import React from 'react';
+import React, { PropsWithChildren } from 'react';
 import Heading from 'components/common/heading/Heading.styled';
 
 interface Props {
@@ -7,7 +7,11 @@ interface Props {
   className?: string;
 }
 
-const PageHeading: React.FC<Props> = ({ text, className, children }) => {
+const PageHeading: React.FC<PropsWithChildren<Props>> = ({
+  text,
+  className,
+  children,
+}) => {
   return (
     <div className={className}>
       <Heading>{text}</Heading>

+ 1 - 1
kafka-ui-react-app/src/components/common/Pagination/__tests__/Pagination.spec.tsx

@@ -1,5 +1,5 @@
 import React from 'react';
-import { StaticRouter } from 'react-router';
+import { StaticRouter } from 'react-router-dom';
 import Pagination, {
   PaginationProps,
 } from 'components/common/Pagination/Pagination';

+ 12 - 10
kafka-ui-react-app/src/components/common/SmartTable/TableRow.tsx

@@ -67,18 +67,20 @@ export const TableRow = <T, TId extends IdType, OT = never>({
         const Cell = cell as React.FC<TableCellProps<T, TId, OT>> | undefined;
         const TdComponent = customTd || Td;
 
+        const content = Cell ? (
+          <Cell
+            tableState={tableState}
+            hovered={hovered}
+            rowIndex={index}
+            dataItem={dataItem}
+          />
+        ) : (
+          field && propertyLookup(field, dataItem)
+        );
+
         return (
           <TdComponent maxWidth={maxWidth}>
-            {Cell ? (
-              <Cell
-                tableState={tableState}
-                hovered={hovered}
-                rowIndex={index}
-                dataItem={dataItem}
-              />
-            ) : (
-              field && propertyLookup(field, dataItem)
-            )}
+            {content as React.ReactNode}
           </TdComponent>
         );
       })}

+ 2 - 2
kafka-ui-react-app/src/components/common/Tabs/Tabs.tsx

@@ -1,5 +1,5 @@
 /* eslint-disable jsx-a11y/anchor-is-valid */
-import React from 'react';
+import React, { PropsWithChildren } from 'react';
 import classNames from 'classnames';
 
 interface TabsProps {
@@ -8,7 +8,7 @@ interface TabsProps {
   onChange?(index: number): void;
 }
 
-const Tabs: React.FC<TabsProps> = ({
+const Tabs: React.FC<PropsWithChildren<TabsProps>> = ({
   tabs,
   defaultSelectedIndex = 0,
   onChange,

+ 5 - 2
kafka-ui-react-app/src/components/common/heading/Heading.styled.tsx

@@ -1,4 +1,4 @@
-import React from 'react';
+import React, { PropsWithChildren } from 'react';
 import styled from 'styled-components';
 
 type HeadingLevel = 1 | 2 | 3 | 4 | 5 | 6;
@@ -13,7 +13,10 @@ const HeadingBase = styled.h1<HeadingBaseProps>`
 export interface Props {
   level?: HeadingLevel;
 }
-const Heading: React.FC<Props> = ({ level = 1, ...rest }) => {
+const Heading: React.FC<PropsWithChildren<Props>> = ({
+  level = 1,
+  ...rest
+}) => {
   return <HeadingBase as={`h${level}`} $level={level} {...rest} />;
 };
 

+ 4 - 2
kafka-ui-react-app/src/components/common/table/TableHeaderCell/TableHeaderCell.tsx

@@ -1,4 +1,4 @@
-import React from 'react';
+import React, { PropsWithChildren } from 'react';
 import { SortOrder, TopicColumnsToSort } from 'generated-sources';
 import * as S from 'components/common/table/TableHeaderCell/TableHeaderCell.styled';
 
@@ -12,7 +12,9 @@ export interface TableHeaderCellProps {
   handleOrderBy?: (orderBy: TopicColumnsToSort | null) => void;
 }
 
-const TableHeaderCell: React.FC<TableHeaderCellProps> = (props) => {
+const TableHeaderCell: React.FC<PropsWithChildren<TableHeaderCellProps>> = (
+  props
+) => {
   const {
     title,
     previewText,

+ 7 - 4
kafka-ui-react-app/src/index.tsx

@@ -1,5 +1,5 @@
 import React from 'react';
-import ReactDOM from 'react-dom';
+import { createRoot } from 'react-dom/client';
 import { BrowserRouter } from 'react-router-dom';
 import { Provider } from 'react-redux';
 import * as serviceWorker from 'serviceWorker';
@@ -8,13 +8,16 @@ import { store } from 'redux/store';
 import 'theme/index.scss';
 import 'lib/constants';
 
-ReactDOM.render(
+const container =
+  document.getElementById('root') || document.createElement('div');
+const root = createRoot(container);
+
+root.render(
   <Provider store={store}>
     <BrowserRouter basename={window.basePath || '/'}>
       <App />
     </BrowserRouter>
-  </Provider>,
-  document.getElementById('root')
+  </Provider>
 );
 
 // If you want your app to work offline and load faster, you can change

+ 1 - 1
kafka-ui-react-app/src/lib/hooks/__tests__/useModal.spec.ts

@@ -1,4 +1,4 @@
-import { renderHook, act } from '@testing-library/react-hooks';
+import { renderHook, act } from '@testing-library/react';
 import useModal from 'lib/hooks/useModal';
 
 describe('useModal CustomHook', () => {

+ 1 - 1
kafka-ui-react-app/src/lib/hooks/usePagination.ts

@@ -1,4 +1,4 @@
-import { useLocation } from 'react-router';
+import { useLocation } from 'react-router-dom';
 
 const usePagination = () => {
   const { search, pathname } = useLocation();

+ 1 - 1
kafka-ui-react-app/src/lib/hooks/useSearch.ts

@@ -1,5 +1,5 @@
 import { useCallback, useEffect, useMemo } from 'react';
-import { useHistory, useLocation } from 'react-router';
+import { useHistory, useLocation } from 'react-router-dom';
 
 const SEARCH_QUERY_ARG = 'q';
 

+ 4 - 2
kafka-ui-react-app/src/lib/testHelpers.tsx

@@ -1,4 +1,4 @@
-import React, { ReactElement } from 'react';
+import React, { PropsWithChildren, ReactElement } from 'react';
 import { StaticRouter } from 'react-router-dom';
 import { Provider } from 'react-redux';
 import { ThemeProvider } from 'styled-components';
@@ -40,7 +40,9 @@ const customRender = (
   }: CustomRenderOptions = {}
 ) => {
   // overrides @testing-library/react render.
-  const AllTheProviders: React.FC = ({ children }) => {
+  const AllTheProviders: React.FC<PropsWithChildren<unknown>> = ({
+    children,
+  }) => {
     return (
       <ThemeProvider theme={theme}>
         <Provider store={store}>

Some files were not shown because too many files changed in this diff