diff --git a/kafka-ui-react-app/package.json b/kafka-ui-react-app/package.json index 407c7c1f49..defe27f5c6 100644 --- a/kafka-ui-react-app/package.json +++ b/kafka-ui-react-app/package.json @@ -10,13 +10,13 @@ "@fortawesome/fontawesome-free": "^6.1.1", "@hookform/error-message": "^2.0.0", "@hookform/resolvers": "^2.7.1", - "@reduxjs/toolkit": "^1.8.2", + "@reduxjs/toolkit": "^1.8.3", "@rooks/use-outside-click-ref": "^4.10.1", "@testing-library/react": "^13.2.0", "@types/testing-library__jest-dom": "^5.14.5", "@types/yup": "^0.29.13", "@vitejs/plugin-react": "^2.0.0", - "ace-builds": "^1.4.12", + "ace-builds": "^1.7.1", "ajv": "^8.6.3", "babel-jest": "^28.1.1", "bulma": "^0.9.3", @@ -27,7 +27,6 @@ "jest-watch-typeahead": "^2.0.0", "json-schema-faker": "^0.5.0-rcv.39", "lodash": "^4.17.21", - "node-fetch": "^2.6.1", "pretty-ms": "7.0.1", "react": "^18.1.0", "react-ace": "^10.1.0", @@ -37,10 +36,9 @@ "react-is": "^18.2.0", "react-multi-select-component": "^4.0.6", "react-query": "^3.39.1", - "react-redux": "^7.2.6", + "react-redux": "^8.0.2", "react-router-dom": "^6.3.0", - "redux": "^4.1.1", - "redux-thunk": "^2.3.0", + "redux": "^4.2.0", "sass": "^1.52.3", "styled-components": "^5.3.1", "use-debounce": "^8.0.1", @@ -59,6 +57,7 @@ "start": "vite", "gen:sources": "rimraf src/generated-sources && openapi-generator-cli generate", "build": "vite build", + "preview": "vite preview", "lint": "eslint --ext .tsx,.ts src/", "lint:fix": "eslint --ext .tsx,.ts src/ --fix", "lint:CI": "eslint --ext .tsx,.ts src/ --max-warnings=0", @@ -89,9 +88,7 @@ "@types/react": "^18.0.9", "@types/react-datepicker": "^4.4.2", "@types/react-dom": "^18.0.3", - "@types/react-redux": "^7.1.18", "@types/react-router-dom": "^5.3.3", - "@types/redux-mock-store": "^1.0.3", "@types/styled-components": "^5.1.13", "@typescript-eslint/eslint-plugin": "^5.29.0", "@typescript-eslint/parser": "^5.29.0", @@ -116,7 +113,6 @@ "jest-styled-components": "^7.0.8", "lint-staged": "^13.0.2", "prettier": "^2.3.1", - "redux-mock-store": "^1.5.4", "rimraf": "^3.0.2", "ts-jest": "^28.0.5", "ts-node": "^10.8.1", diff --git a/kafka-ui-react-app/pnpm-lock.yaml b/kafka-ui-react-app/pnpm-lock.yaml index ba5acbcee8..014f2caf68 100644 --- a/kafka-ui-react-app/pnpm-lock.yaml +++ b/kafka-ui-react-app/pnpm-lock.yaml @@ -12,7 +12,7 @@ specifiers: '@hookform/resolvers': ^2.7.1 '@jest/types': ^28.1.1 '@openapitools/openapi-generator-cli': ^2.5.1 - '@reduxjs/toolkit': ^1.8.2 + '@reduxjs/toolkit': ^1.8.3 '@rooks/use-outside-click-ref': ^4.10.1 '@testing-library/dom': ^8.11.1 '@testing-library/jest-dom': ^5.16.4 @@ -25,16 +25,14 @@ specifiers: '@types/react': ^18.0.9 '@types/react-datepicker': ^4.4.2 '@types/react-dom': ^18.0.3 - '@types/react-redux': ^7.1.18 '@types/react-router-dom': ^5.3.3 - '@types/redux-mock-store': ^1.0.3 '@types/styled-components': ^5.1.13 '@types/testing-library__jest-dom': ^5.14.5 '@types/yup': ^0.29.13 '@typescript-eslint/eslint-plugin': ^5.29.0 '@typescript-eslint/parser': ^5.29.0 '@vitejs/plugin-react': ^2.0.0 - ace-builds: ^1.4.12 + ace-builds: ^1.7.1 ajv: ^8.6.3 babel-jest: ^28.1.1 bulma: ^0.9.3 @@ -65,7 +63,6 @@ specifiers: json-schema-faker: ^0.5.0-rcv.39 lint-staged: ^13.0.2 lodash: ^4.17.21 - node-fetch: ^2.6.1 prettier: ^2.3.1 pretty-ms: 7.0.1 react: ^18.1.0 @@ -76,11 +73,9 @@ specifiers: react-is: ^18.2.0 react-multi-select-component: ^4.0.6 react-query: ^3.39.1 - react-redux: ^7.2.6 + react-redux: ^8.0.2 react-router-dom: ^6.3.0 - redux: ^4.1.1 - redux-mock-store: ^1.5.4 - redux-thunk: ^2.3.0 + redux: ^4.2.0 rimraf: ^3.0.2 sass: ^1.52.3 styled-components: ^5.3.1 @@ -101,24 +96,23 @@ dependencies: '@fortawesome/fontawesome-free': 6.1.1 '@hookform/error-message': 2.0.0_l2dcsysovzdujulgxvsen7vbsm '@hookform/resolvers': 2.8.9_react-hook-form@7.6.9 - '@reduxjs/toolkit': 1.8.2_ydj23m2apjohmawqetv7uxfa2i + '@reduxjs/toolkit': 1.8.3_ctm756ikdwcjcvyfxxwskzbr6q '@rooks/use-outside-click-ref': 4.11.2_react@18.1.0 '@testing-library/react': 13.2.0_ef5jwxihqo6n7gxfmzogljlgcm '@types/testing-library__jest-dom': 5.14.5 '@types/yup': 0.29.13 '@vitejs/plugin-react': 2.0.0_vite@3.0.2 - ace-builds: 1.4.13 + ace-builds: 1.7.1 ajv: 8.8.2 babel-jest: 28.1.1_@babel+core@7.18.2 bulma: 0.9.3 classnames: 2.3.1 dayjs: 1.11.3 - fetch-mock: 9.11.0_node-fetch@2.6.7 + fetch-mock: 9.11.0 jest: 28.1.1_yqiaopbgmqcuvx27p5xxvum6wm jest-watch-typeahead: 2.0.0_jest@28.1.1 json-schema-faker: 0.5.0-rcv.40 lodash: 4.17.21 - node-fetch: 2.6.7 pretty-ms: 7.0.1 react: 18.1.0 react-ace: 10.1.0_ef5jwxihqo6n7gxfmzogljlgcm @@ -128,10 +122,9 @@ dependencies: react-is: 18.2.0 react-multi-select-component: 4.0.6_react@18.1.0 react-query: 3.39.1_ef5jwxihqo6n7gxfmzogljlgcm - react-redux: 7.2.6_ef5jwxihqo6n7gxfmzogljlgcm + react-redux: 8.0.2_nfqigfgwurfoimtkde74cji6ga react-router-dom: 6.3.0_ef5jwxihqo6n7gxfmzogljlgcm - redux: 4.1.2 - redux-thunk: 2.4.1_redux@4.1.2 + redux: 4.2.0 sass: 1.52.3 styled-components: 5.3.1_uuaz5p7xzfmtjacf6iqf7idnby use-debounce: 8.0.1_react@18.1.0 @@ -156,9 +149,7 @@ devDependencies: '@types/react': 18.0.9 '@types/react-datepicker': 4.4.2_react@18.1.0 '@types/react-dom': 18.0.5 - '@types/react-redux': 7.1.24 '@types/react-router-dom': 5.3.3 - '@types/redux-mock-store': 1.0.3 '@types/styled-components': 5.1.18 '@typescript-eslint/eslint-plugin': 5.29.0_uaxwak76nssfibsnotx5epygnu '@typescript-eslint/parser': 5.29.0_vjep2yp2sits3sqnodefgcbnfi @@ -176,14 +167,13 @@ devDependencies: eslint-plugin-prettier: 4.0.0_q7a4ir2sdihdzpzdlnbgmzjlpq eslint-plugin-react: 7.29.4_eslint@8.16.0 eslint-plugin-react-hooks: 4.5.0_eslint@8.16.0 - fetch-mock-jest: 1.5.1_node-fetch@2.6.7 + fetch-mock-jest: 1.5.1 husky: 8.0.1 jest-environment-jsdom: 28.1.1 jest-sonar-reporter: 2.0.0 jest-styled-components: 7.0.8_styled-components@5.3.1 lint-staged: 13.0.2 prettier: 2.5.1 - redux-mock-store: 1.5.4 rimraf: 3.0.2 ts-jest: 28.0.5_c4h4g76dcvkfgjwf6rprlfxfli ts-node: 10.8.1_t4lrjbt3sxauai4t5o275zsepa @@ -2343,11 +2333,11 @@ packages: /@popperjs/core/2.9.2: resolution: {integrity: sha512-VZMYa7+fXHdwIq1TDhSXoVmSPEGM/aa+6Aiq3nVVJ9bXr24zScr+NlKFKC3iPljA7ho/GAZr+d2jOf5GIRC30Q==} - /@reduxjs/toolkit/1.8.2_ydj23m2apjohmawqetv7uxfa2i: - resolution: {integrity: sha512-CtPw5TkN1pHRigMFCOS/0qg3b/yfPV5qGCsltVnIz7bx4PKTJlGHYfIxm97qskLknMzuGfjExaYdXJ77QTL0vg==} + /@reduxjs/toolkit/1.8.3_ctm756ikdwcjcvyfxxwskzbr6q: + resolution: {integrity: sha512-lU/LDIfORmjBbyDLaqFN2JB9YmAT1BElET9y0ZszwhSBa5Ef3t6o5CrHupw5J1iOXwd+o92QfQZ8OJpwXvsssg==} peerDependencies: react: ^16.9.0 || ^17.0.0 || ^18 - react-redux: ^7.2.1 || ^8.0.0-beta + react-redux: ^7.2.1 || ^8.0.2 peerDependenciesMeta: react: optional: true @@ -2356,9 +2346,9 @@ packages: dependencies: immer: 9.0.12 react: 18.1.0 - react-redux: 7.2.6_ef5jwxihqo6n7gxfmzogljlgcm - redux: 4.1.2 - redux-thunk: 2.4.1_redux@4.1.2 + react-redux: 8.0.2_nfqigfgwurfoimtkde74cji6ga + redux: 4.2.0 + redux-thunk: 2.4.1_redux@4.2.0 reselect: 4.1.5 dev: false @@ -2583,14 +2573,6 @@ packages: dependencies: '@types/react': 18.0.9 - /@types/react-redux/7.1.24: - resolution: {integrity: sha512-7FkurKcS1k0FHZEtdbbgN8Oc6b+stGSfZYjQGicofJ0j4U0qIn/jaSvnP2pLwZKiai3/17xqqxkkrxTgN8UNbQ==} - dependencies: - '@types/hoist-non-react-statics': 3.3.1 - '@types/react': 18.0.9 - hoist-non-react-statics: 3.3.2 - redux: 4.1.2 - /@types/react-router-dom/5.3.3: resolution: {integrity: sha512-kpqnYK4wcdm5UaWI3fLcELopqLrHgLqNsdpHauzlQktfkHL3npOSwtj1Uz9oKBAzs7lFtVkV8j83voAz2D8fhw==} dependencies: @@ -2613,12 +2595,6 @@ packages: '@types/scheduler': 0.16.2 csstype: 3.0.8 - /@types/redux-mock-store/1.0.3: - resolution: {integrity: sha512-Wqe3tJa6x9MxMN4DJnMfZoBRBRak1XTPklqj4qkVm5VBpZnC8PSADf4kLuFQ9NAdHaowfWoEeUMz7NWc2GMtnA==} - dependencies: - redux: 4.1.2 - dev: true - /@types/scheduler/0.16.2: resolution: {integrity: sha512-hppQEBDmlwhFAXKJX2KnWLYu5yMfi91yazPb2l+lbJiwW+wdo1gNeRA+3RgNSO39WYX2euey41KEwnqesU2Jew==} @@ -2642,6 +2618,10 @@ packages: resolution: {integrity: sha512-Q5vtl1W5ue16D+nIaW8JWebSSraJVlK+EthKn7e7UcD4KWsaSJ8BqGPXNaPghgtcn/fhvrN17Tv8ksUsQpiplw==} dev: true + /@types/use-sync-external-store/0.0.3: + resolution: {integrity: sha512-EwmlvuaxPNej9+T4v5AuBPJa2x2UOJVdjCtDHgcDqitUeOtjnJKJ+apYjVcAoBEMjKW1VVFGZLUb5+qqa09XFA==} + dev: false + /@types/yargs-parser/20.2.0: resolution: {integrity: sha512-37RSHht+gzzgYeobbG+KWryeAW8J33Nhr69cjTqSYymXVZEN9NbRYWoYlRtDhHKPVT1FyNKwaTPC1NynKZpzRA==} @@ -2875,10 +2855,6 @@ packages: resolution: {integrity: sha512-j2afSsaIENvHZN2B8GOpF566vZ5WVk5opAiMTvWgaQT8DkbOqsTfvNAvHoRGU2zzP8cPoqys+xHTRDWW8L+/BA==} dev: true - /ace-builds/1.4.13: - resolution: {integrity: sha512-SOLzdaQkY6ecPKYRDDg+MY1WoGgXA34cIvYJNNoBMGGUswHmlauU2Hy0UL96vW0Fs/LgFbMUjD+6vqzWTldIYQ==} - dev: false - /ace-builds/1.7.1: resolution: {integrity: sha512-1mcbP5kXvr729sJ9dA/8tul0pjuvKbma0LF/ZMRwPEwjoNWNpe/x0OXpaPJo36aRpZCjRZMl5zsME3hAKTiaNw==} dev: false @@ -4730,7 +4706,7 @@ packages: dependencies: bser: 2.1.1 - /fetch-mock-jest/1.5.1_node-fetch@2.6.7: + /fetch-mock-jest/1.5.1: resolution: {integrity: sha512-+utwzP8C+Pax1GSka3nFXILWMY3Er2L+s090FOgqVNrNCPp0fDqgXnAHAJf12PLHi0z4PhcTaZNTz8e7K3fjqQ==} engines: {node: '>=8.0.0'} peerDependencies: @@ -4739,13 +4715,12 @@ packages: node-fetch: optional: true dependencies: - fetch-mock: 9.11.0_node-fetch@2.6.7 - node-fetch: 2.6.7 + fetch-mock: 9.11.0 transitivePeerDependencies: - supports-color dev: true - /fetch-mock/9.11.0_node-fetch@2.6.7: + /fetch-mock/9.11.0: resolution: {integrity: sha512-PG1XUv+x7iag5p/iNHD4/jdpxL9FtVSqRMUQhPab4hVDt80T1MH5ehzVrL2IdXO9Q2iBggArFvPqjUbHFuI58Q==} engines: {node: '>=4.0.0'} peerDependencies: @@ -4761,7 +4736,6 @@ packages: glob-to-regexp: 0.4.1 is-subset: 0.1.1 lodash.isequal: 4.5.0 - node-fetch: 2.6.7 path-to-regexp: 2.4.0 querystring: 0.2.1 whatwg-url: 6.5.0 @@ -6063,10 +6037,6 @@ packages: /lodash.isequal/4.5.0: resolution: {integrity: sha512-pDo3lu8Jhfjqls6GkMgpahsF9kCyayhgykjyLMNFTKWrpVdAQtYyB4muAMWozBB4ig/dtWAmsMxLEI8wuz+DYQ==} - /lodash.isplainobject/4.0.6: - resolution: {integrity: sha512-oSXzaWypCMHkPC3NvBEaPHf0KsA5mvPrOPgQWDsbg8n7orZ290M0BmC/jgRZ4vcJ6DTAhjrsSYgdsW/F+MFOBA==} - dev: true - /lodash.memoize/4.1.2: resolution: {integrity: sha512-t7j+NzmgnQzTAYXcsHYLgimltOV1MXHtlOWf6GjL9Kj8GK5FInw5JotxvbOs+IvV1/Dzo04/fCGfLVs7aXb4Ag==} dev: true @@ -6246,6 +6216,7 @@ packages: optional: true dependencies: whatwg-url: 5.0.0 + dev: true /node-int64/0.4.0: resolution: {integrity: sha512-O5lz91xSOeoXP6DulyHfllpq+Eg00MWitZIbtPfoSEvqIHdl5gfcY6hYzDWnj0qD5tz52PI08u9qUvSVeUBeHw==} @@ -6750,26 +6721,38 @@ packages: react-dom: 18.1.0_react@18.1.0 dev: false - /react-redux/7.2.6_ef5jwxihqo6n7gxfmzogljlgcm: - resolution: {integrity: sha512-10RPdsz0UUrRL1NZE0ejTkucnclYSgXp5q+tB5SWx2qeG2ZJQJyymgAhwKy73yiL/13btfB6fPr+rgbMAaZIAQ==} + /react-redux/8.0.2_nfqigfgwurfoimtkde74cji6ga: + resolution: {integrity: sha512-nBwiscMw3NoP59NFCXFf02f8xdo+vSHT/uZ1ldDwF7XaTpzm+Phk97VT4urYBl5TYAPNVaFm12UHAEyzkpNzRA==} peerDependencies: - react: ^16.8.3 || ^17 - react-dom: '*' - react-native: '*' + '@types/react': ^16.8 || ^17.0 || ^18.0 + '@types/react-dom': ^16.8 || ^17.0 || ^18.0 + react: ^16.8 || ^17.0 || ^18.0 + react-dom: ^16.8 || ^17.0 || ^18.0 + react-native: '>=0.59' + redux: ^4 peerDependenciesMeta: + '@types/react': + optional: true + '@types/react-dom': + optional: true react-dom: optional: true react-native: optional: true + redux: + optional: true dependencies: '@babel/runtime': 7.17.9 - '@types/react-redux': 7.1.24 + '@types/hoist-non-react-statics': 3.3.1 + '@types/react': 18.0.9 + '@types/react-dom': 18.0.5 + '@types/use-sync-external-store': 0.0.3 hoist-non-react-statics: 3.3.2 - loose-envify: 1.4.0 - prop-types: 15.8.1 react: 18.1.0 react-dom: 18.1.0_react@18.1.0 - react-is: 17.0.2 + react-is: 18.2.0 + redux: 4.2.0 + use-sync-external-store: 1.2.0_react@18.1.0 dev: false /react-refresh/0.14.0: @@ -6837,24 +6820,19 @@ packages: strip-indent: 3.0.0 dev: true - /redux-mock-store/1.5.4: - resolution: {integrity: sha512-xmcA0O/tjCLXhh9Fuiq6pMrJCwFRaouA8436zcikdIpYWWCjU76CRk+i2bHx8EeiSiMGnB85/lZdU3wIJVXHTA==} - dependencies: - lodash.isplainobject: 4.0.6 - dev: true - - /redux-thunk/2.4.1_redux@4.1.2: + /redux-thunk/2.4.1_redux@4.2.0: resolution: {integrity: sha512-OOYGNY5Jy2TWvTL1KgAlVy6dcx3siPJ1wTq741EPyUKfn6W6nChdICjZwCd0p8AZBs5kWpZlbkXW2nE/zjUa+Q==} peerDependencies: redux: ^4 dependencies: - redux: 4.1.2 + redux: 4.2.0 dev: false - /redux/4.1.2: - resolution: {integrity: sha512-SH8PglcebESbd/shgf6mii6EIoRM0zrQyjcuQ+ojmfxjTtE0z9Y8pa62iA/OJ58qjP6j27uyW4kUF4jl/jd6sw==} + /redux/4.2.0: + resolution: {integrity: sha512-oSBmcKKIuIR4ME29/AeNUnl5L+hvBq7OaJWzaptTQJAntaPvxIJqfnjbaEiCzzaIz+XmVILfqAM3Ob0aXLPfjA==} dependencies: '@babel/runtime': 7.17.9 + dev: false /reflect-metadata/0.1.13: resolution: {integrity: sha512-Ts1Y/anZELhSsjMcU605fU9RE4Oi3p5ORujwbIKXfWa+0Zxs510Qrmrce5/Jowq3cHSZSJqBjypxmHarc+vEWg==} @@ -7439,6 +7417,7 @@ packages: /tr46/0.0.3: resolution: {integrity: sha512-N3WMsuqV66lT30CrXNbEjx4GEwlow3v6rr4mCcv6prnfwhS01rkgyFdjPNBYd9br7LpXV1+Emh01fHnq2Gdgrw==} + dev: true /tr46/1.0.1: resolution: {integrity: sha512-dTpowEjclQ7Kgx5SdBkqRzVhERQXov8/l9Ft9dVM9fmg0W0KQSVaXX9T4i6twCPNtYiZM53lpSSUAwJbFPOHxA==} @@ -7685,6 +7664,14 @@ packages: react: 18.1.0 dev: false + /use-sync-external-store/1.2.0_react@18.1.0: + resolution: {integrity: sha512-eEgnFxGQ1Ife9bzYs6VLi8/4X6CObHMw9Qr9tPY43iKwsPw8xE8+EFsf/2cFZ5S3esXgpWgtSCtLNS41F+sKPA==} + peerDependencies: + react: ^16.8.0 || ^17.0.0 || ^18.0.0 + dependencies: + react: 18.1.0 + dev: false + /util-deprecate/1.0.2: resolution: {integrity: sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==} dev: true @@ -7782,6 +7769,7 @@ packages: /webidl-conversions/3.0.1: resolution: {integrity: sha512-2JAn3z8AR6rjK8Sm8orRC0h/bcl/DqL7tRPdGZ4I1CjdF+EaMLmYxBHyXuKL849eucPFhvBoxMsflfOb8kxaeQ==} + dev: true /webidl-conversions/4.0.2: resolution: {integrity: sha512-YQ+BmxuTgd6UXZW3+ICGfyqRyHXVlD5GtQr5+qjiNW7bF0cqrzX500HVXPBOvgXb5YnzDd+h0zqyv61KUD7+Sg==} @@ -7828,6 +7816,7 @@ packages: dependencies: tr46: 0.0.3 webidl-conversions: 3.0.1 + dev: true /whatwg-url/6.5.0: resolution: {integrity: sha512-rhRZRqx/TLJQWUpQ6bmrt2UV4f0HCQ463yQuONJqC6fO2VoEb1pTYddbe59SkYq87aoM5A3bdhMZiUiVws+fzQ==} diff --git a/kafka-ui-react-app/src/components/KsqlDb/List/List.tsx b/kafka-ui-react-app/src/components/KsqlDb/List/List.tsx index 7c9ab90a96..f911d2807f 100644 --- a/kafka-ui-react-app/src/components/KsqlDb/List/List.tsx +++ b/kafka-ui-react-app/src/components/KsqlDb/List/List.tsx @@ -1,7 +1,6 @@ import React, { FC } from 'react'; import useAppParams from 'lib/hooks/useAppParams'; import * as Metrics from 'components/common/Metrics'; -import { useSelector, useDispatch } from 'react-redux'; import { getKsqlDbTables } from 'redux/reducers/ksqlDb/selectors'; import { clusterKsqlDbQueryRelativePath, @@ -16,15 +15,16 @@ import { Button } from 'components/common/Button/Button'; import Navbar from 'components/common/Navigation/Navbar.styled'; import { NavLink, Route, Routes, Navigate } from 'react-router-dom'; import { fetchKsqlDbTables } from 'redux/reducers/ksqlDb/ksqlDbSlice'; +import { useAppDispatch, useAppSelector } from 'lib/hooks/redux'; import KsqlDbItem, { KsqlDbItemType } from './KsqlDbItem/KsqlDbItem'; const List: FC = () => { const { clusterName } = useAppParams(); - const dispatch = useDispatch(); + const dispatch = useAppDispatch(); const { rows, fetching, tablesCount, streamsCount } = - useSelector(getKsqlDbTables); + useAppSelector(getKsqlDbTables); React.useEffect(() => { dispatch(fetchKsqlDbTables(clusterName)); diff --git a/kafka-ui-react-app/src/components/KsqlDb/Query/Query.tsx b/kafka-ui-react-app/src/components/KsqlDb/Query/Query.tsx index c5757e2cae..6dfee76000 100644 --- a/kafka-ui-react-app/src/components/KsqlDb/Query/Query.tsx +++ b/kafka-ui-react-app/src/components/KsqlDb/Query/Query.tsx @@ -5,13 +5,13 @@ import { executeKsql, resetExecutionResult, } from 'redux/reducers/ksqlDb/ksqlDbSlice'; -import { useDispatch, useSelector } from 'react-redux'; import { getKsqlExecution } from 'redux/reducers/ksqlDb/selectors'; import { BASE_PARAMS } from 'lib/constants'; import { KsqlResponse, KsqlTableResponse } from 'generated-sources'; import { alertAdded, alertDissmissed } from 'redux/reducers/alerts/alertsSlice'; import now from 'lodash/now'; import { ClusterNameRoute } from 'lib/paths'; +import { useAppDispatch, useAppSelector } from 'lib/hooks/redux'; import type { FormValues } from './QueryForm/QueryForm'; import * as S from './Query.styled'; @@ -69,9 +69,9 @@ const Query: FC = () => { isOpen: false, }); const [fetching, setFetching] = useState(false); - const dispatch = useDispatch(); + const dispatch = useAppDispatch(); - const { executionResult } = useSelector(getKsqlExecution); + const { executionResult } = useAppSelector(getKsqlExecution); const [KSQLTable, setKSQLTable] = useState(null); const reset = useCallback(() => { diff --git a/kafka-ui-react-app/src/components/Topics/New/__test__/New.spec.tsx b/kafka-ui-react-app/src/components/Topics/New/__test__/New.spec.tsx index e03a2cfa0b..0f76b0e978 100644 --- a/kafka-ui-react-app/src/components/Topics/New/__test__/New.spec.tsx +++ b/kafka-ui-react-app/src/components/Topics/New/__test__/New.spec.tsx @@ -1,9 +1,6 @@ import React from 'react'; import New from 'components/Topics/New/New'; import { Route, Routes } from 'react-router-dom'; -import configureStore from 'redux-mock-store'; -import { RootState } from 'redux/interfaces'; -import * as redux from 'react-redux'; import { act, screen, waitFor } from '@testing-library/react'; import { clusterTopicCopyPath, @@ -12,44 +9,26 @@ import { } from 'lib/paths'; import userEvent from '@testing-library/user-event'; import { render } from 'lib/testHelpers'; - -const { Provider } = redux; - -const mockStore = configureStore(); +import { useAppDispatch } from 'lib/hooks/redux'; const clusterName = 'local'; const topicName = 'test-topic'; -const initialState: Partial = {}; -const storeMock = mockStore(initialState); - const mockNavigate = jest.fn(); jest.mock('react-router-dom', () => ({ ...jest.requireActual('react-router-dom'), useNavigate: () => mockNavigate, })); +jest.mock('lib/hooks/redux', () => ({ + ...jest.requireActual('lib/hooks/redux'), + useAppDispatch: jest.fn(), +})); -const renderComponent = (path: string, store = storeMock) => { +const renderComponent = (path: string) => { render( - - - - } - /> - - - - - } - /> - + } /> + } /> , { initialEntries: [path] } @@ -93,34 +72,29 @@ describe('New', () => { }); it('submits valid form', async () => { - const useDispatchSpy = jest.spyOn(redux, 'useDispatch'); const useDispatchMock = jest.fn(() => ({ meta: { requestStatus: 'fulfilled' }, - })) as jest.Mock; - useDispatchSpy.mockReturnValue(useDispatchMock); + })); + (useAppDispatch as jest.Mock).mockImplementation(() => useDispatchMock); await act(() => renderComponent(clusterTopicNewPath(clusterName))); - await act(() => { userEvent.type(screen.getByPlaceholderText('Topic Name'), topicName); }); - await act(() => { userEvent.click(screen.getByText('Create topic')); }); - - await waitFor(() => expect(mockNavigate).toBeCalledTimes(1)); - expect(mockNavigate).toHaveBeenLastCalledWith(`../${topicName}`); - expect(useDispatchMock).toHaveBeenCalledTimes(1); + await waitFor(() => expect(useDispatchMock).toHaveBeenCalledTimes(1)); + await waitFor(() => + expect(mockNavigate).toHaveBeenLastCalledWith(`../${topicName}`) + ); }); it('does not redirect page when request is not fulfilled', async () => { - const useDispatchSpy = jest.spyOn(redux, 'useDispatch'); const useDispatchMock = jest.fn(() => ({ meta: { requestStatus: 'pending' }, - })) as jest.Mock; - - useDispatchSpy.mockReturnValue(useDispatchMock); + })); + (useAppDispatch as jest.Mock).mockImplementation(() => useDispatchMock); await act(() => renderComponent(clusterTopicNewPath(clusterName))); await act(() => userEvent.type(screen.getByPlaceholderText('Topic Name'), topicName) @@ -130,9 +104,8 @@ describe('New', () => { }); it('submits valid form that result in an error', async () => { - const useDispatchSpy = jest.spyOn(redux, 'useDispatch'); const useDispatchMock = jest.fn(); - useDispatchSpy.mockReturnValue(useDispatchMock); + (useAppDispatch as jest.Mock).mockImplementation(() => useDispatchMock); await act(() => renderComponent(clusterTopicNewPath(clusterName))); await act(() => { diff --git a/kafka-ui-react-app/src/components/Topics/New/__test__/fixtures.ts b/kafka-ui-react-app/src/components/Topics/New/__test__/fixtures.ts deleted file mode 100644 index a78c9ccb8e..0000000000 --- a/kafka-ui-react-app/src/components/Topics/New/__test__/fixtures.ts +++ /dev/null @@ -1,36 +0,0 @@ -import { CleanUpPolicy, Topic } from 'generated-sources'; - -export const createTopicPayload: Record = { - name: 'test-topic', - partitions: 1, - replicationFactor: 1, - configs: { - 'cleanup.policy': 'delete', - 'retention.ms': '604800000', - 'retention.bytes': '-1', - 'max.message.bytes': '1000012', - 'min.insync.replicas': '1', - }, -}; - -export const createTopicResponsePayload: Topic = { - name: 'local', - internal: false, - partitionCount: 1, - replicationFactor: 1, - replicas: 1, - inSyncReplicas: 1, - segmentSize: 0, - segmentCount: 0, - underReplicatedPartitions: 0, - cleanUpPolicy: CleanUpPolicy.DELETE, - partitions: [ - { - partition: 0, - leader: 1, - replicas: [{ broker: 1, leader: false, inSync: true }], - offsetMax: 0, - offsetMin: 0, - }, - ], -}; diff --git a/kafka-ui-react-app/src/components/Topics/Topic/Details/Details.tsx b/kafka-ui-react-app/src/components/Topics/Topic/Details/Details.tsx index 177d258d92..d7712df78b 100644 --- a/kafka-ui-react-app/src/components/Topics/Topic/Details/Details.tsx +++ b/kafka-ui-react-app/src/components/Topics/Topic/Details/Details.tsx @@ -11,7 +11,6 @@ import { } from 'lib/paths'; import ClusterContext from 'components/contexts/ClusterContext'; import ConfirmationModal from 'components/common/ConfirmationModal/ConfirmationModal'; -import { useDispatch } from 'react-redux'; import PageHeading from 'components/common/PageHeading/PageHeading'; import { Button } from 'components/common/Button/Button'; import Dropdown from 'components/common/Dropdown/Dropdown'; @@ -20,7 +19,7 @@ import DropdownItem from 'components/common/Dropdown/DropdownItem'; import styled from 'styled-components'; import Navbar from 'components/common/Navigation/Navbar.styled'; import * as S from 'components/Topics/Topic/Details/Details.styled'; -import { useAppSelector } from 'lib/hooks/redux'; +import { useAppDispatch, useAppSelector } from 'lib/hooks/redux'; import { getIsTopicDeletePolicy, getIsTopicInternal, @@ -72,7 +71,7 @@ const Details: React.FC = ({ ); const navigate = useNavigate(); - const dispatch = useDispatch(); + const dispatch = useAppDispatch(); const { isReadOnly, isTopicDeletionAllowed } = React.useContext(ClusterContext); const [isDeleteTopicConfirmationVisible, setDeleteTopicConfirmationVisible] = diff --git a/kafka-ui-react-app/src/components/Topics/Topic/Details/Messages/MessagesTable.tsx b/kafka-ui-react-app/src/components/Topics/Topic/Details/Messages/MessagesTable.tsx index 83192b3f90..4696b75e03 100644 --- a/kafka-ui-react-app/src/components/Topics/Topic/Details/Messages/MessagesTable.tsx +++ b/kafka-ui-react-app/src/components/Topics/Topic/Details/Messages/MessagesTable.tsx @@ -3,20 +3,20 @@ import { Table } from 'components/common/table/Table/Table.styled'; import TableHeaderCell from 'components/common/table/TableHeaderCell/TableHeaderCell'; import { TopicMessage } from 'generated-sources'; import React, { useContext } from 'react'; -import { useSelector } from 'react-redux'; import { getTopicMessges, getIsTopicMessagesFetching, } from 'redux/reducers/topicMessages/selectors'; import TopicMessagesContext from 'components/contexts/TopicMessagesContext'; +import { useAppSelector } from 'lib/hooks/redux'; import Message from './Message'; const MessagesTable: React.FC = () => { const { isLive } = useContext(TopicMessagesContext); - const messages = useSelector(getTopicMessges); - const isFetching = useSelector(getIsTopicMessagesFetching); + const messages = useAppSelector(getTopicMessges); + const isFetching = useAppSelector(getIsTopicMessagesFetching); return ( diff --git a/kafka-ui-react-app/src/lib/fixtures/kafkaConnect.ts b/kafka-ui-react-app/src/lib/fixtures/kafkaConnect.ts index 91563a7dce..f885ea405d 100644 --- a/kafka-ui-react-app/src/lib/fixtures/kafkaConnect.ts +++ b/kafka-ui-react-app/src/lib/fixtures/kafkaConnect.ts @@ -104,7 +104,7 @@ export const tasks: Task[] = [ { id: { connector: 'first', task: 4 }, status: { - id: 3, + id: 4, state: ConnectorTaskStatus.PAUSED, workerId: 'kafka-connect0:8083', }, diff --git a/kafka-ui-react-app/src/lib/testHelpers.tsx b/kafka-ui-react-app/src/lib/testHelpers.tsx index e9d546bf37..14db7e8739 100644 --- a/kafka-ui-react-app/src/lib/testHelpers.tsx +++ b/kafka-ui-react-app/src/lib/testHelpers.tsx @@ -13,7 +13,6 @@ import { AnyAction, Store } from 'redux'; import { RootState } from 'redux/interfaces'; import { configureStore } from '@reduxjs/toolkit'; import rootReducer from 'redux/reducers'; -import mockStoreCreator from 'redux/store/configureStore/mockStoreCreator'; import { QueryClient, QueryClientProvider, UseQueryResult } from 'react-query'; interface CustomRenderOptions extends Omit { @@ -100,7 +99,3 @@ export class EventSourceMock { this.close = jest.fn(); } } - -export const getTypeAndPayload = (store: typeof mockStoreCreator) => { - return store.getActions().map(({ type, payload }) => ({ type, payload })); -}; diff --git a/kafka-ui-react-app/src/redux/reducers/topics/__test__/reducer.spec.ts b/kafka-ui-react-app/src/redux/reducers/topics/__test__/reducer.spec.ts deleted file mode 100644 index b1bcae7ebc..0000000000 --- a/kafka-ui-react-app/src/redux/reducers/topics/__test__/reducer.spec.ts +++ /dev/null @@ -1,945 +0,0 @@ -import { - MessageSchemaSourceEnum, - SortOrder, - TopicColumnsToSort, - ConfigSource, -} from 'generated-sources'; -import reducer, { - clearTopicsMessages, - setTopicsSearch, - setTopicsOrderBy, - fetchTopicConsumerGroups, - fetchTopicMessageSchema, - recreateTopic, - createTopic, - deleteTopic, - fetchTopicsList, - fetchTopicDetails, - fetchTopicConfig, - updateTopic, - updateTopicPartitionsCount, - updateTopicReplicationFactor, - deleteTopics, -} from 'redux/reducers/topics/topicsSlice'; -import { - createTopicPayload, - createTopicResponsePayload, -} from 'components/Topics/New/__test__/fixtures'; -import { consumerGroupPayload } from 'redux/reducers/consumerGroups/__test__/fixtures'; -import fetchMock from 'fetch-mock-jest'; -import mockStoreCreator from 'redux/store/configureStore/mockStoreCreator'; -import { getTypeAndPayload } from 'lib/testHelpers'; -import { - alertAdded, - showSuccessAlert, -} from 'redux/reducers/alerts/alertsSlice'; - -const topic = { - name: 'topic', -}; - -const messageSchema = { - key: { - name: 'key', - source: MessageSchemaSourceEnum.SCHEMA_REGISTRY, - schema: `{ -"$schema": "http://json-schema.org/draft-07/schema#", -"$id": "http://example.com/myURI.schema.json", -"title": "TestRecord", -"type": "object", -"additionalProperties": false, -"properties": { - "f1": { - "type": "integer" - }, - "f2": { - "type": "string" - }, - "schema": { - "type": "string" - } -} -} -`, - }, - value: { - name: 'value', - source: MessageSchemaSourceEnum.SCHEMA_REGISTRY, - schema: `{ -"$schema": "http://json-schema.org/draft-07/schema#", -"$id": "http://example.com/myURI1.schema.json", -"title": "TestRecord", -"type": "object", -"additionalProperties": false, -"properties": { - "f1": { - "type": "integer" - }, - "f2": { - "type": "string" - }, - "schema": { - "type": "string" - } -} -} -`, - }, -}; - -const config = [ - { - name: 'compression.type', - value: 'producer', - defaultValue: 'producer', - source: ConfigSource.DYNAMIC_TOPIC_CONFIG, - isSensitive: false, - isReadOnly: false, - synonyms: [ - { - name: 'compression.type', - value: 'producer', - source: ConfigSource.DYNAMIC_TOPIC_CONFIG, - }, - { - name: 'compression.type', - value: 'producer', - source: ConfigSource.DEFAULT_CONFIG, - }, - ], - }, -]; -const details = { - name: 'local', - internal: false, - partitionCount: 1, - replicationFactor: 1, - replicas: 1, - inSyncReplicas: 1, - segmentSize: 0, - segmentCount: 0, - cleanUpPolicy: 'DELETE', - partitions: [ - { - partition: 0, - leader: 1, - replicas: [{ broker: 1, leader: false, inSync: true }], - offsetMax: 0, - offsetMin: 0, - }, - ], - bytesInPerSec: 0.1, - bytesOutPerSec: 0.1, -}; - -let state = { - byName: { - [topic.name]: topic, - }, - allNames: [topic.name], - messages: [], - totalPages: 1, - search: '', - orderBy: null, - sortOrder: SortOrder.ASC, - consumerGroups: [], -}; -const clusterName = 'local'; - -describe('topics Slice', () => { - describe('topics reducer', () => { - describe('fetch topic details', () => { - it('fetchTopicDetails/fulfilled', () => { - expect( - reducer(state, { - type: fetchTopicDetails.fulfilled, - payload: { - clusterName, - topicName: topic.name, - topicDetails: details, - }, - }) - ).toEqual({ - ...state, - byName: { - [topic.name]: { - ...topic, - ...details, - }, - }, - allNames: [topic.name], - }); - }); - }); - describe('fetch topics', () => { - it('fetchTopicsList/fulfilled', () => { - expect( - reducer(state, { - type: fetchTopicsList.fulfilled, - payload: { clusterName, topicName: topic.name }, - }) - ).toEqual({ - ...state, - byName: { topic }, - allNames: [topic.name], - }); - }); - }); - describe('fetch topic config', () => { - it('fetchTopicConfig/fulfilled', () => { - expect( - reducer(state, { - type: fetchTopicConfig.fulfilled, - payload: { - clusterName, - topicName: topic.name, - topicConfig: config, - }, - }) - ).toEqual({ - ...state, - byName: { - [topic.name]: { - ...topic, - config: config.map((conf) => ({ ...conf })), - }, - }, - allNames: [topic.name], - }); - }); - }); - describe('update topic', () => { - it('updateTopic/fulfilled', () => { - const updatedTopic = { - name: 'topic', - partitions: 1, - }; - expect( - reducer(state, { - type: updateTopic.fulfilled, - payload: { - clusterName, - topicName: topic.name, - topic: updatedTopic, - }, - }) - ).toEqual({ - ...state, - byName: { - [topic.name]: { - ...updatedTopic, - }, - }, - }); - }); - }); - describe('delete topic', () => { - it('deleteTopic/fulfilled', () => { - expect( - reducer(state, { - type: deleteTopic.fulfilled, - payload: { clusterName, topicName: topic.name }, - }) - ).toEqual({ - ...state, - byName: {}, - allNames: [], - }); - }); - - it('clearTopicsMessages/fulfilled', () => { - expect( - reducer(state, { - type: clearTopicsMessages.fulfilled, - payload: { clusterName, topicName: topic.name }, - }) - ).toEqual({ - ...state, - messages: [], - }); - }); - - it('recreateTopic/fulfilled', () => { - expect( - reducer(state, { - type: recreateTopic.fulfilled, - payload: { topic, topicName: topic.name }, - }) - ).toEqual({ - ...state, - byName: { - [topic.name]: topic, - }, - }); - }); - }); - - describe('create topics', () => { - it('createTopic/fulfilled', () => { - expect( - reducer(state, { - type: createTopic.fulfilled, - payload: { clusterName, data: createTopicPayload }, - }) - ).toEqual({ - ...state, - }); - }); - }); - - describe('search topics', () => { - it('setTopicsSearch', () => { - expect( - reducer(state, { - type: setTopicsSearch, - payload: 'test', - }) - ).toEqual({ - ...state, - search: 'test', - }); - }); - }); - - describe('order topics', () => { - it('setTopicsOrderBy', () => { - expect( - reducer(state, { - type: setTopicsOrderBy, - payload: TopicColumnsToSort.NAME, - }) - ).toEqual({ - ...state, - orderBy: TopicColumnsToSort.NAME, - }); - }); - }); - - describe('topic consumer groups', () => { - it('fetchTopicConsumerGroups/fulfilled', () => { - expect( - reducer(state, { - type: fetchTopicConsumerGroups.fulfilled, - payload: { - clusterName, - topicName: topic.name, - consumerGroups: consumerGroupPayload, - }, - }) - ).toEqual({ - ...state, - byName: { - [topic.name]: { - ...topic, - ...consumerGroupPayload, - }, - }, - }); - }); - }); - - describe('message sending', () => { - it('fetchTopicMessageSchema/fulfilled', () => { - state = { - byName: { - [topic.name]: topic, - }, - allNames: [topic.name], - messages: [], - totalPages: 1, - search: '', - orderBy: null, - sortOrder: SortOrder.ASC, - consumerGroups: [], - }; - expect( - reducer(state, { - type: fetchTopicMessageSchema.fulfilled, - payload: { topicName: topic.name, schema: messageSchema }, - }).byName - ).toEqual({ - [topic.name]: { ...topic, messageSchema }, - }); - }); - }); - }); - describe('Thunks', () => { - const store = mockStoreCreator; - const topicName = topic.name; - const RealDate = Date.now; - - beforeAll(() => { - global.Date.now = jest.fn(() => - new Date('2019-04-07T10:20:30Z').getTime() - ); - }); - afterAll(() => { - global.Date.now = RealDate; - }); - afterEach(() => { - fetchMock.restore(); - store.clearActions(); - }); - describe('fetchTopicsList', () => { - const topicResponse = { - pageCount: 1, - topics: [createTopicResponsePayload], - }; - it('fetchTopicsList/fulfilled', async () => { - fetchMock.getOnce(`/api/clusters/${clusterName}/topics`, topicResponse); - await store.dispatch(fetchTopicsList({ clusterName })); - - expect(getTypeAndPayload(store)).toEqual([ - { type: fetchTopicsList.pending.type }, - { - type: fetchTopicsList.fulfilled.type, - payload: { ...topicResponse }, - }, - ]); - }); - it('fetchTopicsList/rejected', async () => { - fetchMock.getOnce(`/api/clusters/${clusterName}/topics`, 404); - await store.dispatch(fetchTopicsList({ clusterName })); - - expect(getTypeAndPayload(store)).toEqual([ - { type: fetchTopicsList.pending.type }, - { - type: fetchTopicsList.rejected.type, - payload: { - status: 404, - statusText: 'Not Found', - url: `/api/clusters/${clusterName}/topics`, - message: undefined, - }, - }, - ]); - }); - }); - describe('fetchTopicDetails', () => { - it('fetchTopicDetails/fulfilled', async () => { - fetchMock.getOnce( - `/api/clusters/${clusterName}/topics/${topicName}`, - details - ); - await store.dispatch(fetchTopicDetails({ clusterName, topicName })); - - expect(getTypeAndPayload(store)).toEqual([ - { type: fetchTopicDetails.pending.type }, - { - type: fetchTopicDetails.fulfilled.type, - payload: { topicDetails: { ...details }, topicName }, - }, - ]); - }); - it('fetchTopicDetails/rejected', async () => { - fetchMock.getOnce( - `/api/clusters/${clusterName}/topics/${topicName}`, - 404 - ); - await store.dispatch(fetchTopicDetails({ clusterName, topicName })); - - expect(getTypeAndPayload(store)).toEqual([ - { type: fetchTopicDetails.pending.type }, - { - type: fetchTopicDetails.rejected.type, - payload: { - status: 404, - statusText: 'Not Found', - url: `/api/clusters/${clusterName}/topics/${topicName}`, - message: undefined, - }, - }, - ]); - }); - }); - describe('fetchTopicConfig', () => { - it('fetchTopicConfig/fulfilled', async () => { - fetchMock.getOnce( - `/api/clusters/${clusterName}/topics/${topicName}/config`, - config - ); - await store.dispatch(fetchTopicConfig({ clusterName, topicName })); - - expect(getTypeAndPayload(store)).toEqual([ - { type: fetchTopicConfig.pending.type }, - { - type: fetchTopicConfig.fulfilled.type, - payload: { - topicConfig: config, - topicName, - }, - }, - ]); - }); - it('fetchTopicConfig/rejected', async () => { - fetchMock.getOnce( - `/api/clusters/${clusterName}/topics/${topicName}/config`, - 404 - ); - await store.dispatch(fetchTopicConfig({ clusterName, topicName })); - - expect(getTypeAndPayload(store)).toEqual([ - { type: fetchTopicConfig.pending.type }, - { - type: fetchTopicConfig.rejected.type, - payload: { - status: 404, - statusText: 'Not Found', - url: `/api/clusters/${clusterName}/topics/${topicName}/config`, - message: undefined, - }, - }, - ]); - }); - }); - describe('deleteTopic', () => { - it('deleteTopic/fulfilled', async () => { - fetchMock.deleteOnce( - `/api/clusters/${clusterName}/topics/${topicName}`, - topicName - ); - await store.dispatch(deleteTopic({ clusterName, topicName })); - - expect(getTypeAndPayload(store)).toEqual([ - { type: deleteTopic.pending.type }, - { type: showSuccessAlert.pending.type }, - { - type: alertAdded.type, - payload: { - id: 'message-topic-local', - title: '', - type: 'success', - createdAt: global.Date.now(), - message: 'Topic successfully deleted!', - }, - }, - { type: showSuccessAlert.fulfilled.type }, - { - type: deleteTopic.fulfilled.type, - payload: { topicName }, - }, - ]); - }); - it('deleteTopic/rejected', async () => { - fetchMock.deleteOnce( - `/api/clusters/${clusterName}/topics/${topicName}`, - 404 - ); - await store.dispatch(deleteTopic({ clusterName, topicName })); - - expect(getTypeAndPayload(store)).toEqual([ - { type: deleteTopic.pending.type }, - { - type: deleteTopic.rejected.type, - payload: { - status: 404, - statusText: 'Not Found', - url: `/api/clusters/${clusterName}/topics/${topicName}`, - message: undefined, - }, - }, - ]); - }); - }); - describe('deleteTopics', () => { - it('deleteTopics/fulfilled', async () => { - fetchMock.delete(`/api/clusters/${clusterName}/topics/${topicName}`, [ - topicName, - 'topic2', - ]); - await store.dispatch( - deleteTopics({ clusterName, topicNames: [topicName, 'topic2'] }) - ); - - expect(getTypeAndPayload(store)).toEqual([ - { type: deleteTopics.pending.type }, - { type: deleteTopic.pending.type }, - { type: deleteTopic.pending.type }, - { type: fetchTopicsList.pending.type }, - { type: deleteTopics.fulfilled.type }, - ]); - }); - }); - describe('recreateTopic', () => { - const recreateResponse = { - cleanUpPolicy: 'DELETE', - inSyncReplicas: 1, - internal: false, - name: topicName, - partitionCount: 1, - partitions: undefined, - replicas: 1, - replicationFactor: 1, - segmentCount: 0, - segmentSize: 0, - underReplicatedPartitions: undefined, - }; - it('recreateTopic/fulfilled', async () => { - fetchMock.postOnce( - `/api/clusters/${clusterName}/topics/${topicName}`, - recreateResponse - ); - await store.dispatch(recreateTopic({ clusterName, topicName })); - - expect(getTypeAndPayload(store)).toEqual([ - { type: recreateTopic.pending.type }, - { type: showSuccessAlert.pending.type }, - { - type: alertAdded.type, - payload: { - id: 'message-topic-local', - title: '', - type: 'success', - createdAt: global.Date.now(), - message: 'Topic successfully recreated!', - }, - }, - { type: showSuccessAlert.fulfilled.type }, - { - type: recreateTopic.fulfilled.type, - payload: { [topicName]: { ...recreateResponse } }, - }, - ]); - }); - it('recreateTopic/rejected', async () => { - fetchMock.postOnce( - `/api/clusters/${clusterName}/topics/${topicName}`, - 404 - ); - await store.dispatch(recreateTopic({ clusterName, topicName })); - - expect(getTypeAndPayload(store)).toEqual([ - { type: recreateTopic.pending.type }, - { - type: recreateTopic.rejected.type, - payload: { - status: 404, - statusText: 'Not Found', - url: `/api/clusters/${clusterName}/topics/${topicName}`, - message: undefined, - }, - }, - ]); - }); - }); - describe('fetchTopicConsumerGroups', () => { - const consumerGroups = [ - { - groupId: 'groupId1', - members: 0, - topics: 1, - simple: false, - partitionAssignor: '', - coordinator: { - id: 1, - port: undefined, - host: 'host', - }, - messagesBehind: undefined, - state: undefined, - }, - { - groupId: 'groupId2', - members: 0, - topics: 1, - simple: false, - partitionAssignor: '', - coordinator: { - id: 1, - port: undefined, - host: 'host', - }, - messagesBehind: undefined, - state: undefined, - }, - ]; - it('fetchTopicConsumerGroups/fulfilled', async () => { - fetchMock.getOnce( - `/api/clusters/${clusterName}/topics/${topicName}/consumer-groups`, - consumerGroups - ); - await store.dispatch( - fetchTopicConsumerGroups({ clusterName, topicName }) - ); - - expect(getTypeAndPayload(store)).toEqual([ - { type: fetchTopicConsumerGroups.pending.type }, - { - type: fetchTopicConsumerGroups.fulfilled.type, - payload: { consumerGroups, topicName }, - }, - ]); - }); - it('fetchTopicConsumerGroups/rejected', async () => { - fetchMock.getOnce( - `/api/clusters/${clusterName}/topics/${topicName}/consumer-groups`, - 404 - ); - await store.dispatch( - fetchTopicConsumerGroups({ clusterName, topicName }) - ); - - expect(getTypeAndPayload(store)).toEqual([ - { type: fetchTopicConsumerGroups.pending.type }, - { - type: fetchTopicConsumerGroups.rejected.type, - payload: { - status: 404, - statusText: 'Not Found', - url: `/api/clusters/${clusterName}/topics/${topicName}/consumer-groups`, - message: undefined, - }, - }, - ]); - }); - }); - describe('updateTopicPartitionsCount', () => { - it('updateTopicPartitionsCount/fulfilled', async () => { - fetchMock.patchOnce( - `/api/clusters/${clusterName}/topics/${topicName}/partitions`, - { message: 'success' } - ); - await store.dispatch( - updateTopicPartitionsCount({ - clusterName, - topicName, - partitions: 1, - }) - ); - expect(getTypeAndPayload(store)).toEqual([ - { type: updateTopicPartitionsCount.pending.type }, - { type: showSuccessAlert.pending.type }, - { - type: alertAdded.type, - payload: { - id: 'message-topic-local-1', - title: '', - type: 'success', - createdAt: global.Date.now(), - message: 'Number of partitions successfully increased!', - }, - }, - { type: fetchTopicDetails.pending.type }, - { type: showSuccessAlert.fulfilled.type }, - { - type: updateTopicPartitionsCount.fulfilled.type, - }, - ]); - }); - it('updateTopicPartitionsCount/rejected', async () => { - fetchMock.patchOnce( - `/api/clusters/${clusterName}/topics/${topicName}/partitions`, - 404 - ); - await store.dispatch( - updateTopicPartitionsCount({ - clusterName, - topicName, - partitions: 1, - }) - ); - - expect(getTypeAndPayload(store)).toEqual([ - { type: updateTopicPartitionsCount.pending.type }, - { - type: updateTopicPartitionsCount.rejected.type, - payload: { - status: 404, - statusText: 'Not Found', - url: `/api/clusters/${clusterName}/topics/${topicName}/partitions`, - message: undefined, - }, - }, - ]); - }); - }); - describe('updateTopicReplicationFactor', () => { - it('updateTopicReplicationFactor/fulfilled', async () => { - fetchMock.patchOnce( - `/api/clusters/${clusterName}/topics/${topicName}/replications`, - { message: 'success' } - ); - await store.dispatch( - updateTopicReplicationFactor({ - clusterName, - topicName, - replicationFactor: 1, - }) - ); - - expect(getTypeAndPayload(store)).toEqual([ - { type: updateTopicReplicationFactor.pending.type }, - { - type: updateTopicReplicationFactor.fulfilled.type, - }, - ]); - }); - it('updateTopicReplicationFactor/rejected', async () => { - fetchMock.patchOnce( - `/api/clusters/${clusterName}/topics/${topicName}/replications`, - 404 - ); - await store.dispatch( - updateTopicReplicationFactor({ - clusterName, - topicName, - replicationFactor: 1, - }) - ); - - expect(getTypeAndPayload(store)).toEqual([ - { type: updateTopicReplicationFactor.pending.type }, - { - type: updateTopicReplicationFactor.rejected.type, - payload: { - status: 404, - statusText: 'Not Found', - url: `/api/clusters/${clusterName}/topics/${topicName}/replications`, - message: undefined, - }, - }, - ]); - }); - }); - describe('createTopic', () => { - const newTopic = { - name: 'newTopic', - partitions: 0, - replicationFactor: 0, - minInSyncReplicas: 0, - cleanupPolicy: 'DELETE', - retentionMs: 1, - retentionBytes: 1, - maxMessageBytes: 1, - customParams: [ - { - name: '', - value: '', - }, - ], - }; - it('createTopic/fulfilled', async () => { - fetchMock.postOnce(`/api/clusters/${clusterName}/topics`, { - message: 'success', - }); - await store.dispatch( - createTopic({ - clusterName, - data: newTopic, - }) - ); - - expect(getTypeAndPayload(store)).toEqual([ - { type: createTopic.pending.type }, - { - type: createTopic.fulfilled.type, - }, - ]); - }); - it('createTopic/rejected', async () => { - fetchMock.postOnce(`/api/clusters/${clusterName}/topics`, 404); - await store.dispatch( - createTopic({ - clusterName, - data: newTopic, - }) - ); - - expect(getTypeAndPayload(store)).toEqual([ - { type: createTopic.pending.type }, - { - type: createTopic.rejected.type, - payload: { - status: 404, - statusText: 'Not Found', - url: `/api/clusters/${clusterName}/topics`, - message: undefined, - }, - }, - ]); - }); - }); - describe('updateTopic', () => { - const updateTopicResponse = { - name: topicName, - partitions: 0, - replicationFactor: 0, - minInSyncReplicas: 0, - cleanupPolicy: 'DELETE', - retentionMs: 0, - retentionBytes: 0, - maxMessageBytes: 0, - customParams: { - byIndex: {}, - allIndexes: [], - }, - }; - it('updateTopic/fulfilled', async () => { - fetchMock.patchOnce( - `/api/clusters/${clusterName}/topics/${topicName}`, - createTopicResponsePayload - ); - await store.dispatch( - updateTopic({ - clusterName, - topicName, - form: updateTopicResponse, - }) - ); - - expect(getTypeAndPayload(store)).toEqual([ - { type: updateTopic.pending.type }, - { - type: updateTopic.fulfilled.type, - payload: { [topicName]: { ...createTopicResponsePayload } }, - }, - ]); - }); - it('updateTopic/rejected', async () => { - fetchMock.patchOnce( - `/api/clusters/${clusterName}/topics/${topicName}`, - 404 - ); - await store.dispatch( - updateTopic({ - clusterName, - topicName, - form: updateTopicResponse, - }) - ); - - expect(getTypeAndPayload(store)).toEqual([ - { type: updateTopic.pending.type }, - { - type: updateTopic.rejected.type, - payload: { - status: 404, - statusText: 'Not Found', - url: `/api/clusters/${clusterName}/topics/${topicName}`, - message: undefined, - }, - }, - ]); - }); - }); - describe('clearTopicsMessages', () => { - it('clearTopicsMessages/fulfilled', async () => { - fetchMock.deleteOnce( - `/api/clusters/${clusterName}/topics/${topicName}/messages`, - [topicName, 'topic2'] - ); - await store.dispatch( - clearTopicsMessages({ - clusterName, - topicNames: [topicName, 'topic2'], - }) - ); - - expect(getTypeAndPayload(store)).toEqual([ - { type: clearTopicsMessages.pending.type }, - { type: clearTopicsMessages.fulfilled.type }, - ]); - }); - }); - }); -}); diff --git a/kafka-ui-react-app/src/redux/store/configureStore/mockStoreCreator.ts b/kafka-ui-react-app/src/redux/store/configureStore/mockStoreCreator.ts deleted file mode 100644 index 67bab43dd6..0000000000 --- a/kafka-ui-react-app/src/redux/store/configureStore/mockStoreCreator.ts +++ /dev/null @@ -1,12 +0,0 @@ -import configureMockStore, { MockStoreCreator } from 'redux-mock-store'; -import thunk, { ThunkDispatch } from 'redux-thunk'; -import { AnyAction, Middleware } from 'redux'; -import { RootState } from 'redux/interfaces'; - -const middlewares: Array = [thunk]; -type DispatchExts = ThunkDispatch; - -const mockStoreCreator: MockStoreCreator = - configureMockStore(middlewares); - -export default mockStoreCreator(); diff --git a/kafka-ui-react-app/vite.config.ts b/kafka-ui-react-app/vite.config.ts index 3a0810782f..6570b334a4 100644 --- a/kafka-ui-react-app/vite.config.ts +++ b/kafka-ui-react-app/vite.config.ts @@ -15,12 +15,11 @@ export default defineConfig(({ mode }) => { rollupOptions: { output: { manualChunks: { - venod: [ + vendor: [ 'react', 'react-router-dom', 'react-dom', 'redux', - 'redux-thunk', 'react-redux', 'styled-components', 'react-ace', @@ -32,7 +31,7 @@ export default defineConfig(({ mode }) => { define: { 'process.env.NODE_ENV': `"${mode}"`, 'process.env.VITE_TAG': `"${process.env.VITE_TAG}"`, - 'process.env.GIT_COMMIT': `"${process.env.VITE_COMMIT}"`, + 'process.env.VITE_COMMIT': `"${process.env.VITE_COMMIT}"`, }, }; const proxy = process.env.VITE_DEV_PROXY;