diff --git a/ui/src/AppBoot.test.tsx b/ui/src/AppBoot.test.tsx
index 296feaddc..ce88957b8 100644
--- a/ui/src/AppBoot.test.tsx
+++ b/ui/src/AppBoot.test.tsx
@@ -20,18 +20,14 @@ const MockSettings = (defaultFilters: string[]) => {
const filtersBase64 = btoa(JSON.stringify(defaultFilters));
const settings = document.createElement("span");
settings.id = "settings";
- (settings as any).dataset = {
- defaultFiltersBase64: filtersBase64,
- };
+ settings.dataset.defaultFiltersBase64 = filtersBase64;
return settings;
});
};
-const FiltersSetting = (filters: any) => {
+const FiltersSetting = (filters: unknown) => {
const settings = document.createElement("span");
- (settings as any).dataset = {
- defaultFiltersBase64: btoa(JSON.stringify(filters)),
- };
+ settings.dataset.defaultFiltersBase64 = btoa(JSON.stringify(filters));
return ParseDefaultFilters(settings);
};
@@ -45,20 +41,19 @@ describe("SettingsElement()", () => {
const spy = MockSettings([]);
const settings = SettingsElement();
expect(spy).toHaveBeenCalledTimes(1);
- expect((settings as any).id).toBe("settings");
+ expect(settings?.id).toBe("settings");
});
});
describe("ParseDefaultFilters()", () => {
it("returns [] on missing filters attr", () => {
const settings = document.createElement("span");
- (settings as any).dataset = {};
expect(ParseDefaultFilters(settings)).toHaveLength(0);
});
it("returns [] on empty filters attr", () => {
const settings = document.createElement("span");
- (settings as any).dataset = { defaultFiltersBase64: "" };
+ settings.dataset.defaultFiltersBase64 = "";
expect(ParseDefaultFilters(settings)).toHaveLength(0);
});
@@ -82,9 +77,7 @@ describe("ParseDefaultFilters()", () => {
it("ignores template placeholder values", () => {
// Scenario: default filters attribute rendered with the literal template placeholder should be ignored
const settings = document.createElement("span");
- (settings as any).dataset = {
- defaultFiltersBase64: "{{ .DefaultFilter }}",
- };
+ settings.dataset.defaultFiltersBase64 = "{{ .DefaultFilter }}";
expect(ParseDefaultFilters(settings)).toHaveLength(0);
});
});
diff --git a/ui/src/Components/AlertAck/index.test.tsx b/ui/src/Components/AlertAck/index.test.tsx
index 48db0086e..1508eb01f 100644
--- a/ui/src/Components/AlertAck/index.test.tsx
+++ b/ui/src/Components/AlertAck/index.test.tsx
@@ -310,7 +310,7 @@ describe("", () => {
silenceFormStore.data.setAuthor("karma/ui");
await renderAndClick();
expect(
- JSON.parse((fetchMock.callHistory.lastCall()?.options as any).body),
+ JSON.parse(fetchMock.callHistory.lastCall()!.options.body as string),
).toEqual({
comment: "COMMENT",
createdBy: "karma/ui",
@@ -342,7 +342,7 @@ describe("", () => {
});
await renderAndClick();
expect(
- JSON.parse((fetchMock.callHistory.lastCall()?.options as any).body),
+ JSON.parse(fetchMock.callHistory.lastCall()!.options.body as string),
).toEqual({
comment: "comment",
createdBy: "me",
@@ -374,7 +374,7 @@ describe("", () => {
});
await renderAndClick();
expect(
- JSON.parse((fetchMock.callHistory.lastCall()?.options as any).body),
+ JSON.parse(fetchMock.callHistory.lastCall()!.options.body as string),
).toEqual({
comment:
"ACK! This alert was acknowledged using karma on Tue, 01 Feb 2000 00:00:00 GMT",
@@ -407,7 +407,7 @@ describe("", () => {
});
await renderAndClick();
const comment = JSON.parse(
- (fetchMock.callHistory.lastCall()?.options as any).body,
+ fetchMock.callHistory.lastCall()!.options.body as string,
).comment;
expect(comment).not.toEqual(
"ACK! This alert was acknowledged using karma on Tue Feb 01 2000 00:00:00 GMT",
@@ -432,7 +432,7 @@ describe("", () => {
});
await renderAndClick();
expect(
- JSON.parse((fetchMock.callHistory.lastCall()?.options as any).body),
+ JSON.parse(fetchMock.callHistory.lastCall()!.options.body as string),
).toEqual({
comment: "FOO: bar",
createdBy: "auth@example.com",
@@ -466,7 +466,7 @@ describe("", () => {
silenceFormStore.data.setAuthor("bob@example.com");
await renderAndClick();
expect(
- JSON.parse((fetchMock.callHistory.lastCall()?.options as any).body),
+ JSON.parse(fetchMock.callHistory.lastCall()!.options.body as string),
).toEqual({
comment: "FOO: bar",
createdBy: "bob@example.com",
@@ -499,7 +499,7 @@ describe("", () => {
silenceFormStore.data.setAuthor("");
await renderAndClick();
expect(
- JSON.parse((fetchMock.callHistory.lastCall()?.options as any).body),
+ JSON.parse(fetchMock.callHistory.lastCall()!.options.body as string),
).toEqual({
comment: "FOO: bar",
createdBy: "me",
diff --git a/ui/src/Components/AlertHistory/index.test.tsx b/ui/src/Components/AlertHistory/index.test.tsx
index a4193f4ec..923acbdc7 100644
--- a/ui/src/Components/AlertHistory/index.test.tsx
+++ b/ui/src/Components/AlertHistory/index.test.tsx
@@ -6,6 +6,7 @@ import fetchMock from "@fetch-mock/jest";
import { useInView } from "react-intersection-observer";
+import { mockInViewResponse } from "__fixtures__/InView";
import { MockAlertGroup, MockAlert } from "__fixtures__/Alerts";
import {
EmptyHistoryResponse,
@@ -289,10 +290,9 @@ describe("", () => {
},
);
- (useInView as jest.MockedFunction).mockReturnValue([
- jest.fn(),
- false,
- ] as any);
+ (useInView as jest.MockedFunction).mockReturnValue(
+ mockInViewResponse(false),
+ );
MockAlerts(3);
const { unmount } = render(
@@ -320,10 +320,9 @@ describe("", () => {
},
);
- (useInView as jest.MockedFunction).mockReturnValue([
- jest.fn(),
- false,
- ] as any);
+ (useInView as jest.MockedFunction).mockReturnValue(
+ mockInViewResponse(false),
+ );
MockAlerts(3);
const { rerender, unmount } = render(
@@ -334,10 +333,9 @@ describe("", () => {
});
expect(fetchMock.callHistory.calls()).toHaveLength(0);
- (useInView as jest.MockedFunction).mockReturnValue([
- jest.fn(),
- true,
- ] as any);
+ (useInView as jest.MockedFunction).mockReturnValue(
+ mockInViewResponse(true),
+ );
rerender();
await act(async () => {
@@ -361,11 +359,9 @@ describe("", () => {
},
);
- const inView = true;
- (useInView as jest.MockedFunction).mockReturnValue([
- jest.fn(),
- inView,
- ] as any);
+ (useInView as jest.MockedFunction).mockReturnValue(
+ mockInViewResponse(true),
+ );
MockAlerts(3);
const { unmount } = render(
diff --git a/ui/src/Components/MainModal/Configuration/AlertGroupCollapseConfiguration.test.tsx b/ui/src/Components/MainModal/Configuration/AlertGroupCollapseConfiguration.test.tsx
index b6adec6ba..ba6b1a94d 100644
--- a/ui/src/Components/MainModal/Configuration/AlertGroupCollapseConfiguration.test.tsx
+++ b/ui/src/Components/MainModal/Configuration/AlertGroupCollapseConfiguration.test.tsx
@@ -1,7 +1,7 @@
import { render, fireEvent, waitFor } from "@testing-library/react";
import { MockThemeContext } from "__fixtures__/Theme";
-import { Settings } from "Stores/Settings";
+import { Settings, type CollapseStateT } from "Stores/Settings";
import { ThemeContext } from "Components/Theme";
import { AlertGroupCollapseConfiguration } from "./AlertGroupCollapseConfiguration";
@@ -26,7 +26,9 @@ describe("", () => {
});
it("resets stored config to defaults if it is invalid", async () => {
- settingsStore.alertGroupConfig.setDefaultCollapseState("foo" as any);
+ settingsStore.alertGroupConfig.setDefaultCollapseState(
+ "foo" as unknown as CollapseStateT,
+ );
const { container } = renderConfiguration();
const select = container.querySelector("div.react-select__value-container");
expect(select?.textContent).toBe(
diff --git a/ui/src/Components/MainModal/Configuration/AlertGroupSortConfiguration.test.tsx b/ui/src/Components/MainModal/Configuration/AlertGroupSortConfiguration.test.tsx
index 8faeff5a5..ec4966889 100644
--- a/ui/src/Components/MainModal/Configuration/AlertGroupSortConfiguration.test.tsx
+++ b/ui/src/Components/MainModal/Configuration/AlertGroupSortConfiguration.test.tsx
@@ -2,7 +2,7 @@ import { render, fireEvent } from "@testing-library/react";
import { MockThemeContext } from "__fixtures__/Theme";
import { useFetchGetMock } from "__fixtures__/useFetchGet";
-import { Settings } from "Stores/Settings";
+import { Settings, type SortOrderT } from "Stores/Settings";
import { ThemeContext } from "Components/Theme";
import { AlertGroupSortConfiguration } from "./AlertGroupSortConfiguration";
@@ -43,7 +43,7 @@ describe("", () => {
});
it("invalid sortOrder value is reset on mount", () => {
- settingsStore.gridConfig.setSortOrder("badValue" as any);
+ settingsStore.gridConfig.setSortOrder("badValue" as unknown as SortOrderT);
renderConfiguration();
expect(settingsStore.gridConfig.config.sortOrder).toBe(
settingsStore.gridConfig.options.default.value,
diff --git a/ui/src/Components/Modal/index.test.tsx b/ui/src/Components/Modal/index.test.tsx
index 35eba924f..20091a5ef 100644
--- a/ui/src/Components/Modal/index.test.tsx
+++ b/ui/src/Components/Modal/index.test.tsx
@@ -145,7 +145,7 @@ describe("", () => {
it("scroll isn't enabled if ref is null", () => {
const useRefSpy = jest.spyOn(React, "useRef").mockImplementation(() =>
- Object.defineProperty({} as any, "current", {
+ Object.defineProperty({} as { current: unknown }, "current", {
get: () => null,
set: () => {},
}),
diff --git a/ui/src/Components/NavBar/FilterInput/History.test.tsx b/ui/src/Components/NavBar/FilterInput/History.test.tsx
index ea0133b13..96c58e33d 100644
--- a/ui/src/Components/NavBar/FilterInput/History.test.tsx
+++ b/ui/src/Components/NavBar/FilterInput/History.test.tsx
@@ -345,7 +345,7 @@ describe("History localStorage", () => {
it("localStored persists observable changes to localStorage", () => {
const { localStored } = require("Common/LocalStore");
const { runInAction } = require("mobx");
- const store = localStored("test_persist_key", { filters: [] as any[] });
+ const store = localStored("test_persist_key", { filters: [] as unknown[] });
// Flush the initial write (reaction fires via setTimeout 0)
jest.runAllTimers();
diff --git a/ui/src/ErrorBoundary.test.tsx b/ui/src/ErrorBoundary.test.tsx
index 7ec6eec74..faf394ff4 100644
--- a/ui/src/ErrorBoundary.test.tsx
+++ b/ui/src/ErrorBoundary.test.tsx
@@ -4,7 +4,7 @@ import { render, screen } from "@testing-library/react";
import { ErrorBoundary } from "./ErrorBoundary";
-let consoleSpy: any;
+let consoleSpy: jest.SpyInstance;
beforeEach(() => {
jest.useFakeTimers();
@@ -76,7 +76,7 @@ describe("", () => {
// Verifies that reloadApp uses functional setState to decrement reloadSeconds
const boundary = new ErrorBoundary({ children: });
const setStateSpy = jest.spyOn(boundary, "setState");
- (boundary as any).state = { cachedError: null, reloadSeconds: 2 };
+ boundary.state = { cachedError: null, reloadSeconds: 2 };
boundary.reloadApp();
@@ -91,7 +91,7 @@ describe("", () => {
// Verifies that reloadApp calls window.location.reload when reloadSeconds <= 1 (line 65)
const boundary = new ErrorBoundary({ children: });
const setStateSpy = jest.spyOn(boundary, "setState");
- (boundary as any).state = { cachedError: null, reloadSeconds: 1 };
+ boundary.state = { cachedError: null, reloadSeconds: 1 };
boundary.reloadApp();
@@ -103,7 +103,7 @@ describe("", () => {
const boundary = new ErrorBoundary({ children: });
const setStateSpy = jest.spyOn(boundary, "setState");
const error = new Error("Test error");
- (boundary as any).state = { cachedError: error, reloadSeconds: 60 };
+ boundary.state = { cachedError: error, reloadSeconds: 60 };
boundary.componentDidCatch(error, { componentStack: "" });
@@ -115,7 +115,7 @@ describe("", () => {
const boundary = new ErrorBoundary({ children: });
const setIntervalSpy = jest.spyOn(global, "setInterval");
const error = new Error("Test error");
- (boundary as any).timer = 123;
+ boundary.timer = 123 as unknown as ReturnType;
boundary.componentDidCatch(error, { componentStack: "" });
diff --git a/ui/src/Hooks/useFetchGet.test.tsx b/ui/src/Hooks/useFetchGet.test.tsx
index 0caf977d4..f7e5a939d 100644
--- a/ui/src/Hooks/useFetchGet.test.tsx
+++ b/ui/src/Hooks/useFetchGet.test.tsx
@@ -511,7 +511,7 @@ describe("useFetchGet", () => {
});
return "ok";
},
- } as any);
+ } as unknown as Response);
jest.useRealTimers();
const Component = () => {
diff --git a/ui/src/Hooks/useFlashTransition.test.tsx b/ui/src/Hooks/useFlashTransition.test.tsx
index a3bc81157..b1d9e5fdd 100644
--- a/ui/src/Hooks/useFlashTransition.test.tsx
+++ b/ui/src/Hooks/useFlashTransition.test.tsx
@@ -4,6 +4,8 @@ import { renderHook } from "@testing-library/react";
import { useInView } from "react-intersection-observer";
+import { mockInViewResponse } from "__fixtures__/InView";
+
import { useFlashTransition, defaultProps } from "./useFlashTransition";
describe("useFlashTransition", () => {
@@ -12,10 +14,9 @@ describe("useFlashTransition", () => {
});
it("does nothing when value changes but element is out of viewport", () => {
- (useInView as jest.MockedFunction).mockReturnValue([
- jest.fn(),
- false,
- ] as any);
+ (useInView as jest.MockedFunction).mockReturnValue(
+ mockInViewResponse(false),
+ );
let value = 0;
const { result, rerender } = renderHook(() => useFlashTransition(value));
@@ -27,10 +28,9 @@ describe("useFlashTransition", () => {
});
it("flashes when value changes and element is in viewport", () => {
- (useInView as jest.MockedFunction).mockReturnValue([
- jest.fn(),
- true,
- ] as any);
+ (useInView as jest.MockedFunction).mockReturnValue(
+ mockInViewResponse(true),
+ );
let value = 2;
const { result, rerender } = renderHook(() => useFlashTransition(value));
@@ -46,10 +46,9 @@ describe("useFlashTransition", () => {
});
it("flashes when value changes and element moves into viewport", () => {
- (useInView as jest.MockedFunction).mockReturnValue([
- jest.fn(),
- false,
- ] as any);
+ (useInView as jest.MockedFunction).mockReturnValue(
+ mockInViewResponse(false),
+ );
let value = 2;
const { result, rerender } = renderHook(() => useFlashTransition(value));
@@ -59,10 +58,9 @@ describe("useFlashTransition", () => {
expect(result.current.props).toMatchObject(defaultProps);
act(() => {
- (useInView as jest.MockedFunction).mockReturnValue([
- jest.fn(),
- true,
- ] as any);
+ (useInView as jest.MockedFunction).mockReturnValue(
+ mockInViewResponse(true),
+ );
});
rerender();
expect(result.current.props).toMatchObject({
@@ -73,10 +71,9 @@ describe("useFlashTransition", () => {
});
it("stops flashing props.onEntered is called", () => {
- (useInView as jest.MockedFunction).mockReturnValue([
- jest.fn(),
- true,
- ] as any);
+ (useInView as jest.MockedFunction).mockReturnValue(
+ mockInViewResponse(true),
+ );
let value = 2;
const { result, rerender } = renderHook(() => useFlashTransition(value));
@@ -89,25 +86,23 @@ describe("useFlashTransition", () => {
enter: true,
});
- act(() => (result.current.props as any).onEntered());
+ act(() => result.current.props.onEntered!({} as HTMLElement, false));
expect(result.current.props).toMatchObject(defaultProps);
});
it("unmounts cleanly when not flashing", () => {
- (useInView as jest.MockedFunction).mockReturnValue([
- jest.fn(),
- false,
- ] as any);
+ (useInView as jest.MockedFunction).mockReturnValue(
+ mockInViewResponse(false),
+ );
const { unmount } = renderHook(() => useFlashTransition(4));
unmount();
});
it("unmounts cleanly when flashing", () => {
- (useInView as jest.MockedFunction).mockReturnValue([
- jest.fn(),
- false,
- ] as any);
+ (useInView as jest.MockedFunction).mockReturnValue(
+ mockInViewResponse(false),
+ );
let value = 5;
const { rerender, unmount } = renderHook(() => useFlashTransition(value));
diff --git a/ui/src/Stores/AlertStore.test.ts b/ui/src/Stores/AlertStore.test.ts
index 9597da954..2f733d049 100644
--- a/ui/src/Stores/AlertStore.test.ts
+++ b/ui/src/Stores/AlertStore.test.ts
@@ -12,7 +12,7 @@ import {
NewUnappliedFilter,
} from "Stores/AlertStore";
-declare let global: any;
+declare let global: typeof globalThis;
beforeEach(() => {
fetchMock.mockReset();
@@ -491,12 +491,16 @@ describe("UpdateLocationSearch", () => {
});
it("{a: foo} is not pushed to location.search", () => {
- UpdateLocationSearch({ a: "foo" } as any);
+ UpdateLocationSearch({ a: "foo" } as unknown as Parameters<
+ typeof UpdateLocationSearch
+ >[0]);
expect(window.location.search).toBe("?q=");
});
it("{a: foo, q: bar} is pushed to location.search", () => {
- UpdateLocationSearch({ a: "foo", q: ["bar"] } as any);
+ UpdateLocationSearch({ a: "foo", q: ["bar"] } as unknown as Parameters<
+ typeof UpdateLocationSearch
+ >[0]);
expect(window.location.search).toBe("?q=bar");
});
@@ -649,7 +653,7 @@ describe("AlertStore.fetch", () => {
type: "opaque",
body: "auth needed",
json: jest.fn(() => EmptyAPIResponse()),
- }) as any,
+ }) as unknown as Promise,
);
await expect(
@@ -685,7 +689,9 @@ describe("AlertStore.fetch", () => {
const store = new AlertStore(["label=value"]);
// initial fetch, should update settings
- store.settings.setValues({ foo: "bar" } as any);
+ store.settings.setValues({ foo: "bar" } as unknown as Parameters<
+ typeof store.settings.setValues
+ >[0]);
await expect(
store.fetch("", false, "", "", false, {}, 5, {}),
).resolves.toBeUndefined();
diff --git a/ui/src/__fixtures__/InView.ts b/ui/src/__fixtures__/InView.ts
new file mode 100644
index 000000000..b2a523bbc
--- /dev/null
+++ b/ui/src/__fixtures__/InView.ts
@@ -0,0 +1,13 @@
+import type { InViewHookResponse } from "react-intersection-observer";
+
+const mockInViewResponse = (inView: boolean): InViewHookResponse => {
+ const ref = jest.fn() as (node?: Element | null) => void;
+ const response = Object.assign([ref, inView, undefined] as const, {
+ ref,
+ inView,
+ entry: undefined,
+ });
+ return response as unknown as InViewHookResponse;
+};
+
+export { mockInViewResponse };
diff --git a/ui/src/index.test.tsx b/ui/src/index.test.tsx
index db9714407..b0b913913 100644
--- a/ui/src/index.test.tsx
+++ b/ui/src/index.test.tsx
@@ -20,11 +20,12 @@ import { EmptyAPIResponse } from "__fixtures__/Fetch";
import { DefaultsBase64 } from "__fixtures__/Defaults";
import { mockMatchMedia } from "__fixtures__/matchMedia";
-const settingsElement = {
- dataset: {
- defaultFiltersBase64: "WyJmb289YmFyIiwiYmFyPX5iYXoiXQ==",
- },
-};
+const settingsElement = document.createElement("span");
+settingsElement.dataset.defaultFiltersBase64 =
+ "WyJmb289YmFyIiwiYmFyPX5iYXoiXQ==";
+
+const defaultsElement = document.createElement("span");
+defaultsElement.innerHTML = DefaultsBase64;
beforeEach(() => {
window.matchMedia = mockMatchMedia({});
@@ -41,7 +42,7 @@ it("renders without crashing with missing defaults div", async () => {
.spyOn(global.document, "getElementById")
.mockImplementation((name: string) => {
return name === "settings"
- ? (settingsElement as any)
+ ? settingsElement
: name === "defaults"
? null
: name === "root"
@@ -71,11 +72,9 @@ it("renders without crashing with defaults present", async () => {
.spyOn(global.document, "getElementById")
.mockImplementation((name: string) => {
return name === "settings"
- ? (settingsElement as any)
+ ? settingsElement
: name === "defaults"
- ? {
- innerHTML: DefaultsBase64,
- }
+ ? defaultsElement
: name === "root"
? root
: null;
diff --git a/ui/src/setupTests.ts b/ui/src/setupTests.ts
index 488d2a969..8a3c485d6 100644
--- a/ui/src/setupTests.ts
+++ b/ui/src/setupTests.ts
@@ -1,11 +1,11 @@
-import React from "react";
-
import "@testing-library/jest-dom";
import fetchMock, { manageFetchMockGlobally } from "@fetch-mock/jest";
import { useInView } from "react-intersection-observer";
+import { mockInViewResponse } from "__fixtures__/InView";
+
import { createMocks as createIdleTimerMocks } from "react-idle-timer";
import { configure } from "mobx";
@@ -35,9 +35,6 @@ jest.mock("react-intersection-observer");
FetchRetryConfig.minTimeout = 2;
FetchRetryConfig.maxTimeout = 10;
-// floating-ui uses useLayoutEffect
-React.useLayoutEffect = React.useEffect;
-
// Fail tests on any console output except explicitly allowed messages
const allowedMessages = [
// React.lazy suspended resource warnings (React 19 testing limitation)
@@ -66,9 +63,7 @@ beforeEach(() => {
useFetchGet as jest.MockedFunction
).mockImplementation(useFetchGetMock);
- (useInView as jest.MockedFunction).mockReturnValue([
- jest.fn(),
- true,
- // eslint-disable-next-line @typescript-eslint/no-explicit-any
- ] as any);
+ (useInView as jest.MockedFunction).mockReturnValue(
+ mockInViewResponse(true),
+ );
});
diff --git a/ui/src/testEnvironment.ts b/ui/src/testEnvironment.ts
index 74f04f015..bb52b2c30 100644
--- a/ui/src/testEnvironment.ts
+++ b/ui/src/testEnvironment.ts
@@ -7,6 +7,11 @@ class ResizeObserverPolyfill {
disconnect() {}
}
+interface GlobalWithResizeObserver {
+ ResizeObserver: typeof ResizeObserverPolyfill;
+ window: { ResizeObserver: typeof ResizeObserverPolyfill };
+}
+
export default class CustomTestEnvironment extends TestEnvironment {
async setup() {
await super.setup();
@@ -14,11 +19,8 @@ export default class CustomTestEnvironment extends TestEnvironment {
this.global.Response = Response;
this.global.ReadableStream = ReadableStream;
this.global.fetch = fetch;
- // Polyfill ResizeObserver for react-cool-dimensions (checks window.ResizeObserver)
- // eslint-disable-next-line @typescript-eslint/no-explicit-any
- (this.global as any).ResizeObserver = ResizeObserverPolyfill;
- // Also set on window for libraries that check window.ResizeObserver
- // eslint-disable-next-line @typescript-eslint/no-explicit-any
- (this.global as any).window.ResizeObserver = ResizeObserverPolyfill;
+ const g = this.global as unknown as GlobalWithResizeObserver;
+ g.ResizeObserver = ResizeObserverPolyfill;
+ g.window.ResizeObserver = ResizeObserverPolyfill;
}
}