Prepare for jest->vitest (#5137)

* Skip unwritten tests

Signed-off-by: Michael Telatynski <7t3chguy@gmail.com>

* Tidy jest fake timers

Signed-off-by: Michael Telatynski <7t3chguy@gmail.com>

* Remove unnecessary sessionStorage mock

Signed-off-by: Michael Telatynski <7t3chguy@gmail.com>

* Improve types

Signed-off-by: Michael Telatynski <7t3chguy@gmail.com>

* Improve async assertions

Signed-off-by: Michael Telatynski <7t3chguy@gmail.com>

* Improve error assertions

Signed-off-by: Michael Telatynski <7t3chguy@gmail.com>

* Improve object assertions

Signed-off-by: Michael Telatynski <7t3chguy@gmail.com>

* Remove assertion testing unclear mock

This test failed when ran individually, same as after the clearAllMocks call

Signed-off-by: Michael Telatynski <7t3chguy@gmail.com>

* Avoid awaiting non-thenables

Signed-off-by: Michael Telatynski <7t3chguy@gmail.com>

* Pass nop function when stubbing out console, vitest won't accept it any other way

Signed-off-by: Michael Telatynski <7t3chguy@gmail.com>

* Remove unnecessary mock which causes tests to fail after updating fetch-mock & fix typo

Signed-off-by: Michael Telatynski <7t3chguy@gmail.com>

* Fix mistaken assertions not testing all values in array

Signed-off-by: Michael Telatynski <7t3chguy@gmail.com>

* Fix hidden non-running tests in room.spec.ts

Signed-off-by: Michael Telatynski <7t3chguy@gmail.com>

---------

Signed-off-by: Michael Telatynski <7t3chguy@gmail.com>
This commit is contained in:
Michael Telatynski
2026-01-13 15:19:50 +00:00
committed by GitHub
parent 3d0ebdf6f1
commit b3fedf3a4e
22 changed files with 96 additions and 92 deletions
+1 -1
View File
@@ -113,7 +113,7 @@ describe("cross-signing", () => {
);
afterEach(async () => {
await aliceClient.stopClient();
aliceClient.stopClient();
fetchMock.mockReset();
});
+3 -4
View File
@@ -276,7 +276,7 @@ describe("crypto", () => {
);
afterEach(async () => {
await aliceClient.stopClient();
aliceClient.stopClient();
// Allow in-flight things to complete before we tear down the test
await jest.runAllTimersAsync();
@@ -724,7 +724,7 @@ describe("crypto", () => {
syncResponder.sendOrQueueSyncResponse(syncResponse);
await syncPromise(aliceClient);
await awaitDecryptionError;
await expect(awaitDecryptionError).resolves.toBeUndefined();
});
});
@@ -1448,7 +1448,7 @@ describe("crypto", () => {
await syncPromise(aliceClient);
// Verify that `/upload` is called on Alice's homesever
// Verify that `/upload` is called on Alice's homeserver
const { keysCount, fallbackKeysCount } = await uploadPromise;
expect(keysCount).toBeGreaterThan(0);
expect(fallbackKeysCount).toBe(0);
@@ -1718,7 +1718,6 @@ describe("crypto", () => {
* @param backupVersion - The version of the created backup
*/
async function bootstrapSecurity(backupVersion: string): Promise<void> {
mockSetupCrossSigningRequests();
mockSetupMegolmBackupRequests(backupVersion);
// promise which will resolve when a `KeyBackupStatus` event is emitted with `enabled: true`
+1 -3
View File
@@ -133,9 +133,7 @@ describe("megolm-keys backup", () => {
});
afterEach(async () => {
if (aliceClient !== undefined) {
await aliceClient.stopClient();
}
aliceClient?.stopClient();
// Allow in-flight things to complete before we tear down the test
await jest.runAllTimersAsync();
+1 -2
View File
@@ -96,8 +96,7 @@ describe("Encrypted State Events", () => {
}, 10000);
afterEach(async () => {
await aliceClient.stopClient();
await jest.runAllTimersAsync();
aliceClient.stopClient();
fetchMock.mockReset();
});
+1 -3
View File
@@ -141,9 +141,7 @@ describe("verification", () => {
});
afterEach(async () => {
if (aliceClient !== undefined) {
await aliceClient.stopClient();
}
aliceClient?.stopClient();
// Allow in-flight things to complete before we tear down the test
await jest.runAllTimersAsync();
+1 -1
View File
@@ -190,7 +190,7 @@ describe("AutoDiscovery", function () {
};
return Promise.all([
httpBackend.flushAllExpected(),
AutoDiscovery.findClientConfig("example.org").then(expect(expected).toEqual),
AutoDiscovery.findClientConfig("example.org").then((config) => expect(config).toEqual(expected)),
]);
});
+2 -2
View File
@@ -228,7 +228,7 @@ describe("EventTimelineSet", () => {
let thread: Thread;
beforeEach(() => {
(client.supportsThreads as jest.Mock).mockReturnValue(true);
mocked(client.supportsThreads).mockReturnValue(true);
thread = new Thread("!thread_id:server", messageEvent, { room, client });
});
@@ -384,7 +384,7 @@ describe("EventTimelineSet", () => {
let thread: Thread;
beforeEach(() => {
(client.supportsThreads as jest.Mock).mockReturnValue(true);
mocked(client.supportsThreads).mockReturnValue(true);
thread = new Thread("!thread_id:server", messageEvent, { room, client });
});
+8 -4
View File
@@ -17,7 +17,7 @@ limitations under the License.
import { type MatrixClient } from "../../src/client";
import { logger } from "../../src/logger";
import { InteractiveAuth, AuthType } from "../../src/interactive-auth";
import { InteractiveAuth, AuthType, NoAuthFlowFoundError } from "../../src/interactive-auth";
import { HTTPError, MatrixError } from "../../src/http-api";
import { sleep } from "../../src/utils";
import { secureRandomString } from "../../src/randomstring";
@@ -337,7 +337,9 @@ describe("InteractiveAuth", () => {
throw err;
});
await expect(ia.attemptAuth.bind(ia)).rejects.toThrow(new Error("No appropriate authentication flow found"));
await expect(ia.attemptAuth.bind(ia)).rejects.toThrow(
new NoAuthFlowFoundError("No appropriate authentication flow found", [], []),
);
});
it("should start an auth stage and reject if no auth flow but has session", async () => {
@@ -372,7 +374,9 @@ describe("InteractiveAuth", () => {
throw err;
});
await expect(ia.attemptAuth.bind(ia)).rejects.toThrow(new Error("No appropriate authentication flow found"));
await expect(ia.attemptAuth.bind(ia)).rejects.toThrow(
new NoAuthFlowFoundError("No appropriate authentication flow found", [], []),
);
});
it("should handle unexpected error types without data property set", async () => {
@@ -397,7 +401,7 @@ describe("InteractiveAuth", () => {
throw err;
});
await expect(ia.attemptAuth.bind(ia)).rejects.toThrow(new Error("myerror"));
await expect(ia.attemptAuth.bind(ia)).rejects.toThrow(new HTTPError("myerror", 401));
});
it("should allow dummy auth", async () => {
+7 -5
View File
@@ -14,7 +14,9 @@ See the License for the specific language governing permissions and
limitations under the License.
*/
import { type MatrixEvent } from "../../../src";
import { mocked } from "jest-mock";
import { type IContent, type MatrixEvent } from "../../../src";
import {
CallMembership,
type SessionMembershipData,
@@ -32,8 +34,8 @@ function makeMockEvent(originTs = 0): MatrixEvent {
} as unknown as MatrixEvent;
}
function createCallMembership(ev: MatrixEvent, content: unknown): CallMembership {
(ev.getContent as jest.Mock).mockReturnValue(content);
function createCallMembership(ev: MatrixEvent, content: IContent): CallMembership {
mocked(ev.getContent).mockReturnValue(content);
const data = CallMembership.membershipDataFromMatrixEvent(ev);
return new CallMembership(ev, data, "xx");
}
@@ -307,11 +309,11 @@ describe("CallMembership", () => {
}).toThrow();
});
it("considers memberships unexpired if local age low enough", () => {
it.skip("considers memberships unexpired if local age low enough", () => {
// TODO link prev event
});
it("considers memberships expired if local age large enough", () => {
it.skip("considers memberships expired if local age large enough", () => {
// TODO link prev event
});
@@ -1345,6 +1345,13 @@ describe("MatrixRTCSession", () => {
});
describe("receiving", () => {
beforeAll(() => {
jest.useFakeTimers();
});
afterAll(() => {
jest.useRealTimers();
});
it("collects keys from encryption events", async () => {
const mockRoom = makeMockRoom([membershipTemplate]);
sess = MatrixRTCSession.sessionForSlot(client, mockRoom, callSession);
@@ -126,7 +126,7 @@ describe("MembershipManager", () => {
// Spys/Mocks
const restartScheduledDelayedEventHandle = createAsyncHandle<void>(
client._unstable_restartScheduledDelayedEvent as Mock,
client._unstable_restartScheduledDelayedEvent,
);
// Test
@@ -293,7 +293,7 @@ describe("MembershipManager", () => {
describe("delayed leave event", () => {
it("does not try again to schedule a delayed leave event if not supported", () => {
const delayedHandle = createAsyncHandle(client._unstable_sendDelayedStateEvent as Mock);
const delayedHandle = createAsyncHandle(client._unstable_sendDelayedStateEvent);
const manager = new MembershipManager({}, room, client, callSession);
manager.join([focus]);
delayedHandle.reject?.(
@@ -305,7 +305,7 @@ describe("MembershipManager", () => {
expect(client._unstable_sendDelayedStateEvent).toHaveBeenCalledTimes(1);
});
it("does try to schedule a delayed leave event again if rate limited", async () => {
const delayedHandle = createAsyncHandle(client._unstable_sendDelayedStateEvent as Mock);
const delayedHandle = createAsyncHandle(client._unstable_sendDelayedStateEvent);
const manager = new MembershipManager({}, room, client, callSession);
manager.join([focus]);
delayedHandle.reject?.(new HTTPError("rate limited", 429, undefined));
@@ -916,7 +916,7 @@ describe("MembershipManager", () => {
describe("sends an rtc membership event", () => {
it("sends a membership event and schedules delayed leave when joining a call", async () => {
const restartScheduledDelayedEventHandle = createAsyncHandle<void>(
client._unstable_restartScheduledDelayedEvent as Mock,
client._unstable_restartScheduledDelayedEvent,
);
const memberManager = new StickyEventMembershipManager(
undefined,
+4 -4
View File
@@ -15,7 +15,7 @@ limitations under the License.
*/
import { EventEmitter } from "stream";
import { type Mocked } from "jest-mock";
import { mocked, type Mocked } from "jest-mock";
import { EventType, type Room, RoomEvent, type MatrixClient, type MatrixEvent } from "../../../src";
import { CallMembership, type SessionMembershipData } from "../../../src/matrixrtc";
@@ -72,8 +72,8 @@ export type MockClient = Pick<
*/
export function makeMockClient(userId: string, deviceId: string): MockClient {
return {
getDeviceId: () => deviceId,
getUserId: () => userId,
getDeviceId: jest.fn(() => deviceId),
getUserId: jest.fn(() => userId),
sendEvent: jest.fn(),
sendStateEvent: jest.fn(),
cancelPendingEvent: jest.fn(),
@@ -194,7 +194,7 @@ export function mockCallMembership(
rtcBackendIdentity?: string,
): CallMembership {
const ev = mockRTCEvent(membershipData, roomId);
(ev.getContent as jest.Mock).mockReturnValue(membershipData);
mocked(ev.getContent).mockReturnValue(membershipData);
const data = CallMembership.membershipDataFromMatrixEvent(ev);
return new CallMembership(ev, data, rtcBackendIdentity ?? "xx", logger);
}
+15 -3
View File
@@ -159,7 +159,7 @@ describe("MatrixEvent", () => {
// Then it disappears from the thread and appears in the main timeline
expect(ev.threadRootId).toBeUndefined();
expect(mainTimelineLiveEventIds(room)).toEqual([threadRoot.getId(), ev.getId()]);
expect(mainTimelineLiveEventIds(room)).toEqual([threadRoot.getId(), ev.getId(), redaction.getId()]);
expect(threadLiveEventIds(room, 0)).not.toContain(ev.getId());
});
@@ -183,7 +183,12 @@ describe("MatrixEvent", () => {
// Then the reaction moves into the main timeline
expect(reaction.threadRootId).toBeUndefined();
expect(mainTimelineLiveEventIds(room)).toEqual([threadRoot.getId(), ev.getId(), reaction.getId()]);
expect(mainTimelineLiveEventIds(room)).toEqual([
threadRoot.getId(),
ev.getId(),
reaction.getId(),
redaction.getId(),
]);
expect(threadLiveEventIds(room, 0)).not.toContain(reaction.getId());
});
@@ -207,7 +212,12 @@ describe("MatrixEvent", () => {
// Then the edit moves into the main timeline
expect(edit.threadRootId).toBeUndefined();
expect(mainTimelineLiveEventIds(room)).toEqual([threadRoot.getId(), ev.getId(), edit.getId()]);
expect(mainTimelineLiveEventIds(room)).toEqual([
threadRoot.getId(),
ev.getId(),
edit.getId(),
redaction.getId(),
]);
expect(threadLiveEventIds(room, 0)).not.toContain(edit.getId());
});
@@ -245,6 +255,7 @@ describe("MatrixEvent", () => {
reply1.getId(),
reply2.getId(),
reaction.getId(),
redaction.getId(),
]);
expect(threadLiveEventIds(room, 0)).not.toContain(reply1.getId());
expect(threadLiveEventIds(room, 0)).not.toContain(reply2.getId());
@@ -330,6 +341,7 @@ describe("MatrixEvent", () => {
function createRedaction(redactedEventid: string): MatrixEvent {
return new MatrixEvent({
event_id: `$redact-${redactedEventid}`,
type: "m.room.redaction",
redacts: redactedEventid,
});
+1 -17
View File
@@ -50,6 +50,7 @@ describe("oidc authorization", () => {
beforeAll(() => {
jest.spyOn(logger, "warn");
jest.useFakeTimers();
jest.setSystemTime(now);
fetchMock.get(delegatedAuthConfig.issuer + ".well-known/openid-configuration", mockOpenIdConfiguration());
@@ -212,21 +213,6 @@ describe("oidc authorization", () => {
sub: "123",
};
const mockSessionStorage = (state: Record<string, unknown>): void => {
jest.spyOn(sessionStorage.__proto__, "getItem").mockImplementation((key: unknown) => {
return state[key as string] ?? null;
});
jest.spyOn(sessionStorage.__proto__, "setItem").mockImplementation(
// @ts-ignore mock type
(key: string, value: unknown) => (state[key] = value),
);
jest.spyOn(sessionStorage.__proto__, "removeItem").mockImplementation((key: unknown) => {
const { [key as string]: value, ...newState } = state;
state = newState;
return value;
});
};
const getValueFromStorage = <T = string>(state: string, key: string): T => {
const storedState = window.sessionStorage.getItem(`mx_oidc_${state}`)!;
return JSON.parse(storedState)[key] as unknown as T;
@@ -279,8 +265,6 @@ describe("oidc authorization", () => {
keys: [],
});
mockSessionStorage({});
mocked(jwtDecode).mockReturnValue(validDecodedIdToken);
});
+7 -15
View File
@@ -781,7 +781,9 @@ describe("Room", function () {
);
});
const resetTimelineTests = function (timelineSupport: boolean) {
describe.each(["enabled", "disabled"])("resetLiveTimeline with timeline support %s", (enabled) => {
const timelineSupport = enabled === "enabled";
let events: MatrixEvent[];
beforeEach(function () {
@@ -812,7 +814,8 @@ describe("Room", function () {
];
});
it("should copy state from previous timeline", async function () {
// XXX: This test was previously not running and it is broken
it.skip("should copy state from previous timeline", async function () {
await room.addLiveEvents([events[0], events[1]], { addToState: false });
expect(room.getLiveTimeline().getEvents().length).toEqual(2);
room.resetLiveTimeline("sometoken", "someothertoken");
@@ -882,13 +885,6 @@ describe("Room", function () {
const tl = room.getTimelineForEvent(events[0].getId()!);
expect(tl).toBe(timelineSupport ? firstLiveTimeline : null);
});
};
describe("resetLiveTimeline with timeline support enabled", () => {
resetTimelineTests.bind(null, true);
});
describe("resetLiveTimeline with timeline support disabled", () => {
resetTimelineTests.bind(null, false);
});
describe("compareEventOrdering", function () {
@@ -1838,10 +1834,6 @@ describe("Room", function () {
jest.spyOn(room.client, "supportsThreads").mockReturnValue(true);
});
afterEach(() => {
jest.restoreAllMocks();
});
it("returns true if there is an unthreaded receipt for a later event in a thread", async () => {
// Given a thread exists in the room
const { thread, events } = mkThread({ room, length: 3 });
@@ -3628,11 +3620,11 @@ describe("Room", function () {
describe("roomNameGenerator", () => {
const client = new TestClient(userA).client;
client.roomNameGenerator = jest.fn().mockReturnValue(null);
client.roomNameGenerator = jest.fn().mockImplementation(() => null);
const room = new Room(roomId, client, userA);
it("should call fn when recalculating room name", () => {
(client.roomNameGenerator as jest.Mock).mockClear();
mocked(client.roomNameGenerator!).mockClear();
room.recalculate();
expect(client.roomNameGenerator).toHaveBeenCalled();
});
@@ -302,7 +302,7 @@ describe("OutgoingRequestProcessor", () => {
const result = processor.makeOutgoingRequest(request);
// wait for the HTTP request to be made
await authRequestCalledPromise;
await expect(authRequestCalledPromise).resolves.toBeUndefined();
// while the HTTP request is in flight, the OlmMachine gets stopped.
olmMachine.close();
+6 -6
View File
@@ -1755,7 +1755,7 @@ describe("RustCrypto", () => {
fetchMock.get("path:/_matrix/client/v3/room_keys/version", testData.SIGNED_BACKUP_DATA);
const rustCrypto = await makeTestRustCrypto(makeMatrixHttpApi());
await expect(rustCrypto.getKeyBackupInfo()).resolves.toStrictEqual(testData.SIGNED_BACKUP_DATA);
await expect(rustCrypto.getKeyBackupInfo()).resolves.toMatchObject(testData.SIGNED_BACKUP_DATA);
});
it("should return null if not available", async () => {
@@ -1831,7 +1831,7 @@ describe("RustCrypto", () => {
// we need to process a sync so that the OlmMachine will upload keys
await rustCrypto1.preprocessToDeviceMessages([]);
await rustCrypto1.onSyncCompleted({});
rustCrypto1.onSyncCompleted({});
fetchMock.get("path:/_matrix/client/unstable/org.matrix.msc3814.v1/dehydrated_device", {
status: 404,
@@ -1846,7 +1846,7 @@ describe("RustCrypto", () => {
return {};
});
await rustCrypto1.startDehydration();
await rustCrypto1.stop();
rustCrypto1.stop();
// Create another RustCrypto, using the same SecretStorage, to
// rehydrate the device.
@@ -1884,8 +1884,8 @@ describe("RustCrypto", () => {
// means that the device was successfully rehydrated.
const rehydrationCompletedPromise = emitPromise(rustCrypto2, CryptoEvent.RehydrationCompleted);
await rustCrypto2.startDehydration();
await rehydrationCompletedPromise;
await rustCrypto2.stop();
await expect(rehydrationCompletedPromise).resolves.toBeTruthy();
rustCrypto2.stop();
});
describe("start dehydration options", () => {
@@ -1963,7 +1963,7 @@ describe("RustCrypto", () => {
});
// we need to process a sync so that the OlmMachine will upload keys
await rustCrypto.preprocessToDeviceMessages([]);
await rustCrypto.onSyncCompleted({});
rustCrypto.onSyncCompleted({});
// set up mocks needed for device dehydration
dehydratedDeviceInfo = undefined;
+12 -3
View File
@@ -219,7 +219,10 @@ describe("VerificationRequest", () => {
aliceVerifier.on(VerifierEvent.ShowSas, compareSas);
bobVerifier.on(VerifierEvent.ShowSas, compareSas);
await Promise.all([aliceVerifier.verify(), await bobVerifier.verify()]);
await expect(Promise.all([aliceVerifier.verify(), await bobVerifier.verify()])).resolves.toEqual([
undefined,
undefined,
]);
} finally {
await aliceRequestLoop.stop();
await bobRequestLoop.stop();
@@ -348,7 +351,10 @@ describe("VerificationRequest", () => {
aliceVerifier.on(VerifierEvent.ShowSas, compareSas);
bobVerifier.on(VerifierEvent.ShowSas, compareSas);
await Promise.all([aliceVerifier.verify(), await bobVerifier.verify()]);
await expect(Promise.all([aliceVerifier.verify(), await bobVerifier.verify()])).resolves.toEqual([
undefined,
undefined,
]);
} finally {
await aliceRequestLoop.stop();
await bobRequestLoop.stop();
@@ -458,7 +464,10 @@ describe("VerificationRequest", () => {
showQrCodeCallbacks.confirm();
});
await Promise.all([aliceVerifier.verify(), await bobVerifier.verify()]);
await expect(Promise.all([aliceVerifier.verify(), await bobVerifier.verify()])).resolves.toEqual([
undefined,
undefined,
]);
} finally {
await aliceRequestLoop.stop();
await bobRequestLoop.stop();
+1 -1
View File
@@ -266,7 +266,7 @@ describe("ServerSideSecretStorageImpl", function () {
accountDataAdapter.getAccountDataFromServer.mockImplementation(mockGetAccountData);
// suppress the expected warning on the console
jest.spyOn(console, "warn").mockImplementation();
jest.spyOn(console, "warn").mockImplementation(() => {});
// now attempt the store
await secretStorage.store("mysecret", "supersecret", ["keyid"]);
+3 -3
View File
@@ -236,7 +236,7 @@ describe("IndexedDBStore", () => {
// @ts-ignore - private field access
(store.backend as LocalIndexedDBStoreBackend).db!.onclose!({} as Event);
await storeClosedResolvers.promise;
await expect(storeClosedResolvers.promise).resolves.toBeUndefined();
});
it("should use remote backend if workerFactory passed", async () => {
@@ -256,7 +256,7 @@ describe("IndexedDBStore", () => {
workerFactory: () => new MockWorker() as Worker,
});
store.startup();
await workerPostMessageResolvers.promise;
await expect(workerPostMessageResolvers.promise).resolves.toBeUndefined();
});
it("remote worker should pass closed event", async () => {
@@ -275,7 +275,7 @@ describe("IndexedDBStore", () => {
const storeClosedResolvers = Promise.withResolvers<void>();
store.on("closed", storeClosedResolvers.resolve);
(worker as any).onmessage({ data: { command: "closed" } });
await storeClosedResolvers.promise;
await expect(storeClosedResolvers.promise).resolves.toBeUndefined();
});
it("remote worker should pass command failures", async () => {
+3 -3
View File
@@ -360,8 +360,8 @@ describe("Group Call", function () {
}
});
it("does not throw when calling updateLocalUsermediaStream() without local usermedia stream", () => {
expect(async () => await groupCall.updateLocalUsermediaStream({} as MediaStream)).not.toThrow();
it("does not throw when calling updateLocalUsermediaStream() without local usermedia stream", async () => {
await expect(groupCall.updateLocalUsermediaStream({} as MediaStream)).resolves.toBeUndefined();
});
it.each([GroupCallState.Ended, GroupCallState.Entered, GroupCallState.InitializingLocalCallFeed])(
@@ -391,6 +391,7 @@ describe("Group Call", function () {
const newFeed = new MockCallFeed(FAKE_USER_ID_1, FAKE_DEVICE_ID_1, new MockMediaStream("new"));
beforeEach(async () => {
jest.clearAllMocks();
jest.spyOn(currentFeed, "dispose");
jest.spyOn(newFeed, "measureVolumeActivity");
@@ -496,7 +497,6 @@ describe("Group Call", function () {
expect(groupCall.screenshareFeeds).toStrictEqual(newFeeds);
expect(currentFeed.dispose).toHaveBeenCalled();
expect(newFeed.measureVolumeActivity).toHaveBeenCalledWith(true);
expect(groupCall.emit).toHaveBeenCalledWith(GroupCallEvent.ScreenshareFeedsChanged, newFeeds);
});
});
+7 -7
View File
@@ -271,14 +271,14 @@ describe("Media Handler", function () {
beforeEach(() => {
// replace this with one that returns a new object each time so we can
// tell whether we've ended up with the same stream
mockMediaDevices.getUserMedia.mockImplementation((constraints: MediaStreamConstraints) => {
mockMediaDevices.getUserMedia.mockImplementation((constraints: MediaStreamConstraints | undefined) => {
const stream = new MockMediaStream("local_stream");
if (constraints.audio) {
if (constraints?.audio) {
const track = new MockMediaStreamTrack("audio_track", "audio");
track.settings = { deviceId: FAKE_AUDIO_INPUT_ID };
stream.addTrack(track);
}
if (constraints.video) {
if (constraints?.video) {
const track = new MockMediaStreamTrack("video_track", "video");
track.settings = { deviceId: FAKE_VIDEO_INPUT_ID };
stream.addTrack(track);
@@ -336,9 +336,9 @@ describe("Media Handler", function () {
// First call with exact should fail
mockMediaDevices.getUserMedia
.mockRejectedValueOnce(new Error("OverconstrainedError"))
.mockImplementation((constraints: MediaStreamConstraints) => {
.mockImplementation((constraints: MediaStreamConstraints | undefined) => {
const stream = new MockMediaStream("local_stream");
if (constraints.audio) {
if (constraints?.audio) {
const track = new MockMediaStreamTrack("audio_track", "audio");
track.settings = { deviceId: FAKE_AUDIO_INPUT_ID };
stream.addTrack(track);
@@ -373,9 +373,9 @@ describe("Media Handler", function () {
// First call with exact should fail
mockMediaDevices.getUserMedia
.mockRejectedValueOnce(new Error("OverconstrainedError"))
.mockImplementation((constraints: MediaStreamConstraints) => {
.mockImplementation((constraints: MediaStreamConstraints | undefined) => {
const stream = new MockMediaStream("local_stream");
if (constraints.video) {
if (constraints?.video) {
const track = new MockMediaStreamTrack("video_track", "video");
track.settings = { deviceId: FAKE_VIDEO_INPUT_ID };
stream.addTrack(track);