From a1b046b5d88212c0776dea2fc3725fe2234a3168 Mon Sep 17 00:00:00 2001 From: Kerry Date: Thu, 6 Oct 2022 08:11:25 +0200 Subject: [PATCH] test typescriptification - spec/integ (#2714) * renamed: spec/integ/devicelist-integ.spec.js -> spec/integ/devicelist-integ.spec.ts * fix ts issue in devicelist-integ.spec * renamed: spec/integ/matrix-client-event-emitter.spec.js -> spec/integ/matrix-client-event-emitter.spec.ts * ts issues in matrix-client-event-emitter integ * strict fixes * renamed: spec/integ/matrix-client-methods.spec.js -> spec/integ/matrix-client-methods.spec.ts * fix ts issues * renamed: spec/integ/matrix-client-opts.spec.js -> spec/integ/matrix-client-opts.spec.ts * ts fixes in matrix-client-methods / matrix-client-opts * renamed: spec/integ/matrix-client-room-timeline.spec.js -> spec/integ/matrix-client-room-timeline.spec.ts * most ts fixes in matrix-client-room-timeline * remove obsoleted prev_events from mockenvents * make xmlhttprequest ts * strict errors in matrix-client-event-timeline spec * strict in devicelist * strict fixes in matrix-client-crypto.spec * strict fixes in spec/integ/matrix-client-room-timeline * strict issues in matrix-client-opts.specc * strict issues in matrix-client-syncing * strict issues in spec/integ/megolm * strict fixes in spec/integ/matrix-client-retrying.spec * strict fixes for spec/integ/sliding-sync * eslint fixes * more strict errors sneaking in from develop * kill al httpbackends * kill matrix-client-methods.spec httpbackend properly --- .../{setupTests.js => setupTests.ts} | 2 + ...integ.spec.js => devicelist-integ.spec.ts} | 19 +- spec/integ/matrix-client-crypto.spec.ts | 34 +- ...js => matrix-client-event-emitter.spec.ts} | 254 +++++------ .../matrix-client-event-timeline.spec.ts | 281 ++++++------ ....spec.js => matrix-client-methods.spec.ts} | 414 +++++++++++------- ...pts.spec.js => matrix-client-opts.spec.ts} | 16 +- spec/integ/matrix-client-retrying.spec.ts | 52 ++- ...js => matrix-client-room-timeline.spec.ts} | 320 +++++++------- spec/integ/matrix-client-syncing.spec.ts | 130 +++--- spec/integ/megolm-backup.spec.ts | 37 +- spec/integ/megolm-integ.spec.ts | 124 +++--- spec/integ/sliding-sync-sdk.spec.ts | 180 ++++---- spec/integ/sliding-sync.spec.ts | 156 +++---- 14 files changed, 1074 insertions(+), 945 deletions(-) rename spec/browserify/{setupTests.js => setupTests.ts} (95%) rename spec/integ/{devicelist-integ.spec.js => devicelist-integ.spec.ts} (95%) rename spec/integ/{matrix-client-event-emitter.spec.js => matrix-client-event-emitter.spec.ts} (51%) rename spec/integ/{matrix-client-methods.spec.js => matrix-client-methods.spec.ts} (77%) rename spec/integ/{matrix-client-opts.spec.js => matrix-client-opts.spec.ts} (93%) rename spec/integ/{matrix-client-room-timeline.spec.js => matrix-client-room-timeline.spec.ts} (76%) diff --git a/spec/browserify/setupTests.js b/spec/browserify/setupTests.ts similarity index 95% rename from spec/browserify/setupTests.js rename to spec/browserify/setupTests.ts index 16120f78a..833d8591c 100644 --- a/spec/browserify/setupTests.js +++ b/spec/browserify/setupTests.ts @@ -15,9 +15,11 @@ limitations under the License. */ // stub for browser-matrix browserify tests +// @ts-ignore global.XMLHttpRequest = jest.fn(); afterAll(() => { // clean up XMLHttpRequest mock + // @ts-ignore global.XMLHttpRequest = undefined; }); diff --git a/spec/integ/devicelist-integ.spec.js b/spec/integ/devicelist-integ.spec.ts similarity index 95% rename from spec/integ/devicelist-integ.spec.js rename to spec/integ/devicelist-integ.spec.ts index 8be2ca59a..acd8f9c80 100644 --- a/spec/integ/devicelist-integ.spec.js +++ b/spec/integ/devicelist-integ.spec.ts @@ -122,7 +122,7 @@ describe("DeviceList management:", function() { aliceTestClient.httpBackend.when( 'PUT', '/send/', ).respond(200, { - event_id: '$event_id', + event_id: '$event_id', }); return Promise.all([ @@ -290,8 +290,9 @@ describe("DeviceList management:", function() { aliceTestClient.client.cryptoStore.getEndToEndDeviceData(null, (data) => { const bobStat = data.trackingStatus['@bob:xyz']; + // Alice should be tracking bob's device list expect(bobStat).toBeGreaterThan( - 0, "Alice should be tracking bob's device list", + 0, ); }); }); @@ -326,8 +327,9 @@ describe("DeviceList management:", function() { aliceTestClient.client.cryptoStore.getEndToEndDeviceData(null, (data) => { const bobStat = data.trackingStatus['@bob:xyz']; + // Alice should have marked bob's device list as untracked expect(bobStat).toEqual( - 0, "Alice should have marked bob's device list as untracked", + 0, ); }); }); @@ -362,8 +364,9 @@ describe("DeviceList management:", function() { aliceTestClient.client.cryptoStore.getEndToEndDeviceData(null, (data) => { const bobStat = data.trackingStatus['@bob:xyz']; + // Alice should have marked bob's device list as untracked expect(bobStat).toEqual( - 0, "Alice should have marked bob's device list as untracked", + 0, ); }); }); @@ -378,13 +381,15 @@ describe("DeviceList management:", function() { anotherTestClient.httpBackend.when('GET', '/sync').respond( 200, getSyncResponse([])); await anotherTestClient.flushSync(); - await anotherTestClient.client.crypto.deviceList.saveIfDirty(); + await anotherTestClient.client?.crypto?.deviceList?.saveIfDirty(); + // @ts-ignore accessing private property anotherTestClient.client.cryptoStore.getEndToEndDeviceData(null, (data) => { - const bobStat = data.trackingStatus['@bob:xyz']; + const bobStat = data!.trackingStatus['@bob:xyz']; + // Alice should have marked bob's device list as untracked expect(bobStat).toEqual( - 0, "Alice should have marked bob's device list as untracked", + 0, ); }); } finally { diff --git a/spec/integ/matrix-client-crypto.spec.ts b/spec/integ/matrix-client-crypto.spec.ts index 262975ead..f86394979 100644 --- a/spec/integ/matrix-client-crypto.spec.ts +++ b/spec/integ/matrix-client-crypto.spec.ts @@ -31,8 +31,9 @@ import '../olm-loader'; import { logger } from '../../src/logger'; import * as testUtils from "../test-utils/test-utils"; import { TestClient } from "../TestClient"; -import { CRYPTO_ENABLED } from "../../src/client"; +import { CRYPTO_ENABLED, IUploadKeysRequest } from "../../src/client"; import { ClientEvent, IContent, ISendEventResponse, MatrixClient, MatrixEvent } from "../../src/matrix"; +import { DeviceInfo } from '../../src/crypto/deviceinfo'; let aliTestClient: TestClient; const roomId = "!room:localhost"; @@ -71,12 +72,12 @@ function expectQueryKeys(querier: TestClient, uploader: TestClient): Promise { const keys = await bobTestClient.awaitOneTimeKeyUpload(); aliTestClient.httpBackend.when( "POST", "/keys/claim", - ).respond(200, function(_path, content) { - const claimType = content.one_time_keys[bobUserId][bobDeviceId]; + ).respond(200, function(_path, content: IUploadKeysRequest) { + const claimType = content.one_time_keys![bobUserId][bobDeviceId]; expect(claimType).toEqual("signed_curve25519"); - let keyId = null; + let keyId = ''; for (keyId in keys) { if (bobTestClient.oneTimeKeys.hasOwnProperty(keyId)) { if (keyId.indexOf(claimType + ":") === 0) { @@ -135,10 +136,10 @@ async function aliDownloadsKeys(): Promise { await aliTestClient.client.crypto!.deviceList.saveIfDirty(); // @ts-ignore - protected aliTestClient.client.cryptoStore.getEndToEndDeviceData(null, (data) => { - const devices = data.devices[bobUserId]; + const devices = data!.devices[bobUserId]!; expect(devices[bobDeviceId].keys).toEqual(bobTestClient.deviceKeys.keys); expect(devices[bobDeviceId].verified). - toBe(0); // DeviceVerification.UNVERIFIED + toBe(DeviceInfo.DeviceVerification.UNVERIFIED); }); } @@ -237,7 +238,7 @@ function sendMessage(client: MatrixClient): Promise { async function expectSendMessageRequest(httpBackend: TestClient["httpBackend"]): Promise { const path = "/send/m.room.encrypted/"; - const prom = new Promise((resolve) => { + const prom = new Promise((resolve) => { httpBackend.when("PUT", path).respond(200, function(_path, content) { resolve(content); return { @@ -252,14 +253,14 @@ async function expectSendMessageRequest(httpBackend: TestClient["httpBackend"]): } function aliRecvMessage(): Promise { - const message = bobMessages.shift(); + const message = bobMessages.shift()!; return recvMessage( aliTestClient.httpBackend, aliTestClient.client, bobUserId, message, ); } function bobRecvMessage(): Promise { - const message = aliMessages.shift(); + const message = aliMessages.shift()!; return recvMessage( bobTestClient.httpBackend, bobTestClient.client, aliUserId, message, ); @@ -509,7 +510,7 @@ describe("MatrixClient crypto", () => { await firstSync(aliTestClient); await aliEnablesEncryption(); await aliSendsFirstMessage(); - const message = aliMessages.shift(); + const message = aliMessages.shift()!; const syncData = { next_batch: "x", rooms: { @@ -664,11 +665,10 @@ describe("MatrixClient crypto", () => { ]); logger.log(aliTestClient + ': started'); httpBackend.when("POST", "/keys/upload") - .respond(200, (_path, content) => { + .respond(200, (_path, content: IUploadKeysRequest) => { expect(content.one_time_keys).toBeTruthy(); expect(content.one_time_keys).not.toEqual({}); - expect(Object.keys(content.one_time_keys).length).toBeGreaterThanOrEqual(1); - logger.log('received %i one-time keys', Object.keys(content.one_time_keys).length); + expect(Object.keys(content.one_time_keys!).length).toBeGreaterThanOrEqual(1); // cancel futher calls by telling the client // we have more than we need return { diff --git a/spec/integ/matrix-client-event-emitter.spec.js b/spec/integ/matrix-client-event-emitter.spec.ts similarity index 51% rename from spec/integ/matrix-client-event-emitter.spec.js rename to spec/integ/matrix-client-event-emitter.spec.ts index bb3c873b3..1ad244b54 100644 --- a/spec/integ/matrix-client-event-emitter.spec.js +++ b/spec/integ/matrix-client-event-emitter.spec.ts @@ -1,25 +1,59 @@ +/* +Copyright 2022 The Matrix.org Foundation C.I.C. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +import HttpBackend from "matrix-mock-request"; + +import { + ClientEvent, + HttpApiEvent, + IEvent, + MatrixClient, + RoomEvent, + RoomMemberEvent, + RoomStateEvent, + UserEvent, +} from "../../src"; import * as utils from "../test-utils/test-utils"; import { TestClient } from "../TestClient"; describe("MatrixClient events", function() { - let client; - let httpBackend; const selfUserId = "@alice:localhost"; const selfAccessToken = "aseukfgwef"; + let client: MatrixClient | undefined; + let httpBackend: HttpBackend | undefined; + + const setupTests = (): [MatrixClient, HttpBackend] => { + const testClient = new TestClient(selfUserId, "DEVICE", selfAccessToken); + const client = testClient.client; + const httpBackend = testClient.httpBackend; + httpBackend!.when("GET", "/versions").respond(200, {}); + httpBackend!.when("GET", "/pushrules").respond(200, {}); + httpBackend!.when("POST", "/filter").respond(200, { filter_id: "a filter id" }); + + return [client!, httpBackend]; + }; beforeEach(function() { - const testClient = new TestClient(selfUserId, "DEVICE", selfAccessToken); - client = testClient.client; - httpBackend = testClient.httpBackend; - httpBackend.when("GET", "/versions").respond(200, {}); - httpBackend.when("GET", "/pushrules").respond(200, {}); - httpBackend.when("POST", "/filter").respond(200, { filter_id: "a filter id" }); + [client!, httpBackend] = setupTests(); }); afterEach(function() { - httpBackend.verifyNoOutstandingExpectation(); - client.stopClient(); - return httpBackend.stop(); + httpBackend?.verifyNoOutstandingExpectation(); + client?.stopClient(); + return httpBackend?.stop(); }); describe("emissions", function() { @@ -92,53 +126,49 @@ describe("MatrixClient events", function() { }; it("should emit events from both the first and subsequent /sync calls", - function() { - httpBackend.when("GET", "/sync").respond(200, SYNC_DATA); - httpBackend.when("GET", "/sync").respond(200, NEXT_SYNC_DATA); + function() { + httpBackend!.when("GET", "/sync").respond(200, SYNC_DATA); + httpBackend!.when("GET", "/sync").respond(200, NEXT_SYNC_DATA); - let expectedEvents = []; - expectedEvents = expectedEvents.concat( - SYNC_DATA.presence.events, - SYNC_DATA.rooms.join["!erufh:bar"].timeline.events, - SYNC_DATA.rooms.join["!erufh:bar"].state.events, - NEXT_SYNC_DATA.rooms.join["!erufh:bar"].timeline.events, - NEXT_SYNC_DATA.rooms.join["!erufh:bar"].ephemeral.events, - ); + let expectedEvents: Partial[] = []; + expectedEvents = expectedEvents.concat( + SYNC_DATA.presence.events, + SYNC_DATA.rooms.join["!erufh:bar"].timeline.events, + SYNC_DATA.rooms.join["!erufh:bar"].state.events, + NEXT_SYNC_DATA.rooms.join["!erufh:bar"].timeline.events, + NEXT_SYNC_DATA.rooms.join["!erufh:bar"].ephemeral.events, + ); - client.on("event", function(event) { - let found = false; - for (let i = 0; i < expectedEvents.length; i++) { - if (expectedEvents[i].event_id === event.getId()) { - expectedEvents.splice(i, 1); - found = true; - break; + client!.on(ClientEvent.Event, function(event) { + let found = false; + for (let i = 0; i < expectedEvents.length; i++) { + if (expectedEvents[i].event_id === event.getId()) { + expectedEvents.splice(i, 1); + found = true; + break; + } } - } - expect(found).toBe( - true, "Unexpected 'event' emitted: " + event.getType(), - ); - }); + expect(found).toBe(true); + }); - client.startClient(); + client!.startClient(); - return Promise.all([ + return Promise.all([ // wait for two SYNCING events - utils.syncPromise(client).then(() => { - return utils.syncPromise(client); - }), - httpBackend.flushAllExpected(), - ]).then(() => { - expect(expectedEvents.length).toEqual( - 0, "Failed to see all events from /sync calls", - ); + utils.syncPromise(client!).then(() => { + return utils.syncPromise(client!); + }), + httpBackend!.flushAllExpected(), + ]).then(() => { + expect(expectedEvents.length).toEqual(0); + }); }); - }); it("should emit User events", function(done) { - httpBackend.when("GET", "/sync").respond(200, SYNC_DATA); - httpBackend.when("GET", "/sync").respond(200, NEXT_SYNC_DATA); + httpBackend!.when("GET", "/sync").respond(200, SYNC_DATA); + httpBackend!.when("GET", "/sync").respond(200, NEXT_SYNC_DATA); let fired = false; - client.on("User.presence", function(event, user) { + client!.on(UserEvent.Presence, function(event, user) { fired = true; expect(user).toBeTruthy(); expect(event).toBeTruthy(); @@ -146,58 +176,52 @@ describe("MatrixClient events", function() { return; } - expect(event.event).toMatch(SYNC_DATA.presence.events[0]); + expect(event.event).toEqual(SYNC_DATA.presence.events[0]); expect(user.presence).toEqual( - SYNC_DATA.presence.events[0].content.presence, + SYNC_DATA.presence.events[0]?.content?.presence, ); }); - client.startClient(); + client!.startClient(); - httpBackend.flushAllExpected().then(function() { - expect(fired).toBe(true, "User.presence didn't fire."); + httpBackend!.flushAllExpected().then(function() { + expect(fired).toBe(true); done(); }); }); it("should emit Room events", function() { - httpBackend.when("GET", "/sync").respond(200, SYNC_DATA); - httpBackend.when("GET", "/sync").respond(200, NEXT_SYNC_DATA); + httpBackend!.when("GET", "/sync").respond(200, SYNC_DATA); + httpBackend!.when("GET", "/sync").respond(200, NEXT_SYNC_DATA); let roomInvokeCount = 0; let roomNameInvokeCount = 0; let timelineFireCount = 0; - client.on("Room", function(room) { + client!.on(ClientEvent.Room, function(room) { roomInvokeCount++; expect(room.roomId).toEqual("!erufh:bar"); }); - client.on("Room.timeline", function(event, room) { + client!.on(RoomEvent.Timeline, function(event, room) { timelineFireCount++; expect(room.roomId).toEqual("!erufh:bar"); }); - client.on("Room.name", function(room) { + client!.on(RoomEvent.Name, function(room) { roomNameInvokeCount++; }); - client.startClient(); + client!.startClient(); return Promise.all([ - httpBackend.flushAllExpected(), - utils.syncPromise(client, 2), + httpBackend!.flushAllExpected(), + utils.syncPromise(client!, 2), ]).then(function() { - expect(roomInvokeCount).toEqual( - 1, "Room fired wrong number of times.", - ); - expect(roomNameInvokeCount).toEqual( - 1, "Room.name fired wrong number of times.", - ); - expect(timelineFireCount).toEqual( - 3, "Room.timeline fired the wrong number of times", - ); + expect(roomInvokeCount).toEqual(1); + expect(roomNameInvokeCount).toEqual(1); + expect(timelineFireCount).toEqual(3); }); }); it("should emit RoomState events", function() { - httpBackend.when("GET", "/sync").respond(200, SYNC_DATA); - httpBackend.when("GET", "/sync").respond(200, NEXT_SYNC_DATA); + httpBackend!.when("GET", "/sync").respond(200, SYNC_DATA); + httpBackend!.when("GET", "/sync").respond(200, NEXT_SYNC_DATA); const roomStateEventTypes = [ "m.room.member", "m.room.create", @@ -205,126 +229,106 @@ describe("MatrixClient events", function() { let eventsInvokeCount = 0; let membersInvokeCount = 0; let newMemberInvokeCount = 0; - client.on("RoomState.events", function(event, state) { + client!.on(RoomStateEvent.Events, function(event, state) { eventsInvokeCount++; const index = roomStateEventTypes.indexOf(event.getType()); - expect(index).not.toEqual( - -1, "Unexpected room state event type: " + event.getType(), - ); + expect(index).not.toEqual(-1); if (index >= 0) { roomStateEventTypes.splice(index, 1); } }); - client.on("RoomState.members", function(event, state, member) { + client!.on(RoomStateEvent.Members, function(event, state, member) { membersInvokeCount++; expect(member.roomId).toEqual("!erufh:bar"); expect(member.userId).toEqual("@foo:bar"); expect(member.membership).toEqual("join"); }); - client.on("RoomState.newMember", function(event, state, member) { + client!.on(RoomStateEvent.NewMember, function(event, state, member) { newMemberInvokeCount++; expect(member.roomId).toEqual("!erufh:bar"); expect(member.userId).toEqual("@foo:bar"); expect(member.membership).toBeFalsy(); }); - client.startClient(); + client!.startClient(); return Promise.all([ - httpBackend.flushAllExpected(), - utils.syncPromise(client, 2), + httpBackend!.flushAllExpected(), + utils.syncPromise(client!, 2), ]).then(function() { - expect(membersInvokeCount).toEqual( - 1, "RoomState.members fired wrong number of times", - ); - expect(newMemberInvokeCount).toEqual( - 1, "RoomState.newMember fired wrong number of times", - ); - expect(eventsInvokeCount).toEqual( - 2, "RoomState.events fired wrong number of times", - ); + expect(membersInvokeCount).toEqual(1); + expect(newMemberInvokeCount).toEqual(1); + expect(eventsInvokeCount).toEqual(2); }); }); it("should emit RoomMember events", function() { - httpBackend.when("GET", "/sync").respond(200, SYNC_DATA); - httpBackend.when("GET", "/sync").respond(200, NEXT_SYNC_DATA); + httpBackend!.when("GET", "/sync").respond(200, SYNC_DATA); + httpBackend!.when("GET", "/sync").respond(200, NEXT_SYNC_DATA); let typingInvokeCount = 0; let powerLevelInvokeCount = 0; let nameInvokeCount = 0; let membershipInvokeCount = 0; - client.on("RoomMember.name", function(event, member) { + client!.on(RoomMemberEvent.Name, function(event, member) { nameInvokeCount++; }); - client.on("RoomMember.typing", function(event, member) { + client!.on(RoomMemberEvent.Typing, function(event, member) { typingInvokeCount++; expect(member.typing).toBe(true); }); - client.on("RoomMember.powerLevel", function(event, member) { + client!.on(RoomMemberEvent.PowerLevel, function(event, member) { powerLevelInvokeCount++; }); - client.on("RoomMember.membership", function(event, member) { + client!.on(RoomMemberEvent.Membership, function(event, member) { membershipInvokeCount++; expect(member.membership).toEqual("join"); }); - client.startClient(); + client!.startClient(); return Promise.all([ - httpBackend.flushAllExpected(), - utils.syncPromise(client, 2), + httpBackend!.flushAllExpected(), + utils.syncPromise(client!, 2), ]).then(function() { - expect(typingInvokeCount).toEqual( - 1, "RoomMember.typing fired wrong number of times", - ); - expect(powerLevelInvokeCount).toEqual( - 0, "RoomMember.powerLevel fired wrong number of times", - ); - expect(nameInvokeCount).toEqual( - 0, "RoomMember.name fired wrong number of times", - ); - expect(membershipInvokeCount).toEqual( - 1, "RoomMember.membership fired wrong number of times", - ); + expect(typingInvokeCount).toEqual(1); + expect(powerLevelInvokeCount).toEqual(0); + expect(nameInvokeCount).toEqual(0); + expect(membershipInvokeCount).toEqual(1); }); }); it("should emit Session.logged_out on M_UNKNOWN_TOKEN", function() { const error = { errcode: 'M_UNKNOWN_TOKEN' }; - httpBackend.when("GET", "/sync").respond(401, error); + httpBackend!.when("GET", "/sync").respond(401, error); let sessionLoggedOutCount = 0; - client.on("Session.logged_out", function(errObj) { + client!.on(HttpApiEvent.SessionLoggedOut, function(errObj) { sessionLoggedOutCount++; expect(errObj.data).toEqual(error); }); - client.startClient(); + client!.startClient(); - return httpBackend.flushAllExpected().then(function() { - expect(sessionLoggedOutCount).toEqual( - 1, "Session.logged_out fired wrong number of times", - ); + return httpBackend!.flushAllExpected().then(function() { + expect(sessionLoggedOutCount).toEqual(1); }); }); it("should emit Session.logged_out on M_UNKNOWN_TOKEN (soft logout)", function() { const error = { errcode: 'M_UNKNOWN_TOKEN', soft_logout: true }; - httpBackend.when("GET", "/sync").respond(401, error); + httpBackend!.when("GET", "/sync").respond(401, error); let sessionLoggedOutCount = 0; - client.on("Session.logged_out", function(errObj) { + client!.on(HttpApiEvent.SessionLoggedOut, function(errObj) { sessionLoggedOutCount++; expect(errObj.data).toEqual(error); }); - client.startClient(); + client!.startClient(); - return httpBackend.flushAllExpected().then(function() { - expect(sessionLoggedOutCount).toEqual( - 1, "Session.logged_out fired wrong number of times", - ); + return httpBackend!.flushAllExpected().then(function() { + expect(sessionLoggedOutCount).toEqual(1); }); }); }); diff --git a/spec/integ/matrix-client-event-timeline.spec.ts b/spec/integ/matrix-client-event-timeline.spec.ts index 8640e82be..f2bfa5f6a 100644 --- a/spec/integ/matrix-client-event-timeline.spec.ts +++ b/spec/integ/matrix-client-event-timeline.spec.ts @@ -142,6 +142,7 @@ const THREAD_REPLY = utils.mkEvent({ event: false, }); +// @ts-ignore we know this is a defined path for THREAD ROOT THREAD_ROOT.unsigned["m.relations"]["io.element.thread"].latest_event = THREAD_REPLY; const SYNC_THREAD_ROOT = withoutRoomId(THREAD_ROOT); @@ -214,8 +215,8 @@ describe("getEventTimeline support", function() { httpBackend = testClient.httpBackend; return startClient(httpBackend, client).then(function() { - const room = client.getRoom(roomId); - const timelineSet = room.getTimelineSets()[0]; + const room = client.getRoom(roomId)!; + const timelineSet = room!.getTimelineSets()[0]; expect(client.getEventTimeline(timelineSet, "event")).rejects.toBeTruthy(); }); }); @@ -232,8 +233,8 @@ describe("getEventTimeline support", function() { httpBackend = testClient.httpBackend; return startClient(httpBackend, client).then(() => { - const room = client.getRoom(roomId); - const timelineSet = room.getTimelineSets()[0]; + const room = client.getRoom(roomId)!; + const timelineSet = room!.getTimelineSets()[0]; expect(client.getEventTimeline(timelineSet, "event")).rejects.toBeFalsy(); }); }); @@ -257,10 +258,10 @@ describe("getEventTimeline support", function() { it("scrollback should be able to scroll back to before a gappy /sync", function() { // need a client with timelineSupport disabled to make this work - let room: Room; + let room: Room | undefined; return startClient(httpBackend, client).then(function() { - room = client.getRoom(roomId); + room = client.getRoom(roomId)!; httpBackend.when("GET", "/sync").respond(200, { next_batch: "s_5_4", @@ -300,8 +301,8 @@ describe("getEventTimeline support", function() { utils.syncPromise(client, 2), ]); }).then(function() { - expect(room.timeline.length).toEqual(1); - expect(room.timeline[0].event).toEqual(EVENTS[1]); + expect(room!.timeline.length).toEqual(1); + expect(room!.timeline[0].event).toEqual(EVENTS[1]); httpBackend.when("GET", "/messages").respond(200, { chunk: [EVENTS[0]], @@ -309,12 +310,12 @@ describe("getEventTimeline support", function() { end: "pagin_end", }); httpBackend.flush("/messages", 1); - return client.scrollback(room); + return client.scrollback(room!); }).then(function() { - expect(room.timeline.length).toEqual(2); - expect(room.timeline[0].event).toEqual(EVENTS[0]); - expect(room.timeline[1].event).toEqual(EVENTS[1]); - expect(room.oldState.paginationToken).toEqual("pagin_end"); + expect(room!.timeline.length).toEqual(2); + expect(room!.timeline[0].event).toEqual(EVENTS[0]); + expect(room!.timeline[1].event).toEqual(EVENTS[1]); + expect(room!.oldState.paginationToken).toEqual("pagin_end"); }); }); }); @@ -345,7 +346,7 @@ describe("MatrixClient event timelines", function() { describe("getEventTimeline", function() { it("should create a new timeline for new events", function() { - const room = client.getRoom(roomId); + const room = client.getRoom(roomId)!; const timelineSet = room.getTimelineSets()[0]; httpBackend.when("GET", "/rooms/!foo%3Abar/context/event1%3Abar") .respond(200, function() { @@ -364,14 +365,14 @@ describe("MatrixClient event timelines", function() { return Promise.all([ client.getEventTimeline(timelineSet, "event1:bar").then(function(tl) { - expect(tl.getEvents().length).toEqual(4); + expect(tl!.getEvents().length).toEqual(4); for (let i = 0; i < 4; i++) { - expect(tl.getEvents()[i].event).toEqual(EVENTS[i]); - expect(tl.getEvents()[i].sender.name).toEqual(userName); + expect(tl!.getEvents()[i].event).toEqual(EVENTS[i]); + expect(tl!.getEvents()[i]?.sender.name).toEqual(userName); } - expect(tl.getPaginationToken(EventTimeline.BACKWARDS)) + expect(tl!.getPaginationToken(EventTimeline.BACKWARDS)) .toEqual("start_token"); - expect(tl.getPaginationToken(EventTimeline.FORWARDS)) + expect(tl!.getPaginationToken(EventTimeline.FORWARDS)) .toEqual("end_token"); }), httpBackend.flushAllExpected(), @@ -379,7 +380,7 @@ describe("MatrixClient event timelines", function() { }); it("should return existing timeline for known events", function() { - const room = client.getRoom(roomId); + const room = client.getRoom(roomId)!; const timelineSet = room.getTimelineSets()[0]; httpBackend.when("GET", "/sync").respond(200, { next_batch: "s_5_4", @@ -401,12 +402,12 @@ describe("MatrixClient event timelines", function() { httpBackend.flush("/sync"), utils.syncPromise(client), ]).then(function() { - return client.getEventTimeline(timelineSet, EVENTS[0].event_id); + return client.getEventTimeline(timelineSet, EVENTS[0].event_id!); }).then(function(tl) { - expect(tl.getEvents().length).toEqual(2); - expect(tl.getEvents()[1].event).toEqual(EVENTS[0]); - expect(tl.getEvents()[1].sender.name).toEqual(userName); - expect(tl.getPaginationToken(EventTimeline.BACKWARDS)) + expect(tl!.getEvents().length).toEqual(2); + expect(tl!.getEvents()[1].event).toEqual(EVENTS[0]); + expect(tl!.getEvents()[1]?.sender.name).toEqual(userName); + expect(tl!.getPaginationToken(EventTimeline.BACKWARDS)) .toEqual("f_1_1"); // expect(tl.getPaginationToken(EventTimeline.FORWARDS)) // .toEqual("s_5_4"); @@ -414,7 +415,7 @@ describe("MatrixClient event timelines", function() { }); it("should update timelines where they overlap a previous /sync", function() { - const room = client.getRoom(roomId); + const room = client.getRoom(roomId)!; const timelineSet = room.getTimelineSets()[0]; httpBackend.when("GET", "/sync").respond(200, { next_batch: "s_5_4", @@ -433,7 +434,7 @@ describe("MatrixClient event timelines", function() { }); httpBackend.when("GET", "/rooms/!foo%3Abar/context/" + - encodeURIComponent(EVENTS[2].event_id)) + encodeURIComponent(EVENTS[2].event_id!)) .respond(200, function() { return { start: "start_token", @@ -447,13 +448,13 @@ describe("MatrixClient event timelines", function() { const prom = new Promise((resolve, reject) => { client.on(ClientEvent.Sync, function() { - client.getEventTimeline(timelineSet, EVENTS[2].event_id, + client.getEventTimeline(timelineSet, EVENTS[2].event_id!, ).then(function(tl) { - expect(tl.getEvents().length).toEqual(4); - expect(tl.getEvents()[0].event).toEqual(EVENTS[1]); - expect(tl.getEvents()[1].event).toEqual(EVENTS[2]); - expect(tl.getEvents()[3].event).toEqual(EVENTS[3]); - expect(tl.getPaginationToken(EventTimeline.BACKWARDS)) + expect(tl!.getEvents().length).toEqual(4); + expect(tl!.getEvents()[0].event).toEqual(EVENTS[1]); + expect(tl!.getEvents()[1].event).toEqual(EVENTS[2]); + expect(tl!.getEvents()[3].event).toEqual(EVENTS[3]); + expect(tl!.getPaginationToken(EventTimeline.BACKWARDS)) .toEqual("start_token"); // expect(tl.getPaginationToken(EventTimeline.FORWARDS)) // .toEqual("s_5_4"); @@ -468,13 +469,13 @@ describe("MatrixClient event timelines", function() { }); it("should join timelines where they overlap a previous /context", function() { - const room = client.getRoom(roomId); + const room = client.getRoom(roomId)!; const timelineSet = room.getTimelineSets()[0]; // we fetch event 0, then 2, then 3, and finally 1. 1 is returned // with context which joins them all up. httpBackend.when("GET", "/rooms/!foo%3Abar/context/" + - encodeURIComponent(EVENTS[0].event_id)) + encodeURIComponent(EVENTS[0].event_id!)) .respond(200, function() { return { start: "start_token0", @@ -487,7 +488,7 @@ describe("MatrixClient event timelines", function() { }); httpBackend.when("GET", "/rooms/!foo%3Abar/context/" + - encodeURIComponent(EVENTS[2].event_id)) + encodeURIComponent(EVENTS[2].event_id!)) .respond(200, function() { return { start: "start_token2", @@ -500,7 +501,7 @@ describe("MatrixClient event timelines", function() { }); httpBackend.when("GET", "/rooms/!foo%3Abar/context/" + - encodeURIComponent(EVENTS[3].event_id)) + encodeURIComponent(EVENTS[3].event_id!)) .respond(200, function() { return { start: "start_token3", @@ -513,7 +514,7 @@ describe("MatrixClient event timelines", function() { }); httpBackend.when("GET", "/rooms/!foo%3Abar/context/" + - encodeURIComponent(EVENTS[1].event_id)) + encodeURIComponent(EVENTS[1].event_id!)) .respond(200, function() { return { start: "start_token4", @@ -528,26 +529,26 @@ describe("MatrixClient event timelines", function() { let tl0; let tl3; return Promise.all([ - client.getEventTimeline(timelineSet, EVENTS[0].event_id, + client.getEventTimeline(timelineSet, EVENTS[0].event_id!, ).then(function(tl) { - expect(tl.getEvents().length).toEqual(1); + expect(tl!.getEvents().length).toEqual(1); tl0 = tl; - return client.getEventTimeline(timelineSet, EVENTS[2].event_id); + return client.getEventTimeline(timelineSet, EVENTS[2].event_id!); }).then(function(tl) { - expect(tl.getEvents().length).toEqual(1); - return client.getEventTimeline(timelineSet, EVENTS[3].event_id); + expect(tl!.getEvents().length).toEqual(1); + return client.getEventTimeline(timelineSet, EVENTS[3].event_id!); }).then(function(tl) { - expect(tl.getEvents().length).toEqual(1); + expect(tl!.getEvents().length).toEqual(1); tl3 = tl; - return client.getEventTimeline(timelineSet, EVENTS[1].event_id); + return client.getEventTimeline(timelineSet, EVENTS[1].event_id!); }).then(function(tl) { // we expect it to get merged in with event 2 - expect(tl.getEvents().length).toEqual(2); - expect(tl.getEvents()[0].event).toEqual(EVENTS[1]); - expect(tl.getEvents()[1].event).toEqual(EVENTS[2]); - expect(tl.getNeighbouringTimeline(EventTimeline.BACKWARDS)) + expect(tl!.getEvents().length).toEqual(2); + expect(tl!.getEvents()[0].event).toEqual(EVENTS[1]); + expect(tl!.getEvents()[1].event).toEqual(EVENTS[2]); + expect(tl!.getNeighbouringTimeline(EventTimeline.BACKWARDS)) .toBe(tl0); - expect(tl.getNeighbouringTimeline(EventTimeline.FORWARDS)) + expect(tl!.getNeighbouringTimeline(EventTimeline.FORWARDS)) .toBe(tl3); expect(tl0.getPaginationToken(EventTimeline.BACKWARDS)) .toEqual("start_token0"); @@ -563,7 +564,7 @@ describe("MatrixClient event timelines", function() { }); it("should fail gracefully if there is no event field", function() { - const room = client.getRoom(roomId); + const room = client.getRoom(roomId)!; const timelineSet = room.getTimelineSets()[0]; // we fetch event 0, then 2, then 3, and finally 1. 1 is returned // with context which joins them all up. @@ -595,11 +596,11 @@ describe("MatrixClient event timelines", function() { client.clientOpts.experimentalThreadSupport = true; Thread.setServerSideSupport(FeatureSupport.Experimental); client.stopClient(); // we don't need the client to be syncing at this time - const room = client.getRoom(roomId); - const thread = room.createThread(THREAD_ROOT.event_id, undefined, [], false); + const room = client.getRoom(roomId)!; + const thread = room.createThread(THREAD_ROOT.event_id!, undefined, [], false); const timelineSet = thread.timelineSet; - httpBackend.when("GET", "/rooms/!foo%3Abar/context/" + encodeURIComponent(THREAD_REPLY.event_id)) + httpBackend.when("GET", "/rooms/!foo%3Abar/context/" + encodeURIComponent(THREAD_REPLY.event_id!)) .respond(200, function() { return { start: "start_token0", @@ -611,13 +612,13 @@ describe("MatrixClient event timelines", function() { }; }); - httpBackend.when("GET", "/rooms/!foo%3Abar/event/" + encodeURIComponent(THREAD_ROOT.event_id)) + httpBackend.when("GET", "/rooms/!foo%3Abar/event/" + encodeURIComponent(THREAD_ROOT.event_id!)) .respond(200, function() { return THREAD_ROOT; }); httpBackend.when("GET", "/rooms/!foo%3Abar/relations/" + - encodeURIComponent(THREAD_ROOT.event_id) + "/" + + encodeURIComponent(THREAD_ROOT.event_id!) + "/" + encodeURIComponent(THREAD_RELATION_TYPE.name) + "?limit=20") .respond(200, function() { return { @@ -627,13 +628,13 @@ describe("MatrixClient event timelines", function() { }; }); - const timelinePromise = client.getEventTimeline(timelineSet, THREAD_REPLY.event_id); + const timelinePromise = client.getEventTimeline(timelineSet, THREAD_REPLY.event_id!); await httpBackend.flushAllExpected(); const timeline = await timelinePromise; - expect(timeline.getEvents().find(e => e.getId() === THREAD_ROOT.event_id)).toBeTruthy(); - expect(timeline.getEvents().find(e => e.getId() === THREAD_REPLY.event_id)).toBeTruthy(); + expect(timeline!.getEvents().find(e => e.getId() === THREAD_ROOT.event_id!)).toBeTruthy(); + expect(timeline!.getEvents().find(e => e.getId() === THREAD_REPLY.event_id!)).toBeTruthy(); }); it("should return relevant timeline from non-thread timelineSet when asking for the thread root", async () => { @@ -641,12 +642,12 @@ describe("MatrixClient event timelines", function() { client.clientOpts.experimentalThreadSupport = true; Thread.setServerSideSupport(FeatureSupport.Experimental); client.stopClient(); // we don't need the client to be syncing at this time - const room = client.getRoom(roomId); + const room = client.getRoom(roomId)!; const threadRoot = new MatrixEvent(THREAD_ROOT); - const thread = room.createThread(THREAD_ROOT.event_id, threadRoot, [threadRoot], false); - const timelineSet = room.getTimelineSets()[0]; + const thread = room.createThread(THREAD_ROOT.event_id!, threadRoot, [threadRoot], false)!; + const timelineSet = room.getTimelineSets()[0]!; - httpBackend.when("GET", "/rooms/!foo%3Abar/context/" + encodeURIComponent(THREAD_ROOT.event_id)) + httpBackend.when("GET", "/rooms/!foo%3Abar/context/" + encodeURIComponent(THREAD_ROOT.event_id!)) .respond(200, function() { return { start: "start_token0", @@ -659,13 +660,13 @@ describe("MatrixClient event timelines", function() { }); const [timeline] = await Promise.all([ - client.getEventTimeline(timelineSet, THREAD_ROOT.event_id), + client.getEventTimeline(timelineSet, THREAD_ROOT.event_id!), httpBackend.flushAllExpected(), ]); - expect(timeline).not.toBe(thread.liveTimeline); + expect(timeline!).not.toBe(thread.liveTimeline); expect(timelineSet.getTimelines()).toContain(timeline); - expect(timeline.getEvents().find(e => e.getId() === THREAD_ROOT.event_id)).toBeTruthy(); + expect(timeline!.getEvents().find(e => e.getId() === THREAD_ROOT.event_id!)).toBeTruthy(); }); it("should return undefined when event is not in the thread that the given timelineSet is representing", () => { @@ -673,12 +674,12 @@ describe("MatrixClient event timelines", function() { client.clientOpts.experimentalThreadSupport = true; Thread.setServerSideSupport(FeatureSupport.Experimental); client.stopClient(); // we don't need the client to be syncing at this time - const room = client.getRoom(roomId); + const room = client.getRoom(roomId)!; const threadRoot = new MatrixEvent(THREAD_ROOT); - const thread = room.createThread(THREAD_ROOT.event_id, threadRoot, [threadRoot], false); + const thread = room.createThread(THREAD_ROOT.event_id!, threadRoot, [threadRoot], false); const timelineSet = thread.timelineSet; - httpBackend.when("GET", "/rooms/!foo%3Abar/context/" + encodeURIComponent(EVENTS[0].event_id)) + httpBackend.when("GET", "/rooms/!foo%3Abar/context/" + encodeURIComponent(EVENTS[0].event_id!)) .respond(200, function() { return { start: "start_token0", @@ -691,7 +692,7 @@ describe("MatrixClient event timelines", function() { }); return Promise.all([ - expect(client.getEventTimeline(timelineSet, EVENTS[0].event_id)).resolves.toBeUndefined(), + expect(client.getEventTimeline(timelineSet, EVENTS[0].event_id!)).resolves.toBeUndefined(), httpBackend.flushAllExpected(), ]); }); @@ -701,10 +702,10 @@ describe("MatrixClient event timelines", function() { client.clientOpts.experimentalThreadSupport = true; Thread.setServerSideSupport(FeatureSupport.Experimental); client.stopClient(); // we don't need the client to be syncing at this time - const room = client.getRoom(roomId); + const room = client.getRoom(roomId)!; const timelineSet = room.getTimelineSets()[0]; - httpBackend.when("GET", "/rooms/!foo%3Abar/context/" + encodeURIComponent(THREAD_REPLY.event_id)) + httpBackend.when("GET", "/rooms/!foo%3Abar/context/" + encodeURIComponent(THREAD_REPLY.event_id!)) .respond(200, function() { return { start: "start_token0", @@ -717,7 +718,7 @@ describe("MatrixClient event timelines", function() { }); return Promise.all([ - expect(client.getEventTimeline(timelineSet, THREAD_REPLY.event_id)).resolves.toBeUndefined(), + expect(client.getEventTimeline(timelineSet, THREAD_REPLY.event_id!)).resolves.toBeUndefined(), httpBackend.flushAllExpected(), ]); }); @@ -726,10 +727,10 @@ describe("MatrixClient event timelines", function() { // @ts-ignore client.clientOpts.lazyLoadMembers = true; client.stopClient(); // we don't need the client to be syncing at this time - const room = client.getRoom(roomId); + const room = client.getRoom(roomId)!; const timelineSet = room.getTimelineSets()[0]; - const req = httpBackend.when("GET", "/rooms/!foo%3Abar/context/" + encodeURIComponent(EVENTS[0].event_id)); + const req = httpBackend.when("GET", "/rooms/!foo%3Abar/context/" + encodeURIComponent(EVENTS[0].event_id!)); req.respond(200, function() { return { start: "start_token0", @@ -741,11 +742,11 @@ describe("MatrixClient event timelines", function() { }; }); req.check((request) => { - expect(request.queryParams.filter).toEqual(JSON.stringify(Filter.LAZY_LOADING_MESSAGES_FILTER)); + expect(request.queryParams?.filter).toEqual(JSON.stringify(Filter.LAZY_LOADING_MESSAGES_FILTER)); }); await Promise.all([ - client.getEventTimeline(timelineSet, EVENTS[0].event_id), + client.getEventTimeline(timelineSet, EVENTS[0].event_id!), httpBackend.flushAllExpected(), ]); }); @@ -810,7 +811,7 @@ describe("MatrixClient event timelines", function() { }); it("should create a new timeline for new events", function() { - const room = client.getRoom(roomId); + const room = client.getRoom(roomId)!; const timelineSet = room.getTimelineSets()[0]; const latestMessageId = 'event1:bar'; @@ -845,14 +846,14 @@ describe("MatrixClient event timelines", function() { // for `getEventTimeline` and make sure it's called with the // correct parameters. This doesn't feel too bad to make sure // `getLatestTimeline` is doing the right thing though. - expect(tl.getEvents().length).toEqual(4); + expect(tl!.getEvents().length).toEqual(4); for (let i = 0; i < 4; i++) { - expect(tl.getEvents()[i].event).toEqual(EVENTS[i]); - expect(tl.getEvents()[i].sender.name).toEqual(userName); + expect(tl!.getEvents()[i].event).toEqual(EVENTS[i]); + expect(tl!.getEvents()[i]?.sender.name).toEqual(userName); } - expect(tl.getPaginationToken(EventTimeline.BACKWARDS)) + expect(tl!.getPaginationToken(EventTimeline.BACKWARDS)) .toEqual("start_token"); - expect(tl.getPaginationToken(EventTimeline.FORWARDS)) + expect(tl!.getPaginationToken(EventTimeline.FORWARDS)) .toEqual("end_token"); }), httpBackend.flushAllExpected(), @@ -860,7 +861,7 @@ describe("MatrixClient event timelines", function() { }); it("should throw error when /messages does not return a message", () => { - const room = client.getRoom(roomId); + const room = client.getRoom(roomId)!; const timelineSet = room.getTimelineSets()[0]; httpBackend.when("GET", "/rooms/!foo%3Abar/messages") @@ -881,11 +882,11 @@ describe("MatrixClient event timelines", function() { describe("paginateEventTimeline", function() { it("should allow you to paginate backwards", function() { - const room = client.getRoom(roomId); + const room = client.getRoom(roomId)!; const timelineSet = room.getTimelineSets()[0]; httpBackend.when("GET", "/rooms/!foo%3Abar/context/" + - encodeURIComponent(EVENTS[0].event_id)) + encodeURIComponent(EVENTS[0].event_id!)) .respond(200, function() { return { start: "start_token0", @@ -899,7 +900,7 @@ describe("MatrixClient event timelines", function() { httpBackend.when("GET", "/rooms/!foo%3Abar/messages") .check(function(req) { - const params = req.queryParams; + const params = req.queryParams!; expect(params.dir).toEqual("b"); expect(params.from).toEqual("start_token0"); expect(params.limit).toEqual("30"); @@ -912,19 +913,19 @@ describe("MatrixClient event timelines", function() { let tl; return Promise.all([ - client.getEventTimeline(timelineSet, EVENTS[0].event_id, + client.getEventTimeline(timelineSet, EVENTS[0].event_id!, ).then(function(tl0) { tl = tl0; return client.paginateEventTimeline(tl, { backwards: true }); }).then(function(success) { expect(success).toBeTruthy(); - expect(tl.getEvents().length).toEqual(3); - expect(tl.getEvents()[0].event).toEqual(EVENTS[2]); - expect(tl.getEvents()[1].event).toEqual(EVENTS[1]); - expect(tl.getEvents()[2].event).toEqual(EVENTS[0]); - expect(tl.getPaginationToken(EventTimeline.BACKWARDS)) + expect(tl!.getEvents().length).toEqual(3); + expect(tl!.getEvents()[0].event).toEqual(EVENTS[2]); + expect(tl!.getEvents()[1].event).toEqual(EVENTS[1]); + expect(tl!.getEvents()[2].event).toEqual(EVENTS[0]); + expect(tl!.getPaginationToken(EventTimeline.BACKWARDS)) .toEqual("start_token1"); - expect(tl.getPaginationToken(EventTimeline.FORWARDS)) + expect(tl!.getPaginationToken(EventTimeline.FORWARDS)) .toEqual("end_token0"); }), httpBackend.flushAllExpected(), @@ -932,11 +933,11 @@ describe("MatrixClient event timelines", function() { }); it("should stop paginating when it encounters no `end` token", () => { - const room = client.getRoom(roomId); + const room = client.getRoom(roomId)!; const timelineSet = room.getTimelineSets()[0]; httpBackend.when("GET", "/rooms/!foo%3Abar/context/" + - encodeURIComponent(EVENTS[0].event_id)) + encodeURIComponent(EVENTS[0].event_id!)) .respond(200, () => ({ start: "start_token0", events_before: [], @@ -948,7 +949,7 @@ describe("MatrixClient event timelines", function() { httpBackend.when("GET", "/rooms/!foo%3Abar/messages") .check(function(req) { - const params = req.queryParams; + const params = req.queryParams!; expect(params.dir).toEqual("b"); expect(params.from).toEqual("start_token0"); expect(params.limit).toEqual("30"); @@ -959,23 +960,23 @@ describe("MatrixClient event timelines", function() { return Promise.all([ (async () => { - const tl = await client.getEventTimeline(timelineSet, EVENTS[0].event_id); - const success = await client.paginateEventTimeline(tl, { backwards: true }); + const tl = await client.getEventTimeline(timelineSet, EVENTS[0].event_id!); + const success = await client.paginateEventTimeline(tl!, { backwards: true }); expect(success).toBeFalsy(); - expect(tl.getEvents().length).toEqual(1); - expect(tl.getPaginationToken(EventTimeline.BACKWARDS)).toEqual(null); - expect(tl.getPaginationToken(EventTimeline.FORWARDS)).toEqual("end_token0"); + expect(tl!.getEvents().length).toEqual(1); + expect(tl!.getPaginationToken(EventTimeline.BACKWARDS)).toEqual(null); + expect(tl!.getPaginationToken(EventTimeline.FORWARDS)).toEqual("end_token0"); })(), httpBackend.flushAllExpected(), ]); }); it("should allow you to paginate forwards", function() { - const room = client.getRoom(roomId); + const room = client.getRoom(roomId)!; const timelineSet = room.getTimelineSets()[0]; httpBackend.when("GET", "/rooms/!foo%3Abar/context/" + - encodeURIComponent(EVENTS[0].event_id)) + encodeURIComponent(EVENTS[0].event_id!)) .respond(200, function() { return { start: "start_token0", @@ -989,7 +990,7 @@ describe("MatrixClient event timelines", function() { httpBackend.when("GET", "/rooms/!foo%3Abar/messages") .check(function(req) { - const params = req.queryParams; + const params = req.queryParams!; expect(params.dir).toEqual("f"); expect(params.from).toEqual("end_token0"); expect(params.limit).toEqual("20"); @@ -1002,20 +1003,20 @@ describe("MatrixClient event timelines", function() { let tl; return Promise.all([ - client.getEventTimeline(timelineSet, EVENTS[0].event_id, + client.getEventTimeline(timelineSet, EVENTS[0].event_id!, ).then(function(tl0) { tl = tl0; return client.paginateEventTimeline( tl, { backwards: false, limit: 20 }); }).then(function(success) { expect(success).toBeTruthy(); - expect(tl.getEvents().length).toEqual(3); - expect(tl.getEvents()[0].event).toEqual(EVENTS[0]); - expect(tl.getEvents()[1].event).toEqual(EVENTS[1]); - expect(tl.getEvents()[2].event).toEqual(EVENTS[2]); - expect(tl.getPaginationToken(EventTimeline.BACKWARDS)) + expect(tl!.getEvents().length).toEqual(3); + expect(tl!.getEvents()[0].event).toEqual(EVENTS[0]); + expect(tl!.getEvents()[1].event).toEqual(EVENTS[1]); + expect(tl!.getEvents()[2].event).toEqual(EVENTS[2]); + expect(tl!.getPaginationToken(EventTimeline.BACKWARDS)) .toEqual("start_token0"); - expect(tl.getPaginationToken(EventTimeline.FORWARDS)) + expect(tl!.getPaginationToken(EventTimeline.FORWARDS)) .toEqual("end_token1"); }), httpBackend.flushAllExpected(), @@ -1285,17 +1286,17 @@ describe("MatrixClient event timelines", function() { }); it("should work when /send returns before /sync", function() { - const room = client.getRoom(roomId); - const timelineSet = room.getTimelineSets()[0]; + const room = client.getRoom(roomId)!; + const timelineSet = room.getTimelineSets()[0]!; return Promise.all([ client.sendTextMessage(roomId, "a body", TXN_ID).then(function(res) { - expect(res.event_id).toEqual(event.event_id); - return client.getEventTimeline(timelineSet, event.event_id); + expect(res.event_id).toEqual(event.event_id!); + return client.getEventTimeline(timelineSet, event.event_id!); }).then(function(tl) { // 2 because the initial sync contained an event - expect(tl.getEvents().length).toEqual(2); - expect(tl.getEvents()[1].getContent().body).toEqual("a body"); + expect(tl!.getEvents().length).toEqual(2); + expect(tl!.getEvents()[1].getContent().body).toEqual("a body"); // now let the sync complete, and check it again return Promise.all([ @@ -1303,10 +1304,10 @@ describe("MatrixClient event timelines", function() { utils.syncPromise(client), ]); }).then(function() { - return client.getEventTimeline(timelineSet, event.event_id); + return client.getEventTimeline(timelineSet, event.event_id!); }).then(function(tl) { - expect(tl.getEvents().length).toEqual(2); - expect(tl.getEvents()[1].event).toEqual(event); + expect(tl!.getEvents().length).toEqual(2); + expect(tl!.getEvents()[1].event).toEqual(event); }), httpBackend.flush("/send/m.room.message/" + TXN_ID, 1), @@ -1314,7 +1315,7 @@ describe("MatrixClient event timelines", function() { }); it("should work when /send returns after /sync", function() { - const room = client.getRoom(roomId); + const room = client.getRoom(roomId)!; const timelineSet = room.getTimelineSets()[0]; return Promise.all([ @@ -1322,23 +1323,23 @@ describe("MatrixClient event timelines", function() { // - but note that it won't complete until after the /sync does, below. client.sendTextMessage(roomId, "a body", TXN_ID).then(function(res) { logger.log("sendTextMessage completed"); - expect(res.event_id).toEqual(event.event_id); - return client.getEventTimeline(timelineSet, event.event_id); + expect(res.event_id).toEqual(event.event_id!); + return client.getEventTimeline(timelineSet, event.event_id!); }).then(function(tl) { logger.log("getEventTimeline completed (2)"); - expect(tl.getEvents().length).toEqual(2); - expect(tl.getEvents()[1].getContent().body).toEqual("a body"); + expect(tl!.getEvents().length).toEqual(2); + expect(tl!.getEvents()[1].getContent().body).toEqual("a body"); }), Promise.all([ httpBackend.flush("/sync", 1), utils.syncPromise(client), ]).then(function() { - return client.getEventTimeline(timelineSet, event.event_id); + return client.getEventTimeline(timelineSet, event.event_id!); }).then(function(tl) { logger.log("getEventTimeline completed (1)"); - expect(tl.getEvents().length).toEqual(2); - expect(tl.getEvents()[1].event).toEqual(event); + expect(tl!.getEvents().length).toEqual(2); + expect(tl!.getEvents()[1].event).toEqual(event); // now let the send complete. return httpBackend.flush("/send/m.room.message/" + TXN_ID, 1); @@ -1382,10 +1383,10 @@ describe("MatrixClient event timelines", function() { httpBackend.flushAllExpected(), utils.syncPromise(client), ]).then(function() { - const room = client.getRoom(roomId); - const tl = room.getLiveTimeline(); - expect(tl.getEvents().length).toEqual(3); - expect(tl.getEvents()[1].isRedacted()).toBe(true); + const room = client.getRoom(roomId)!; + const tl = room.getLiveTimeline()!; + expect(tl!.getEvents().length).toEqual(3); + expect(tl!.getEvents()[1].isRedacted()).toBe(true); const sync2 = { next_batch: "batch2", @@ -1411,8 +1412,8 @@ describe("MatrixClient event timelines", function() { utils.syncPromise(client), ]); }).then(function() { - const room = client.getRoom(roomId); - const tl = room.getLiveTimeline(); + const room = client.getRoom(roomId)!; + const tl = room.getLiveTimeline()!; expect(tl.getEvents().length).toEqual(1); }); }); @@ -1439,11 +1440,11 @@ describe("MatrixClient event timelines", function() { }); await Promise.all([httpBackend.flushAllExpected(), utils.syncPromise(client)]); - const room = client.getRoom(roomId); - const thread = room.getThread(THREAD_ROOT.event_id); + const room = client.getRoom(roomId)!; + const thread = room.getThread(THREAD_ROOT.event_id!)!; const timelineSet = thread.timelineSet; - httpBackend.when("GET", "/rooms/!foo%3Abar/context/" + encodeURIComponent(THREAD_ROOT.event_id)) + httpBackend.when("GET", "/rooms/!foo%3Abar/context/" + encodeURIComponent(THREAD_ROOT.event_id!)) .respond(200, { start: "start_token", events_before: [], @@ -1453,7 +1454,7 @@ describe("MatrixClient event timelines", function() { end: "end_token", }); httpBackend.when("GET", "/rooms/!foo%3Abar/relations/" + - encodeURIComponent(THREAD_ROOT.event_id) + "/" + + encodeURIComponent(THREAD_ROOT.event_id!) + "/" + encodeURIComponent(THREAD_RELATION_TYPE.name) + "?limit=20") .respond(200, function() { return { @@ -1463,7 +1464,7 @@ describe("MatrixClient event timelines", function() { }; }); await Promise.all([ - client.getEventTimeline(timelineSet, THREAD_ROOT.event_id), + client.getEventTimeline(timelineSet, THREAD_ROOT.event_id!), httpBackend.flushAllExpected(), ]); diff --git a/spec/integ/matrix-client-methods.spec.js b/spec/integ/matrix-client-methods.spec.ts similarity index 77% rename from spec/integ/matrix-client-methods.spec.js rename to spec/integ/matrix-client-methods.spec.ts index 69bfa89ca..fdf32d9e9 100644 --- a/spec/integ/matrix-client-methods.spec.js +++ b/spec/integ/matrix-client-methods.spec.ts @@ -13,68 +13,84 @@ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. */ +import HttpBackend from "matrix-mock-request"; import * as utils from "../test-utils/test-utils"; -import { CRYPTO_ENABLED } from "../../src/client"; +import { CRYPTO_ENABLED, MatrixClient, IStoredClientOpts } from "../../src/client"; import { MatrixEvent } from "../../src/models/event"; import { Filter, MemoryStore, Room } from "../../src/matrix"; import { TestClient } from "../TestClient"; import { THREAD_RELATION_TYPE } from "../../src/models/thread"; +import { IFilterDefinition } from "../../src/filter"; +import { FileType } from "../../src/http-api"; +import { ISearchResults } from "../../src/@types/search"; +import { IStore } from "../../src/store"; describe("MatrixClient", function() { - let client = null; - let httpBackend = null; - let store = null; const userId = "@alice:localhost"; 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; - beforeEach(function() { - store = new MemoryStore(); + const defaultClientOpts: IStoredClientOpts = { + canResetEntireTimeline: roomId => false, + experimentalThreadSupport: false, + crypto: {} as unknown as IStoredClientOpts['crypto'], + }; + const setupTests = (): [MatrixClient, HttpBackend, MemoryStore] => { + const store = new MemoryStore(); const testClient = new TestClient(userId, "aliceDevice", accessToken, undefined, { - store, + store: store as IStore, identityServer: { getAccessToken: () => Promise.resolve(identityAccessToken), }, idBaseUrl: `https://${idServerDomain}`, }); - httpBackend = testClient.httpBackend; - client = testClient.client; + + return [testClient.client, testClient.httpBackend, store]; + }; + + beforeEach(function() { + [client, httpBackend, store] = setupTests(); }); afterEach(function() { - httpBackend.verifyNoOutstandingExpectation(); - return httpBackend.stop(); + httpBackend!.verifyNoOutstandingExpectation(); + return httpBackend!.stop(); }); describe("uploadContent", function() { const buf = Buffer.from('hello world'); it("should upload the file", function() { - httpBackend.when( + httpBackend!.when( "POST", "/_matrix/media/r0/upload", ).check(function(req) { expect(req.rawData).toEqual(buf); - expect(req.queryParams.filename).toEqual("hi.txt"); - if (!(req.queryParams.access_token == accessToken || + expect(req.queryParams?.filename).toEqual("hi.txt"); + if (!(req.queryParams?.access_token == accessToken || req.headers["Authorization"] == "Bearer " + accessToken)) { expect(true).toBe(false); } expect(req.headers["Content-Type"]).toEqual("text/plain"); + // @ts-ignore private property expect(req.opts.json).toBeFalsy(); + // @ts-ignore private property expect(req.opts.timeout).toBe(undefined); }).respond(200, "content", true); - const prom = client.uploadContent({ + const prom = client!.uploadContent({ stream: buf, name: "hi.txt", type: "text/plain", - }); + } as unknown as FileType); 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); @@ -83,51 +99,53 @@ describe("MatrixClient", function() { // for backwards compatibility, we return the raw JSON expect(response).toEqual("content"); - const uploads = client.getCurrentUploads(); + const uploads = client!.getCurrentUploads(); expect(uploads.length).toEqual(0); }); - httpBackend.flush(); + httpBackend!.flush(''); return prom2; }); it("should parse the response if rawResponse=false", function() { - httpBackend.when( + httpBackend!.when( "POST", "/_matrix/media/r0/upload", ).check(function(req) { + // @ts-ignore private property expect(req.opts.json).toBeFalsy(); }).respond(200, { "content_uri": "uri" }); - const prom = client.uploadContent({ + const prom = client!.uploadContent({ stream: buf, name: "hi.txt", type: "text/plain", - }, { + } as unknown as FileType, { rawResponse: false, }).then(function(response) { expect(response.content_uri).toEqual("uri"); }); - httpBackend.flush(); + httpBackend!.flush(''); return prom; }); it("should parse errors into a MatrixError", function() { - httpBackend.when( + httpBackend!.when( "POST", "/_matrix/media/r0/upload", ).check(function(req) { expect(req.rawData).toEqual(buf); + // @ts-ignore private property expect(req.opts.json).toBeFalsy(); }).respond(400, { "errcode": "M_SNAFU", "error": "broken", }); - const prom = client.uploadContent({ + const prom = client!.uploadContent({ stream: buf, name: "hi.txt", type: "text/plain", - }).then(function(response) { + } as unknown as FileType).then(function(response) { throw Error("request not failed"); }, function(error) { expect(error.httpStatus).toEqual(400); @@ -135,18 +153,18 @@ describe("MatrixClient", function() { expect(error.message).toEqual("broken"); }); - httpBackend.flush(); + httpBackend!.flush(''); return prom; }); it("should return a promise which can be cancelled", function() { - const prom = client.uploadContent({ + const prom = client!.uploadContent({ stream: buf, name: "hi.txt", type: "text/plain", - }); + } as unknown as FileType); - 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); @@ -156,11 +174,11 @@ describe("MatrixClient", function() { }, function(error) { expect(error).toEqual("aborted"); - const uploads = client.getCurrentUploads(); + const uploads = client!.getCurrentUploads(); expect(uploads.length).toEqual(0); }); - const r = client.cancelUpload(prom); + const r = client!.cancelUpload(prom); expect(r).toBe(true); return prom2; }); @@ -169,17 +187,20 @@ describe("MatrixClient", function() { 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 = () => Promise.resolve({}); + const room = new Room(roomId, client!, userId); + client!.fetchRoomEvent = () => Promise.resolve({ + type: 'test', + content: {}, + }); room.addLiveEvents([ utils.mkMembership({ user: userId, room: roomId, mship: "join", event: true, }), ]); - httpBackend.verifyNoOutstandingRequests(); - store.storeRoom(room); - client.joinRoom(roomId); - httpBackend.verifyNoOutstandingRequests(); + httpBackend!.verifyNoOutstandingRequests(); + store!.storeRoom(room); + client!.joinRoom(roomId); + httpBackend!.verifyNoOutstandingRequests(); }); }); @@ -190,67 +211,67 @@ describe("MatrixClient", function() { const filter = Filter.fromJson(userId, filterId, { event_format: "client", }); - store.storeFilter(filter); - client.getFilter(userId, filterId, true).then(function(gotFilter) { + store!.storeFilter(filter); + client!.getFilter(userId, filterId, true).then(function(gotFilter) { expect(gotFilter).toEqual(filter); done(); }); - httpBackend.verifyNoOutstandingRequests(); + httpBackend!.verifyNoOutstandingRequests(); }); it("should do an HTTP request if !allowCached even if one exists", - function(done) { - const httpFilterDefinition = { - event_format: "federation", - }; + function(done) { + const httpFilterDefinition = { + event_format: "federation", + }; - httpBackend.when( - "GET", "/user/" + encodeURIComponent(userId) + "/filter/" + filterId, - ).respond(200, httpFilterDefinition); + httpBackend!.when( + "GET", "/user/" + encodeURIComponent(userId) + "/filter/" + filterId, + ).respond(200, httpFilterDefinition); - const storeFilter = Filter.fromJson(userId, filterId, { - event_format: "client", + const storeFilter = Filter.fromJson(userId, filterId, { + event_format: "client", + }); + store!.storeFilter(storeFilter); + client!.getFilter(userId, filterId, false).then(function(gotFilter) { + expect(gotFilter.getDefinition()).toEqual(httpFilterDefinition); + done(); + }); + + httpBackend!.flush(''); }); - store.storeFilter(storeFilter); - client.getFilter(userId, filterId, false).then(function(gotFilter) { - expect(gotFilter.getDefinition()).toEqual(httpFilterDefinition); - done(); - }); - - httpBackend.flush(); - }); it("should do an HTTP request if nothing is in the cache and then store it", - function(done) { - const httpFilterDefinition = { - event_format: "federation", - }; - expect(store.getFilter(userId, filterId)).toBe(null); + function(done) { + const httpFilterDefinition = { + event_format: "federation", + }; + expect(store!.getFilter(userId, filterId)).toBe(null); - httpBackend.when( - "GET", "/user/" + encodeURIComponent(userId) + "/filter/" + filterId, - ).respond(200, httpFilterDefinition); - client.getFilter(userId, filterId, true).then(function(gotFilter) { - expect(gotFilter.getDefinition()).toEqual(httpFilterDefinition); - expect(store.getFilter(userId, filterId)).toBeTruthy(); - done(); + httpBackend!.when( + "GET", "/user/" + encodeURIComponent(userId) + "/filter/" + filterId, + ).respond(200, httpFilterDefinition); + client!.getFilter(userId, filterId, true).then(function(gotFilter) { + expect(gotFilter.getDefinition()).toEqual(httpFilterDefinition); + expect(store!.getFilter(userId, filterId)).toBeTruthy(); + done(); + }); + + httpBackend!.flush(''); }); - - httpBackend.flush(); - }); }); describe("createFilter", function() { const filterId = "f1llllllerid"; it("should do an HTTP request and then store the filter", function(done) { - expect(store.getFilter(userId, filterId)).toBe(null); + expect(store!.getFilter(userId, filterId)).toBe(null); const filterDefinition = { - event_format: "client", + event_format: "client" as IFilterDefinition['event_format'], }; - httpBackend.when( + httpBackend!.when( "POST", "/user/" + encodeURIComponent(userId) + "/filter", ).check(function(req) { expect(req.data).toEqual(filterDefinition); @@ -258,13 +279,13 @@ describe("MatrixClient", function() { filter_id: filterId, }); - client.createFilter(filterDefinition).then(function(gotFilter) { + client!.createFilter(filterDefinition).then(function(gotFilter) { expect(gotFilter.getDefinition()).toEqual(filterDefinition); - expect(store.getFilter(userId, filterId)).toEqual(gotFilter); + expect(store!.getFilter(userId, filterId)).toEqual(gotFilter); done(); }); - httpBackend.flush(); + httpBackend!.flush(''); }); }); @@ -291,10 +312,10 @@ describe("MatrixClient", function() { }, }; - client.searchMessageText({ + client!.searchMessageText({ query: "monkeys", }); - httpBackend.when("POST", "/search").check(function(req) { + httpBackend!.when("POST", "/search").check(function(req) { expect(req.data).toEqual({ search_categories: { room_events: { @@ -304,7 +325,7 @@ describe("MatrixClient", function() { }); }).respond(200, response); - return httpBackend.flush(); + return httpBackend!.flush(''); }); describe("should filter out context from different timelines (threads)", () => { @@ -313,11 +334,14 @@ describe("MatrixClient", function() { search_categories: { room_events: { count: 24, + highlights: [], results: [{ rank: 0.1, result: { event_id: "$flibble:localhost", type: "m.room.message", + sender: '@test:locahost', + origin_server_ts: 123, user_id: "@alice:localhost", room_id: "!feuiwhf:localhost", content: { @@ -326,9 +350,12 @@ describe("MatrixClient", function() { }, }, context: { + profile_info: {}, events_after: [{ event_id: "$ev-after:server", type: "m.room.message", + sender: '@test:locahost', + origin_server_ts: 123, user_id: "@alice:localhost", room_id: "!feuiwhf:localhost", content: { @@ -343,6 +370,8 @@ describe("MatrixClient", function() { events_before: [{ event_id: "$ev-before:server", type: "m.room.message", + sender: '@test:locahost', + origin_server_ts: 123, user_id: "@alice:localhost", room_id: "!feuiwhf:localhost", content: { @@ -356,15 +385,17 @@ describe("MatrixClient", function() { }, }; - const data = { + const data: ISearchResults = { results: [], highlights: [], }; - client.processRoomEventsSearch(data, response); + client!.processRoomEventsSearch(data, response); expect(data.results).toHaveLength(1); - expect(data.results[0].context.timeline).toHaveLength(2); - expect(data.results[0].context.timeline.find(e => e.getId() === "$ev-after:server")).toBeFalsy(); + expect(data.results[0].context.getTimeline()).toHaveLength(2); + expect( + data.results[0].context.getTimeline().find(e => e.getId() === "$ev-after:server"), + ).toBeFalsy(); }); it("filters out thread replies from threads other than the thread the result replied to", () => { @@ -372,11 +403,14 @@ describe("MatrixClient", function() { search_categories: { room_events: { count: 24, + highlights: [], results: [{ rank: 0.1, result: { event_id: "$flibble:localhost", type: "m.room.message", + sender: '@test:locahost', + origin_server_ts: 123, user_id: "@alice:localhost", room_id: "!feuiwhf:localhost", content: { @@ -389,9 +423,12 @@ describe("MatrixClient", function() { }, }, context: { + profile_info: {}, events_after: [{ event_id: "$ev-after:server", type: "m.room.message", + sender: '@test:locahost', + origin_server_ts: 123, user_id: "@alice:localhost", room_id: "!feuiwhf:localhost", content: { @@ -410,15 +447,17 @@ describe("MatrixClient", function() { }, }; - const data = { + const data: ISearchResults = { results: [], highlights: [], }; - client.processRoomEventsSearch(data, response); + client!.processRoomEventsSearch(data, response); expect(data.results).toHaveLength(1); - expect(data.results[0].context.timeline).toHaveLength(1); - expect(data.results[0].context.timeline.find(e => e.getId() === "$flibble:localhost")).toBeTruthy(); + expect(data.results[0].context.getTimeline()).toHaveLength(1); + expect( + data.results[0].context.getTimeline().find(e => e.getId() === "$flibble:localhost"), + ).toBeTruthy(); }); it("filters out main timeline events when result is a thread reply", () => { @@ -426,10 +465,13 @@ describe("MatrixClient", function() { search_categories: { room_events: { count: 24, + highlights: [], results: [{ rank: 0.1, result: { event_id: "$flibble:localhost", + sender: '@test:locahost', + origin_server_ts: 123, type: "m.room.message", user_id: "@alice:localhost", room_id: "!feuiwhf:localhost", @@ -445,6 +487,8 @@ describe("MatrixClient", function() { context: { events_after: [{ event_id: "$ev-after:server", + sender: '@test:locahost', + origin_server_ts: 123, type: "m.room.message", user_id: "@alice:localhost", room_id: "!feuiwhf:localhost", @@ -454,21 +498,24 @@ describe("MatrixClient", function() { }, }], events_before: [], + profile_info: {}, }, }], }, }, }; - const data = { + const data: ISearchResults = { results: [], highlights: [], }; - client.processRoomEventsSearch(data, response); + client!.processRoomEventsSearch(data, response); expect(data.results).toHaveLength(1); - expect(data.results[0].context.timeline).toHaveLength(1); - expect(data.results[0].context.timeline.find(e => e.getId() === "$flibble:localhost")).toBeTruthy(); + expect(data.results[0].context.getTimeline()).toHaveLength(1); + expect( + data.results[0].context.getTimeline().find(e => e.getId() === "$flibble:localhost"), + ).toBeTruthy(); }); }); }); @@ -479,16 +526,16 @@ describe("MatrixClient", function() { } beforeEach(function() { - return client.initCrypto(); + return client!.initCrypto(); }); 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"], @@ -512,7 +559,7 @@ describe("MatrixClient", function() { keys: { "ed25519:dev2": ed25519key }, signatures: { chaz: { - "ed25519:dev2": + "ed25519:dev2": "FwslH/Q7EYSb7swDJbNB5PSzcbEO1xRRBF1riuijqvL" + "EkrK9/XVN8jl4h7thGuRITQ01siBQnNmMK9t45QfcCQ", }, @@ -528,7 +575,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); @@ -536,7 +583,7 @@ describe("MatrixClient", function() { logger.log("chaz:", sign(chazKeys.dev2)); */ - httpBackend.when("POST", "/keys/query").check(function(req) { + httpBackend!.when("POST", "/keys/query").check(function(req) { expect(req.data).toEqual({ device_keys: { 'boris': [], 'chaz': [], @@ -548,7 +595,7 @@ describe("MatrixClient", function() { }, }); - const prom = client.downloadKeys(["boris", "chaz"]).then(function(res) { + const prom = client!.downloadKeys(["boris", "chaz"]).then(function(res) { assertObjectContains(res.boris.dev1, { verified: 0, // DeviceVerification.UNVERIFIED keys: { "ed25519:dev1": ed25519key }, @@ -564,23 +611,23 @@ describe("MatrixClient", function() { }); }); - httpBackend.flush(); + httpBackend!.flush(''); return prom; }); }); describe("deleteDevice", function() { - const auth = { a: 1 }; + const auth = { identifier: 1 }; it("should pass through an auth dict", function() { - httpBackend.when( + 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; }); }); @@ -588,7 +635,7 @@ describe("MatrixClient", function() { describe("partitionThreadedEvents", function() { let 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() { @@ -599,7 +646,11 @@ describe("MatrixClient", function() { }); it("copies pre-thread in-timeline vote events onto both timelines", function() { - client.clientOpts = { experimentalThreadSupport: true }; + // @ts-ignore setting private property + client!.clientOpts = { + ...defaultClientOpts, + experimentalThreadSupport: true, + }; const eventPollResponseReference = buildEventPollResponseReference(); const eventPollStartThreadRoot = buildEventPollStartThreadRoot(); @@ -611,6 +662,7 @@ describe("MatrixClient", function() { eventPollResponseReference, ]; // Vote has no threadId yet + // @ts-ignore private property expect(eventPollResponseReference.threadId).toBeFalsy(); const [timeline, threaded] = room.partitionThreadedEvents(events); @@ -634,7 +686,11 @@ describe("MatrixClient", function() { }); it("copies pre-thread in-timeline reactions onto both timelines", function() { - client.clientOpts = { experimentalThreadSupport: true }; + // @ts-ignore setting private property + client!.clientOpts = { + ...defaultClientOpts, + experimentalThreadSupport: true, + }; const eventPollStartThreadRoot = buildEventPollStartThreadRoot(); const eventMessageInThread = buildEventMessageInThread(eventPollStartThreadRoot); @@ -661,7 +717,11 @@ describe("MatrixClient", function() { }); it("copies post-thread in-timeline vote events onto both timelines", function() { - client.clientOpts = { experimentalThreadSupport: true }; + // @ts-ignore setting private property + client!.clientOpts = { + ...defaultClientOpts, + experimentalThreadSupport: true, + }; const eventPollResponseReference = buildEventPollResponseReference(); const eventPollStartThreadRoot = buildEventPollStartThreadRoot(); @@ -688,7 +748,11 @@ describe("MatrixClient", function() { }); it("copies post-thread in-timeline reactions onto both timelines", function() { - client.clientOpts = { experimentalThreadSupport: true }; + // @ts-ignore setting private property + client!.clientOpts = { + ...defaultClientOpts, + experimentalThreadSupport: true, + }; const eventPollStartThreadRoot = buildEventPollStartThreadRoot(); const eventMessageInThread = buildEventMessageInThread(eventPollStartThreadRoot); @@ -715,7 +779,11 @@ describe("MatrixClient", function() { }); it("sends room state events to the main timeline only", function() { - client.clientOpts = { experimentalThreadSupport: true }; + // @ts-ignore setting private property + client!.clientOpts = { + ...defaultClientOpts, + experimentalThreadSupport: true, + }; // This is based on recording the events in a real room: const eventPollStartThreadRoot = buildEventPollStartThreadRoot(); @@ -768,7 +836,11 @@ describe("MatrixClient", function() { }); it("sends redactions of reactions to thread responses to thread timeline only", () => { - client.clientOpts = { experimentalThreadSupport: true }; + // @ts-ignore setting private property + client!.clientOpts = { + ...defaultClientOpts, + experimentalThreadSupport: true, + }; const threadRootEvent = buildEventPollStartThreadRoot(); const eventMessageInThread = buildEventMessageInThread(threadRootEvent); @@ -797,7 +869,11 @@ describe("MatrixClient", function() { }); it("sends reply to reply to thread root outside of thread to main timeline only", () => { - client.clientOpts = { experimentalThreadSupport: true }; + // @ts-ignore setting private property + client!.clientOpts = { + ...defaultClientOpts, + experimentalThreadSupport: true, + }; const threadRootEvent = buildEventPollStartThreadRoot(); const eventMessageInThread = buildEventMessageInThread(threadRootEvent); @@ -826,7 +902,11 @@ describe("MatrixClient", function() { }); it("sends reply to thread responses to main timeline only", () => { - client.clientOpts = { experimentalThreadSupport: true }; + // @ts-ignore setting private property + client!.clientOpts = { + ...defaultClientOpts, + experimentalThreadSupport: true, + }; const threadRootEvent = buildEventPollStartThreadRoot(); const eventMessageInThread = buildEventMessageInThread(threadRootEvent); @@ -860,9 +940,9 @@ describe("MatrixClient", function() { fields: {}, }]; - 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); }); }); @@ -875,9 +955,9 @@ describe("MatrixClient", function() { fields: {}, }]; - 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); }); }); @@ -888,10 +968,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); }); }); @@ -903,12 +983,12 @@ describe("MatrixClient", function() { left: [], }; - 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"); + 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); }); }); @@ -919,9 +999,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); }); }); @@ -935,9 +1015,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); }); }); @@ -948,9 +1028,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); }); }); @@ -958,9 +1038,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); }); }); @@ -968,10 +1048,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); }); }); @@ -986,10 +1066,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); }); }); @@ -1001,19 +1081,19 @@ 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.when("POST", "/register/email/requestToken").check(req => { + const prom = client!.requestRegisterEmailToken("bob@email", "secret", 1); + httpBackend!.when("POST", "/register/email/requestToken").check(req => { expect(req.data).toStrictEqual({ email: "bob@email", client_secret: "secret", send_attempt: 1, }); }).respond(200, response); - await httpBackend.flush(); + await httpBackend!.flush(''); expect(await prom).toStrictEqual(response); }); }); @@ -1022,11 +1102,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.when("POST", "/invite").check(req => { + httpBackend!.when("POST", "/invite").check(req => { expect(req.data).toStrictEqual({ id_server: idServerDomain, id_access_token: identityAccessToken, @@ -1035,8 +1115,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 }); }); @@ -1056,11 +1136,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.when("POST", "/createRoom").check(req => { + httpBackend!.when("POST", "/createRoom").check(req => { expect(req.data).toMatchObject({ invite_3pid: expect.arrayContaining([{ ...input.invite_3pid[0], @@ -1070,8 +1150,8 @@ describe("MatrixClient", function() { expect(req.data.invite_3pid.length).toBe(1); }).respond(200, response); - const prom = client.createRoom(input); - await httpBackend.flush(); + const prom = client!.createRoom(input); + await httpBackend!.flush(''); expect(await prom).toStrictEqual(response); }); }); @@ -1079,22 +1159,22 @@ describe("MatrixClient", function() { describe("requestLoginToken", () => { it("should hit the expected API endpoint with UIA", async () => { const response = {}; - const uiaData = { foo: "baa" }; - const prom = client.requestLoginToken(uiaData); - httpBackend + const uiaData = {}; + const prom = client!.requestLoginToken(uiaData); + httpBackend! .when("POST", "/unstable/org.matrix.msc3882/login/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 () => { const response = {}; - const prom = client.requestLoginToken(); - httpBackend + const prom = client!.requestLoginToken(); + httpBackend! .when("POST", "/unstable/org.matrix.msc3882/login/token", {}) .respond(200, response); - await httpBackend.flush(); + await httpBackend!.flush(''); expect(await prom).toStrictEqual(response); }); }); diff --git a/spec/integ/matrix-client-opts.spec.js b/spec/integ/matrix-client-opts.spec.ts similarity index 93% rename from spec/integ/matrix-client-opts.spec.js rename to spec/integ/matrix-client-opts.spec.ts index 8e342b259..714030074 100644 --- a/spec/integ/matrix-client-opts.spec.js +++ b/spec/integ/matrix-client-opts.spec.ts @@ -5,10 +5,12 @@ import { MatrixClient } from "../../src/matrix"; import { MatrixScheduler } from "../../src/scheduler"; import { MemoryStore } from "../../src/store/memory"; import { MatrixError } from "../../src/http-api"; +import { ICreateClientOpts } from "../../src/client"; +import { IStore } from "../../src/store"; describe("MatrixClient opts", function() { const baseUrl = "http://localhost.or.something"; - let httpBackend = null; + let httpBackend = new HttpBackend(); const userId = "@alice:localhost"; const userB = "@bob:localhost"; const accessToken = "aseukfgwef"; @@ -67,7 +69,7 @@ describe("MatrixClient opts", function() { let client; beforeEach(function() { client = new MatrixClient({ - request: httpBackend.requestFn, + request: httpBackend.requestFn as unknown as ICreateClientOpts['request'], store: undefined, baseUrl: baseUrl, userId: userId, @@ -99,7 +101,7 @@ describe("MatrixClient opts", function() { ]; client.on("event", function(event) { expect(expectedEventTypes.indexOf(event.getType())).not.toEqual( - -1, "Recv unexpected event type: " + event.getType(), + -1, ); expectedEventTypes.splice( expectedEventTypes.indexOf(event.getType()), 1, @@ -118,7 +120,7 @@ describe("MatrixClient opts", function() { utils.syncPromise(client), ]); expect(expectedEventTypes.length).toEqual( - 0, "Expected to see event types: " + expectedEventTypes, + 0, ); }); }); @@ -127,8 +129,8 @@ describe("MatrixClient opts", function() { let client; beforeEach(function() { client = new MatrixClient({ - request: httpBackend.requestFn, - store: new MemoryStore(), + request: httpBackend.requestFn as unknown as ICreateClientOpts['request'], + store: new MemoryStore() as IStore, baseUrl: baseUrl, userId: userId, accessToken: accessToken, @@ -146,7 +148,7 @@ describe("MatrixClient opts", function() { error: "Ruh roh", })); client.sendTextMessage("!foo:bar", "a body", "txn1").then(function(res) { - expect(false).toBe(true, "sendTextMessage resolved but shouldn't"); + expect(false).toBe(true); }, function(err) { expect(err.errcode).toEqual("M_SOMETHING"); done(); diff --git a/spec/integ/matrix-client-retrying.spec.ts b/spec/integ/matrix-client-retrying.spec.ts index 31354b89a..877e80ac9 100644 --- a/spec/integ/matrix-client-retrying.spec.ts +++ b/spec/integ/matrix-client-retrying.spec.ts @@ -14,22 +14,22 @@ See the License for the specific language governing permissions and limitations under the License. */ -import { EventStatus, RoomEvent, MatrixClient } from "../../src/matrix"; -import { MatrixScheduler } from "../../src/scheduler"; +import HttpBackend from "matrix-mock-request"; + +import { EventStatus, RoomEvent, MatrixClient, MatrixScheduler } from "../../src/matrix"; import { Room } from "../../src/models/room"; import { TestClient } from "../TestClient"; describe("MatrixClient retrying", function() { - let client: MatrixClient = null; - let httpBackend: TestClient["httpBackend"] = null; - let scheduler; const userId = "@alice:localhost"; const accessToken = "aseukfgwef"; const roomId = "!room:here"; - let room: Room; + let client: MatrixClient | undefined; + let httpBackend: HttpBackend | undefined; + let room: Room | undefined; - beforeEach(function() { - scheduler = new MatrixScheduler(); + const setupTests = (): [MatrixClient, HttpBackend, Room] => { + const scheduler = new MatrixScheduler(); const testClient = new TestClient( userId, "DEVICE", @@ -37,15 +37,21 @@ describe("MatrixClient retrying", function() { undefined, { scheduler }, ); - httpBackend = testClient.httpBackend; - client = testClient.client; - room = new Room(roomId, client, userId); - client.store.storeRoom(room); + const httpBackend = testClient.httpBackend; + const client = testClient.client; + const room = new Room(roomId, client, userId); + client!.store.storeRoom(room); + + return [client, httpBackend, room]; + }; + + beforeEach(function() { + [client, httpBackend, room] = setupTests(); }); afterEach(function() { - httpBackend.verifyNoOutstandingExpectation(); - return httpBackend.stop(); + httpBackend!.verifyNoOutstandingExpectation(); + return httpBackend!.stop(); }); xit("should retry according to MatrixScheduler.retryFn", function() { @@ -66,7 +72,7 @@ describe("MatrixClient retrying", function() { it("should mark events as EventStatus.CANCELLED when cancelled", function() { // send a couple of events; the second will be queued - const p1 = client.sendMessage(roomId, { + const p1 = client!.sendMessage(roomId, { "msgtype": "m.text", "body": "m1", }).then(function() { @@ -79,13 +85,13 @@ describe("MatrixClient retrying", function() { // XXX: it turns out that the promise returned by this message // never gets resolved. // https://github.com/matrix-org/matrix-js-sdk/issues/496 - client.sendMessage(roomId, { + client!.sendMessage(roomId, { "msgtype": "m.text", "body": "m2", }); // both events should be in the timeline at this point - const tl = room.getLiveTimeline().getEvents(); + const tl = room!.getLiveTimeline().getEvents(); expect(tl.length).toEqual(2); const ev1 = tl[0]; const ev2 = tl[1]; @@ -94,24 +100,24 @@ describe("MatrixClient retrying", function() { expect(ev2.status).toEqual(EventStatus.SENDING); // the first message should get sent, and the second should get queued - httpBackend.when("PUT", "/send/m.room.message/").check(function() { + httpBackend!.when("PUT", "/send/m.room.message/").check(function() { // ev2 should now have been queued expect(ev2.status).toEqual(EventStatus.QUEUED); // now we can cancel the second and check everything looks sane - client.cancelPendingEvent(ev2); + client!.cancelPendingEvent(ev2); expect(ev2.status).toEqual(EventStatus.CANCELLED); expect(tl.length).toEqual(1); // shouldn't be able to cancel the first message yet expect(function() { - client.cancelPendingEvent(ev1); + client!.cancelPendingEvent(ev1); }).toThrow(); }).respond(400); // fail the first message // wait for the localecho of ev1 to be updated const p3 = new Promise((resolve, reject) => { - room.on(RoomEvent.LocalEchoUpdated, (ev0) => { + room!.on(RoomEvent.LocalEchoUpdated, (ev0) => { if (ev0 === ev1) { resolve(); } @@ -121,7 +127,7 @@ describe("MatrixClient retrying", function() { expect(tl.length).toEqual(1); // cancel the first message - client.cancelPendingEvent(ev1); + client!.cancelPendingEvent(ev1); expect(ev1.status).toEqual(EventStatus.CANCELLED); expect(tl.length).toEqual(0); }); @@ -129,7 +135,7 @@ describe("MatrixClient retrying", function() { return Promise.all([ p1, p3, - httpBackend.flushAllExpected(), + httpBackend!.flushAllExpected(), ]); }); diff --git a/spec/integ/matrix-client-room-timeline.spec.js b/spec/integ/matrix-client-room-timeline.spec.ts similarity index 76% rename from spec/integ/matrix-client-room-timeline.spec.js rename to spec/integ/matrix-client-room-timeline.spec.ts index acf751a8c..48ecee32b 100644 --- a/spec/integ/matrix-client-room-timeline.spec.js +++ b/spec/integ/matrix-client-room-timeline.spec.ts @@ -1,16 +1,35 @@ +/* +Copyright 2022 The Matrix.org Foundation C.I.C. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +import HttpBackend from "matrix-mock-request"; + import * as utils from "../test-utils/test-utils"; import { EventStatus } from "../../src/models/event"; -import { RoomEvent } from "../../src"; +import { ClientEvent, IEvent, MatrixClient, RoomEvent } from "../../src"; import { TestClient } from "../TestClient"; describe("MatrixClient room timelines", function() { - let client = null; - let httpBackend = null; const userId = "@alice:localhost"; const userName = "Alice"; const accessToken = "aseukfgwef"; const roomId = "!foo:bar"; const otherUserId = "@bob:localhost"; + let client: MatrixClient | undefined; + let httpBackend: HttpBackend | undefined; + const USER_MEMBERSHIP_EVENT = utils.mkMembership({ room: roomId, mship: "join", user: userId, name: userName, }); @@ -55,8 +74,7 @@ describe("MatrixClient room timelines", function() { }, }; - function setNextSyncData(events) { - events = events || []; + function setNextSyncData(events: Partial[] = []) { NEXT_SYNC_DATA = { next_batch: "n", presence: { events: [] }, @@ -77,19 +95,9 @@ describe("MatrixClient room timelines", function() { throw new Error("setNextSyncData only works with one room id"); } if (e.state_key) { - if (e.__prev_event === undefined) { - throw new Error( - "setNextSyncData needs the prev state set to '__prev_event' " + - "for " + e.type, - ); - } - if (e.__prev_event !== null) { - // push the previous state for this event type - NEXT_SYNC_DATA.rooms.join[roomId].state.events.push(e.__prev_event); - } // push the current NEXT_SYNC_DATA.rooms.join[roomId].timeline.events.push(e); - } else if (["m.typing", "m.receipt"].indexOf(e.type) !== -1) { + } else if (["m.typing", "m.receipt"].indexOf(e.type!) !== -1) { NEXT_SYNC_DATA.rooms.join[roomId].ephemeral.events.push(e); } else { NEXT_SYNC_DATA.rooms.join[roomId].timeline.events.push(e); @@ -97,7 +105,7 @@ describe("MatrixClient room timelines", function() { }); } - beforeEach(async function() { + const setupTestClient = (): [MatrixClient, HttpBackend] => { // these tests should work with or without timelineSupport const testClient = new TestClient( userId, @@ -106,41 +114,46 @@ describe("MatrixClient room timelines", function() { undefined, { timelineSupport: true }, ); - httpBackend = testClient.httpBackend; - client = testClient.client; + const httpBackend = testClient.httpBackend; + const client = testClient.client; setNextSyncData(); - httpBackend.when("GET", "/versions").respond(200, {}); - httpBackend.when("GET", "/pushrules").respond(200, {}); - httpBackend.when("POST", "/filter").respond(200, { filter_id: "fid" }); - httpBackend.when("GET", "/sync").respond(200, SYNC_DATA); - httpBackend.when("GET", "/sync").respond(200, function() { + httpBackend!.when("GET", "/versions").respond(200, {}); + httpBackend!.when("GET", "/pushrules").respond(200, {}); + httpBackend!.when("POST", "/filter").respond(200, { filter_id: "fid" }); + httpBackend!.when("GET", "/sync").respond(200, SYNC_DATA); + httpBackend!.when("GET", "/sync").respond(200, function() { return NEXT_SYNC_DATA; }); - client.startClient(); + client!.startClient(); + return [client!, httpBackend]; + }; + + beforeEach(async function() { + [client!, httpBackend] = setupTestClient(); await httpBackend.flush("/versions"); await httpBackend.flush("/pushrules"); await httpBackend.flush("/filter"); }); afterEach(function() { - httpBackend.verifyNoOutstandingExpectation(); - client.stopClient(); - return httpBackend.stop(); + httpBackend!.verifyNoOutstandingExpectation(); + client!.stopClient(); + return httpBackend!.stop(); }); describe("local echo events", function() { it("should be added immediately after calling MatrixClient.sendEvent " + "with EventStatus.SENDING and the right event.sender", function(done) { - client.on("sync", function(state) { + client!.on(ClientEvent.Sync, function(state) { if (state !== "PREPARED") { return; } - const room = client.getRoom(roomId); + const room = client!.getRoom(roomId)!; expect(room.timeline.length).toEqual(1); - client.sendTextMessage(roomId, "I am a fish", "txn1"); + client!.sendTextMessage(roomId, "I am a fish", "txn1"); // check it was added expect(room.timeline.length).toEqual(2); // check status @@ -150,68 +163,68 @@ describe("MatrixClient room timelines", function() { expect(member.userId).toEqual(userId); expect(member.name).toEqual(userName); - httpBackend.flush("/sync", 1).then(function() { + httpBackend!.flush("/sync", 1).then(function() { done(); }); }); - httpBackend.flush("/sync", 1); + httpBackend!.flush("/sync", 1); }); it("should be updated correctly when the send request finishes " + "BEFORE the event comes down the event stream", function(done) { const eventId = "$foo:bar"; - httpBackend.when("PUT", "/txn1").respond(200, { + httpBackend!.when("PUT", "/txn1").respond(200, { event_id: eventId, }); const ev = utils.mkMessage({ - body: "I am a fish", user: userId, room: roomId, + msg: "I am a fish", user: userId, room: roomId, }); ev.event_id = eventId; ev.unsigned = { transaction_id: "txn1" }; setNextSyncData([ev]); - client.on("sync", function(state) { + client!.on(ClientEvent.Sync, function(state) { if (state !== "PREPARED") { return; } - const room = client.getRoom(roomId); - client.sendTextMessage(roomId, "I am a fish", "txn1").then( - function() { - expect(room.timeline[1].getId()).toEqual(eventId); - httpBackend.flush("/sync", 1).then(function() { + const room = client!.getRoom(roomId)!; + client!.sendTextMessage(roomId, "I am a fish", "txn1").then( + function() { expect(room.timeline[1].getId()).toEqual(eventId); - done(); + httpBackend!.flush("/sync", 1).then(function() { + expect(room.timeline[1].getId()).toEqual(eventId); + done(); + }); }); - }); - httpBackend.flush("/txn1", 1); + httpBackend!.flush("/txn1", 1); }); - httpBackend.flush("/sync", 1); + httpBackend!.flush("/sync", 1); }); it("should be updated correctly when the send request finishes " + "AFTER the event comes down the event stream", function(done) { const eventId = "$foo:bar"; - httpBackend.when("PUT", "/txn1").respond(200, { + httpBackend!.when("PUT", "/txn1").respond(200, { event_id: eventId, }); const ev = utils.mkMessage({ - body: "I am a fish", user: userId, room: roomId, + msg: "I am a fish", user: userId, room: roomId, }); ev.event_id = eventId; ev.unsigned = { transaction_id: "txn1" }; setNextSyncData([ev]); - client.on("sync", function(state) { + client!.on(ClientEvent.Sync, function(state) { if (state !== "PREPARED") { return; } - const room = client.getRoom(roomId); - const promise = client.sendTextMessage(roomId, "I am a fish", "txn1"); - httpBackend.flush("/sync", 1).then(function() { + const room = client!.getRoom(roomId)!; + const promise = client!.sendTextMessage(roomId, "I am a fish", "txn1"); + httpBackend!.flush("/sync", 1).then(function() { expect(room.timeline.length).toEqual(2); - httpBackend.flush("/txn1", 1); + httpBackend!.flush("/txn1", 1); promise.then(function() { expect(room.timeline.length).toEqual(2); expect(room.timeline[1].getId()).toEqual(eventId); @@ -219,7 +232,7 @@ describe("MatrixClient room timelines", function() { }); }); }); - httpBackend.flush("/sync", 1); + httpBackend!.flush("/sync", 1); }); }); @@ -229,7 +242,7 @@ describe("MatrixClient room timelines", function() { beforeEach(function() { sbEvents = []; - httpBackend.when("GET", "/messages").respond(200, function() { + httpBackend!.when("GET", "/messages").respond(200, function() { return { chunk: sbEvents, start: "pagin_start", @@ -240,26 +253,26 @@ describe("MatrixClient room timelines", function() { it("should set Room.oldState.paginationToken to null at the start" + " of the timeline.", function(done) { - client.on("sync", function(state) { + client!.on(ClientEvent.Sync, function(state) { if (state !== "PREPARED") { return; } - const room = client.getRoom(roomId); + const room = client!.getRoom(roomId)!; expect(room.timeline.length).toEqual(1); - client.scrollback(room).then(function() { + client!.scrollback(room).then(function() { expect(room.timeline.length).toEqual(1); expect(room.oldState.paginationToken).toBe(null); // still have a sync to flush - httpBackend.flush("/sync", 1).then(() => { + httpBackend!.flush("/sync", 1).then(() => { done(); }); }); - httpBackend.flush("/messages", 1); + httpBackend!.flush("/messages", 1); }); - httpBackend.flush("/sync", 1); + httpBackend!.flush("/sync", 1); }); it("should set the right event.sender values", function(done) { @@ -275,7 +288,7 @@ describe("MatrixClient room timelines", function() { // make an m.room.member event for alice's join const joinMshipEvent = utils.mkMembership({ mship: "join", user: userId, room: roomId, name: "Old Alice", - url: null, + url: undefined, }); // make an m.room.member event with prev_content for alice's nick @@ -286,7 +299,7 @@ describe("MatrixClient room timelines", function() { }); oldMshipEvent.prev_content = { displayname: "Old Alice", - avatar_url: null, + avatar_url: undefined, membership: "join", }; @@ -303,15 +316,15 @@ describe("MatrixClient room timelines", function() { joinMshipEvent, ]; - client.on("sync", function(state) { + client!.on(ClientEvent.Sync, function(state) { if (state !== "PREPARED") { return; } - const room = client.getRoom(roomId); + const room = client!.getRoom(roomId)!; // sync response expect(room.timeline.length).toEqual(1); - client.scrollback(room).then(function() { + client!.scrollback(room).then(function() { expect(room.timeline.length).toEqual(5); const joinMsg = room.timeline[0]; expect(joinMsg.sender.name).toEqual("Old Alice"); @@ -321,14 +334,14 @@ describe("MatrixClient room timelines", function() { expect(newMsg.sender.name).toEqual(userName); // still have a sync to flush - httpBackend.flush("/sync", 1).then(() => { + httpBackend!.flush("/sync", 1).then(() => { done(); }); }); - httpBackend.flush("/messages", 1); + httpBackend!.flush("/messages", 1); }); - httpBackend.flush("/sync", 1); + httpBackend!.flush("/sync", 1); }); it("should add it them to the right place in the timeline", function(done) { @@ -342,27 +355,27 @@ describe("MatrixClient room timelines", function() { }), ]; - client.on("sync", function(state) { + client!.on(ClientEvent.Sync, function(state) { if (state !== "PREPARED") { return; } - const room = client.getRoom(roomId); + const room = client!.getRoom(roomId)!; expect(room.timeline.length).toEqual(1); - client.scrollback(room).then(function() { + client!.scrollback(room).then(function() { expect(room.timeline.length).toEqual(3); expect(room.timeline[0].event).toEqual(sbEvents[1]); expect(room.timeline[1].event).toEqual(sbEvents[0]); // still have a sync to flush - httpBackend.flush("/sync", 1).then(() => { + httpBackend!.flush("/sync", 1).then(() => { done(); }); }); - httpBackend.flush("/messages", 1); + httpBackend!.flush("/messages", 1); }); - httpBackend.flush("/sync", 1); + httpBackend!.flush("/sync", 1); }); it("should use 'end' as the next pagination token", function(done) { @@ -373,25 +386,25 @@ describe("MatrixClient room timelines", function() { }), ]; - client.on("sync", function(state) { + client!.on(ClientEvent.Sync, function(state) { if (state !== "PREPARED") { return; } - const room = client.getRoom(roomId); + const room = client!.getRoom(roomId)!; expect(room.oldState.paginationToken).toBeTruthy(); - client.scrollback(room, 1).then(function() { + client!.scrollback(room, 1).then(function() { expect(room.oldState.paginationToken).toEqual(sbEndTok); }); - httpBackend.flush("/messages", 1).then(function() { + httpBackend!.flush("/messages", 1).then(function() { // still have a sync to flush - httpBackend.flush("/sync", 1).then(() => { + httpBackend!.flush("/sync", 1).then(() => { done(); }); }); }); - httpBackend.flush("/sync", 1); + httpBackend!.flush("/sync", 1); }); }); @@ -404,23 +417,23 @@ describe("MatrixClient room timelines", function() { setNextSyncData(eventData); return Promise.all([ - httpBackend.flush("/sync", 1), - utils.syncPromise(client), + httpBackend!.flush("/sync", 1), + utils.syncPromise(client!), ]).then(() => { - const room = client.getRoom(roomId); + const room = client!.getRoom(roomId)!; let index = 0; - client.on("Room.timeline", function(event, rm, toStart) { + client!.on(RoomEvent.Timeline, function(event, rm, toStart) { expect(toStart).toBe(false); expect(rm).toEqual(room); expect(event.event).toEqual(eventData[index]); index += 1; }); - httpBackend.flush("/messages", 1); + httpBackend!.flush("/messages", 1); return Promise.all([ - httpBackend.flush("/sync", 1), - utils.syncPromise(client), + httpBackend!.flush("/sync", 1), + utils.syncPromise(client!), ]).then(function() { expect(index).toEqual(2); expect(room.timeline.length).toEqual(3); @@ -442,17 +455,16 @@ describe("MatrixClient room timelines", function() { }), utils.mkMessage({ user: userId, room: roomId }), ]; - eventData[1].__prev_event = USER_MEMBERSHIP_EVENT; setNextSyncData(eventData); return Promise.all([ - httpBackend.flush("/sync", 1), - utils.syncPromise(client), + httpBackend!.flush("/sync", 1), + utils.syncPromise(client!), ]).then(() => { - const room = client.getRoom(roomId); + const room = client!.getRoom(roomId)!; return Promise.all([ - httpBackend.flush("/sync", 1), - utils.syncPromise(client), + httpBackend!.flush("/sync", 1), + utils.syncPromise(client!), ]).then(function() { const preNameEvent = room.timeline[room.timeline.length - 3]; const postNameEvent = room.timeline[room.timeline.length - 1]; @@ -468,22 +480,21 @@ describe("MatrixClient room timelines", function() { name: "Room 2", }, }); - secondRoomNameEvent.__prev_event = ROOM_NAME_EVENT; setNextSyncData([secondRoomNameEvent]); return Promise.all([ - httpBackend.flush("/sync", 1), - utils.syncPromise(client), + httpBackend!.flush("/sync", 1), + utils.syncPromise(client!), ]).then(() => { - const room = client.getRoom(roomId); + const room = client!.getRoom(roomId)!; let nameEmitCount = 0; - client.on("Room.name", function(rm) { + client!.on(RoomEvent.Name, function(rm) { nameEmitCount += 1; }); return Promise.all([ - httpBackend.flush("/sync", 1), - utils.syncPromise(client), + httpBackend!.flush("/sync", 1), + utils.syncPromise(client!), ]).then(function() { expect(nameEmitCount).toEqual(1); expect(room.name).toEqual("Room 2"); @@ -493,12 +504,11 @@ describe("MatrixClient room timelines", function() { name: "Room 3", }, }); - thirdRoomNameEvent.__prev_event = secondRoomNameEvent; setNextSyncData([thirdRoomNameEvent]); - httpBackend.when("GET", "/sync").respond(200, NEXT_SYNC_DATA); + httpBackend!.when("GET", "/sync").respond(200, NEXT_SYNC_DATA); return Promise.all([ - httpBackend.flush("/sync", 1), - utils.syncPromise(client), + httpBackend!.flush("/sync", 1), + utils.syncPromise(client!), ]); }).then(function() { expect(nameEmitCount).toEqual(2); @@ -518,26 +528,24 @@ describe("MatrixClient room timelines", function() { user: userC, room: roomId, mship: "invite", skey: userD, }), ]; - eventData[0].__prev_event = null; - eventData[1].__prev_event = null; setNextSyncData(eventData); return Promise.all([ - httpBackend.flush("/sync", 1), - utils.syncPromise(client), + httpBackend!.flush("/sync", 1), + utils.syncPromise(client!), ]).then(() => { - const room = client.getRoom(roomId); + const room = client!.getRoom(roomId)!; return Promise.all([ - httpBackend.flush("/sync", 1), - utils.syncPromise(client), + httpBackend!.flush("/sync", 1), + utils.syncPromise(client!), ]).then(function() { expect(room.currentState.getMembers().length).toEqual(4); - expect(room.currentState.getMember(userC).name).toEqual("C"); - expect(room.currentState.getMember(userC).membership).toEqual( + expect(room.currentState.getMember(userC)!.name).toEqual("C"); + expect(room.currentState.getMember(userC)!.membership).toEqual( "join", ); - expect(room.currentState.getMember(userD).name).toEqual(userD); - expect(room.currentState.getMember(userD).membership).toEqual( + expect(room.currentState.getMember(userD)!.name).toEqual(userD); + expect(room.currentState.getMember(userD)!.membership).toEqual( "invite", ); }); @@ -554,26 +562,26 @@ describe("MatrixClient room timelines", function() { NEXT_SYNC_DATA.rooms.join[roomId].timeline.limited = true; return Promise.all([ - httpBackend.flush("/versions", 1), - httpBackend.flush("/sync", 1), - utils.syncPromise(client), + httpBackend!.flush("/versions", 1), + httpBackend!.flush("/sync", 1), + utils.syncPromise(client!), ]).then(() => { - const room = client.getRoom(roomId); + const room = client!.getRoom(roomId)!; - httpBackend.flush("/messages", 1); + httpBackend!.flush("/messages", 1); return Promise.all([ - httpBackend.flush("/sync", 1), - utils.syncPromise(client), + httpBackend!.flush("/sync", 1), + utils.syncPromise(client!), ]).then(function() { expect(room.timeline.length).toEqual(1); expect(room.timeline[0].event).toEqual(eventData[0]); expect(room.currentState.getMembers().length).toEqual(2); - expect(room.currentState.getMember(userId).name).toEqual(userName); - expect(room.currentState.getMember(userId).membership).toEqual( + expect(room.currentState.getMember(userId)!.name).toEqual(userName); + expect(room.currentState.getMember(userId)!.membership).toEqual( "join", ); - expect(room.currentState.getMember(otherUserId).name).toEqual("Bob"); - expect(room.currentState.getMember(otherUserId).membership).toEqual( + expect(room.currentState.getMember(otherUserId)!.name).toEqual("Bob"); + expect(room.currentState.getMember(otherUserId)!.membership).toEqual( "join", ); }); @@ -588,21 +596,21 @@ describe("MatrixClient room timelines", function() { NEXT_SYNC_DATA.rooms.join[roomId].timeline.limited = true; return Promise.all([ - httpBackend.flush("/sync", 1), - utils.syncPromise(client), + httpBackend!.flush("/sync", 1), + utils.syncPromise(client!), ]).then(() => { - const room = client.getRoom(roomId); + const room = client!.getRoom(roomId)!; let emitCount = 0; - client.on("Room.timelineReset", function(emitRoom) { + client!.on(RoomEvent.TimelineReset, function(emitRoom) { expect(emitRoom).toEqual(room); emitCount++; }); - httpBackend.flush("/messages", 1); + httpBackend!.flush("/messages", 1); return Promise.all([ - httpBackend.flush("/sync", 1), - utils.syncPromise(client), + httpBackend!.flush("/sync", 1), + utils.syncPromise(client!), ]).then(function() { expect(emitCount).toEqual(1); }); @@ -618,7 +626,7 @@ describe("MatrixClient room timelines", function() { ]; const contextUrl = `/rooms/${encodeURIComponent(roomId)}/context/` + - `${encodeURIComponent(initialSyncEventData[2].event_id)}`; + `${encodeURIComponent(initialSyncEventData[2].event_id!)}`; const contextResponse = { start: "start_token", events_before: [initialSyncEventData[1], initialSyncEventData[0]], @@ -636,19 +644,19 @@ describe("MatrixClient room timelines", function() { // Create a room from the sync await Promise.all([ - httpBackend.flushAllExpected(), - utils.syncPromise(client, 1), + httpBackend!.flushAllExpected(), + utils.syncPromise(client!, 1), ]); // Get the room after the first sync so the room is created - room = client.getRoom(roomId); + room = client!.getRoom(roomId)!; expect(room).toBeTruthy(); }); it('should clear and refresh messages in timeline', async () => { // `/context` request for `refreshLiveTimeline()` -> `getEventTimeline()` // to construct a new timeline from. - httpBackend.when("GET", contextUrl) + httpBackend!.when("GET", contextUrl) .respond(200, function() { // The timeline should be cleared at this point in the refresh expect(room.timeline.length).toEqual(0); @@ -659,7 +667,7 @@ describe("MatrixClient room timelines", function() { // Refresh the timeline. await Promise.all([ room.refreshLiveTimeline(), - httpBackend.flushAllExpected(), + httpBackend!.flushAllExpected(), ]); // Make sure the message are visible @@ -681,7 +689,7 @@ describe("MatrixClient room timelines", function() { // middle of all of this refresh timeline logic. We want to make // sure the sync pagination still works as expected after messing // the refresh timline logic messes with the pagination tokens. - httpBackend.when("GET", contextUrl) + httpBackend!.when("GET", contextUrl) .respond(200, () => { // Now finally return and make the `/context` request respond return contextResponse; @@ -700,7 +708,7 @@ describe("MatrixClient room timelines", function() { const racingSyncEventData = [ utils.mkMessage({ user: userId, room: roomId }), ]; - const waitForRaceySyncAfterResetPromise = new Promise((resolve, reject) => { + const waitForRaceySyncAfterResetPromise = new Promise((resolve, reject) => { let eventFired = false; // Throw a more descriptive error if this part of the test times out. const failTimeout = setTimeout(() => { @@ -726,12 +734,12 @@ describe("MatrixClient room timelines", function() { // Then make a `/sync` happen by sending a message and seeing that it // shows up (simulate a /sync naturally racing with us). setNextSyncData(racingSyncEventData); - httpBackend.when("GET", "/sync").respond(200, function() { + httpBackend!.when("GET", "/sync").respond(200, function() { return NEXT_SYNC_DATA; }); await Promise.all([ - httpBackend.flush("/sync", 1), - utils.syncPromise(client, 1), + httpBackend!.flush("/sync", 1), + utils.syncPromise(client!, 1), ]); // Make sure the timeline has the racey sync data const afterRaceySyncTimelineEvents = room @@ -761,7 +769,7 @@ describe("MatrixClient room timelines", function() { await Promise.all([ refreshLiveTimelinePromise, // Then flush the remaining `/context` to left the refresh logic complete - httpBackend.flushAllExpected(), + httpBackend!.flushAllExpected(), ]); // Make sure sync pagination still works by seeing a new message show up @@ -770,12 +778,12 @@ describe("MatrixClient room timelines", function() { utils.mkMessage({ user: userId, room: roomId }), ]; setNextSyncData(afterRefreshEventData); - httpBackend.when("GET", "/sync").respond(200, function() { + httpBackend!.when("GET", "/sync").respond(200, function() { return NEXT_SYNC_DATA; }); await Promise.all([ - httpBackend.flushAllExpected(), - utils.syncPromise(client, 1), + httpBackend!.flushAllExpected(), + utils.syncPromise(client!, 1), ]); // Make sure the timeline includes the the events from the `/sync` @@ -794,7 +802,7 @@ describe("MatrixClient room timelines", function() { it('Timeline recovers after `/context` request to generate new timeline fails', async () => { // `/context` request for `refreshLiveTimeline()` -> `getEventTimeline()` // to construct a new timeline from. - httpBackend.when("GET", contextUrl) + httpBackend!.when("GET", contextUrl) .respond(500, function() { // The timeline should be cleared at this point in the refresh expect(room.timeline.length).toEqual(0); @@ -809,7 +817,7 @@ describe("MatrixClient room timelines", function() { // Refresh the timeline and expect it to fail const settledFailedRefreshPromises = await Promise.allSettled([ room.refreshLiveTimeline(), - httpBackend.flushAllExpected(), + httpBackend!.flushAllExpected(), ]); // We only expect `TEST_FAKE_ERROR` here. Anything else is // unexpected and should fail the test. @@ -825,7 +833,7 @@ describe("MatrixClient room timelines", function() { // `/messages` request for `refreshLiveTimeline()` -> // `getLatestTimeline()` to construct a new timeline from. - httpBackend.when("GET", `/rooms/${encodeURIComponent(roomId)}/messages`) + httpBackend!.when("GET", `/rooms/${encodeURIComponent(roomId)}/messages`) .respond(200, function() { return { chunk: [{ @@ -837,7 +845,7 @@ describe("MatrixClient room timelines", function() { // `/context` request for `refreshLiveTimeline()` -> // `getLatestTimeline()` -> `getEventTimeline()` to construct a new // timeline from. - httpBackend.when("GET", contextUrl) + httpBackend!.when("GET", contextUrl) .respond(200, function() { // The timeline should be cleared at this point in the refresh expect(room.timeline.length).toEqual(0); @@ -848,7 +856,7 @@ describe("MatrixClient room timelines", function() { // Refresh the timeline again but this time it should pass await Promise.all([ room.refreshLiveTimeline(), - httpBackend.flushAllExpected(), + httpBackend!.flushAllExpected(), ]); // Make sure sync pagination still works by seeing a new message show up @@ -857,12 +865,12 @@ describe("MatrixClient room timelines", function() { utils.mkMessage({ user: userId, room: roomId }), ]; setNextSyncData(afterRefreshEventData); - httpBackend.when("GET", "/sync").respond(200, function() { + httpBackend!.when("GET", "/sync").respond(200, function() { return NEXT_SYNC_DATA; }); await Promise.all([ - httpBackend.flushAllExpected(), - utils.syncPromise(client, 1), + httpBackend!.flushAllExpected(), + utils.syncPromise(client!, 1), ]); // Make sure the message are visible diff --git a/spec/integ/matrix-client-syncing.spec.ts b/spec/integ/matrix-client-syncing.spec.ts index f1c43c4f9..78795051c 100644 --- a/spec/integ/matrix-client-syncing.spec.ts +++ b/spec/integ/matrix-client-syncing.spec.ts @@ -16,7 +16,6 @@ limitations under the License. import 'fake-indexeddb/auto'; -import { Optional } from "matrix-events-sdk/lib/types"; import HttpBackend from "matrix-mock-request"; import { @@ -29,6 +28,11 @@ import { MatrixClient, ClientEvent, IndexedDBCryptoStore, + ISyncResponse, + IRoomEvent, + IJoinedRoom, + IStateEvent, + IMinimalEvent, NotificationCountType, } from "../../src"; import { UNREAD_THREAD_NOTIFICATIONS } from '../../src/@types/sync'; @@ -36,8 +40,6 @@ import * as utils from "../test-utils/test-utils"; import { TestClient } from "../TestClient"; describe("MatrixClient syncing", () => { - let client: Optional = null; - let httpBackend: Optional = null; const selfUserId = "@alice:localhost"; const selfAccessToken = "aseukfgwef"; const otherUserId = "@bob:localhost"; @@ -46,14 +48,21 @@ describe("MatrixClient syncing", () => { const userC = "@claire:bar"; const roomOne = "!foo:localhost"; const roomTwo = "!bar:localhost"; + let client: MatrixClient | undefined; + let httpBackend: HttpBackend | undefined; - beforeEach(() => { + const setupTestClient = (): [MatrixClient, HttpBackend] => { const testClient = new TestClient(selfUserId, "DEVICE", selfAccessToken); - httpBackend = testClient.httpBackend; - client = testClient.client; + const httpBackend = testClient.httpBackend; + const client = testClient.client; httpBackend!.when("GET", "/versions").respond(200, {}); httpBackend!.when("GET", "/pushrules").respond(200, {}); httpBackend!.when("POST", "/filter").respond(200, { filter_id: "a filter id" }); + return [client, httpBackend]; + }; + + beforeEach(() => { + [client, httpBackend] = setupTestClient(); }); afterEach(() => { @@ -82,7 +91,7 @@ describe("MatrixClient syncing", () => { it("should pass the 'next_batch' token from /sync to the since= param of the next /sync", (done) => { httpBackend!.when("GET", "/sync").respond(200, syncData); httpBackend!.when("GET", "/sync").check((req) => { - expect(req.queryParams.since).toEqual(syncData.next_batch); + expect(req.queryParams!.since).toEqual(syncData.next_batch); }).respond(200, syncData); client!.startClient(); @@ -93,7 +102,7 @@ describe("MatrixClient syncing", () => { }); it("should emit RoomEvent.MyMembership for invite->leave->invite cycles", async () => { - await client.initCrypto(); + await client!.initCrypto(); const roomId = "!cycles:example.org"; @@ -204,7 +213,7 @@ describe("MatrixClient syncing", () => { client!.doesServerSupportLazyLoading = jest.fn().mockResolvedValue(true); httpBackend!.when("GET", "/sync").check((req) => { - expect(JSON.parse(req.queryParams.filter).room.state.lazy_load_members).toBeTruthy(); + expect(JSON.parse(req.queryParams!.filter).room.state.lazy_load_members).toBeTruthy(); }).respond(200, syncData); client!.setGuest(false); @@ -219,7 +228,7 @@ describe("MatrixClient syncing", () => { client!.doesServerSupportLazyLoading = jest.fn().mockResolvedValue(true); httpBackend!.when("GET", "/sync").check((req) => { - expect(JSON.parse(req.queryParams.filter).room?.state?.lazy_load_members).toBeFalsy(); + expect(JSON.parse(req.queryParams!.filter).room?.state?.lazy_load_members).toBeFalsy(); }).respond(200, syncData); client!.setGuest(true); @@ -277,11 +286,11 @@ describe("MatrixClient syncing", () => { it("should only apply initialSyncLimit to the initial sync", () => { // 1st request httpBackend!.when("GET", "/sync").check((req) => { - expect(JSON.parse(req.queryParams.filter).room.timeline.limit).toEqual(1); + expect(JSON.parse(req.queryParams!.filter).room.timeline.limit).toEqual(1); }).respond(200, syncData); // 2nd request httpBackend!.when("GET", "/sync").check((req) => { - expect(req.queryParams.filter).toEqual("a filter id"); + expect(req.queryParams!.filter).toEqual("a filter id"); }).respond(200, syncData); client!.startClient({ initialSyncLimit: 1 }); @@ -292,7 +301,7 @@ describe("MatrixClient syncing", () => { it("should not apply initialSyncLimit to a first sync if we have a stored token", () => { httpBackend!.when("GET", "/sync").check((req) => { - expect(req.queryParams.filter).toEqual("a filter id"); + expect(req.queryParams!.filter).toEqual("a filter id"); }).respond(200, syncData); client!.store.getSavedSyncToken = jest.fn().mockResolvedValue("this-is-a-token"); @@ -303,26 +312,29 @@ describe("MatrixClient syncing", () => { }); describe("resolving invites to profile info", () => { - const syncData = { + const syncData: ISyncResponse = { + account_data: { + events: [], + }, next_batch: "s_5_3", presence: { events: [], }, rooms: { - join: { - - }, + join: {}, + invite: {}, + leave: {}, }, }; beforeEach(() => { - syncData.presence.events = []; + syncData.presence!.events = []; syncData.rooms.join[roomOne] = { timeline: { events: [ utils.mkMessage({ room: roomOne, user: otherUserId, msg: "hello", - }), + }) as IRoomEvent, ], }, state: { @@ -341,14 +353,14 @@ describe("MatrixClient syncing", () => { }), ], }, - }; + } as unknown as IJoinedRoom; }); it("should resolve incoming invites from /sync", () => { syncData.rooms.join[roomOne].state.events.push( utils.mkMembership({ room: roomOne, mship: "invite", user: userC, - }), + }) as IStateEvent, ); httpBackend!.when("GET", "/sync").respond(200, syncData); @@ -367,26 +379,26 @@ describe("MatrixClient syncing", () => { httpBackend!.flushAllExpected(), awaitSyncEvent(), ]).then(() => { - const member = client!.getRoom(roomOne).getMember(userC); + const member = client!.getRoom(roomOne)!.getMember(userC)!; expect(member.name).toEqual("The Boss"); expect( - member.getAvatarUrl("home.server.url", null, null, null, false, false), + member.getAvatarUrl("home.server.url", 1, 1, '', false, false), ).toBeTruthy(); }); }); it("should use cached values from m.presence wherever possible", () => { - syncData.presence.events = [ + syncData.presence!.events = [ utils.mkPresence({ user: userC, presence: "online", name: "The Ghost", - }), + }) as IMinimalEvent, ]; syncData.rooms.join[roomOne].state.events.push( utils.mkMembership({ room: roomOne, mship: "invite", user: userC, - }), + }) as IStateEvent, ); httpBackend!.when("GET", "/sync").respond(200, syncData); @@ -399,28 +411,28 @@ describe("MatrixClient syncing", () => { httpBackend!.flushAllExpected(), awaitSyncEvent(), ]).then(() => { - const member = client!.getRoom(roomOne).getMember(userC); + const member = client!.getRoom(roomOne)!.getMember(userC)!; expect(member.name).toEqual("The Ghost"); }); }); it("should result in events on the room member firing", () => { - syncData.presence.events = [ + syncData.presence!.events = [ utils.mkPresence({ user: userC, presence: "online", name: "The Ghost", - }), + }) as IMinimalEvent, ]; syncData.rooms.join[roomOne].state.events.push( utils.mkMembership({ room: roomOne, mship: "invite", user: userC, - }), + }) as IStateEvent, ); httpBackend!.when("GET", "/sync").respond(200, syncData); - let latestFiredName = null; + let latestFiredName: string; client!.on(RoomMemberEvent.Name, (event, m) => { if (m.userId === userC && m.roomId === roomOne) { latestFiredName = m.name; @@ -443,7 +455,7 @@ describe("MatrixClient syncing", () => { syncData.rooms.join[roomOne].state.events.push( utils.mkMembership({ room: roomOne, mship: "invite", user: userC, - }), + }) as IStateEvent, ); httpBackend!.when("GET", "/sync").respond(200, syncData); @@ -454,10 +466,10 @@ describe("MatrixClient syncing", () => { httpBackend!.flushAllExpected(), awaitSyncEvent(), ]).then(() => { - const member = client!.getRoom(roomOne).getMember(userC); + const member = client!.getRoom(roomOne)!.getMember(userC)!; expect(member.name).toEqual(userC); expect( - member.getAvatarUrl("home.server.url", null, null, null, false, false), + member.getAvatarUrl("home.server.url", 1, 1, '', false, false), ).toBe(null); }); }); @@ -489,8 +501,8 @@ describe("MatrixClient syncing", () => { httpBackend!.flushAllExpected(), awaitSyncEvent(), ]).then(() => { - expect(client!.getUser(userA).presence).toEqual("online"); - expect(client!.getUser(userB).presence).toEqual("unavailable"); + expect(client!.getUser(userA)!.presence).toEqual("online"); + expect(client!.getUser(userB)!.presence).toEqual("unavailable"); }); }); }); @@ -611,7 +623,7 @@ describe("MatrixClient syncing", () => { httpBackend!.flushAllExpected(), awaitSyncEvent(2), ]).then(() => { - const room = client!.getRoom(roomOne); + const room = client!.getRoom(roomOne)!; // should have clobbered the name to the one from /events expect(room.name).toEqual( nextSyncData.rooms.join[roomOne].state.events[0].content.name, @@ -629,7 +641,7 @@ describe("MatrixClient syncing", () => { httpBackend!.flushAllExpected(), awaitSyncEvent(2), ]).then(() => { - const room = client!.getRoom(roomTwo); + const room = client!.getRoom(roomTwo)!; // should have added the message from /events expect(room.timeline.length).toEqual(2); expect(room.timeline[1].getContent().body).toEqual(msgText); @@ -645,7 +657,7 @@ describe("MatrixClient syncing", () => { httpBackend!.flushAllExpected(), awaitSyncEvent(2), ]).then(() => { - const room = client!.getRoom(roomTwo); + const room = client!.getRoom(roomTwo)!; // should use the display name of the other person. expect(room.name).toEqual(otherDisplayName); }); @@ -661,11 +673,11 @@ describe("MatrixClient syncing", () => { httpBackend!.flushAllExpected(), awaitSyncEvent(2), ]).then(() => { - const room = client!.getRoom(roomTwo); - let member = room.getMember(otherUserId); + const room = client!.getRoom(roomTwo)!; + let member = room.getMember(otherUserId)!; expect(member).toBeTruthy(); expect(member.typing).toEqual(true); - member = room.getMember(selfUserId); + member = room.getMember(selfUserId)!; expect(member).toBeTruthy(); expect(member.typing).toEqual(false); }); @@ -684,7 +696,7 @@ describe("MatrixClient syncing", () => { httpBackend!.flushAllExpected(), awaitSyncEvent(2), ]).then(() => { - const room = client!.getRoom(roomOne); + const room = client!.getRoom(roomOne)!; const stateAtStart = room.getLiveTimeline().getState( EventTimeline.BACKWARDS, ); @@ -782,7 +794,7 @@ describe("MatrixClient syncing", () => { awaitSyncEvent(2), ]); - const room = client!.getRoom(roomOne); + const room = client!.getRoom(roomOne)!; expect(room.getTimelineNeedsRefresh()).toEqual(false); }); @@ -852,7 +864,7 @@ describe("MatrixClient syncing", () => { awaitSyncEvent(), ]); - const room = client!.getRoom(roomOne); + const room = client!.getRoom(roomOne)!; expect(room.getTimelineNeedsRefresh()).toEqual(false); }); @@ -882,7 +894,7 @@ describe("MatrixClient syncing", () => { awaitSyncEvent(), ]); - const room = client!.getRoom(roomOne); + const room = client!.getRoom(roomOne)!; expect(room.getTimelineNeedsRefresh()).toEqual(false); }); @@ -915,7 +927,7 @@ describe("MatrixClient syncing", () => { awaitSyncEvent(), ]); - const room = client!.getRoom(roomOne); + const room = client!.getRoom(roomOne)!; expect(room.getTimelineNeedsRefresh()).toEqual(false); }); @@ -949,7 +961,7 @@ describe("MatrixClient syncing", () => { ]); // Get the room after the first sync so the room is created - const room = client!.getRoom(roomOne); + const room = client!.getRoom(roomOne)!; let emitCount = 0; room.on(RoomEvent.HistoryImportedWithinTimeline, (markerEvent, room) => { @@ -1005,7 +1017,7 @@ describe("MatrixClient syncing", () => { awaitSyncEvent(2), ]); - const room = client!.getRoom(roomOne); + const room = client!.getRoom(roomOne)!; expect(room.getTimelineNeedsRefresh()).toEqual(true); }); }); @@ -1060,7 +1072,7 @@ describe("MatrixClient syncing", () => { ]); // Get the room after the first sync so the room is created - const room = client!.getRoom(roomOne); + const room = client!.getRoom(roomOne)!; expect(room).toBeTruthy(); let stateEventEmitCount = 0; @@ -1134,7 +1146,7 @@ describe("MatrixClient syncing", () => { ]); // Get the room after the first sync so the room is created - const room = client!.getRoom(roomOne); + const room = client!.getRoom(roomOne)!; expect(room).toBeTruthy(); let stateEventEmitCount = 0; @@ -1231,7 +1243,7 @@ describe("MatrixClient syncing", () => { httpBackend!.flushAllExpected(), awaitSyncEvent(), ]).then(() => { - const room = client!.getRoom(roomTwo); + const room = client!.getRoom(roomTwo)!; expect(room).toBeTruthy(); const tok = room.getLiveTimeline() .getPaginationToken(EventTimeline.BACKWARDS); @@ -1274,7 +1286,7 @@ describe("MatrixClient syncing", () => { httpBackend!.flushAllExpected(), awaitSyncEvent(), ]).then(() => { - const room = client!.getRoom(roomOne); + const room = client!.getRoom(roomOne)!; const tl = room.getLiveTimeline(); expect(tl.getEvents().length).toEqual(1); expect(resetCallCount).toEqual(1); @@ -1353,7 +1365,7 @@ describe("MatrixClient syncing", () => { httpBackend!.flushAllExpected(), awaitSyncEvent(), ]).then(() => { - const room = client!.getRoom(roomOne); + const room = client!.getRoom(roomOne)!; expect(room.getReceiptsForEvent(new MatrixEvent(ackEvent))).toEqual([{ type: "m.read", userId: userC, @@ -1426,8 +1438,8 @@ describe("MatrixClient syncing", () => { ]).then(() => { const room = client!.getRoom(roomOne); - expect(room.getThreadUnreadNotificationCount(THREAD_ID, NotificationCountType.Total)).toBe(5); - expect(room.getThreadUnreadNotificationCount(THREAD_ID, NotificationCountType.Highlight)).toBe(2); + expect(room!.getThreadUnreadNotificationCount(THREAD_ID, NotificationCountType.Total)).toBe(5); + expect(room!.getThreadUnreadNotificationCount(THREAD_ID, NotificationCountType.Highlight)).toBe(2); }); }); }); @@ -1469,7 +1481,7 @@ describe("MatrixClient syncing", () => { const prom = new Promise((resolve) => { httpBackend!.when("GET", "/sync").check((req) => { - expect(req.queryParams.filter).toEqual("another_id"); + expect(req.queryParams!.filter).toEqual("another_id"); resolve(); }).respond(200, {}); }); @@ -1514,7 +1526,7 @@ describe("MatrixClient syncing", () => { return Promise.all([ client!.syncLeftRooms().then(() => { - const room = client!.getRoom(roomTwo); + const room = client!.getRoom(roomTwo)!; const tok = room.getLiveTimeline().getPaginationToken( EventTimeline.BACKWARDS); @@ -1536,7 +1548,7 @@ describe("MatrixClient syncing", () => { * @returns {Promise} promise which resolves after the sync events have happened */ function awaitSyncEvent(numSyncs?: number) { - return utils.syncPromise(client, numSyncs); + return utils.syncPromise(client!, numSyncs); } }); diff --git a/spec/integ/megolm-backup.spec.ts b/spec/integ/megolm-backup.spec.ts index 5fa675519..492e4f1dc 100644 --- a/spec/integ/megolm-backup.spec.ts +++ b/spec/integ/megolm-backup.spec.ts @@ -95,26 +95,31 @@ describe("megolm key backups", function() { return; } const Olm = global.Olm; - - let testOlmAccount: Account; + let testOlmAccount: Olm.Account; let aliceTestClient: TestClient; + const setupTestClient = (): [Account, TestClient] => { + const aliceTestClient = new TestClient( + "@alice:localhost", "xzcvb", "akjgkrgjs", + ); + const testOlmAccount = new Olm.Account(); + testOlmAccount!.create(); + + return [testOlmAccount, aliceTestClient]; + }; + beforeAll(function() { return Olm.init(); }); beforeEach(async function() { - aliceTestClient = new TestClient( - "@alice:localhost", "xzcvb", "akjgkrgjs", - ); - testOlmAccount = new Olm.Account(); - testOlmAccount.create(); - await aliceTestClient.client.initCrypto(); - aliceTestClient.client.crypto.backupManager.backupInfo = CURVE25519_BACKUP_INFO; + [testOlmAccount, aliceTestClient] = setupTestClient(); + await aliceTestClient!.client.initCrypto(); + aliceTestClient!.client.crypto!.backupManager.backupInfo = CURVE25519_BACKUP_INFO; }); afterEach(function() { - return aliceTestClient.stop(); + return aliceTestClient!.stop(); }); it("Alice checks key backups when receiving a message she can't decrypt", function() { @@ -130,22 +135,22 @@ describe("megolm key backups", function() { }, }; - return aliceTestClient.start().then(() => { + return aliceTestClient!.start().then(() => { return createOlmSession(testOlmAccount, aliceTestClient); }).then(() => { const privkey = decodeRecoveryKey(RECOVERY_KEY); - return aliceTestClient.client.crypto.storeSessionBackupPrivateKey(privkey); + return aliceTestClient!.client!.crypto!.storeSessionBackupPrivateKey(privkey); }).then(() => { - aliceTestClient.httpBackend.when("GET", "/sync").respond(200, syncResponse); - aliceTestClient.expectKeyBackupQuery( + aliceTestClient!.httpBackend.when("GET", "/sync").respond(200, syncResponse); + aliceTestClient!.expectKeyBackupQuery( ROOM_ID, SESSION_ID, 200, CURVE25519_KEY_BACKUP_DATA, ); - return aliceTestClient.httpBackend.flushAllExpected(); + return aliceTestClient!.httpBackend.flushAllExpected(); }).then(function(): Promise { - const room = aliceTestClient.client.getRoom(ROOM_ID); + const room = aliceTestClient!.client.getRoom(ROOM_ID)!; const event = room.getLiveTimeline().getEvents()[0]; if (event.getContent()) { diff --git a/spec/integ/megolm-integ.spec.ts b/spec/integ/megolm-integ.spec.ts index ae2771f3b..9454749a9 100644 --- a/spec/integ/megolm-integ.spec.ts +++ b/spec/integ/megolm-integ.spec.ts @@ -207,9 +207,11 @@ describe("megolm", () => { } const Olm = global.Olm; - let testOlmAccount: Olm.Account; - let testSenderKey: string; - let aliceTestClient: TestClient; + let testOlmAccount = {} as unknown as Olm.Account; + let testSenderKey = ''; + let aliceTestClient = new TestClient( + "@alice:localhost", "device2", "access_token2", + ); /** * Get the device keys for testOlmAccount in a format suitable for a @@ -283,12 +285,12 @@ describe("megolm", () => { it("Alice receives a megolm message", async () => { await aliceTestClient.start(); - aliceTestClient.client.crypto.deviceList.downloadKeys = () => Promise.resolve({}); + aliceTestClient.client.crypto!.deviceList.downloadKeys = () => Promise.resolve({}); const p2pSession = await createOlmSession(testOlmAccount, aliceTestClient); const groupSession = new Olm.OutboundGroupSession(); groupSession.create(); - aliceTestClient.client.crypto.deviceList.getUserByIdentityKey = () => "@bob:xyz"; + aliceTestClient.client.crypto!.deviceList.getUserByIdentityKey = () => "@bob:xyz"; // make the room_key event const roomKeyEncrypted = encryptGroupSessionKey({ @@ -322,7 +324,7 @@ describe("megolm", () => { aliceTestClient.httpBackend.when("GET", "/sync").respond(200, syncResponse); await aliceTestClient.flushSync(); - const room = aliceTestClient.client.getRoom(ROOM_ID); + const room = aliceTestClient.client.getRoom(ROOM_ID)!; const event = room.getLiveTimeline().getEvents()[0]; expect(event.isEncrypted()).toBe(true); const decryptedEvent = await testUtils.awaitDecryption(event); @@ -332,12 +334,12 @@ describe("megolm", () => { it("Alice receives a megolm message before the session keys", async () => { // https://github.com/vector-im/element-web/issues/2273 await aliceTestClient.start(); - aliceTestClient.client.crypto.deviceList.downloadKeys = () => Promise.resolve({}); + aliceTestClient.client.crypto!.deviceList.downloadKeys = () => Promise.resolve({}); const p2pSession = await createOlmSession(testOlmAccount, aliceTestClient); const groupSession = new Olm.OutboundGroupSession(); groupSession.create(); - aliceTestClient.client.crypto.deviceList.getUserByIdentityKey = () => "@bob:xyz"; + aliceTestClient.client.crypto!.deviceList.getUserByIdentityKey = () => "@bob:xyz"; // make the room_key event, but don't send it yet const roomKeyEncrypted = encryptGroupSessionKey({ @@ -362,7 +364,7 @@ describe("megolm", () => { }); await aliceTestClient.flushSync(); - const room = aliceTestClient.client.getRoom(ROOM_ID); + const room = aliceTestClient.client.getRoom(ROOM_ID)!; expect(room.getLiveTimeline().getEvents()[0].getContent().msgtype).toEqual('m.bad.encrypted'); // now she gets the room_key event @@ -392,12 +394,12 @@ describe("megolm", () => { it("Alice gets a second room_key message", async () => { await aliceTestClient.start(); - aliceTestClient.client.crypto.deviceList.downloadKeys = () => Promise.resolve({}); + aliceTestClient.client.crypto!.deviceList.downloadKeys = () => Promise.resolve({}); const p2pSession = await createOlmSession(testOlmAccount, aliceTestClient); const groupSession = new Olm.OutboundGroupSession(); groupSession.create(); - aliceTestClient.client.crypto.deviceList.getUserByIdentityKey = () => "@bob:xyz"; + aliceTestClient.client.crypto!.deviceList.getUserByIdentityKey = () => "@bob:xyz"; // make the room_key event const roomKeyEncrypted1 = encryptGroupSessionKey({ @@ -451,7 +453,7 @@ describe("megolm", () => { await aliceTestClient.flushSync(); await aliceTestClient.flushSync(); - const room = aliceTestClient.client.getRoom(ROOM_ID); + const room = aliceTestClient.client.getRoom(ROOM_ID)!; await room.decryptCriticalEvents(); const event = room.getLiveTimeline().getEvents()[0]; expect(event.getContent().body).toEqual('42'); @@ -499,7 +501,7 @@ describe("megolm", () => { let inboundGroupSession: Olm.InboundGroupSession; aliceTestClient.httpBackend.when( 'PUT', '/sendToDevice/m.room.encrypted/', - ).respond(200, function(_path, content) { + ).respond(200, function(_path, content: any) { const m = content.messages['@bob:xyz'].DEVICE_ID; const ct = m.ciphertext[testSenderKey]; const decrypted = JSON.parse(p2pSession.decrypt(ct.type, ct.body)); @@ -525,7 +527,7 @@ describe("megolm", () => { return { event_id: '$event_id' }; }); - const room = aliceTestClient.client.getRoom(ROOM_ID); + const room = aliceTestClient.client.getRoom(ROOM_ID)!; const pendingMsg = room.getPendingEvents()[0]; await Promise.all([ @@ -628,7 +630,7 @@ describe("megolm", () => { let megolmSessionId: string; aliceTestClient.httpBackend.when( 'PUT', '/sendToDevice/m.room.encrypted/', - ).respond(200, function(_path, content) { + ).respond(200, function(_path, content: any) { logger.log('sendToDevice: ', content); const m = content.messages['@bob:xyz'].DEVICE_ID; const ct = m.ciphertext[testSenderKey]; @@ -706,7 +708,7 @@ describe("megolm", () => { // invalidate the device cache for all members in e2e rooms (ie, // herself), and do a key query. aliceTestClient.expectKeyQuery( - getTestKeysQueryResponse(aliceTestClient.userId), + getTestKeysQueryResponse(aliceTestClient.userId!), ); await aliceTestClient.httpBackend.flushAllExpected(); @@ -716,28 +718,30 @@ describe("megolm", () => { await aliceTestClient.client.sendTextMessage(ROOM_ID, 'test'); throw new Error("sendTextMessage succeeded on an unknown device"); } catch (e) { - expect(e.name).toEqual("UnknownDeviceError"); - expect(Object.keys(e.devices)).toEqual([aliceTestClient.userId]); - expect(Object.keys(e.devices[aliceTestClient.userId])). + expect((e as any).name).toEqual("UnknownDeviceError"); + expect(Object.keys((e as any).devices)).toEqual([aliceTestClient.userId!]); + expect(Object.keys((e as any)?.devices[aliceTestClient.userId!])). toEqual(['DEVICE_ID']); } // mark the device as known, and resend. - aliceTestClient.client.setDeviceKnown(aliceTestClient.userId, 'DEVICE_ID'); + aliceTestClient.client.setDeviceKnown(aliceTestClient.userId!, 'DEVICE_ID'); aliceTestClient.httpBackend.when('POST', '/keys/claim').respond( - 200, function(_path, content) { - expect(content.one_time_keys[aliceTestClient.userId].DEVICE_ID) + 200, function(_path, content: IClaimOTKsResult) { + expect(content.one_time_keys[aliceTestClient.userId!].DEVICE_ID) .toEqual("signed_curve25519"); - return getTestKeysClaimResponse(aliceTestClient.userId); + return getTestKeysClaimResponse(aliceTestClient.userId!); }); let p2pSession: Olm.Session; let inboundGroupSession: Olm.InboundGroupSession; aliceTestClient.httpBackend.when( 'PUT', '/sendToDevice/m.room.encrypted/', - ).respond(200, function(_path, content) { + ).respond(200, function(_path, content: { + messages: { [userId: string]: { [deviceId: string]: Record }}; + }) { logger.log("sendToDevice: ", content); - const m = content.messages[aliceTestClient.userId].DEVICE_ID; + const m = content.messages[aliceTestClient.userId!].DEVICE_ID; const ct = m.ciphertext[testSenderKey]; expect(ct.type).toEqual(0); // pre-key message @@ -751,7 +755,7 @@ describe("megolm", () => { return {}; }); - let decrypted: IEvent; + let decrypted: Partial = {}; aliceTestClient.httpBackend.when( 'PUT', '/send/', ).respond(200, function(_path, content: IContent) { @@ -766,7 +770,7 @@ describe("megolm", () => { }); // Grab the event that we'll need to resend - const room = aliceTestClient.client.getRoom(ROOM_ID); + const room = aliceTestClient.client.getRoom(ROOM_ID)!; const pendingEvents = room.getPendingEvents(); expect(pendingEvents.length).toEqual(1); const unsentEvent = pendingEvents[0]; @@ -781,7 +785,7 @@ describe("megolm", () => { ]); expect(decrypted.type).toEqual('m.room.message'); - expect(decrypted.content.body).toEqual('test'); + expect(decrypted.content?.body).toEqual('test'); }); it('Alice should wait for device list to complete when sending a megolm message', async () => { @@ -830,11 +834,11 @@ describe("megolm", () => { it("Alice exports megolm keys and imports them to a new device", async () => { aliceTestClient.expectKeyQuery({ device_keys: { '@alice:localhost': {} }, failures: {} }); await aliceTestClient.start(); - aliceTestClient.client.crypto.deviceList.downloadKeys = () => Promise.resolve({}); + aliceTestClient.client.crypto!.deviceList.downloadKeys = () => Promise.resolve({}); // establish an olm session with alice const p2pSession = await createOlmSession(testOlmAccount, aliceTestClient); - aliceTestClient.client.crypto.deviceList.getUserByIdentityKey = () => "@bob:xyz"; + aliceTestClient.client.crypto!.deviceList.getUserByIdentityKey = () => "@bob:xyz"; const groupSession = new Olm.OutboundGroupSession(); groupSession.create(); @@ -867,7 +871,7 @@ describe("megolm", () => { }); await aliceTestClient.flushSync(); - const room = aliceTestClient.client.getRoom(ROOM_ID); + const room = aliceTestClient.client.getRoom(ROOM_ID)!; await room.decryptCriticalEvents(); expect(room.getLiveTimeline().getEvents()[0].getContent().body).toEqual('42'); @@ -883,7 +887,7 @@ describe("megolm", () => { await aliceTestClient.client.importRoomKeys(exported); await aliceTestClient.start(); - aliceTestClient.client.crypto.deviceList.getUserByIdentityKey = () => "@bob:xyz"; + aliceTestClient.client.crypto!.deviceList.getUserByIdentityKey = () => "@bob:xyz"; const syncResponse = { next_batch: 1, @@ -927,7 +931,7 @@ describe("megolm", () => { ...rawEvent, room: ROOM_ID, }); - await event1.attemptDecryption(testClient.client.crypto, { isRetry: true }); + await event1.attemptDecryption(testClient.client.crypto!, { isRetry: true }); expect(event1.isKeySourceUntrusted()).toBeTruthy(); const event2 = testUtils.mkEvent({ @@ -943,26 +947,26 @@ describe("megolm", () => { // @ts-ignore - private event2.senderCurve25519Key = testSenderKey; // @ts-ignore - private - testClient.client.crypto.onRoomKeyEvent(event2); + testClient.client.crypto!.onRoomKeyEvent(event2); const event3 = testUtils.mkEvent({ event: true, ...rawEvent, room: ROOM_ID, }); - await event3.attemptDecryption(testClient.client.crypto, { isRetry: true }); + await event3.attemptDecryption(testClient.client.crypto!, { isRetry: true }); expect(event3.isKeySourceUntrusted()).toBeFalsy(); testClient.stop(); }); it("Alice can decrypt a message with falsey content", async () => { await aliceTestClient.start(); - aliceTestClient.client.crypto.deviceList.downloadKeys = () => Promise.resolve({}); + aliceTestClient.client.crypto!.deviceList.downloadKeys = () => Promise.resolve({}); const p2pSession = await createOlmSession(testOlmAccount, aliceTestClient); const groupSession = new Olm.OutboundGroupSession(); groupSession.create(); - aliceTestClient.client.crypto.deviceList.getUserByIdentityKey = () => "@bob:xyz"; + aliceTestClient.client.crypto!.deviceList.getUserByIdentityKey = () => "@bob:xyz"; // make the room_key event const roomKeyEncrypted = encryptGroupSessionKey({ @@ -1005,7 +1009,7 @@ describe("megolm", () => { aliceTestClient.httpBackend.when("GET", "/sync").respond(200, syncResponse); await aliceTestClient.flushSync(); - const room = aliceTestClient.client.getRoom(ROOM_ID); + const room = aliceTestClient.client.getRoom(ROOM_ID)!; const event = room.getLiveTimeline().getEvents()[0]; expect(event.isEncrypted()).toBe(true); const decryptedEvent = await testUtils.awaitDecryption(event); @@ -1018,12 +1022,12 @@ describe("megolm", () => { "should successfully decrypt bundled redaction events that don't include a room_id in their /sync data", async () => { await aliceTestClient.start(); - aliceTestClient.client.crypto.deviceList.downloadKeys = () => Promise.resolve({}); + aliceTestClient.client.crypto!.deviceList.downloadKeys = () => Promise.resolve({}); const p2pSession = await createOlmSession(testOlmAccount, aliceTestClient); const groupSession = new Olm.OutboundGroupSession(); groupSession.create(); - aliceTestClient.client.crypto.deviceList.getUserByIdentityKey = () => "@bob:xyz"; + aliceTestClient.client.crypto!.deviceList.getUserByIdentityKey = () => "@bob:xyz"; // make the room_key event const roomKeyEncrypted = encryptGroupSessionKey({ @@ -1072,10 +1076,10 @@ describe("megolm", () => { aliceTestClient.httpBackend.when("GET", "/sync").respond(200, syncResponse); await aliceTestClient.flushSync(); - const room = aliceTestClient.client.getRoom(ROOM_ID); + const room = aliceTestClient.client.getRoom(ROOM_ID)!; const event = room.getLiveTimeline().getEvents()[0]; expect(event.isEncrypted()).toBe(true); - await event.attemptDecryption(aliceTestClient.client.crypto); + await event.attemptDecryption(aliceTestClient.client.crypto!); expect(event.getContent()).toEqual({}); const redactionEvent: any = event.getRedactionEvent(); expect(redactionEvent.content.reason).toEqual("redaction test"); @@ -1089,7 +1093,7 @@ describe("megolm", () => { await beccaTestClient.client.initCrypto(); await aliceTestClient.start(); - aliceTestClient.client.crypto.deviceList.downloadKeys = () => Promise.resolve({}); + aliceTestClient.client.crypto!.deviceList.downloadKeys = () => Promise.resolve({}); await beccaTestClient.start(); const beccaRoom = new Room(ROOM_ID, beccaTestClient.client, "@becca:localhost", {}); @@ -1107,7 +1111,7 @@ describe("megolm", () => { }, }); - await beccaTestClient.client.crypto.encryptEvent(event, beccaRoom); + await beccaTestClient.client.crypto!.encryptEvent(event, beccaRoom); // remove keys from the event // @ts-ignore private properties event.clearEvent = undefined; @@ -1116,23 +1120,23 @@ describe("megolm", () => { // @ts-ignore private properties event.claimedEd25519Key = null; - const device = new DeviceInfo(beccaTestClient.client.deviceId); - aliceTestClient.client.crypto.deviceList.getDeviceByIdentityKey = () => device; - aliceTestClient.client.crypto.deviceList.getUserByIdentityKey = () => beccaTestClient.client.getUserId(); + const device = new DeviceInfo(beccaTestClient.client.deviceId!); + aliceTestClient.client.crypto!.deviceList.getDeviceByIdentityKey = () => device; + aliceTestClient.client.crypto!.deviceList.getUserByIdentityKey = () => beccaTestClient.client.getUserId()!; // Create an olm session for Becca and Alice's devices const aliceOtks = await aliceTestClient.awaitOneTimeKeyUpload(); const aliceOtkId = Object.keys(aliceOtks)[0]; const aliceOtk = aliceOtks[aliceOtkId]; const p2pSession = new global.Olm.Session(); - await beccaTestClient.client.crypto.cryptoStore.doTxn( + await beccaTestClient.client.crypto!.cryptoStore.doTxn( 'readonly', [IndexedDBCryptoStore.STORE_ACCOUNT], (txn) => { - beccaTestClient.client.crypto.cryptoStore.getAccount(txn, (pickledAccount: string) => { + beccaTestClient.client.crypto!.cryptoStore.getAccount(txn, (pickledAccount: string) => { const account = new global.Olm.Account(); try { - account.unpickle(beccaTestClient.client.crypto.olmDevice.pickleKey, pickledAccount); + account.unpickle(beccaTestClient.client.crypto!.olmDevice.pickleKey, pickledAccount); p2pSession.create_outbound(account, aliceTestClient.getDeviceKey(), aliceOtk.key); } finally { account.free(); @@ -1142,7 +1146,7 @@ describe("megolm", () => { ); const content = event.getWireContent(); - const groupSessionKey = await beccaTestClient.client.crypto.olmDevice.getInboundGroupSessionKey( + const groupSessionKey = await beccaTestClient.client.crypto!.olmDevice.getInboundGroupSessionKey( ROOM_ID, content.sender_key, content.session_id, @@ -1213,7 +1217,7 @@ describe("megolm", () => { }); await aliceTestClient.flushSync(); - const room = aliceTestClient.client.getRoom(ROOM_ID); + const room = aliceTestClient.client.getRoom(ROOM_ID)!; const roomEvent = room.getLiveTimeline().getEvents()[0]; expect(roomEvent.isEncrypted()).toBe(true); const decryptedEvent = await testUtils.awaitDecryption(roomEvent); @@ -1246,7 +1250,7 @@ describe("megolm", () => { }, }); - await beccaTestClient.client.crypto.encryptEvent(event, beccaRoom); + await beccaTestClient.client.crypto!.encryptEvent(event, beccaRoom); // remove keys from the event // @ts-ignore private properties event.clearEvent = undefined; @@ -1255,22 +1259,22 @@ describe("megolm", () => { // @ts-ignore private properties event.claimedEd25519Key = null; - const device = new DeviceInfo(beccaTestClient.client.deviceId); - aliceTestClient.client.crypto.deviceList.getDeviceByIdentityKey = () => device; + const device = new DeviceInfo(beccaTestClient.client.deviceId!); + aliceTestClient.client.crypto!.deviceList.getDeviceByIdentityKey = () => device; // Create an olm session for Becca and Alice's devices const aliceOtks = await aliceTestClient.awaitOneTimeKeyUpload(); const aliceOtkId = Object.keys(aliceOtks)[0]; const aliceOtk = aliceOtks[aliceOtkId]; const p2pSession = new global.Olm.Session(); - await beccaTestClient.client.crypto.cryptoStore.doTxn( + await beccaTestClient.client.crypto!.cryptoStore.doTxn( 'readonly', [IndexedDBCryptoStore.STORE_ACCOUNT], (txn) => { - beccaTestClient.client.crypto.cryptoStore.getAccount(txn, (pickledAccount: string) => { + beccaTestClient.client.crypto!.cryptoStore.getAccount(txn, (pickledAccount: string) => { const account = new global.Olm.Account(); try { - account.unpickle(beccaTestClient.client.crypto.olmDevice.pickleKey, pickledAccount); + account.unpickle(beccaTestClient.client.crypto!.olmDevice.pickleKey, pickledAccount); p2pSession.create_outbound(account, aliceTestClient.getDeviceKey(), aliceOtk.key); } finally { account.free(); @@ -1280,7 +1284,7 @@ describe("megolm", () => { ); const content = event.getWireContent(); - const groupSessionKey = await beccaTestClient.client.crypto.olmDevice.getInboundGroupSessionKey( + const groupSessionKey = await beccaTestClient.client.crypto!.olmDevice.getInboundGroupSessionKey( ROOM_ID, content.sender_key, content.session_id, @@ -1352,7 +1356,7 @@ describe("megolm", () => { await aliceTestClient.flushSync(); // Decryption should fail, because Alice hasn't received any keys she can trust - const room = aliceTestClient.client.getRoom(ROOM_ID); + const room = aliceTestClient.client.getRoom(ROOM_ID)!; const roomEvent = room.getLiveTimeline().getEvents()[0]; expect(roomEvent.isEncrypted()).toBe(true); const decryptedEvent = await testUtils.awaitDecryption(roomEvent); diff --git a/spec/integ/sliding-sync-sdk.spec.ts b/spec/integ/sliding-sync-sdk.spec.ts index f7dc68754..47e6ba8df 100644 --- a/spec/integ/sliding-sync-sdk.spec.ts +++ b/spec/integ/sliding-sync-sdk.spec.ts @@ -31,10 +31,10 @@ import { IStoredClientOpts } from "../../src/client"; import { logger } from "../../src/logger"; describe("SlidingSyncSdk", () => { - let client: MatrixClient = null; - let httpBackend: MockHttpBackend = null; - let sdk: SlidingSyncSdk = null; - let mockSlidingSync: SlidingSync = null; + let client: MatrixClient | undefined; + let httpBackend: MockHttpBackend | undefined; + let sdk: SlidingSyncSdk | undefined; + let mockSlidingSync: SlidingSync | undefined; const selfUserId = "@alice:localhost"; const selfAccessToken = "aseukfgwef"; @@ -66,7 +66,7 @@ describe("SlidingSyncSdk", () => { event_id: "$" + eventIdCounter, }; }; - const mkOwnStateEvent = (evType: string, content: object, stateKey?: string): IStateEvent => { + const mkOwnStateEvent = (evType: string, content: object, stateKey = ''): IStateEvent => { eventIdCounter++; return { type: evType, @@ -103,24 +103,24 @@ describe("SlidingSyncSdk", () => { client = testClient.client; mockSlidingSync = mockifySlidingSync(new SlidingSync("", [], {}, client, 0)); if (testOpts.withCrypto) { - httpBackend.when("GET", "/room_keys/version").respond(404, {}); - await client.initCrypto(); - testOpts.crypto = client.crypto; + httpBackend!.when("GET", "/room_keys/version").respond(404, {}); + await client!.initCrypto(); + testOpts.crypto = client!.crypto; } - httpBackend.when("GET", "/_matrix/client/r0/pushrules").respond(200, {}); + httpBackend!.when("GET", "/_matrix/client/r0/pushrules").respond(200, {}); sdk = new SlidingSyncSdk(mockSlidingSync, client, testOpts); }; // tear down client/httpBackend globals const teardownClient = () => { - client.stopClient(); - return httpBackend.stop(); + client!.stopClient(); + return httpBackend!.stop(); }; // find an extension on a SlidingSyncSdk instance const findExtension = (name: string): Extension => { - expect(mockSlidingSync.registerExtension).toHaveBeenCalled(); - const mockFn = mockSlidingSync.registerExtension as jest.Mock; + expect(mockSlidingSync!.registerExtension).toHaveBeenCalled(); + const mockFn = mockSlidingSync!.registerExtension as jest.Mock; // find the extension for (let i = 0; i < mockFn.mock.calls.length; i++) { const calledExtension = mockFn.mock.calls[i][0] as Extension; @@ -137,14 +137,14 @@ describe("SlidingSyncSdk", () => { }); afterAll(teardownClient); it("can sync()", async () => { - const hasSynced = sdk.sync(); - await httpBackend.flushAllExpected(); + const hasSynced = sdk!.sync(); + await httpBackend!.flushAllExpected(); await hasSynced; - expect(mockSlidingSync.start).toBeCalled(); + expect(mockSlidingSync!.start).toBeCalled(); }); it("can stop()", async () => { - sdk.stop(); - expect(mockSlidingSync.stop).toBeCalled(); + sdk!.stop(); + expect(mockSlidingSync!.stop).toBeCalled(); }); }); @@ -156,8 +156,8 @@ describe("SlidingSyncSdk", () => { describe("initial", () => { beforeAll(async () => { - const hasSynced = sdk.sync(); - await httpBackend.flushAllExpected(); + const hasSynced = sdk!.sync(); + await httpBackend!.flushAllExpected(); await hasSynced; }); // inject some rooms with different fields set. @@ -277,8 +277,8 @@ describe("SlidingSyncSdk", () => { }; it("can be created with required_state and timeline", () => { - mockSlidingSync.emit(SlidingSyncEvent.RoomData, roomA, data[roomA]); - const gotRoom = client.getRoom(roomA); + mockSlidingSync!.emit(SlidingSyncEvent.RoomData, roomA, data[roomA]); + const gotRoom = client!.getRoom(roomA); expect(gotRoom).toBeDefined(); if (gotRoom == null) { return; } expect(gotRoom.name).toEqual(data[roomA].name); @@ -287,8 +287,8 @@ describe("SlidingSyncSdk", () => { }); it("can be created with timeline only", () => { - mockSlidingSync.emit(SlidingSyncEvent.RoomData, roomB, data[roomB]); - const gotRoom = client.getRoom(roomB); + mockSlidingSync!.emit(SlidingSyncEvent.RoomData, roomB, data[roomB]); + const gotRoom = client!.getRoom(roomB); expect(gotRoom).toBeDefined(); if (gotRoom == null) { return; } expect(gotRoom.name).toEqual(data[roomB].name); @@ -297,8 +297,8 @@ describe("SlidingSyncSdk", () => { }); it("can be created with a highlight_count", () => { - mockSlidingSync.emit(SlidingSyncEvent.RoomData, roomC, data[roomC]); - const gotRoom = client.getRoom(roomC); + mockSlidingSync!.emit(SlidingSyncEvent.RoomData, roomC, data[roomC]); + const gotRoom = client!.getRoom(roomC); expect(gotRoom).toBeDefined(); if (gotRoom == null) { return; } expect( @@ -307,8 +307,8 @@ describe("SlidingSyncSdk", () => { }); it("can be created with a notification_count", () => { - mockSlidingSync.emit(SlidingSyncEvent.RoomData, roomD, data[roomD]); - const gotRoom = client.getRoom(roomD); + mockSlidingSync!.emit(SlidingSyncEvent.RoomData, roomD, data[roomD]); + const gotRoom = client!.getRoom(roomD); expect(gotRoom).toBeDefined(); if (gotRoom == null) { return; } expect( @@ -317,8 +317,8 @@ describe("SlidingSyncSdk", () => { }); it("can be created with an invited/joined_count", () => { - mockSlidingSync.emit(SlidingSyncEvent.RoomData, roomG, data[roomG]); - const gotRoom = client.getRoom(roomG); + mockSlidingSync!.emit(SlidingSyncEvent.RoomData, roomG, data[roomG]); + const gotRoom = client!.getRoom(roomG); expect(gotRoom).toBeDefined(); if (gotRoom == null) { return; } expect(gotRoom.getInvitedMemberCount()).toEqual(data[roomG].invited_count); @@ -326,8 +326,8 @@ describe("SlidingSyncSdk", () => { }); it("can be created with invite_state", () => { - mockSlidingSync.emit(SlidingSyncEvent.RoomData, roomE, data[roomE]); - const gotRoom = client.getRoom(roomE); + mockSlidingSync!.emit(SlidingSyncEvent.RoomData, roomE, data[roomE]); + const gotRoom = client!.getRoom(roomE); expect(gotRoom).toBeDefined(); if (gotRoom == null) { return; } expect(gotRoom.getMyMembership()).toEqual("invite"); @@ -335,8 +335,8 @@ describe("SlidingSyncSdk", () => { }); it("uses the 'name' field to caluclate the room name", () => { - mockSlidingSync.emit(SlidingSyncEvent.RoomData, roomF, data[roomF]); - const gotRoom = client.getRoom(roomF); + mockSlidingSync!.emit(SlidingSyncEvent.RoomData, roomF, data[roomF]); + const gotRoom = client!.getRoom(roomF); expect(gotRoom).toBeDefined(); if (gotRoom == null) { return; } expect( @@ -347,12 +347,12 @@ describe("SlidingSyncSdk", () => { describe("updating", () => { it("can update with a new timeline event", async () => { const newEvent = mkOwnEvent(EventType.RoomMessage, { body: "new event A" }); - mockSlidingSync.emit(SlidingSyncEvent.RoomData, roomA, { + mockSlidingSync!.emit(SlidingSyncEvent.RoomData, roomA, { timeline: [newEvent], required_state: [], name: data[roomA].name, }); - const gotRoom = client.getRoom(roomA); + const gotRoom = client!.getRoom(roomA); expect(gotRoom).toBeDefined(); if (gotRoom == null) { return; } const newTimeline = data[roomA].timeline; @@ -361,31 +361,31 @@ describe("SlidingSyncSdk", () => { }); it("can update with a new required_state event", async () => { - let gotRoom = client.getRoom(roomB); + let gotRoom = client!.getRoom(roomB); expect(gotRoom).toBeDefined(); if (gotRoom == null) { return; } expect(gotRoom.getJoinRule()).toEqual(JoinRule.Invite); // default - mockSlidingSync.emit(SlidingSyncEvent.RoomData, roomB, { + mockSlidingSync!.emit(SlidingSyncEvent.RoomData, roomB, { required_state: [ mkOwnStateEvent("m.room.join_rules", { join_rule: "restricted" }, ""), ], timeline: [], name: data[roomB].name, }); - gotRoom = client.getRoom(roomB); + gotRoom = client!.getRoom(roomB); expect(gotRoom).toBeDefined(); if (gotRoom == null) { return; } expect(gotRoom.getJoinRule()).toEqual(JoinRule.Restricted); }); it("can update with a new highlight_count", async () => { - mockSlidingSync.emit(SlidingSyncEvent.RoomData, roomC, { + mockSlidingSync!.emit(SlidingSyncEvent.RoomData, roomC, { name: data[roomC].name, required_state: [], timeline: [], highlight_count: 1, }); - const gotRoom = client.getRoom(roomC); + const gotRoom = client!.getRoom(roomC); expect(gotRoom).toBeDefined(); if (gotRoom == null) { return; } expect( @@ -394,13 +394,13 @@ describe("SlidingSyncSdk", () => { }); it("can update with a new notification_count", async () => { - mockSlidingSync.emit(SlidingSyncEvent.RoomData, roomD, { + mockSlidingSync!.emit(SlidingSyncEvent.RoomData, roomD, { name: data[roomD].name, required_state: [], timeline: [], notification_count: 1, }); - const gotRoom = client.getRoom(roomD); + const gotRoom = client!.getRoom(roomD); expect(gotRoom).toBeDefined(); if (gotRoom == null) { return; } expect( @@ -409,13 +409,13 @@ describe("SlidingSyncSdk", () => { }); it("can update with a new joined_count", () => { - mockSlidingSync.emit(SlidingSyncEvent.RoomData, roomG, { + mockSlidingSync!.emit(SlidingSyncEvent.RoomData, roomG, { name: data[roomD].name, required_state: [], timeline: [], joined_count: 1, }); - const gotRoom = client.getRoom(roomG); + const gotRoom = client!.getRoom(roomG); expect(gotRoom).toBeDefined(); if (gotRoom == null) { return; } expect(gotRoom.getJoinedMemberCount()).toEqual(1); @@ -433,13 +433,13 @@ describe("SlidingSyncSdk", () => { mkOwnEvent(EventType.RoomMessage, { body: "old event C" }), ...timeline, ]; - mockSlidingSync.emit(SlidingSyncEvent.RoomData, roomA, { + mockSlidingSync!.emit(SlidingSyncEvent.RoomData, roomA, { timeline: oldTimeline, required_state: [], name: data[roomA].name, initial: true, // e.g requested via room subscription }); - const gotRoom = client.getRoom(roomA); + const gotRoom = client!.getRoom(roomA); expect(gotRoom).toBeDefined(); if (gotRoom == null) { return; } @@ -458,50 +458,50 @@ describe("SlidingSyncSdk", () => { describe("lifecycle", () => { beforeAll(async () => { await setupClient(); - const hasSynced = sdk.sync(); - await httpBackend.flushAllExpected(); + const hasSynced = sdk!.sync(); + await httpBackend!.flushAllExpected(); await hasSynced; }); const FAILED_SYNC_ERROR_THRESHOLD = 3; // would be nice to export the const in the actual class... it("emits SyncState.Reconnecting when < FAILED_SYNC_ERROR_THRESHOLD & SyncState.Error when over", async () => { - mockSlidingSync.emit( + mockSlidingSync!.emit( SlidingSyncEvent.Lifecycle, SlidingSyncState.Complete, { pos: "h", lists: [], rooms: {}, extensions: {} }, null, ); - expect(sdk.getSyncState()).toEqual(SyncState.Syncing); + expect(sdk!.getSyncState()).toEqual(SyncState.Syncing); - mockSlidingSync.emit( + mockSlidingSync!.emit( SlidingSyncEvent.Lifecycle, SlidingSyncState.RequestFinished, null, new Error("generic"), ); - expect(sdk.getSyncState()).toEqual(SyncState.Reconnecting); + expect(sdk!.getSyncState()).toEqual(SyncState.Reconnecting); for (let i = 0; i < FAILED_SYNC_ERROR_THRESHOLD; i++) { - mockSlidingSync.emit( + mockSlidingSync!.emit( SlidingSyncEvent.Lifecycle, SlidingSyncState.RequestFinished, null, new Error("generic"), ); } - expect(sdk.getSyncState()).toEqual(SyncState.Error); + expect(sdk!.getSyncState()).toEqual(SyncState.Error); }); it("emits SyncState.Syncing after a previous SyncState.Error", async () => { - mockSlidingSync.emit( + mockSlidingSync!.emit( SlidingSyncEvent.Lifecycle, SlidingSyncState.Complete, { pos: "i", lists: [], rooms: {}, extensions: {} }, null, ); - expect(sdk.getSyncState()).toEqual(SyncState.Syncing); + expect(sdk!.getSyncState()).toEqual(SyncState.Syncing); }); it("emits SyncState.Error immediately when receiving M_UNKNOWN_TOKEN and stops syncing", async () => { - expect(mockSlidingSync.stop).not.toBeCalled(); - mockSlidingSync.emit(SlidingSyncEvent.Lifecycle, SlidingSyncState.RequestFinished, null, new MatrixError({ + expect(mockSlidingSync!.stop).not.toBeCalled(); + mockSlidingSync!.emit(SlidingSyncEvent.Lifecycle, SlidingSyncState.RequestFinished, null, new MatrixError({ errcode: "M_UNKNOWN_TOKEN", message: "Oh no your access token is no longer valid", })); - expect(sdk.getSyncState()).toEqual(SyncState.Error); - expect(mockSlidingSync.stop).toBeCalled(); + expect(sdk!.getSyncState()).toEqual(SyncState.Error); + expect(mockSlidingSync!.stop).toBeCalled(); }); }); @@ -517,8 +517,8 @@ describe("SlidingSyncSdk", () => { avatar_url: "mxc://foobar", displayname: "The Invitee", }; - httpBackend.when("GET", "/profile").respond(200, inviteeProfile); - mockSlidingSync.emit(SlidingSyncEvent.RoomData, roomId, { + httpBackend!.when("GET", "/profile").respond(200, inviteeProfile); + mockSlidingSync!.emit(SlidingSyncEvent.RoomData, roomId, { initial: true, name: "Room with Invite", required_state: [], @@ -529,10 +529,10 @@ describe("SlidingSyncSdk", () => { mkOwnStateEvent(EventType.RoomMember, { membership: "invite" }, invitee), ], }); - await httpBackend.flush("/profile", 1, 1000); - const room = client.getRoom(roomId); + await httpBackend!.flush("/profile", 1, 1000); + const room = client!.getRoom(roomId)!; expect(room).toBeDefined(); - const inviteeMember = room.getMember(invitee); + const inviteeMember = room.getMember(invitee)!; expect(inviteeMember).toBeDefined(); expect(inviteeMember.getMxcAvatarUrl()).toEqual(inviteeProfile.avatar_url); expect(inviteeMember.name).toEqual(inviteeProfile.displayname); @@ -545,8 +545,8 @@ describe("SlidingSyncSdk", () => { await setupClient({ withCrypto: true, }); - const hasSynced = sdk.sync(); - await httpBackend.flushAllExpected(); + const hasSynced = sdk!.sync(); + await httpBackend!.flushAllExpected(); await hasSynced; ext = findExtension("e2ee"); }); @@ -554,7 +554,7 @@ describe("SlidingSyncSdk", () => { // needed else we do some async operations in the background which can cause Jest to whine: // "Cannot log after tests are done. Did you forget to wait for something async in your test?" // Attempted to log "Saving device tracking data null"." - client.crypto.stop(); + client!.crypto!.stop(); }); it("gets enabled on the initial request only", () => { expect(ext.onRequest(true)).toEqual({ @@ -572,38 +572,38 @@ describe("SlidingSyncSdk", () => { // TODO: more assertions? }); it("can update OTK counts", () => { - client.crypto.updateOneTimeKeyCount = jest.fn(); + client!.crypto!.updateOneTimeKeyCount = jest.fn(); ext.onResponse({ device_one_time_keys_count: { signed_curve25519: 42, }, }); - expect(client.crypto.updateOneTimeKeyCount).toHaveBeenCalledWith(42); + expect(client!.crypto!.updateOneTimeKeyCount).toHaveBeenCalledWith(42); ext.onResponse({ device_one_time_keys_count: { not_signed_curve25519: 42, // missing field -> default to 0 }, }); - expect(client.crypto.updateOneTimeKeyCount).toHaveBeenCalledWith(0); + expect(client!.crypto!.updateOneTimeKeyCount).toHaveBeenCalledWith(0); }); it("can update fallback keys", () => { ext.onResponse({ device_unused_fallback_key_types: ["signed_curve25519"], }); - expect(client.crypto.getNeedsNewFallback()).toEqual(false); + expect(client!.crypto!.getNeedsNewFallback()).toEqual(false); ext.onResponse({ device_unused_fallback_key_types: ["not_signed_curve25519"], }); - expect(client.crypto.getNeedsNewFallback()).toEqual(true); + expect(client!.crypto!.getNeedsNewFallback()).toEqual(true); }); }); describe("ExtensionAccountData", () => { let ext: Extension; beforeAll(async () => { await setupClient(); - const hasSynced = sdk.sync(); - await httpBackend.flushAllExpected(); + const hasSynced = sdk!.sync(); + await httpBackend!.flushAllExpected(); await hasSynced; ext = findExtension("account_data"); }); @@ -618,7 +618,7 @@ describe("SlidingSyncSdk", () => { const globalContent = { info: "here", }; - let globalData = client.getAccountData(globalType); + let globalData = client!.getAccountData(globalType); expect(globalData).toBeUndefined(); ext.onResponse({ global: [ @@ -628,13 +628,13 @@ describe("SlidingSyncSdk", () => { }, ], }); - globalData = client.getAccountData(globalType); + globalData = client!.getAccountData(globalType)!; expect(globalData).toBeDefined(); expect(globalData.getContent()).toEqual(globalContent); }); it("processes rooms account data", async () => { const roomId = "!room:id"; - mockSlidingSync.emit(SlidingSyncEvent.RoomData, roomId, { + mockSlidingSync!.emit(SlidingSyncEvent.RoomData, roomId, { name: "Room with account data", required_state: [], timeline: [ @@ -660,9 +660,9 @@ describe("SlidingSyncSdk", () => { ], }, }); - const room = client.getRoom(roomId); + const room = client!.getRoom(roomId)!; expect(room).toBeDefined(); - const event = room.getAccountData(roomType); + const event = room.getAccountData(roomType)!; expect(event).toBeDefined(); expect(event.getContent()).toEqual(roomContent); }); @@ -681,9 +681,9 @@ describe("SlidingSyncSdk", () => { ], }, }); - const room = client.getRoom(unknownRoomId); + const room = client!.getRoom(unknownRoomId); expect(room).toBeNull(); - expect(client.getAccountData(roomType)).toBeUndefined(); + expect(client!.getAccountData(roomType)).toBeUndefined(); }); it("can update push rules via account data", async () => { const roomId = "!foo:bar"; @@ -703,7 +703,7 @@ describe("SlidingSyncSdk", () => { }], }, }; - let pushRule = client.getRoomPushRule("global", roomId); + let pushRule = client!.getRoomPushRule("global", roomId); expect(pushRule).toBeUndefined(); ext.onResponse({ global: [ @@ -713,16 +713,16 @@ describe("SlidingSyncSdk", () => { }, ], }); - pushRule = client.getRoomPushRule("global", roomId); - expect(pushRule).toEqual(pushRulesContent.global[PushRuleKind.RoomSpecific][0]); + pushRule = client!.getRoomPushRule("global", roomId)!; + expect(pushRule).toEqual(pushRulesContent.global[PushRuleKind.RoomSpecific]![0]); }); }); describe("ExtensionToDevice", () => { let ext: Extension; beforeAll(async () => { await setupClient(); - const hasSynced = sdk.sync(); - await httpBackend.flushAllExpected(); + const hasSynced = sdk!.sync(); + await httpBackend!.flushAllExpected(); await hasSynced; ext = findExtension("to_device"); }); @@ -753,7 +753,7 @@ describe("SlidingSyncSdk", () => { foo: "bar", }; let called = false; - client.once(ClientEvent.ToDeviceEvent, (ev) => { + client!.once(ClientEvent.ToDeviceEvent, (ev) => { expect(ev.getContent()).toEqual(toDeviceContent); expect(ev.getType()).toEqual(toDeviceType); called = true; @@ -771,7 +771,7 @@ describe("SlidingSyncSdk", () => { }); it("can cancel key verification requests", async () => { const seen: Record = {}; - client.on(ClientEvent.ToDeviceEvent, (ev) => { + client!.on(ClientEvent.ToDeviceEvent, (ev) => { const evType = ev.getType(); expect(seen[evType]).toBeFalsy(); seen[evType] = true; diff --git a/spec/integ/sliding-sync.spec.ts b/spec/integ/sliding-sync.spec.ts index 4cfe39215..0364bed0f 100644 --- a/spec/integ/sliding-sync.spec.ts +++ b/spec/integ/sliding-sync.spec.ts @@ -30,8 +30,8 @@ import { sleep } from "../../src/utils"; * Each test will call different functions on SlidingSync which may depend on state from previous tests. */ describe("SlidingSync", () => { - let client: MatrixClient = null; - let httpBackend: MockHttpBackend = null; + let client: MatrixClient | undefined; + let httpBackend: MockHttpBackend | undefined; const selfUserId = "@alice:localhost"; const selfAccessToken = "aseukfgwef"; const proxyBaseUrl = "http://localhost:8008"; @@ -46,9 +46,9 @@ describe("SlidingSync", () => { // tear down client/httpBackend globals const teardownClient = () => { - httpBackend.verifyNoOutstandingExpectation(); - client.stopClient(); - return httpBackend.stop(); + httpBackend!.verifyNoOutstandingExpectation(); + client!.stopClient(); + return httpBackend!.stop(); }; describe("start/stop", () => { @@ -57,14 +57,14 @@ describe("SlidingSync", () => { let slidingSync: SlidingSync; it("should start the sync loop upon calling start()", async () => { - slidingSync = new SlidingSync(proxyBaseUrl, [], {}, client, 1); + slidingSync = new SlidingSync(proxyBaseUrl, [], {}, client!, 1); const fakeResp = { pos: "a", lists: [], rooms: {}, extensions: {}, }; - httpBackend.when("POST", syncUrl).respond(200, fakeResp); + httpBackend!.when("POST", syncUrl).respond(200, fakeResp); const p = listenUntil(slidingSync, "SlidingSync.Lifecycle", (state, resp, err) => { expect(state).toEqual(SlidingSyncState.RequestFinished); expect(resp).toEqual(fakeResp); @@ -72,13 +72,13 @@ describe("SlidingSync", () => { return true; }); slidingSync.start(); - await httpBackend.flushAllExpected(); + await httpBackend!.flushAllExpected(); await p; }); it("should stop the sync loop upon calling stop()", () => { slidingSync.stop(); - httpBackend.verifyNoOutstandingExpectation(); + httpBackend!.verifyNoOutstandingExpectation(); }); }); @@ -103,9 +103,9 @@ describe("SlidingSync", () => { it("should be able to subscribe to a room", async () => { // add the subscription - slidingSync = new SlidingSync(proxyBaseUrl, [], roomSubInfo, client, 1); + slidingSync = new SlidingSync(proxyBaseUrl, [], roomSubInfo, client!, 1); slidingSync.modifyRoomSubscriptions(new Set([roomId])); - httpBackend.when("POST", syncUrl).check(function(req) { + httpBackend!.when("POST", syncUrl).check(function(req) { const body = req.data; logger.log("room sub", body); expect(body.room_subscriptions).toBeTruthy(); @@ -125,7 +125,7 @@ describe("SlidingSync", () => { return true; }); slidingSync.start(); - await httpBackend.flushAllExpected(); + await httpBackend!.flushAllExpected(); await p; }); @@ -137,7 +137,7 @@ describe("SlidingSync", () => { ["m.room.member", "*"], ], }; - httpBackend.when("POST", syncUrl).check(function(req) { + httpBackend!.when("POST", syncUrl).check(function(req) { const body = req.data; logger.log("adjusted sub", body); expect(body.room_subscriptions).toBeTruthy(); @@ -158,7 +158,7 @@ describe("SlidingSync", () => { }); slidingSync.modifyRoomSubscriptionInfo(newSubInfo); - await httpBackend.flushAllExpected(); + await httpBackend!.flushAllExpected(); await p; // need to set what the new subscription info is for subsequent tests roomSubInfo = newSubInfo; @@ -179,7 +179,7 @@ describe("SlidingSync", () => { required_state: [], timeline: [], }; - httpBackend.when("POST", syncUrl).check(function(req) { + httpBackend!.when("POST", syncUrl).check(function(req) { const body = req.data; logger.log("new subs", body); expect(body.room_subscriptions).toBeTruthy(); @@ -204,12 +204,12 @@ describe("SlidingSync", () => { const subs = slidingSync.getRoomSubscriptions(); subs.add(anotherRoomID); slidingSync.modifyRoomSubscriptions(subs); - await httpBackend.flushAllExpected(); + await httpBackend!.flushAllExpected(); await p; }); it("should be able to unsubscribe from a room", async () => { - httpBackend.when("POST", syncUrl).check(function(req) { + httpBackend!.when("POST", syncUrl).check(function(req) { const body = req.data; logger.log("unsub request", body); expect(body.room_subscriptions).toBeFalsy(); @@ -226,7 +226,7 @@ describe("SlidingSync", () => { // remove the subscription for the first room slidingSync.modifyRoomSubscriptions(new Set([anotherRoomID])); - await httpBackend.flushAllExpected(); + await httpBackend!.flushAllExpected(); await p; slidingSync.stop(); @@ -273,8 +273,8 @@ describe("SlidingSync", () => { is_dm: true, }, }; - slidingSync = new SlidingSync(proxyBaseUrl, [listReq], {}, client, 1); - httpBackend.when("POST", syncUrl).check(function(req) { + slidingSync = new SlidingSync(proxyBaseUrl, [listReq], {}, client!, 1); + httpBackend!.when("POST", syncUrl).check(function(req) { const body = req.data; logger.log("list", body); expect(body.lists).toBeTruthy(); @@ -301,7 +301,7 @@ describe("SlidingSync", () => { return state === SlidingSyncState.Complete; }); slidingSync.start(); - await httpBackend.flushAllExpected(); + await httpBackend!.flushAllExpected(); await responseProcessed; expect(listenerData[roomA]).toEqual(rooms[roomA]); @@ -327,7 +327,7 @@ describe("SlidingSync", () => { it("should be possible to adjust list ranges", async () => { // modify the list ranges - httpBackend.when("POST", syncUrl).check(function(req) { + httpBackend!.when("POST", syncUrl).check(function(req) { const body = req.data; logger.log("next ranges", body.lists[0].ranges); expect(body.lists).toBeTruthy(); @@ -351,7 +351,7 @@ describe("SlidingSync", () => { return state === SlidingSyncState.RequestFinished; }); slidingSync.setListRanges(0, newRanges); - await httpBackend.flushAllExpected(); + await httpBackend!.flushAllExpected(); await responseProcessed; }); @@ -364,7 +364,7 @@ describe("SlidingSync", () => { "is_dm": true, }, }; - httpBackend.when("POST", syncUrl).check(function(req) { + httpBackend!.when("POST", syncUrl).check(function(req) { const body = req.data; logger.log("extra list", body); expect(body.lists).toBeTruthy(); @@ -403,13 +403,13 @@ describe("SlidingSync", () => { return state === SlidingSyncState.Complete; }); slidingSync.setList(1, extraListReq); - await httpBackend.flushAllExpected(); + await httpBackend!.flushAllExpected(); await responseProcessed; }); it("should be possible to get list DELETE/INSERTs", async () => { // move C (2) to A (0) - httpBackend.when("POST", syncUrl).respond(200, { + httpBackend!.when("POST", syncUrl).respond(200, { pos: "e", lists: [{ count: 500, @@ -440,12 +440,12 @@ describe("SlidingSync", () => { let responseProcessed = listenUntil(slidingSync, "SlidingSync.Lifecycle", (state) => { return state === SlidingSyncState.Complete; }); - await httpBackend.flushAllExpected(); + await httpBackend!.flushAllExpected(); await responseProcessed; await listPromise; // move C (0) back to A (2) - httpBackend.when("POST", syncUrl).respond(200, { + httpBackend!.when("POST", syncUrl).respond(200, { pos: "f", lists: [{ count: 500, @@ -476,13 +476,13 @@ describe("SlidingSync", () => { responseProcessed = listenUntil(slidingSync, "SlidingSync.Lifecycle", (state) => { return state === SlidingSyncState.Complete; }); - await httpBackend.flushAllExpected(); + await httpBackend!.flushAllExpected(); await responseProcessed; await listPromise; }); it("should ignore invalid list indexes", async () => { - httpBackend.when("POST", syncUrl).respond(200, { + httpBackend!.when("POST", syncUrl).respond(200, { pos: "e", lists: [{ count: 500, @@ -509,13 +509,13 @@ describe("SlidingSync", () => { const responseProcessed = listenUntil(slidingSync, "SlidingSync.Lifecycle", (state) => { return state === SlidingSyncState.Complete; }); - await httpBackend.flushAllExpected(); + await httpBackend!.flushAllExpected(); await responseProcessed; await listPromise; }); it("should be possible to update a list", async () => { - httpBackend.when("POST", syncUrl).respond(200, { + httpBackend!.when("POST", syncUrl).respond(200, { pos: "g", lists: [{ count: 42, @@ -555,7 +555,7 @@ describe("SlidingSync", () => { const responseProcessed = listenUntil(slidingSync, "SlidingSync.Lifecycle", (state) => { return state === SlidingSyncState.Complete; }); - await httpBackend.flushAllExpected(); + await httpBackend!.flushAllExpected(); await responseProcessed; await listPromise; }); @@ -567,7 +567,7 @@ describe("SlidingSync", () => { 1: roomC, }; expect(slidingSync.getListData(0).roomIndexToRoomId).toEqual(indexToRoomId); - httpBackend.when("POST", syncUrl).respond(200, { + httpBackend!.when("POST", syncUrl).respond(200, { pos: "f", // currently the list is [B,C] so we will insert D then immediately delete it lists: [{ @@ -598,7 +598,7 @@ describe("SlidingSync", () => { const responseProcessed = listenUntil(slidingSync, "SlidingSync.Lifecycle", (state) => { return state === SlidingSyncState.Complete; }); - await httpBackend.flushAllExpected(); + await httpBackend!.flushAllExpected(); await responseProcessed; await listPromise; }); @@ -608,7 +608,7 @@ describe("SlidingSync", () => { 0: roomB, 1: roomC, }); - httpBackend.when("POST", syncUrl).respond(200, { + httpBackend!.when("POST", syncUrl).respond(200, { pos: "g", lists: [{ count: 499, @@ -634,7 +634,7 @@ describe("SlidingSync", () => { const responseProcessed = listenUntil(slidingSync, "SlidingSync.Lifecycle", (state) => { return state === SlidingSyncState.Complete; }); - await httpBackend.flushAllExpected(); + await httpBackend!.flushAllExpected(); await responseProcessed; await listPromise; }); @@ -643,7 +643,7 @@ describe("SlidingSync", () => { expect(slidingSync.getListData(0).roomIndexToRoomId).toEqual({ 0: roomC, }); - httpBackend.when("POST", syncUrl).respond(200, { + httpBackend!.when("POST", syncUrl).respond(200, { pos: "h", lists: [{ count: 500, @@ -670,11 +670,11 @@ describe("SlidingSync", () => { let responseProcessed = listenUntil(slidingSync, "SlidingSync.Lifecycle", (state) => { return state === SlidingSyncState.Complete; }); - await httpBackend.flushAllExpected(); + await httpBackend!.flushAllExpected(); await responseProcessed; await listPromise; - httpBackend.when("POST", syncUrl).respond(200, { + httpBackend!.when("POST", syncUrl).respond(200, { pos: "h", lists: [{ count: 501, @@ -702,7 +702,7 @@ describe("SlidingSync", () => { responseProcessed = listenUntil(slidingSync, "SlidingSync.Lifecycle", (state) => { return state === SlidingSyncState.Complete; }); - await httpBackend.flushAllExpected(); + await httpBackend!.flushAllExpected(); await responseProcessed; await listPromise; slidingSync.stop(); @@ -725,11 +725,11 @@ describe("SlidingSync", () => { ], }; // add the subscription - slidingSync = new SlidingSync(proxyBaseUrl, [], roomSubInfo, client, 1); + slidingSync = new SlidingSync(proxyBaseUrl, [], roomSubInfo, client!, 1); // modification before SlidingSync.start() const subscribePromise = slidingSync.modifyRoomSubscriptions(new Set([roomId])); let txnId; - httpBackend.when("POST", syncUrl).check(function(req) { + httpBackend!.when("POST", syncUrl).check(function(req) { const body = req.data; logger.debug("got ", body); expect(body.room_subscriptions).toBeTruthy(); @@ -752,7 +752,7 @@ describe("SlidingSync", () => { }; }); slidingSync.start(); - await httpBackend.flushAllExpected(); + await httpBackend!.flushAllExpected(); await subscribePromise; }); it("should resolve setList during a connection", async () => { @@ -761,7 +761,7 @@ describe("SlidingSync", () => { }; const promise = slidingSync.setList(0, newList); let txnId; - httpBackend.when("POST", syncUrl).check(function(req) { + httpBackend!.when("POST", syncUrl).check(function(req) { const body = req.data; logger.debug("got ", body); expect(body.room_subscriptions).toBeFalsy(); @@ -776,14 +776,14 @@ describe("SlidingSync", () => { extensions: {}, }; }); - await httpBackend.flushAllExpected(); + await httpBackend!.flushAllExpected(); await promise; expect(txnId).toBeDefined(); }); it("should resolve setListRanges during a connection", async () => { const promise = slidingSync.setListRanges(0, [[20, 40]]); let txnId; - httpBackend.when("POST", syncUrl).check(function(req) { + httpBackend!.when("POST", syncUrl).check(function(req) { const body = req.data; logger.debug("got ", body); expect(body.room_subscriptions).toBeFalsy(); @@ -800,7 +800,7 @@ describe("SlidingSync", () => { extensions: {}, }; }); - await httpBackend.flushAllExpected(); + await httpBackend!.flushAllExpected(); await promise; expect(txnId).toBeDefined(); }); @@ -809,7 +809,7 @@ describe("SlidingSync", () => { timeline_limit: 99, }); let txnId; - httpBackend.when("POST", syncUrl).check(function(req) { + httpBackend!.when("POST", syncUrl).check(function(req) { const body = req.data; logger.debug("got ", body); expect(body.room_subscriptions).toBeTruthy(); @@ -825,22 +825,22 @@ describe("SlidingSync", () => { extensions: {}, }; }); - await httpBackend.flushAllExpected(); + await httpBackend!.flushAllExpected(); await promise; expect(txnId).toBeDefined(); }); it("should reject earlier pending promises if a later transaction is acknowledged", async () => { // i.e if we have [A,B,C] and see txn_id=C then A,B should be rejected. - const gotTxnIds = []; + const gotTxnIds: any[] = []; const pushTxn = function(req) { gotTxnIds.push(req.data.txn_id); }; const failPromise = slidingSync.setListRanges(0, [[20, 40]]); - httpBackend.when("POST", syncUrl).check(pushTxn).respond(200, { pos: "e" }); // missing txn_id - await httpBackend.flushAllExpected(); + httpBackend!.when("POST", syncUrl).check(pushTxn).respond(200, { pos: "e" }); // missing txn_id + await httpBackend!.flushAllExpected(); const failPromise2 = slidingSync.setListRanges(0, [[60, 70]]); - httpBackend.when("POST", syncUrl).check(pushTxn).respond(200, { pos: "f" }); // missing txn_id - await httpBackend.flushAllExpected(); + httpBackend!.when("POST", syncUrl).check(pushTxn).respond(200, { pos: "f" }); // missing txn_id + await httpBackend!.flushAllExpected(); // attach rejection handlers now else if we do it later Jest treats that as an unhandled rejection // which is a fail. @@ -849,7 +849,7 @@ describe("SlidingSync", () => { const okPromise = slidingSync.setListRanges(0, [[0, 20]]); let txnId; - httpBackend.when("POST", syncUrl).check((req) => { + httpBackend!.when("POST", syncUrl).check((req) => { txnId = req.data.txn_id; }).respond(200, () => { // include the txn_id, earlier requests should now be reject()ed. @@ -858,23 +858,23 @@ describe("SlidingSync", () => { txn_id: txnId, }; }); - await httpBackend.flushAllExpected(); + await httpBackend!.flushAllExpected(); await okPromise; expect(txnId).toBeDefined(); }); it("should not reject later pending promises if an earlier transaction is acknowledged", async () => { // i.e if we have [A,B,C] and see txn_id=B then C should not be rejected but A should. - const gotTxnIds = []; + const gotTxnIds: any[] = []; const pushTxn = function(req) { - gotTxnIds.push(req.data.txn_id); + gotTxnIds.push(req.data?.txn_id); }; const A = slidingSync.setListRanges(0, [[20, 40]]); - httpBackend.when("POST", syncUrl).check(pushTxn).respond(200, { pos: "A" }); - await httpBackend.flushAllExpected(); + httpBackend!.when("POST", syncUrl).check(pushTxn).respond(200, { pos: "A" }); + await httpBackend!.flushAllExpected(); const B = slidingSync.setListRanges(0, [[60, 70]]); - httpBackend.when("POST", syncUrl).check(pushTxn).respond(200, { pos: "B" }); // missing txn_id - await httpBackend.flushAllExpected(); + httpBackend!.when("POST", syncUrl).check(pushTxn).respond(200, { pos: "B" }); // missing txn_id + await httpBackend!.flushAllExpected(); // attach rejection handlers now else if we do it later Jest treats that as an unhandled rejection // which is a fail. @@ -885,14 +885,14 @@ describe("SlidingSync", () => { C.finally(() => { pendingC = false; }); - httpBackend.when("POST", syncUrl).check(pushTxn).respond(200, () => { + httpBackend!.when("POST", syncUrl).check(pushTxn).respond(200, () => { // include the txn_id for B, so C's promise is outstanding return { pos: "C", txn_id: gotTxnIds[1], }; }); - await httpBackend.flushAllExpected(); + await httpBackend!.flushAllExpected(); // A is rejected, see above expect(B).resolves.toEqual(gotTxnIds[1]); // B is resolved expect(pendingC).toBe(true); // C is pending still @@ -904,7 +904,7 @@ describe("SlidingSync", () => { pending = false; }); let txnId; - httpBackend.when("POST", syncUrl).check(function(req) { + httpBackend!.when("POST", syncUrl).check(function(req) { const body = req.data; logger.debug("got ", body); expect(body.room_subscriptions).toBeFalsy(); @@ -921,7 +921,7 @@ describe("SlidingSync", () => { extensions: {}, }; }); - await httpBackend.flushAllExpected(); + await httpBackend!.flushAllExpected(); expect(txnId).toBeDefined(); expect(pending).toBe(true); slidingSync.stop(); @@ -963,10 +963,10 @@ describe("SlidingSync", () => { }; it("should be able to register an extension", async () => { - slidingSync = new SlidingSync(proxyBaseUrl, [], {}, client, 1); + slidingSync = new SlidingSync(proxyBaseUrl, [], {}, client!, 1); slidingSync.registerExtension(extPre); - const callbackOrder = []; + const callbackOrder: string[] = []; let extensionOnResponseCalled = false; onPreExtensionRequest = () => { return extReq; @@ -977,7 +977,7 @@ describe("SlidingSync", () => { expect(resp).toEqual(extResp); }; - httpBackend.when("POST", syncUrl).check(function(req) { + httpBackend!.when("POST", syncUrl).check(function(req) { const body = req.data; logger.log("ext req", body); expect(body.extensions).toBeTruthy(); @@ -998,7 +998,7 @@ describe("SlidingSync", () => { } }); slidingSync.start(); - await httpBackend.flushAllExpected(); + await httpBackend!.flushAllExpected(); await p; expect(extensionOnResponseCalled).toBe(true); expect(callbackOrder).toEqual(["onPreExtensionResponse", "Lifecycle"]); @@ -1012,7 +1012,7 @@ describe("SlidingSync", () => { onPreExtensionResponse = (resp) => { responseCalled = true; }; - httpBackend.when("POST", syncUrl).check(function(req) { + httpBackend!.when("POST", syncUrl).check(function(req) { const body = req.data; logger.log("ext req nothing", body); expect(body.extensions).toBeTruthy(); @@ -1030,7 +1030,7 @@ describe("SlidingSync", () => { const p = listenUntil(slidingSync, "SlidingSync.Lifecycle", (state, resp, err) => { return state === SlidingSyncState.Complete; }); - await httpBackend.flushAllExpected(); + await httpBackend!.flushAllExpected(); await p; expect(responseCalled).toBe(false); }); @@ -1041,13 +1041,13 @@ describe("SlidingSync", () => { return extReq; }; let responseCalled = false; - const callbackOrder = []; + const callbackOrder: string[] = []; onPostExtensionResponse = (resp) => { expect(resp).toEqual(extResp); responseCalled = true; callbackOrder.push("onPostExtensionResponse"); }; - httpBackend.when("POST", syncUrl).check(function(req) { + httpBackend!.when("POST", syncUrl).check(function(req) { const body = req.data; logger.log("ext req after start", body); expect(body.extensions).toBeTruthy(); @@ -1071,7 +1071,7 @@ describe("SlidingSync", () => { return true; } }); - await httpBackend.flushAllExpected(); + await httpBackend!.flushAllExpected(); await p; expect(responseCalled).toBe(true); expect(callbackOrder).toEqual(["Lifecycle", "onPostExtensionResponse"]); @@ -1079,7 +1079,7 @@ describe("SlidingSync", () => { }); it("is not possible to register the same extension name twice", async () => { - slidingSync = new SlidingSync(proxyBaseUrl, [], {}, client, 1); + slidingSync = new SlidingSync(proxyBaseUrl, [], {}, client!, 1); slidingSync.registerExtension(extPre); expect(() => { slidingSync.registerExtension(extPre); }).toThrow(); }); @@ -1106,7 +1106,7 @@ function listenUntil( callback: (...args: any[]) => T, timeoutMs = 500, ): Promise { - const trace = new Error().stack.split(`\n`)[2]; + const trace = new Error().stack?.split(`\n`)[2]; return Promise.race([new Promise((resolve, reject) => { const wrapper = (...args) => { try {