Sfoglia il codice sorgente

[CHORE] Example of actions & thunks specs

Oleg Shuralev 4 anni fa
parent
commit
fcea2952c5

+ 136 - 10
kafka-ui-react-app/package-lock.json

@@ -2720,8 +2720,28 @@
     "@types/node": {
       "version": "12.19.15",
       "resolved": "https://registry.npmjs.org/@types/node/-/node-12.19.15.tgz",
-      "integrity": "sha512-lowukE3GUI+VSYSu6VcBXl14d61Rp5hA1D+61r16qnwC0lYNSqdxcvRh0pswejorHfS+HgwBasM8jLXz0/aOsw==",
-      "dev": true
+      "integrity": "sha512-lowukE3GUI+VSYSu6VcBXl14d61Rp5hA1D+61r16qnwC0lYNSqdxcvRh0pswejorHfS+HgwBasM8jLXz0/aOsw=="
+    },
+    "@types/node-fetch": {
+      "version": "2.5.8",
+      "resolved": "https://registry.npmjs.org/@types/node-fetch/-/node-fetch-2.5.8.tgz",
+      "integrity": "sha512-fbjI6ja0N5ZA8TV53RUqzsKNkl9fv8Oj3T7zxW7FGv1GSH7gwJaNF8dzCjrqKaxKeUpTz4yT1DaJFq/omNpGfw==",
+      "requires": {
+        "@types/node": "*",
+        "form-data": "^3.0.0"
+      },
+      "dependencies": {
+        "form-data": {
+          "version": "3.0.1",
+          "resolved": "https://registry.npmjs.org/form-data/-/form-data-3.0.1.tgz",
+          "integrity": "sha512-RHkBKtLWUVwd7SqRIvCZMEvAMoGUp0XU+seQiZejj0COz3RI3hWP4sCv3gZWWLjJTd7rGwcsF5eKZGii0r/hbg==",
+          "requires": {
+            "asynckit": "^0.4.0",
+            "combined-stream": "^1.0.8",
+            "mime-types": "^2.1.12"
+          }
+        }
+      }
     },
     "@types/normalize-package-data": {
       "version": "2.4.0",
@@ -2824,6 +2844,15 @@
         "redux": "*"
       }
     },
+    "@types/redux-mock-store": {
+      "version": "1.0.2",
+      "resolved": "https://registry.npmjs.org/@types/redux-mock-store/-/redux-mock-store-1.0.2.tgz",
+      "integrity": "sha512-6LBtAQBN34i7SI5X+Qs4zpTEZO1tTDZ6sZ9fzFjYwTl3nLQXaBtwYdoV44CzNnyKu438xJ1lSIYyw0YMvunESw==",
+      "dev": true,
+      "requires": {
+        "redux": "^4.0.5"
+      }
+    },
     "@types/redux-thunk": {
       "version": "2.1.0",
       "resolved": "https://registry.npmjs.org/@types/redux-thunk/-/redux-thunk-2.1.0.tgz",
@@ -4084,8 +4113,7 @@
     "asynckit": {
       "version": "0.4.0",
       "resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz",
-      "integrity": "sha1-x57Zf380y48robyXkLzDZkdLS3k=",
-      "dev": true
+      "integrity": "sha1-x57Zf380y48robyXkLzDZkdLS3k="
     },
     "at-least-node": {
       "version": "1.0.0",
@@ -5563,7 +5591,6 @@
       "version": "1.0.8",
       "resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.8.tgz",
       "integrity": "sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==",
-      "dev": true,
       "requires": {
         "delayed-stream": "~1.0.0"
       }
@@ -6512,8 +6539,7 @@
     "delayed-stream": {
       "version": "1.0.0",
       "resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz",
-      "integrity": "sha1-3zrhmayt+31ECqrgsp4icrJOxhk=",
-      "dev": true
+      "integrity": "sha1-3zrhmayt+31ECqrgsp4icrJOxhk="
     },
     "delegates": {
       "version": "1.0.0",
@@ -8201,6 +8227,82 @@
         "bser": "2.1.1"
       }
     },
+    "fetch-mock": {
+      "version": "9.11.0",
+      "resolved": "https://registry.npmjs.org/fetch-mock/-/fetch-mock-9.11.0.tgz",
+      "integrity": "sha512-PG1XUv+x7iag5p/iNHD4/jdpxL9FtVSqRMUQhPab4hVDt80T1MH5ehzVrL2IdXO9Q2iBggArFvPqjUbHFuI58Q==",
+      "dev": true,
+      "requires": {
+        "@babel/core": "^7.0.0",
+        "@babel/runtime": "^7.0.0",
+        "core-js": "^3.0.0",
+        "debug": "^4.1.1",
+        "glob-to-regexp": "^0.4.0",
+        "is-subset": "^0.1.1",
+        "lodash.isequal": "^4.5.0",
+        "path-to-regexp": "^2.2.1",
+        "querystring": "^0.2.0",
+        "whatwg-url": "^6.5.0"
+      },
+      "dependencies": {
+        "debug": {
+          "version": "4.3.1",
+          "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.1.tgz",
+          "integrity": "sha512-doEwdvm4PCeK4K3RQN2ZC2BYUBaxwLARCqZmMjtF8a51J2Rb0xpVloFRnCODwqjpwnAoao4pelN8l3RJdv3gRQ==",
+          "dev": true,
+          "requires": {
+            "ms": "2.1.2"
+          }
+        },
+        "ms": {
+          "version": "2.1.2",
+          "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz",
+          "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==",
+          "dev": true
+        },
+        "path-to-regexp": {
+          "version": "2.4.0",
+          "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-2.4.0.tgz",
+          "integrity": "sha512-G6zHoVqC6GGTQkZwF4lkuEyMbVOjoBKAEybQUypI1WTkqinCOrq2x6U2+phkJ1XsEMTy4LjtwPI7HW+NVrRR2w==",
+          "dev": true
+        },
+        "tr46": {
+          "version": "1.0.1",
+          "resolved": "https://registry.npmjs.org/tr46/-/tr46-1.0.1.tgz",
+          "integrity": "sha1-qLE/1r/SSJUZZ0zN5VujaTtwbQk=",
+          "dev": true,
+          "requires": {
+            "punycode": "^2.1.0"
+          }
+        },
+        "webidl-conversions": {
+          "version": "4.0.2",
+          "resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-4.0.2.tgz",
+          "integrity": "sha512-YQ+BmxuTgd6UXZW3+ICGfyqRyHXVlD5GtQr5+qjiNW7bF0cqrzX500HVXPBOvgXb5YnzDd+h0zqyv61KUD7+Sg==",
+          "dev": true
+        },
+        "whatwg-url": {
+          "version": "6.5.0",
+          "resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-6.5.0.tgz",
+          "integrity": "sha512-rhRZRqx/TLJQWUpQ6bmrt2UV4f0HCQ463yQuONJqC6fO2VoEb1pTYddbe59SkYq87aoM5A3bdhMZiUiVws+fzQ==",
+          "dev": true,
+          "requires": {
+            "lodash.sortby": "^4.7.0",
+            "tr46": "^1.0.1",
+            "webidl-conversions": "^4.0.2"
+          }
+        }
+      }
+    },
+    "fetch-mock-jest": {
+      "version": "1.5.1",
+      "resolved": "https://registry.npmjs.org/fetch-mock-jest/-/fetch-mock-jest-1.5.1.tgz",
+      "integrity": "sha512-+utwzP8C+Pax1GSka3nFXILWMY3Er2L+s090FOgqVNrNCPp0fDqgXnAHAJf12PLHi0z4PhcTaZNTz8e7K3fjqQ==",
+      "dev": true,
+      "requires": {
+        "fetch-mock": "^9.11.0"
+      }
+    },
     "figgy-pudding": {
       "version": "3.5.2",
       "resolved": "https://registry.npmjs.org/figgy-pudding/-/figgy-pudding-3.5.2.tgz",
@@ -8931,6 +9033,12 @@
         "is-glob": "^4.0.1"
       }
     },
+    "glob-to-regexp": {
+      "version": "0.4.1",
+      "resolved": "https://registry.npmjs.org/glob-to-regexp/-/glob-to-regexp-0.4.1.tgz",
+      "integrity": "sha512-lkX1HJXwyMcprw/5YUZc2s7DrpAiHB21/V+E1rHUrVNokkvB6bqMzT0VfV6/86ZNabt1k14YOIaT7nDvOX3Iiw==",
+      "dev": true
+    },
     "global-modules": {
       "version": "2.0.0",
       "resolved": "https://registry.npmjs.org/global-modules/-/global-modules-2.0.0.tgz",
@@ -12063,6 +12171,12 @@
       "integrity": "sha1-QVxEePK8wwEgwizhDtMib30+GOA=",
       "dev": true
     },
+    "lodash.isplainobject": {
+      "version": "4.0.6",
+      "resolved": "https://registry.npmjs.org/lodash.isplainobject/-/lodash.isplainobject-4.0.6.tgz",
+      "integrity": "sha1-fFJqUtibRcRcxpC4gWO+BJf1UMs=",
+      "dev": true
+    },
     "lodash.memoize": {
       "version": "4.1.2",
       "resolved": "https://registry.npmjs.org/lodash.memoize/-/lodash.memoize-4.1.2.tgz",
@@ -12446,14 +12560,12 @@
     "mime-db": {
       "version": "1.45.0",
       "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.45.0.tgz",
-      "integrity": "sha512-CkqLUxUk15hofLoLyljJSrukZi8mAtgd+yE5uO4tqRZsdsAJKv0O+rFMhVDRJgozy+yG6md5KwuXhD4ocIoP+w==",
-      "dev": true
+      "integrity": "sha512-CkqLUxUk15hofLoLyljJSrukZi8mAtgd+yE5uO4tqRZsdsAJKv0O+rFMhVDRJgozy+yG6md5KwuXhD4ocIoP+w=="
     },
     "mime-types": {
       "version": "2.1.28",
       "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.28.tgz",
       "integrity": "sha512-0TO2yJ5YHYr7M2zzT7gDU1tbwHxEUWBCLt0lscSNpcdAfFyJOVEpRYNS7EXVcTLNj/25QO8gulHC5JtTzSE2UQ==",
-      "dev": true,
       "requires": {
         "mime-db": "1.45.0"
       }
@@ -12799,6 +12911,11 @@
       "dev": true,
       "optional": true
     },
+    "node-fetch": {
+      "version": "2.6.1",
+      "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.6.1.tgz",
+      "integrity": "sha512-V4aYg89jEoVRxRb2fJdAg8FHvI7cEyYdVAh94HH0UIK8oJxUfkjlDQN9RbMx+bEjP7+ggMiFRprSti032Oipxw=="
+    },
     "node-forge": {
       "version": "0.10.0",
       "resolved": "https://registry.npmjs.org/node-forge/-/node-forge-0.10.0.tgz",
@@ -16004,6 +16121,15 @@
         "symbol-observable": "^1.2.0"
       }
     },
+    "redux-mock-store": {
+      "version": "1.5.4",
+      "resolved": "https://registry.npmjs.org/redux-mock-store/-/redux-mock-store-1.5.4.tgz",
+      "integrity": "sha512-xmcA0O/tjCLXhh9Fuiq6pMrJCwFRaouA8436zcikdIpYWWCjU76CRk+i2bHx8EeiSiMGnB85/lZdU3wIJVXHTA==",
+      "dev": true,
+      "requires": {
+        "lodash.isplainobject": "^4.0.6"
+      }
+    },
     "redux-thunk": {
       "version": "2.3.0",
       "resolved": "https://registry.npmjs.org/redux-thunk/-/redux-thunk-2.3.0.tgz",

+ 5 - 0
kafka-ui-react-app/package.json

@@ -3,6 +3,7 @@
   "version": "0.1.0",
   "private": true,
   "dependencies": {
+    "@types/node-fetch": "^2.5.8",
     "bulma": "^0.9.2",
     "bulma-switch": "^2.0.0",
     "classnames": "^2.2.6",
@@ -11,6 +12,7 @@
     "eslint-import-resolver-typescript": "^2.3.0",
     "immer": "^8.0.1",
     "lodash": "^4.17.20",
+    "node-fetch": "^2.6.1",
     "pretty-ms": "^6.0.1",
     "react": "^17.0.1",
     "react-datepicker": "^3.3.0",
@@ -80,6 +82,7 @@
     "@types/react-redux": "^7.1.11",
     "@types/react-router-dom": "^5.1.6",
     "@types/redux": "^3.6.0",
+    "@types/redux-mock-store": "^1.0.2",
     "@types/redux-thunk": "^2.1.0",
     "@types/uuid": "^8.3.0",
     "@typescript-eslint/eslint-plugin": "^4.9.0",
@@ -98,11 +101,13 @@
     "eslint-plugin-react": "^7.21.5",
     "eslint-plugin-react-hooks": "^2.5.1",
     "esprint": "^0.6.0",
+    "fetch-mock-jest": "^1.5.1",
     "husky": "^4.3.0",
     "lint-staged": "^10.5.2",
     "node-sass": "^4.14.1",
     "prettier": "^2.2.1",
     "react-scripts": "4.0.2",
+    "redux-mock-store": "^1.5.4",
     "ts-jest": "^26.4.4",
     "ts-node": "^9.1.1",
     "typescript": "~4.1.2"

+ 28 - 0
kafka-ui-react-app/src/redux/actions/__test__/actions.spec.ts

@@ -0,0 +1,28 @@
+import * as actions from '../actions';
+
+describe('Actions', () => {
+  describe('fetchClusterStatsAction', () => {
+    it('creates an REQUEST action', () => {
+      expect(actions.fetchClusterStatsAction.request()).toEqual({
+        type: 'GET_CLUSTER_STATUS__REQUEST',
+      });
+    });
+
+    it('creates an SUCCESS action', () => {
+      expect(
+        actions.fetchClusterStatsAction.success({ brokerCount: 1 })
+      ).toEqual({
+        type: 'GET_CLUSTER_STATUS__SUCCESS',
+        payload: {
+          brokerCount: 1,
+        },
+      });
+    });
+
+    it('creates an FAILURE action', () => {
+      expect(actions.fetchClusterStatsAction.failure()).toEqual({
+        type: 'GET_CLUSTER_STATUS__FAILURE',
+      });
+    });
+  });
+});

+ 13 - 0
kafka-ui-react-app/src/redux/actions/__test__/fixtures.ts

@@ -0,0 +1,13 @@
+import { ClusterStats } from 'generated-sources';
+
+export const clusterStats: ClusterStats = {
+  brokerCount: 1,
+  zooKeeperStatus: 1,
+  activeControllers: 1,
+  onlinePartitionCount: 6,
+  offlinePartitionCount: 0,
+  inSyncReplicasCount: 6,
+  outOfSyncReplicasCount: 0,
+  underReplicatedPartitionCount: 0,
+  diskUsage: [{ brokerId: 1, segmentSize: 6538, segmentCount: 6 }],
+};

+ 52 - 0
kafka-ui-react-app/src/redux/actions/__test__/thunks.spec.ts

@@ -0,0 +1,52 @@
+import configureMockStore, {
+  MockStoreCreator,
+  MockStoreEnhanced,
+} from 'redux-mock-store';
+import thunk, { ThunkDispatch } from 'redux-thunk';
+import fetchMock from 'fetch-mock-jest';
+import { Middleware } from 'redux';
+import { RootState, Action } from 'redux/interfaces';
+import * as actions from 'redux/actions/actions';
+import * as thunks from 'redux/actions/thunks';
+import * as fixtures from './fixtures';
+
+const middlewares: Array<Middleware> = [thunk];
+type DispatchExts = ThunkDispatch<RootState, undefined, Action>;
+
+const mockStoreCreator: MockStoreCreator<
+  RootState,
+  DispatchExts
+> = configureMockStore<RootState, DispatchExts>(middlewares);
+
+const store: MockStoreEnhanced<RootState, DispatchExts> = mockStoreCreator();
+
+const clusterName = 'local';
+
+describe('Thunks', () => {
+  afterEach(() => {
+    fetchMock.restore();
+    store.clearActions();
+  });
+
+  describe('fetchClusterStats', () => {
+    it('creates GET_CLUSTER_STATUS__SUCCESS when fetching cluster stats', async () => {
+      fetchMock.getOnce(`/api/clusters/${clusterName}/stats`, {
+        body: fixtures.clusterStats,
+      });
+      await store.dispatch(thunks.fetchClusterStats(clusterName));
+      expect(store.getActions()).toEqual([
+        actions.fetchClusterStatsAction.request(),
+        actions.fetchClusterStatsAction.success(fixtures.clusterStats),
+      ]);
+    });
+
+    it('creates GET_CLUSTER_STATUS__FAILURE when fetching cluster stats', async () => {
+      fetchMock.getOnce(`/api/clusters/${clusterName}/stats`, 404);
+      await store.dispatch(thunks.fetchClusterStats(clusterName));
+      expect(store.getActions()).toEqual([
+        actions.fetchClusterStatsAction.request(),
+        actions.fetchClusterStatsAction.failure(),
+      ]);
+    });
+  });
+});