From 386dc3a9a4d96f31e14273f85458daf45f8bc329 Mon Sep 17 00:00:00 2001 From: Lukasz Mierzwa Date: Mon, 9 Mar 2026 14:33:46 +0000 Subject: [PATCH] fix(ui): fix ts issues --- ui/src/AppBoot.test.tsx | 19 +++---- ui/src/Components/AlertAck/index.test.tsx | 14 ++--- ui/src/Components/AlertHistory/index.test.tsx | 30 +++++------ .../AlertGroupCollapseConfiguration.test.tsx | 6 ++- .../AlertGroupSortConfiguration.test.tsx | 4 +- ui/src/Components/Modal/index.test.tsx | 2 +- .../NavBar/FilterInput/History.test.tsx | 2 +- ui/src/ErrorBoundary.test.tsx | 10 ++-- ui/src/Hooks/useFetchGet.test.tsx | 2 +- ui/src/Hooks/useFlashTransition.test.tsx | 53 +++++++++---------- ui/src/Stores/AlertStore.test.ts | 16 ++++-- ui/src/__fixtures__/InView.ts | 13 +++++ ui/src/index.test.tsx | 19 ++++--- ui/src/setupTests.ts | 15 ++---- ui/src/testEnvironment.ts | 14 ++--- 15 files changed, 110 insertions(+), 109 deletions(-) create mode 100644 ui/src/__fixtures__/InView.ts 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; } }