Migrate more jest tests to vitest (#33898)

* Migrate more jest tests to vitest

* Fix jest config

* Fix jest config

* Make remaining jest tests type-happy
This commit is contained in:
Michael Telatynski
2026-06-19 15:34:18 +00:00
committed by GitHub
parent fab71c80ed
commit 45234b9c94
62 changed files with 625 additions and 488 deletions
@@ -1,295 +0,0 @@
/*
* Copyright 2024 New Vector Ltd.
* Copyright 2024 The Matrix.org Foundation C.I.C.
*
* SPDX-License-Identifier: AGPL-3.0-only OR GPL-3.0-only OR LicenseRef-Element-Commercial
* Please see LICENSE files in the repository root for full details.
*/
import { EventTimeline, EventType, type IEvent, type MatrixClient, MatrixEvent, Room } from "matrix-js-sdk/src/matrix";
import { mocked } from "jest-mock";
import { createTestClient } from "../../test-utils";
import PinningUtils from "../../../src/utils/PinningUtils";
import SettingsStore from "../../../src/settings/SettingsStore";
import { isContentActionable } from "../../../src/utils/EventUtils";
import { ReadPinsEventId } from "../../../src/components/views/right_panel/types";
jest.mock("../../../src/utils/EventUtils", () => {
return {
isContentActionable: jest.fn(),
canPinEvent: jest.fn(),
};
});
describe("PinningUtils", () => {
const roomId = "!room:example.org";
const userId = "@alice:example.org";
const mockedIsContentActionable = mocked(isContentActionable);
let matrixClient: MatrixClient;
let room: Room;
/**
* Create a pinned event with the given content.
* @param content
*/
function makePinEvent(content?: Partial<IEvent>) {
return new MatrixEvent({
type: EventType.RoomMessage,
sender: userId,
content: {
body: "First pinned message",
msgtype: "m.text",
},
room_id: roomId,
origin_server_ts: 0,
event_id: "$eventId",
...content,
});
}
beforeEach(() => {
// Enable feature pinning
jest.spyOn(SettingsStore, "getValue").mockReturnValue(true);
mockedIsContentActionable.mockImplementation(() => true);
matrixClient = createTestClient();
room = new Room(roomId, matrixClient, userId);
matrixClient.getRoom = jest.fn().mockReturnValue(room);
jest.spyOn(
matrixClient.getRoom(roomId)!.getLiveTimeline().getState(EventTimeline.FORWARDS)!,
"mayClientSendStateEvent",
).mockReturnValue(true);
});
describe("isUnpinnable", () => {
test.each(PinningUtils.PINNABLE_EVENT_TYPES)("should return true for pinnable event types", (eventType) => {
const event = makePinEvent({ type: eventType });
expect(PinningUtils.isUnpinnable(event)).toBe(true);
});
test("should return false for a non pinnable event type", () => {
const event = makePinEvent({ type: EventType.RoomCreate });
expect(PinningUtils.isUnpinnable(event)).toBe(false);
});
test("should return true for a redacted event", () => {
const event = makePinEvent({ unsigned: { redacted_because: "because" as unknown as IEvent } });
expect(PinningUtils.isUnpinnable(event)).toBe(true);
});
});
describe("isPinnable", () => {
test.each(PinningUtils.PINNABLE_EVENT_TYPES)("should return true for pinnable event types", (eventType) => {
const event = makePinEvent({ type: eventType });
expect(PinningUtils.isPinnable(event)).toBe(true);
});
test("should return false for a redacted event", () => {
const event = makePinEvent({ unsigned: { redacted_because: "because" as unknown as IEvent } });
expect(PinningUtils.isPinnable(event)).toBe(false);
});
});
describe("isPinned", () => {
test("should return false if no room", () => {
matrixClient.getRoom = jest.fn().mockReturnValue(undefined);
const event = makePinEvent();
expect(PinningUtils.isPinned(matrixClient, event)).toBe(false);
});
test("should return false if no pinned event", () => {
jest.spyOn(
matrixClient.getRoom(roomId)!.getLiveTimeline().getState(EventTimeline.FORWARDS)!,
"getStateEvents",
).mockReturnValue(null);
const event = makePinEvent();
expect(PinningUtils.isPinned(matrixClient, event)).toBe(false);
});
test("should return false if pinned events do not contain the event id", () => {
jest.spyOn(
matrixClient.getRoom(roomId)!.getLiveTimeline().getState(EventTimeline.FORWARDS)!,
"getStateEvents",
).mockReturnValue({
// @ts-ignore
getContent: () => ({ pinned: ["$otherEventId"] }),
});
const event = makePinEvent();
expect(PinningUtils.isPinned(matrixClient, event)).toBe(false);
});
test("should return true if pinned events contains the event id", () => {
const event = makePinEvent();
jest.spyOn(
matrixClient.getRoom(roomId)!.getLiveTimeline().getState(EventTimeline.FORWARDS)!,
"getStateEvents",
).mockReturnValue({
// @ts-ignore
getContent: () => ({ pinned: [event.getId()] }),
});
expect(PinningUtils.isPinned(matrixClient, event)).toBe(true);
});
});
describe("canPin & canUnpin", () => {
describe("canPin", () => {
test("should return false if event is not actionable", () => {
mockedIsContentActionable.mockImplementation(() => false);
const event = makePinEvent();
expect(PinningUtils.canPin(matrixClient, event)).toBe(false);
});
test("should return false if no room", () => {
matrixClient.getRoom = jest.fn().mockReturnValue(undefined);
const event = makePinEvent();
expect(PinningUtils.canPin(matrixClient, event)).toBe(false);
});
test("should return false if client cannot send state event", () => {
jest.spyOn(
matrixClient.getRoom(roomId)!.getLiveTimeline().getState(EventTimeline.FORWARDS)!,
"mayClientSendStateEvent",
).mockReturnValue(false);
const event = makePinEvent();
expect(PinningUtils.canPin(matrixClient, event)).toBe(false);
});
test("should return false if event is not pinnable", () => {
const event = makePinEvent({ type: EventType.RoomCreate });
expect(PinningUtils.canPin(matrixClient, event)).toBe(false);
});
test("should return true if all conditions are met", () => {
const event = makePinEvent();
expect(PinningUtils.canPin(matrixClient, event)).toBe(true);
});
});
describe("canUnpin", () => {
test("should return false if event is not unpinnable", () => {
const event = makePinEvent({ type: EventType.RoomCreate });
expect(PinningUtils.canUnpin(matrixClient, event)).toBe(false);
});
test("should return true if all conditions are met", () => {
const event = makePinEvent();
expect(PinningUtils.canUnpin(matrixClient, event)).toBe(true);
});
test("should return true if the event is redacted", () => {
const event = makePinEvent({ unsigned: { redacted_because: "because" as unknown as IEvent } });
expect(PinningUtils.canUnpin(matrixClient, event)).toBe(true);
});
});
});
describe("pinOrUnpinEvent", () => {
test("should do nothing if no room", async () => {
matrixClient.getRoom = jest.fn().mockReturnValue(undefined);
const event = makePinEvent();
await PinningUtils.pinOrUnpinEvent(matrixClient, event);
expect(matrixClient.sendStateEvent).not.toHaveBeenCalled();
});
test("should do nothing if no event id", async () => {
const event = makePinEvent({ event_id: undefined });
await PinningUtils.pinOrUnpinEvent(matrixClient, event);
expect(matrixClient.sendStateEvent).not.toHaveBeenCalled();
});
test("should pin the event if not pinned", async () => {
jest.spyOn(
matrixClient.getRoom(roomId)!.getLiveTimeline().getState(EventTimeline.FORWARDS)!,
"getStateEvents",
).mockReturnValue({
// @ts-ignore
getContent: () => ({ pinned: ["$otherEventId"] }),
});
jest.spyOn(room, "getAccountData").mockReturnValue({
getContent: jest.fn().mockReturnValue({
event_ids: ["$otherEventId"],
}),
} as unknown as MatrixEvent);
const event = makePinEvent();
await PinningUtils.pinOrUnpinEvent(matrixClient, event);
expect(matrixClient.setRoomAccountData).toHaveBeenCalledWith(roomId, ReadPinsEventId, {
event_ids: ["$otherEventId", event.getId()],
});
expect(matrixClient.sendStateEvent).toHaveBeenCalledWith(
roomId,
EventType.RoomPinnedEvents,
{ pinned: ["$otherEventId", event.getId()] },
"",
);
});
test("should unpin the event if already pinned", async () => {
const event = makePinEvent();
jest.spyOn(
matrixClient.getRoom(roomId)!.getLiveTimeline().getState(EventTimeline.FORWARDS)!,
"getStateEvents",
).mockReturnValue({
// @ts-ignore
getContent: () => ({ pinned: [event.getId(), "$otherEventId"] }),
});
await PinningUtils.pinOrUnpinEvent(matrixClient, event);
expect(matrixClient.sendStateEvent).toHaveBeenCalledWith(
roomId,
EventType.RoomPinnedEvents,
{ pinned: ["$otherEventId"] },
"",
);
});
});
describe("userHasPinOrUnpinPermission", () => {
test("should return true if user can pin or unpin", () => {
expect(PinningUtils.userHasPinOrUnpinPermission(matrixClient, room)).toBe(true);
});
test("should return false if client cannot send state event", () => {
jest.spyOn(
matrixClient.getRoom(roomId)!.getLiveTimeline().getState(EventTimeline.FORWARDS)!,
"mayClientSendStateEvent",
).mockReturnValue(false);
expect(PinningUtils.userHasPinOrUnpinPermission(matrixClient, room)).toBe(false);
});
});
describe("unpinAllEvents", () => {
it("should unpin all events in the given room", async () => {
await PinningUtils.unpinAllEvents(matrixClient, roomId);
expect(matrixClient.sendStateEvent).toHaveBeenCalledWith(
roomId,
EventType.RoomPinnedEvents,
{ pinned: [] },
"",
);
});
});
});
@@ -1,100 +0,0 @@
/*
Copyright 2024 New Vector Ltd.
Copyright 2021 The Matrix.org Foundation C.I.C.
SPDX-License-Identifier: AGPL-3.0-only OR GPL-3.0-only OR LicenseRef-Element-Commercial
Please see LICENSE files in the repository root for full details.
*/
import { Singleflight } from "../../../src/utils/Singleflight";
describe("Singleflight", () => {
afterEach(() => {
Singleflight.forgetAll();
});
it("should throw for bad context variables", () => {
const permutations: [object | null, string | null][] = [
[null, null],
[{}, null],
[null, "test"],
];
for (const p of permutations) {
expect(() => Singleflight.for(p[0], p[1])).toThrow("An instance and key must be supplied");
}
});
it("should execute the function once", () => {
const instance = {};
const key = "test";
const val = {}; // unique object for reference check
const fn = jest.fn().mockReturnValue(val);
const sf = Singleflight.for(instance, key);
const r1 = sf.do(fn);
expect(r1).toBe(val);
expect(fn.mock.calls.length).toBe(1);
const r2 = sf.do(fn);
expect(r2).toBe(val);
expect(fn.mock.calls.length).toBe(1);
});
it("should execute the function once, even with new contexts", () => {
const instance = {};
const key = "test";
const val = {}; // unique object for reference check
const fn = jest.fn().mockReturnValue(val);
let sf = Singleflight.for(instance, key);
const r1 = sf.do(fn);
expect(r1).toBe(val);
expect(fn.mock.calls.length).toBe(1);
sf = Singleflight.for(instance, key); // RESET FOR TEST
const r2 = sf.do(fn);
expect(r2).toBe(val);
expect(fn.mock.calls.length).toBe(1);
});
it("should execute the function twice if the result was forgotten", () => {
const instance = {};
const key = "test";
const val = {}; // unique object for reference check
const fn = jest.fn().mockReturnValue(val);
const sf = Singleflight.for(instance, key);
const r1 = sf.do(fn);
expect(r1).toBe(val);
expect(fn.mock.calls.length).toBe(1);
sf.forget();
const r2 = sf.do(fn);
expect(r2).toBe(val);
expect(fn.mock.calls.length).toBe(2);
});
it("should execute the function twice if the instance was forgotten", () => {
const instance = {};
const key = "test";
const val = {}; // unique object for reference check
const fn = jest.fn().mockReturnValue(val);
const sf = Singleflight.for(instance, key);
const r1 = sf.do(fn);
expect(r1).toBe(val);
expect(fn.mock.calls.length).toBe(1);
Singleflight.forgetAllFor(instance);
const r2 = sf.do(fn);
expect(r2).toBe(val);
expect(fn.mock.calls.length).toBe(2);
});
it("should execute the function twice if everything was forgotten", () => {
const instance = {};
const key = "test";
const val = {}; // unique object for reference check
const fn = jest.fn().mockReturnValue(val);
const sf = Singleflight.for(instance, key);
const r1 = sf.do(fn);
expect(r1).toBe(val);
expect(fn.mock.calls.length).toBe(1);
Singleflight.forgetAll();
const r2 = sf.do(fn);
expect(r2).toBe(val);
expect(fn.mock.calls.length).toBe(2);
});
});
@@ -7,7 +7,7 @@ Please see LICENSE files in the repository root for full details.
*/
import { logger } from "matrix-js-sdk/src/logger";
import { type Mocked } from "jest-mock";
import { type Mocked } from "jest-mock-vitest-adapter";
import {
type GenericPosition,
@@ -6,7 +6,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 { type Mocked, mocked } from "jest-mock";
import { type Mocked, mocked } from "jest-mock-vitest-adapter";
import { type Device, type MatrixClient } from "matrix-js-sdk/src/matrix";
import { getDeviceCryptoInfo, getUserDeviceIds } from "../../../../src/utils/crypto/deviceInfo";
@@ -1,18 +0,0 @@
/*
Copyright 2026 Element Creations Ltd.
SPDX-License-Identifier: AGPL-3.0-only OR GPL-3.0-only OR LicenseRef-Element-Commercial
Please see LICENSE files in the repository root for full details.
*/
import { onSubmitPreventDefault } from "../../../src/utils/form.ts";
describe("onSubmitPreventDefault", () => {
it("should preventDefault", () => {
const event = new SubmitEvent("submit");
const spy = jest.spyOn(event, "preventDefault");
onSubmitPreventDefault(event);
expect(spy).toHaveBeenCalled();
});
});
@@ -6,7 +6,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 { mocked, type Mocked } from "jest-mock";
import { mocked, type Mocked } from "jest-mock-vitest-adapter";
import { type MatrixClient, type Room } from "matrix-js-sdk/src/matrix";
import { sleep } from "matrix-js-sdk/src/utils";
-219
View File
@@ -1,219 +0,0 @@
/*
Copyright 2024 New Vector Ltd.
Copyright 2021 The Matrix.org Foundation C.I.C.
SPDX-License-Identifier: AGPL-3.0-only OR GPL-3.0-only OR LicenseRef-Element-Commercial
Please see LICENSE files in the repository root for full details.
*/
import { EnhancedMap, mapDiff } from "../../../src/utils/maps";
describe("maps", () => {
describe("mapDiff", () => {
it("should indicate no differences when the pointers are the same", () => {
const a = new Map([
[1, 1],
[2, 2],
[3, 3],
]);
const result = mapDiff(a, a);
expect(result).toBeDefined();
expect(result.added).toBeDefined();
expect(result.removed).toBeDefined();
expect(result.changed).toBeDefined();
expect(result.added).toHaveLength(0);
expect(result.removed).toHaveLength(0);
expect(result.changed).toHaveLength(0);
});
it("should indicate no differences when there are none", () => {
const a = new Map([
[1, 1],
[2, 2],
[3, 3],
]);
const b = new Map([
[1, 1],
[2, 2],
[3, 3],
]);
const result = mapDiff(a, b);
expect(result).toBeDefined();
expect(result.added).toBeDefined();
expect(result.removed).toBeDefined();
expect(result.changed).toBeDefined();
expect(result.added).toHaveLength(0);
expect(result.removed).toHaveLength(0);
expect(result.changed).toHaveLength(0);
});
it("should indicate added properties", () => {
const a = new Map([
[1, 1],
[2, 2],
[3, 3],
]);
const b = new Map([
[1, 1],
[2, 2],
[3, 3],
[4, 4],
]);
const result = mapDiff(a, b);
expect(result).toBeDefined();
expect(result.added).toBeDefined();
expect(result.removed).toBeDefined();
expect(result.changed).toBeDefined();
expect(result.added).toHaveLength(1);
expect(result.removed).toHaveLength(0);
expect(result.changed).toHaveLength(0);
expect(result.added).toEqual([4]);
});
it("should indicate removed properties", () => {
const a = new Map([
[1, 1],
[2, 2],
[3, 3],
]);
const b = new Map([
[1, 1],
[2, 2],
]);
const result = mapDiff(a, b);
expect(result).toBeDefined();
expect(result.added).toBeDefined();
expect(result.removed).toBeDefined();
expect(result.changed).toBeDefined();
expect(result.added).toHaveLength(0);
expect(result.removed).toHaveLength(1);
expect(result.changed).toHaveLength(0);
expect(result.removed).toEqual([3]);
});
it("should indicate changed properties", () => {
const a = new Map([
[1, 1],
[2, 2],
[3, 3],
]);
const b = new Map([
[1, 1],
[2, 2],
[3, 4],
]); // note change
const result = mapDiff(a, b);
expect(result).toBeDefined();
expect(result.added).toBeDefined();
expect(result.removed).toBeDefined();
expect(result.changed).toBeDefined();
expect(result.added).toHaveLength(0);
expect(result.removed).toHaveLength(0);
expect(result.changed).toHaveLength(1);
expect(result.changed).toEqual([3]);
});
it("should indicate changed, added, and removed properties", () => {
const a = new Map([
[1, 1],
[2, 2],
[3, 3],
]);
const b = new Map([
[1, 1],
[2, 8],
[4, 4],
]); // note change
const result = mapDiff(a, b);
expect(result).toBeDefined();
expect(result.added).toBeDefined();
expect(result.removed).toBeDefined();
expect(result.changed).toBeDefined();
expect(result.added).toHaveLength(1);
expect(result.removed).toHaveLength(1);
expect(result.changed).toHaveLength(1);
expect(result.added).toEqual([4]);
expect(result.removed).toEqual([3]);
expect(result.changed).toEqual([2]);
});
it("should indicate changes for difference in pointers", () => {
const a = new Map([[1, {}]]); // {} always creates a new object
const b = new Map([[1, {}]]);
const result = mapDiff(a, b);
expect(result).toBeDefined();
expect(result.added).toBeDefined();
expect(result.removed).toBeDefined();
expect(result.changed).toBeDefined();
expect(result.added).toHaveLength(0);
expect(result.removed).toHaveLength(0);
expect(result.changed).toHaveLength(1);
expect(result.changed).toEqual([1]);
});
});
describe("EnhancedMap", () => {
// Most of these tests will make sure it implements the Map<K, V> class
it("should be empty by default", () => {
const result = new EnhancedMap();
expect(result.size).toBe(0);
});
it("should use the provided entries", () => {
const obj = { a: 1, b: 2 };
const result = new EnhancedMap(Object.entries(obj));
expect(result.size).toBe(2);
expect(result.get("a")).toBe(1);
expect(result.get("b")).toBe(2);
});
it("should create keys if they do not exist", () => {
const key = "a";
const val = {}; // we'll check pointers
const result = new EnhancedMap<string, any>();
expect(result.size).toBe(0);
let get = result.getOrCreate(key, val);
expect(get).toBeDefined();
expect(get).toBe(val);
expect(result.size).toBe(1);
get = result.getOrCreate(key, 44); // specifically change `val`
expect(get).toBeDefined();
expect(get).toBe(val);
expect(result.size).toBe(1);
get = result.get(key); // use the base class function
expect(get).toBeDefined();
expect(get).toBe(val);
expect(result.size).toBe(1);
});
it("should proxy remove to delete and return it", () => {
const val = {};
const result = new EnhancedMap<string, any>();
result.set("a", val);
expect(result.size).toBe(1);
const removed = result.remove("a");
expect(result.size).toBe(0);
expect(removed).toBeDefined();
expect(removed).toBe(val);
});
it("should support removing unknown keys", () => {
const val = {};
const result = new EnhancedMap<string, any>();
result.set("a", val);
expect(result.size).toBe(1);
const removed = result.remove("not-a");
expect(result.size).toBe(1);
expect(removed).not.toBeDefined();
});
});
});
@@ -14,7 +14,7 @@ import {
ReceiptType,
type AccountDataEvents,
} from "matrix-js-sdk/src/matrix";
import { type Mocked, mocked } from "jest-mock";
import { type Mocked, mocked } from "jest-mock-vitest-adapter";
import {
localNotificationsAreSilenced,
@@ -382,10 +382,10 @@ describe("Permalinks", function () {
});
it("should generate a room permalink for room IDs with some candidate servers", function () {
mockClient.getRoom.mockImplementation((roomId: Room["roomId"]) => {
return mockRoom(roomId, [
makeMemberWithPL(roomId, "@alice:first", 100),
makeMemberWithPL(roomId, "@bob:second", 0),
mockClient.getRoom.mockImplementation((roomId?: string) => {
return mockRoom(roomId!, [
makeMemberWithPL(roomId!, "@alice:first", 100),
makeMemberWithPL(roomId!, "@bob:second", 0),
]);
});
const result = makeRoomPermalink(mockClient, "!somewhere:example.org");
@@ -399,10 +399,10 @@ describe("Permalinks", function () {
});
it("should generate a room permalink for room aliases without candidate servers", function () {
mockClient.getRoom.mockImplementation((roomId: Room["roomId"]) => {
return mockRoom(roomId, [
makeMemberWithPL(roomId, "@alice:first", 100),
makeMemberWithPL(roomId, "@bob:second", 0),
mockClient.getRoom.mockImplementation((roomId?: string) => {
return mockRoom(roomId!, [
makeMemberWithPL(roomId!, "@alice:first", 100),
makeMemberWithPL(roomId!, "@bob:second", 0),
]);
});
const result = makeRoomPermalink(mockClient, "#somewhere:example.org");