diff --git a/spec/integ/matrix-client-methods.spec.ts b/spec/integ/matrix-client-methods.spec.ts index 48dcccba9..18e02624f 100644 --- a/spec/integ/matrix-client-methods.spec.ts +++ b/spec/integ/matrix-client-methods.spec.ts @@ -33,9 +33,9 @@ describe("MatrixClient", function () { const accessToken = "aseukfgwef"; const idServerDomain = "identity.localhost"; // not a real server const identityAccessToken = "woop-i-am-a-secret"; - let client: MatrixClient | undefined; - let httpBackend: HttpBackend | undefined; - let store: MemoryStore | undefined; + let client: MatrixClient; + let httpBackend: HttpBackend; + let store: MemoryStore; const defaultClientOpts: IStoredClientOpts = { threadSupport: false, @@ -59,8 +59,8 @@ describe("MatrixClient", function () { }); afterEach(function () { - httpBackend!.verifyNoOutstandingExpectation(); - return httpBackend!.stop(); + httpBackend.verifyNoOutstandingExpectation(); + return httpBackend.stop(); }); describe("uploadContent", function () { @@ -72,7 +72,7 @@ describe("MatrixClient", function () { }; it("should upload the file", function () { - httpBackend! + httpBackend .when("POST", "/_matrix/media/r0/upload") .check(function (req) { expect(req.rawData).toEqual(buf); @@ -86,11 +86,11 @@ describe("MatrixClient", function () { }) .respond(200, '{"content_uri": "content"}', true); - const prom = client!.uploadContent(file, opts); + const prom = client.uploadContent(file, opts); expect(prom).toBeTruthy(); - const uploads = client!.getCurrentUploads(); + const uploads = client.getCurrentUploads(); expect(uploads.length).toEqual(1); expect(uploads[0].promise).toBe(prom); expect(uploads[0].loaded).toEqual(0); @@ -98,16 +98,16 @@ describe("MatrixClient", function () { const prom2 = prom.then(function (response) { expect(response.content_uri).toEqual("content"); - const uploads = client!.getCurrentUploads(); + const uploads = client.getCurrentUploads(); expect(uploads.length).toEqual(0); }); - httpBackend!.flush(""); + httpBackend.flush(""); return prom2; }); it("should parse errors into a MatrixError", function () { - httpBackend! + httpBackend .when("POST", "/_matrix/media/r0/upload") .check(function (req) { expect(req.rawData).toEqual(buf); @@ -119,7 +119,7 @@ describe("MatrixClient", function () { error: "broken", }); - const prom = client!.uploadContent(file, opts).then( + const prom = client.uploadContent(file, opts).then( function (response) { throw Error("request not failed"); }, @@ -130,30 +130,30 @@ describe("MatrixClient", function () { }, ); - httpBackend!.flush(""); + httpBackend.flush(""); return prom; }); it("should return a promise which can be cancelled", async () => { - const prom = client!.uploadContent(file, opts); + const prom = client.uploadContent(file, opts); - const uploads = client!.getCurrentUploads(); + const uploads = client.getCurrentUploads(); expect(uploads.length).toEqual(1); expect(uploads[0].promise).toBe(prom); expect(uploads[0].loaded).toEqual(0); - const r = client!.cancelUpload(prom); + const r = client.cancelUpload(prom); expect(r).toBe(true); await expect(prom).rejects.toThrow("Aborted"); - expect(client!.getCurrentUploads()).toHaveLength(0); + expect(client.getCurrentUploads()).toHaveLength(0); }); }); describe("joinRoom", function () { it("should no-op if you've already joined a room", function () { const roomId = "!foo:bar"; - const room = new Room(roomId, client!, userId); - client!.fetchRoomEvent = () => + const room = new Room(roomId, client, userId); + client.fetchRoomEvent = () => Promise.resolve({ type: "test", content: {}, @@ -166,10 +166,10 @@ describe("MatrixClient", function () { event: true, }), ]); - httpBackend!.verifyNoOutstandingRequests(); - store!.storeRoom(room); - client!.joinRoom(roomId); - httpBackend!.verifyNoOutstandingRequests(); + httpBackend.verifyNoOutstandingRequests(); + store.storeRoom(room); + client.joinRoom(roomId); + httpBackend.verifyNoOutstandingRequests(); }); it("should send request to inviteSignUrl if specified", async () => { @@ -183,24 +183,24 @@ describe("MatrixClient", function () { signatures: {}, }; - httpBackend! + httpBackend .when("POST", inviteSignUrl) .check((request) => { - expect(request.queryParams?.mxid).toEqual(client!.getUserId()); + expect(request.queryParams?.mxid).toEqual(client.getUserId()); }) .respond(200, signature); - httpBackend! + httpBackend .when("POST", "/join/" + encodeURIComponent(roomId)) .check((request) => { expect(request.data.third_party_signed).toEqual(signature); }) .respond(200, { room_id: roomId }); - const prom = client!.joinRoom(roomId, { + const prom = client.joinRoom(roomId, { inviteSignUrl, viaServers, }); - await httpBackend!.flushAllExpected(); + await httpBackend.flushAllExpected(); expect((await prom).roomId).toBe(roomId); }); }); @@ -212,10 +212,10 @@ describe("MatrixClient", function () { const filter = Filter.fromJson(userId, filterId, { event_format: "client", }); - store!.storeFilter(filter); - const gotFilter = await client!.getFilter(userId, filterId, true); + store.storeFilter(filter); + const gotFilter = await client.getFilter(userId, filterId, true); expect(gotFilter).toEqual(filter); - httpBackend!.verifyNoOutstandingRequests(); + httpBackend.verifyNoOutstandingRequests(); }); it("should do an HTTP request if !allowCached even if one exists", async () => { @@ -223,15 +223,15 @@ describe("MatrixClient", function () { event_format: "federation", }; - httpBackend! + httpBackend .when("GET", "/user/" + encodeURIComponent(userId) + "/filter/" + filterId) .respond(200, httpFilterDefinition); const storeFilter = Filter.fromJson(userId, filterId, { event_format: "client", }); - store!.storeFilter(storeFilter); - const [gotFilter] = await Promise.all([client!.getFilter(userId, filterId, false), httpBackend!.flush("")]); + store.storeFilter(storeFilter); + const [gotFilter] = await Promise.all([client.getFilter(userId, filterId, false), httpBackend.flush("")]); expect(gotFilter.getDefinition()).toEqual(httpFilterDefinition); }); @@ -239,14 +239,14 @@ describe("MatrixClient", function () { const httpFilterDefinition = { event_format: "federation", }; - expect(store!.getFilter(userId, filterId)).toBe(null); + expect(store.getFilter(userId, filterId)).toBe(null); - httpBackend! + httpBackend .when("GET", "/user/" + encodeURIComponent(userId) + "/filter/" + filterId) .respond(200, httpFilterDefinition); - const [gotFilter] = await Promise.all([client!.getFilter(userId, filterId, true), httpBackend!.flush("")]); + const [gotFilter] = await Promise.all([client.getFilter(userId, filterId, true), httpBackend.flush("")]); expect(gotFilter.getDefinition()).toEqual(httpFilterDefinition); - expect(store!.getFilter(userId, filterId)).toBeTruthy(); + expect(store.getFilter(userId, filterId)).toBeTruthy(); }); }); @@ -254,13 +254,13 @@ describe("MatrixClient", function () { const filterId = "f1llllllerid"; it("should do an HTTP request and then store the filter", async () => { - expect(store!.getFilter(userId, filterId)).toBe(null); + expect(store.getFilter(userId, filterId)).toBe(null); const filterDefinition = { event_format: "client" as IFilterDefinition["event_format"], }; - httpBackend! + httpBackend .when("POST", "/user/" + encodeURIComponent(userId) + "/filter") .check(function (req) { expect(req.data).toEqual(filterDefinition); @@ -269,9 +269,9 @@ describe("MatrixClient", function () { filter_id: filterId, }); - const [gotFilter] = await Promise.all([client!.createFilter(filterDefinition), httpBackend!.flush("")]); + const [gotFilter] = await Promise.all([client.createFilter(filterDefinition), httpBackend.flush("")]); expect(gotFilter.getDefinition()).toEqual(filterDefinition); - expect(store!.getFilter(userId, filterId)).toEqual(gotFilter); + expect(store.getFilter(userId, filterId)).toEqual(gotFilter); }); }); @@ -300,10 +300,10 @@ describe("MatrixClient", function () { }, }; - client!.searchMessageText({ + client.searchMessageText({ query: "monkeys", }); - httpBackend! + httpBackend .when("POST", "/search") .check(function (req) { expect(req.data).toEqual({ @@ -316,7 +316,7 @@ describe("MatrixClient", function () { }) .respond(200, response); - return httpBackend!.flush(""); + return httpBackend.flush(""); }); describe("should filter out context from different timelines (threads)", () => { @@ -386,7 +386,7 @@ describe("MatrixClient", function () { results: [], highlights: [], }; - client!.processRoomEventsSearch(data, response); + client.processRoomEventsSearch(data, response); expect(data.results).toHaveLength(1); expect(data.results[0].context.getTimeline()).toHaveLength(2); @@ -450,7 +450,7 @@ describe("MatrixClient", function () { results: [], highlights: [], }; - client!.processRoomEventsSearch(data, response); + client.processRoomEventsSearch(data, response); expect(data.results).toHaveLength(1); expect(data.results[0].context.getTimeline()).toHaveLength(1); @@ -512,7 +512,7 @@ describe("MatrixClient", function () { results: [], highlights: [], }; - client!.processRoomEventsSearch(data, response); + client.processRoomEventsSearch(data, response); expect(data.results).toHaveLength(1); expect(data.results[0].context.getTimeline()).toHaveLength(1); @@ -530,17 +530,17 @@ describe("MatrixClient", function () { beforeEach(function () { // running initCrypto should trigger a key upload - httpBackend!.when("POST", "/keys/upload").respond(200, {}); - return Promise.all([client!.initCrypto(), httpBackend!.flush("/keys/upload", 1)]); + httpBackend.when("POST", "/keys/upload").respond(200, {}); + return Promise.all([client.initCrypto(), httpBackend.flush("/keys/upload", 1)]); }); afterEach(() => { - client!.stopClient(); + client.stopClient(); }); it("should do an HTTP request and then store the keys", function () { const ed25519key = "7wG2lzAqbjcyEkOP7O4gU7ItYcn+chKzh5sT/5r2l78"; - // ed25519key = client!.getDeviceEd25519Key(); + // ed25519key = client.getDeviceEd25519Key(); const borisKeys = { dev1: { algorithms: ["1"], @@ -580,7 +580,7 @@ describe("MatrixClient", function () { var b = JSON.parse(JSON.stringify(o)); delete(b.signatures); delete(b.unsigned); - return client!.crypto.olmDevice.sign(anotherjson.stringify(b)); + return client.crypto.olmDevice.sign(anotherjson.stringify(b)); }; logger.log("Ed25519: " + ed25519key); @@ -588,7 +588,7 @@ describe("MatrixClient", function () { logger.log("chaz:", sign(chazKeys.dev2)); */ - httpBackend! + httpBackend .when("POST", "/keys/query") .check(function (req) { expect(req.data).toEqual({ @@ -605,7 +605,7 @@ describe("MatrixClient", function () { }, }); - const prom = client!.downloadKeys(["boris", "chaz"]).then(function (res) { + const prom = client.downloadKeys(["boris", "chaz"]).then(function (res) { assertObjectContains(res.get("boris")!.get("dev1")!, { verified: 0, // DeviceVerification.UNVERIFIED keys: { "ed25519:dev1": ed25519key }, @@ -621,7 +621,7 @@ describe("MatrixClient", function () { }); }); - httpBackend!.flush(""); + httpBackend.flush(""); return prom; }); }); @@ -629,16 +629,16 @@ describe("MatrixClient", function () { describe("deleteDevice", function () { const auth = { identifier: 1 }; it("should pass through an auth dict", function () { - httpBackend! + httpBackend .when("DELETE", "/_matrix/client/r0/devices/my_device") .check(function (req) { expect(req.data).toEqual({ auth: auth }); }) .respond(200); - const prom = client!.deleteDevice("my_device", auth); + const prom = client.deleteDevice("my_device", auth); - httpBackend!.flush(""); + httpBackend.flush(""); return prom; }); }); @@ -646,7 +646,7 @@ describe("MatrixClient", function () { describe("partitionThreadedEvents", function () { let room: Room; beforeEach(() => { - room = new Room("!STrMRsukXHtqQdSeHa:matrix.org", client!, userId); + room = new Room("!STrMRsukXHtqQdSeHa:matrix.org", client, userId); }); it("returns empty arrays when given an empty arrays", function () { @@ -658,7 +658,7 @@ describe("MatrixClient", function () { it("copies pre-thread in-timeline vote events onto both timelines", function () { // @ts-ignore setting private property - client!.clientOpts = { + client.clientOpts = { ...defaultClientOpts, threadSupport: true, }; @@ -689,7 +689,7 @@ describe("MatrixClient", function () { it("copies pre-thread in-timeline reactions onto both timelines", function () { // @ts-ignore setting private property - client!.clientOpts = { + client.clientOpts = { ...defaultClientOpts, threadSupport: true, }; @@ -713,7 +713,7 @@ describe("MatrixClient", function () { it("copies post-thread in-timeline vote events onto both timelines", function () { // @ts-ignore setting private property - client!.clientOpts = { + client.clientOpts = { ...defaultClientOpts, threadSupport: true, }; @@ -737,7 +737,7 @@ describe("MatrixClient", function () { it("copies post-thread in-timeline reactions onto both timelines", function () { // @ts-ignore setting private property - client!.clientOpts = { + client.clientOpts = { ...defaultClientOpts, threadSupport: true, }; @@ -761,7 +761,7 @@ describe("MatrixClient", function () { it("sends room state events to the main timeline only", function () { // @ts-ignore setting private property - client!.clientOpts = { + client.clientOpts = { ...defaultClientOpts, threadSupport: true, }; @@ -818,7 +818,7 @@ describe("MatrixClient", function () { it("sends redactions of reactions to thread responses to thread timeline only", () => { // @ts-ignore setting private property - client!.clientOpts = { + client.clientOpts = { ...defaultClientOpts, threadSupport: true, }; @@ -844,7 +844,7 @@ describe("MatrixClient", function () { it("sends reply to reply to thread root outside of thread to main timeline only", () => { // @ts-ignore setting private property - client!.clientOpts = { + client.clientOpts = { ...defaultClientOpts, threadSupport: true, }; @@ -865,7 +865,7 @@ describe("MatrixClient", function () { it("sends reply to thread responses to main timeline only", () => { // @ts-ignore setting private property - client!.clientOpts = { + client.clientOpts = { ...defaultClientOpts, threadSupport: true, }; @@ -894,9 +894,9 @@ describe("MatrixClient", function () { }, ]; - const prom = client!.getThirdpartyUser("irc", {}); - httpBackend!.when("GET", "/thirdparty/user/irc").respond(200, response); - await httpBackend!.flush(""); + const prom = client.getThirdpartyUser("irc", {}); + httpBackend.when("GET", "/thirdparty/user/irc").respond(200, response); + await httpBackend.flush(""); expect(await prom).toStrictEqual(response); }); }); @@ -911,9 +911,9 @@ describe("MatrixClient", function () { }, ]; - const prom = client!.getThirdpartyLocation("irc", {}); - httpBackend!.when("GET", "/thirdparty/location/irc").respond(200, response); - await httpBackend!.flush(""); + const prom = client.getThirdpartyLocation("irc", {}); + httpBackend.when("GET", "/thirdparty/location/irc").respond(200, response); + await httpBackend.flush(""); expect(await prom).toStrictEqual(response); }); }); @@ -924,10 +924,10 @@ describe("MatrixClient", function () { pushers: [], }; - const prom = client!.getPushers(); - httpBackend!.when("GET", "/_matrix/client/versions").respond(200, {}); - httpBackend!.when("GET", "/pushers").respond(200, response); - await httpBackend!.flush(""); + const prom = client.getPushers(); + httpBackend.when("GET", "/_matrix/client/versions").respond(200, {}); + httpBackend.when("GET", "/pushers").respond(200, response); + await httpBackend.flush(""); expect(await prom).toStrictEqual(response); }); }); @@ -939,15 +939,15 @@ describe("MatrixClient", function () { left: [], }; - const prom = client!.getKeyChanges("old", "new"); - httpBackend! + const prom = client.getKeyChanges("old", "new"); + httpBackend .when("GET", "/keys/changes") .check((req) => { expect(req.queryParams?.from).toEqual("old"); expect(req.queryParams?.to).toEqual("new"); }) .respond(200, response); - await httpBackend!.flush(""); + await httpBackend.flush(""); expect(await prom).toStrictEqual(response); }); }); @@ -958,9 +958,9 @@ describe("MatrixClient", function () { devices: [], }; - const prom = client!.getDevices(); - httpBackend!.when("GET", "/devices").respond(200, response); - await httpBackend!.flush(""); + const prom = client.getDevices(); + httpBackend.when("GET", "/devices").respond(200, response); + await httpBackend.flush(""); expect(await prom).toStrictEqual(response); }); }); @@ -974,9 +974,9 @@ describe("MatrixClient", function () { last_seen_ts: 1, }; - const prom = client!.getDevice("DEADBEEF"); - httpBackend!.when("GET", "/devices/DEADBEEF").respond(200, response); - await httpBackend!.flush(""); + const prom = client.getDevice("DEADBEEF"); + httpBackend.when("GET", "/devices/DEADBEEF").respond(200, response); + await httpBackend.flush(""); expect(await prom).toStrictEqual(response); }); }); @@ -987,9 +987,9 @@ describe("MatrixClient", function () { threepids: [], }; - const prom = client!.getThreePids(); - httpBackend!.when("GET", "/account/3pid").respond(200, response); - await httpBackend!.flush(""); + const prom = client.getThreePids(); + httpBackend.when("GET", "/account/3pid").respond(200, response); + await httpBackend.flush(""); expect(await prom).toStrictEqual(response); }); }); @@ -997,9 +997,9 @@ describe("MatrixClient", function () { describe("deleteAlias", () => { it("should hit the expected API endpoint", async () => { const response = {}; - const prom = client!.deleteAlias("#foo:bar"); - httpBackend!.when("DELETE", "/directory/room/" + encodeURIComponent("#foo:bar")).respond(200, response); - await httpBackend!.flush(""); + const prom = client.deleteAlias("#foo:bar"); + httpBackend.when("DELETE", "/directory/room/" + encodeURIComponent("#foo:bar")).respond(200, response); + await httpBackend.flush(""); expect(await prom).toStrictEqual(response); }); }); @@ -1007,10 +1007,10 @@ describe("MatrixClient", function () { describe("deleteRoomTag", () => { it("should hit the expected API endpoint", async () => { const response = {}; - const prom = client!.deleteRoomTag("!roomId:server", "u.tag"); + const prom = client.deleteRoomTag("!roomId:server", "u.tag"); const url = `/user/${encodeURIComponent(userId)}/rooms/${encodeURIComponent("!roomId:server")}/tags/u.tag`; - httpBackend!.when("DELETE", url).respond(200, response); - await httpBackend!.flush(""); + httpBackend.when("DELETE", url).respond(200, response); + await httpBackend.flush(""); expect(await prom).toStrictEqual(response); }); }); @@ -1025,10 +1025,10 @@ describe("MatrixClient", function () { }, }; - const prom = client!.getRoomTags("!roomId:server"); + const prom = client.getRoomTags("!roomId:server"); const url = `/user/${encodeURIComponent(userId)}/rooms/${encodeURIComponent("!roomId:server")}/tags`; - httpBackend!.when("GET", url).respond(200, response); - await httpBackend!.flush(""); + httpBackend.when("GET", url).respond(200, response); + await httpBackend.flush(""); expect(await prom).toStrictEqual(response); }); }); @@ -1040,12 +1040,12 @@ describe("MatrixClient", function () { submit_url: "https://foobar.matrix/_matrix/matrix", }; - httpBackend!.when("GET", "/_matrix/client/versions").respond(200, { + httpBackend.when("GET", "/_matrix/client/versions").respond(200, { versions: ["r0.6.0"], }); - const prom = client!.requestRegisterEmailToken("bob@email", "secret", 1); - httpBackend! + const prom = client.requestRegisterEmailToken("bob@email", "secret", 1); + httpBackend .when("POST", "/register/email/requestToken") .check((req) => { expect(req.data).toStrictEqual({ @@ -1055,7 +1055,7 @@ describe("MatrixClient", function () { }); }) .respond(200, response); - await httpBackend!.flush(""); + await httpBackend.flush(""); expect(await prom).toStrictEqual(response); }); }); @@ -1064,11 +1064,11 @@ describe("MatrixClient", function () { it("should supply an id_access_token", async () => { const targetEmail = "gerald@example.org"; - httpBackend!.when("GET", "/_matrix/client/versions").respond(200, { + httpBackend.when("GET", "/_matrix/client/versions").respond(200, { versions: ["r0.6.0"], }); - httpBackend! + httpBackend .when("POST", "/invite") .check((req) => { expect(req.data).toStrictEqual({ @@ -1080,8 +1080,8 @@ describe("MatrixClient", function () { }) .respond(200, {}); - const prom = client!.inviteByThreePid("!room:example.org", "email", targetEmail); - await httpBackend!.flush(""); + const prom = client.inviteByThreePid("!room:example.org", "email", targetEmail); + await httpBackend.flush(""); await prom; // returns empty object, so no validation needed }); }); @@ -1103,11 +1103,11 @@ describe("MatrixClient", function () { ], }; - httpBackend!.when("GET", "/_matrix/client/versions").respond(200, { + httpBackend.when("GET", "/_matrix/client/versions").respond(200, { versions: ["r0.6.0"], }); - httpBackend! + httpBackend .when("POST", "/createRoom") .check((req) => { expect(req.data).toMatchObject({ @@ -1122,57 +1122,57 @@ describe("MatrixClient", function () { }) .respond(200, response); - const prom = client!.createRoom(input); - await httpBackend!.flush(""); + const prom = client.createRoom(input); + await httpBackend.flush(""); expect(await prom).toStrictEqual(response); }); }); describe("requestLoginToken", () => { it("should hit the expected API endpoint with UIA", async () => { - httpBackend! + httpBackend .when("GET", "/capabilities") .respond(200, { capabilities: { "org.matrix.msc3882.get_login_token": { enabled: true } } }); const response = {}; const uiaData = {}; - const prom = client!.requestLoginToken(uiaData); - httpBackend! + const prom = client.requestLoginToken(uiaData); + httpBackend .when("POST", "/unstable/org.matrix.msc3882/login/get_token", { auth: uiaData }) .respond(200, response); - await httpBackend!.flush(""); + await httpBackend.flush(""); expect(await prom).toStrictEqual(response); }); it("should hit the expected API endpoint without UIA", async () => { - httpBackend! + httpBackend .when("GET", "/capabilities") .respond(200, { capabilities: { "org.matrix.msc3882.get_login_token": { enabled: true } } }); const response = { login_token: "xyz", expires_in_ms: 5000 }; - const prom = client!.requestLoginToken(); - httpBackend!.when("POST", "/unstable/org.matrix.msc3882/login/get_token", {}).respond(200, response); - await httpBackend!.flush(""); + const prom = client.requestLoginToken(); + httpBackend.when("POST", "/unstable/org.matrix.msc3882/login/get_token", {}).respond(200, response); + await httpBackend.flush(""); // check that expires_in has been populated for compatibility with r0 expect(await prom).toStrictEqual({ ...response, expires_in: 5 }); }); it("should hit the r1 endpoint when capability is disabled", async () => { - httpBackend! + httpBackend .when("GET", "/capabilities") .respond(200, { capabilities: { "org.matrix.msc3882.get_login_token": { enabled: false } } }); const response = { login_token: "xyz", expires_in_ms: 5000 }; - const prom = client!.requestLoginToken(); - httpBackend!.when("POST", "/unstable/org.matrix.msc3882/login/get_token", {}).respond(200, response); - await httpBackend!.flush(""); + const prom = client.requestLoginToken(); + httpBackend.when("POST", "/unstable/org.matrix.msc3882/login/get_token", {}).respond(200, response); + await httpBackend.flush(""); // check that expires_in has been populated for compatibility with r0 expect(await prom).toStrictEqual({ ...response, expires_in: 5 }); }); it("should hit the r0 endpoint for fallback", async () => { - httpBackend!.when("GET", "/capabilities").respond(200, {}); + httpBackend.when("GET", "/capabilities").respond(200, {}); const response = { login_token: "xyz", expires_in: 5 }; - const prom = client!.requestLoginToken(); - httpBackend!.when("POST", "/unstable/org.matrix.msc3882/login/token", {}).respond(200, response); - await httpBackend!.flush(""); + const prom = client.requestLoginToken(); + httpBackend.when("POST", "/unstable/org.matrix.msc3882/login/token", {}).respond(200, response); + await httpBackend.flush(""); // check that expires_in has been populated for compatibility with r1 expect(await prom).toStrictEqual({ ...response, expires_in_ms: 5000 }); }); @@ -1180,18 +1180,18 @@ describe("MatrixClient", function () { describe("logout", () => { it("should abort pending requests when called with stopClient=true", async () => { - httpBackend!.when("POST", "/logout").respond(200, {}); + httpBackend.when("POST", "/logout").respond(200, {}); const fn = jest.fn(); - client!.http.request(Method.Get, "/test").catch(fn); - client!.logout(true); - await httpBackend!.flush(undefined); + client.http.request(Method.Get, "/test").catch(fn); + client.logout(true); + await httpBackend.flush(undefined); expect(fn).toHaveBeenCalled(); }); }); describe("sendHtmlEmote", () => { it("should send valid html emote", async () => { - httpBackend! + httpBackend .when("PUT", "/send") .check((req) => { expect(req.data).toStrictEqual({ @@ -1202,15 +1202,15 @@ describe("MatrixClient", function () { }); }) .respond(200, { event_id: "$foobar" }); - const prom = client!.sendHtmlEmote("!room:server", "Body", "

Body

"); - await httpBackend!.flush(undefined); + const prom = client.sendHtmlEmote("!room:server", "Body", "

Body

"); + await httpBackend.flush(undefined); await expect(prom).resolves.toStrictEqual({ event_id: "$foobar" }); }); }); describe("sendHtmlMessage", () => { it("should send valid html message", async () => { - httpBackend! + httpBackend .when("PUT", "/send") .check((req) => { expect(req.data).toStrictEqual({ @@ -1221,34 +1221,34 @@ describe("MatrixClient", function () { }); }) .respond(200, { event_id: "$foobar" }); - const prom = client!.sendHtmlMessage("!room:server", "Body", "

Body

"); - await httpBackend!.flush(undefined); + const prom = client.sendHtmlMessage("!room:server", "Body", "

Body

"); + await httpBackend.flush(undefined); await expect(prom).resolves.toStrictEqual({ event_id: "$foobar" }); }); }); describe("forget", () => { it("should remove from store by default", async () => { - const room = new Room("!roomId:server", client!, userId); - client!.store.storeRoom(room); - expect(client!.store.getRooms()).toContain(room); + const room = new Room("!roomId:server", client, userId); + client.store.storeRoom(room); + expect(client.store.getRooms()).toContain(room); - httpBackend!.when("POST", "/forget").respond(200, {}); - await Promise.all([client!.forget(room.roomId), httpBackend!.flushAllExpected()]); - expect(client!.store.getRooms()).not.toContain(room); + httpBackend.when("POST", "/forget").respond(200, {}); + await Promise.all([client.forget(room.roomId), httpBackend.flushAllExpected()]); + expect(client.store.getRooms()).not.toContain(room); }); }); describe("getCapabilities", () => { it("should cache by default", async () => { - httpBackend!.when("GET", "/capabilities").respond(200, { + httpBackend.when("GET", "/capabilities").respond(200, { capabilities: { "m.change_password": false, }, }); - const prom = httpBackend!.flushAllExpected(); - const capabilities1 = await client!.getCapabilities(); - const capabilities2 = await client!.getCapabilities(); + const prom = httpBackend.flushAllExpected(); + const capabilities1 = await client.getCapabilities(); + const capabilities2 = await client.getCapabilities(); await prom; expect(capabilities1).toStrictEqual(capabilities2); @@ -1257,16 +1257,16 @@ describe("MatrixClient", function () { describe("getTerms", () => { it("should return Identity Server terms", async () => { - httpBackend!.when("GET", "/terms").respond(200, { foo: "bar" }); - const prom = client!.getTerms(SERVICE_TYPES.IS, "http://identity.server"); - await httpBackend!.flushAllExpected(); + httpBackend.when("GET", "/terms").respond(200, { foo: "bar" }); + const prom = client.getTerms(SERVICE_TYPES.IS, "http://identity.server"); + await httpBackend.flushAllExpected(); await expect(prom).resolves.toEqual({ foo: "bar" }); }); it("should return Integrations Manager terms", async () => { - httpBackend!.when("GET", "/terms").respond(200, { foo: "bar" }); - const prom = client!.getTerms(SERVICE_TYPES.IM, "http://im.server"); - await httpBackend!.flushAllExpected(); + httpBackend.when("GET", "/terms").respond(200, { foo: "bar" }); + const prom = client.getTerms(SERVICE_TYPES.IM, "http://im.server"); + await httpBackend.flushAllExpected(); await expect(prom).resolves.toEqual({ foo: "bar" }); }); }); @@ -1275,46 +1275,46 @@ describe("MatrixClient", function () { it("should send `user_accepts` via body of POST request", async () => { const terms = ["https://vector.im/notice-1"]; - httpBackend! + httpBackend .when("POST", "/terms") .check((req) => { expect(req.data.user_accepts).toStrictEqual(terms); }) .respond(200, {}); - const prom = client!.agreeToTerms(SERVICE_TYPES.IS, "https://vector.im", "at", terms); - await httpBackend!.flushAllExpected(); + const prom = client.agreeToTerms(SERVICE_TYPES.IS, "https://vector.im", "at", terms); + await httpBackend.flushAllExpected(); await prom; }); }); describe("publicRooms", () => { it("should use GET request if no server or filter is specified", () => { - httpBackend!.when("GET", "/publicRooms").respond(200, {}); - client!.publicRooms({}); - return httpBackend!.flushAllExpected(); + httpBackend.when("GET", "/publicRooms").respond(200, {}); + client.publicRooms({}); + return httpBackend.flushAllExpected(); }); it("should use GET request if only server is specified", () => { - httpBackend! + httpBackend .when("GET", "/publicRooms") .check((request) => { expect(request.queryParams?.server).toBe("server1"); }) .respond(200, {}); - client!.publicRooms({ server: "server1" }); - return httpBackend!.flushAllExpected(); + client.publicRooms({ server: "server1" }); + return httpBackend.flushAllExpected(); }); it("should use POST request if filter is specified", () => { - httpBackend! + httpBackend .when("POST", "/publicRooms") .check((request) => { expect(request.data.filter.generic_search_term).toBe("foobar"); }) .respond(200, {}); - client!.publicRooms({ filter: { generic_search_term: "foobar" } }); - return httpBackend!.flushAllExpected(); + client.publicRooms({ filter: { generic_search_term: "foobar" } }); + return httpBackend.flushAllExpected(); }); }); @@ -1323,17 +1323,17 @@ describe("MatrixClient", function () { const token = "!token&"; const userId = "@m:t"; - httpBackend!.when("POST", "/login").respond(200, { + httpBackend.when("POST", "/login").respond(200, { access_token: token, user_id: userId, }); - const prom = client!.login("fake.login", {}); - await httpBackend!.flushAllExpected(); + const prom = client.login("fake.login", {}); + await httpBackend.flushAllExpected(); const resp = await prom; expect(resp.access_token).toBe(token); expect(resp.user_id).toBe(userId); - expect(client!.getUserId()).toBe(userId); - expect(client!.http.opts.accessToken).toBe(token); + expect(client.getUserId()).toBe(userId); + expect(client.http.opts.accessToken).toBe(token); }); }); @@ -1346,7 +1346,7 @@ describe("MatrixClient", function () { expires_in: 12345, }; - httpBackend! + httpBackend .when("POST", "/account/register") .check((req) => { expect(req.data).toStrictEqual(token); @@ -1356,8 +1356,8 @@ describe("MatrixClient", function () { token: "tt", }); - const prom = client!.registerWithIdentityServer(token); - await httpBackend!.flushAllExpected(); + const prom = client.registerWithIdentityServer(token); + await httpBackend.flushAllExpected(); const resp = await prom; expect(resp.access_token).toBe("at"); expect(resp.token).toBe("tt"); @@ -1387,28 +1387,28 @@ describe("MatrixClient", function () { expectation: {}, }, ])("should modify power levels of $userId correctly", async ({ userId, powerLevel, expectation }) => { - httpBackend!.when("GET", "/state/m.room.power_levels/").respond(200, { + httpBackend.when("GET", "/state/m.room.power_levels/").respond(200, { users: { "alice@localhost": 50, }, }); - httpBackend! + httpBackend .when("PUT", "/state/m.room.power_levels") .check((req) => { expect(req.data.users).toStrictEqual(expectation); }) .respond(200, {}); - const prom = client!.setPowerLevel("!room_id:server", userId, powerLevel); - await httpBackend!.flushAllExpected(); + const prom = client.setPowerLevel("!room_id:server", userId, powerLevel); + await httpBackend.flushAllExpected(); await prom; }); it("should use power level from room state if available", async () => { - client!.clientRunning = true; - client!.isInitialSyncComplete = () => true; - const room = new Room("!room_id:server", client!, client!.getUserId()!); + client.clientRunning = true; + client.isInitialSyncComplete = () => true; + const room = new Room("!room_id:server", client, client.getUserId()!); room.currentState.events.set("m.room.power_levels", new Map()); room.currentState.events.get("m.room.power_levels")!.set( "", @@ -1422,9 +1422,9 @@ describe("MatrixClient", function () { }, }), ); - client!.getRoom = () => room; + client.getRoom = () => room; - httpBackend! + httpBackend .when("PUT", "/state/m.room.power_levels") .check((req) => { expect(req.data).toStrictEqual({ @@ -1436,29 +1436,29 @@ describe("MatrixClient", function () { }) .respond(200, {}); - const prom = client!.setPowerLevel("!room_id:server", userId, 42); - await httpBackend!.flushAllExpected(); + const prom = client.setPowerLevel("!room_id:server", userId, 42); + await httpBackend.flushAllExpected(); await prom; }); it("should throw error if state API errors", async () => { - httpBackend!.when("GET", "/state/m.room.power_levels/").respond(500, { + httpBackend.when("GET", "/state/m.room.power_levels/").respond(500, { errcode: "ERR_DERP", }); - const prom = client!.setPowerLevel("!room_id:server", userId, 42); + const prom = client.setPowerLevel("!room_id:server", userId, 42); await Promise.all([ expect(prom).rejects.toMatchInlineSnapshot(`[ERR_DERP: MatrixError: [500] Unknown message]`), - httpBackend!.flushAllExpected(), + httpBackend.flushAllExpected(), ]); }); it("should not throw error if /state/ API returns M_NOT_FOUND", async () => { - httpBackend!.when("GET", "/state/m.room.power_levels/").respond(404, { + httpBackend.when("GET", "/state/m.room.power_levels/").respond(404, { errcode: "M_NOT_FOUND", }); - httpBackend! + httpBackend .when("PUT", "/state/m.room.power_levels") .check((req) => { expect(req.data).toStrictEqual({ @@ -1469,8 +1469,8 @@ describe("MatrixClient", function () { }) .respond(200, {}); - const prom = client!.setPowerLevel("!room_id:server", userId, 42); - await httpBackend!.flushAllExpected(); + const prom = client.setPowerLevel("!room_id:server", userId, 42); + await httpBackend.flushAllExpected(); await prom; }); }); @@ -1478,42 +1478,42 @@ describe("MatrixClient", function () { describe("uploadKeys", () => { // uploadKeys() is a no-op nowadays, so there's not much to test here. it("should complete successfully", async () => { - await client!.uploadKeys(); + await client.uploadKeys(); }); }); describe("getCryptoTrustCrossSignedDevices", () => { it("should throw if e2e is disabled", () => { - expect(() => client!.getCryptoTrustCrossSignedDevices()).toThrow("End-to-end encryption disabled"); + expect(() => client.getCryptoTrustCrossSignedDevices()).toThrow("End-to-end encryption disabled"); }); it("should proxy to the crypto backend", async () => { const mockBackend = { getTrustCrossSignedDevices: jest.fn().mockReturnValue(true), } as unknown as Mocked; - client!["cryptoBackend"] = mockBackend; + client["cryptoBackend"] = mockBackend; - expect(client!.getCryptoTrustCrossSignedDevices()).toBe(true); + expect(client.getCryptoTrustCrossSignedDevices()).toBe(true); mockBackend.getTrustCrossSignedDevices.mockReturnValue(false); - expect(client!.getCryptoTrustCrossSignedDevices()).toBe(false); + expect(client.getCryptoTrustCrossSignedDevices()).toBe(false); }); }); describe("setCryptoTrustCrossSignedDevices", () => { it("should throw if e2e is disabled", () => { - expect(() => client!.setCryptoTrustCrossSignedDevices(false)).toThrow("End-to-end encryption disabled"); + expect(() => client.setCryptoTrustCrossSignedDevices(false)).toThrow("End-to-end encryption disabled"); }); it("should proxy to the crypto backend", async () => { const mockBackend = { setTrustCrossSignedDevices: jest.fn(), } as unknown as Mocked; - client!["cryptoBackend"] = mockBackend; + client["cryptoBackend"] = mockBackend; - client!.setCryptoTrustCrossSignedDevices(true); + client.setCryptoTrustCrossSignedDevices(true); expect(mockBackend.setTrustCrossSignedDevices).toHaveBeenLastCalledWith(true); - client!.setCryptoTrustCrossSignedDevices(false); + client.setCryptoTrustCrossSignedDevices(false); expect(mockBackend.setTrustCrossSignedDevices).toHaveBeenLastCalledWith(false); }); }); @@ -1523,10 +1523,73 @@ describe("MatrixClient", function () { const setPresence = jest.fn(); // @ts-ignore client.syncApi = { setPresence }; - client?.setSyncPresence(SetPresence.Unavailable); + client.setSyncPresence(SetPresence.Unavailable); expect(setPresence).toHaveBeenCalledWith(SetPresence.Unavailable); }); }); + + describe("sendTyping", () => { + it("should bail early for guests", async () => { + client.setGuest(true); + await client.sendTyping("!room:server", true, 100); + }); + + it("should call /typing API", async () => { + client.setGuest(false); + httpBackend + .when("PUT", "/rooms/!room%3Aserver/typing/" + encodeURIComponent(client.getSafeUserId())) + .check((req) => { + expect(req.data).toEqual({ + typing: true, + timeout: 100, + }); + }) + .respond(200, {}); + await Promise.all([client.sendTyping("!room:server", true, 100), httpBackend.flushAllExpected()]); + }); + }); + + describe("setGuestAccess", () => { + it("should set both guest access and history visibility if opts.allowRead=true", async () => { + httpBackend + .when("PUT", "/rooms/!room%3Aserver/state/m.room.guest_access/") + .check((req) => { + expect(req.data).toEqual({ + guest_access: "forbidden", + }); + }) + .respond(200, { event_id: "$event1" }); + httpBackend + .when("PUT", "/rooms/!room%3Aserver/state/m.room.history_visibility/") + .check((req) => { + expect(req.data).toEqual({ + history_visibility: "world_readable", + }); + }) + .respond(200, { event_id: "$event2" }); + await Promise.all([ + client.setGuestAccess("!room:server", { allowRead: true, allowJoin: false }), + httpBackend.flushAllExpected(), + ]); + }); + }); + + describe("searchUserDirectory", () => { + it("should call /user_directory/search API", async () => { + httpBackend + .when("POST", "/user_directory/search") + .check((req) => { + expect(req.data).toEqual({ + search_term: "This is my query", + }); + }) + .respond(200, {}); + await Promise.all([ + client.searchUserDirectory({ term: "This is my query" }), + httpBackend.flushAllExpected(), + ]); + }); + }); }); function withThreadId(event: MatrixEvent, newThreadId: string): MatrixEvent { diff --git a/src/client.ts b/src/client.ts index e84a19d0e..16da17917 100644 --- a/src/client.ts +++ b/src/client.ts @@ -71,6 +71,7 @@ import { UploadResponse, HTTPError, IRequestOpts, + Body, } from "./http-api"; import { Crypto, @@ -2012,12 +2013,11 @@ export class MatrixClient extends TypedEventEmitterThis method is experimental * and may change without warning. * @param guest - True if this is a guest account. + * @experimental if the token is a macaroon, it should be encoded in it that it is a 'guest' + * access token, which means that the SDK can determine this entirely without + * the dev manually flipping this flag. */ public setGuest(guest: boolean): void { - // EXPERIMENTAL: - // If the token is a macaroon, it should be encoded in it that it is a 'guest' - // access token, which means that the SDK can determine this entirely without - // the dev manually flipping this flag. this.isGuestAccount = guest; } @@ -4405,7 +4405,7 @@ export class MatrixClient extends TypedEventEmitter, txnId?: string, ): Promise { if (!txnId) { @@ -4982,7 +4982,12 @@ export class MatrixClient extends TypedEventEmitter { + public async sendReceipt( + event: MatrixEvent, + receiptType: ReceiptType, + body?: Record, + unthreaded = false, + ): Promise<{}> { if (this.isGuest()) { return Promise.resolve({}); // guests cannot send receipts so don't bother. } @@ -5140,7 +5145,7 @@ export class MatrixClient extends TypedEventEmitter[] = []; + const promises: Promise[] = []; const doLeave = (roomId: string): Promise => { return this.leave(roomId) @@ -5935,7 +5940,7 @@ export class MatrixClient extends TypedEventEmitter = Promise.resolve(undefined); + let readPromise: Promise = Promise.resolve(); if (opts.allowRead) { readPromise = this.sendStateEvent( roomId, @@ -6599,7 +6604,7 @@ export class MatrixClient extends TypedEventEmitter( endpoint: string, - params: Record, + params: QueryDict, ): Promise { const postParams = Object.assign({}, params); @@ -7949,8 +7954,11 @@ export class MatrixClient extends TypedEventEmitter { - const body: any = {}; + public deactivateAccount( + auth?: AuthDict, + erase?: boolean, + ): Promise<{ id_server_unbind_result: IdServerUnbindResult }> { + const body: Body = {}; if (auth) { body.auth = auth; } @@ -8188,7 +8196,7 @@ export class MatrixClient extends TypedEventEmitter { @@ -8406,13 +8414,13 @@ export class MatrixClient extends TypedEventEmitter { - // TODO: Types const path = utils.encodeUri("/directory/list/appservice/$networkId/$roomId", { $networkId: networkId, $roomId: roomId, @@ -8428,7 +8436,7 @@ export class MatrixClient extends TypedEventEmitter { - const body: any = { + const body: Body = { search_term: term, }; @@ -8506,14 +8514,12 @@ export class MatrixClient extends TypedEventEmitter { - // TODO: Types + public addThreePid(creds: IBindThreePidBody, bind: boolean): Promise<{ submit_url?: string }> { const path = "/account/3pid"; const data = { threePidCreds: creds, @@ -8673,7 +8679,7 @@ export class MatrixClient extends TypedEventEmitter { - const body: any = { devices }; + const body: Body = { devices }; if (auth) { body.auth = auth; @@ -8868,7 +8874,7 @@ export class MatrixClient extends TypedEventEmitter { - const queryParams: any = {}; + const queryParams: QueryDict = {}; if (nextBatch) { queryParams.next_batch = nextBatch; } @@ -9488,7 +9494,7 @@ export class MatrixClient extends TypedEventEmitter { + public getThirdpartyUser(protocol: string, params?: QueryDict): Promise { const path = utils.encodeUri("/thirdparty/user/$protocol", { $protocol: protocol, }); @@ -9664,7 +9670,7 @@ export class MatrixClient extends TypedEventEmitter { - const qps: Record = {}; + const qps: QueryDict = {}; if (req.pos) { qps.pos = req.pos; delete req.pos; diff --git a/src/crypto/CrossSigning.ts b/src/crypto/CrossSigning.ts index e71967cb2..aa1c218c8 100644 --- a/src/crypto/CrossSigning.ts +++ b/src/crypto/CrossSigning.ts @@ -18,8 +18,7 @@ limitations under the License. * Cross signing methods */ -import { PkSigning } from "@matrix-org/olm"; - +import type { PkSigning } from "@matrix-org/olm"; import { decodeBase64, encodeBase64, IObject, pkSign, pkVerify } from "./olmlib"; import { logger } from "../logger"; import { IndexedDBCryptoStore } from "../crypto/store/indexeddb-crypto-store"; @@ -245,7 +244,7 @@ export class CrossSigningInfo { * @returns A map from key type (string) to private key (Uint8Array) */ public async getCrossSigningKeysFromCache(): Promise> { - const keys = new Map(); + const keys = new Map(); const cacheCallbacks = this.cacheCallbacks; if (!cacheCallbacks) return keys; for (const type of ["master", "self_signing", "user_signing"]) { @@ -294,8 +293,8 @@ export class CrossSigningInfo { const privateKeys: Record = {}; const keys: Record = {}; - let masterSigning; - let masterPub; + let masterSigning: PkSigning | undefined; + let masterPub: string | undefined; try { if (level & CrossSigningLevel.MASTER) { diff --git a/src/crypto/index.ts b/src/crypto/index.ts index 4d96216be..beeefa546 100644 --- a/src/crypto/index.ts +++ b/src/crypto/index.ts @@ -48,7 +48,7 @@ import { InRoomChannel, InRoomRequests } from "./verification/request/InRoomChan import { Request, ToDeviceChannel, ToDeviceRequests } from "./verification/request/ToDeviceChannel"; import { IllegalMethod } from "./verification/IllegalMethod"; import { KeySignatureUploadError } from "../errors"; -import { calculateKeyCheck, decryptAES, encryptAES } from "./aes"; +import { calculateKeyCheck, decryptAES, encryptAES, IEncryptedPayload } from "./aes"; import { DehydrationManager } from "./dehydration"; import { BackupManager } from "./backup"; import { IStore } from "../store"; @@ -1242,8 +1242,7 @@ export class Crypto extends TypedEventEmitter { - let key = await new Promise((resolve) => { - // TODO types + let key = await new Promise((resolve) => { this.cryptoStore.doTxn("readonly", [IndexedDBCryptoStore.STORE_ACCOUNT], (txn) => { this.cryptoStore.getSecretStorePrivateKey(txn, resolve, "m.megolm_backup.v1"); }); @@ -1254,7 +1253,7 @@ export class Crypto extends TypedEventEmitter, - ): Promise { + ): Promise { // Check if the 'device' is actually a cross signing key // The js-sdk's verification treats cross-signing keys as devices // and so uses this method to mark them verified. const xsk = this.deviceList.getStoredCrossSigningForUser(userId); - if (xsk && xsk.getId() === deviceId) { + if (xsk?.getId() === deviceId) { if (blocked !== null || known !== null) { throw new Error("Cannot set blocked or known for a cross-signing key"); } @@ -2257,7 +2256,7 @@ export class Crypto extends TypedEventEmitter | BodyInit; - interface TypedResponse extends Response { json(): Promise; } diff --git a/src/http-api/interface.ts b/src/http-api/interface.ts index 9946aa37b..ae9400f38 100644 --- a/src/http-api/interface.ts +++ b/src/http-api/interface.ts @@ -16,6 +16,8 @@ limitations under the License. import { MatrixError } from "./errors"; +export type Body = Record | BodyInit; + export interface IHttpOpts { fetchFn?: typeof global.fetch; diff --git a/src/rendezvous/MSC3906Rendezvous.ts b/src/rendezvous/MSC3906Rendezvous.ts index 3528a66de..de7be644d 100644 --- a/src/rendezvous/MSC3906Rendezvous.ts +++ b/src/rendezvous/MSC3906Rendezvous.ts @@ -17,7 +17,12 @@ limitations under the License. import { UnstableValue } from "matrix-events-sdk"; import { RendezvousChannel, RendezvousFailureListener, RendezvousFailureReason, RendezvousIntent } from "."; -import { IMSC3882GetLoginTokenCapability, MatrixClient, UNSTABLE_MSC3882_CAPABILITY } from "../client"; +import { + ICrossSigningKey, + IMSC3882GetLoginTokenCapability, + MatrixClient, + UNSTABLE_MSC3882_CAPABILITY, +} from "../client"; import { CrossSigningInfo } from "../crypto/CrossSigning"; import { DeviceInfo } from "../crypto/deviceinfo"; import { buildFeatureSupportMap, Feature, ServerSupport } from "../feature"; @@ -178,7 +183,9 @@ export class MSC3906Rendezvous { return deviceId; } - private async verifyAndCrossSignDevice(deviceInfo: DeviceInfo): Promise { + private async verifyAndCrossSignDevice( + deviceInfo: DeviceInfo, + ): Promise { if (!this.client.crypto) { throw new Error("Crypto not available on client"); } @@ -223,7 +230,7 @@ export class MSC3906Rendezvous { */ public async verifyNewDeviceOnExistingDevice( timeout = 10 * 1000, - ): Promise { + ): Promise { if (!this.newDeviceId) { throw new Error("No new device to sign"); } diff --git a/src/webrtc/stats/media/mediaTrackHandler.ts b/src/webrtc/stats/media/mediaTrackHandler.ts index 787ce1a64..31f1264b3 100644 --- a/src/webrtc/stats/media/mediaTrackHandler.ts +++ b/src/webrtc/stats/media/mediaTrackHandler.ts @@ -23,14 +23,13 @@ export class MediaTrackHandler { const isNotNullAndKind = (track: MediaStreamTrack | null): boolean => { return track !== null && track.kind === kind; }; - // @ts-ignore The linter don't get it return this.pc .getTransceivers() .filter((t) => t.currentDirection === "sendonly" || t.currentDirection === "sendrecv") .filter((t) => t.sender !== null) .map((t) => t.sender) .map((s) => s.track) - .filter(isNotNullAndKind); + .filter(isNotNullAndKind) as MediaStreamTrack[]; } public getTackById(trackId: string): MediaStreamTrack | undefined {