Skip to content

Commit

Permalink
Handle async polling in tests. Improved selector memoize. Replace dep…
Browse files Browse the repository at this point in the history
…recated AnyAction with UnknownAction.
  • Loading branch information
huwshimi committed Jan 14, 2024
1 parent 03fa6b9 commit 1a6f0a8
Show file tree
Hide file tree
Showing 6 changed files with 32 additions and 16 deletions.
2 changes: 2 additions & 0 deletions src/store/app/actions.ts
Original file line number Diff line number Diff line change
Expand Up @@ -34,4 +34,6 @@ export type ControllerArgs =
export const connectAndPollControllers = createAction<{
controllers: ControllerArgs[];
isJuju: boolean;
// This arg is intended to prevent polling from starting in a testing scenario.
poll?: number;
}>("app/connectAndPollControllers");
8 changes: 3 additions & 5 deletions src/store/general/selectors.ts
Original file line number Diff line number Diff line change
Expand Up @@ -120,12 +120,10 @@ export const getControllerFeatures = createSelector(
export const getControllerFeatureEnabled = createSelector(
[
getControllerFeatures,
(_state, wsControllerURL, feature: keyof ControllerFeatures) => ({
wsControllerURL,
feature,
}),
(_state, wsControllerURL) => wsControllerURL,
(_state, _wsControllerURL, feature: keyof ControllerFeatures) => feature,
],
(controllerFeatures, { wsControllerURL, feature }) =>
(controllerFeatures, wsControllerURL, feature) =>
controllerFeatures?.[wsControllerURL]?.[feature],
);

Expand Down
6 changes: 3 additions & 3 deletions src/store/middleware/check-auth.test.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import type { AnyAction, MiddlewareAPI } from "redux";
import type { UnknownAction, MiddlewareAPI } from "redux";

import * as jujuModule from "juju/api";
import { thunks as appThunks } from "store/app";
Expand Down Expand Up @@ -51,7 +51,7 @@ describe("model poller", () => {
}));
});

const runMiddleware = async (action: Partial<AnyAction>) => {
const runMiddleware = async (action: UnknownAction) => {
const middleware = checkAuthMiddleware(fakeStore);
await middleware(next)(action);
return middleware;
Expand All @@ -65,7 +65,7 @@ describe("model poller", () => {
});

it("ignores function actions", async () => {
const action = jest.fn();
const action = jest.fn() as unknown as UnknownAction;
await runMiddleware(action);
expect(next).toHaveBeenCalledWith(action);
});
Expand Down
18 changes: 13 additions & 5 deletions src/store/middleware/model-poller.test.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import type { Client, Connection, Transport } from "@canonical/jujulib";
import type { AnyAction, MiddlewareAPI } from "redux";
import type { UnknownAction, MiddlewareAPI } from "redux";

import * as jujuModule from "juju/api";
import type { RelationshipTuple } from "juju/jimm/JIMMV4";
Expand Down Expand Up @@ -98,11 +98,13 @@ describe("model poller", () => {
} as unknown as Client;
});

const runMiddleware = async (actionOverrides?: Partial<AnyAction>) => {
const runMiddleware = async (actionOverrides?: Partial<UnknownAction>) => {
const action = {
...appActions.connectAndPollControllers({
controllers,
isJuju: true,
// Turn off polling to prevent the middleware running indefinitely.
poll: 0,
}),
...(actionOverrides ?? {}),
};
Expand Down Expand Up @@ -132,7 +134,9 @@ describe("model poller", () => {
});

it("does not pass through matched actions", async () => {
await runMiddleware({ payload: { controllers: [], isJuju: true } });
await runMiddleware({
payload: { controllers: [], isJuju: true, poll: 0 },
});
expect(next).not.toHaveBeenCalled();
});

Expand Down Expand Up @@ -387,6 +391,7 @@ describe("model poller", () => {
payload: {
controllers,
isJuju: false,
poll: 0,
},
});
expect(next).not.toHaveBeenCalled();
Expand Down Expand Up @@ -448,8 +453,11 @@ describe("model poller", () => {
intervalId,
juju,
}));
await runMiddleware();
jest.advanceTimersByTime(30000);
runMiddleware({ payload: { controllers, isJuju: true, poll: 2 } }).catch(
console.error,
);
await new Promise(jest.requireActual("timers").setImmediate);
jest.advanceTimersByTime(3000000);
// Resolve the async calls again.
await new Promise(jest.requireActual("timers").setImmediate);
expect(next).not.toHaveBeenCalled();
Expand Down
8 changes: 8 additions & 0 deletions src/store/middleware/model-poller.ts
Original file line number Diff line number Diff line change
Expand Up @@ -190,6 +190,7 @@ export const modelPollerMiddleware: Middleware<
}
}

let pollCount = 0;
do {
const identity = conn?.info?.user?.identity;
if (identity) {
Expand All @@ -216,6 +217,13 @@ export const modelPollerMiddleware: Middleware<
}
}

// Allow the polling to run a certain number of times in tests.
if (process.env.NODE_ENV === "test") {
if (pollCount === action.payload.poll) {
break;
}
pollCount++;
}
// Wait 30s then start again.
await new Promise((resolve) => {
setTimeout(() => {
Expand Down
6 changes: 3 additions & 3 deletions src/store/store.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import type { AnyAction } from "@reduxjs/toolkit";
import type { UnknownAction } from "@reduxjs/toolkit";
import { combineReducers, configureStore } from "@reduxjs/toolkit";
import { useCallback } from "react";
import type { TypedUseSelectorHook } from "react-redux";
Expand Down Expand Up @@ -52,8 +52,8 @@ export const useAppDispatch: () => AppDispatch = useDispatch;
export const usePromiseDispatch = () => {
const dispatch = useAppDispatch();
return useCallback(
<Result>(action: AnyAction) =>
(dispatch as (action: AnyAction) => Promise<Result>)(action),
<Result>(action: UnknownAction) =>
(dispatch as (action: UnknownAction) => Promise<Result>)(action),
[dispatch],
);
};
Expand Down

0 comments on commit 1a6f0a8

Please sign in to comment.