Migrate batch of tests to vitest (#34111)

* Migrate batch of tests tro vitest

* Iterate

* Migrate another test

* Fix lockfile
This commit is contained in:
Michael Telatynski
2026-07-06 14:10:50 +00:00
committed by GitHub
parent d4f72dfa69
commit d5164acb50
18 changed files with 285 additions and 258 deletions
+1
View File
@@ -228,6 +228,7 @@
"typescript": "catalog:",
"util": "^0.12.5",
"vitest": "catalog:",
"vitest-canvas-mock": "^1.1.4",
"web-streams-polyfill": "^4.0.0",
"webpack": "^5.89.0",
"webpack-bundle-analyzer": "^5.0.0",
@@ -6,15 +6,15 @@ 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.
*/
import { mocked } from "jest-mock";
import { vi, describe, it, expect, beforeEach, type Mock } from "vitest";
import { type Room, type RoomMember, RoomType } from "matrix-js-sdk/src/matrix";
import { avatarUrlForRoom } from "../../src/Avatar";
import { type Media, mediaFromMxc } from "../../src/customisations/Media";
import DMRoomMap from "../../src/utils/DMRoomMap";
import { avatarUrlForRoom } from "./Avatar";
import { type Media, mediaFromMxc } from "./customisations/Media";
import DMRoomMap from "./utils/DMRoomMap";
jest.mock("../../src/customisations/Media", () => ({
mediaFromMxc: jest.fn(),
vi.mock("./customisations/Media", () => ({
mediaFromMxc: vi.fn(),
}));
const roomId = "!room:example.com";
@@ -22,31 +22,31 @@ const avatarUrl1 = "https://example.com/avatar1";
const avatarUrl2 = "https://example.com/avatar2";
describe("avatarUrlForRoom", () => {
let getThumbnailOfSourceHttp: jest.Mock;
let getThumbnailOfSourceHttp: Mock;
let room: Room;
let roomMember: RoomMember;
let dmRoomMap: DMRoomMap;
beforeEach(() => {
getThumbnailOfSourceHttp = jest.fn();
mocked(mediaFromMxc).mockImplementation((): Media => {
getThumbnailOfSourceHttp = vi.fn();
vi.mocked(mediaFromMxc).mockImplementation((): Media => {
return {
getThumbnailOfSourceHttp,
} as unknown as Media;
});
room = {
roomId,
getMxcAvatarUrl: jest.fn(),
isSpaceRoom: jest.fn(),
getType: jest.fn(),
getAvatarFallbackMember: jest.fn(),
getMxcAvatarUrl: vi.fn(),
isSpaceRoom: vi.fn(),
getType: vi.fn(),
getAvatarFallbackMember: vi.fn(),
} as unknown as Room;
dmRoomMap = {
getUserIdForRoomId: jest.fn(),
getUserIdForRoomId: vi.fn(),
} as unknown as DMRoomMap;
DMRoomMap.setShared(dmRoomMap);
roomMember = {
getMxcAvatarUrl: jest.fn(),
getMxcAvatarUrl: vi.fn(),
} as unknown as RoomMember;
});
@@ -55,40 +55,40 @@ describe("avatarUrlForRoom", () => {
});
it("should return the HTTP source if the room provides a MXC url", () => {
mocked(room.getMxcAvatarUrl).mockReturnValue(avatarUrl1);
vi.mocked(room.getMxcAvatarUrl).mockReturnValue(avatarUrl1);
getThumbnailOfSourceHttp.mockReturnValue(avatarUrl2);
expect(avatarUrlForRoom(room, 128, 256, "crop")).toEqual(avatarUrl2);
expect(getThumbnailOfSourceHttp).toHaveBeenCalledWith(128, 256, "crop");
});
it("should return null for a space room", () => {
mocked(room.isSpaceRoom).mockReturnValue(true);
mocked(room.getType).mockReturnValue(RoomType.Space);
vi.mocked(room.isSpaceRoom).mockReturnValue(true);
vi.mocked(room.getType).mockReturnValue(RoomType.Space);
expect(avatarUrlForRoom(room, 128, 128)).toBeNull();
});
it("should return null if the room is not a DM", () => {
mocked(dmRoomMap).getUserIdForRoomId.mockReturnValue(undefined);
vi.mocked(dmRoomMap).getUserIdForRoomId.mockReturnValue(undefined);
expect(avatarUrlForRoom(room, 128, 128)).toBeNull();
expect(dmRoomMap.getUserIdForRoomId).toHaveBeenCalledWith(roomId);
});
it("should return null if there is no other member in the room", () => {
mocked(dmRoomMap).getUserIdForRoomId.mockReturnValue("@user:example.com");
mocked(room.getAvatarFallbackMember).mockReturnValue(undefined);
vi.mocked(dmRoomMap).getUserIdForRoomId.mockReturnValue("@user:example.com");
vi.mocked(room.getAvatarFallbackMember).mockReturnValue(undefined);
expect(avatarUrlForRoom(room, 128, 128)).toBeNull();
});
it("should return null if the other member has no avatar URL", () => {
mocked(dmRoomMap).getUserIdForRoomId.mockReturnValue("@user:example.com");
mocked(room.getAvatarFallbackMember).mockReturnValue(roomMember);
vi.mocked(dmRoomMap).getUserIdForRoomId.mockReturnValue("@user:example.com");
vi.mocked(room.getAvatarFallbackMember).mockReturnValue(roomMember);
expect(avatarUrlForRoom(room, 128, 128)).toBeNull();
});
it("should return the other member's avatar URL", () => {
mocked(dmRoomMap).getUserIdForRoomId.mockReturnValue("@user:example.com");
mocked(room.getAvatarFallbackMember).mockReturnValue(roomMember);
mocked(roomMember.getMxcAvatarUrl).mockReturnValue(avatarUrl2);
vi.mocked(dmRoomMap).getUserIdForRoomId.mockReturnValue("@user:example.com");
vi.mocked(room.getAvatarFallbackMember).mockReturnValue(roomMember);
vi.mocked(roomMember.getMxcAvatarUrl).mockReturnValue(avatarUrl2);
getThumbnailOfSourceHttp.mockReturnValue(avatarUrl2);
expect(avatarUrlForRoom(room, 128, 256, "crop")).toEqual(avatarUrl2);
expect(getThumbnailOfSourceHttp).toHaveBeenCalledWith(128, 256, "crop");
@@ -6,7 +6,9 @@ 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.
*/
import { isKeyComboMatch, type KeyCombo } from "../../src/KeyBindingsManager";
import { describe, it, expect } from "vitest";
import { isKeyComboMatch, type KeyCombo } from "./KeyBindingsManager";
function mockKeyEvent(
key: string,
@@ -6,37 +6,34 @@ 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.
*/
import { Crypto } from "@peculiar/webcrypto";
// @vitest-environment happy-dom
import { vi, describe, it, expect, beforeEach, afterEach, type MockedObject } from "vitest";
import { logger } from "matrix-js-sdk/src/logger";
import * as MatrixJs from "matrix-js-sdk/src/matrix";
import { decodeBase64, encodeUnpaddedBase64 } from "matrix-js-sdk/src/matrix";
import * as encryptAESSecretStorageItemModule from "matrix-js-sdk/src/utils/encryptAESSecretStorageItem";
import { mocked, type MockedObject } from "jest-mock-vitest-adapter";
import fetchMock from "@fetch-mock/jest";
import fetchMock from "@fetch-mock/vitest";
import { flushPromises, getMockClientWithEventEmitter, mockClientMethodsUser, mockPlatformPeg } from "test-utils";
import { makeDelegatedAuthConfig } from "test-utils/oidc";
import StorageEvictedDialog from "../../src/components/views/dialogs/StorageEvictedDialog";
import * as Lifecycle from "../../src/Lifecycle";
import { MatrixClientPeg } from "../../src/MatrixClientPeg";
import Modal from "../../src/Modal";
import * as StorageAccess from "../../src/utils/StorageAccess";
import { idbSave } from "../../src/utils/StorageAccess";
import { flushPromises, getMockClientWithEventEmitter, mockClientMethodsUser, mockPlatformPeg } from "../test-utils";
import { OidcClientStore } from "../../src/stores/oidc/OidcClientStore";
import { makeDelegatedAuthConfig } from "../test-utils/oidc";
import { Action } from "../../src/dispatcher/actions";
import PlatformPeg from "../../src/PlatformPeg";
import { persistAccessTokenInStorage, persistRefreshTokenInStorage } from "../../src/utils/tokens/tokens";
import { encryptPickleKey } from "../../src/utils/tokens/pickling";
import * as StorageManager from "../../src/utils/StorageManager.ts";
import type BasePlatform from "../../src/BasePlatform.ts";
import * as createMatrixClientModule from "../../src/utils/createMatrixClient";
import StorageEvictedDialog from "./components/views/dialogs/StorageEvictedDialog";
import * as Lifecycle from "./Lifecycle";
import { MatrixClientPeg } from "./MatrixClientPeg";
import Modal from "./Modal";
import * as StorageAccess from "./utils/StorageAccess";
import { idbSave } from "./utils/StorageAccess";
import { OidcClientStore } from "./stores/oidc/OidcClientStore";
import { Action } from "./dispatcher/actions";
import PlatformPeg from "./PlatformPeg";
import { persistAccessTokenInStorage, persistRefreshTokenInStorage } from "./utils/tokens/tokens";
import { encryptPickleKey } from "./utils/tokens/pickling";
import * as StorageManager from "./utils/StorageManager.ts";
import type BasePlatform from "./BasePlatform.ts";
import * as createMatrixClientModule from "./utils/createMatrixClient";
const { logout, restoreSessionFromStorage, setLoggedIn } = Lifecycle;
const webCrypto = new Crypto();
const windowCrypto = window.crypto;
describe("Lifecycle", () => {
const homeserverUrl = "https://domain";
const identityServerUrl = "https://is.org";
@@ -46,65 +43,55 @@ describe("Lifecycle", () => {
let mockPlatform: MockedObject<BasePlatform>;
const realLocalStorage = global.localStorage;
let mockClient!: MockedObject<MatrixJs.MatrixClient>;
beforeEach(() => {
jest.restoreAllMocks();
mockPlatform = mockPlatformPeg();
mockClient = getMockClientWithEventEmitter({
...mockClientMethodsUser(),
stopClient: jest.fn(),
removeAllListeners: jest.fn(),
clearStores: jest.fn(),
getAccountData: jest.fn(),
getDeviceId: jest.fn().mockReturnValue(deviceId),
isVersionSupported: jest.fn().mockResolvedValue(true),
getCrypto: jest.fn(),
getClientWellKnown: jest.fn(),
waitForClientWellKnown: jest.fn(),
getThirdpartyProtocols: jest.fn(),
stopClient: vi.fn(),
removeAllListeners: vi.fn(),
clearStores: vi.fn(),
getAccountData: vi.fn(),
getDeviceId: vi.fn().mockReturnValue(deviceId),
isVersionSupported: vi.fn().mockResolvedValue(true),
getCrypto: vi.fn(),
getClientWellKnown: vi.fn(),
waitForClientWellKnown: vi.fn(),
getThirdpartyProtocols: vi.fn(),
store: {
destroy: jest.fn(),
destroy: vi.fn(),
},
getVersions: jest.fn().mockResolvedValue({ versions: ["v1.1"] }),
logout: jest.fn().mockResolvedValue(undefined),
getAccessToken: jest.fn(),
getRefreshToken: jest.fn(),
setGuest: jest.fn(),
setNotifTimelineSet: jest.fn(),
getVersions: vi.fn().mockResolvedValue({ versions: ["v1.1"] }),
logout: vi.fn().mockResolvedValue(undefined),
getAccessToken: vi.fn(),
getRefreshToken: vi.fn(),
isInitialSyncComplete: vi.fn(),
setGuest: vi.fn(),
setNotifTimelineSet: vi.fn(),
});
// stub this
jest.spyOn(MatrixClientPeg, "set").mockImplementation(() => {});
jest.spyOn(MatrixClientPeg, "start").mockResolvedValue(undefined);
vi.spyOn(MatrixClientPeg, "set").mockImplementation(() => {});
vi.spyOn(MatrixClientPeg, "start").mockResolvedValue(undefined);
// reset any mocking
// @ts-ignore mocking
delete global.localStorage;
global.localStorage = realLocalStorage;
vi.spyOn(encryptAESSecretStorageItemModule, "default").mockRestore();
// @ts-ignore mocking
delete window.crypto;
window.crypto = webCrypto;
jest.spyOn(encryptAESSecretStorageItemModule, "default").mockRestore();
localStorage.clear();
sessionStorage.clear();
});
afterAll(() => {
// @ts-ignore unmocking
delete window.crypto;
window.crypto = windowCrypto;
afterEach(() => {
vi.resetAllMocks();
});
const initIdbMock = (mockStore: Record<string, Record<string, unknown>> = {}): void => {
jest.spyOn(StorageAccess, "idbLoad")
vi.spyOn(StorageAccess, "idbLoad")
.mockClear()
.mockImplementation(
// @ts-ignore mock type
async (table: string, key: string) => mockStore[table]?.[key] ?? null,
);
jest.spyOn(StorageAccess, "idbSave")
vi.spyOn(StorageAccess, "idbSave")
.mockClear()
.mockImplementation(
// @ts-ignore mock type
@@ -114,13 +101,13 @@ describe("Lifecycle", () => {
mockStore[tableKey] = table;
},
);
jest.spyOn(StorageAccess, "idbDelete")
vi.spyOn(StorageAccess, "idbDelete")
.mockClear()
.mockImplementation(async (tableKey: string, key: string | string[]) => {
const table = mockStore[tableKey];
delete table?.[key as string];
});
jest.spyOn(StorageAccess, "idbClear")
vi.spyOn(StorageAccess, "idbClear")
.mockClear()
.mockImplementation(async (tableKey: string) => {
mockStore[tableKey] = {};
@@ -159,14 +146,14 @@ describe("Lifecycle", () => {
describe("loadSession", () => {
beforeEach(() => {
// stub this out
jest.spyOn(Modal, "createDialog").mockReturnValue(
vi.spyOn(Modal, "createDialog").mockReturnValue(
// @ts-ignore allow bad mock
{ finished: Promise.resolve([true]) },
);
});
it("should not show any error dialog when checkConsistency throws but abortSignal has triggered", async () => {
jest.spyOn(StorageManager, "checkConsistency").mockRejectedValue(new Error("test error"));
vi.spyOn(StorageManager, "checkConsistency").mockRejectedValue(new Error("test error"));
const abortController = new AbortController();
const prom = Lifecycle.loadSession({
@@ -183,27 +170,29 @@ describe("Lifecycle", () => {
});
describe("restoreSessionFromStorage()", () => {
const realLocalStorage = localStorage;
beforeEach(() => {
initIdbMock();
jest.clearAllMocks();
jest.spyOn(logger, "log").mockClear();
vi.spyOn(logger, "log").mockClear();
jest.spyOn(MatrixJs, "createClient").mockReturnValue(mockClient);
jest.spyOn(createMatrixClientModule, "createClientWithCreds").mockReturnValue(mockClient);
vi.spyOn(MatrixJs, "createClient").mockReturnValue(mockClient);
vi.spyOn(createMatrixClientModule, "createClientWithCreds").mockReturnValue(mockClient);
// stub this out
jest.spyOn(Modal, "createDialog").mockReturnValue(
vi.spyOn(Modal, "createDialog").mockReturnValue(
// @ts-ignore allow bad mock
{ finished: Promise.resolve([true]) },
);
});
afterEach(() => {
vi.stubGlobal("localStorage", realLocalStorage);
});
it("should return false when localStorage is not available", async () => {
// @ts-ignore dirty mocking
delete global.localStorage;
// @ts-ignore dirty mocking
global.localStorage = undefined;
vi.stubGlobal("localStorage", undefined);
expect(await restoreSessionFromStorage()).toEqual(false);
});
@@ -272,7 +261,7 @@ describe("Lifecycle", () => {
});
it("should persist access token when idb is not available", async () => {
jest.spyOn(StorageAccess, "idbSave").mockRejectedValue("oups");
vi.spyOn(StorageAccess, "idbSave").mockRejectedValue("oups");
expect(await restoreSessionFromStorage()).toEqual(true);
expect(StorageAccess.idbSave).toHaveBeenCalledWith("account", "mx_access_token", accessToken);
@@ -384,7 +373,7 @@ describe("Lifecycle", () => {
it("should persist access token when idb is not available", async () => {
// dont fail for pickle key persist
jest.spyOn(StorageAccess, "idbSave").mockImplementation(
vi.spyOn(StorageAccess, "idbSave").mockImplementation(
async (table: string, key: string | string[]) => {
if (table === "account" && key === "mx_access_token") {
throw new Error("oups");
@@ -406,7 +395,7 @@ describe("Lifecycle", () => {
it("should create and start new matrix client with credentials", async () => {
// Check that the rust crypto key is as expected. We have to do this during the call, as
// the buffer is cleared afterwards.
mocked(MatrixClientPeg.start).mockImplementation(async (opts) => {
vi.mocked(MatrixClientPeg.start).mockImplementation(async (opts) => {
expect(opts?.rustCryptoStoreKey).toEqual(decodeBase64(pickleKey));
});
@@ -559,14 +548,14 @@ describe("Lifecycle", () => {
beforeEach(() => {
initIdbMock();
jest.clearAllMocks();
jest.spyOn(logger, "log").mockClear();
vi.clearAllMocks();
vi.spyOn(logger, "log").mockClear();
jest.spyOn(MatrixJs, "createClient").mockReturnValue(mockClient);
vi.spyOn(MatrixJs, "createClient").mockReturnValue(mockClient);
// remove any mock implementations
jest.spyOn(mockPlatform, "createPickleKey").mockRestore();
vi.spyOn(mockPlatform, "createPickleKey").mockRestore();
// but still spy and call through
jest.spyOn(mockPlatform, "createPickleKey");
vi.spyOn(mockPlatform, "createPickleKey");
});
const refreshToken = "test-refresh-token";
@@ -617,8 +606,8 @@ describe("Lifecycle", () => {
describe("without a pickle key", () => {
beforeEach(() => {
jest.spyOn(mockPlatform, "createPickleKey").mockResolvedValue(null);
jest.spyOn(createMatrixClientModule, "createClientWithCreds").mockReturnValue(mockClient);
vi.spyOn(mockPlatform, "createPickleKey").mockResolvedValue(null);
vi.spyOn(createMatrixClientModule, "createClientWithCreds").mockReturnValue(mockClient);
});
it("should persist credentials", async () => {
@@ -650,7 +639,7 @@ describe("Lifecycle", () => {
});
it("should remove any access token from storage when there is none in credentials and idb save fails", async () => {
jest.spyOn(StorageAccess, "idbSave").mockRejectedValue("oups");
vi.spyOn(StorageAccess, "idbSave").mockRejectedValue("oups");
await setLoggedIn({
...credentials,
// @ts-ignore
@@ -726,7 +715,7 @@ describe("Lifecycle", () => {
});
it("should persist token when encrypting the token fails", async () => {
jest.spyOn(encryptAESSecretStorageItemModule, "default").mockRejectedValue("MOCK REJECT ENCRYPTAES");
vi.spyOn(encryptAESSecretStorageItemModule, "default").mockRejectedValue("MOCK REJECT ENCRYPTAES");
await setLoggedIn(credentials);
// persist the unencrypted token
@@ -735,13 +724,11 @@ describe("Lifecycle", () => {
it("should persist token in localStorage when idb fails to save token", async () => {
// dont fail for pickle key persist
jest.spyOn(StorageAccess, "idbSave").mockImplementation(
async (table: string, key: string | string[]) => {
if (table === "account" && key === "mx_access_token") {
throw new Error("oups");
}
},
);
vi.spyOn(StorageAccess, "idbSave").mockImplementation(async (table: string, key: string | string[]) => {
if (table === "account" && key === "mx_access_token") {
throw new Error("oups");
}
});
await setLoggedIn(credentials);
// put plain accessToken in localstorage when we dont have idb
@@ -750,13 +737,11 @@ describe("Lifecycle", () => {
it("should remove any access token from storage when there is none in credentials and idb save fails", async () => {
// dont fail for pickle key persist
jest.spyOn(StorageAccess, "idbSave").mockImplementation(
async (table: string, key: string | string[]) => {
if (table === "account" && key === "mx_access_token") {
throw new Error("oups");
}
},
);
vi.spyOn(StorageAccess, "idbSave").mockImplementation(async (table: string, key: string | string[]) => {
if (table === "account" && key === "mx_access_token") {
throw new Error("oups");
}
});
await setLoggedIn({
...credentials,
// @ts-ignore
@@ -768,7 +753,7 @@ describe("Lifecycle", () => {
});
it("should create new matrix client with credentials", async () => {
jest.spyOn(createMatrixClientModule, "createClientWithCreds").mockReturnValue(mockClient);
vi.spyOn(createMatrixClientModule, "createClientWithCreds").mockReturnValue(mockClient);
expect(await setLoggedIn(credentials)).toEqual(mockClient);
expect(createMatrixClientModule.createClientWithCreds).toHaveBeenCalledWith(
@@ -903,7 +888,7 @@ describe("Lifecycle", () => {
beforeEach(() => {
oidcClientStore = new OidcClientStore(mockClient);
// stub
jest.spyOn(oidcClientStore, "revokeTokens").mockResolvedValue(undefined);
vi.spyOn(oidcClientStore, "revokeTokens").mockResolvedValue(undefined);
mockClient.getAccessToken.mockReturnValue(accessToken);
mockClient.getRefreshToken.mockReturnValue(refreshToken);
@@ -918,7 +903,7 @@ describe("Lifecycle", () => {
});
it("should call logout on the client when oidcClientStore.isUserAuthenticatedWithOidc is falsy", async () => {
jest.spyOn(oidcClientStore, "isUserAuthenticatedWithOidc", "get").mockReturnValue(false);
vi.spyOn(oidcClientStore, "isUserAuthenticatedWithOidc", "get").mockReturnValue(false);
logout(oidcClientStore);
await flushPromises();
@@ -928,7 +913,7 @@ describe("Lifecycle", () => {
});
it("should revoke tokens when user is authenticated with oidc", async () => {
jest.spyOn(oidcClientStore, "isUserAuthenticatedWithOidc", "get").mockReturnValue(true);
vi.spyOn(oidcClientStore, "isUserAuthenticatedWithOidc", "get").mockReturnValue(true);
logout(oidcClientStore);
await flushPromises();
@@ -940,12 +925,12 @@ describe("Lifecycle", () => {
describe("overwritelogin", () => {
beforeEach(async () => {
jest.spyOn(MatrixJs, "createClient").mockReturnValue(mockClient);
vi.spyOn(MatrixJs, "createClient").mockReturnValue(mockClient);
});
it("should replace the current login with a new one", async () => {
const stopSpy = jest.spyOn(mockClient, "stopClient").mockReturnValue(undefined);
jest.spyOn(createMatrixClientModule, "createClientWithCreds").mockReturnValue(mockClient);
const stopSpy = vi.spyOn(mockClient, "stopClient").mockReturnValue(undefined);
vi.spyOn(createMatrixClientModule, "createClientWithCreds").mockReturnValue(mockClient);
const dis = window.mxDispatcher;
const firstLoginEvent: Promise<void> = new Promise((resolve) => {
@@ -963,7 +948,7 @@ describe("Lifecycle", () => {
expect(stopSpy).toHaveBeenCalledTimes(1);
// important the overwrite action should not call unset before replacing.
// So spy on it and make sure it's not called.
jest.spyOn(MatrixClientPeg, "unset").mockReturnValue(undefined);
vi.spyOn(MatrixClientPeg, "unset").mockReturnValue(undefined);
expect(createMatrixClientModule.createClientWithCreds).toHaveBeenCalledWith(
expect.objectContaining({
@@ -6,7 +6,9 @@ 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.
*/
import Markdown from "../../src/Markdown";
import { describe, it, expect } from "vitest";
import Markdown from "./Markdown";
describe("Markdown parser test", () => {
describe("fixing HTML links", () => {
@@ -7,41 +7,38 @@ 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.
*/
import { mocked } from "jest-mock";
// @vitest-environment happy-dom
import { vi, describe, it, expect, beforeEach, afterEach } from "vitest";
import { type PostHog } from "posthog-js";
import { type MatrixClient } from "matrix-js-sdk/src/matrix";
import { type CryptoApi } from "matrix-js-sdk/src/crypto-api";
import { getMockClientWithEventEmitter } from "test-utils";
import {
Anonymity,
getRedactedCurrentLocation,
type IPosthogEvent,
PosthogAnalytics,
} from "../../src/PosthogAnalytics";
import SdkConfig from "../../src/SdkConfig";
import { getMockClientWithEventEmitter } from "../test-utils";
import SettingsStore from "../../src/settings/SettingsStore";
import { Layout } from "../../src/settings/enums/Layout";
import defaultDispatcher from "../../src/dispatcher/dispatcher";
import { Action } from "../../src/dispatcher/actions";
import { SettingLevel } from "../../src/settings/SettingLevel";
import { Anonymity, getRedactedCurrentLocation, type IPosthogEvent, PosthogAnalytics } from "./PosthogAnalytics";
import SdkConfig from "./SdkConfig";
import SettingsStore from "./settings/SettingsStore";
import { Layout } from "./settings/enums/Layout";
import defaultDispatcher from "./dispatcher/dispatcher";
import { Action } from "./dispatcher/actions";
import { SettingLevel } from "./settings/SettingLevel";
const getFakePosthog = (): PostHog =>
({
capture: jest.fn(),
init: jest.fn(),
identify: jest.fn(),
reset: jest.fn(),
register: jest.fn(),
get_distinct_id: jest.fn(),
capture: vi.fn(),
init: vi.fn(),
identify: vi.fn(),
reset: vi.fn(),
register: vi.fn(),
get_distinct_id: vi.fn(),
persistence: {
get_property: jest.fn(),
get_property: vi.fn(),
},
identifyUser: jest.fn(),
identifyUser: vi.fn(),
}) as unknown as PostHog;
interface ITestEvent extends IPosthogEvent {
eventName: "JestTestEvents";
eventName: "TestEvents";
foo?: string;
}
@@ -121,17 +118,17 @@ describe("PosthogAnalytics", () => {
it("Should pass event to posthog", () => {
analytics.setAnonymity(Anonymity.Pseudonymous);
analytics.trackEvent<ITestEvent>({
eventName: "JestTestEvents",
eventName: "TestEvents",
foo: "bar",
});
expect(mocked(fakePosthog).capture.mock.calls[0][0]).toBe("JestTestEvents");
expect(mocked(fakePosthog).capture.mock.calls[0][1]!["foo"]).toEqual("bar");
expect(vi.mocked(fakePosthog).capture.mock.calls[0][0]).toBe("TestEvents");
expect(vi.mocked(fakePosthog).capture.mock.calls[0][1]!["foo"]).toEqual("bar");
});
it("Should not track events if anonymous", async () => {
analytics.setAnonymity(Anonymity.Anonymous);
await analytics.trackEvent<ITestEvent>({
eventName: "JestTestEvents",
eventName: "TestEvents",
foo: "bar",
});
expect(fakePosthog.capture).not.toHaveBeenCalled();
@@ -140,7 +137,7 @@ describe("PosthogAnalytics", () => {
it("Should not track any events if disabled", async () => {
analytics.setAnonymity(Anonymity.Disabled);
analytics.trackEvent<ITestEvent>({
eventName: "JestTestEvents",
eventName: "TestEvents",
foo: "bar",
});
expect(fakePosthog.capture).not.toHaveBeenCalled();
@@ -164,29 +161,29 @@ describe("PosthogAnalytics", () => {
it("Should identify the user to posthog if pseudonymous", async () => {
analytics.setAnonymity(Anonymity.Pseudonymous);
const client = getMockClientWithEventEmitter({
getAccountDataFromServer: jest.fn().mockResolvedValue(null),
setAccountData: jest.fn().mockResolvedValue({}),
getAccountDataFromServer: vi.fn().mockResolvedValue(null),
setAccountData: vi.fn().mockResolvedValue({}),
});
await analytics.identifyUser(client, () => "analytics_id");
expect(mocked(fakePosthog).identify.mock.calls[0][0]).toBe("analytics_id");
expect(vi.mocked(fakePosthog).identify.mock.calls[0][0]).toBe("analytics_id");
});
it("Should not identify the user to posthog if anonymous", async () => {
analytics.setAnonymity(Anonymity.Anonymous);
const client = getMockClientWithEventEmitter({});
await analytics.identifyUser(client, () => "analytics_id");
expect(mocked(fakePosthog).identify.mock.calls.length).toBe(0);
expect(vi.mocked(fakePosthog).identify.mock.calls.length).toBe(0);
});
it("Should identify using the server's analytics id if present", async () => {
analytics.setAnonymity(Anonymity.Pseudonymous);
const client = getMockClientWithEventEmitter({
getAccountDataFromServer: jest.fn().mockResolvedValue({ id: "existing_analytics_id" }),
setAccountData: jest.fn().mockResolvedValue({}),
getAccountDataFromServer: vi.fn().mockResolvedValue({ id: "existing_analytics_id" }),
setAccountData: vi.fn().mockResolvedValue({}),
});
await analytics.identifyUser(client, () => "new_analytics_id");
expect(mocked(fakePosthog).identify.mock.calls[0][0]).toBe("existing_analytics_id");
expect(vi.mocked(fakePosthog).identify.mock.calls[0][0]).toBe("existing_analytics_id");
});
});
@@ -220,9 +217,9 @@ describe("PosthogAnalytics", () => {
true,
);
analytics.trackEvent<ITestEvent>({
eventName: "JestTestEvents",
eventName: "TestEvents",
});
expect(mocked(fakePosthog).capture.mock.calls[0][1]!["$set"]).toMatchObject({
expect(vi.mocked(fakePosthog).capture.mock.calls[0][1]!["$set"]).toMatchObject({
WebLayout: "IRC",
});
});
@@ -237,9 +234,9 @@ describe("PosthogAnalytics", () => {
true,
);
analytics.trackEvent<ITestEvent>({
eventName: "JestTestEvents",
eventName: "TestEvents",
});
expect(mocked(fakePosthog).capture.mock.calls[0][1]!["$set"]).toMatchObject({
expect(vi.mocked(fakePosthog).capture.mock.calls[0][1]!["$set"]).toMatchObject({
WebLayout: "Bubble",
});
});
@@ -254,9 +251,9 @@ describe("PosthogAnalytics", () => {
true,
);
analytics.trackEvent<ITestEvent>({
eventName: "JestTestEvents",
eventName: "TestEvents",
});
expect(mocked(fakePosthog).capture.mock.calls[0][1]!["$set"]).toMatchObject({
expect(vi.mocked(fakePosthog).capture.mock.calls[0][1]!["$set"]).toMatchObject({
WebLayout: "Group",
});
});
@@ -272,10 +269,10 @@ describe("PosthogAnalytics", () => {
true,
);
analytics.trackEvent<ITestEvent>({
eventName: "JestTestEvents",
eventName: "TestEvents",
});
console.log(mocked(fakePosthog).capture.mock.calls[0]);
expect(mocked(fakePosthog).capture.mock.calls[0][1]!["$set"]).toMatchObject({
console.log(vi.mocked(fakePosthog).capture.mock.calls[0]);
expect(vi.mocked(fakePosthog).capture.mock.calls[0][1]!["$set"]).toMatchObject({
WebLayout: "Compact",
});
});
@@ -311,9 +308,9 @@ describe("PosthogAnalytics", () => {
true,
);
analytics.trackEvent<ITestEvent>({
eventName: "JestTestEvents",
eventName: "TestEvents",
});
expect(mocked(fakePosthog).capture.mock.calls[0][1]!["$set"]).toMatchObject({
expect(vi.mocked(fakePosthog).capture.mock.calls[0][1]!["$set"]).toMatchObject({
URLPreviewsEnabled: true,
});
});
@@ -323,10 +320,10 @@ describe("PosthogAnalytics", () => {
let analytics: PosthogAnalytics;
const getFakeClient = (): MatrixClient =>
({
getCrypto: jest.fn(),
setAccountData: jest.fn(),
getCrypto: vi.fn(),
setAccountData: vi.fn(),
// just fake return an `im.vector.analytics` content
getAccountDataFromServer: jest.fn().mockReturnValue({
getAccountDataFromServer: vi.fn().mockReturnValue({
id: "0000000",
pseudonymousAnalyticsOptIn: true,
}),
@@ -350,7 +347,7 @@ describe("PosthogAnalytics", () => {
// To simulate a switch we call updateAnonymityFromSettings.
// As per documentation this function is called On login.
const mockClient = getFakeClient();
mocked(mockClient.getCrypto).mockReturnValue({
vi.mocked(mockClient.getCrypto).mockReturnValue({
getVersion: () => {
return rustBackend ? "Rust SDK 0.6.0 (9c6b550), Vodozemac 0.5.0" : "Olm 3.2.0";
},
@@ -363,7 +360,7 @@ describe("PosthogAnalytics", () => {
await simulateLogin(false);
expect(mocked(fakePosthog).register.mock.lastCall![0]["cryptoSDK"]).toStrictEqual("Legacy");
expect(vi.mocked(fakePosthog).register.mock.lastCall![0]["cryptoSDK"]).toStrictEqual("Legacy");
});
it("should send Legacy cryptoSDK superProperty correctly", async () => {
@@ -373,7 +370,7 @@ describe("PosthogAnalytics", () => {
// Super Properties are properties associated with events that are set once and then sent with every capture call.
// They are set using posthog.register
expect(mocked(fakePosthog).register.mock.lastCall![0]["cryptoSDK"]).toStrictEqual("Legacy");
expect(vi.mocked(fakePosthog).register.mock.lastCall![0]["cryptoSDK"]).toStrictEqual("Legacy");
});
it("should send cryptoSDK superProperty when enabling analytics", async () => {
@@ -383,13 +380,13 @@ describe("PosthogAnalytics", () => {
// This initial call is due to the call to register platformSuperProperties
// The important thing is that the cryptoSDK superProperty is not set.
expect(mocked(fakePosthog).register.mock.lastCall![0]).toStrictEqual({});
expect(vi.mocked(fakePosthog).register.mock.lastCall![0]).toStrictEqual({});
// switching to pseudonymous should ensure that the cryptoSDK superProperty is set correctly
analytics.setAnonymity(Anonymity.Pseudonymous);
// Super Properties are properties associated with events that are set once and then sent with every capture call.
// They are set using posthog.register
expect(mocked(fakePosthog).register.mock.lastCall![0]["cryptoSDK"]).toStrictEqual("Rust");
expect(vi.mocked(fakePosthog).register.mock.lastCall![0]["cryptoSDK"]).toStrictEqual("Rust");
});
});
});
@@ -5,16 +5,20 @@ 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.
*/
import { PosthogAnalytics } from "../../src/PosthogAnalytics";
import PosthogTrackers from "../../src/PosthogTrackers";
// @vitest-environment happy-dom
import { vi, describe, it, expect, afterEach } from "vitest";
import { PosthogAnalytics } from "./PosthogAnalytics";
import PosthogTrackers from "./PosthogTrackers";
describe("PosthogTrackers", () => {
afterEach(() => {
jest.resetAllMocks();
vi.resetAllMocks();
});
it("tracks URL Previews", () => {
jest.spyOn(PosthogAnalytics.instance, "trackEvent");
vi.spyOn(PosthogAnalytics.instance, "trackEvent");
const tracker = new PosthogTrackers();
tracker.trackUrlPreview("$123456", false, [
{
@@ -6,7 +6,9 @@ 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.
*/
import { mocked } from "jest-mock";
// @vitest-environment happy-dom
import { vi, describe, it, expect, beforeEach, type Mocked } from "vitest";
import {
PushRuleActionName,
TweakName,
@@ -19,19 +21,19 @@ import {
type MatrixClient,
} from "matrix-js-sdk/src/matrix";
import { KnownMembership } from "matrix-js-sdk/src/types";
import { mkEvent, mkRoom, mkRoomMember, muteRoom, stubClient, upsertRoomStateEvents } from "test-utils";
import { mkThread } from "test-utils/threads";
import { mkEvent, mkRoom, mkRoomMember, muteRoom, stubClient, upsertRoomStateEvents } from "../test-utils";
import {
getRoomNotifsState,
RoomNotifState,
getUnreadNotificationCount,
determineUnreadState,
getUnsentMessages,
} from "../../src/RoomNotifs";
import { NotificationLevel } from "../../src/stores/notifications/NotificationLevel";
import SettingsStore from "../../src/settings/SettingsStore";
import { MatrixClientPeg } from "../../src/MatrixClientPeg";
import { mkThread } from "../test-utils/threads";
} from "./RoomNotifs";
import { NotificationLevel } from "./stores/notifications/NotificationLevel";
import SettingsStore from "./settings/SettingsStore";
import { MatrixClientPeg } from "./MatrixClientPeg";
describe("getUnsentMessages", () => {
const ROOM_ID = "!roomId";
@@ -95,14 +97,14 @@ describe("getUnsentMessages", () => {
});
describe("RoomNotifs test", () => {
let client: jest.Mocked<MatrixClient>;
let client: Mocked<MatrixClient>;
beforeEach(() => {
client = stubClient() as jest.Mocked<MatrixClient>;
client = stubClient() as Mocked<MatrixClient>;
});
it("getRoomNotifsState handles rules with no conditions", () => {
mocked(client).pushRules = {
vi.mocked(client).pushRules = {
global: {
override: [
{
@@ -118,7 +120,7 @@ describe("RoomNotifs test", () => {
});
it("getRoomNotifsState handles guest users", () => {
mocked(client).isGuest.mockReturnValue(true);
vi.mocked(client).isGuest.mockReturnValue(true);
expect(getRoomNotifsState(client, "!roomId:server")).toBe(RoomNotifState.AllMessages);
});
@@ -258,7 +260,7 @@ describe("RoomNotifs test", () => {
describe("and dynamic room predecessors are enabled", () => {
beforeEach(() => {
jest.spyOn(SettingsStore, "getValue").mockImplementation(
vi.spyOn(SettingsStore, "getValue").mockImplementation(
(settingName) => settingName === "feature_dynamic_room_predecessors",
);
});
@@ -343,7 +345,7 @@ describe("RoomNotifs test", () => {
});
it("indicates the user knock has been denied", async () => {
jest.spyOn(SettingsStore, "getValue").mockImplementation((name) => {
vi.spyOn(SettingsStore, "getValue").mockImplementation((name) => {
return name === "feature_ask_to_join";
});
const roomMember = mkRoomMember(
@@ -355,7 +357,7 @@ describe("RoomNotifs test", () => {
membership: KnownMembership.Knock,
},
);
jest.spyOn(room, "getMember").mockReturnValue(roomMember);
vi.spyOn(room, "getMember").mockReturnValue(roomMember);
const { level, symbol, count } = determineUnreadState(room);
expect(symbol).toBe("!");
@@ -6,11 +6,13 @@ 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.
*/
import { mocked } from "jest-mock";
import { EventType, type MatrixClient, type MatrixEvent } from "matrix-js-sdk/src/matrix";
// @vitest-environment happy-dom
import { setDMRoom } from "../../src/Rooms";
import { mkEvent, stubClient } from "../test-utils";
import { vi, describe, it, expect, beforeEach } from "vitest";
import { EventType, type MatrixClient, type MatrixEvent } from "matrix-js-sdk/src/matrix";
import { mkEvent, stubClient } from "test-utils";
import { setDMRoom } from "./Rooms";
describe("setDMRoom", () => {
const userId1 = "@user1:example.com";
@@ -23,8 +25,8 @@ describe("setDMRoom", () => {
let client: MatrixClient;
beforeEach(() => {
client = mocked(stubClient());
client.getAccountData = jest.fn().mockImplementation((eventType: string): MatrixEvent | undefined => {
client = vi.mocked(stubClient());
client.getAccountData = vi.fn().mockImplementation((eventType: string): MatrixEvent | undefined => {
if (eventType === EventType.Direct) {
return mkEvent({
event: true,
@@ -43,7 +45,7 @@ describe("setDMRoom", () => {
describe("when logged in as a guest and marking a room as DM", () => {
beforeEach(() => {
mocked(client.isGuest).mockReturnValue(true);
vi.mocked(client.isGuest).mockReturnValue(true);
setDMRoom(client, roomId1, userId1);
});
@@ -94,7 +96,7 @@ describe("setDMRoom", () => {
describe("when the direct event is undefined", () => {
beforeEach(() => {
mocked(client.getAccountData).mockReturnValue(undefined);
vi.mocked(client.getAccountData).mockReturnValue(undefined);
setDMRoom(client, roomId1, userId1);
});
@@ -108,8 +110,8 @@ describe("setDMRoom", () => {
describe("when the current content is undefined", () => {
beforeEach(() => {
// @ts-ignore
mocked(client.getAccountData).mockReturnValue({
getContent: jest.fn(),
vi.mocked(client.getAccountData).mockReturnValue({
getContent: vi.fn(),
});
setDMRoom(client, roomId1, userId1);
});
@@ -6,14 +6,16 @@ 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.
*/
import { mocked } from "jest-mock";
import fetchMock from "@fetch-mock/jest";
import { type MatrixClient } from "matrix-js-sdk/src/matrix";
// @vitest-environment happy-dom
import ScalarAuthClient from "../../src/ScalarAuthClient";
import { stubClient } from "../test-utils";
import SdkConfig from "../../src/SdkConfig";
import { WidgetType } from "../../src/widgets/WidgetType";
import { vi, describe, it, expect, beforeEach } from "vitest";
import fetchMock from "@fetch-mock/vitest";
import { type MatrixClient } from "matrix-js-sdk/src/matrix";
import { stubClient } from "test-utils";
import ScalarAuthClient from "./ScalarAuthClient";
import SdkConfig from "./SdkConfig";
import { WidgetType } from "./widgets/WidgetType";
describe("ScalarAuthClient", function () {
const apiUrl = "https://test.com/api";
@@ -27,7 +29,7 @@ describe("ScalarAuthClient", function () {
let client: MatrixClient;
beforeEach(function () {
jest.clearAllMocks();
vi.clearAllMocks();
client = stubClient();
});
@@ -42,9 +44,9 @@ describe("ScalarAuthClient", function () {
body: { user_id: client.getUserId() },
});
client.getOpenIdToken = jest.fn().mockResolvedValue(tokenObject);
client.getOpenIdToken = vi.fn().mockResolvedValue(tokenObject);
sac.exchangeForScalarToken = jest.fn((arg) => {
sac.exchangeForScalarToken = vi.fn((arg) => {
return Promise.resolve(arg === tokenObject ? "wokentoken" : "othertoken");
});
@@ -91,13 +93,13 @@ describe("ScalarAuthClient", function () {
describe("registerForToken", () => {
it("should call `termsInteractionCallback` upon M_TERMS_NOT_SIGNED error", async () => {
const sac = new ScalarAuthClient(apiUrl + 4, uiUrl);
const termsInteractionCallback = jest.fn();
const termsInteractionCallback = vi.fn();
sac.setTermsInteractionCallback(termsInteractionCallback);
fetchMock.get("https://test.com/api4/account?scalar_token=testtoken1&v=1.1", {
body: { errcode: "M_TERMS_NOT_SIGNED" },
});
sac.exchangeForScalarToken = jest.fn(() => Promise.resolve("testtoken1"));
mocked(client.getTerms).mockResolvedValue({ policies: {} });
sac.exchangeForScalarToken = vi.fn(() => Promise.resolve("testtoken1"));
vi.mocked(client.getTerms).mockResolvedValue({ policies: {} });
await expect(sac.registerForToken()).resolves.toBe("testtoken1");
});
@@ -108,7 +110,7 @@ describe("ScalarAuthClient", function () {
body: { errcode: "SERVER_IS_SAD" },
status: 500,
});
sac.exchangeForScalarToken = jest.fn(() => Promise.resolve("testtoken2"));
sac.exchangeForScalarToken = vi.fn(() => Promise.resolve("testtoken2"));
await expect(sac.registerForToken()).rejects.toBeTruthy();
});
@@ -118,7 +120,7 @@ describe("ScalarAuthClient", function () {
fetchMock.get("https://test.com/api6/account?scalar_token=testtoken3&v=1.1", {
body: {},
});
sac.exchangeForScalarToken = jest.fn(() => Promise.resolve("testtoken3"));
sac.exchangeForScalarToken = vi.fn(() => Promise.resolve("testtoken3"));
await expect(sac.registerForToken()).rejects.toThrow("Missing user_id in response");
});
@@ -6,7 +6,9 @@ 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.
*/
import SdkConfig, { DEFAULTS } from "../../src/SdkConfig";
import { describe, it, expect, beforeEach } from "vitest";
import SdkConfig, { DEFAULTS } from "./SdkConfig";
describe("SdkConfig", () => {
describe("with default values", () => {
@@ -6,17 +6,20 @@ 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.
*/
// @vitest-environment happy-dom
import { vi, describe, it, expect, beforeEach } from "vitest";
import { logger } from "matrix-js-sdk/src/logger";
import { getBrowserSupport, checkBrowserSupport, LOCAL_STORAGE_KEY } from "../../src/SupportedBrowser";
import ToastStore from "../../src/stores/ToastStore";
import GenericToast from "../../src/components/views/toasts/GenericToast";
import { getBrowserSupport, checkBrowserSupport, LOCAL_STORAGE_KEY } from "./SupportedBrowser";
import ToastStore from "./stores/ToastStore";
import GenericToast from "./components/views/toasts/GenericToast";
jest.mock("matrix-js-sdk/src/logger");
vi.mock("matrix-js-sdk/src/logger");
describe("SupportedBrowser", () => {
beforeEach(() => {
jest.resetAllMocks();
vi.resetAllMocks();
localStorage.clear();
getBrowserSupport.clear();
});
@@ -24,8 +27,8 @@ describe("SupportedBrowser", () => {
const testUserAgentFactory =
(expectedWarning?: string) =>
async (userAgent: string): Promise<void> => {
const toastSpy = jest.spyOn(ToastStore.sharedInstance(), "addOrReplaceToast");
const warnLogSpy = jest.spyOn(logger, "warn");
const toastSpy = vi.spyOn(ToastStore.sharedInstance(), "addOrReplaceToast");
const warnLogSpy = vi.spyOn(logger, "warn");
Object.defineProperty(window, "navigator", { value: { userAgent: userAgent }, writable: true });
checkBrowserSupport();
if (expectedWarning) {
@@ -91,8 +94,8 @@ describe("SupportedBrowser", () => {
);
it("should not warn for unsupported browser if user accepted already", async () => {
const toastSpy = jest.spyOn(ToastStore.sharedInstance(), "addOrReplaceToast");
const warnLogSpy = jest.spyOn(logger, "warn");
const toastSpy = vi.spyOn(ToastStore.sharedInstance(), "addOrReplaceToast");
const warnLogSpy = vi.spyOn(logger, "warn");
const userAgent =
"Mozilla/5.0 (X11; CrOS x86_64 15633.69.0) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/119.0.6045.212 Safari/537.36";
Object.defineProperty(window, "navigator", { value: { userAgent: userAgent }, writable: true });
@@ -8,7 +8,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.
*/
import * as tzh from "../../src/TimezoneHandler";
// @vitest-environment happy-dom
import { describe, it, expect } from "vitest";
import * as tzh from "./TimezoneHandler";
describe("TimezoneHandler", () => {
it("should support setting a user timezone", async () => {
@@ -1,6 +1,6 @@
// Jest Snapshot v1, https://jestjs.io/docs/snapshot-testing
// Vitest Snapshot v1, https://vitest.dev/guide/snapshot.html
exports[`Favicon should clear a badge if called with a zero value 1`] = `
exports[`Favicon > should clear a badge if called with a zero value 1`] = `
[
{
"props": {
@@ -236,7 +236,7 @@ exports[`Favicon should clear a badge if called with a zero value 1`] = `
]
`;
exports[`Favicon should draw a badge if called with a non-zero value 1`] = `
exports[`Favicon > should draw a badge if called with a non-zero value 1`] = `
[
{
"props": {
@@ -6,15 +6,18 @@ 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.
*/
import "jest-canvas-mock";
// @vitest-environment happy-dom
import Favicon from "../../src/favicon";
import { vi, describe, it, expect, beforeEach } from "vitest";
import "vitest-canvas-mock";
jest.useFakeTimers();
import Favicon from "./favicon";
vi.useFakeTimers();
describe("Favicon", () => {
beforeEach(() => {
jest.restoreAllMocks();
vi.restoreAllMocks();
document.getElementsByTagName("head")[0]?.remove();
const head = document.createElement("head");
window.document.documentElement.prepend(head);
@@ -30,14 +33,14 @@ describe("Favicon", () => {
it("should draw a badge if called with a non-zero value", () => {
const favicon = new Favicon();
favicon.badge(123);
jest.runAllTimers();
vi.runAllTimers();
expect(favicon["context"].__getDrawCalls()).toMatchSnapshot();
});
it("should clear a badge if called with a zero value", () => {
const favicon = new Favicon();
favicon.badge(123);
jest.runAllTimers();
vi.runAllTimers();
favicon.badge(0);
expect(favicon["context"].__getDrawCalls()).toMatchSnapshot();
});
@@ -48,7 +51,7 @@ describe("Favicon", () => {
const favicon = new Favicon();
const originalLink = window.document.querySelector("link");
favicon.badge(123);
jest.runAllTimers();
vi.runAllTimers();
const newLink = window.document.querySelector("link");
expect(originalLink).not.toStrictEqual(newLink);
});
@@ -60,9 +63,9 @@ describe("Favicon", () => {
link.href = "favicon.png";
head.appendChild(link);
const spy = jest.spyOn(document, "createElement");
const spy = vi.spyOn(document, "createElement");
const favicon = new Favicon();
jest.runAllTimers();
vi.runAllTimers();
const img = spy.mock.results[0].value;
img.onload();
@@ -78,9 +81,9 @@ describe("Favicon", () => {
link.href = "favicon.png";
head.appendChild(link);
const spy = jest.spyOn(document, "createElement");
const spy = vi.spyOn(document, "createElement");
const favicon = new Favicon();
jest.runAllTimers();
vi.runAllTimers();
const img = spy.mock.results[0].value;
img.height = 512;
+4 -1
View File
@@ -5,7 +5,7 @@ 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.
*/
import { vi } from "vitest";
import { vi, expect as viExpect } from "vitest";
import { mocked as jestMocked } from "jest-mock";
export const isJest = typeof jest !== "undefined";
@@ -22,4 +22,7 @@ const adapter = {
const mocked = adapter.mocked;
export { adapter as vi, mocked };
const _expect = isJest ? (expect as unknown as typeof viExpect) : viExpect;
export { _expect as expect };
export { type Mocked, type MockedObject } from "vitest";
+1
View File
@@ -16,6 +16,7 @@ import {
} from "matrix-js-sdk/src/matrix";
import { mkMessage, type MessageEventProps } from "./test-utils";
import { expect } from "../setup/adapter.ts";
export const makeThreadEvent = ({
rootEventId,
+14
View File
@@ -1067,6 +1067,9 @@ importers:
vitest:
specifier: 'catalog:'
version: 4.1.9(@opentelemetry/api@1.9.1)(@types/node@25.9.3)(@vitest/browser-playwright@4.1.9)(@vitest/coverage-v8@4.1.9)(happy-dom@20.10.6)(jsdom@26.1.0(patch_hash=040623e87b1c8b676c2a705513c0276c0704dd1b23fc3a1bb77cde8128b64b5f))(vite@8.1.3(@types/node@25.9.3)(esbuild@0.27.4)(jiti@2.7.0)(sugarss@5.0.1(postcss@8.5.16))(terser@5.48.0)(yaml@2.8.4))
vitest-canvas-mock:
specifier: ^1.1.4
version: 1.1.4(vitest@4.1.9)
web-streams-polyfill:
specifier: ^4.0.0
version: 4.3.0
@@ -13675,6 +13678,11 @@ packages:
postcss:
optional: true
vitest-canvas-mock@1.1.4:
resolution: {integrity: sha512-4boWHY+STwAxGl1+uwakNNoQky5EjPLC8HuponXNoAscYyT1h/F7RUvTkl4IyF/MiWr3V8Q626je3Iel3eArqA==}
peerDependencies:
vitest: ^3.0.0 || ^4.0.0
vitest-plugin-vis@5.1.1:
resolution: {integrity: sha512-A/MEvQhDpNAj/5b53GiN139glgWUe0Qh5cDupm25G+06FA/2K85bd3tK+Hi4fTaolNbL5WiLNgnQ8dOL+kC7Pw==}
peerDependencies:
@@ -27960,6 +27968,12 @@ snapshots:
- typescript
- universal-cookie
vitest-canvas-mock@1.1.4(vitest@4.1.9):
dependencies:
cssfontparser: 1.2.1
moo-color: 1.0.3
vitest: 4.1.9(@opentelemetry/api@1.9.1)(@types/node@25.9.3)(@vitest/browser-playwright@4.1.9)(@vitest/coverage-v8@4.1.9)(happy-dom@20.10.6)(jsdom@26.1.0(patch_hash=040623e87b1c8b676c2a705513c0276c0704dd1b23fc3a1bb77cde8128b64b5f))(vite@8.1.3(@types/node@25.9.3)(esbuild@0.27.4)(jiti@2.7.0)(sugarss@5.0.1(postcss@8.5.16))(terser@5.48.0)(yaml@2.8.4))
vitest-plugin-vis@5.1.1(@vitest/browser-playwright@4.1.9)(@vitest/browser@4.1.9)(babel-plugin-macros@3.1.0)(typescript@6.0.3)(vitest@4.1.9):
dependencies:
'@vitest/browser': 4.1.9(vite@8.1.3(@types/node@25.9.3)(esbuild@0.27.4)(jiti@2.7.0)(sugarss@5.0.1(postcss@8.5.16))(terser@5.48.0)(yaml@2.8.4))(vitest@4.1.9)