Migrate more jest tests to vitest (#33922)

* Migrate more jest tests to vitest

* Fix jest config

* Fix jest config

* Make remaining jest tests type-happy

* Iterate

* Fix Notifier import cycle

* Fix tests

* Delint

* Iterate

* Handle SDKContextClass `client` initialisation internally

Rather than via MatrixChat - this is predominantly for Lifecycle tests as they don't use a MatrixChat and it doesn't make much sense for this component to own this state.

* Fix tests

* Iterate

* Simplify diff

* Improve coverage

* Improve coverage

* Iterate
This commit is contained in:
Michael Telatynski
2026-07-07 09:50:40 +00:00
committed by GitHub
parent 30c02ab5d7
commit 6fda0ad60f
10 changed files with 120 additions and 88 deletions
@@ -6,6 +6,10 @@ SPDX-License-Identifier: AGPL-3.0-only OR GPL-3.0-only OR LicenseRef-Element-Com
Please see LICENSE files in the repository root for full details. Please see LICENSE files in the repository root for full details.
*/ */
// @vitest-environment happy-dom
import { vi, describe, it, expect, beforeAll, afterAll } from "vitest";
import { import {
formatSeconds, formatSeconds,
formatRelativeTime, formatRelativeTime,
@@ -25,9 +29,11 @@ import {
HOUR_MS, HOUR_MS,
MINUTE_MS, MINUTE_MS,
DAY_MS, DAY_MS,
} from "../../../src/DateUtils"; } from "./DateUtils";
import { REPEATABLE_DATE, mockIntlDateTimeFormat, unmockIntlDateTimeFormat } from "../../test-utils"; import { REPEATABLE_DATE, mockIntlDateTimeFormat, unmockIntlDateTimeFormat } from "../test/test-utils";
import * as languageSettings from "../../../src/i18n/settings"; import * as languageSettings from "./i18n/settings";
vi.mock("./TimezoneHandler", () => ({ getUserTimezone: () => "UTC" }));
describe("getDaysArray", () => { describe("getDaysArray", () => {
it("should return Sunday-Saturday in long mode", () => { it("should return Sunday-Saturday in long mode", () => {
@@ -166,13 +172,13 @@ describe("getMonthsArray", () => {
describe("formatDate", () => { describe("formatDate", () => {
beforeAll(() => { beforeAll(() => {
jest.useFakeTimers(); vi.useFakeTimers();
jest.setSystemTime(REPEATABLE_DATE); vi.setSystemTime(REPEATABLE_DATE);
}); });
afterAll(() => { afterAll(() => {
jest.setSystemTime(jest.getRealSystemTime()); vi.setSystemTime(vi.getRealSystemTime());
jest.useRealTimers(); vi.useRealTimers();
}); });
it("should return time string if date is within same day", () => { it("should return time string if date is within same day", () => {
@@ -255,14 +261,14 @@ describe("formatSeconds", () => {
describe("formatRelativeTime", () => { describe("formatRelativeTime", () => {
beforeAll(() => { beforeAll(() => {
jest.useFakeTimers(); vi.useFakeTimers();
// Tuesday, 2 November 2021 11:18:03 UTC // Tuesday, 2 November 2021 11:18:03 UTC
jest.setSystemTime(1635851883000); vi.setSystemTime(1635851883000);
}); });
afterAll(() => { afterAll(() => {
jest.setSystemTime(jest.getRealSystemTime()); vi.setSystemTime(vi.getRealSystemTime());
jest.useRealTimers(); vi.useRealTimers();
}); });
it("returns hour format for events created in the same day", () => { it("returns hour format for events created in the same day", () => {
@@ -369,7 +375,7 @@ describe("formatLocalDateShort()", () => {
}); });
const timestamp = new Date("Fri Dec 17 2021 09:09:00 GMT+0100 (Central European Standard Time)").getTime(); const timestamp = new Date("Fri Dec 17 2021 09:09:00 GMT+0100 (Central European Standard Time)").getTime();
it("formats date correctly by locale", () => { it("formats date correctly by locale", () => {
const locale = jest.spyOn(languageSettings, "getUserLanguage"); const locale = vi.spyOn(languageSettings, "getUserLanguage");
mockIntlDateTimeFormat(); mockIntlDateTimeFormat();
// format is DD/MM/YY // format is DD/MM/YY
+4
View File
@@ -8,6 +8,7 @@ Please see LICENSE files in the repository root for full details.
import { vi } from "vitest"; import { vi } from "vitest";
import { mocks } from "../../test/setup/mocks.ts"; import { mocks } from "../../test/setup/mocks.ts";
import SdkConfig, { DEFAULTS } from "../SdkConfig";
// set up AudioContext API mock // set up AudioContext API mock
vi.stubGlobal("AudioContext", function () { vi.stubGlobal("AudioContext", function () {
@@ -28,3 +29,6 @@ if (globalThis.window === undefined) {
setTimeout: globalThis.setTimeout, setTimeout: globalThis.setTimeout,
}); });
} }
// uninitialised SdkConfig causes lots of warnings in console, init with defaults
SdkConfig.put(DEFAULTS);
+3 -1
View File
@@ -5,7 +5,7 @@ SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial
Please see LICENSE files in the repository root for full details. Please see LICENSE files in the repository root for full details.
*/ */
import { beforeEach, afterEach } from "vitest"; import { vi, beforeEach, afterEach } from "vitest";
import fetchMock, { manageFetchMockGlobally } from "@fetch-mock/vitest"; import fetchMock, { manageFetchMockGlobally } from "@fetch-mock/vitest";
import SdkConfig, { DEFAULTS } from "../SdkConfig"; import SdkConfig, { DEFAULTS } from "../SdkConfig";
@@ -15,6 +15,8 @@ import { setupLanguageMock } from "./setupLanguage.ts";
manageFetchMockGlobally(); manageFetchMockGlobally();
beforeEach(() => { beforeEach(() => {
vi.stubEnv("TZ", "UTC");
// set up fetch API mock // set up fetch API mock
fetchMock.hardReset(); fetchMock.hardReset();
fetchMock.catch(404); fetchMock.catch(404);
@@ -6,8 +6,11 @@ SPDX-License-Identifier: AGPL-3.0-only OR GPL-3.0-only OR LicenseRef-Element-Com
Please see LICENSE files in the repository root for full details. Please see LICENSE files in the repository root for full details.
*/ */
// @vitest-environment happy-dom
import { type ReactElement } from "react"; import { type ReactElement } from "react";
import { render } from "jest-matrix-react"; import { render } from "test-utils-rtl";
import { describe, it, expect } from "vitest";
import { MatrixError, ConnectionError } from "matrix-js-sdk/src/matrix"; import { MatrixError, ConnectionError } from "matrix-js-sdk/src/matrix";
import { import {
@@ -17,7 +20,7 @@ import {
messageForResourceLimitError, messageForResourceLimitError,
messageForSyncError, messageForSyncError,
resourceLimitStrings, resourceLimitStrings,
} from "../../../src/utils/ErrorUtils"; } from "./ErrorUtils";
describe("messageForResourceLimitError", () => { describe("messageForResourceLimitError", () => {
it("should match snapshot for monthly_active_user", () => { it("should match snapshot for monthly_active_user", () => {
@@ -6,6 +6,8 @@ SPDX-License-Identifier: AGPL-3.0-only OR GPL-3.0-only OR LicenseRef-Element-Com
Please see LICENSE files in the repository root for full details. Please see LICENSE files in the repository root for full details.
*/ */
// @vitest-environment happy-dom
import { import {
M_LOCATION, M_LOCATION,
EventStatus, EventStatus,
@@ -19,8 +21,9 @@ import {
Room, Room,
Thread, Thread,
} from "matrix-js-sdk/src/matrix"; } from "matrix-js-sdk/src/matrix";
import { vi, describe, it, expect, beforeEach, afterAll } from "vitest";
import { MatrixClientPeg } from "../../../src/MatrixClientPeg"; import { MatrixClientPeg } from "../MatrixClientPeg";
import { import {
canCancel, canCancel,
canEditContent, canEditContent,
@@ -31,25 +34,30 @@ import {
isContentActionable, isContentActionable,
isLocationEvent, isLocationEvent,
isVoiceMessage, isVoiceMessage,
} from "../../../src/utils/EventUtils"; } from "./EventUtils";
import { getMockClientWithEventEmitter, makeBeaconInfoEvent, makePollStartEvent, stubClient } from "../../test-utils"; import {
import dis from "../../../src/dispatcher/dispatcher"; getMockClientWithEventEmitter,
import { Action } from "../../../src/dispatcher/actions"; makeBeaconInfoEvent,
makePollStartEvent,
stubClient,
} from "../../test/test-utils";
import dis from "../dispatcher/dispatcher";
import { Action } from "../dispatcher/actions";
jest.mock("../../../src/dispatcher/dispatcher"); vi.mock("../dispatcher/dispatcher");
describe("EventUtils", () => { describe("EventUtils", () => {
const userId = "@user:server"; const userId = "@user:server";
const roomId = "!room:server"; const roomId = "!room:server";
const mockClient = getMockClientWithEventEmitter({ const mockClient = getMockClientWithEventEmitter({
getUserId: jest.fn().mockReturnValue(userId), getUserId: vi.fn().mockReturnValue(userId),
}); });
beforeEach(() => { beforeEach(() => {
mockClient.getUserId.mockClear().mockReturnValue(userId); mockClient.getUserId.mockClear().mockReturnValue(userId);
}); });
afterAll(() => { afterAll(() => {
jest.spyOn(MatrixClientPeg, "get").mockRestore(); vi.spyOn(MatrixClientPeg, "get").mockRestore();
}); });
// setup events // setup events
@@ -404,7 +412,7 @@ describe("EventUtils", () => {
}; };
beforeEach(() => { beforeEach(() => {
jest.clearAllMocks(); vi.clearAllMocks();
stubClient(); stubClient();
client = MatrixClientPeg.safeGet(); client = MatrixClientPeg.safeGet();
@@ -413,9 +421,9 @@ describe("EventUtils", () => {
pendingEventOrdering: PendingEventOrdering.Detached, pendingEventOrdering: PendingEventOrdering.Detached,
}); });
jest.spyOn(client, "supportsThreads").mockReturnValue(true); vi.spyOn(client, "supportsThreads").mockReturnValue(true);
jest.spyOn(client, "getRoom").mockReturnValue(room); vi.spyOn(client, "getRoom").mockReturnValue(room);
jest.spyOn(client, "fetchRoomEvent").mockImplementation(async (roomId, eventId) => { vi.spyOn(client, "fetchRoomEvent").mockImplementation(async (roomId, eventId) => {
return events[eventId] ?? Promise.reject(); return events[eventId] ?? Promise.reject();
}); });
}); });
@@ -6,8 +6,9 @@ Please see LICENSE files in the repository root for full details.
*/ */
import { type MediaEventContent } from "matrix-js-sdk/src/types"; import { type MediaEventContent } from "matrix-js-sdk/src/types";
import { describe, it, expect } from "vitest";
import { downloadLabelForFile } from "../../../src/utils/FileUtils.ts"; import { downloadLabelForFile } from "./FileUtils.ts";
describe("FileUtils", () => { describe("FileUtils", () => {
describe("downloadLabelForFile", () => { describe("downloadLabelForFile", () => {
@@ -1,6 +1,6 @@
// Jest Snapshot v1, https://jestjs.io/docs/snapshot-testing // Vitest Snapshot v1, https://vitest.dev/guide/snapshot.html
exports[`messageForConnectionError should match snapshot for ConnectionError 1`] = ` exports[`messageForConnectionError > should match snapshot for ConnectionError 1`] = `
<DocumentFragment> <DocumentFragment>
<span> <span>
<span> <span>
@@ -34,13 +34,13 @@ exports[`messageForConnectionError should match snapshot for ConnectionError 1`]
</DocumentFragment> </DocumentFragment>
`; `;
exports[`messageForConnectionError should match snapshot for MatrixError M_NOT_FOUND 1`] = ` exports[`messageForConnectionError > should match snapshot for MatrixError M_NOT_FOUND 1`] = `
<DocumentFragment> <DocumentFragment>
There was a problem communicating with the homeserver, please try again later.(M_NOT_FOUND) There was a problem communicating with the homeserver, please try again later.(M_NOT_FOUND)
</DocumentFragment> </DocumentFragment>
`; `;
exports[`messageForConnectionError should match snapshot for mixed content error 1`] = ` exports[`messageForConnectionError > should match snapshot for mixed content error 1`] = `
<DocumentFragment> <DocumentFragment>
<span> <span>
<span> <span>
@@ -58,19 +58,19 @@ exports[`messageForConnectionError should match snapshot for mixed content error
</DocumentFragment> </DocumentFragment>
`; `;
exports[`messageForConnectionError should match snapshot for unknown error 1`] = ` exports[`messageForConnectionError > should match snapshot for unknown error 1`] = `
<DocumentFragment> <DocumentFragment>
There was a problem communicating with the homeserver, please try again later. There was a problem communicating with the homeserver, please try again later.
</DocumentFragment> </DocumentFragment>
`; `;
exports[`messageForLoginError should match snapshot for 401 1`] = ` exports[`messageForLoginError > should match snapshot for 401 1`] = `
<DocumentFragment> <DocumentFragment>
Incorrect username and/or password. Incorrect username and/or password.
</DocumentFragment> </DocumentFragment>
`; `;
exports[`messageForLoginError should match snapshot for M_RESOURCE_LIMIT_EXCEEDED 1`] = ` exports[`messageForLoginError > should match snapshot for M_RESOURCE_LIMIT_EXCEEDED 1`] = `
<DocumentFragment> <DocumentFragment>
<div> <div>
<div> <div>
@@ -85,19 +85,19 @@ exports[`messageForLoginError should match snapshot for M_RESOURCE_LIMIT_EXCEEDE
</DocumentFragment> </DocumentFragment>
`; `;
exports[`messageForLoginError should match snapshot for M_USER_DEACTIVATED 1`] = ` exports[`messageForLoginError > should match snapshot for M_USER_DEACTIVATED 1`] = `
<DocumentFragment> <DocumentFragment>
This account has been deactivated. This account has been deactivated.
</DocumentFragment> </DocumentFragment>
`; `;
exports[`messageForLoginError should match snapshot for unknown error 1`] = ` exports[`messageForLoginError > should match snapshot for unknown error 1`] = `
<DocumentFragment> <DocumentFragment>
There was a problem communicating with the homeserver, please try again later. (HTTP 400) There was a problem communicating with the homeserver, please try again later. (HTTP 400)
</DocumentFragment> </DocumentFragment>
`; `;
exports[`messageForResourceLimitError should match snapshot for admin contact links 1`] = ` exports[`messageForResourceLimitError > should match snapshot for admin contact links 1`] = `
<DocumentFragment> <DocumentFragment>
<span> <span>
Please Please
@@ -113,13 +113,13 @@ exports[`messageForResourceLimitError should match snapshot for admin contact li
</DocumentFragment> </DocumentFragment>
`; `;
exports[`messageForResourceLimitError should match snapshot for monthly_active_user 1`] = ` exports[`messageForResourceLimitError > should match snapshot for monthly_active_user 1`] = `
<DocumentFragment> <DocumentFragment>
This homeserver has hit its Monthly Active User limit. This homeserver has hit its Monthly Active User limit.
</DocumentFragment> </DocumentFragment>
`; `;
exports[`messageForSyncError should match snapshot for M_RESOURCE_LIMIT_EXCEEDED 1`] = ` exports[`messageForSyncError > should match snapshot for M_RESOURCE_LIMIT_EXCEEDED 1`] = `
<DocumentFragment> <DocumentFragment>
<div> <div>
<div> <div>
@@ -132,7 +132,7 @@ exports[`messageForSyncError should match snapshot for M_RESOURCE_LIMIT_EXCEEDED
</DocumentFragment> </DocumentFragment>
`; `;
exports[`messageForSyncError should match snapshot for other errors 1`] = ` exports[`messageForSyncError > should match snapshot for other errors 1`] = `
<DocumentFragment> <DocumentFragment>
<div> <div>
Unable to connect to Homeserver. Retrying… Unable to connect to Homeserver. Retrying…
@@ -6,25 +6,29 @@ SPDX-License-Identifier: AGPL-3.0-only OR GPL-3.0-only OR LicenseRef-Element-Com
Please see LICENSE files in the repository root for full details. Please see LICENSE files in the repository root for full details.
*/ */
import { mocked, type Mocked } from "jest-mock-vitest-adapter"; // @vitest-environment happy-dom
import { vi, describe, it, expect, beforeEach, afterEach, type Mocked } from "vitest";
import { type MatrixClient, type Room } from "matrix-js-sdk/src/matrix"; import { type MatrixClient, type Room } from "matrix-js-sdk/src/matrix";
import { sleep } from "matrix-js-sdk/src/utils"; import { sleep } from "matrix-js-sdk/src/utils";
import { mkRoom, resetAsyncStoreWithClient, setupAsyncStoreWithClient, stubClient } from "test-utils/test-utils";
import { MatrixClientPeg } from "../../../src/MatrixClientPeg"; import { MatrixClientPeg } from "../MatrixClientPeg";
import { mkRoom, resetAsyncStoreWithClient, setupAsyncStoreWithClient, stubClient } from "../../test-utils"; import defaultDispatcher from "../dispatcher/dispatcher";
import defaultDispatcher from "../../../src/dispatcher/dispatcher"; import { type ViewRoomPayload } from "../dispatcher/payloads/ViewRoomPayload";
import { type ViewRoomPayload } from "../../../src/dispatcher/payloads/ViewRoomPayload"; import { Action } from "../dispatcher/actions";
import { Action } from "../../../src/dispatcher/actions"; import { leaveRoomBehaviour } from "./leave-behaviour";
import { leaveRoomBehaviour } from "../../../src/utils/leave-behaviour"; import { SDKContextClass } from "../contexts/SDKContextClass";
import { SDKContextClass } from "../../../src/contexts/SDKContextClass"; import DMRoomMap from "../utils/DMRoomMap";
import DMRoomMap from "../../../src/utils/DMRoomMap"; import SpaceStore from "../stores/spaces/SpaceStore";
import SpaceStore from "../../../src/stores/spaces/SpaceStore"; import { MetaSpace } from "../stores/spaces";
import { MetaSpace } from "../../../src/stores/spaces"; import { type ActionPayload } from "../dispatcher/payloads";
import { type ActionPayload } from "../../../src/dispatcher/payloads"; import SettingsStore from "../settings/SettingsStore";
import SettingsStore from "../../../src/settings/SettingsStore"; import { CallStore } from "../stores/CallStore";
import { CallStore } from "../../../src/stores/CallStore"; import { type Call } from "../models/Call";
import { type Call } from "../../../src/models/Call"; import LegacyCallHandler from "../LegacyCallHandler";
import LegacyCallHandler from "../../../src/LegacyCallHandler";
vi.mock("../Modal.tsx");
describe("leaveRoomBehaviour", () => { describe("leaveRoomBehaviour", () => {
SDKContextClass.instance.constructEagerStores(); // Initialize RoomViewStore SDKContextClass.instance.constructEagerStores(); // Initialize RoomViewStore
@@ -35,7 +39,7 @@ describe("leaveRoomBehaviour", () => {
beforeEach(async () => { beforeEach(async () => {
stubClient(); stubClient();
client = mocked(MatrixClientPeg.safeGet()); client = vi.mocked(MatrixClientPeg.safeGet());
DMRoomMap.makeShared(client); DMRoomMap.makeShared(client);
room = mkRoom(client, "!1:example.org"); room = mkRoom(client, "!1:example.org");
@@ -58,7 +62,7 @@ describe("leaveRoomBehaviour", () => {
afterEach(async () => { afterEach(async () => {
SpaceStore.instance.setActiveSpace(MetaSpace.Home); SpaceStore.instance.setActiveSpace(MetaSpace.Home);
await resetAsyncStoreWithClient(SpaceStore.instance); await resetAsyncStoreWithClient(SpaceStore.instance);
jest.restoreAllMocks(); vi.restoreAllMocks();
}); });
const viewRoom = (room: Room) => const viewRoom = (room: Room) =>
@@ -72,7 +76,7 @@ describe("leaveRoomBehaviour", () => {
); );
const expectDispatch = async <T extends ActionPayload>(payload: T) => { const expectDispatch = async <T extends ActionPayload>(payload: T) => {
const dispatcherSpy = jest.fn(); const dispatcherSpy = vi.fn();
const dispatcherRef = defaultDispatcher.register(dispatcherSpy); const dispatcherRef = defaultDispatcher.register(dispatcherSpy);
await sleep(0); await sleep(0);
expect(dispatcherSpy).toHaveBeenCalledWith(payload); expect(dispatcherSpy).toHaveBeenCalledWith(payload);
@@ -80,7 +84,7 @@ describe("leaveRoomBehaviour", () => {
}; };
it("hangs up legacy calls when leaving a room", async () => { it("hangs up legacy calls when leaving a room", async () => {
const hangupSpy = jest.spyOn(LegacyCallHandler.instance, "hangupOrReject").mockImplementation(() => {}); const hangupSpy = vi.spyOn(LegacyCallHandler.instance, "hangupOrReject").mockImplementation(() => {});
viewRoom(room); viewRoom(room);
await leaveRoomBehaviour(client, room.roomId); await leaveRoomBehaviour(client, room.roomId);
@@ -90,10 +94,10 @@ describe("leaveRoomBehaviour", () => {
it("disconnects widget-based calls when leaving a room", async () => { it("disconnects widget-based calls when leaving a room", async () => {
const mockCall = { const mockCall = {
disconnect: jest.fn().mockResolvedValue(undefined), disconnect: vi.fn().mockResolvedValue(undefined),
} as unknown as Call; } as unknown as Call;
jest.spyOn(CallStore.instance, "getActiveCall").mockReturnValue(mockCall); vi.spyOn(CallStore.instance, "getActiveCall").mockReturnValue(mockCall);
viewRoom(room); viewRoom(room);
await leaveRoomBehaviour(client, room.roomId); await leaveRoomBehaviour(client, room.roomId);
@@ -109,7 +113,7 @@ describe("leaveRoomBehaviour", () => {
}); });
it("returns to the parent space after leaving a room inside of a space that was being viewed", async () => { it("returns to the parent space after leaving a room inside of a space that was being viewed", async () => {
jest.spyOn(SpaceStore.instance, "getCanonicalParent").mockImplementation((roomId) => vi.spyOn(SpaceStore.instance, "getCanonicalParent").mockImplementation((roomId) =>
roomId === room.roomId ? space : null, roomId === room.roomId ? space : null,
); );
viewRoom(room); viewRoom(room);
@@ -133,7 +137,7 @@ describe("leaveRoomBehaviour", () => {
it("returns to the parent space after leaving a subspace that was being viewed", async () => { it("returns to the parent space after leaving a subspace that was being viewed", async () => {
room.isSpaceRoom.mockReturnValue(true); room.isSpaceRoom.mockReturnValue(true);
jest.spyOn(SpaceStore.instance, "getCanonicalParent").mockImplementation((roomId) => vi.spyOn(SpaceStore.instance, "getCanonicalParent").mockImplementation((roomId) =>
roomId === room.roomId ? space : null, roomId === room.roomId ? space : null,
); );
viewRoom(room); viewRoom(room);
@@ -149,7 +153,7 @@ describe("leaveRoomBehaviour", () => {
describe("If the feature_dynamic_room_predecessors is not enabled", () => { describe("If the feature_dynamic_room_predecessors is not enabled", () => {
beforeEach(() => { beforeEach(() => {
jest.spyOn(SettingsStore, "getValue").mockReturnValue(false); vi.spyOn(SettingsStore, "getValue").mockReturnValue(false);
}); });
it("Passes through the dynamic predecessor setting", async () => { it("Passes through the dynamic predecessor setting", async () => {
@@ -161,7 +165,7 @@ describe("leaveRoomBehaviour", () => {
describe("If the feature_dynamic_room_predecessors is enabled", () => { describe("If the feature_dynamic_room_predecessors is enabled", () => {
beforeEach(() => { beforeEach(() => {
// Turn on feature_dynamic_room_predecessors setting // Turn on feature_dynamic_room_predecessors setting
jest.spyOn(SettingsStore, "getValue").mockImplementation( vi.spyOn(SettingsStore, "getValue").mockImplementation(
(settingName) => settingName === "feature_dynamic_room_predecessors", (settingName) => settingName === "feature_dynamic_room_predecessors",
); );
}); });
@@ -5,18 +5,20 @@
* Please see LICENSE files in the repository root for full details. * Please see LICENSE files in the repository root for full details.
*/ */
import { type MatrixClient, type Room, RoomEvent } from "matrix-js-sdk/src/matrix"; // @vitest-environment happy-dom
import { type MockedObject } from "jest-mock-vitest-adapter";
import { createRef } from "react";
import { mkRoom, stubClient } from "../../test-utils"; import { type MatrixClient, type Room, RoomEvent } from "matrix-js-sdk/src/matrix";
import { WidgetPipViewModel } from "../../../src/viewmodels/room/WidgetPipViewModel"; import { vi, describe, it, expect, beforeEach, afterEach, type MockedObject } from "vitest";
import WidgetStore, { type IApp } from "../../../src/stores/WidgetStore"; import { createRef } from "react";
import defaultDispatcher from "../../../src/dispatcher/dispatcher"; import { mkRoom, stubClient } from "test-utils";
import { Action } from "../../../src/dispatcher/actions";
import { WidgetLayoutStore } from "../../../src/stores/widgets/WidgetLayoutStore"; import { WidgetPipViewModel } from "./WidgetPipViewModel";
import { CallStore, CallStoreEvent } from "../../../src/stores/CallStore"; import WidgetStore, { type IApp } from "../../stores/WidgetStore";
import { type Call } from "../../../src/models/Call"; import defaultDispatcher from "../../dispatcher/dispatcher";
import { Action } from "../../dispatcher/actions";
import { WidgetLayoutStore } from "../../stores/widgets/WidgetLayoutStore";
import { CallStore, CallStoreEvent } from "../../stores/CallStore";
import { type Call } from "../../models/Call";
const userId = "@example:example.org"; const userId = "@example:example.org";
const widgetId = "test-widget-id"; const widgetId = "test-widget-id";
@@ -25,8 +27,8 @@ type BackClickEvent = Parameters<WidgetPipViewModel["onBackClick"]>[0];
const createBackClickEvent = (): BackClickEvent => const createBackClickEvent = (): BackClickEvent =>
({ ({
preventDefault: jest.fn(), preventDefault: vi.fn(),
stopPropagation: jest.fn(), stopPropagation: vi.fn(),
}) as unknown as BackClickEvent; }) as unknown as BackClickEvent;
describe("WidgetPipViewModel", () => { describe("WidgetPipViewModel", () => {
@@ -46,7 +48,7 @@ describe("WidgetPipViewModel", () => {
name: "Test Widget", name: "Test Widget",
data: {}, data: {},
} as unknown as IApp; } as unknown as IApp;
jest.spyOn(WidgetStore.instance, "getApps").mockReturnValue([widget]); vi.spyOn(WidgetStore.instance, "getApps").mockReturnValue([widget]);
vm = new WidgetPipViewModel({ vm = new WidgetPipViewModel({
room, room,
@@ -58,7 +60,7 @@ describe("WidgetPipViewModel", () => {
afterEach(() => { afterEach(() => {
vm.dispose(); vm.dispose();
jest.restoreAllMocks(); vi.restoreAllMocks();
}); });
it("updates room name", () => { it("updates room name", () => {
@@ -68,7 +70,7 @@ describe("WidgetPipViewModel", () => {
}); });
it("updates onBackClick if call changes", () => { it("updates onBackClick if call changes", () => {
const dispatchSpy = jest.spyOn(defaultDispatcher, "dispatch").mockImplementation(() => {}); const dispatchSpy = vi.spyOn(defaultDispatcher, "dispatch").mockImplementation(() => {});
vm.onBackClick(createBackClickEvent()); vm.onBackClick(createBackClickEvent());
expect(dispatchSpy).toHaveBeenCalledWith({ expect(dispatchSpy).toHaveBeenCalledWith({
@@ -91,8 +93,8 @@ describe("WidgetPipViewModel", () => {
}); });
it("updates onBackClick if viewingRoom changes", () => { it("updates onBackClick if viewingRoom changes", () => {
const dispatchSpy = jest.spyOn(defaultDispatcher, "dispatch").mockImplementation(() => {}); const dispatchSpy = vi.spyOn(defaultDispatcher, "dispatch").mockImplementation(() => {});
const moveSpy = jest.spyOn(WidgetLayoutStore.instance, "moveToContainer").mockImplementation(() => {}); const moveSpy = vi.spyOn(WidgetLayoutStore.instance, "moveToContainer").mockImplementation(() => {});
vm.setViewingRoom(true); vm.setViewingRoom(true);
vm.onBackClick(createBackClickEvent()); vm.onBackClick(createBackClickEvent());
@@ -10,7 +10,9 @@ import React, { type ReactElement } from "react";
// eslint-disable-next-line no-restricted-imports // eslint-disable-next-line no-restricted-imports
import { render, type RenderOptions } from "@testing-library/react"; import { render, type RenderOptions } from "@testing-library/react";
import { TooltipProvider } from "@vector-im/compound-web"; import { TooltipProvider } from "@vector-im/compound-web";
import { I18nContext } from "@element-hq/web-shared-components"; import { I18nApi, I18nContext } from "@element-hq/web-shared-components";
const i18nApi = new I18nApi();
/** /**
* Wraps the provided components in: * Wraps the provided components in:
@@ -26,7 +28,7 @@ const wrapWithStandardContexts = (Wrapper: RenderOptions["wrapper"]) => {
if (Wrapper) { if (Wrapper) {
return ( return (
<Wrapper> <Wrapper>
<I18nContext.Provider value={window.mxModuleApi.i18n}> <I18nContext.Provider value={i18nApi}>
<TooltipProvider>{children}</TooltipProvider> <TooltipProvider>{children}</TooltipProvider>
</I18nContext.Provider> </I18nContext.Provider>
</Wrapper> </Wrapper>
@@ -34,7 +36,7 @@ const wrapWithStandardContexts = (Wrapper: RenderOptions["wrapper"]) => {
} else { } else {
return ( return (
<TooltipProvider> <TooltipProvider>
<I18nContext.Provider value={window.mxModuleApi.i18n}>{children}</I18nContext.Provider> <I18nContext.Provider value={i18nApi}>{children}</I18nContext.Provider>
</TooltipProvider> </TooltipProvider>
); );
} }