1
0
mirror of https://github.com/matrix-org/matrix-js-sdk.git synced 2025-07-31 15:24:23 +03:00

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
This commit is contained in:
Kerry
2022-10-06 08:11:25 +02:00
committed by GitHub
parent 3a3dcfb254
commit a1b046b5d8
14 changed files with 1074 additions and 945 deletions

View File

@ -15,9 +15,11 @@ limitations under the License.
*/ */
// stub for browser-matrix browserify tests // stub for browser-matrix browserify tests
// @ts-ignore
global.XMLHttpRequest = jest.fn(); global.XMLHttpRequest = jest.fn();
afterAll(() => { afterAll(() => {
// clean up XMLHttpRequest mock // clean up XMLHttpRequest mock
// @ts-ignore
global.XMLHttpRequest = undefined; global.XMLHttpRequest = undefined;
}); });

View File

@ -122,7 +122,7 @@ describe("DeviceList management:", function() {
aliceTestClient.httpBackend.when( aliceTestClient.httpBackend.when(
'PUT', '/send/', 'PUT', '/send/',
).respond(200, { ).respond(200, {
event_id: '$event_id', event_id: '$event_id',
}); });
return Promise.all([ return Promise.all([
@ -290,8 +290,9 @@ describe("DeviceList management:", function() {
aliceTestClient.client.cryptoStore.getEndToEndDeviceData(null, (data) => { aliceTestClient.client.cryptoStore.getEndToEndDeviceData(null, (data) => {
const bobStat = data.trackingStatus['@bob:xyz']; const bobStat = data.trackingStatus['@bob:xyz'];
// Alice should be tracking bob's device list
expect(bobStat).toBeGreaterThan( 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) => { aliceTestClient.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( 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) => { aliceTestClient.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( 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( anotherTestClient.httpBackend.when('GET', '/sync').respond(
200, getSyncResponse([])); 200, getSyncResponse([]));
await anotherTestClient.flushSync(); 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) => { 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( expect(bobStat).toEqual(
0, "Alice should have marked bob's device list as untracked", 0,
); );
}); });
} finally { } finally {

View File

@ -31,8 +31,9 @@ import '../olm-loader';
import { logger } from '../../src/logger'; import { logger } from '../../src/logger';
import * as testUtils from "../test-utils/test-utils"; import * as testUtils from "../test-utils/test-utils";
import { TestClient } from "../TestClient"; 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 { ClientEvent, IContent, ISendEventResponse, MatrixClient, MatrixEvent } from "../../src/matrix";
import { DeviceInfo } from '../../src/crypto/deviceinfo';
let aliTestClient: TestClient; let aliTestClient: TestClient;
const roomId = "!room:localhost"; const roomId = "!room:localhost";
@ -71,12 +72,12 @@ function expectQueryKeys(querier: TestClient, uploader: TestClient): Promise<num
expect(uploader.deviceKeys).toBeTruthy(); expect(uploader.deviceKeys).toBeTruthy();
const uploaderKeys = {}; const uploaderKeys = {};
uploaderKeys[uploader.deviceId] = uploader.deviceKeys; uploaderKeys[uploader.deviceId!] = uploader.deviceKeys;
querier.httpBackend.when("POST", "/keys/query") querier.httpBackend.when("POST", "/keys/query")
.respond(200, function(_path, content) { .respond(200, function(_path, content: IUploadKeysRequest) {
expect(content.device_keys[uploader.userId]).toEqual([]); expect(content.device_keys![uploader.userId!]).toEqual([]);
const result = {}; const result = {};
result[uploader.userId] = uploaderKeys; result[uploader.userId!] = uploaderKeys;
return { device_keys: result }; return { device_keys: result };
}); });
return querier.httpBackend.flush("/keys/query", 1); return querier.httpBackend.flush("/keys/query", 1);
@ -93,10 +94,10 @@ async function expectAliClaimKeys(): Promise<void> {
const keys = await bobTestClient.awaitOneTimeKeyUpload(); const keys = await bobTestClient.awaitOneTimeKeyUpload();
aliTestClient.httpBackend.when( aliTestClient.httpBackend.when(
"POST", "/keys/claim", "POST", "/keys/claim",
).respond(200, function(_path, content) { ).respond(200, function(_path, content: IUploadKeysRequest) {
const claimType = content.one_time_keys[bobUserId][bobDeviceId]; const claimType = content.one_time_keys![bobUserId][bobDeviceId];
expect(claimType).toEqual("signed_curve25519"); expect(claimType).toEqual("signed_curve25519");
let keyId = null; let keyId = '';
for (keyId in keys) { for (keyId in keys) {
if (bobTestClient.oneTimeKeys.hasOwnProperty(keyId)) { if (bobTestClient.oneTimeKeys.hasOwnProperty(keyId)) {
if (keyId.indexOf(claimType + ":") === 0) { if (keyId.indexOf(claimType + ":") === 0) {
@ -135,10 +136,10 @@ async function aliDownloadsKeys(): Promise<void> {
await aliTestClient.client.crypto!.deviceList.saveIfDirty(); await aliTestClient.client.crypto!.deviceList.saveIfDirty();
// @ts-ignore - protected // @ts-ignore - protected
aliTestClient.client.cryptoStore.getEndToEndDeviceData(null, (data) => { 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].keys).toEqual(bobTestClient.deviceKeys.keys);
expect(devices[bobDeviceId].verified). expect(devices[bobDeviceId].verified).
toBe(0); // DeviceVerification.UNVERIFIED toBe(DeviceInfo.DeviceVerification.UNVERIFIED);
}); });
} }
@ -237,7 +238,7 @@ function sendMessage(client: MatrixClient): Promise<ISendEventResponse> {
async function expectSendMessageRequest(httpBackend: TestClient["httpBackend"]): Promise<IContent> { async function expectSendMessageRequest(httpBackend: TestClient["httpBackend"]): Promise<IContent> {
const path = "/send/m.room.encrypted/"; const path = "/send/m.room.encrypted/";
const prom = new Promise((resolve) => { const prom = new Promise<IContent>((resolve) => {
httpBackend.when("PUT", path).respond(200, function(_path, content) { httpBackend.when("PUT", path).respond(200, function(_path, content) {
resolve(content); resolve(content);
return { return {
@ -252,14 +253,14 @@ async function expectSendMessageRequest(httpBackend: TestClient["httpBackend"]):
} }
function aliRecvMessage(): Promise<void> { function aliRecvMessage(): Promise<void> {
const message = bobMessages.shift(); const message = bobMessages.shift()!;
return recvMessage( return recvMessage(
aliTestClient.httpBackend, aliTestClient.client, bobUserId, message, aliTestClient.httpBackend, aliTestClient.client, bobUserId, message,
); );
} }
function bobRecvMessage(): Promise<void> { function bobRecvMessage(): Promise<void> {
const message = aliMessages.shift(); const message = aliMessages.shift()!;
return recvMessage( return recvMessage(
bobTestClient.httpBackend, bobTestClient.client, aliUserId, message, bobTestClient.httpBackend, bobTestClient.client, aliUserId, message,
); );
@ -509,7 +510,7 @@ describe("MatrixClient crypto", () => {
await firstSync(aliTestClient); await firstSync(aliTestClient);
await aliEnablesEncryption(); await aliEnablesEncryption();
await aliSendsFirstMessage(); await aliSendsFirstMessage();
const message = aliMessages.shift(); const message = aliMessages.shift()!;
const syncData = { const syncData = {
next_batch: "x", next_batch: "x",
rooms: { rooms: {
@ -664,11 +665,10 @@ describe("MatrixClient crypto", () => {
]); ]);
logger.log(aliTestClient + ': started'); logger.log(aliTestClient + ': started');
httpBackend.when("POST", "/keys/upload") 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).toBeTruthy();
expect(content.one_time_keys).not.toEqual({}); expect(content.one_time_keys).not.toEqual({});
expect(Object.keys(content.one_time_keys).length).toBeGreaterThanOrEqual(1); expect(Object.keys(content.one_time_keys!).length).toBeGreaterThanOrEqual(1);
logger.log('received %i one-time keys', Object.keys(content.one_time_keys).length);
// cancel futher calls by telling the client // cancel futher calls by telling the client
// we have more than we need // we have more than we need
return { return {

View File

@ -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 * as utils from "../test-utils/test-utils";
import { TestClient } from "../TestClient"; import { TestClient } from "../TestClient";
describe("MatrixClient events", function() { describe("MatrixClient events", function() {
let client;
let httpBackend;
const selfUserId = "@alice:localhost"; const selfUserId = "@alice:localhost";
const selfAccessToken = "aseukfgwef"; 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() { beforeEach(function() {
const testClient = new TestClient(selfUserId, "DEVICE", selfAccessToken); [client!, httpBackend] = setupTests();
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" });
}); });
afterEach(function() { afterEach(function() {
httpBackend.verifyNoOutstandingExpectation(); httpBackend?.verifyNoOutstandingExpectation();
client.stopClient(); client?.stopClient();
return httpBackend.stop(); return httpBackend?.stop();
}); });
describe("emissions", function() { describe("emissions", function() {
@ -92,53 +126,49 @@ describe("MatrixClient events", function() {
}; };
it("should emit events from both the first and subsequent /sync calls", it("should emit events from both the first and subsequent /sync calls",
function() { function() {
httpBackend.when("GET", "/sync").respond(200, SYNC_DATA); httpBackend!.when("GET", "/sync").respond(200, SYNC_DATA);
httpBackend.when("GET", "/sync").respond(200, NEXT_SYNC_DATA); httpBackend!.when("GET", "/sync").respond(200, NEXT_SYNC_DATA);
let expectedEvents = []; let expectedEvents: Partial<IEvent>[] = [];
expectedEvents = expectedEvents.concat( expectedEvents = expectedEvents.concat(
SYNC_DATA.presence.events, SYNC_DATA.presence.events,
SYNC_DATA.rooms.join["!erufh:bar"].timeline.events, SYNC_DATA.rooms.join["!erufh:bar"].timeline.events,
SYNC_DATA.rooms.join["!erufh:bar"].state.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"].timeline.events,
NEXT_SYNC_DATA.rooms.join["!erufh:bar"].ephemeral.events, NEXT_SYNC_DATA.rooms.join["!erufh:bar"].ephemeral.events,
); );
client.on("event", function(event) { client!.on(ClientEvent.Event, function(event) {
let found = false; let found = false;
for (let i = 0; i < expectedEvents.length; i++) { for (let i = 0; i < expectedEvents.length; i++) {
if (expectedEvents[i].event_id === event.getId()) { if (expectedEvents[i].event_id === event.getId()) {
expectedEvents.splice(i, 1); expectedEvents.splice(i, 1);
found = true; found = true;
break; break;
}
} }
} expect(found).toBe(true);
expect(found).toBe( });
true, "Unexpected 'event' emitted: " + event.getType(),
);
});
client.startClient(); client!.startClient();
return Promise.all([ return Promise.all([
// wait for two SYNCING events // wait for two SYNCING events
utils.syncPromise(client).then(() => { utils.syncPromise(client!).then(() => {
return utils.syncPromise(client); return utils.syncPromise(client!);
}), }),
httpBackend.flushAllExpected(), httpBackend!.flushAllExpected(),
]).then(() => { ]).then(() => {
expect(expectedEvents.length).toEqual( expect(expectedEvents.length).toEqual(0);
0, "Failed to see all events from /sync calls", });
);
}); });
});
it("should emit User events", function(done) { it("should emit User events", function(done) {
httpBackend.when("GET", "/sync").respond(200, SYNC_DATA); httpBackend!.when("GET", "/sync").respond(200, SYNC_DATA);
httpBackend.when("GET", "/sync").respond(200, NEXT_SYNC_DATA); httpBackend!.when("GET", "/sync").respond(200, NEXT_SYNC_DATA);
let fired = false; let fired = false;
client.on("User.presence", function(event, user) { client!.on(UserEvent.Presence, function(event, user) {
fired = true; fired = true;
expect(user).toBeTruthy(); expect(user).toBeTruthy();
expect(event).toBeTruthy(); expect(event).toBeTruthy();
@ -146,58 +176,52 @@ describe("MatrixClient events", function() {
return; return;
} }
expect(event.event).toMatch(SYNC_DATA.presence.events[0]); expect(event.event).toEqual(SYNC_DATA.presence.events[0]);
expect(user.presence).toEqual( 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() { httpBackend!.flushAllExpected().then(function() {
expect(fired).toBe(true, "User.presence didn't fire."); expect(fired).toBe(true);
done(); done();
}); });
}); });
it("should emit Room events", function() { it("should emit Room events", function() {
httpBackend.when("GET", "/sync").respond(200, SYNC_DATA); httpBackend!.when("GET", "/sync").respond(200, SYNC_DATA);
httpBackend.when("GET", "/sync").respond(200, NEXT_SYNC_DATA); httpBackend!.when("GET", "/sync").respond(200, NEXT_SYNC_DATA);
let roomInvokeCount = 0; let roomInvokeCount = 0;
let roomNameInvokeCount = 0; let roomNameInvokeCount = 0;
let timelineFireCount = 0; let timelineFireCount = 0;
client.on("Room", function(room) { client!.on(ClientEvent.Room, function(room) {
roomInvokeCount++; roomInvokeCount++;
expect(room.roomId).toEqual("!erufh:bar"); expect(room.roomId).toEqual("!erufh:bar");
}); });
client.on("Room.timeline", function(event, room) { client!.on(RoomEvent.Timeline, function(event, room) {
timelineFireCount++; timelineFireCount++;
expect(room.roomId).toEqual("!erufh:bar"); expect(room.roomId).toEqual("!erufh:bar");
}); });
client.on("Room.name", function(room) { client!.on(RoomEvent.Name, function(room) {
roomNameInvokeCount++; roomNameInvokeCount++;
}); });
client.startClient(); client!.startClient();
return Promise.all([ return Promise.all([
httpBackend.flushAllExpected(), httpBackend!.flushAllExpected(),
utils.syncPromise(client, 2), utils.syncPromise(client!, 2),
]).then(function() { ]).then(function() {
expect(roomInvokeCount).toEqual( expect(roomInvokeCount).toEqual(1);
1, "Room fired wrong number of times.", expect(roomNameInvokeCount).toEqual(1);
); expect(timelineFireCount).toEqual(3);
expect(roomNameInvokeCount).toEqual(
1, "Room.name fired wrong number of times.",
);
expect(timelineFireCount).toEqual(
3, "Room.timeline fired the wrong number of times",
);
}); });
}); });
it("should emit RoomState events", function() { it("should emit RoomState events", function() {
httpBackend.when("GET", "/sync").respond(200, SYNC_DATA); httpBackend!.when("GET", "/sync").respond(200, SYNC_DATA);
httpBackend.when("GET", "/sync").respond(200, NEXT_SYNC_DATA); httpBackend!.when("GET", "/sync").respond(200, NEXT_SYNC_DATA);
const roomStateEventTypes = [ const roomStateEventTypes = [
"m.room.member", "m.room.create", "m.room.member", "m.room.create",
@ -205,126 +229,106 @@ describe("MatrixClient events", function() {
let eventsInvokeCount = 0; let eventsInvokeCount = 0;
let membersInvokeCount = 0; let membersInvokeCount = 0;
let newMemberInvokeCount = 0; let newMemberInvokeCount = 0;
client.on("RoomState.events", function(event, state) { client!.on(RoomStateEvent.Events, function(event, state) {
eventsInvokeCount++; eventsInvokeCount++;
const index = roomStateEventTypes.indexOf(event.getType()); const index = roomStateEventTypes.indexOf(event.getType());
expect(index).not.toEqual( expect(index).not.toEqual(-1);
-1, "Unexpected room state event type: " + event.getType(),
);
if (index >= 0) { if (index >= 0) {
roomStateEventTypes.splice(index, 1); roomStateEventTypes.splice(index, 1);
} }
}); });
client.on("RoomState.members", function(event, state, member) { client!.on(RoomStateEvent.Members, function(event, state, member) {
membersInvokeCount++; membersInvokeCount++;
expect(member.roomId).toEqual("!erufh:bar"); expect(member.roomId).toEqual("!erufh:bar");
expect(member.userId).toEqual("@foo:bar"); expect(member.userId).toEqual("@foo:bar");
expect(member.membership).toEqual("join"); expect(member.membership).toEqual("join");
}); });
client.on("RoomState.newMember", function(event, state, member) { client!.on(RoomStateEvent.NewMember, function(event, state, member) {
newMemberInvokeCount++; newMemberInvokeCount++;
expect(member.roomId).toEqual("!erufh:bar"); expect(member.roomId).toEqual("!erufh:bar");
expect(member.userId).toEqual("@foo:bar"); expect(member.userId).toEqual("@foo:bar");
expect(member.membership).toBeFalsy(); expect(member.membership).toBeFalsy();
}); });
client.startClient(); client!.startClient();
return Promise.all([ return Promise.all([
httpBackend.flushAllExpected(), httpBackend!.flushAllExpected(),
utils.syncPromise(client, 2), utils.syncPromise(client!, 2),
]).then(function() { ]).then(function() {
expect(membersInvokeCount).toEqual( expect(membersInvokeCount).toEqual(1);
1, "RoomState.members fired wrong number of times", expect(newMemberInvokeCount).toEqual(1);
); expect(eventsInvokeCount).toEqual(2);
expect(newMemberInvokeCount).toEqual(
1, "RoomState.newMember fired wrong number of times",
);
expect(eventsInvokeCount).toEqual(
2, "RoomState.events fired wrong number of times",
);
}); });
}); });
it("should emit RoomMember events", function() { it("should emit RoomMember events", function() {
httpBackend.when("GET", "/sync").respond(200, SYNC_DATA); httpBackend!.when("GET", "/sync").respond(200, SYNC_DATA);
httpBackend.when("GET", "/sync").respond(200, NEXT_SYNC_DATA); httpBackend!.when("GET", "/sync").respond(200, NEXT_SYNC_DATA);
let typingInvokeCount = 0; let typingInvokeCount = 0;
let powerLevelInvokeCount = 0; let powerLevelInvokeCount = 0;
let nameInvokeCount = 0; let nameInvokeCount = 0;
let membershipInvokeCount = 0; let membershipInvokeCount = 0;
client.on("RoomMember.name", function(event, member) { client!.on(RoomMemberEvent.Name, function(event, member) {
nameInvokeCount++; nameInvokeCount++;
}); });
client.on("RoomMember.typing", function(event, member) { client!.on(RoomMemberEvent.Typing, function(event, member) {
typingInvokeCount++; typingInvokeCount++;
expect(member.typing).toBe(true); expect(member.typing).toBe(true);
}); });
client.on("RoomMember.powerLevel", function(event, member) { client!.on(RoomMemberEvent.PowerLevel, function(event, member) {
powerLevelInvokeCount++; powerLevelInvokeCount++;
}); });
client.on("RoomMember.membership", function(event, member) { client!.on(RoomMemberEvent.Membership, function(event, member) {
membershipInvokeCount++; membershipInvokeCount++;
expect(member.membership).toEqual("join"); expect(member.membership).toEqual("join");
}); });
client.startClient(); client!.startClient();
return Promise.all([ return Promise.all([
httpBackend.flushAllExpected(), httpBackend!.flushAllExpected(),
utils.syncPromise(client, 2), utils.syncPromise(client!, 2),
]).then(function() { ]).then(function() {
expect(typingInvokeCount).toEqual( expect(typingInvokeCount).toEqual(1);
1, "RoomMember.typing fired wrong number of times", expect(powerLevelInvokeCount).toEqual(0);
); expect(nameInvokeCount).toEqual(0);
expect(powerLevelInvokeCount).toEqual( expect(membershipInvokeCount).toEqual(1);
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",
);
}); });
}); });
it("should emit Session.logged_out on M_UNKNOWN_TOKEN", function() { it("should emit Session.logged_out on M_UNKNOWN_TOKEN", function() {
const error = { errcode: 'M_UNKNOWN_TOKEN' }; const error = { errcode: 'M_UNKNOWN_TOKEN' };
httpBackend.when("GET", "/sync").respond(401, error); httpBackend!.when("GET", "/sync").respond(401, error);
let sessionLoggedOutCount = 0; let sessionLoggedOutCount = 0;
client.on("Session.logged_out", function(errObj) { client!.on(HttpApiEvent.SessionLoggedOut, function(errObj) {
sessionLoggedOutCount++; sessionLoggedOutCount++;
expect(errObj.data).toEqual(error); expect(errObj.data).toEqual(error);
}); });
client.startClient(); client!.startClient();
return httpBackend.flushAllExpected().then(function() { return httpBackend!.flushAllExpected().then(function() {
expect(sessionLoggedOutCount).toEqual( expect(sessionLoggedOutCount).toEqual(1);
1, "Session.logged_out fired wrong number of times",
);
}); });
}); });
it("should emit Session.logged_out on M_UNKNOWN_TOKEN (soft logout)", function() { it("should emit Session.logged_out on M_UNKNOWN_TOKEN (soft logout)", function() {
const error = { errcode: 'M_UNKNOWN_TOKEN', soft_logout: true }; 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; let sessionLoggedOutCount = 0;
client.on("Session.logged_out", function(errObj) { client!.on(HttpApiEvent.SessionLoggedOut, function(errObj) {
sessionLoggedOutCount++; sessionLoggedOutCount++;
expect(errObj.data).toEqual(error); expect(errObj.data).toEqual(error);
}); });
client.startClient(); client!.startClient();
return httpBackend.flushAllExpected().then(function() { return httpBackend!.flushAllExpected().then(function() {
expect(sessionLoggedOutCount).toEqual( expect(sessionLoggedOutCount).toEqual(1);
1, "Session.logged_out fired wrong number of times",
);
}); });
}); });
}); });

View File

@ -142,6 +142,7 @@ const THREAD_REPLY = utils.mkEvent({
event: false, 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; THREAD_ROOT.unsigned["m.relations"]["io.element.thread"].latest_event = THREAD_REPLY;
const SYNC_THREAD_ROOT = withoutRoomId(THREAD_ROOT); const SYNC_THREAD_ROOT = withoutRoomId(THREAD_ROOT);
@ -214,8 +215,8 @@ describe("getEventTimeline support", function() {
httpBackend = testClient.httpBackend; httpBackend = testClient.httpBackend;
return startClient(httpBackend, client).then(function() { return startClient(httpBackend, client).then(function() {
const room = client.getRoom(roomId); const room = client.getRoom(roomId)!;
const timelineSet = room.getTimelineSets()[0]; const timelineSet = room!.getTimelineSets()[0];
expect(client.getEventTimeline(timelineSet, "event")).rejects.toBeTruthy(); expect(client.getEventTimeline(timelineSet, "event")).rejects.toBeTruthy();
}); });
}); });
@ -232,8 +233,8 @@ describe("getEventTimeline support", function() {
httpBackend = testClient.httpBackend; httpBackend = testClient.httpBackend;
return startClient(httpBackend, client).then(() => { return startClient(httpBackend, client).then(() => {
const room = client.getRoom(roomId); const room = client.getRoom(roomId)!;
const timelineSet = room.getTimelineSets()[0]; const timelineSet = room!.getTimelineSets()[0];
expect(client.getEventTimeline(timelineSet, "event")).rejects.toBeFalsy(); 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() { it("scrollback should be able to scroll back to before a gappy /sync", function() {
// need a client with timelineSupport disabled to make this work // need a client with timelineSupport disabled to make this work
let room: Room; let room: Room | undefined;
return startClient(httpBackend, client).then(function() { return startClient(httpBackend, client).then(function() {
room = client.getRoom(roomId); room = client.getRoom(roomId)!;
httpBackend.when("GET", "/sync").respond(200, { httpBackend.when("GET", "/sync").respond(200, {
next_batch: "s_5_4", next_batch: "s_5_4",
@ -300,8 +301,8 @@ describe("getEventTimeline support", function() {
utils.syncPromise(client, 2), utils.syncPromise(client, 2),
]); ]);
}).then(function() { }).then(function() {
expect(room.timeline.length).toEqual(1); expect(room!.timeline.length).toEqual(1);
expect(room.timeline[0].event).toEqual(EVENTS[1]); expect(room!.timeline[0].event).toEqual(EVENTS[1]);
httpBackend.when("GET", "/messages").respond(200, { httpBackend.when("GET", "/messages").respond(200, {
chunk: [EVENTS[0]], chunk: [EVENTS[0]],
@ -309,12 +310,12 @@ describe("getEventTimeline support", function() {
end: "pagin_end", end: "pagin_end",
}); });
httpBackend.flush("/messages", 1); httpBackend.flush("/messages", 1);
return client.scrollback(room); return client.scrollback(room!);
}).then(function() { }).then(function() {
expect(room.timeline.length).toEqual(2); expect(room!.timeline.length).toEqual(2);
expect(room.timeline[0].event).toEqual(EVENTS[0]); expect(room!.timeline[0].event).toEqual(EVENTS[0]);
expect(room.timeline[1].event).toEqual(EVENTS[1]); expect(room!.timeline[1].event).toEqual(EVENTS[1]);
expect(room.oldState.paginationToken).toEqual("pagin_end"); expect(room!.oldState.paginationToken).toEqual("pagin_end");
}); });
}); });
}); });
@ -345,7 +346,7 @@ describe("MatrixClient event timelines", function() {
describe("getEventTimeline", function() { describe("getEventTimeline", function() {
it("should create a new timeline for new events", 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 timelineSet = room.getTimelineSets()[0];
httpBackend.when("GET", "/rooms/!foo%3Abar/context/event1%3Abar") httpBackend.when("GET", "/rooms/!foo%3Abar/context/event1%3Abar")
.respond(200, function() { .respond(200, function() {
@ -364,14 +365,14 @@ describe("MatrixClient event timelines", function() {
return Promise.all([ return Promise.all([
client.getEventTimeline(timelineSet, "event1:bar").then(function(tl) { 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++) { for (let i = 0; i < 4; i++) {
expect(tl.getEvents()[i].event).toEqual(EVENTS[i]); expect(tl!.getEvents()[i].event).toEqual(EVENTS[i]);
expect(tl.getEvents()[i].sender.name).toEqual(userName); expect(tl!.getEvents()[i]?.sender.name).toEqual(userName);
} }
expect(tl.getPaginationToken(EventTimeline.BACKWARDS)) expect(tl!.getPaginationToken(EventTimeline.BACKWARDS))
.toEqual("start_token"); .toEqual("start_token");
expect(tl.getPaginationToken(EventTimeline.FORWARDS)) expect(tl!.getPaginationToken(EventTimeline.FORWARDS))
.toEqual("end_token"); .toEqual("end_token");
}), }),
httpBackend.flushAllExpected(), httpBackend.flushAllExpected(),
@ -379,7 +380,7 @@ describe("MatrixClient event timelines", function() {
}); });
it("should return existing timeline for known events", 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]; const timelineSet = room.getTimelineSets()[0];
httpBackend.when("GET", "/sync").respond(200, { httpBackend.when("GET", "/sync").respond(200, {
next_batch: "s_5_4", next_batch: "s_5_4",
@ -401,12 +402,12 @@ describe("MatrixClient event timelines", function() {
httpBackend.flush("/sync"), httpBackend.flush("/sync"),
utils.syncPromise(client), utils.syncPromise(client),
]).then(function() { ]).then(function() {
return client.getEventTimeline(timelineSet, EVENTS[0].event_id); return client.getEventTimeline(timelineSet, EVENTS[0].event_id!);
}).then(function(tl) { }).then(function(tl) {
expect(tl.getEvents().length).toEqual(2); expect(tl!.getEvents().length).toEqual(2);
expect(tl.getEvents()[1].event).toEqual(EVENTS[0]); expect(tl!.getEvents()[1].event).toEqual(EVENTS[0]);
expect(tl.getEvents()[1].sender.name).toEqual(userName); expect(tl!.getEvents()[1]?.sender.name).toEqual(userName);
expect(tl.getPaginationToken(EventTimeline.BACKWARDS)) expect(tl!.getPaginationToken(EventTimeline.BACKWARDS))
.toEqual("f_1_1"); .toEqual("f_1_1");
// expect(tl.getPaginationToken(EventTimeline.FORWARDS)) // expect(tl.getPaginationToken(EventTimeline.FORWARDS))
// .toEqual("s_5_4"); // .toEqual("s_5_4");
@ -414,7 +415,7 @@ describe("MatrixClient event timelines", function() {
}); });
it("should update timelines where they overlap a previous /sync", 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]; const timelineSet = room.getTimelineSets()[0];
httpBackend.when("GET", "/sync").respond(200, { httpBackend.when("GET", "/sync").respond(200, {
next_batch: "s_5_4", next_batch: "s_5_4",
@ -433,7 +434,7 @@ describe("MatrixClient event timelines", function() {
}); });
httpBackend.when("GET", "/rooms/!foo%3Abar/context/" + httpBackend.when("GET", "/rooms/!foo%3Abar/context/" +
encodeURIComponent(EVENTS[2].event_id)) encodeURIComponent(EVENTS[2].event_id!))
.respond(200, function() { .respond(200, function() {
return { return {
start: "start_token", start: "start_token",
@ -447,13 +448,13 @@ describe("MatrixClient event timelines", function() {
const prom = new Promise((resolve, reject) => { const prom = new Promise((resolve, reject) => {
client.on(ClientEvent.Sync, function() { client.on(ClientEvent.Sync, function() {
client.getEventTimeline(timelineSet, EVENTS[2].event_id, client.getEventTimeline(timelineSet, EVENTS[2].event_id!,
).then(function(tl) { ).then(function(tl) {
expect(tl.getEvents().length).toEqual(4); expect(tl!.getEvents().length).toEqual(4);
expect(tl.getEvents()[0].event).toEqual(EVENTS[1]); expect(tl!.getEvents()[0].event).toEqual(EVENTS[1]);
expect(tl.getEvents()[1].event).toEqual(EVENTS[2]); expect(tl!.getEvents()[1].event).toEqual(EVENTS[2]);
expect(tl.getEvents()[3].event).toEqual(EVENTS[3]); expect(tl!.getEvents()[3].event).toEqual(EVENTS[3]);
expect(tl.getPaginationToken(EventTimeline.BACKWARDS)) expect(tl!.getPaginationToken(EventTimeline.BACKWARDS))
.toEqual("start_token"); .toEqual("start_token");
// expect(tl.getPaginationToken(EventTimeline.FORWARDS)) // expect(tl.getPaginationToken(EventTimeline.FORWARDS))
// .toEqual("s_5_4"); // .toEqual("s_5_4");
@ -468,13 +469,13 @@ describe("MatrixClient event timelines", function() {
}); });
it("should join timelines where they overlap a previous /context", 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]; const timelineSet = room.getTimelineSets()[0];
// we fetch event 0, then 2, then 3, and finally 1. 1 is returned // we fetch event 0, then 2, then 3, and finally 1. 1 is returned
// with context which joins them all up. // with context which joins them all up.
httpBackend.when("GET", "/rooms/!foo%3Abar/context/" + httpBackend.when("GET", "/rooms/!foo%3Abar/context/" +
encodeURIComponent(EVENTS[0].event_id)) encodeURIComponent(EVENTS[0].event_id!))
.respond(200, function() { .respond(200, function() {
return { return {
start: "start_token0", start: "start_token0",
@ -487,7 +488,7 @@ describe("MatrixClient event timelines", function() {
}); });
httpBackend.when("GET", "/rooms/!foo%3Abar/context/" + httpBackend.when("GET", "/rooms/!foo%3Abar/context/" +
encodeURIComponent(EVENTS[2].event_id)) encodeURIComponent(EVENTS[2].event_id!))
.respond(200, function() { .respond(200, function() {
return { return {
start: "start_token2", start: "start_token2",
@ -500,7 +501,7 @@ describe("MatrixClient event timelines", function() {
}); });
httpBackend.when("GET", "/rooms/!foo%3Abar/context/" + httpBackend.when("GET", "/rooms/!foo%3Abar/context/" +
encodeURIComponent(EVENTS[3].event_id)) encodeURIComponent(EVENTS[3].event_id!))
.respond(200, function() { .respond(200, function() {
return { return {
start: "start_token3", start: "start_token3",
@ -513,7 +514,7 @@ describe("MatrixClient event timelines", function() {
}); });
httpBackend.when("GET", "/rooms/!foo%3Abar/context/" + httpBackend.when("GET", "/rooms/!foo%3Abar/context/" +
encodeURIComponent(EVENTS[1].event_id)) encodeURIComponent(EVENTS[1].event_id!))
.respond(200, function() { .respond(200, function() {
return { return {
start: "start_token4", start: "start_token4",
@ -528,26 +529,26 @@ describe("MatrixClient event timelines", function() {
let tl0; let tl0;
let tl3; let tl3;
return Promise.all([ return Promise.all([
client.getEventTimeline(timelineSet, EVENTS[0].event_id, client.getEventTimeline(timelineSet, EVENTS[0].event_id!,
).then(function(tl) { ).then(function(tl) {
expect(tl.getEvents().length).toEqual(1); expect(tl!.getEvents().length).toEqual(1);
tl0 = tl; tl0 = tl;
return client.getEventTimeline(timelineSet, EVENTS[2].event_id); return client.getEventTimeline(timelineSet, EVENTS[2].event_id!);
}).then(function(tl) { }).then(function(tl) {
expect(tl.getEvents().length).toEqual(1); expect(tl!.getEvents().length).toEqual(1);
return client.getEventTimeline(timelineSet, EVENTS[3].event_id); return client.getEventTimeline(timelineSet, EVENTS[3].event_id!);
}).then(function(tl) { }).then(function(tl) {
expect(tl.getEvents().length).toEqual(1); expect(tl!.getEvents().length).toEqual(1);
tl3 = tl; tl3 = tl;
return client.getEventTimeline(timelineSet, EVENTS[1].event_id); return client.getEventTimeline(timelineSet, EVENTS[1].event_id!);
}).then(function(tl) { }).then(function(tl) {
// we expect it to get merged in with event 2 // we expect it to get merged in with event 2
expect(tl.getEvents().length).toEqual(2); expect(tl!.getEvents().length).toEqual(2);
expect(tl.getEvents()[0].event).toEqual(EVENTS[1]); expect(tl!.getEvents()[0].event).toEqual(EVENTS[1]);
expect(tl.getEvents()[1].event).toEqual(EVENTS[2]); expect(tl!.getEvents()[1].event).toEqual(EVENTS[2]);
expect(tl.getNeighbouringTimeline(EventTimeline.BACKWARDS)) expect(tl!.getNeighbouringTimeline(EventTimeline.BACKWARDS))
.toBe(tl0); .toBe(tl0);
expect(tl.getNeighbouringTimeline(EventTimeline.FORWARDS)) expect(tl!.getNeighbouringTimeline(EventTimeline.FORWARDS))
.toBe(tl3); .toBe(tl3);
expect(tl0.getPaginationToken(EventTimeline.BACKWARDS)) expect(tl0.getPaginationToken(EventTimeline.BACKWARDS))
.toEqual("start_token0"); .toEqual("start_token0");
@ -563,7 +564,7 @@ describe("MatrixClient event timelines", function() {
}); });
it("should fail gracefully if there is no event field", 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]; const timelineSet = room.getTimelineSets()[0];
// we fetch event 0, then 2, then 3, and finally 1. 1 is returned // we fetch event 0, then 2, then 3, and finally 1. 1 is returned
// with context which joins them all up. // with context which joins them all up.
@ -595,11 +596,11 @@ describe("MatrixClient event timelines", function() {
client.clientOpts.experimentalThreadSupport = true; client.clientOpts.experimentalThreadSupport = true;
Thread.setServerSideSupport(FeatureSupport.Experimental); Thread.setServerSideSupport(FeatureSupport.Experimental);
client.stopClient(); // we don't need the client to be syncing at this time 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 thread = room.createThread(THREAD_ROOT.event_id, undefined, [], false); const thread = room.createThread(THREAD_ROOT.event_id!, undefined, [], false);
const timelineSet = thread.timelineSet; 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() { .respond(200, function() {
return { return {
start: "start_token0", 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() { .respond(200, function() {
return THREAD_ROOT; return THREAD_ROOT;
}); });
httpBackend.when("GET", "/rooms/!foo%3Abar/relations/" + httpBackend.when("GET", "/rooms/!foo%3Abar/relations/" +
encodeURIComponent(THREAD_ROOT.event_id) + "/" + encodeURIComponent(THREAD_ROOT.event_id!) + "/" +
encodeURIComponent(THREAD_RELATION_TYPE.name) + "?limit=20") encodeURIComponent(THREAD_RELATION_TYPE.name) + "?limit=20")
.respond(200, function() { .respond(200, function() {
return { 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(); await httpBackend.flushAllExpected();
const timeline = await timelinePromise; const timeline = await timelinePromise;
expect(timeline.getEvents().find(e => e.getId() === THREAD_ROOT.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(); 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 () => { 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; client.clientOpts.experimentalThreadSupport = true;
Thread.setServerSideSupport(FeatureSupport.Experimental); Thread.setServerSideSupport(FeatureSupport.Experimental);
client.stopClient(); // we don't need the client to be syncing at this time 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 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 = room.getTimelineSets()[0]; 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() { .respond(200, function() {
return { return {
start: "start_token0", start: "start_token0",
@ -659,13 +660,13 @@ describe("MatrixClient event timelines", function() {
}); });
const [timeline] = await Promise.all([ const [timeline] = await Promise.all([
client.getEventTimeline(timelineSet, THREAD_ROOT.event_id), client.getEventTimeline(timelineSet, THREAD_ROOT.event_id!),
httpBackend.flushAllExpected(), httpBackend.flushAllExpected(),
]); ]);
expect(timeline).not.toBe(thread.liveTimeline); expect(timeline!).not.toBe(thread.liveTimeline);
expect(timelineSet.getTimelines()).toContain(timeline); 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", () => { 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; client.clientOpts.experimentalThreadSupport = true;
Thread.setServerSideSupport(FeatureSupport.Experimental); Thread.setServerSideSupport(FeatureSupport.Experimental);
client.stopClient(); // we don't need the client to be syncing at this time 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 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; 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() { .respond(200, function() {
return { return {
start: "start_token0", start: "start_token0",
@ -691,7 +692,7 @@ describe("MatrixClient event timelines", function() {
}); });
return Promise.all([ 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(), httpBackend.flushAllExpected(),
]); ]);
}); });
@ -701,10 +702,10 @@ describe("MatrixClient event timelines", function() {
client.clientOpts.experimentalThreadSupport = true; client.clientOpts.experimentalThreadSupport = true;
Thread.setServerSideSupport(FeatureSupport.Experimental); Thread.setServerSideSupport(FeatureSupport.Experimental);
client.stopClient(); // we don't need the client to be syncing at this time 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 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() { .respond(200, function() {
return { return {
start: "start_token0", start: "start_token0",
@ -717,7 +718,7 @@ describe("MatrixClient event timelines", function() {
}); });
return Promise.all([ 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(), httpBackend.flushAllExpected(),
]); ]);
}); });
@ -726,10 +727,10 @@ describe("MatrixClient event timelines", function() {
// @ts-ignore // @ts-ignore
client.clientOpts.lazyLoadMembers = true; client.clientOpts.lazyLoadMembers = true;
client.stopClient(); // we don't need the client to be syncing at this time 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 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() { req.respond(200, function() {
return { return {
start: "start_token0", start: "start_token0",
@ -741,11 +742,11 @@ describe("MatrixClient event timelines", function() {
}; };
}); });
req.check((request) => { 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([ await Promise.all([
client.getEventTimeline(timelineSet, EVENTS[0].event_id), client.getEventTimeline(timelineSet, EVENTS[0].event_id!),
httpBackend.flushAllExpected(), httpBackend.flushAllExpected(),
]); ]);
}); });
@ -810,7 +811,7 @@ describe("MatrixClient event timelines", function() {
}); });
it("should create a new timeline for new events", 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 timelineSet = room.getTimelineSets()[0];
const latestMessageId = 'event1:bar'; const latestMessageId = 'event1:bar';
@ -845,14 +846,14 @@ describe("MatrixClient event timelines", function() {
// for `getEventTimeline` and make sure it's called with the // for `getEventTimeline` and make sure it's called with the
// correct parameters. This doesn't feel too bad to make sure // correct parameters. This doesn't feel too bad to make sure
// `getLatestTimeline` is doing the right thing though. // `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++) { for (let i = 0; i < 4; i++) {
expect(tl.getEvents()[i].event).toEqual(EVENTS[i]); expect(tl!.getEvents()[i].event).toEqual(EVENTS[i]);
expect(tl.getEvents()[i].sender.name).toEqual(userName); expect(tl!.getEvents()[i]?.sender.name).toEqual(userName);
} }
expect(tl.getPaginationToken(EventTimeline.BACKWARDS)) expect(tl!.getPaginationToken(EventTimeline.BACKWARDS))
.toEqual("start_token"); .toEqual("start_token");
expect(tl.getPaginationToken(EventTimeline.FORWARDS)) expect(tl!.getPaginationToken(EventTimeline.FORWARDS))
.toEqual("end_token"); .toEqual("end_token");
}), }),
httpBackend.flushAllExpected(), httpBackend.flushAllExpected(),
@ -860,7 +861,7 @@ describe("MatrixClient event timelines", function() {
}); });
it("should throw error when /messages does not return a message", () => { 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]; const timelineSet = room.getTimelineSets()[0];
httpBackend.when("GET", "/rooms/!foo%3Abar/messages") httpBackend.when("GET", "/rooms/!foo%3Abar/messages")
@ -881,11 +882,11 @@ describe("MatrixClient event timelines", function() {
describe("paginateEventTimeline", function() { describe("paginateEventTimeline", function() {
it("should allow you to paginate backwards", function() { it("should allow you to paginate backwards", function() {
const room = client.getRoom(roomId); const room = client.getRoom(roomId)!;
const timelineSet = room.getTimelineSets()[0]; const timelineSet = room.getTimelineSets()[0];
httpBackend.when("GET", "/rooms/!foo%3Abar/context/" + httpBackend.when("GET", "/rooms/!foo%3Abar/context/" +
encodeURIComponent(EVENTS[0].event_id)) encodeURIComponent(EVENTS[0].event_id!))
.respond(200, function() { .respond(200, function() {
return { return {
start: "start_token0", start: "start_token0",
@ -899,7 +900,7 @@ describe("MatrixClient event timelines", function() {
httpBackend.when("GET", "/rooms/!foo%3Abar/messages") httpBackend.when("GET", "/rooms/!foo%3Abar/messages")
.check(function(req) { .check(function(req) {
const params = req.queryParams; const params = req.queryParams!;
expect(params.dir).toEqual("b"); expect(params.dir).toEqual("b");
expect(params.from).toEqual("start_token0"); expect(params.from).toEqual("start_token0");
expect(params.limit).toEqual("30"); expect(params.limit).toEqual("30");
@ -912,19 +913,19 @@ describe("MatrixClient event timelines", function() {
let tl; let tl;
return Promise.all([ return Promise.all([
client.getEventTimeline(timelineSet, EVENTS[0].event_id, client.getEventTimeline(timelineSet, EVENTS[0].event_id!,
).then(function(tl0) { ).then(function(tl0) {
tl = tl0; tl = tl0;
return client.paginateEventTimeline(tl, { backwards: true }); return client.paginateEventTimeline(tl, { backwards: true });
}).then(function(success) { }).then(function(success) {
expect(success).toBeTruthy(); expect(success).toBeTruthy();
expect(tl.getEvents().length).toEqual(3); expect(tl!.getEvents().length).toEqual(3);
expect(tl.getEvents()[0].event).toEqual(EVENTS[2]); expect(tl!.getEvents()[0].event).toEqual(EVENTS[2]);
expect(tl.getEvents()[1].event).toEqual(EVENTS[1]); expect(tl!.getEvents()[1].event).toEqual(EVENTS[1]);
expect(tl.getEvents()[2].event).toEqual(EVENTS[0]); expect(tl!.getEvents()[2].event).toEqual(EVENTS[0]);
expect(tl.getPaginationToken(EventTimeline.BACKWARDS)) expect(tl!.getPaginationToken(EventTimeline.BACKWARDS))
.toEqual("start_token1"); .toEqual("start_token1");
expect(tl.getPaginationToken(EventTimeline.FORWARDS)) expect(tl!.getPaginationToken(EventTimeline.FORWARDS))
.toEqual("end_token0"); .toEqual("end_token0");
}), }),
httpBackend.flushAllExpected(), httpBackend.flushAllExpected(),
@ -932,11 +933,11 @@ describe("MatrixClient event timelines", function() {
}); });
it("should stop paginating when it encounters no `end` token", () => { 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]; const timelineSet = room.getTimelineSets()[0];
httpBackend.when("GET", "/rooms/!foo%3Abar/context/" + httpBackend.when("GET", "/rooms/!foo%3Abar/context/" +
encodeURIComponent(EVENTS[0].event_id)) encodeURIComponent(EVENTS[0].event_id!))
.respond(200, () => ({ .respond(200, () => ({
start: "start_token0", start: "start_token0",
events_before: [], events_before: [],
@ -948,7 +949,7 @@ describe("MatrixClient event timelines", function() {
httpBackend.when("GET", "/rooms/!foo%3Abar/messages") httpBackend.when("GET", "/rooms/!foo%3Abar/messages")
.check(function(req) { .check(function(req) {
const params = req.queryParams; const params = req.queryParams!;
expect(params.dir).toEqual("b"); expect(params.dir).toEqual("b");
expect(params.from).toEqual("start_token0"); expect(params.from).toEqual("start_token0");
expect(params.limit).toEqual("30"); expect(params.limit).toEqual("30");
@ -959,23 +960,23 @@ describe("MatrixClient event timelines", function() {
return Promise.all([ return Promise.all([
(async () => { (async () => {
const tl = await client.getEventTimeline(timelineSet, EVENTS[0].event_id); const tl = await client.getEventTimeline(timelineSet, EVENTS[0].event_id!);
const success = await client.paginateEventTimeline(tl, { backwards: true }); const success = await client.paginateEventTimeline(tl!, { backwards: true });
expect(success).toBeFalsy(); expect(success).toBeFalsy();
expect(tl.getEvents().length).toEqual(1); expect(tl!.getEvents().length).toEqual(1);
expect(tl.getPaginationToken(EventTimeline.BACKWARDS)).toEqual(null); expect(tl!.getPaginationToken(EventTimeline.BACKWARDS)).toEqual(null);
expect(tl.getPaginationToken(EventTimeline.FORWARDS)).toEqual("end_token0"); expect(tl!.getPaginationToken(EventTimeline.FORWARDS)).toEqual("end_token0");
})(), })(),
httpBackend.flushAllExpected(), httpBackend.flushAllExpected(),
]); ]);
}); });
it("should allow you to paginate forwards", function() { it("should allow you to paginate forwards", function() {
const room = client.getRoom(roomId); const room = client.getRoom(roomId)!;
const timelineSet = room.getTimelineSets()[0]; const timelineSet = room.getTimelineSets()[0];
httpBackend.when("GET", "/rooms/!foo%3Abar/context/" + httpBackend.when("GET", "/rooms/!foo%3Abar/context/" +
encodeURIComponent(EVENTS[0].event_id)) encodeURIComponent(EVENTS[0].event_id!))
.respond(200, function() { .respond(200, function() {
return { return {
start: "start_token0", start: "start_token0",
@ -989,7 +990,7 @@ describe("MatrixClient event timelines", function() {
httpBackend.when("GET", "/rooms/!foo%3Abar/messages") httpBackend.when("GET", "/rooms/!foo%3Abar/messages")
.check(function(req) { .check(function(req) {
const params = req.queryParams; const params = req.queryParams!;
expect(params.dir).toEqual("f"); expect(params.dir).toEqual("f");
expect(params.from).toEqual("end_token0"); expect(params.from).toEqual("end_token0");
expect(params.limit).toEqual("20"); expect(params.limit).toEqual("20");
@ -1002,20 +1003,20 @@ describe("MatrixClient event timelines", function() {
let tl; let tl;
return Promise.all([ return Promise.all([
client.getEventTimeline(timelineSet, EVENTS[0].event_id, client.getEventTimeline(timelineSet, EVENTS[0].event_id!,
).then(function(tl0) { ).then(function(tl0) {
tl = tl0; tl = tl0;
return client.paginateEventTimeline( return client.paginateEventTimeline(
tl, { backwards: false, limit: 20 }); tl, { backwards: false, limit: 20 });
}).then(function(success) { }).then(function(success) {
expect(success).toBeTruthy(); expect(success).toBeTruthy();
expect(tl.getEvents().length).toEqual(3); expect(tl!.getEvents().length).toEqual(3);
expect(tl.getEvents()[0].event).toEqual(EVENTS[0]); expect(tl!.getEvents()[0].event).toEqual(EVENTS[0]);
expect(tl.getEvents()[1].event).toEqual(EVENTS[1]); expect(tl!.getEvents()[1].event).toEqual(EVENTS[1]);
expect(tl.getEvents()[2].event).toEqual(EVENTS[2]); expect(tl!.getEvents()[2].event).toEqual(EVENTS[2]);
expect(tl.getPaginationToken(EventTimeline.BACKWARDS)) expect(tl!.getPaginationToken(EventTimeline.BACKWARDS))
.toEqual("start_token0"); .toEqual("start_token0");
expect(tl.getPaginationToken(EventTimeline.FORWARDS)) expect(tl!.getPaginationToken(EventTimeline.FORWARDS))
.toEqual("end_token1"); .toEqual("end_token1");
}), }),
httpBackend.flushAllExpected(), httpBackend.flushAllExpected(),
@ -1285,17 +1286,17 @@ describe("MatrixClient event timelines", function() {
}); });
it("should work when /send returns before /sync", function() { it("should work when /send returns before /sync", function() {
const room = client.getRoom(roomId); const room = client.getRoom(roomId)!;
const timelineSet = room.getTimelineSets()[0]; const timelineSet = room.getTimelineSets()[0]!;
return Promise.all([ return Promise.all([
client.sendTextMessage(roomId, "a body", TXN_ID).then(function(res) { client.sendTextMessage(roomId, "a body", TXN_ID).then(function(res) {
expect(res.event_id).toEqual(event.event_id); expect(res.event_id).toEqual(event.event_id!);
return client.getEventTimeline(timelineSet, event.event_id); return client.getEventTimeline(timelineSet, event.event_id!);
}).then(function(tl) { }).then(function(tl) {
// 2 because the initial sync contained an event // 2 because the initial sync contained an event
expect(tl.getEvents().length).toEqual(2); expect(tl!.getEvents().length).toEqual(2);
expect(tl.getEvents()[1].getContent().body).toEqual("a body"); expect(tl!.getEvents()[1].getContent().body).toEqual("a body");
// now let the sync complete, and check it again // now let the sync complete, and check it again
return Promise.all([ return Promise.all([
@ -1303,10 +1304,10 @@ describe("MatrixClient event timelines", function() {
utils.syncPromise(client), utils.syncPromise(client),
]); ]);
}).then(function() { }).then(function() {
return client.getEventTimeline(timelineSet, event.event_id); return client.getEventTimeline(timelineSet, event.event_id!);
}).then(function(tl) { }).then(function(tl) {
expect(tl.getEvents().length).toEqual(2); expect(tl!.getEvents().length).toEqual(2);
expect(tl.getEvents()[1].event).toEqual(event); expect(tl!.getEvents()[1].event).toEqual(event);
}), }),
httpBackend.flush("/send/m.room.message/" + TXN_ID, 1), 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() { it("should work when /send returns after /sync", function() {
const room = client.getRoom(roomId); const room = client.getRoom(roomId)!;
const timelineSet = room.getTimelineSets()[0]; const timelineSet = room.getTimelineSets()[0];
return Promise.all([ 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. // - but note that it won't complete until after the /sync does, below.
client.sendTextMessage(roomId, "a body", TXN_ID).then(function(res) { client.sendTextMessage(roomId, "a body", TXN_ID).then(function(res) {
logger.log("sendTextMessage completed"); logger.log("sendTextMessage completed");
expect(res.event_id).toEqual(event.event_id); expect(res.event_id).toEqual(event.event_id!);
return client.getEventTimeline(timelineSet, event.event_id); return client.getEventTimeline(timelineSet, event.event_id!);
}).then(function(tl) { }).then(function(tl) {
logger.log("getEventTimeline completed (2)"); logger.log("getEventTimeline completed (2)");
expect(tl.getEvents().length).toEqual(2); expect(tl!.getEvents().length).toEqual(2);
expect(tl.getEvents()[1].getContent().body).toEqual("a body"); expect(tl!.getEvents()[1].getContent().body).toEqual("a body");
}), }),
Promise.all([ Promise.all([
httpBackend.flush("/sync", 1), httpBackend.flush("/sync", 1),
utils.syncPromise(client), utils.syncPromise(client),
]).then(function() { ]).then(function() {
return client.getEventTimeline(timelineSet, event.event_id); return client.getEventTimeline(timelineSet, event.event_id!);
}).then(function(tl) { }).then(function(tl) {
logger.log("getEventTimeline completed (1)"); logger.log("getEventTimeline completed (1)");
expect(tl.getEvents().length).toEqual(2); expect(tl!.getEvents().length).toEqual(2);
expect(tl.getEvents()[1].event).toEqual(event); expect(tl!.getEvents()[1].event).toEqual(event);
// now let the send complete. // now let the send complete.
return httpBackend.flush("/send/m.room.message/" + TXN_ID, 1); return httpBackend.flush("/send/m.room.message/" + TXN_ID, 1);
@ -1382,10 +1383,10 @@ describe("MatrixClient event timelines", function() {
httpBackend.flushAllExpected(), httpBackend.flushAllExpected(),
utils.syncPromise(client), utils.syncPromise(client),
]).then(function() { ]).then(function() {
const room = client.getRoom(roomId); const room = client.getRoom(roomId)!;
const tl = room.getLiveTimeline(); const tl = room.getLiveTimeline()!;
expect(tl.getEvents().length).toEqual(3); expect(tl!.getEvents().length).toEqual(3);
expect(tl.getEvents()[1].isRedacted()).toBe(true); expect(tl!.getEvents()[1].isRedacted()).toBe(true);
const sync2 = { const sync2 = {
next_batch: "batch2", next_batch: "batch2",
@ -1411,8 +1412,8 @@ describe("MatrixClient event timelines", function() {
utils.syncPromise(client), utils.syncPromise(client),
]); ]);
}).then(function() { }).then(function() {
const room = client.getRoom(roomId); const room = client.getRoom(roomId)!;
const tl = room.getLiveTimeline(); const tl = room.getLiveTimeline()!;
expect(tl.getEvents().length).toEqual(1); expect(tl.getEvents().length).toEqual(1);
}); });
}); });
@ -1439,11 +1440,11 @@ describe("MatrixClient event timelines", function() {
}); });
await Promise.all([httpBackend.flushAllExpected(), utils.syncPromise(client)]); await Promise.all([httpBackend.flushAllExpected(), utils.syncPromise(client)]);
const room = client.getRoom(roomId); const room = client.getRoom(roomId)!;
const thread = room.getThread(THREAD_ROOT.event_id); const thread = room.getThread(THREAD_ROOT.event_id!)!;
const timelineSet = thread.timelineSet; 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, { .respond(200, {
start: "start_token", start: "start_token",
events_before: [], events_before: [],
@ -1453,7 +1454,7 @@ describe("MatrixClient event timelines", function() {
end: "end_token", end: "end_token",
}); });
httpBackend.when("GET", "/rooms/!foo%3Abar/relations/" + httpBackend.when("GET", "/rooms/!foo%3Abar/relations/" +
encodeURIComponent(THREAD_ROOT.event_id) + "/" + encodeURIComponent(THREAD_ROOT.event_id!) + "/" +
encodeURIComponent(THREAD_RELATION_TYPE.name) + "?limit=20") encodeURIComponent(THREAD_RELATION_TYPE.name) + "?limit=20")
.respond(200, function() { .respond(200, function() {
return { return {
@ -1463,7 +1464,7 @@ describe("MatrixClient event timelines", function() {
}; };
}); });
await Promise.all([ await Promise.all([
client.getEventTimeline(timelineSet, THREAD_ROOT.event_id), client.getEventTimeline(timelineSet, THREAD_ROOT.event_id!),
httpBackend.flushAllExpected(), httpBackend.flushAllExpected(),
]); ]);

View File

@ -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 See the License for the specific language governing permissions and
limitations under the License. limitations under the License.
*/ */
import HttpBackend from "matrix-mock-request";
import * as utils from "../test-utils/test-utils"; 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 { MatrixEvent } from "../../src/models/event";
import { Filter, MemoryStore, Room } from "../../src/matrix"; import { Filter, MemoryStore, Room } from "../../src/matrix";
import { TestClient } from "../TestClient"; import { TestClient } from "../TestClient";
import { THREAD_RELATION_TYPE } from "../../src/models/thread"; 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() { describe("MatrixClient", function() {
let client = null;
let httpBackend = null;
let store = null;
const userId = "@alice:localhost"; const userId = "@alice:localhost";
const accessToken = "aseukfgwef"; const accessToken = "aseukfgwef";
const idServerDomain = "identity.localhost"; // not a real server const idServerDomain = "identity.localhost"; // not a real server
const identityAccessToken = "woop-i-am-a-secret"; const identityAccessToken = "woop-i-am-a-secret";
let client: MatrixClient | undefined;
let httpBackend: HttpBackend | undefined;
let store: MemoryStore | undefined;
beforeEach(function() { const defaultClientOpts: IStoredClientOpts = {
store = new MemoryStore(); 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, { const testClient = new TestClient(userId, "aliceDevice", accessToken, undefined, {
store, store: store as IStore,
identityServer: { identityServer: {
getAccessToken: () => Promise.resolve(identityAccessToken), getAccessToken: () => Promise.resolve(identityAccessToken),
}, },
idBaseUrl: `https://${idServerDomain}`, idBaseUrl: `https://${idServerDomain}`,
}); });
httpBackend = testClient.httpBackend;
client = testClient.client; return [testClient.client, testClient.httpBackend, store];
};
beforeEach(function() {
[client, httpBackend, store] = setupTests();
}); });
afterEach(function() { afterEach(function() {
httpBackend.verifyNoOutstandingExpectation(); httpBackend!.verifyNoOutstandingExpectation();
return httpBackend.stop(); return httpBackend!.stop();
}); });
describe("uploadContent", function() { describe("uploadContent", function() {
const buf = Buffer.from('hello world'); const buf = Buffer.from('hello world');
it("should upload the file", function() { it("should upload the file", function() {
httpBackend.when( httpBackend!.when(
"POST", "/_matrix/media/r0/upload", "POST", "/_matrix/media/r0/upload",
).check(function(req) { ).check(function(req) {
expect(req.rawData).toEqual(buf); expect(req.rawData).toEqual(buf);
expect(req.queryParams.filename).toEqual("hi.txt"); expect(req.queryParams?.filename).toEqual("hi.txt");
if (!(req.queryParams.access_token == accessToken || if (!(req.queryParams?.access_token == accessToken ||
req.headers["Authorization"] == "Bearer " + accessToken)) { req.headers["Authorization"] == "Bearer " + accessToken)) {
expect(true).toBe(false); expect(true).toBe(false);
} }
expect(req.headers["Content-Type"]).toEqual("text/plain"); expect(req.headers["Content-Type"]).toEqual("text/plain");
// @ts-ignore private property
expect(req.opts.json).toBeFalsy(); expect(req.opts.json).toBeFalsy();
// @ts-ignore private property
expect(req.opts.timeout).toBe(undefined); expect(req.opts.timeout).toBe(undefined);
}).respond(200, "content", true); }).respond(200, "content", true);
const prom = client.uploadContent({ const prom = client!.uploadContent({
stream: buf, stream: buf,
name: "hi.txt", name: "hi.txt",
type: "text/plain", type: "text/plain",
}); } as unknown as FileType);
expect(prom).toBeTruthy(); expect(prom).toBeTruthy();
const uploads = client.getCurrentUploads(); const uploads = client!.getCurrentUploads();
expect(uploads.length).toEqual(1); expect(uploads.length).toEqual(1);
expect(uploads[0].promise).toBe(prom); expect(uploads[0].promise).toBe(prom);
expect(uploads[0].loaded).toEqual(0); expect(uploads[0].loaded).toEqual(0);
@ -83,51 +99,53 @@ describe("MatrixClient", function() {
// for backwards compatibility, we return the raw JSON // for backwards compatibility, we return the raw JSON
expect(response).toEqual("content"); expect(response).toEqual("content");
const uploads = client.getCurrentUploads(); const uploads = client!.getCurrentUploads();
expect(uploads.length).toEqual(0); expect(uploads.length).toEqual(0);
}); });
httpBackend.flush(); httpBackend!.flush('');
return prom2; return prom2;
}); });
it("should parse the response if rawResponse=false", function() { it("should parse the response if rawResponse=false", function() {
httpBackend.when( httpBackend!.when(
"POST", "/_matrix/media/r0/upload", "POST", "/_matrix/media/r0/upload",
).check(function(req) { ).check(function(req) {
// @ts-ignore private property
expect(req.opts.json).toBeFalsy(); expect(req.opts.json).toBeFalsy();
}).respond(200, { "content_uri": "uri" }); }).respond(200, { "content_uri": "uri" });
const prom = client.uploadContent({ const prom = client!.uploadContent({
stream: buf, stream: buf,
name: "hi.txt", name: "hi.txt",
type: "text/plain", type: "text/plain",
}, { } as unknown as FileType, {
rawResponse: false, rawResponse: false,
}).then(function(response) { }).then(function(response) {
expect(response.content_uri).toEqual("uri"); expect(response.content_uri).toEqual("uri");
}); });
httpBackend.flush(); httpBackend!.flush('');
return prom; return prom;
}); });
it("should parse errors into a MatrixError", function() { it("should parse errors into a MatrixError", function() {
httpBackend.when( httpBackend!.when(
"POST", "/_matrix/media/r0/upload", "POST", "/_matrix/media/r0/upload",
).check(function(req) { ).check(function(req) {
expect(req.rawData).toEqual(buf); expect(req.rawData).toEqual(buf);
// @ts-ignore private property
expect(req.opts.json).toBeFalsy(); expect(req.opts.json).toBeFalsy();
}).respond(400, { }).respond(400, {
"errcode": "M_SNAFU", "errcode": "M_SNAFU",
"error": "broken", "error": "broken",
}); });
const prom = client.uploadContent({ const prom = client!.uploadContent({
stream: buf, stream: buf,
name: "hi.txt", name: "hi.txt",
type: "text/plain", type: "text/plain",
}).then(function(response) { } as unknown as FileType).then(function(response) {
throw Error("request not failed"); throw Error("request not failed");
}, function(error) { }, function(error) {
expect(error.httpStatus).toEqual(400); expect(error.httpStatus).toEqual(400);
@ -135,18 +153,18 @@ describe("MatrixClient", function() {
expect(error.message).toEqual("broken"); expect(error.message).toEqual("broken");
}); });
httpBackend.flush(); httpBackend!.flush('');
return prom; return prom;
}); });
it("should return a promise which can be cancelled", function() { it("should return a promise which can be cancelled", function() {
const prom = client.uploadContent({ const prom = client!.uploadContent({
stream: buf, stream: buf,
name: "hi.txt", name: "hi.txt",
type: "text/plain", type: "text/plain",
}); } as unknown as FileType);
const uploads = client.getCurrentUploads(); const uploads = client!.getCurrentUploads();
expect(uploads.length).toEqual(1); expect(uploads.length).toEqual(1);
expect(uploads[0].promise).toBe(prom); expect(uploads[0].promise).toBe(prom);
expect(uploads[0].loaded).toEqual(0); expect(uploads[0].loaded).toEqual(0);
@ -156,11 +174,11 @@ describe("MatrixClient", function() {
}, function(error) { }, function(error) {
expect(error).toEqual("aborted"); expect(error).toEqual("aborted");
const uploads = client.getCurrentUploads(); const uploads = client!.getCurrentUploads();
expect(uploads.length).toEqual(0); expect(uploads.length).toEqual(0);
}); });
const r = client.cancelUpload(prom); const r = client!.cancelUpload(prom);
expect(r).toBe(true); expect(r).toBe(true);
return prom2; return prom2;
}); });
@ -169,17 +187,20 @@ describe("MatrixClient", function() {
describe("joinRoom", function() { describe("joinRoom", function() {
it("should no-op if you've already joined a room", function() { it("should no-op if you've already joined a room", function() {
const roomId = "!foo:bar"; const roomId = "!foo:bar";
const room = new Room(roomId, client, userId); const room = new Room(roomId, client!, userId);
client.fetchRoomEvent = () => Promise.resolve({}); client!.fetchRoomEvent = () => Promise.resolve({
type: 'test',
content: {},
});
room.addLiveEvents([ room.addLiveEvents([
utils.mkMembership({ utils.mkMembership({
user: userId, room: roomId, mship: "join", event: true, user: userId, room: roomId, mship: "join", event: true,
}), }),
]); ]);
httpBackend.verifyNoOutstandingRequests(); httpBackend!.verifyNoOutstandingRequests();
store.storeRoom(room); store!.storeRoom(room);
client.joinRoom(roomId); client!.joinRoom(roomId);
httpBackend.verifyNoOutstandingRequests(); httpBackend!.verifyNoOutstandingRequests();
}); });
}); });
@ -190,67 +211,67 @@ describe("MatrixClient", function() {
const filter = Filter.fromJson(userId, filterId, { const filter = Filter.fromJson(userId, filterId, {
event_format: "client", event_format: "client",
}); });
store.storeFilter(filter); store!.storeFilter(filter);
client.getFilter(userId, filterId, true).then(function(gotFilter) { client!.getFilter(userId, filterId, true).then(function(gotFilter) {
expect(gotFilter).toEqual(filter); expect(gotFilter).toEqual(filter);
done(); done();
}); });
httpBackend.verifyNoOutstandingRequests(); httpBackend!.verifyNoOutstandingRequests();
}); });
it("should do an HTTP request if !allowCached even if one exists", it("should do an HTTP request if !allowCached even if one exists",
function(done) { function(done) {
const httpFilterDefinition = { const httpFilterDefinition = {
event_format: "federation", event_format: "federation",
}; };
httpBackend.when( httpBackend!.when(
"GET", "/user/" + encodeURIComponent(userId) + "/filter/" + filterId, "GET", "/user/" + encodeURIComponent(userId) + "/filter/" + filterId,
).respond(200, httpFilterDefinition); ).respond(200, httpFilterDefinition);
const storeFilter = Filter.fromJson(userId, filterId, { const storeFilter = Filter.fromJson(userId, filterId, {
event_format: "client", 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", it("should do an HTTP request if nothing is in the cache and then store it",
function(done) { function(done) {
const httpFilterDefinition = { const httpFilterDefinition = {
event_format: "federation", event_format: "federation",
}; };
expect(store.getFilter(userId, filterId)).toBe(null); expect(store!.getFilter(userId, filterId)).toBe(null);
httpBackend.when( httpBackend!.when(
"GET", "/user/" + encodeURIComponent(userId) + "/filter/" + filterId, "GET", "/user/" + encodeURIComponent(userId) + "/filter/" + filterId,
).respond(200, httpFilterDefinition); ).respond(200, httpFilterDefinition);
client.getFilter(userId, filterId, true).then(function(gotFilter) { client!.getFilter(userId, filterId, true).then(function(gotFilter) {
expect(gotFilter.getDefinition()).toEqual(httpFilterDefinition); expect(gotFilter.getDefinition()).toEqual(httpFilterDefinition);
expect(store.getFilter(userId, filterId)).toBeTruthy(); expect(store!.getFilter(userId, filterId)).toBeTruthy();
done(); done();
});
httpBackend!.flush('');
}); });
httpBackend.flush();
});
}); });
describe("createFilter", function() { describe("createFilter", function() {
const filterId = "f1llllllerid"; const filterId = "f1llllllerid";
it("should do an HTTP request and then store the filter", function(done) { 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 = { const filterDefinition = {
event_format: "client", event_format: "client" as IFilterDefinition['event_format'],
}; };
httpBackend.when( httpBackend!.when(
"POST", "/user/" + encodeURIComponent(userId) + "/filter", "POST", "/user/" + encodeURIComponent(userId) + "/filter",
).check(function(req) { ).check(function(req) {
expect(req.data).toEqual(filterDefinition); expect(req.data).toEqual(filterDefinition);
@ -258,13 +279,13 @@ describe("MatrixClient", function() {
filter_id: filterId, filter_id: filterId,
}); });
client.createFilter(filterDefinition).then(function(gotFilter) { client!.createFilter(filterDefinition).then(function(gotFilter) {
expect(gotFilter.getDefinition()).toEqual(filterDefinition); expect(gotFilter.getDefinition()).toEqual(filterDefinition);
expect(store.getFilter(userId, filterId)).toEqual(gotFilter); expect(store!.getFilter(userId, filterId)).toEqual(gotFilter);
done(); done();
}); });
httpBackend.flush(); httpBackend!.flush('');
}); });
}); });
@ -291,10 +312,10 @@ describe("MatrixClient", function() {
}, },
}; };
client.searchMessageText({ client!.searchMessageText({
query: "monkeys", query: "monkeys",
}); });
httpBackend.when("POST", "/search").check(function(req) { httpBackend!.when("POST", "/search").check(function(req) {
expect(req.data).toEqual({ expect(req.data).toEqual({
search_categories: { search_categories: {
room_events: { room_events: {
@ -304,7 +325,7 @@ describe("MatrixClient", function() {
}); });
}).respond(200, response); }).respond(200, response);
return httpBackend.flush(); return httpBackend!.flush('');
}); });
describe("should filter out context from different timelines (threads)", () => { describe("should filter out context from different timelines (threads)", () => {
@ -313,11 +334,14 @@ describe("MatrixClient", function() {
search_categories: { search_categories: {
room_events: { room_events: {
count: 24, count: 24,
highlights: [],
results: [{ results: [{
rank: 0.1, rank: 0.1,
result: { result: {
event_id: "$flibble:localhost", event_id: "$flibble:localhost",
type: "m.room.message", type: "m.room.message",
sender: '@test:locahost',
origin_server_ts: 123,
user_id: "@alice:localhost", user_id: "@alice:localhost",
room_id: "!feuiwhf:localhost", room_id: "!feuiwhf:localhost",
content: { content: {
@ -326,9 +350,12 @@ describe("MatrixClient", function() {
}, },
}, },
context: { context: {
profile_info: {},
events_after: [{ events_after: [{
event_id: "$ev-after:server", event_id: "$ev-after:server",
type: "m.room.message", type: "m.room.message",
sender: '@test:locahost',
origin_server_ts: 123,
user_id: "@alice:localhost", user_id: "@alice:localhost",
room_id: "!feuiwhf:localhost", room_id: "!feuiwhf:localhost",
content: { content: {
@ -343,6 +370,8 @@ describe("MatrixClient", function() {
events_before: [{ events_before: [{
event_id: "$ev-before:server", event_id: "$ev-before:server",
type: "m.room.message", type: "m.room.message",
sender: '@test:locahost',
origin_server_ts: 123,
user_id: "@alice:localhost", user_id: "@alice:localhost",
room_id: "!feuiwhf:localhost", room_id: "!feuiwhf:localhost",
content: { content: {
@ -356,15 +385,17 @@ describe("MatrixClient", function() {
}, },
}; };
const data = { const data: ISearchResults = {
results: [], results: [],
highlights: [], highlights: [],
}; };
client.processRoomEventsSearch(data, response); client!.processRoomEventsSearch(data, response);
expect(data.results).toHaveLength(1); expect(data.results).toHaveLength(1);
expect(data.results[0].context.timeline).toHaveLength(2); expect(data.results[0].context.getTimeline()).toHaveLength(2);
expect(data.results[0].context.timeline.find(e => e.getId() === "$ev-after:server")).toBeFalsy(); 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", () => { it("filters out thread replies from threads other than the thread the result replied to", () => {
@ -372,11 +403,14 @@ describe("MatrixClient", function() {
search_categories: { search_categories: {
room_events: { room_events: {
count: 24, count: 24,
highlights: [],
results: [{ results: [{
rank: 0.1, rank: 0.1,
result: { result: {
event_id: "$flibble:localhost", event_id: "$flibble:localhost",
type: "m.room.message", type: "m.room.message",
sender: '@test:locahost',
origin_server_ts: 123,
user_id: "@alice:localhost", user_id: "@alice:localhost",
room_id: "!feuiwhf:localhost", room_id: "!feuiwhf:localhost",
content: { content: {
@ -389,9 +423,12 @@ describe("MatrixClient", function() {
}, },
}, },
context: { context: {
profile_info: {},
events_after: [{ events_after: [{
event_id: "$ev-after:server", event_id: "$ev-after:server",
type: "m.room.message", type: "m.room.message",
sender: '@test:locahost',
origin_server_ts: 123,
user_id: "@alice:localhost", user_id: "@alice:localhost",
room_id: "!feuiwhf:localhost", room_id: "!feuiwhf:localhost",
content: { content: {
@ -410,15 +447,17 @@ describe("MatrixClient", function() {
}, },
}; };
const data = { const data: ISearchResults = {
results: [], results: [],
highlights: [], highlights: [],
}; };
client.processRoomEventsSearch(data, response); client!.processRoomEventsSearch(data, response);
expect(data.results).toHaveLength(1); expect(data.results).toHaveLength(1);
expect(data.results[0].context.timeline).toHaveLength(1); expect(data.results[0].context.getTimeline()).toHaveLength(1);
expect(data.results[0].context.timeline.find(e => e.getId() === "$flibble:localhost")).toBeTruthy(); 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", () => { it("filters out main timeline events when result is a thread reply", () => {
@ -426,10 +465,13 @@ describe("MatrixClient", function() {
search_categories: { search_categories: {
room_events: { room_events: {
count: 24, count: 24,
highlights: [],
results: [{ results: [{
rank: 0.1, rank: 0.1,
result: { result: {
event_id: "$flibble:localhost", event_id: "$flibble:localhost",
sender: '@test:locahost',
origin_server_ts: 123,
type: "m.room.message", type: "m.room.message",
user_id: "@alice:localhost", user_id: "@alice:localhost",
room_id: "!feuiwhf:localhost", room_id: "!feuiwhf:localhost",
@ -445,6 +487,8 @@ describe("MatrixClient", function() {
context: { context: {
events_after: [{ events_after: [{
event_id: "$ev-after:server", event_id: "$ev-after:server",
sender: '@test:locahost',
origin_server_ts: 123,
type: "m.room.message", type: "m.room.message",
user_id: "@alice:localhost", user_id: "@alice:localhost",
room_id: "!feuiwhf:localhost", room_id: "!feuiwhf:localhost",
@ -454,21 +498,24 @@ describe("MatrixClient", function() {
}, },
}], }],
events_before: [], events_before: [],
profile_info: {},
}, },
}], }],
}, },
}, },
}; };
const data = { const data: ISearchResults = {
results: [], results: [],
highlights: [], highlights: [],
}; };
client.processRoomEventsSearch(data, response); client!.processRoomEventsSearch(data, response);
expect(data.results).toHaveLength(1); expect(data.results).toHaveLength(1);
expect(data.results[0].context.timeline).toHaveLength(1); expect(data.results[0].context.getTimeline()).toHaveLength(1);
expect(data.results[0].context.timeline.find(e => e.getId() === "$flibble:localhost")).toBeTruthy(); expect(
data.results[0].context.getTimeline().find(e => e.getId() === "$flibble:localhost"),
).toBeTruthy();
}); });
}); });
}); });
@ -479,16 +526,16 @@ describe("MatrixClient", function() {
} }
beforeEach(function() { beforeEach(function() {
return client.initCrypto(); return client!.initCrypto();
}); });
afterEach(() => { afterEach(() => {
client.stopClient(); client!.stopClient();
}); });
it("should do an HTTP request and then store the keys", function() { it("should do an HTTP request and then store the keys", function() {
const ed25519key = "7wG2lzAqbjcyEkOP7O4gU7ItYcn+chKzh5sT/5r2l78"; const ed25519key = "7wG2lzAqbjcyEkOP7O4gU7ItYcn+chKzh5sT/5r2l78";
// ed25519key = client.getDeviceEd25519Key(); // ed25519key = client!.getDeviceEd25519Key();
const borisKeys = { const borisKeys = {
dev1: { dev1: {
algorithms: ["1"], algorithms: ["1"],
@ -512,7 +559,7 @@ describe("MatrixClient", function() {
keys: { "ed25519:dev2": ed25519key }, keys: { "ed25519:dev2": ed25519key },
signatures: { signatures: {
chaz: { chaz: {
"ed25519:dev2": "ed25519:dev2":
"FwslH/Q7EYSb7swDJbNB5PSzcbEO1xRRBF1riuijqvL" + "FwslH/Q7EYSb7swDJbNB5PSzcbEO1xRRBF1riuijqvL" +
"EkrK9/XVN8jl4h7thGuRITQ01siBQnNmMK9t45QfcCQ", "EkrK9/XVN8jl4h7thGuRITQ01siBQnNmMK9t45QfcCQ",
}, },
@ -528,7 +575,7 @@ describe("MatrixClient", function() {
var b = JSON.parse(JSON.stringify(o)); var b = JSON.parse(JSON.stringify(o));
delete(b.signatures); delete(b.signatures);
delete(b.unsigned); delete(b.unsigned);
return client.crypto.olmDevice.sign(anotherjson.stringify(b)); return client!.crypto.olmDevice.sign(anotherjson.stringify(b));
}; };
logger.log("Ed25519: " + ed25519key); logger.log("Ed25519: " + ed25519key);
@ -536,7 +583,7 @@ describe("MatrixClient", function() {
logger.log("chaz:", sign(chazKeys.dev2)); 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: { expect(req.data).toEqual({ device_keys: {
'boris': [], 'boris': [],
'chaz': [], '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, { assertObjectContains(res.boris.dev1, {
verified: 0, // DeviceVerification.UNVERIFIED verified: 0, // DeviceVerification.UNVERIFIED
keys: { "ed25519:dev1": ed25519key }, keys: { "ed25519:dev1": ed25519key },
@ -564,23 +611,23 @@ describe("MatrixClient", function() {
}); });
}); });
httpBackend.flush(); httpBackend!.flush('');
return prom; return prom;
}); });
}); });
describe("deleteDevice", function() { describe("deleteDevice", function() {
const auth = { a: 1 }; const auth = { identifier: 1 };
it("should pass through an auth dict", function() { it("should pass through an auth dict", function() {
httpBackend.when( httpBackend!.when(
"DELETE", "/_matrix/client/r0/devices/my_device", "DELETE", "/_matrix/client/r0/devices/my_device",
).check(function(req) { ).check(function(req) {
expect(req.data).toEqual({ auth: auth }); expect(req.data).toEqual({ auth: auth });
}).respond(200); }).respond(200);
const prom = client.deleteDevice("my_device", auth); const prom = client!.deleteDevice("my_device", auth);
httpBackend.flush(); httpBackend!.flush('');
return prom; return prom;
}); });
}); });
@ -588,7 +635,7 @@ describe("MatrixClient", function() {
describe("partitionThreadedEvents", function() { describe("partitionThreadedEvents", function() {
let room; let room;
beforeEach(() => { 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() { 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() { 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 eventPollResponseReference = buildEventPollResponseReference();
const eventPollStartThreadRoot = buildEventPollStartThreadRoot(); const eventPollStartThreadRoot = buildEventPollStartThreadRoot();
@ -611,6 +662,7 @@ describe("MatrixClient", function() {
eventPollResponseReference, eventPollResponseReference,
]; ];
// Vote has no threadId yet // Vote has no threadId yet
// @ts-ignore private property
expect(eventPollResponseReference.threadId).toBeFalsy(); expect(eventPollResponseReference.threadId).toBeFalsy();
const [timeline, threaded] = room.partitionThreadedEvents(events); const [timeline, threaded] = room.partitionThreadedEvents(events);
@ -634,7 +686,11 @@ describe("MatrixClient", function() {
}); });
it("copies pre-thread in-timeline reactions onto both timelines", 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 eventPollStartThreadRoot = buildEventPollStartThreadRoot();
const eventMessageInThread = buildEventMessageInThread(eventPollStartThreadRoot); const eventMessageInThread = buildEventMessageInThread(eventPollStartThreadRoot);
@ -661,7 +717,11 @@ describe("MatrixClient", function() {
}); });
it("copies post-thread in-timeline vote events onto both timelines", 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 eventPollResponseReference = buildEventPollResponseReference();
const eventPollStartThreadRoot = buildEventPollStartThreadRoot(); const eventPollStartThreadRoot = buildEventPollStartThreadRoot();
@ -688,7 +748,11 @@ describe("MatrixClient", function() {
}); });
it("copies post-thread in-timeline reactions onto both timelines", 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 eventPollStartThreadRoot = buildEventPollStartThreadRoot();
const eventMessageInThread = buildEventMessageInThread(eventPollStartThreadRoot); const eventMessageInThread = buildEventMessageInThread(eventPollStartThreadRoot);
@ -715,7 +779,11 @@ describe("MatrixClient", function() {
}); });
it("sends room state events to the main timeline only", 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: // This is based on recording the events in a real room:
const eventPollStartThreadRoot = buildEventPollStartThreadRoot(); const eventPollStartThreadRoot = buildEventPollStartThreadRoot();
@ -768,7 +836,11 @@ describe("MatrixClient", function() {
}); });
it("sends redactions of reactions to thread responses to thread timeline only", () => { 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 threadRootEvent = buildEventPollStartThreadRoot();
const eventMessageInThread = buildEventMessageInThread(threadRootEvent); 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", () => { 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 threadRootEvent = buildEventPollStartThreadRoot();
const eventMessageInThread = buildEventMessageInThread(threadRootEvent); const eventMessageInThread = buildEventMessageInThread(threadRootEvent);
@ -826,7 +902,11 @@ describe("MatrixClient", function() {
}); });
it("sends reply to thread responses to main timeline only", () => { 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 threadRootEvent = buildEventPollStartThreadRoot();
const eventMessageInThread = buildEventMessageInThread(threadRootEvent); const eventMessageInThread = buildEventMessageInThread(threadRootEvent);
@ -860,9 +940,9 @@ describe("MatrixClient", function() {
fields: {}, fields: {},
}]; }];
const prom = client.getThirdpartyUser("irc", {}); const prom = client!.getThirdpartyUser("irc", {});
httpBackend.when("GET", "/thirdparty/user/irc").respond(200, response); httpBackend!.when("GET", "/thirdparty/user/irc").respond(200, response);
await httpBackend.flush(); await httpBackend!.flush('');
expect(await prom).toStrictEqual(response); expect(await prom).toStrictEqual(response);
}); });
}); });
@ -875,9 +955,9 @@ describe("MatrixClient", function() {
fields: {}, fields: {},
}]; }];
const prom = client.getThirdpartyLocation("irc", {}); const prom = client!.getThirdpartyLocation("irc", {});
httpBackend.when("GET", "/thirdparty/location/irc").respond(200, response); httpBackend!.when("GET", "/thirdparty/location/irc").respond(200, response);
await httpBackend.flush(); await httpBackend!.flush('');
expect(await prom).toStrictEqual(response); expect(await prom).toStrictEqual(response);
}); });
}); });
@ -888,10 +968,10 @@ describe("MatrixClient", function() {
pushers: [], pushers: [],
}; };
const prom = client.getPushers(); const prom = client!.getPushers();
httpBackend.when("GET", "/_matrix/client/versions").respond(200, {}); httpBackend!.when("GET", "/_matrix/client/versions").respond(200, {});
httpBackend.when("GET", "/pushers").respond(200, response); httpBackend!.when("GET", "/pushers").respond(200, response);
await httpBackend.flush(); await httpBackend!.flush('');
expect(await prom).toStrictEqual(response); expect(await prom).toStrictEqual(response);
}); });
}); });
@ -903,12 +983,12 @@ describe("MatrixClient", function() {
left: [], left: [],
}; };
const prom = client.getKeyChanges("old", "new"); const prom = client!.getKeyChanges("old", "new");
httpBackend.when("GET", "/keys/changes").check((req) => { httpBackend!.when("GET", "/keys/changes").check((req) => {
expect(req.queryParams.from).toEqual("old"); expect(req.queryParams?.from).toEqual("old");
expect(req.queryParams.to).toEqual("new"); expect(req.queryParams?.to).toEqual("new");
}).respond(200, response); }).respond(200, response);
await httpBackend.flush(); await httpBackend!.flush('');
expect(await prom).toStrictEqual(response); expect(await prom).toStrictEqual(response);
}); });
}); });
@ -919,9 +999,9 @@ describe("MatrixClient", function() {
devices: [], devices: [],
}; };
const prom = client.getDevices(); const prom = client!.getDevices();
httpBackend.when("GET", "/devices").respond(200, response); httpBackend!.when("GET", "/devices").respond(200, response);
await httpBackend.flush(); await httpBackend!.flush('');
expect(await prom).toStrictEqual(response); expect(await prom).toStrictEqual(response);
}); });
}); });
@ -935,9 +1015,9 @@ describe("MatrixClient", function() {
last_seen_ts: 1, last_seen_ts: 1,
}; };
const prom = client.getDevice("DEADBEEF"); const prom = client!.getDevice("DEADBEEF");
httpBackend.when("GET", "/devices/DEADBEEF").respond(200, response); httpBackend!.when("GET", "/devices/DEADBEEF").respond(200, response);
await httpBackend.flush(); await httpBackend!.flush('');
expect(await prom).toStrictEqual(response); expect(await prom).toStrictEqual(response);
}); });
}); });
@ -948,9 +1028,9 @@ describe("MatrixClient", function() {
threepids: [], threepids: [],
}; };
const prom = client.getThreePids(); const prom = client!.getThreePids();
httpBackend.when("GET", "/account/3pid").respond(200, response); httpBackend!.when("GET", "/account/3pid").respond(200, response);
await httpBackend.flush(); await httpBackend!.flush('');
expect(await prom).toStrictEqual(response); expect(await prom).toStrictEqual(response);
}); });
}); });
@ -958,9 +1038,9 @@ describe("MatrixClient", function() {
describe("deleteAlias", () => { describe("deleteAlias", () => {
it("should hit the expected API endpoint", async () => { it("should hit the expected API endpoint", async () => {
const response = {}; const response = {};
const prom = client.deleteAlias("#foo:bar"); const prom = client!.deleteAlias("#foo:bar");
httpBackend.when("DELETE", "/directory/room/" + encodeURIComponent("#foo:bar")).respond(200, response); httpBackend!.when("DELETE", "/directory/room/" + encodeURIComponent("#foo:bar")).respond(200, response);
await httpBackend.flush(); await httpBackend!.flush('');
expect(await prom).toStrictEqual(response); expect(await prom).toStrictEqual(response);
}); });
}); });
@ -968,10 +1048,10 @@ describe("MatrixClient", function() {
describe("deleteRoomTag", () => { describe("deleteRoomTag", () => {
it("should hit the expected API endpoint", async () => { it("should hit the expected API endpoint", async () => {
const response = {}; 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`; const url = `/user/${encodeURIComponent(userId)}/rooms/${encodeURIComponent("!roomId:server")}/tags/u.tag`;
httpBackend.when("DELETE", url).respond(200, response); httpBackend!.when("DELETE", url).respond(200, response);
await httpBackend.flush(); await httpBackend!.flush('');
expect(await prom).toStrictEqual(response); 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`; const url = `/user/${encodeURIComponent(userId)}/rooms/${encodeURIComponent("!roomId:server")}/tags`;
httpBackend.when("GET", url).respond(200, response); httpBackend!.when("GET", url).respond(200, response);
await httpBackend.flush(); await httpBackend!.flush('');
expect(await prom).toStrictEqual(response); expect(await prom).toStrictEqual(response);
}); });
}); });
@ -1001,19 +1081,19 @@ describe("MatrixClient", function() {
submit_url: "https://foobar.matrix/_matrix/matrix", 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"], versions: ["r0.6.0"],
}); });
const prom = client.requestRegisterEmailToken("bob@email", "secret", 1); const prom = client!.requestRegisterEmailToken("bob@email", "secret", 1);
httpBackend.when("POST", "/register/email/requestToken").check(req => { httpBackend!.when("POST", "/register/email/requestToken").check(req => {
expect(req.data).toStrictEqual({ expect(req.data).toStrictEqual({
email: "bob@email", email: "bob@email",
client_secret: "secret", client_secret: "secret",
send_attempt: 1, send_attempt: 1,
}); });
}).respond(200, response); }).respond(200, response);
await httpBackend.flush(); await httpBackend!.flush('');
expect(await prom).toStrictEqual(response); expect(await prom).toStrictEqual(response);
}); });
}); });
@ -1022,11 +1102,11 @@ describe("MatrixClient", function() {
it("should supply an id_access_token", async () => { it("should supply an id_access_token", async () => {
const targetEmail = "gerald@example.org"; 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"], versions: ["r0.6.0"],
}); });
httpBackend.when("POST", "/invite").check(req => { httpBackend!.when("POST", "/invite").check(req => {
expect(req.data).toStrictEqual({ expect(req.data).toStrictEqual({
id_server: idServerDomain, id_server: idServerDomain,
id_access_token: identityAccessToken, id_access_token: identityAccessToken,
@ -1035,8 +1115,8 @@ describe("MatrixClient", function() {
}); });
}).respond(200, {}); }).respond(200, {});
const prom = client.inviteByThreePid("!room:example.org", "email", targetEmail); const prom = client!.inviteByThreePid("!room:example.org", "email", targetEmail);
await httpBackend.flush(); await httpBackend!.flush('');
await prom; // returns empty object, so no validation needed 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"], versions: ["r0.6.0"],
}); });
httpBackend.when("POST", "/createRoom").check(req => { httpBackend!.when("POST", "/createRoom").check(req => {
expect(req.data).toMatchObject({ expect(req.data).toMatchObject({
invite_3pid: expect.arrayContaining([{ invite_3pid: expect.arrayContaining([{
...input.invite_3pid[0], ...input.invite_3pid[0],
@ -1070,8 +1150,8 @@ describe("MatrixClient", function() {
expect(req.data.invite_3pid.length).toBe(1); expect(req.data.invite_3pid.length).toBe(1);
}).respond(200, response); }).respond(200, response);
const prom = client.createRoom(input); const prom = client!.createRoom(input);
await httpBackend.flush(); await httpBackend!.flush('');
expect(await prom).toStrictEqual(response); expect(await prom).toStrictEqual(response);
}); });
}); });
@ -1079,22 +1159,22 @@ describe("MatrixClient", function() {
describe("requestLoginToken", () => { describe("requestLoginToken", () => {
it("should hit the expected API endpoint with UIA", async () => { it("should hit the expected API endpoint with UIA", async () => {
const response = {}; const response = {};
const uiaData = { foo: "baa" }; const uiaData = {};
const prom = client.requestLoginToken(uiaData); const prom = client!.requestLoginToken(uiaData);
httpBackend httpBackend!
.when("POST", "/unstable/org.matrix.msc3882/login/token", { auth: uiaData }) .when("POST", "/unstable/org.matrix.msc3882/login/token", { auth: uiaData })
.respond(200, response); .respond(200, response);
await httpBackend.flush(); await httpBackend!.flush('');
expect(await prom).toStrictEqual(response); expect(await prom).toStrictEqual(response);
}); });
it("should hit the expected API endpoint without UIA", async () => { it("should hit the expected API endpoint without UIA", async () => {
const response = {}; const response = {};
const prom = client.requestLoginToken(); const prom = client!.requestLoginToken();
httpBackend httpBackend!
.when("POST", "/unstable/org.matrix.msc3882/login/token", {}) .when("POST", "/unstable/org.matrix.msc3882/login/token", {})
.respond(200, response); .respond(200, response);
await httpBackend.flush(); await httpBackend!.flush('');
expect(await prom).toStrictEqual(response); expect(await prom).toStrictEqual(response);
}); });
}); });

View File

@ -5,10 +5,12 @@ import { MatrixClient } from "../../src/matrix";
import { MatrixScheduler } from "../../src/scheduler"; import { MatrixScheduler } from "../../src/scheduler";
import { MemoryStore } from "../../src/store/memory"; import { MemoryStore } from "../../src/store/memory";
import { MatrixError } from "../../src/http-api"; import { MatrixError } from "../../src/http-api";
import { ICreateClientOpts } from "../../src/client";
import { IStore } from "../../src/store";
describe("MatrixClient opts", function() { describe("MatrixClient opts", function() {
const baseUrl = "http://localhost.or.something"; const baseUrl = "http://localhost.or.something";
let httpBackend = null; let httpBackend = new HttpBackend();
const userId = "@alice:localhost"; const userId = "@alice:localhost";
const userB = "@bob:localhost"; const userB = "@bob:localhost";
const accessToken = "aseukfgwef"; const accessToken = "aseukfgwef";
@ -67,7 +69,7 @@ describe("MatrixClient opts", function() {
let client; let client;
beforeEach(function() { beforeEach(function() {
client = new MatrixClient({ client = new MatrixClient({
request: httpBackend.requestFn, request: httpBackend.requestFn as unknown as ICreateClientOpts['request'],
store: undefined, store: undefined,
baseUrl: baseUrl, baseUrl: baseUrl,
userId: userId, userId: userId,
@ -99,7 +101,7 @@ describe("MatrixClient opts", function() {
]; ];
client.on("event", function(event) { client.on("event", function(event) {
expect(expectedEventTypes.indexOf(event.getType())).not.toEqual( expect(expectedEventTypes.indexOf(event.getType())).not.toEqual(
-1, "Recv unexpected event type: " + event.getType(), -1,
); );
expectedEventTypes.splice( expectedEventTypes.splice(
expectedEventTypes.indexOf(event.getType()), 1, expectedEventTypes.indexOf(event.getType()), 1,
@ -118,7 +120,7 @@ describe("MatrixClient opts", function() {
utils.syncPromise(client), utils.syncPromise(client),
]); ]);
expect(expectedEventTypes.length).toEqual( expect(expectedEventTypes.length).toEqual(
0, "Expected to see event types: " + expectedEventTypes, 0,
); );
}); });
}); });
@ -127,8 +129,8 @@ describe("MatrixClient opts", function() {
let client; let client;
beforeEach(function() { beforeEach(function() {
client = new MatrixClient({ client = new MatrixClient({
request: httpBackend.requestFn, request: httpBackend.requestFn as unknown as ICreateClientOpts['request'],
store: new MemoryStore(), store: new MemoryStore() as IStore,
baseUrl: baseUrl, baseUrl: baseUrl,
userId: userId, userId: userId,
accessToken: accessToken, accessToken: accessToken,
@ -146,7 +148,7 @@ describe("MatrixClient opts", function() {
error: "Ruh roh", error: "Ruh roh",
})); }));
client.sendTextMessage("!foo:bar", "a body", "txn1").then(function(res) { 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) { }, function(err) {
expect(err.errcode).toEqual("M_SOMETHING"); expect(err.errcode).toEqual("M_SOMETHING");
done(); done();

View File

@ -14,22 +14,22 @@ See the License for the specific language governing permissions and
limitations under the License. limitations under the License.
*/ */
import { EventStatus, RoomEvent, MatrixClient } from "../../src/matrix"; import HttpBackend from "matrix-mock-request";
import { MatrixScheduler } from "../../src/scheduler";
import { EventStatus, RoomEvent, MatrixClient, MatrixScheduler } from "../../src/matrix";
import { Room } from "../../src/models/room"; import { Room } from "../../src/models/room";
import { TestClient } from "../TestClient"; import { TestClient } from "../TestClient";
describe("MatrixClient retrying", function() { describe("MatrixClient retrying", function() {
let client: MatrixClient = null;
let httpBackend: TestClient["httpBackend"] = null;
let scheduler;
const userId = "@alice:localhost"; const userId = "@alice:localhost";
const accessToken = "aseukfgwef"; const accessToken = "aseukfgwef";
const roomId = "!room:here"; const roomId = "!room:here";
let room: Room; let client: MatrixClient | undefined;
let httpBackend: HttpBackend | undefined;
let room: Room | undefined;
beforeEach(function() { const setupTests = (): [MatrixClient, HttpBackend, Room] => {
scheduler = new MatrixScheduler(); const scheduler = new MatrixScheduler();
const testClient = new TestClient( const testClient = new TestClient(
userId, userId,
"DEVICE", "DEVICE",
@ -37,15 +37,21 @@ describe("MatrixClient retrying", function() {
undefined, undefined,
{ scheduler }, { scheduler },
); );
httpBackend = testClient.httpBackend; const httpBackend = testClient.httpBackend;
client = testClient.client; const client = testClient.client;
room = new Room(roomId, client, userId); const room = new Room(roomId, client, userId);
client.store.storeRoom(room); client!.store.storeRoom(room);
return [client, httpBackend, room];
};
beforeEach(function() {
[client, httpBackend, room] = setupTests();
}); });
afterEach(function() { afterEach(function() {
httpBackend.verifyNoOutstandingExpectation(); httpBackend!.verifyNoOutstandingExpectation();
return httpBackend.stop(); return httpBackend!.stop();
}); });
xit("should retry according to MatrixScheduler.retryFn", function() { 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() { it("should mark events as EventStatus.CANCELLED when cancelled", function() {
// send a couple of events; the second will be queued // send a couple of events; the second will be queued
const p1 = client.sendMessage(roomId, { const p1 = client!.sendMessage(roomId, {
"msgtype": "m.text", "msgtype": "m.text",
"body": "m1", "body": "m1",
}).then(function() { }).then(function() {
@ -79,13 +85,13 @@ describe("MatrixClient retrying", function() {
// XXX: it turns out that the promise returned by this message // XXX: it turns out that the promise returned by this message
// never gets resolved. // never gets resolved.
// https://github.com/matrix-org/matrix-js-sdk/issues/496 // https://github.com/matrix-org/matrix-js-sdk/issues/496
client.sendMessage(roomId, { client!.sendMessage(roomId, {
"msgtype": "m.text", "msgtype": "m.text",
"body": "m2", "body": "m2",
}); });
// both events should be in the timeline at this point // 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); expect(tl.length).toEqual(2);
const ev1 = tl[0]; const ev1 = tl[0];
const ev2 = tl[1]; const ev2 = tl[1];
@ -94,24 +100,24 @@ describe("MatrixClient retrying", function() {
expect(ev2.status).toEqual(EventStatus.SENDING); expect(ev2.status).toEqual(EventStatus.SENDING);
// the first message should get sent, and the second should get queued // 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 // ev2 should now have been queued
expect(ev2.status).toEqual(EventStatus.QUEUED); expect(ev2.status).toEqual(EventStatus.QUEUED);
// now we can cancel the second and check everything looks sane // now we can cancel the second and check everything looks sane
client.cancelPendingEvent(ev2); client!.cancelPendingEvent(ev2);
expect(ev2.status).toEqual(EventStatus.CANCELLED); expect(ev2.status).toEqual(EventStatus.CANCELLED);
expect(tl.length).toEqual(1); expect(tl.length).toEqual(1);
// shouldn't be able to cancel the first message yet // shouldn't be able to cancel the first message yet
expect(function() { expect(function() {
client.cancelPendingEvent(ev1); client!.cancelPendingEvent(ev1);
}).toThrow(); }).toThrow();
}).respond(400); // fail the first message }).respond(400); // fail the first message
// wait for the localecho of ev1 to be updated // wait for the localecho of ev1 to be updated
const p3 = new Promise<void>((resolve, reject) => { const p3 = new Promise<void>((resolve, reject) => {
room.on(RoomEvent.LocalEchoUpdated, (ev0) => { room!.on(RoomEvent.LocalEchoUpdated, (ev0) => {
if (ev0 === ev1) { if (ev0 === ev1) {
resolve(); resolve();
} }
@ -121,7 +127,7 @@ describe("MatrixClient retrying", function() {
expect(tl.length).toEqual(1); expect(tl.length).toEqual(1);
// cancel the first message // cancel the first message
client.cancelPendingEvent(ev1); client!.cancelPendingEvent(ev1);
expect(ev1.status).toEqual(EventStatus.CANCELLED); expect(ev1.status).toEqual(EventStatus.CANCELLED);
expect(tl.length).toEqual(0); expect(tl.length).toEqual(0);
}); });
@ -129,7 +135,7 @@ describe("MatrixClient retrying", function() {
return Promise.all([ return Promise.all([
p1, p1,
p3, p3,
httpBackend.flushAllExpected(), httpBackend!.flushAllExpected(),
]); ]);
}); });

View File

@ -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 * as utils from "../test-utils/test-utils";
import { EventStatus } from "../../src/models/event"; import { EventStatus } from "../../src/models/event";
import { RoomEvent } from "../../src"; import { ClientEvent, IEvent, MatrixClient, RoomEvent } from "../../src";
import { TestClient } from "../TestClient"; import { TestClient } from "../TestClient";
describe("MatrixClient room timelines", function() { describe("MatrixClient room timelines", function() {
let client = null;
let httpBackend = null;
const userId = "@alice:localhost"; const userId = "@alice:localhost";
const userName = "Alice"; const userName = "Alice";
const accessToken = "aseukfgwef"; const accessToken = "aseukfgwef";
const roomId = "!foo:bar"; const roomId = "!foo:bar";
const otherUserId = "@bob:localhost"; const otherUserId = "@bob:localhost";
let client: MatrixClient | undefined;
let httpBackend: HttpBackend | undefined;
const USER_MEMBERSHIP_EVENT = utils.mkMembership({ const USER_MEMBERSHIP_EVENT = utils.mkMembership({
room: roomId, mship: "join", user: userId, name: userName, room: roomId, mship: "join", user: userId, name: userName,
}); });
@ -55,8 +74,7 @@ describe("MatrixClient room timelines", function() {
}, },
}; };
function setNextSyncData(events) { function setNextSyncData(events: Partial<IEvent>[] = []) {
events = events || [];
NEXT_SYNC_DATA = { NEXT_SYNC_DATA = {
next_batch: "n", next_batch: "n",
presence: { events: [] }, presence: { events: [] },
@ -77,19 +95,9 @@ describe("MatrixClient room timelines", function() {
throw new Error("setNextSyncData only works with one room id"); throw new Error("setNextSyncData only works with one room id");
} }
if (e.state_key) { 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 // push the current
NEXT_SYNC_DATA.rooms.join[roomId].timeline.events.push(e); 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); NEXT_SYNC_DATA.rooms.join[roomId].ephemeral.events.push(e);
} else { } else {
NEXT_SYNC_DATA.rooms.join[roomId].timeline.events.push(e); 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 // these tests should work with or without timelineSupport
const testClient = new TestClient( const testClient = new TestClient(
userId, userId,
@ -106,41 +114,46 @@ describe("MatrixClient room timelines", function() {
undefined, undefined,
{ timelineSupport: true }, { timelineSupport: true },
); );
httpBackend = testClient.httpBackend; const httpBackend = testClient.httpBackend;
client = testClient.client; const client = testClient.client;
setNextSyncData(); setNextSyncData();
httpBackend.when("GET", "/versions").respond(200, {}); httpBackend!.when("GET", "/versions").respond(200, {});
httpBackend.when("GET", "/pushrules").respond(200, {}); httpBackend!.when("GET", "/pushrules").respond(200, {});
httpBackend.when("POST", "/filter").respond(200, { filter_id: "fid" }); httpBackend!.when("POST", "/filter").respond(200, { filter_id: "fid" });
httpBackend.when("GET", "/sync").respond(200, SYNC_DATA); httpBackend!.when("GET", "/sync").respond(200, SYNC_DATA);
httpBackend.when("GET", "/sync").respond(200, function() { httpBackend!.when("GET", "/sync").respond(200, function() {
return NEXT_SYNC_DATA; return NEXT_SYNC_DATA;
}); });
client.startClient(); client!.startClient();
return [client!, httpBackend];
};
beforeEach(async function() {
[client!, httpBackend] = setupTestClient();
await httpBackend.flush("/versions"); await httpBackend.flush("/versions");
await httpBackend.flush("/pushrules"); await httpBackend.flush("/pushrules");
await httpBackend.flush("/filter"); await httpBackend.flush("/filter");
}); });
afterEach(function() { afterEach(function() {
httpBackend.verifyNoOutstandingExpectation(); httpBackend!.verifyNoOutstandingExpectation();
client.stopClient(); client!.stopClient();
return httpBackend.stop(); return httpBackend!.stop();
}); });
describe("local echo events", function() { describe("local echo events", function() {
it("should be added immediately after calling MatrixClient.sendEvent " + it("should be added immediately after calling MatrixClient.sendEvent " +
"with EventStatus.SENDING and the right event.sender", function(done) { "with EventStatus.SENDING and the right event.sender", function(done) {
client.on("sync", function(state) { client!.on(ClientEvent.Sync, function(state) {
if (state !== "PREPARED") { if (state !== "PREPARED") {
return; return;
} }
const room = client.getRoom(roomId); const room = client!.getRoom(roomId)!;
expect(room.timeline.length).toEqual(1); 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 // check it was added
expect(room.timeline.length).toEqual(2); expect(room.timeline.length).toEqual(2);
// check status // check status
@ -150,68 +163,68 @@ describe("MatrixClient room timelines", function() {
expect(member.userId).toEqual(userId); expect(member.userId).toEqual(userId);
expect(member.name).toEqual(userName); expect(member.name).toEqual(userName);
httpBackend.flush("/sync", 1).then(function() { httpBackend!.flush("/sync", 1).then(function() {
done(); done();
}); });
}); });
httpBackend.flush("/sync", 1); httpBackend!.flush("/sync", 1);
}); });
it("should be updated correctly when the send request finishes " + it("should be updated correctly when the send request finishes " +
"BEFORE the event comes down the event stream", function(done) { "BEFORE the event comes down the event stream", function(done) {
const eventId = "$foo:bar"; const eventId = "$foo:bar";
httpBackend.when("PUT", "/txn1").respond(200, { httpBackend!.when("PUT", "/txn1").respond(200, {
event_id: eventId, event_id: eventId,
}); });
const ev = utils.mkMessage({ 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.event_id = eventId;
ev.unsigned = { transaction_id: "txn1" }; ev.unsigned = { transaction_id: "txn1" };
setNextSyncData([ev]); setNextSyncData([ev]);
client.on("sync", function(state) { client!.on(ClientEvent.Sync, function(state) {
if (state !== "PREPARED") { if (state !== "PREPARED") {
return; return;
} }
const room = client.getRoom(roomId); const room = client!.getRoom(roomId)!;
client.sendTextMessage(roomId, "I am a fish", "txn1").then( client!.sendTextMessage(roomId, "I am a fish", "txn1").then(
function() { function() {
expect(room.timeline[1].getId()).toEqual(eventId);
httpBackend.flush("/sync", 1).then(function() {
expect(room.timeline[1].getId()).toEqual(eventId); 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 " + it("should be updated correctly when the send request finishes " +
"AFTER the event comes down the event stream", function(done) { "AFTER the event comes down the event stream", function(done) {
const eventId = "$foo:bar"; const eventId = "$foo:bar";
httpBackend.when("PUT", "/txn1").respond(200, { httpBackend!.when("PUT", "/txn1").respond(200, {
event_id: eventId, event_id: eventId,
}); });
const ev = utils.mkMessage({ 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.event_id = eventId;
ev.unsigned = { transaction_id: "txn1" }; ev.unsigned = { transaction_id: "txn1" };
setNextSyncData([ev]); setNextSyncData([ev]);
client.on("sync", function(state) { client!.on(ClientEvent.Sync, function(state) {
if (state !== "PREPARED") { if (state !== "PREPARED") {
return; return;
} }
const room = client.getRoom(roomId); const room = client!.getRoom(roomId)!;
const promise = client.sendTextMessage(roomId, "I am a fish", "txn1"); const promise = client!.sendTextMessage(roomId, "I am a fish", "txn1");
httpBackend.flush("/sync", 1).then(function() { httpBackend!.flush("/sync", 1).then(function() {
expect(room.timeline.length).toEqual(2); expect(room.timeline.length).toEqual(2);
httpBackend.flush("/txn1", 1); httpBackend!.flush("/txn1", 1);
promise.then(function() { promise.then(function() {
expect(room.timeline.length).toEqual(2); expect(room.timeline.length).toEqual(2);
expect(room.timeline[1].getId()).toEqual(eventId); 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() { beforeEach(function() {
sbEvents = []; sbEvents = [];
httpBackend.when("GET", "/messages").respond(200, function() { httpBackend!.when("GET", "/messages").respond(200, function() {
return { return {
chunk: sbEvents, chunk: sbEvents,
start: "pagin_start", start: "pagin_start",
@ -240,26 +253,26 @@ describe("MatrixClient room timelines", function() {
it("should set Room.oldState.paginationToken to null at the start" + it("should set Room.oldState.paginationToken to null at the start" +
" of the timeline.", function(done) { " of the timeline.", function(done) {
client.on("sync", function(state) { client!.on(ClientEvent.Sync, function(state) {
if (state !== "PREPARED") { if (state !== "PREPARED") {
return; return;
} }
const room = client.getRoom(roomId); const room = client!.getRoom(roomId)!;
expect(room.timeline.length).toEqual(1); expect(room.timeline.length).toEqual(1);
client.scrollback(room).then(function() { client!.scrollback(room).then(function() {
expect(room.timeline.length).toEqual(1); expect(room.timeline.length).toEqual(1);
expect(room.oldState.paginationToken).toBe(null); expect(room.oldState.paginationToken).toBe(null);
// still have a sync to flush // still have a sync to flush
httpBackend.flush("/sync", 1).then(() => { httpBackend!.flush("/sync", 1).then(() => {
done(); 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) { 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 // make an m.room.member event for alice's join
const joinMshipEvent = utils.mkMembership({ const joinMshipEvent = utils.mkMembership({
mship: "join", user: userId, room: roomId, name: "Old Alice", 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 // 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 = { oldMshipEvent.prev_content = {
displayname: "Old Alice", displayname: "Old Alice",
avatar_url: null, avatar_url: undefined,
membership: "join", membership: "join",
}; };
@ -303,15 +316,15 @@ describe("MatrixClient room timelines", function() {
joinMshipEvent, joinMshipEvent,
]; ];
client.on("sync", function(state) { client!.on(ClientEvent.Sync, function(state) {
if (state !== "PREPARED") { if (state !== "PREPARED") {
return; return;
} }
const room = client.getRoom(roomId); const room = client!.getRoom(roomId)!;
// sync response // sync response
expect(room.timeline.length).toEqual(1); expect(room.timeline.length).toEqual(1);
client.scrollback(room).then(function() { client!.scrollback(room).then(function() {
expect(room.timeline.length).toEqual(5); expect(room.timeline.length).toEqual(5);
const joinMsg = room.timeline[0]; const joinMsg = room.timeline[0];
expect(joinMsg.sender.name).toEqual("Old Alice"); expect(joinMsg.sender.name).toEqual("Old Alice");
@ -321,14 +334,14 @@ describe("MatrixClient room timelines", function() {
expect(newMsg.sender.name).toEqual(userName); expect(newMsg.sender.name).toEqual(userName);
// still have a sync to flush // still have a sync to flush
httpBackend.flush("/sync", 1).then(() => { httpBackend!.flush("/sync", 1).then(() => {
done(); 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) { 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") { if (state !== "PREPARED") {
return; return;
} }
const room = client.getRoom(roomId); const room = client!.getRoom(roomId)!;
expect(room.timeline.length).toEqual(1); 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.length).toEqual(3);
expect(room.timeline[0].event).toEqual(sbEvents[1]); expect(room.timeline[0].event).toEqual(sbEvents[1]);
expect(room.timeline[1].event).toEqual(sbEvents[0]); expect(room.timeline[1].event).toEqual(sbEvents[0]);
// still have a sync to flush // still have a sync to flush
httpBackend.flush("/sync", 1).then(() => { httpBackend!.flush("/sync", 1).then(() => {
done(); 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) { 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") { if (state !== "PREPARED") {
return; return;
} }
const room = client.getRoom(roomId); const room = client!.getRoom(roomId)!;
expect(room.oldState.paginationToken).toBeTruthy(); expect(room.oldState.paginationToken).toBeTruthy();
client.scrollback(room, 1).then(function() { client!.scrollback(room, 1).then(function() {
expect(room.oldState.paginationToken).toEqual(sbEndTok); expect(room.oldState.paginationToken).toEqual(sbEndTok);
}); });
httpBackend.flush("/messages", 1).then(function() { httpBackend!.flush("/messages", 1).then(function() {
// still have a sync to flush // still have a sync to flush
httpBackend.flush("/sync", 1).then(() => { httpBackend!.flush("/sync", 1).then(() => {
done(); done();
}); });
}); });
}); });
httpBackend.flush("/sync", 1); httpBackend!.flush("/sync", 1);
}); });
}); });
@ -404,23 +417,23 @@ describe("MatrixClient room timelines", function() {
setNextSyncData(eventData); setNextSyncData(eventData);
return Promise.all([ return Promise.all([
httpBackend.flush("/sync", 1), httpBackend!.flush("/sync", 1),
utils.syncPromise(client), utils.syncPromise(client!),
]).then(() => { ]).then(() => {
const room = client.getRoom(roomId); const room = client!.getRoom(roomId)!;
let index = 0; let index = 0;
client.on("Room.timeline", function(event, rm, toStart) { client!.on(RoomEvent.Timeline, function(event, rm, toStart) {
expect(toStart).toBe(false); expect(toStart).toBe(false);
expect(rm).toEqual(room); expect(rm).toEqual(room);
expect(event.event).toEqual(eventData[index]); expect(event.event).toEqual(eventData[index]);
index += 1; index += 1;
}); });
httpBackend.flush("/messages", 1); httpBackend!.flush("/messages", 1);
return Promise.all([ return Promise.all([
httpBackend.flush("/sync", 1), httpBackend!.flush("/sync", 1),
utils.syncPromise(client), utils.syncPromise(client!),
]).then(function() { ]).then(function() {
expect(index).toEqual(2); expect(index).toEqual(2);
expect(room.timeline.length).toEqual(3); expect(room.timeline.length).toEqual(3);
@ -442,17 +455,16 @@ describe("MatrixClient room timelines", function() {
}), }),
utils.mkMessage({ user: userId, room: roomId }), utils.mkMessage({ user: userId, room: roomId }),
]; ];
eventData[1].__prev_event = USER_MEMBERSHIP_EVENT;
setNextSyncData(eventData); setNextSyncData(eventData);
return Promise.all([ return Promise.all([
httpBackend.flush("/sync", 1), httpBackend!.flush("/sync", 1),
utils.syncPromise(client), utils.syncPromise(client!),
]).then(() => { ]).then(() => {
const room = client.getRoom(roomId); const room = client!.getRoom(roomId)!;
return Promise.all([ return Promise.all([
httpBackend.flush("/sync", 1), httpBackend!.flush("/sync", 1),
utils.syncPromise(client), utils.syncPromise(client!),
]).then(function() { ]).then(function() {
const preNameEvent = room.timeline[room.timeline.length - 3]; const preNameEvent = room.timeline[room.timeline.length - 3];
const postNameEvent = room.timeline[room.timeline.length - 1]; const postNameEvent = room.timeline[room.timeline.length - 1];
@ -468,22 +480,21 @@ describe("MatrixClient room timelines", function() {
name: "Room 2", name: "Room 2",
}, },
}); });
secondRoomNameEvent.__prev_event = ROOM_NAME_EVENT;
setNextSyncData([secondRoomNameEvent]); setNextSyncData([secondRoomNameEvent]);
return Promise.all([ return Promise.all([
httpBackend.flush("/sync", 1), httpBackend!.flush("/sync", 1),
utils.syncPromise(client), utils.syncPromise(client!),
]).then(() => { ]).then(() => {
const room = client.getRoom(roomId); const room = client!.getRoom(roomId)!;
let nameEmitCount = 0; let nameEmitCount = 0;
client.on("Room.name", function(rm) { client!.on(RoomEvent.Name, function(rm) {
nameEmitCount += 1; nameEmitCount += 1;
}); });
return Promise.all([ return Promise.all([
httpBackend.flush("/sync", 1), httpBackend!.flush("/sync", 1),
utils.syncPromise(client), utils.syncPromise(client!),
]).then(function() { ]).then(function() {
expect(nameEmitCount).toEqual(1); expect(nameEmitCount).toEqual(1);
expect(room.name).toEqual("Room 2"); expect(room.name).toEqual("Room 2");
@ -493,12 +504,11 @@ describe("MatrixClient room timelines", function() {
name: "Room 3", name: "Room 3",
}, },
}); });
thirdRoomNameEvent.__prev_event = secondRoomNameEvent;
setNextSyncData([thirdRoomNameEvent]); setNextSyncData([thirdRoomNameEvent]);
httpBackend.when("GET", "/sync").respond(200, NEXT_SYNC_DATA); httpBackend!.when("GET", "/sync").respond(200, NEXT_SYNC_DATA);
return Promise.all([ return Promise.all([
httpBackend.flush("/sync", 1), httpBackend!.flush("/sync", 1),
utils.syncPromise(client), utils.syncPromise(client!),
]); ]);
}).then(function() { }).then(function() {
expect(nameEmitCount).toEqual(2); expect(nameEmitCount).toEqual(2);
@ -518,26 +528,24 @@ describe("MatrixClient room timelines", function() {
user: userC, room: roomId, mship: "invite", skey: userD, user: userC, room: roomId, mship: "invite", skey: userD,
}), }),
]; ];
eventData[0].__prev_event = null;
eventData[1].__prev_event = null;
setNextSyncData(eventData); setNextSyncData(eventData);
return Promise.all([ return Promise.all([
httpBackend.flush("/sync", 1), httpBackend!.flush("/sync", 1),
utils.syncPromise(client), utils.syncPromise(client!),
]).then(() => { ]).then(() => {
const room = client.getRoom(roomId); const room = client!.getRoom(roomId)!;
return Promise.all([ return Promise.all([
httpBackend.flush("/sync", 1), httpBackend!.flush("/sync", 1),
utils.syncPromise(client), utils.syncPromise(client!),
]).then(function() { ]).then(function() {
expect(room.currentState.getMembers().length).toEqual(4); expect(room.currentState.getMembers().length).toEqual(4);
expect(room.currentState.getMember(userC).name).toEqual("C"); expect(room.currentState.getMember(userC)!.name).toEqual("C");
expect(room.currentState.getMember(userC).membership).toEqual( expect(room.currentState.getMember(userC)!.membership).toEqual(
"join", "join",
); );
expect(room.currentState.getMember(userD).name).toEqual(userD); expect(room.currentState.getMember(userD)!.name).toEqual(userD);
expect(room.currentState.getMember(userD).membership).toEqual( expect(room.currentState.getMember(userD)!.membership).toEqual(
"invite", "invite",
); );
}); });
@ -554,26 +562,26 @@ describe("MatrixClient room timelines", function() {
NEXT_SYNC_DATA.rooms.join[roomId].timeline.limited = true; NEXT_SYNC_DATA.rooms.join[roomId].timeline.limited = true;
return Promise.all([ return Promise.all([
httpBackend.flush("/versions", 1), httpBackend!.flush("/versions", 1),
httpBackend.flush("/sync", 1), httpBackend!.flush("/sync", 1),
utils.syncPromise(client), utils.syncPromise(client!),
]).then(() => { ]).then(() => {
const room = client.getRoom(roomId); const room = client!.getRoom(roomId)!;
httpBackend.flush("/messages", 1); httpBackend!.flush("/messages", 1);
return Promise.all([ return Promise.all([
httpBackend.flush("/sync", 1), httpBackend!.flush("/sync", 1),
utils.syncPromise(client), utils.syncPromise(client!),
]).then(function() { ]).then(function() {
expect(room.timeline.length).toEqual(1); expect(room.timeline.length).toEqual(1);
expect(room.timeline[0].event).toEqual(eventData[0]); expect(room.timeline[0].event).toEqual(eventData[0]);
expect(room.currentState.getMembers().length).toEqual(2); expect(room.currentState.getMembers().length).toEqual(2);
expect(room.currentState.getMember(userId).name).toEqual(userName); expect(room.currentState.getMember(userId)!.name).toEqual(userName);
expect(room.currentState.getMember(userId).membership).toEqual( expect(room.currentState.getMember(userId)!.membership).toEqual(
"join", "join",
); );
expect(room.currentState.getMember(otherUserId).name).toEqual("Bob"); expect(room.currentState.getMember(otherUserId)!.name).toEqual("Bob");
expect(room.currentState.getMember(otherUserId).membership).toEqual( expect(room.currentState.getMember(otherUserId)!.membership).toEqual(
"join", "join",
); );
}); });
@ -588,21 +596,21 @@ describe("MatrixClient room timelines", function() {
NEXT_SYNC_DATA.rooms.join[roomId].timeline.limited = true; NEXT_SYNC_DATA.rooms.join[roomId].timeline.limited = true;
return Promise.all([ return Promise.all([
httpBackend.flush("/sync", 1), httpBackend!.flush("/sync", 1),
utils.syncPromise(client), utils.syncPromise(client!),
]).then(() => { ]).then(() => {
const room = client.getRoom(roomId); const room = client!.getRoom(roomId)!;
let emitCount = 0; let emitCount = 0;
client.on("Room.timelineReset", function(emitRoom) { client!.on(RoomEvent.TimelineReset, function(emitRoom) {
expect(emitRoom).toEqual(room); expect(emitRoom).toEqual(room);
emitCount++; emitCount++;
}); });
httpBackend.flush("/messages", 1); httpBackend!.flush("/messages", 1);
return Promise.all([ return Promise.all([
httpBackend.flush("/sync", 1), httpBackend!.flush("/sync", 1),
utils.syncPromise(client), utils.syncPromise(client!),
]).then(function() { ]).then(function() {
expect(emitCount).toEqual(1); expect(emitCount).toEqual(1);
}); });
@ -618,7 +626,7 @@ describe("MatrixClient room timelines", function() {
]; ];
const contextUrl = `/rooms/${encodeURIComponent(roomId)}/context/` + const contextUrl = `/rooms/${encodeURIComponent(roomId)}/context/` +
`${encodeURIComponent(initialSyncEventData[2].event_id)}`; `${encodeURIComponent(initialSyncEventData[2].event_id!)}`;
const contextResponse = { const contextResponse = {
start: "start_token", start: "start_token",
events_before: [initialSyncEventData[1], initialSyncEventData[0]], events_before: [initialSyncEventData[1], initialSyncEventData[0]],
@ -636,19 +644,19 @@ describe("MatrixClient room timelines", function() {
// Create a room from the sync // Create a room from the sync
await Promise.all([ await Promise.all([
httpBackend.flushAllExpected(), httpBackend!.flushAllExpected(),
utils.syncPromise(client, 1), utils.syncPromise(client!, 1),
]); ]);
// Get the room after the first sync so the room is created // Get the room after the first sync so the room is created
room = client.getRoom(roomId); room = client!.getRoom(roomId)!;
expect(room).toBeTruthy(); expect(room).toBeTruthy();
}); });
it('should clear and refresh messages in timeline', async () => { it('should clear and refresh messages in timeline', async () => {
// `/context` request for `refreshLiveTimeline()` -> `getEventTimeline()` // `/context` request for `refreshLiveTimeline()` -> `getEventTimeline()`
// to construct a new timeline from. // to construct a new timeline from.
httpBackend.when("GET", contextUrl) httpBackend!.when("GET", contextUrl)
.respond(200, function() { .respond(200, function() {
// The timeline should be cleared at this point in the refresh // The timeline should be cleared at this point in the refresh
expect(room.timeline.length).toEqual(0); expect(room.timeline.length).toEqual(0);
@ -659,7 +667,7 @@ describe("MatrixClient room timelines", function() {
// Refresh the timeline. // Refresh the timeline.
await Promise.all([ await Promise.all([
room.refreshLiveTimeline(), room.refreshLiveTimeline(),
httpBackend.flushAllExpected(), httpBackend!.flushAllExpected(),
]); ]);
// Make sure the message are visible // 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 // middle of all of this refresh timeline logic. We want to make
// sure the sync pagination still works as expected after messing // sure the sync pagination still works as expected after messing
// the refresh timline logic messes with the pagination tokens. // the refresh timline logic messes with the pagination tokens.
httpBackend.when("GET", contextUrl) httpBackend!.when("GET", contextUrl)
.respond(200, () => { .respond(200, () => {
// Now finally return and make the `/context` request respond // Now finally return and make the `/context` request respond
return contextResponse; return contextResponse;
@ -700,7 +708,7 @@ describe("MatrixClient room timelines", function() {
const racingSyncEventData = [ const racingSyncEventData = [
utils.mkMessage({ user: userId, room: roomId }), utils.mkMessage({ user: userId, room: roomId }),
]; ];
const waitForRaceySyncAfterResetPromise = new Promise((resolve, reject) => { const waitForRaceySyncAfterResetPromise = new Promise<void>((resolve, reject) => {
let eventFired = false; let eventFired = false;
// Throw a more descriptive error if this part of the test times out. // Throw a more descriptive error if this part of the test times out.
const failTimeout = setTimeout(() => { 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 // Then make a `/sync` happen by sending a message and seeing that it
// shows up (simulate a /sync naturally racing with us). // shows up (simulate a /sync naturally racing with us).
setNextSyncData(racingSyncEventData); setNextSyncData(racingSyncEventData);
httpBackend.when("GET", "/sync").respond(200, function() { httpBackend!.when("GET", "/sync").respond(200, function() {
return NEXT_SYNC_DATA; return NEXT_SYNC_DATA;
}); });
await Promise.all([ await Promise.all([
httpBackend.flush("/sync", 1), httpBackend!.flush("/sync", 1),
utils.syncPromise(client, 1), utils.syncPromise(client!, 1),
]); ]);
// Make sure the timeline has the racey sync data // Make sure the timeline has the racey sync data
const afterRaceySyncTimelineEvents = room const afterRaceySyncTimelineEvents = room
@ -761,7 +769,7 @@ describe("MatrixClient room timelines", function() {
await Promise.all([ await Promise.all([
refreshLiveTimelinePromise, refreshLiveTimelinePromise,
// Then flush the remaining `/context` to left the refresh logic complete // 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 // 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 }), utils.mkMessage({ user: userId, room: roomId }),
]; ];
setNextSyncData(afterRefreshEventData); setNextSyncData(afterRefreshEventData);
httpBackend.when("GET", "/sync").respond(200, function() { httpBackend!.when("GET", "/sync").respond(200, function() {
return NEXT_SYNC_DATA; return NEXT_SYNC_DATA;
}); });
await Promise.all([ await Promise.all([
httpBackend.flushAllExpected(), httpBackend!.flushAllExpected(),
utils.syncPromise(client, 1), utils.syncPromise(client!, 1),
]); ]);
// Make sure the timeline includes the the events from the `/sync` // 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 () => { it('Timeline recovers after `/context` request to generate new timeline fails', async () => {
// `/context` request for `refreshLiveTimeline()` -> `getEventTimeline()` // `/context` request for `refreshLiveTimeline()` -> `getEventTimeline()`
// to construct a new timeline from. // to construct a new timeline from.
httpBackend.when("GET", contextUrl) httpBackend!.when("GET", contextUrl)
.respond(500, function() { .respond(500, function() {
// The timeline should be cleared at this point in the refresh // The timeline should be cleared at this point in the refresh
expect(room.timeline.length).toEqual(0); expect(room.timeline.length).toEqual(0);
@ -809,7 +817,7 @@ describe("MatrixClient room timelines", function() {
// Refresh the timeline and expect it to fail // Refresh the timeline and expect it to fail
const settledFailedRefreshPromises = await Promise.allSettled([ const settledFailedRefreshPromises = await Promise.allSettled([
room.refreshLiveTimeline(), room.refreshLiveTimeline(),
httpBackend.flushAllExpected(), httpBackend!.flushAllExpected(),
]); ]);
// We only expect `TEST_FAKE_ERROR` here. Anything else is // We only expect `TEST_FAKE_ERROR` here. Anything else is
// unexpected and should fail the test. // unexpected and should fail the test.
@ -825,7 +833,7 @@ describe("MatrixClient room timelines", function() {
// `/messages` request for `refreshLiveTimeline()` -> // `/messages` request for `refreshLiveTimeline()` ->
// `getLatestTimeline()` to construct a new timeline from. // `getLatestTimeline()` to construct a new timeline from.
httpBackend.when("GET", `/rooms/${encodeURIComponent(roomId)}/messages`) httpBackend!.when("GET", `/rooms/${encodeURIComponent(roomId)}/messages`)
.respond(200, function() { .respond(200, function() {
return { return {
chunk: [{ chunk: [{
@ -837,7 +845,7 @@ describe("MatrixClient room timelines", function() {
// `/context` request for `refreshLiveTimeline()` -> // `/context` request for `refreshLiveTimeline()` ->
// `getLatestTimeline()` -> `getEventTimeline()` to construct a new // `getLatestTimeline()` -> `getEventTimeline()` to construct a new
// timeline from. // timeline from.
httpBackend.when("GET", contextUrl) httpBackend!.when("GET", contextUrl)
.respond(200, function() { .respond(200, function() {
// The timeline should be cleared at this point in the refresh // The timeline should be cleared at this point in the refresh
expect(room.timeline.length).toEqual(0); 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 // Refresh the timeline again but this time it should pass
await Promise.all([ await Promise.all([
room.refreshLiveTimeline(), room.refreshLiveTimeline(),
httpBackend.flushAllExpected(), httpBackend!.flushAllExpected(),
]); ]);
// Make sure sync pagination still works by seeing a new message show up // 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 }), utils.mkMessage({ user: userId, room: roomId }),
]; ];
setNextSyncData(afterRefreshEventData); setNextSyncData(afterRefreshEventData);
httpBackend.when("GET", "/sync").respond(200, function() { httpBackend!.when("GET", "/sync").respond(200, function() {
return NEXT_SYNC_DATA; return NEXT_SYNC_DATA;
}); });
await Promise.all([ await Promise.all([
httpBackend.flushAllExpected(), httpBackend!.flushAllExpected(),
utils.syncPromise(client, 1), utils.syncPromise(client!, 1),
]); ]);
// Make sure the message are visible // Make sure the message are visible

View File

@ -16,7 +16,6 @@ limitations under the License.
import 'fake-indexeddb/auto'; import 'fake-indexeddb/auto';
import { Optional } from "matrix-events-sdk/lib/types";
import HttpBackend from "matrix-mock-request"; import HttpBackend from "matrix-mock-request";
import { import {
@ -29,6 +28,11 @@ import {
MatrixClient, MatrixClient,
ClientEvent, ClientEvent,
IndexedDBCryptoStore, IndexedDBCryptoStore,
ISyncResponse,
IRoomEvent,
IJoinedRoom,
IStateEvent,
IMinimalEvent,
NotificationCountType, NotificationCountType,
} from "../../src"; } from "../../src";
import { UNREAD_THREAD_NOTIFICATIONS } from '../../src/@types/sync'; import { UNREAD_THREAD_NOTIFICATIONS } from '../../src/@types/sync';
@ -36,8 +40,6 @@ import * as utils from "../test-utils/test-utils";
import { TestClient } from "../TestClient"; import { TestClient } from "../TestClient";
describe("MatrixClient syncing", () => { describe("MatrixClient syncing", () => {
let client: Optional<MatrixClient> = null;
let httpBackend: Optional<HttpBackend> = null;
const selfUserId = "@alice:localhost"; const selfUserId = "@alice:localhost";
const selfAccessToken = "aseukfgwef"; const selfAccessToken = "aseukfgwef";
const otherUserId = "@bob:localhost"; const otherUserId = "@bob:localhost";
@ -46,14 +48,21 @@ describe("MatrixClient syncing", () => {
const userC = "@claire:bar"; const userC = "@claire:bar";
const roomOne = "!foo:localhost"; const roomOne = "!foo:localhost";
const roomTwo = "!bar: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); const testClient = new TestClient(selfUserId, "DEVICE", selfAccessToken);
httpBackend = testClient.httpBackend; const httpBackend = testClient.httpBackend;
client = testClient.client; const client = testClient.client;
httpBackend!.when("GET", "/versions").respond(200, {}); httpBackend!.when("GET", "/versions").respond(200, {});
httpBackend!.when("GET", "/pushrules").respond(200, {}); httpBackend!.when("GET", "/pushrules").respond(200, {});
httpBackend!.when("POST", "/filter").respond(200, { filter_id: "a filter id" }); httpBackend!.when("POST", "/filter").respond(200, { filter_id: "a filter id" });
return [client, httpBackend];
};
beforeEach(() => {
[client, httpBackend] = setupTestClient();
}); });
afterEach(() => { 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) => { 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").respond(200, syncData);
httpBackend!.when("GET", "/sync").check((req) => { 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); }).respond(200, syncData);
client!.startClient(); client!.startClient();
@ -93,7 +102,7 @@ describe("MatrixClient syncing", () => {
}); });
it("should emit RoomEvent.MyMembership for invite->leave->invite cycles", async () => { it("should emit RoomEvent.MyMembership for invite->leave->invite cycles", async () => {
await client.initCrypto(); await client!.initCrypto();
const roomId = "!cycles:example.org"; const roomId = "!cycles:example.org";
@ -204,7 +213,7 @@ describe("MatrixClient syncing", () => {
client!.doesServerSupportLazyLoading = jest.fn().mockResolvedValue(true); client!.doesServerSupportLazyLoading = jest.fn().mockResolvedValue(true);
httpBackend!.when("GET", "/sync").check((req) => { 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); }).respond(200, syncData);
client!.setGuest(false); client!.setGuest(false);
@ -219,7 +228,7 @@ describe("MatrixClient syncing", () => {
client!.doesServerSupportLazyLoading = jest.fn().mockResolvedValue(true); client!.doesServerSupportLazyLoading = jest.fn().mockResolvedValue(true);
httpBackend!.when("GET", "/sync").check((req) => { 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); }).respond(200, syncData);
client!.setGuest(true); client!.setGuest(true);
@ -277,11 +286,11 @@ describe("MatrixClient syncing", () => {
it("should only apply initialSyncLimit to the initial sync", () => { it("should only apply initialSyncLimit to the initial sync", () => {
// 1st request // 1st request
httpBackend!.when("GET", "/sync").check((req) => { 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); }).respond(200, syncData);
// 2nd request // 2nd request
httpBackend!.when("GET", "/sync").check((req) => { 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); }).respond(200, syncData);
client!.startClient({ initialSyncLimit: 1 }); 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", () => { it("should not apply initialSyncLimit to a first sync if we have a stored token", () => {
httpBackend!.when("GET", "/sync").check((req) => { 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); }).respond(200, syncData);
client!.store.getSavedSyncToken = jest.fn().mockResolvedValue("this-is-a-token"); client!.store.getSavedSyncToken = jest.fn().mockResolvedValue("this-is-a-token");
@ -303,26 +312,29 @@ describe("MatrixClient syncing", () => {
}); });
describe("resolving invites to profile info", () => { describe("resolving invites to profile info", () => {
const syncData = { const syncData: ISyncResponse = {
account_data: {
events: [],
},
next_batch: "s_5_3", next_batch: "s_5_3",
presence: { presence: {
events: [], events: [],
}, },
rooms: { rooms: {
join: { join: {},
invite: {},
}, leave: {},
}, },
}; };
beforeEach(() => { beforeEach(() => {
syncData.presence.events = []; syncData.presence!.events = [];
syncData.rooms.join[roomOne] = { syncData.rooms.join[roomOne] = {
timeline: { timeline: {
events: [ events: [
utils.mkMessage({ utils.mkMessage({
room: roomOne, user: otherUserId, msg: "hello", room: roomOne, user: otherUserId, msg: "hello",
}), }) as IRoomEvent,
], ],
}, },
state: { state: {
@ -341,14 +353,14 @@ describe("MatrixClient syncing", () => {
}), }),
], ],
}, },
}; } as unknown as IJoinedRoom;
}); });
it("should resolve incoming invites from /sync", () => { it("should resolve incoming invites from /sync", () => {
syncData.rooms.join[roomOne].state.events.push( syncData.rooms.join[roomOne].state.events.push(
utils.mkMembership({ utils.mkMembership({
room: roomOne, mship: "invite", user: userC, room: roomOne, mship: "invite", user: userC,
}), }) as IStateEvent,
); );
httpBackend!.when("GET", "/sync").respond(200, syncData); httpBackend!.when("GET", "/sync").respond(200, syncData);
@ -367,26 +379,26 @@ describe("MatrixClient syncing", () => {
httpBackend!.flushAllExpected(), httpBackend!.flushAllExpected(),
awaitSyncEvent(), awaitSyncEvent(),
]).then(() => { ]).then(() => {
const member = client!.getRoom(roomOne).getMember(userC); const member = client!.getRoom(roomOne)!.getMember(userC)!;
expect(member.name).toEqual("The Boss"); expect(member.name).toEqual("The Boss");
expect( expect(
member.getAvatarUrl("home.server.url", null, null, null, false, false), member.getAvatarUrl("home.server.url", 1, 1, '', false, false),
).toBeTruthy(); ).toBeTruthy();
}); });
}); });
it("should use cached values from m.presence wherever possible", () => { it("should use cached values from m.presence wherever possible", () => {
syncData.presence.events = [ syncData.presence!.events = [
utils.mkPresence({ utils.mkPresence({
user: userC, user: userC,
presence: "online", presence: "online",
name: "The Ghost", name: "The Ghost",
}), }) as IMinimalEvent,
]; ];
syncData.rooms.join[roomOne].state.events.push( syncData.rooms.join[roomOne].state.events.push(
utils.mkMembership({ utils.mkMembership({
room: roomOne, mship: "invite", user: userC, room: roomOne, mship: "invite", user: userC,
}), }) as IStateEvent,
); );
httpBackend!.when("GET", "/sync").respond(200, syncData); httpBackend!.when("GET", "/sync").respond(200, syncData);
@ -399,28 +411,28 @@ describe("MatrixClient syncing", () => {
httpBackend!.flushAllExpected(), httpBackend!.flushAllExpected(),
awaitSyncEvent(), awaitSyncEvent(),
]).then(() => { ]).then(() => {
const member = client!.getRoom(roomOne).getMember(userC); const member = client!.getRoom(roomOne)!.getMember(userC)!;
expect(member.name).toEqual("The Ghost"); expect(member.name).toEqual("The Ghost");
}); });
}); });
it("should result in events on the room member firing", () => { it("should result in events on the room member firing", () => {
syncData.presence.events = [ syncData.presence!.events = [
utils.mkPresence({ utils.mkPresence({
user: userC, user: userC,
presence: "online", presence: "online",
name: "The Ghost", name: "The Ghost",
}), }) as IMinimalEvent,
]; ];
syncData.rooms.join[roomOne].state.events.push( syncData.rooms.join[roomOne].state.events.push(
utils.mkMembership({ utils.mkMembership({
room: roomOne, mship: "invite", user: userC, room: roomOne, mship: "invite", user: userC,
}), }) as IStateEvent,
); );
httpBackend!.when("GET", "/sync").respond(200, syncData); httpBackend!.when("GET", "/sync").respond(200, syncData);
let latestFiredName = null; let latestFiredName: string;
client!.on(RoomMemberEvent.Name, (event, m) => { client!.on(RoomMemberEvent.Name, (event, m) => {
if (m.userId === userC && m.roomId === roomOne) { if (m.userId === userC && m.roomId === roomOne) {
latestFiredName = m.name; latestFiredName = m.name;
@ -443,7 +455,7 @@ describe("MatrixClient syncing", () => {
syncData.rooms.join[roomOne].state.events.push( syncData.rooms.join[roomOne].state.events.push(
utils.mkMembership({ utils.mkMembership({
room: roomOne, mship: "invite", user: userC, room: roomOne, mship: "invite", user: userC,
}), }) as IStateEvent,
); );
httpBackend!.when("GET", "/sync").respond(200, syncData); httpBackend!.when("GET", "/sync").respond(200, syncData);
@ -454,10 +466,10 @@ describe("MatrixClient syncing", () => {
httpBackend!.flushAllExpected(), httpBackend!.flushAllExpected(),
awaitSyncEvent(), awaitSyncEvent(),
]).then(() => { ]).then(() => {
const member = client!.getRoom(roomOne).getMember(userC); const member = client!.getRoom(roomOne)!.getMember(userC)!;
expect(member.name).toEqual(userC); expect(member.name).toEqual(userC);
expect( expect(
member.getAvatarUrl("home.server.url", null, null, null, false, false), member.getAvatarUrl("home.server.url", 1, 1, '', false, false),
).toBe(null); ).toBe(null);
}); });
}); });
@ -489,8 +501,8 @@ describe("MatrixClient syncing", () => {
httpBackend!.flushAllExpected(), httpBackend!.flushAllExpected(),
awaitSyncEvent(), awaitSyncEvent(),
]).then(() => { ]).then(() => {
expect(client!.getUser(userA).presence).toEqual("online"); expect(client!.getUser(userA)!.presence).toEqual("online");
expect(client!.getUser(userB).presence).toEqual("unavailable"); expect(client!.getUser(userB)!.presence).toEqual("unavailable");
}); });
}); });
}); });
@ -611,7 +623,7 @@ describe("MatrixClient syncing", () => {
httpBackend!.flushAllExpected(), httpBackend!.flushAllExpected(),
awaitSyncEvent(2), awaitSyncEvent(2),
]).then(() => { ]).then(() => {
const room = client!.getRoom(roomOne); const room = client!.getRoom(roomOne)!;
// should have clobbered the name to the one from /events // should have clobbered the name to the one from /events
expect(room.name).toEqual( expect(room.name).toEqual(
nextSyncData.rooms.join[roomOne].state.events[0].content.name, nextSyncData.rooms.join[roomOne].state.events[0].content.name,
@ -629,7 +641,7 @@ describe("MatrixClient syncing", () => {
httpBackend!.flushAllExpected(), httpBackend!.flushAllExpected(),
awaitSyncEvent(2), awaitSyncEvent(2),
]).then(() => { ]).then(() => {
const room = client!.getRoom(roomTwo); const room = client!.getRoom(roomTwo)!;
// should have added the message from /events // should have added the message from /events
expect(room.timeline.length).toEqual(2); expect(room.timeline.length).toEqual(2);
expect(room.timeline[1].getContent().body).toEqual(msgText); expect(room.timeline[1].getContent().body).toEqual(msgText);
@ -645,7 +657,7 @@ describe("MatrixClient syncing", () => {
httpBackend!.flushAllExpected(), httpBackend!.flushAllExpected(),
awaitSyncEvent(2), awaitSyncEvent(2),
]).then(() => { ]).then(() => {
const room = client!.getRoom(roomTwo); const room = client!.getRoom(roomTwo)!;
// should use the display name of the other person. // should use the display name of the other person.
expect(room.name).toEqual(otherDisplayName); expect(room.name).toEqual(otherDisplayName);
}); });
@ -661,11 +673,11 @@ describe("MatrixClient syncing", () => {
httpBackend!.flushAllExpected(), httpBackend!.flushAllExpected(),
awaitSyncEvent(2), awaitSyncEvent(2),
]).then(() => { ]).then(() => {
const room = client!.getRoom(roomTwo); const room = client!.getRoom(roomTwo)!;
let member = room.getMember(otherUserId); let member = room.getMember(otherUserId)!;
expect(member).toBeTruthy(); expect(member).toBeTruthy();
expect(member.typing).toEqual(true); expect(member.typing).toEqual(true);
member = room.getMember(selfUserId); member = room.getMember(selfUserId)!;
expect(member).toBeTruthy(); expect(member).toBeTruthy();
expect(member.typing).toEqual(false); expect(member.typing).toEqual(false);
}); });
@ -684,7 +696,7 @@ describe("MatrixClient syncing", () => {
httpBackend!.flushAllExpected(), httpBackend!.flushAllExpected(),
awaitSyncEvent(2), awaitSyncEvent(2),
]).then(() => { ]).then(() => {
const room = client!.getRoom(roomOne); const room = client!.getRoom(roomOne)!;
const stateAtStart = room.getLiveTimeline().getState( const stateAtStart = room.getLiveTimeline().getState(
EventTimeline.BACKWARDS, EventTimeline.BACKWARDS,
); );
@ -782,7 +794,7 @@ describe("MatrixClient syncing", () => {
awaitSyncEvent(2), awaitSyncEvent(2),
]); ]);
const room = client!.getRoom(roomOne); const room = client!.getRoom(roomOne)!;
expect(room.getTimelineNeedsRefresh()).toEqual(false); expect(room.getTimelineNeedsRefresh()).toEqual(false);
}); });
@ -852,7 +864,7 @@ describe("MatrixClient syncing", () => {
awaitSyncEvent(), awaitSyncEvent(),
]); ]);
const room = client!.getRoom(roomOne); const room = client!.getRoom(roomOne)!;
expect(room.getTimelineNeedsRefresh()).toEqual(false); expect(room.getTimelineNeedsRefresh()).toEqual(false);
}); });
@ -882,7 +894,7 @@ describe("MatrixClient syncing", () => {
awaitSyncEvent(), awaitSyncEvent(),
]); ]);
const room = client!.getRoom(roomOne); const room = client!.getRoom(roomOne)!;
expect(room.getTimelineNeedsRefresh()).toEqual(false); expect(room.getTimelineNeedsRefresh()).toEqual(false);
}); });
@ -915,7 +927,7 @@ describe("MatrixClient syncing", () => {
awaitSyncEvent(), awaitSyncEvent(),
]); ]);
const room = client!.getRoom(roomOne); const room = client!.getRoom(roomOne)!;
expect(room.getTimelineNeedsRefresh()).toEqual(false); expect(room.getTimelineNeedsRefresh()).toEqual(false);
}); });
@ -949,7 +961,7 @@ describe("MatrixClient syncing", () => {
]); ]);
// Get the room after the first sync so the room is created // 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; let emitCount = 0;
room.on(RoomEvent.HistoryImportedWithinTimeline, (markerEvent, room) => { room.on(RoomEvent.HistoryImportedWithinTimeline, (markerEvent, room) => {
@ -1005,7 +1017,7 @@ describe("MatrixClient syncing", () => {
awaitSyncEvent(2), awaitSyncEvent(2),
]); ]);
const room = client!.getRoom(roomOne); const room = client!.getRoom(roomOne)!;
expect(room.getTimelineNeedsRefresh()).toEqual(true); expect(room.getTimelineNeedsRefresh()).toEqual(true);
}); });
}); });
@ -1060,7 +1072,7 @@ describe("MatrixClient syncing", () => {
]); ]);
// Get the room after the first sync so the room is created // 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(); expect(room).toBeTruthy();
let stateEventEmitCount = 0; let stateEventEmitCount = 0;
@ -1134,7 +1146,7 @@ describe("MatrixClient syncing", () => {
]); ]);
// Get the room after the first sync so the room is created // 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(); expect(room).toBeTruthy();
let stateEventEmitCount = 0; let stateEventEmitCount = 0;
@ -1231,7 +1243,7 @@ describe("MatrixClient syncing", () => {
httpBackend!.flushAllExpected(), httpBackend!.flushAllExpected(),
awaitSyncEvent(), awaitSyncEvent(),
]).then(() => { ]).then(() => {
const room = client!.getRoom(roomTwo); const room = client!.getRoom(roomTwo)!;
expect(room).toBeTruthy(); expect(room).toBeTruthy();
const tok = room.getLiveTimeline() const tok = room.getLiveTimeline()
.getPaginationToken(EventTimeline.BACKWARDS); .getPaginationToken(EventTimeline.BACKWARDS);
@ -1274,7 +1286,7 @@ describe("MatrixClient syncing", () => {
httpBackend!.flushAllExpected(), httpBackend!.flushAllExpected(),
awaitSyncEvent(), awaitSyncEvent(),
]).then(() => { ]).then(() => {
const room = client!.getRoom(roomOne); const room = client!.getRoom(roomOne)!;
const tl = room.getLiveTimeline(); const tl = room.getLiveTimeline();
expect(tl.getEvents().length).toEqual(1); expect(tl.getEvents().length).toEqual(1);
expect(resetCallCount).toEqual(1); expect(resetCallCount).toEqual(1);
@ -1353,7 +1365,7 @@ describe("MatrixClient syncing", () => {
httpBackend!.flushAllExpected(), httpBackend!.flushAllExpected(),
awaitSyncEvent(), awaitSyncEvent(),
]).then(() => { ]).then(() => {
const room = client!.getRoom(roomOne); const room = client!.getRoom(roomOne)!;
expect(room.getReceiptsForEvent(new MatrixEvent(ackEvent))).toEqual([{ expect(room.getReceiptsForEvent(new MatrixEvent(ackEvent))).toEqual([{
type: "m.read", type: "m.read",
userId: userC, userId: userC,
@ -1426,8 +1438,8 @@ describe("MatrixClient syncing", () => {
]).then(() => { ]).then(() => {
const room = client!.getRoom(roomOne); const room = client!.getRoom(roomOne);
expect(room.getThreadUnreadNotificationCount(THREAD_ID, NotificationCountType.Total)).toBe(5); expect(room!.getThreadUnreadNotificationCount(THREAD_ID, NotificationCountType.Total)).toBe(5);
expect(room.getThreadUnreadNotificationCount(THREAD_ID, NotificationCountType.Highlight)).toBe(2); expect(room!.getThreadUnreadNotificationCount(THREAD_ID, NotificationCountType.Highlight)).toBe(2);
}); });
}); });
}); });
@ -1469,7 +1481,7 @@ describe("MatrixClient syncing", () => {
const prom = new Promise<void>((resolve) => { const prom = new Promise<void>((resolve) => {
httpBackend!.when("GET", "/sync").check((req) => { httpBackend!.when("GET", "/sync").check((req) => {
expect(req.queryParams.filter).toEqual("another_id"); expect(req.queryParams!.filter).toEqual("another_id");
resolve(); resolve();
}).respond(200, {}); }).respond(200, {});
}); });
@ -1514,7 +1526,7 @@ describe("MatrixClient syncing", () => {
return Promise.all([ return Promise.all([
client!.syncLeftRooms().then(() => { client!.syncLeftRooms().then(() => {
const room = client!.getRoom(roomTwo); const room = client!.getRoom(roomTwo)!;
const tok = room.getLiveTimeline().getPaginationToken( const tok = room.getLiveTimeline().getPaginationToken(
EventTimeline.BACKWARDS); EventTimeline.BACKWARDS);
@ -1536,7 +1548,7 @@ describe("MatrixClient syncing", () => {
* @returns {Promise} promise which resolves after the sync events have happened * @returns {Promise} promise which resolves after the sync events have happened
*/ */
function awaitSyncEvent(numSyncs?: number) { function awaitSyncEvent(numSyncs?: number) {
return utils.syncPromise(client, numSyncs); return utils.syncPromise(client!, numSyncs);
} }
}); });

View File

@ -95,26 +95,31 @@ describe("megolm key backups", function() {
return; return;
} }
const Olm = global.Olm; const Olm = global.Olm;
let testOlmAccount: Olm.Account;
let testOlmAccount: Account;
let aliceTestClient: TestClient; 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() { beforeAll(function() {
return Olm.init(); return Olm.init();
}); });
beforeEach(async function() { beforeEach(async function() {
aliceTestClient = new TestClient( [testOlmAccount, aliceTestClient] = setupTestClient();
"@alice:localhost", "xzcvb", "akjgkrgjs", await aliceTestClient!.client.initCrypto();
); aliceTestClient!.client.crypto!.backupManager.backupInfo = CURVE25519_BACKUP_INFO;
testOlmAccount = new Olm.Account();
testOlmAccount.create();
await aliceTestClient.client.initCrypto();
aliceTestClient.client.crypto.backupManager.backupInfo = CURVE25519_BACKUP_INFO;
}); });
afterEach(function() { afterEach(function() {
return aliceTestClient.stop(); return aliceTestClient!.stop();
}); });
it("Alice checks key backups when receiving a message she can't decrypt", function() { 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); return createOlmSession(testOlmAccount, aliceTestClient);
}).then(() => { }).then(() => {
const privkey = decodeRecoveryKey(RECOVERY_KEY); const privkey = decodeRecoveryKey(RECOVERY_KEY);
return aliceTestClient.client.crypto.storeSessionBackupPrivateKey(privkey); return aliceTestClient!.client!.crypto!.storeSessionBackupPrivateKey(privkey);
}).then(() => { }).then(() => {
aliceTestClient.httpBackend.when("GET", "/sync").respond(200, syncResponse); aliceTestClient!.httpBackend.when("GET", "/sync").respond(200, syncResponse);
aliceTestClient.expectKeyBackupQuery( aliceTestClient!.expectKeyBackupQuery(
ROOM_ID, ROOM_ID,
SESSION_ID, SESSION_ID,
200, 200,
CURVE25519_KEY_BACKUP_DATA, CURVE25519_KEY_BACKUP_DATA,
); );
return aliceTestClient.httpBackend.flushAllExpected(); return aliceTestClient!.httpBackend.flushAllExpected();
}).then(function(): Promise<MatrixEvent> { }).then(function(): Promise<MatrixEvent> {
const room = aliceTestClient.client.getRoom(ROOM_ID); const room = aliceTestClient!.client.getRoom(ROOM_ID)!;
const event = room.getLiveTimeline().getEvents()[0]; const event = room.getLiveTimeline().getEvents()[0];
if (event.getContent()) { if (event.getContent()) {

View File

@ -207,9 +207,11 @@ describe("megolm", () => {
} }
const Olm = global.Olm; const Olm = global.Olm;
let testOlmAccount: Olm.Account; let testOlmAccount = {} as unknown as Olm.Account;
let testSenderKey: string; let testSenderKey = '';
let aliceTestClient: TestClient; let aliceTestClient = new TestClient(
"@alice:localhost", "device2", "access_token2",
);
/** /**
* Get the device keys for testOlmAccount in a format suitable for a * 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 () => { it("Alice receives a megolm message", async () => {
await aliceTestClient.start(); await aliceTestClient.start();
aliceTestClient.client.crypto.deviceList.downloadKeys = () => Promise.resolve({}); aliceTestClient.client.crypto!.deviceList.downloadKeys = () => Promise.resolve({});
const p2pSession = await createOlmSession(testOlmAccount, aliceTestClient); const p2pSession = await createOlmSession(testOlmAccount, aliceTestClient);
const groupSession = new Olm.OutboundGroupSession(); const groupSession = new Olm.OutboundGroupSession();
groupSession.create(); groupSession.create();
aliceTestClient.client.crypto.deviceList.getUserByIdentityKey = () => "@bob:xyz"; aliceTestClient.client.crypto!.deviceList.getUserByIdentityKey = () => "@bob:xyz";
// make the room_key event // make the room_key event
const roomKeyEncrypted = encryptGroupSessionKey({ const roomKeyEncrypted = encryptGroupSessionKey({
@ -322,7 +324,7 @@ describe("megolm", () => {
aliceTestClient.httpBackend.when("GET", "/sync").respond(200, syncResponse); aliceTestClient.httpBackend.when("GET", "/sync").respond(200, syncResponse);
await aliceTestClient.flushSync(); await aliceTestClient.flushSync();
const room = aliceTestClient.client.getRoom(ROOM_ID); const room = aliceTestClient.client.getRoom(ROOM_ID)!;
const event = room.getLiveTimeline().getEvents()[0]; const event = room.getLiveTimeline().getEvents()[0];
expect(event.isEncrypted()).toBe(true); expect(event.isEncrypted()).toBe(true);
const decryptedEvent = await testUtils.awaitDecryption(event); const decryptedEvent = await testUtils.awaitDecryption(event);
@ -332,12 +334,12 @@ describe("megolm", () => {
it("Alice receives a megolm message before the session keys", async () => { it("Alice receives a megolm message before the session keys", async () => {
// https://github.com/vector-im/element-web/issues/2273 // https://github.com/vector-im/element-web/issues/2273
await aliceTestClient.start(); await aliceTestClient.start();
aliceTestClient.client.crypto.deviceList.downloadKeys = () => Promise.resolve({}); aliceTestClient.client.crypto!.deviceList.downloadKeys = () => Promise.resolve({});
const p2pSession = await createOlmSession(testOlmAccount, aliceTestClient); const p2pSession = await createOlmSession(testOlmAccount, aliceTestClient);
const groupSession = new Olm.OutboundGroupSession(); const groupSession = new Olm.OutboundGroupSession();
groupSession.create(); 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 // make the room_key event, but don't send it yet
const roomKeyEncrypted = encryptGroupSessionKey({ const roomKeyEncrypted = encryptGroupSessionKey({
@ -362,7 +364,7 @@ describe("megolm", () => {
}); });
await aliceTestClient.flushSync(); 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'); expect(room.getLiveTimeline().getEvents()[0].getContent().msgtype).toEqual('m.bad.encrypted');
// now she gets the room_key event // now she gets the room_key event
@ -392,12 +394,12 @@ describe("megolm", () => {
it("Alice gets a second room_key message", async () => { it("Alice gets a second room_key message", async () => {
await aliceTestClient.start(); await aliceTestClient.start();
aliceTestClient.client.crypto.deviceList.downloadKeys = () => Promise.resolve({}); aliceTestClient.client.crypto!.deviceList.downloadKeys = () => Promise.resolve({});
const p2pSession = await createOlmSession(testOlmAccount, aliceTestClient); const p2pSession = await createOlmSession(testOlmAccount, aliceTestClient);
const groupSession = new Olm.OutboundGroupSession(); const groupSession = new Olm.OutboundGroupSession();
groupSession.create(); groupSession.create();
aliceTestClient.client.crypto.deviceList.getUserByIdentityKey = () => "@bob:xyz"; aliceTestClient.client.crypto!.deviceList.getUserByIdentityKey = () => "@bob:xyz";
// make the room_key event // make the room_key event
const roomKeyEncrypted1 = encryptGroupSessionKey({ const roomKeyEncrypted1 = encryptGroupSessionKey({
@ -451,7 +453,7 @@ describe("megolm", () => {
await aliceTestClient.flushSync(); await aliceTestClient.flushSync();
await aliceTestClient.flushSync(); await aliceTestClient.flushSync();
const room = aliceTestClient.client.getRoom(ROOM_ID); const room = aliceTestClient.client.getRoom(ROOM_ID)!;
await room.decryptCriticalEvents(); await room.decryptCriticalEvents();
const event = room.getLiveTimeline().getEvents()[0]; const event = room.getLiveTimeline().getEvents()[0];
expect(event.getContent().body).toEqual('42'); expect(event.getContent().body).toEqual('42');
@ -499,7 +501,7 @@ describe("megolm", () => {
let inboundGroupSession: Olm.InboundGroupSession; let inboundGroupSession: Olm.InboundGroupSession;
aliceTestClient.httpBackend.when( aliceTestClient.httpBackend.when(
'PUT', '/sendToDevice/m.room.encrypted/', '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 m = content.messages['@bob:xyz'].DEVICE_ID;
const ct = m.ciphertext[testSenderKey]; const ct = m.ciphertext[testSenderKey];
const decrypted = JSON.parse(p2pSession.decrypt(ct.type, ct.body)); const decrypted = JSON.parse(p2pSession.decrypt(ct.type, ct.body));
@ -525,7 +527,7 @@ describe("megolm", () => {
return { event_id: '$event_id' }; return { event_id: '$event_id' };
}); });
const room = aliceTestClient.client.getRoom(ROOM_ID); const room = aliceTestClient.client.getRoom(ROOM_ID)!;
const pendingMsg = room.getPendingEvents()[0]; const pendingMsg = room.getPendingEvents()[0];
await Promise.all([ await Promise.all([
@ -628,7 +630,7 @@ describe("megolm", () => {
let megolmSessionId: string; let megolmSessionId: string;
aliceTestClient.httpBackend.when( aliceTestClient.httpBackend.when(
'PUT', '/sendToDevice/m.room.encrypted/', 'PUT', '/sendToDevice/m.room.encrypted/',
).respond(200, function(_path, content) { ).respond(200, function(_path, content: any) {
logger.log('sendToDevice: ', content); logger.log('sendToDevice: ', content);
const m = content.messages['@bob:xyz'].DEVICE_ID; const m = content.messages['@bob:xyz'].DEVICE_ID;
const ct = m.ciphertext[testSenderKey]; const ct = m.ciphertext[testSenderKey];
@ -706,7 +708,7 @@ describe("megolm", () => {
// invalidate the device cache for all members in e2e rooms (ie, // invalidate the device cache for all members in e2e rooms (ie,
// herself), and do a key query. // herself), and do a key query.
aliceTestClient.expectKeyQuery( aliceTestClient.expectKeyQuery(
getTestKeysQueryResponse(aliceTestClient.userId), getTestKeysQueryResponse(aliceTestClient.userId!),
); );
await aliceTestClient.httpBackend.flushAllExpected(); await aliceTestClient.httpBackend.flushAllExpected();
@ -716,28 +718,30 @@ describe("megolm", () => {
await aliceTestClient.client.sendTextMessage(ROOM_ID, 'test'); await aliceTestClient.client.sendTextMessage(ROOM_ID, 'test');
throw new Error("sendTextMessage succeeded on an unknown device"); throw new Error("sendTextMessage succeeded on an unknown device");
} catch (e) { } catch (e) {
expect(e.name).toEqual("UnknownDeviceError"); expect((e as any).name).toEqual("UnknownDeviceError");
expect(Object.keys(e.devices)).toEqual([aliceTestClient.userId]); expect(Object.keys((e as any).devices)).toEqual([aliceTestClient.userId!]);
expect(Object.keys(e.devices[aliceTestClient.userId])). expect(Object.keys((e as any)?.devices[aliceTestClient.userId!])).
toEqual(['DEVICE_ID']); toEqual(['DEVICE_ID']);
} }
// mark the device as known, and resend. // 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( aliceTestClient.httpBackend.when('POST', '/keys/claim').respond(
200, function(_path, content) { 200, function(_path, content: IClaimOTKsResult) {
expect(content.one_time_keys[aliceTestClient.userId].DEVICE_ID) expect(content.one_time_keys[aliceTestClient.userId!].DEVICE_ID)
.toEqual("signed_curve25519"); .toEqual("signed_curve25519");
return getTestKeysClaimResponse(aliceTestClient.userId); return getTestKeysClaimResponse(aliceTestClient.userId!);
}); });
let p2pSession: Olm.Session; let p2pSession: Olm.Session;
let inboundGroupSession: Olm.InboundGroupSession; let inboundGroupSession: Olm.InboundGroupSession;
aliceTestClient.httpBackend.when( aliceTestClient.httpBackend.when(
'PUT', '/sendToDevice/m.room.encrypted/', 'PUT', '/sendToDevice/m.room.encrypted/',
).respond(200, function(_path, content) { ).respond(200, function(_path, content: {
messages: { [userId: string]: { [deviceId: string]: Record<string, any> }};
}) {
logger.log("sendToDevice: ", content); 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]; const ct = m.ciphertext[testSenderKey];
expect(ct.type).toEqual(0); // pre-key message expect(ct.type).toEqual(0); // pre-key message
@ -751,7 +755,7 @@ describe("megolm", () => {
return {}; return {};
}); });
let decrypted: IEvent; let decrypted: Partial<IEvent> = {};
aliceTestClient.httpBackend.when( aliceTestClient.httpBackend.when(
'PUT', '/send/', 'PUT', '/send/',
).respond(200, function(_path, content: IContent) { ).respond(200, function(_path, content: IContent) {
@ -766,7 +770,7 @@ describe("megolm", () => {
}); });
// Grab the event that we'll need to resend // 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(); const pendingEvents = room.getPendingEvents();
expect(pendingEvents.length).toEqual(1); expect(pendingEvents.length).toEqual(1);
const unsentEvent = pendingEvents[0]; const unsentEvent = pendingEvents[0];
@ -781,7 +785,7 @@ describe("megolm", () => {
]); ]);
expect(decrypted.type).toEqual('m.room.message'); 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 () => { 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 () => { it("Alice exports megolm keys and imports them to a new device", async () => {
aliceTestClient.expectKeyQuery({ device_keys: { '@alice:localhost': {} }, failures: {} }); aliceTestClient.expectKeyQuery({ device_keys: { '@alice:localhost': {} }, failures: {} });
await aliceTestClient.start(); await aliceTestClient.start();
aliceTestClient.client.crypto.deviceList.downloadKeys = () => Promise.resolve({}); aliceTestClient.client.crypto!.deviceList.downloadKeys = () => Promise.resolve({});
// establish an olm session with alice // establish an olm session with alice
const p2pSession = await createOlmSession(testOlmAccount, aliceTestClient); 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(); const groupSession = new Olm.OutboundGroupSession();
groupSession.create(); groupSession.create();
@ -867,7 +871,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(); await room.decryptCriticalEvents();
expect(room.getLiveTimeline().getEvents()[0].getContent().body).toEqual('42'); expect(room.getLiveTimeline().getEvents()[0].getContent().body).toEqual('42');
@ -883,7 +887,7 @@ describe("megolm", () => {
await aliceTestClient.client.importRoomKeys(exported); await aliceTestClient.client.importRoomKeys(exported);
await aliceTestClient.start(); await aliceTestClient.start();
aliceTestClient.client.crypto.deviceList.getUserByIdentityKey = () => "@bob:xyz"; aliceTestClient.client.crypto!.deviceList.getUserByIdentityKey = () => "@bob:xyz";
const syncResponse = { const syncResponse = {
next_batch: 1, next_batch: 1,
@ -927,7 +931,7 @@ describe("megolm", () => {
...rawEvent, ...rawEvent,
room: ROOM_ID, room: ROOM_ID,
}); });
await event1.attemptDecryption(testClient.client.crypto, { isRetry: true }); await event1.attemptDecryption(testClient.client.crypto!, { isRetry: true });
expect(event1.isKeySourceUntrusted()).toBeTruthy(); expect(event1.isKeySourceUntrusted()).toBeTruthy();
const event2 = testUtils.mkEvent({ const event2 = testUtils.mkEvent({
@ -943,26 +947,26 @@ describe("megolm", () => {
// @ts-ignore - private // @ts-ignore - private
event2.senderCurve25519Key = testSenderKey; event2.senderCurve25519Key = testSenderKey;
// @ts-ignore - private // @ts-ignore - private
testClient.client.crypto.onRoomKeyEvent(event2); testClient.client.crypto!.onRoomKeyEvent(event2);
const event3 = testUtils.mkEvent({ const event3 = testUtils.mkEvent({
event: true, event: true,
...rawEvent, ...rawEvent,
room: ROOM_ID, room: ROOM_ID,
}); });
await event3.attemptDecryption(testClient.client.crypto, { isRetry: true }); await event3.attemptDecryption(testClient.client.crypto!, { isRetry: true });
expect(event3.isKeySourceUntrusted()).toBeFalsy(); expect(event3.isKeySourceUntrusted()).toBeFalsy();
testClient.stop(); testClient.stop();
}); });
it("Alice can decrypt a message with falsey content", async () => { it("Alice can decrypt a message with falsey content", async () => {
await aliceTestClient.start(); await aliceTestClient.start();
aliceTestClient.client.crypto.deviceList.downloadKeys = () => Promise.resolve({}); aliceTestClient.client.crypto!.deviceList.downloadKeys = () => Promise.resolve({});
const p2pSession = await createOlmSession(testOlmAccount, aliceTestClient); const p2pSession = await createOlmSession(testOlmAccount, aliceTestClient);
const groupSession = new Olm.OutboundGroupSession(); const groupSession = new Olm.OutboundGroupSession();
groupSession.create(); groupSession.create();
aliceTestClient.client.crypto.deviceList.getUserByIdentityKey = () => "@bob:xyz"; aliceTestClient.client.crypto!.deviceList.getUserByIdentityKey = () => "@bob:xyz";
// make the room_key event // make the room_key event
const roomKeyEncrypted = encryptGroupSessionKey({ const roomKeyEncrypted = encryptGroupSessionKey({
@ -1005,7 +1009,7 @@ describe("megolm", () => {
aliceTestClient.httpBackend.when("GET", "/sync").respond(200, syncResponse); aliceTestClient.httpBackend.when("GET", "/sync").respond(200, syncResponse);
await aliceTestClient.flushSync(); await aliceTestClient.flushSync();
const room = aliceTestClient.client.getRoom(ROOM_ID); const room = aliceTestClient.client.getRoom(ROOM_ID)!;
const event = room.getLiveTimeline().getEvents()[0]; const event = room.getLiveTimeline().getEvents()[0];
expect(event.isEncrypted()).toBe(true); expect(event.isEncrypted()).toBe(true);
const decryptedEvent = await testUtils.awaitDecryption(event); 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", "should successfully decrypt bundled redaction events that don't include a room_id in their /sync data",
async () => { async () => {
await aliceTestClient.start(); await aliceTestClient.start();
aliceTestClient.client.crypto.deviceList.downloadKeys = () => Promise.resolve({}); aliceTestClient.client.crypto!.deviceList.downloadKeys = () => Promise.resolve({});
const p2pSession = await createOlmSession(testOlmAccount, aliceTestClient); const p2pSession = await createOlmSession(testOlmAccount, aliceTestClient);
const groupSession = new Olm.OutboundGroupSession(); const groupSession = new Olm.OutboundGroupSession();
groupSession.create(); groupSession.create();
aliceTestClient.client.crypto.deviceList.getUserByIdentityKey = () => "@bob:xyz"; aliceTestClient.client.crypto!.deviceList.getUserByIdentityKey = () => "@bob:xyz";
// make the room_key event // make the room_key event
const roomKeyEncrypted = encryptGroupSessionKey({ const roomKeyEncrypted = encryptGroupSessionKey({
@ -1072,10 +1076,10 @@ describe("megolm", () => {
aliceTestClient.httpBackend.when("GET", "/sync").respond(200, syncResponse); aliceTestClient.httpBackend.when("GET", "/sync").respond(200, syncResponse);
await aliceTestClient.flushSync(); await aliceTestClient.flushSync();
const room = aliceTestClient.client.getRoom(ROOM_ID); const room = aliceTestClient.client.getRoom(ROOM_ID)!;
const event = room.getLiveTimeline().getEvents()[0]; const event = room.getLiveTimeline().getEvents()[0];
expect(event.isEncrypted()).toBe(true); expect(event.isEncrypted()).toBe(true);
await event.attemptDecryption(aliceTestClient.client.crypto); await event.attemptDecryption(aliceTestClient.client.crypto!);
expect(event.getContent()).toEqual({}); expect(event.getContent()).toEqual({});
const redactionEvent: any = event.getRedactionEvent(); const redactionEvent: any = event.getRedactionEvent();
expect(redactionEvent.content.reason).toEqual("redaction test"); expect(redactionEvent.content.reason).toEqual("redaction test");
@ -1089,7 +1093,7 @@ describe("megolm", () => {
await beccaTestClient.client.initCrypto(); await beccaTestClient.client.initCrypto();
await aliceTestClient.start(); await aliceTestClient.start();
aliceTestClient.client.crypto.deviceList.downloadKeys = () => Promise.resolve({}); aliceTestClient.client.crypto!.deviceList.downloadKeys = () => Promise.resolve({});
await beccaTestClient.start(); await beccaTestClient.start();
const beccaRoom = new Room(ROOM_ID, beccaTestClient.client, "@becca:localhost", {}); 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 // remove keys from the event
// @ts-ignore private properties // @ts-ignore private properties
event.clearEvent = undefined; event.clearEvent = undefined;
@ -1116,23 +1120,23 @@ describe("megolm", () => {
// @ts-ignore private properties // @ts-ignore private properties
event.claimedEd25519Key = null; event.claimedEd25519Key = null;
const device = new DeviceInfo(beccaTestClient.client.deviceId); const device = new DeviceInfo(beccaTestClient.client.deviceId!);
aliceTestClient.client.crypto.deviceList.getDeviceByIdentityKey = () => device; aliceTestClient.client.crypto!.deviceList.getDeviceByIdentityKey = () => device;
aliceTestClient.client.crypto.deviceList.getUserByIdentityKey = () => beccaTestClient.client.getUserId(); aliceTestClient.client.crypto!.deviceList.getUserByIdentityKey = () => beccaTestClient.client.getUserId()!;
// Create an olm session for Becca and Alice's devices // Create an olm session for Becca and Alice's devices
const aliceOtks = await aliceTestClient.awaitOneTimeKeyUpload(); const aliceOtks = await aliceTestClient.awaitOneTimeKeyUpload();
const aliceOtkId = Object.keys(aliceOtks)[0]; const aliceOtkId = Object.keys(aliceOtks)[0];
const aliceOtk = aliceOtks[aliceOtkId]; const aliceOtk = aliceOtks[aliceOtkId];
const p2pSession = new global.Olm.Session(); const p2pSession = new global.Olm.Session();
await beccaTestClient.client.crypto.cryptoStore.doTxn( await beccaTestClient.client.crypto!.cryptoStore.doTxn(
'readonly', 'readonly',
[IndexedDBCryptoStore.STORE_ACCOUNT], [IndexedDBCryptoStore.STORE_ACCOUNT],
(txn) => { (txn) => {
beccaTestClient.client.crypto.cryptoStore.getAccount(txn, (pickledAccount: string) => { beccaTestClient.client.crypto!.cryptoStore.getAccount(txn, (pickledAccount: string) => {
const account = new global.Olm.Account(); const account = new global.Olm.Account();
try { 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); p2pSession.create_outbound(account, aliceTestClient.getDeviceKey(), aliceOtk.key);
} finally { } finally {
account.free(); account.free();
@ -1142,7 +1146,7 @@ describe("megolm", () => {
); );
const content = event.getWireContent(); const content = event.getWireContent();
const groupSessionKey = await beccaTestClient.client.crypto.olmDevice.getInboundGroupSessionKey( const groupSessionKey = await beccaTestClient.client.crypto!.olmDevice.getInboundGroupSessionKey(
ROOM_ID, ROOM_ID,
content.sender_key, content.sender_key,
content.session_id, content.session_id,
@ -1213,7 +1217,7 @@ describe("megolm", () => {
}); });
await aliceTestClient.flushSync(); await aliceTestClient.flushSync();
const room = aliceTestClient.client.getRoom(ROOM_ID); const room = aliceTestClient.client.getRoom(ROOM_ID)!;
const roomEvent = room.getLiveTimeline().getEvents()[0]; const roomEvent = room.getLiveTimeline().getEvents()[0];
expect(roomEvent.isEncrypted()).toBe(true); expect(roomEvent.isEncrypted()).toBe(true);
const decryptedEvent = await testUtils.awaitDecryption(roomEvent); 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 // remove keys from the event
// @ts-ignore private properties // @ts-ignore private properties
event.clearEvent = undefined; event.clearEvent = undefined;
@ -1255,22 +1259,22 @@ describe("megolm", () => {
// @ts-ignore private properties // @ts-ignore private properties
event.claimedEd25519Key = null; event.claimedEd25519Key = null;
const device = new DeviceInfo(beccaTestClient.client.deviceId); const device = new DeviceInfo(beccaTestClient.client.deviceId!);
aliceTestClient.client.crypto.deviceList.getDeviceByIdentityKey = () => device; aliceTestClient.client.crypto!.deviceList.getDeviceByIdentityKey = () => device;
// Create an olm session for Becca and Alice's devices // Create an olm session for Becca and Alice's devices
const aliceOtks = await aliceTestClient.awaitOneTimeKeyUpload(); const aliceOtks = await aliceTestClient.awaitOneTimeKeyUpload();
const aliceOtkId = Object.keys(aliceOtks)[0]; const aliceOtkId = Object.keys(aliceOtks)[0];
const aliceOtk = aliceOtks[aliceOtkId]; const aliceOtk = aliceOtks[aliceOtkId];
const p2pSession = new global.Olm.Session(); const p2pSession = new global.Olm.Session();
await beccaTestClient.client.crypto.cryptoStore.doTxn( await beccaTestClient.client.crypto!.cryptoStore.doTxn(
'readonly', 'readonly',
[IndexedDBCryptoStore.STORE_ACCOUNT], [IndexedDBCryptoStore.STORE_ACCOUNT],
(txn) => { (txn) => {
beccaTestClient.client.crypto.cryptoStore.getAccount(txn, (pickledAccount: string) => { beccaTestClient.client.crypto!.cryptoStore.getAccount(txn, (pickledAccount: string) => {
const account = new global.Olm.Account(); const account = new global.Olm.Account();
try { 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); p2pSession.create_outbound(account, aliceTestClient.getDeviceKey(), aliceOtk.key);
} finally { } finally {
account.free(); account.free();
@ -1280,7 +1284,7 @@ describe("megolm", () => {
); );
const content = event.getWireContent(); const content = event.getWireContent();
const groupSessionKey = await beccaTestClient.client.crypto.olmDevice.getInboundGroupSessionKey( const groupSessionKey = await beccaTestClient.client.crypto!.olmDevice.getInboundGroupSessionKey(
ROOM_ID, ROOM_ID,
content.sender_key, content.sender_key,
content.session_id, content.session_id,
@ -1352,7 +1356,7 @@ describe("megolm", () => {
await aliceTestClient.flushSync(); await aliceTestClient.flushSync();
// Decryption should fail, because Alice hasn't received any keys she can trust // 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]; const roomEvent = room.getLiveTimeline().getEvents()[0];
expect(roomEvent.isEncrypted()).toBe(true); expect(roomEvent.isEncrypted()).toBe(true);
const decryptedEvent = await testUtils.awaitDecryption(roomEvent); const decryptedEvent = await testUtils.awaitDecryption(roomEvent);

View File

@ -31,10 +31,10 @@ import { IStoredClientOpts } from "../../src/client";
import { logger } from "../../src/logger"; import { logger } from "../../src/logger";
describe("SlidingSyncSdk", () => { describe("SlidingSyncSdk", () => {
let client: MatrixClient = null; let client: MatrixClient | undefined;
let httpBackend: MockHttpBackend = null; let httpBackend: MockHttpBackend | undefined;
let sdk: SlidingSyncSdk = null; let sdk: SlidingSyncSdk | undefined;
let mockSlidingSync: SlidingSync = null; let mockSlidingSync: SlidingSync | undefined;
const selfUserId = "@alice:localhost"; const selfUserId = "@alice:localhost";
const selfAccessToken = "aseukfgwef"; const selfAccessToken = "aseukfgwef";
@ -66,7 +66,7 @@ describe("SlidingSyncSdk", () => {
event_id: "$" + eventIdCounter, event_id: "$" + eventIdCounter,
}; };
}; };
const mkOwnStateEvent = (evType: string, content: object, stateKey?: string): IStateEvent => { const mkOwnStateEvent = (evType: string, content: object, stateKey = ''): IStateEvent => {
eventIdCounter++; eventIdCounter++;
return { return {
type: evType, type: evType,
@ -103,24 +103,24 @@ describe("SlidingSyncSdk", () => {
client = testClient.client; client = testClient.client;
mockSlidingSync = mockifySlidingSync(new SlidingSync("", [], {}, client, 0)); mockSlidingSync = mockifySlidingSync(new SlidingSync("", [], {}, client, 0));
if (testOpts.withCrypto) { if (testOpts.withCrypto) {
httpBackend.when("GET", "/room_keys/version").respond(404, {}); httpBackend!.when("GET", "/room_keys/version").respond(404, {});
await client.initCrypto(); await client!.initCrypto();
testOpts.crypto = client.crypto; 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); sdk = new SlidingSyncSdk(mockSlidingSync, client, testOpts);
}; };
// tear down client/httpBackend globals // tear down client/httpBackend globals
const teardownClient = () => { const teardownClient = () => {
client.stopClient(); client!.stopClient();
return httpBackend.stop(); return httpBackend!.stop();
}; };
// find an extension on a SlidingSyncSdk instance // find an extension on a SlidingSyncSdk instance
const findExtension = (name: string): Extension => { const findExtension = (name: string): Extension => {
expect(mockSlidingSync.registerExtension).toHaveBeenCalled(); expect(mockSlidingSync!.registerExtension).toHaveBeenCalled();
const mockFn = mockSlidingSync.registerExtension as jest.Mock; const mockFn = mockSlidingSync!.registerExtension as jest.Mock;
// find the extension // find the extension
for (let i = 0; i < mockFn.mock.calls.length; i++) { for (let i = 0; i < mockFn.mock.calls.length; i++) {
const calledExtension = mockFn.mock.calls[i][0] as Extension; const calledExtension = mockFn.mock.calls[i][0] as Extension;
@ -137,14 +137,14 @@ describe("SlidingSyncSdk", () => {
}); });
afterAll(teardownClient); afterAll(teardownClient);
it("can sync()", async () => { it("can sync()", async () => {
const hasSynced = sdk.sync(); const hasSynced = sdk!.sync();
await httpBackend.flushAllExpected(); await httpBackend!.flushAllExpected();
await hasSynced; await hasSynced;
expect(mockSlidingSync.start).toBeCalled(); expect(mockSlidingSync!.start).toBeCalled();
}); });
it("can stop()", async () => { it("can stop()", async () => {
sdk.stop(); sdk!.stop();
expect(mockSlidingSync.stop).toBeCalled(); expect(mockSlidingSync!.stop).toBeCalled();
}); });
}); });
@ -156,8 +156,8 @@ describe("SlidingSyncSdk", () => {
describe("initial", () => { describe("initial", () => {
beforeAll(async () => { beforeAll(async () => {
const hasSynced = sdk.sync(); const hasSynced = sdk!.sync();
await httpBackend.flushAllExpected(); await httpBackend!.flushAllExpected();
await hasSynced; await hasSynced;
}); });
// inject some rooms with different fields set. // inject some rooms with different fields set.
@ -277,8 +277,8 @@ describe("SlidingSyncSdk", () => {
}; };
it("can be created with required_state and timeline", () => { it("can be created with required_state and timeline", () => {
mockSlidingSync.emit(SlidingSyncEvent.RoomData, roomA, data[roomA]); mockSlidingSync!.emit(SlidingSyncEvent.RoomData, roomA, data[roomA]);
const gotRoom = client.getRoom(roomA); const gotRoom = client!.getRoom(roomA);
expect(gotRoom).toBeDefined(); expect(gotRoom).toBeDefined();
if (gotRoom == null) { return; } if (gotRoom == null) { return; }
expect(gotRoom.name).toEqual(data[roomA].name); expect(gotRoom.name).toEqual(data[roomA].name);
@ -287,8 +287,8 @@ describe("SlidingSyncSdk", () => {
}); });
it("can be created with timeline only", () => { it("can be created with timeline only", () => {
mockSlidingSync.emit(SlidingSyncEvent.RoomData, roomB, data[roomB]); mockSlidingSync!.emit(SlidingSyncEvent.RoomData, roomB, data[roomB]);
const gotRoom = client.getRoom(roomB); const gotRoom = client!.getRoom(roomB);
expect(gotRoom).toBeDefined(); expect(gotRoom).toBeDefined();
if (gotRoom == null) { return; } if (gotRoom == null) { return; }
expect(gotRoom.name).toEqual(data[roomB].name); expect(gotRoom.name).toEqual(data[roomB].name);
@ -297,8 +297,8 @@ describe("SlidingSyncSdk", () => {
}); });
it("can be created with a highlight_count", () => { it("can be created with a highlight_count", () => {
mockSlidingSync.emit(SlidingSyncEvent.RoomData, roomC, data[roomC]); mockSlidingSync!.emit(SlidingSyncEvent.RoomData, roomC, data[roomC]);
const gotRoom = client.getRoom(roomC); const gotRoom = client!.getRoom(roomC);
expect(gotRoom).toBeDefined(); expect(gotRoom).toBeDefined();
if (gotRoom == null) { return; } if (gotRoom == null) { return; }
expect( expect(
@ -307,8 +307,8 @@ describe("SlidingSyncSdk", () => {
}); });
it("can be created with a notification_count", () => { it("can be created with a notification_count", () => {
mockSlidingSync.emit(SlidingSyncEvent.RoomData, roomD, data[roomD]); mockSlidingSync!.emit(SlidingSyncEvent.RoomData, roomD, data[roomD]);
const gotRoom = client.getRoom(roomD); const gotRoom = client!.getRoom(roomD);
expect(gotRoom).toBeDefined(); expect(gotRoom).toBeDefined();
if (gotRoom == null) { return; } if (gotRoom == null) { return; }
expect( expect(
@ -317,8 +317,8 @@ describe("SlidingSyncSdk", () => {
}); });
it("can be created with an invited/joined_count", () => { it("can be created with an invited/joined_count", () => {
mockSlidingSync.emit(SlidingSyncEvent.RoomData, roomG, data[roomG]); mockSlidingSync!.emit(SlidingSyncEvent.RoomData, roomG, data[roomG]);
const gotRoom = client.getRoom(roomG); const gotRoom = client!.getRoom(roomG);
expect(gotRoom).toBeDefined(); expect(gotRoom).toBeDefined();
if (gotRoom == null) { return; } if (gotRoom == null) { return; }
expect(gotRoom.getInvitedMemberCount()).toEqual(data[roomG].invited_count); expect(gotRoom.getInvitedMemberCount()).toEqual(data[roomG].invited_count);
@ -326,8 +326,8 @@ describe("SlidingSyncSdk", () => {
}); });
it("can be created with invite_state", () => { it("can be created with invite_state", () => {
mockSlidingSync.emit(SlidingSyncEvent.RoomData, roomE, data[roomE]); mockSlidingSync!.emit(SlidingSyncEvent.RoomData, roomE, data[roomE]);
const gotRoom = client.getRoom(roomE); const gotRoom = client!.getRoom(roomE);
expect(gotRoom).toBeDefined(); expect(gotRoom).toBeDefined();
if (gotRoom == null) { return; } if (gotRoom == null) { return; }
expect(gotRoom.getMyMembership()).toEqual("invite"); expect(gotRoom.getMyMembership()).toEqual("invite");
@ -335,8 +335,8 @@ describe("SlidingSyncSdk", () => {
}); });
it("uses the 'name' field to caluclate the room name", () => { it("uses the 'name' field to caluclate the room name", () => {
mockSlidingSync.emit(SlidingSyncEvent.RoomData, roomF, data[roomF]); mockSlidingSync!.emit(SlidingSyncEvent.RoomData, roomF, data[roomF]);
const gotRoom = client.getRoom(roomF); const gotRoom = client!.getRoom(roomF);
expect(gotRoom).toBeDefined(); expect(gotRoom).toBeDefined();
if (gotRoom == null) { return; } if (gotRoom == null) { return; }
expect( expect(
@ -347,12 +347,12 @@ describe("SlidingSyncSdk", () => {
describe("updating", () => { describe("updating", () => {
it("can update with a new timeline event", async () => { it("can update with a new timeline event", async () => {
const newEvent = mkOwnEvent(EventType.RoomMessage, { body: "new event A" }); const newEvent = mkOwnEvent(EventType.RoomMessage, { body: "new event A" });
mockSlidingSync.emit(SlidingSyncEvent.RoomData, roomA, { mockSlidingSync!.emit(SlidingSyncEvent.RoomData, roomA, {
timeline: [newEvent], timeline: [newEvent],
required_state: [], required_state: [],
name: data[roomA].name, name: data[roomA].name,
}); });
const gotRoom = client.getRoom(roomA); const gotRoom = client!.getRoom(roomA);
expect(gotRoom).toBeDefined(); expect(gotRoom).toBeDefined();
if (gotRoom == null) { return; } if (gotRoom == null) { return; }
const newTimeline = data[roomA].timeline; const newTimeline = data[roomA].timeline;
@ -361,31 +361,31 @@ describe("SlidingSyncSdk", () => {
}); });
it("can update with a new required_state event", async () => { it("can update with a new required_state event", async () => {
let gotRoom = client.getRoom(roomB); let gotRoom = client!.getRoom(roomB);
expect(gotRoom).toBeDefined(); expect(gotRoom).toBeDefined();
if (gotRoom == null) { return; } if (gotRoom == null) { return; }
expect(gotRoom.getJoinRule()).toEqual(JoinRule.Invite); // default expect(gotRoom.getJoinRule()).toEqual(JoinRule.Invite); // default
mockSlidingSync.emit(SlidingSyncEvent.RoomData, roomB, { mockSlidingSync!.emit(SlidingSyncEvent.RoomData, roomB, {
required_state: [ required_state: [
mkOwnStateEvent("m.room.join_rules", { join_rule: "restricted" }, ""), mkOwnStateEvent("m.room.join_rules", { join_rule: "restricted" }, ""),
], ],
timeline: [], timeline: [],
name: data[roomB].name, name: data[roomB].name,
}); });
gotRoom = client.getRoom(roomB); gotRoom = client!.getRoom(roomB);
expect(gotRoom).toBeDefined(); expect(gotRoom).toBeDefined();
if (gotRoom == null) { return; } if (gotRoom == null) { return; }
expect(gotRoom.getJoinRule()).toEqual(JoinRule.Restricted); expect(gotRoom.getJoinRule()).toEqual(JoinRule.Restricted);
}); });
it("can update with a new highlight_count", async () => { it("can update with a new highlight_count", async () => {
mockSlidingSync.emit(SlidingSyncEvent.RoomData, roomC, { mockSlidingSync!.emit(SlidingSyncEvent.RoomData, roomC, {
name: data[roomC].name, name: data[roomC].name,
required_state: [], required_state: [],
timeline: [], timeline: [],
highlight_count: 1, highlight_count: 1,
}); });
const gotRoom = client.getRoom(roomC); const gotRoom = client!.getRoom(roomC);
expect(gotRoom).toBeDefined(); expect(gotRoom).toBeDefined();
if (gotRoom == null) { return; } if (gotRoom == null) { return; }
expect( expect(
@ -394,13 +394,13 @@ describe("SlidingSyncSdk", () => {
}); });
it("can update with a new notification_count", async () => { it("can update with a new notification_count", async () => {
mockSlidingSync.emit(SlidingSyncEvent.RoomData, roomD, { mockSlidingSync!.emit(SlidingSyncEvent.RoomData, roomD, {
name: data[roomD].name, name: data[roomD].name,
required_state: [], required_state: [],
timeline: [], timeline: [],
notification_count: 1, notification_count: 1,
}); });
const gotRoom = client.getRoom(roomD); const gotRoom = client!.getRoom(roomD);
expect(gotRoom).toBeDefined(); expect(gotRoom).toBeDefined();
if (gotRoom == null) { return; } if (gotRoom == null) { return; }
expect( expect(
@ -409,13 +409,13 @@ describe("SlidingSyncSdk", () => {
}); });
it("can update with a new joined_count", () => { it("can update with a new joined_count", () => {
mockSlidingSync.emit(SlidingSyncEvent.RoomData, roomG, { mockSlidingSync!.emit(SlidingSyncEvent.RoomData, roomG, {
name: data[roomD].name, name: data[roomD].name,
required_state: [], required_state: [],
timeline: [], timeline: [],
joined_count: 1, joined_count: 1,
}); });
const gotRoom = client.getRoom(roomG); const gotRoom = client!.getRoom(roomG);
expect(gotRoom).toBeDefined(); expect(gotRoom).toBeDefined();
if (gotRoom == null) { return; } if (gotRoom == null) { return; }
expect(gotRoom.getJoinedMemberCount()).toEqual(1); expect(gotRoom.getJoinedMemberCount()).toEqual(1);
@ -433,13 +433,13 @@ describe("SlidingSyncSdk", () => {
mkOwnEvent(EventType.RoomMessage, { body: "old event C" }), mkOwnEvent(EventType.RoomMessage, { body: "old event C" }),
...timeline, ...timeline,
]; ];
mockSlidingSync.emit(SlidingSyncEvent.RoomData, roomA, { mockSlidingSync!.emit(SlidingSyncEvent.RoomData, roomA, {
timeline: oldTimeline, timeline: oldTimeline,
required_state: [], required_state: [],
name: data[roomA].name, name: data[roomA].name,
initial: true, // e.g requested via room subscription initial: true, // e.g requested via room subscription
}); });
const gotRoom = client.getRoom(roomA); const gotRoom = client!.getRoom(roomA);
expect(gotRoom).toBeDefined(); expect(gotRoom).toBeDefined();
if (gotRoom == null) { return; } if (gotRoom == null) { return; }
@ -458,50 +458,50 @@ describe("SlidingSyncSdk", () => {
describe("lifecycle", () => { describe("lifecycle", () => {
beforeAll(async () => { beforeAll(async () => {
await setupClient(); await setupClient();
const hasSynced = sdk.sync(); const hasSynced = sdk!.sync();
await httpBackend.flushAllExpected(); await httpBackend!.flushAllExpected();
await hasSynced; await hasSynced;
}); });
const FAILED_SYNC_ERROR_THRESHOLD = 3; // would be nice to export the const in the actual class... 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 () => { it("emits SyncState.Reconnecting when < FAILED_SYNC_ERROR_THRESHOLD & SyncState.Error when over", async () => {
mockSlidingSync.emit( mockSlidingSync!.emit(
SlidingSyncEvent.Lifecycle, SlidingSyncState.Complete, SlidingSyncEvent.Lifecycle, SlidingSyncState.Complete,
{ pos: "h", lists: [], rooms: {}, extensions: {} }, null, { 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"), 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++) { for (let i = 0; i < FAILED_SYNC_ERROR_THRESHOLD; i++) {
mockSlidingSync.emit( mockSlidingSync!.emit(
SlidingSyncEvent.Lifecycle, SlidingSyncState.RequestFinished, null, new Error("generic"), 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 () => { it("emits SyncState.Syncing after a previous SyncState.Error", async () => {
mockSlidingSync.emit( mockSlidingSync!.emit(
SlidingSyncEvent.Lifecycle, SlidingSyncEvent.Lifecycle,
SlidingSyncState.Complete, SlidingSyncState.Complete,
{ pos: "i", lists: [], rooms: {}, extensions: {} }, { pos: "i", lists: [], rooms: {}, extensions: {} },
null, 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 () => { it("emits SyncState.Error immediately when receiving M_UNKNOWN_TOKEN and stops syncing", async () => {
expect(mockSlidingSync.stop).not.toBeCalled(); expect(mockSlidingSync!.stop).not.toBeCalled();
mockSlidingSync.emit(SlidingSyncEvent.Lifecycle, SlidingSyncState.RequestFinished, null, new MatrixError({ mockSlidingSync!.emit(SlidingSyncEvent.Lifecycle, SlidingSyncState.RequestFinished, null, new MatrixError({
errcode: "M_UNKNOWN_TOKEN", errcode: "M_UNKNOWN_TOKEN",
message: "Oh no your access token is no longer valid", message: "Oh no your access token is no longer valid",
})); }));
expect(sdk.getSyncState()).toEqual(SyncState.Error); expect(sdk!.getSyncState()).toEqual(SyncState.Error);
expect(mockSlidingSync.stop).toBeCalled(); expect(mockSlidingSync!.stop).toBeCalled();
}); });
}); });
@ -517,8 +517,8 @@ describe("SlidingSyncSdk", () => {
avatar_url: "mxc://foobar", avatar_url: "mxc://foobar",
displayname: "The Invitee", displayname: "The Invitee",
}; };
httpBackend.when("GET", "/profile").respond(200, inviteeProfile); httpBackend!.when("GET", "/profile").respond(200, inviteeProfile);
mockSlidingSync.emit(SlidingSyncEvent.RoomData, roomId, { mockSlidingSync!.emit(SlidingSyncEvent.RoomData, roomId, {
initial: true, initial: true,
name: "Room with Invite", name: "Room with Invite",
required_state: [], required_state: [],
@ -529,10 +529,10 @@ describe("SlidingSyncSdk", () => {
mkOwnStateEvent(EventType.RoomMember, { membership: "invite" }, invitee), mkOwnStateEvent(EventType.RoomMember, { membership: "invite" }, invitee),
], ],
}); });
await httpBackend.flush("/profile", 1, 1000); await httpBackend!.flush("/profile", 1, 1000);
const room = client.getRoom(roomId); const room = client!.getRoom(roomId)!;
expect(room).toBeDefined(); expect(room).toBeDefined();
const inviteeMember = room.getMember(invitee); const inviteeMember = room.getMember(invitee)!;
expect(inviteeMember).toBeDefined(); expect(inviteeMember).toBeDefined();
expect(inviteeMember.getMxcAvatarUrl()).toEqual(inviteeProfile.avatar_url); expect(inviteeMember.getMxcAvatarUrl()).toEqual(inviteeProfile.avatar_url);
expect(inviteeMember.name).toEqual(inviteeProfile.displayname); expect(inviteeMember.name).toEqual(inviteeProfile.displayname);
@ -545,8 +545,8 @@ describe("SlidingSyncSdk", () => {
await setupClient({ await setupClient({
withCrypto: true, withCrypto: true,
}); });
const hasSynced = sdk.sync(); const hasSynced = sdk!.sync();
await httpBackend.flushAllExpected(); await httpBackend!.flushAllExpected();
await hasSynced; await hasSynced;
ext = findExtension("e2ee"); 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: // 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?" // "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"." // Attempted to log "Saving device tracking data null"."
client.crypto.stop(); client!.crypto!.stop();
}); });
it("gets enabled on the initial request only", () => { it("gets enabled on the initial request only", () => {
expect(ext.onRequest(true)).toEqual({ expect(ext.onRequest(true)).toEqual({
@ -572,38 +572,38 @@ describe("SlidingSyncSdk", () => {
// TODO: more assertions? // TODO: more assertions?
}); });
it("can update OTK counts", () => { it("can update OTK counts", () => {
client.crypto.updateOneTimeKeyCount = jest.fn(); client!.crypto!.updateOneTimeKeyCount = jest.fn();
ext.onResponse({ ext.onResponse({
device_one_time_keys_count: { device_one_time_keys_count: {
signed_curve25519: 42, signed_curve25519: 42,
}, },
}); });
expect(client.crypto.updateOneTimeKeyCount).toHaveBeenCalledWith(42); expect(client!.crypto!.updateOneTimeKeyCount).toHaveBeenCalledWith(42);
ext.onResponse({ ext.onResponse({
device_one_time_keys_count: { device_one_time_keys_count: {
not_signed_curve25519: 42, not_signed_curve25519: 42,
// missing field -> default to 0 // missing field -> default to 0
}, },
}); });
expect(client.crypto.updateOneTimeKeyCount).toHaveBeenCalledWith(0); expect(client!.crypto!.updateOneTimeKeyCount).toHaveBeenCalledWith(0);
}); });
it("can update fallback keys", () => { it("can update fallback keys", () => {
ext.onResponse({ ext.onResponse({
device_unused_fallback_key_types: ["signed_curve25519"], device_unused_fallback_key_types: ["signed_curve25519"],
}); });
expect(client.crypto.getNeedsNewFallback()).toEqual(false); expect(client!.crypto!.getNeedsNewFallback()).toEqual(false);
ext.onResponse({ ext.onResponse({
device_unused_fallback_key_types: ["not_signed_curve25519"], device_unused_fallback_key_types: ["not_signed_curve25519"],
}); });
expect(client.crypto.getNeedsNewFallback()).toEqual(true); expect(client!.crypto!.getNeedsNewFallback()).toEqual(true);
}); });
}); });
describe("ExtensionAccountData", () => { describe("ExtensionAccountData", () => {
let ext: Extension; let ext: Extension;
beforeAll(async () => { beforeAll(async () => {
await setupClient(); await setupClient();
const hasSynced = sdk.sync(); const hasSynced = sdk!.sync();
await httpBackend.flushAllExpected(); await httpBackend!.flushAllExpected();
await hasSynced; await hasSynced;
ext = findExtension("account_data"); ext = findExtension("account_data");
}); });
@ -618,7 +618,7 @@ describe("SlidingSyncSdk", () => {
const globalContent = { const globalContent = {
info: "here", info: "here",
}; };
let globalData = client.getAccountData(globalType); let globalData = client!.getAccountData(globalType);
expect(globalData).toBeUndefined(); expect(globalData).toBeUndefined();
ext.onResponse({ ext.onResponse({
global: [ global: [
@ -628,13 +628,13 @@ describe("SlidingSyncSdk", () => {
}, },
], ],
}); });
globalData = client.getAccountData(globalType); globalData = client!.getAccountData(globalType)!;
expect(globalData).toBeDefined(); expect(globalData).toBeDefined();
expect(globalData.getContent()).toEqual(globalContent); expect(globalData.getContent()).toEqual(globalContent);
}); });
it("processes rooms account data", async () => { it("processes rooms account data", async () => {
const roomId = "!room:id"; const roomId = "!room:id";
mockSlidingSync.emit(SlidingSyncEvent.RoomData, roomId, { mockSlidingSync!.emit(SlidingSyncEvent.RoomData, roomId, {
name: "Room with account data", name: "Room with account data",
required_state: [], required_state: [],
timeline: [ timeline: [
@ -660,9 +660,9 @@ describe("SlidingSyncSdk", () => {
], ],
}, },
}); });
const room = client.getRoom(roomId); const room = client!.getRoom(roomId)!;
expect(room).toBeDefined(); expect(room).toBeDefined();
const event = room.getAccountData(roomType); const event = room.getAccountData(roomType)!;
expect(event).toBeDefined(); expect(event).toBeDefined();
expect(event.getContent()).toEqual(roomContent); 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(room).toBeNull();
expect(client.getAccountData(roomType)).toBeUndefined(); expect(client!.getAccountData(roomType)).toBeUndefined();
}); });
it("can update push rules via account data", async () => { it("can update push rules via account data", async () => {
const roomId = "!foo:bar"; 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(); expect(pushRule).toBeUndefined();
ext.onResponse({ ext.onResponse({
global: [ global: [
@ -713,16 +713,16 @@ describe("SlidingSyncSdk", () => {
}, },
], ],
}); });
pushRule = client.getRoomPushRule("global", roomId); pushRule = client!.getRoomPushRule("global", roomId)!;
expect(pushRule).toEqual(pushRulesContent.global[PushRuleKind.RoomSpecific][0]); expect(pushRule).toEqual(pushRulesContent.global[PushRuleKind.RoomSpecific]![0]);
}); });
}); });
describe("ExtensionToDevice", () => { describe("ExtensionToDevice", () => {
let ext: Extension; let ext: Extension;
beforeAll(async () => { beforeAll(async () => {
await setupClient(); await setupClient();
const hasSynced = sdk.sync(); const hasSynced = sdk!.sync();
await httpBackend.flushAllExpected(); await httpBackend!.flushAllExpected();
await hasSynced; await hasSynced;
ext = findExtension("to_device"); ext = findExtension("to_device");
}); });
@ -753,7 +753,7 @@ describe("SlidingSyncSdk", () => {
foo: "bar", foo: "bar",
}; };
let called = false; let called = false;
client.once(ClientEvent.ToDeviceEvent, (ev) => { client!.once(ClientEvent.ToDeviceEvent, (ev) => {
expect(ev.getContent()).toEqual(toDeviceContent); expect(ev.getContent()).toEqual(toDeviceContent);
expect(ev.getType()).toEqual(toDeviceType); expect(ev.getType()).toEqual(toDeviceType);
called = true; called = true;
@ -771,7 +771,7 @@ describe("SlidingSyncSdk", () => {
}); });
it("can cancel key verification requests", async () => { it("can cancel key verification requests", async () => {
const seen: Record<string, boolean> = {}; const seen: Record<string, boolean> = {};
client.on(ClientEvent.ToDeviceEvent, (ev) => { client!.on(ClientEvent.ToDeviceEvent, (ev) => {
const evType = ev.getType(); const evType = ev.getType();
expect(seen[evType]).toBeFalsy(); expect(seen[evType]).toBeFalsy();
seen[evType] = true; seen[evType] = true;

View File

@ -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. * Each test will call different functions on SlidingSync which may depend on state from previous tests.
*/ */
describe("SlidingSync", () => { describe("SlidingSync", () => {
let client: MatrixClient = null; let client: MatrixClient | undefined;
let httpBackend: MockHttpBackend = null; let httpBackend: MockHttpBackend | undefined;
const selfUserId = "@alice:localhost"; const selfUserId = "@alice:localhost";
const selfAccessToken = "aseukfgwef"; const selfAccessToken = "aseukfgwef";
const proxyBaseUrl = "http://localhost:8008"; const proxyBaseUrl = "http://localhost:8008";
@ -46,9 +46,9 @@ describe("SlidingSync", () => {
// tear down client/httpBackend globals // tear down client/httpBackend globals
const teardownClient = () => { const teardownClient = () => {
httpBackend.verifyNoOutstandingExpectation(); httpBackend!.verifyNoOutstandingExpectation();
client.stopClient(); client!.stopClient();
return httpBackend.stop(); return httpBackend!.stop();
}; };
describe("start/stop", () => { describe("start/stop", () => {
@ -57,14 +57,14 @@ describe("SlidingSync", () => {
let slidingSync: SlidingSync; let slidingSync: SlidingSync;
it("should start the sync loop upon calling start()", async () => { it("should start the sync loop upon calling start()", async () => {
slidingSync = new SlidingSync(proxyBaseUrl, [], {}, client, 1); slidingSync = new SlidingSync(proxyBaseUrl, [], {}, client!, 1);
const fakeResp = { const fakeResp = {
pos: "a", pos: "a",
lists: [], lists: [],
rooms: {}, rooms: {},
extensions: {}, extensions: {},
}; };
httpBackend.when("POST", syncUrl).respond(200, fakeResp); httpBackend!.when("POST", syncUrl).respond(200, fakeResp);
const p = listenUntil(slidingSync, "SlidingSync.Lifecycle", (state, resp, err) => { const p = listenUntil(slidingSync, "SlidingSync.Lifecycle", (state, resp, err) => {
expect(state).toEqual(SlidingSyncState.RequestFinished); expect(state).toEqual(SlidingSyncState.RequestFinished);
expect(resp).toEqual(fakeResp); expect(resp).toEqual(fakeResp);
@ -72,13 +72,13 @@ describe("SlidingSync", () => {
return true; return true;
}); });
slidingSync.start(); slidingSync.start();
await httpBackend.flushAllExpected(); await httpBackend!.flushAllExpected();
await p; await p;
}); });
it("should stop the sync loop upon calling stop()", () => { it("should stop the sync loop upon calling stop()", () => {
slidingSync.stop(); slidingSync.stop();
httpBackend.verifyNoOutstandingExpectation(); httpBackend!.verifyNoOutstandingExpectation();
}); });
}); });
@ -103,9 +103,9 @@ describe("SlidingSync", () => {
it("should be able to subscribe to a room", async () => { it("should be able to subscribe to a room", async () => {
// add the subscription // add the subscription
slidingSync = new SlidingSync(proxyBaseUrl, [], roomSubInfo, client, 1); slidingSync = new SlidingSync(proxyBaseUrl, [], roomSubInfo, client!, 1);
slidingSync.modifyRoomSubscriptions(new Set([roomId])); slidingSync.modifyRoomSubscriptions(new Set([roomId]));
httpBackend.when("POST", syncUrl).check(function(req) { httpBackend!.when("POST", syncUrl).check(function(req) {
const body = req.data; const body = req.data;
logger.log("room sub", body); logger.log("room sub", body);
expect(body.room_subscriptions).toBeTruthy(); expect(body.room_subscriptions).toBeTruthy();
@ -125,7 +125,7 @@ describe("SlidingSync", () => {
return true; return true;
}); });
slidingSync.start(); slidingSync.start();
await httpBackend.flushAllExpected(); await httpBackend!.flushAllExpected();
await p; await p;
}); });
@ -137,7 +137,7 @@ describe("SlidingSync", () => {
["m.room.member", "*"], ["m.room.member", "*"],
], ],
}; };
httpBackend.when("POST", syncUrl).check(function(req) { httpBackend!.when("POST", syncUrl).check(function(req) {
const body = req.data; const body = req.data;
logger.log("adjusted sub", body); logger.log("adjusted sub", body);
expect(body.room_subscriptions).toBeTruthy(); expect(body.room_subscriptions).toBeTruthy();
@ -158,7 +158,7 @@ describe("SlidingSync", () => {
}); });
slidingSync.modifyRoomSubscriptionInfo(newSubInfo); slidingSync.modifyRoomSubscriptionInfo(newSubInfo);
await httpBackend.flushAllExpected(); await httpBackend!.flushAllExpected();
await p; await p;
// need to set what the new subscription info is for subsequent tests // need to set what the new subscription info is for subsequent tests
roomSubInfo = newSubInfo; roomSubInfo = newSubInfo;
@ -179,7 +179,7 @@ describe("SlidingSync", () => {
required_state: [], required_state: [],
timeline: [], timeline: [],
}; };
httpBackend.when("POST", syncUrl).check(function(req) { httpBackend!.when("POST", syncUrl).check(function(req) {
const body = req.data; const body = req.data;
logger.log("new subs", body); logger.log("new subs", body);
expect(body.room_subscriptions).toBeTruthy(); expect(body.room_subscriptions).toBeTruthy();
@ -204,12 +204,12 @@ describe("SlidingSync", () => {
const subs = slidingSync.getRoomSubscriptions(); const subs = slidingSync.getRoomSubscriptions();
subs.add(anotherRoomID); subs.add(anotherRoomID);
slidingSync.modifyRoomSubscriptions(subs); slidingSync.modifyRoomSubscriptions(subs);
await httpBackend.flushAllExpected(); await httpBackend!.flushAllExpected();
await p; await p;
}); });
it("should be able to unsubscribe from a room", async () => { 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; const body = req.data;
logger.log("unsub request", body); logger.log("unsub request", body);
expect(body.room_subscriptions).toBeFalsy(); expect(body.room_subscriptions).toBeFalsy();
@ -226,7 +226,7 @@ describe("SlidingSync", () => {
// remove the subscription for the first room // remove the subscription for the first room
slidingSync.modifyRoomSubscriptions(new Set([anotherRoomID])); slidingSync.modifyRoomSubscriptions(new Set([anotherRoomID]));
await httpBackend.flushAllExpected(); await httpBackend!.flushAllExpected();
await p; await p;
slidingSync.stop(); slidingSync.stop();
@ -273,8 +273,8 @@ describe("SlidingSync", () => {
is_dm: true, is_dm: true,
}, },
}; };
slidingSync = new SlidingSync(proxyBaseUrl, [listReq], {}, client, 1); slidingSync = new SlidingSync(proxyBaseUrl, [listReq], {}, client!, 1);
httpBackend.when("POST", syncUrl).check(function(req) { httpBackend!.when("POST", syncUrl).check(function(req) {
const body = req.data; const body = req.data;
logger.log("list", body); logger.log("list", body);
expect(body.lists).toBeTruthy(); expect(body.lists).toBeTruthy();
@ -301,7 +301,7 @@ describe("SlidingSync", () => {
return state === SlidingSyncState.Complete; return state === SlidingSyncState.Complete;
}); });
slidingSync.start(); slidingSync.start();
await httpBackend.flushAllExpected(); await httpBackend!.flushAllExpected();
await responseProcessed; await responseProcessed;
expect(listenerData[roomA]).toEqual(rooms[roomA]); expect(listenerData[roomA]).toEqual(rooms[roomA]);
@ -327,7 +327,7 @@ describe("SlidingSync", () => {
it("should be possible to adjust list ranges", async () => { it("should be possible to adjust list ranges", async () => {
// modify the list ranges // modify the list ranges
httpBackend.when("POST", syncUrl).check(function(req) { httpBackend!.when("POST", syncUrl).check(function(req) {
const body = req.data; const body = req.data;
logger.log("next ranges", body.lists[0].ranges); logger.log("next ranges", body.lists[0].ranges);
expect(body.lists).toBeTruthy(); expect(body.lists).toBeTruthy();
@ -351,7 +351,7 @@ describe("SlidingSync", () => {
return state === SlidingSyncState.RequestFinished; return state === SlidingSyncState.RequestFinished;
}); });
slidingSync.setListRanges(0, newRanges); slidingSync.setListRanges(0, newRanges);
await httpBackend.flushAllExpected(); await httpBackend!.flushAllExpected();
await responseProcessed; await responseProcessed;
}); });
@ -364,7 +364,7 @@ describe("SlidingSync", () => {
"is_dm": true, "is_dm": true,
}, },
}; };
httpBackend.when("POST", syncUrl).check(function(req) { httpBackend!.when("POST", syncUrl).check(function(req) {
const body = req.data; const body = req.data;
logger.log("extra list", body); logger.log("extra list", body);
expect(body.lists).toBeTruthy(); expect(body.lists).toBeTruthy();
@ -403,13 +403,13 @@ describe("SlidingSync", () => {
return state === SlidingSyncState.Complete; return state === SlidingSyncState.Complete;
}); });
slidingSync.setList(1, extraListReq); slidingSync.setList(1, extraListReq);
await httpBackend.flushAllExpected(); await httpBackend!.flushAllExpected();
await responseProcessed; await responseProcessed;
}); });
it("should be possible to get list DELETE/INSERTs", async () => { it("should be possible to get list DELETE/INSERTs", async () => {
// move C (2) to A (0) // move C (2) to A (0)
httpBackend.when("POST", syncUrl).respond(200, { httpBackend!.when("POST", syncUrl).respond(200, {
pos: "e", pos: "e",
lists: [{ lists: [{
count: 500, count: 500,
@ -440,12 +440,12 @@ describe("SlidingSync", () => {
let responseProcessed = listenUntil(slidingSync, "SlidingSync.Lifecycle", (state) => { let responseProcessed = listenUntil(slidingSync, "SlidingSync.Lifecycle", (state) => {
return state === SlidingSyncState.Complete; return state === SlidingSyncState.Complete;
}); });
await httpBackend.flushAllExpected(); await httpBackend!.flushAllExpected();
await responseProcessed; await responseProcessed;
await listPromise; await listPromise;
// move C (0) back to A (2) // move C (0) back to A (2)
httpBackend.when("POST", syncUrl).respond(200, { httpBackend!.when("POST", syncUrl).respond(200, {
pos: "f", pos: "f",
lists: [{ lists: [{
count: 500, count: 500,
@ -476,13 +476,13 @@ describe("SlidingSync", () => {
responseProcessed = listenUntil(slidingSync, "SlidingSync.Lifecycle", (state) => { responseProcessed = listenUntil(slidingSync, "SlidingSync.Lifecycle", (state) => {
return state === SlidingSyncState.Complete; return state === SlidingSyncState.Complete;
}); });
await httpBackend.flushAllExpected(); await httpBackend!.flushAllExpected();
await responseProcessed; await responseProcessed;
await listPromise; await listPromise;
}); });
it("should ignore invalid list indexes", async () => { it("should ignore invalid list indexes", async () => {
httpBackend.when("POST", syncUrl).respond(200, { httpBackend!.when("POST", syncUrl).respond(200, {
pos: "e", pos: "e",
lists: [{ lists: [{
count: 500, count: 500,
@ -509,13 +509,13 @@ describe("SlidingSync", () => {
const responseProcessed = listenUntil(slidingSync, "SlidingSync.Lifecycle", (state) => { const responseProcessed = listenUntil(slidingSync, "SlidingSync.Lifecycle", (state) => {
return state === SlidingSyncState.Complete; return state === SlidingSyncState.Complete;
}); });
await httpBackend.flushAllExpected(); await httpBackend!.flushAllExpected();
await responseProcessed; await responseProcessed;
await listPromise; await listPromise;
}); });
it("should be possible to update a list", async () => { it("should be possible to update a list", async () => {
httpBackend.when("POST", syncUrl).respond(200, { httpBackend!.when("POST", syncUrl).respond(200, {
pos: "g", pos: "g",
lists: [{ lists: [{
count: 42, count: 42,
@ -555,7 +555,7 @@ describe("SlidingSync", () => {
const responseProcessed = listenUntil(slidingSync, "SlidingSync.Lifecycle", (state) => { const responseProcessed = listenUntil(slidingSync, "SlidingSync.Lifecycle", (state) => {
return state === SlidingSyncState.Complete; return state === SlidingSyncState.Complete;
}); });
await httpBackend.flushAllExpected(); await httpBackend!.flushAllExpected();
await responseProcessed; await responseProcessed;
await listPromise; await listPromise;
}); });
@ -567,7 +567,7 @@ describe("SlidingSync", () => {
1: roomC, 1: roomC,
}; };
expect(slidingSync.getListData(0).roomIndexToRoomId).toEqual(indexToRoomId); expect(slidingSync.getListData(0).roomIndexToRoomId).toEqual(indexToRoomId);
httpBackend.when("POST", syncUrl).respond(200, { httpBackend!.when("POST", syncUrl).respond(200, {
pos: "f", pos: "f",
// currently the list is [B,C] so we will insert D then immediately delete it // currently the list is [B,C] so we will insert D then immediately delete it
lists: [{ lists: [{
@ -598,7 +598,7 @@ describe("SlidingSync", () => {
const responseProcessed = listenUntil(slidingSync, "SlidingSync.Lifecycle", (state) => { const responseProcessed = listenUntil(slidingSync, "SlidingSync.Lifecycle", (state) => {
return state === SlidingSyncState.Complete; return state === SlidingSyncState.Complete;
}); });
await httpBackend.flushAllExpected(); await httpBackend!.flushAllExpected();
await responseProcessed; await responseProcessed;
await listPromise; await listPromise;
}); });
@ -608,7 +608,7 @@ describe("SlidingSync", () => {
0: roomB, 0: roomB,
1: roomC, 1: roomC,
}); });
httpBackend.when("POST", syncUrl).respond(200, { httpBackend!.when("POST", syncUrl).respond(200, {
pos: "g", pos: "g",
lists: [{ lists: [{
count: 499, count: 499,
@ -634,7 +634,7 @@ describe("SlidingSync", () => {
const responseProcessed = listenUntil(slidingSync, "SlidingSync.Lifecycle", (state) => { const responseProcessed = listenUntil(slidingSync, "SlidingSync.Lifecycle", (state) => {
return state === SlidingSyncState.Complete; return state === SlidingSyncState.Complete;
}); });
await httpBackend.flushAllExpected(); await httpBackend!.flushAllExpected();
await responseProcessed; await responseProcessed;
await listPromise; await listPromise;
}); });
@ -643,7 +643,7 @@ describe("SlidingSync", () => {
expect(slidingSync.getListData(0).roomIndexToRoomId).toEqual({ expect(slidingSync.getListData(0).roomIndexToRoomId).toEqual({
0: roomC, 0: roomC,
}); });
httpBackend.when("POST", syncUrl).respond(200, { httpBackend!.when("POST", syncUrl).respond(200, {
pos: "h", pos: "h",
lists: [{ lists: [{
count: 500, count: 500,
@ -670,11 +670,11 @@ describe("SlidingSync", () => {
let responseProcessed = listenUntil(slidingSync, "SlidingSync.Lifecycle", (state) => { let responseProcessed = listenUntil(slidingSync, "SlidingSync.Lifecycle", (state) => {
return state === SlidingSyncState.Complete; return state === SlidingSyncState.Complete;
}); });
await httpBackend.flushAllExpected(); await httpBackend!.flushAllExpected();
await responseProcessed; await responseProcessed;
await listPromise; await listPromise;
httpBackend.when("POST", syncUrl).respond(200, { httpBackend!.when("POST", syncUrl).respond(200, {
pos: "h", pos: "h",
lists: [{ lists: [{
count: 501, count: 501,
@ -702,7 +702,7 @@ describe("SlidingSync", () => {
responseProcessed = listenUntil(slidingSync, "SlidingSync.Lifecycle", (state) => { responseProcessed = listenUntil(slidingSync, "SlidingSync.Lifecycle", (state) => {
return state === SlidingSyncState.Complete; return state === SlidingSyncState.Complete;
}); });
await httpBackend.flushAllExpected(); await httpBackend!.flushAllExpected();
await responseProcessed; await responseProcessed;
await listPromise; await listPromise;
slidingSync.stop(); slidingSync.stop();
@ -725,11 +725,11 @@ describe("SlidingSync", () => {
], ],
}; };
// add the subscription // add the subscription
slidingSync = new SlidingSync(proxyBaseUrl, [], roomSubInfo, client, 1); slidingSync = new SlidingSync(proxyBaseUrl, [], roomSubInfo, client!, 1);
// modification before SlidingSync.start() // modification before SlidingSync.start()
const subscribePromise = slidingSync.modifyRoomSubscriptions(new Set([roomId])); const subscribePromise = slidingSync.modifyRoomSubscriptions(new Set([roomId]));
let txnId; let txnId;
httpBackend.when("POST", syncUrl).check(function(req) { httpBackend!.when("POST", syncUrl).check(function(req) {
const body = req.data; const body = req.data;
logger.debug("got ", body); logger.debug("got ", body);
expect(body.room_subscriptions).toBeTruthy(); expect(body.room_subscriptions).toBeTruthy();
@ -752,7 +752,7 @@ describe("SlidingSync", () => {
}; };
}); });
slidingSync.start(); slidingSync.start();
await httpBackend.flushAllExpected(); await httpBackend!.flushAllExpected();
await subscribePromise; await subscribePromise;
}); });
it("should resolve setList during a connection", async () => { it("should resolve setList during a connection", async () => {
@ -761,7 +761,7 @@ describe("SlidingSync", () => {
}; };
const promise = slidingSync.setList(0, newList); const promise = slidingSync.setList(0, newList);
let txnId; let txnId;
httpBackend.when("POST", syncUrl).check(function(req) { httpBackend!.when("POST", syncUrl).check(function(req) {
const body = req.data; const body = req.data;
logger.debug("got ", body); logger.debug("got ", body);
expect(body.room_subscriptions).toBeFalsy(); expect(body.room_subscriptions).toBeFalsy();
@ -776,14 +776,14 @@ describe("SlidingSync", () => {
extensions: {}, extensions: {},
}; };
}); });
await httpBackend.flushAllExpected(); await httpBackend!.flushAllExpected();
await promise; await promise;
expect(txnId).toBeDefined(); expect(txnId).toBeDefined();
}); });
it("should resolve setListRanges during a connection", async () => { it("should resolve setListRanges during a connection", async () => {
const promise = slidingSync.setListRanges(0, [[20, 40]]); const promise = slidingSync.setListRanges(0, [[20, 40]]);
let txnId; let txnId;
httpBackend.when("POST", syncUrl).check(function(req) { httpBackend!.when("POST", syncUrl).check(function(req) {
const body = req.data; const body = req.data;
logger.debug("got ", body); logger.debug("got ", body);
expect(body.room_subscriptions).toBeFalsy(); expect(body.room_subscriptions).toBeFalsy();
@ -800,7 +800,7 @@ describe("SlidingSync", () => {
extensions: {}, extensions: {},
}; };
}); });
await httpBackend.flushAllExpected(); await httpBackend!.flushAllExpected();
await promise; await promise;
expect(txnId).toBeDefined(); expect(txnId).toBeDefined();
}); });
@ -809,7 +809,7 @@ describe("SlidingSync", () => {
timeline_limit: 99, timeline_limit: 99,
}); });
let txnId; let txnId;
httpBackend.when("POST", syncUrl).check(function(req) { httpBackend!.when("POST", syncUrl).check(function(req) {
const body = req.data; const body = req.data;
logger.debug("got ", body); logger.debug("got ", body);
expect(body.room_subscriptions).toBeTruthy(); expect(body.room_subscriptions).toBeTruthy();
@ -825,22 +825,22 @@ describe("SlidingSync", () => {
extensions: {}, extensions: {},
}; };
}); });
await httpBackend.flushAllExpected(); await httpBackend!.flushAllExpected();
await promise; await promise;
expect(txnId).toBeDefined(); expect(txnId).toBeDefined();
}); });
it("should reject earlier pending promises if a later transaction is acknowledged", async () => { 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. // 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) { const pushTxn = function(req) {
gotTxnIds.push(req.data.txn_id); gotTxnIds.push(req.data.txn_id);
}; };
const failPromise = slidingSync.setListRanges(0, [[20, 40]]); const failPromise = slidingSync.setListRanges(0, [[20, 40]]);
httpBackend.when("POST", syncUrl).check(pushTxn).respond(200, { pos: "e" }); // missing txn_id httpBackend!.when("POST", syncUrl).check(pushTxn).respond(200, { pos: "e" }); // missing txn_id
await httpBackend.flushAllExpected(); await httpBackend!.flushAllExpected();
const failPromise2 = slidingSync.setListRanges(0, [[60, 70]]); const failPromise2 = slidingSync.setListRanges(0, [[60, 70]]);
httpBackend.when("POST", syncUrl).check(pushTxn).respond(200, { pos: "f" }); // missing txn_id httpBackend!.when("POST", syncUrl).check(pushTxn).respond(200, { pos: "f" }); // missing txn_id
await httpBackend.flushAllExpected(); await httpBackend!.flushAllExpected();
// attach rejection handlers now else if we do it later Jest treats that as an unhandled rejection // attach rejection handlers now else if we do it later Jest treats that as an unhandled rejection
// which is a fail. // which is a fail.
@ -849,7 +849,7 @@ describe("SlidingSync", () => {
const okPromise = slidingSync.setListRanges(0, [[0, 20]]); const okPromise = slidingSync.setListRanges(0, [[0, 20]]);
let txnId; let txnId;
httpBackend.when("POST", syncUrl).check((req) => { httpBackend!.when("POST", syncUrl).check((req) => {
txnId = req.data.txn_id; txnId = req.data.txn_id;
}).respond(200, () => { }).respond(200, () => {
// include the txn_id, earlier requests should now be reject()ed. // include the txn_id, earlier requests should now be reject()ed.
@ -858,23 +858,23 @@ describe("SlidingSync", () => {
txn_id: txnId, txn_id: txnId,
}; };
}); });
await httpBackend.flushAllExpected(); await httpBackend!.flushAllExpected();
await okPromise; await okPromise;
expect(txnId).toBeDefined(); expect(txnId).toBeDefined();
}); });
it("should not reject later pending promises if an earlier transaction is acknowledged", async () => { 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. // 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) { const pushTxn = function(req) {
gotTxnIds.push(req.data.txn_id); gotTxnIds.push(req.data?.txn_id);
}; };
const A = slidingSync.setListRanges(0, [[20, 40]]); const A = slidingSync.setListRanges(0, [[20, 40]]);
httpBackend.when("POST", syncUrl).check(pushTxn).respond(200, { pos: "A" }); httpBackend!.when("POST", syncUrl).check(pushTxn).respond(200, { pos: "A" });
await httpBackend.flushAllExpected(); await httpBackend!.flushAllExpected();
const B = slidingSync.setListRanges(0, [[60, 70]]); const B = slidingSync.setListRanges(0, [[60, 70]]);
httpBackend.when("POST", syncUrl).check(pushTxn).respond(200, { pos: "B" }); // missing txn_id httpBackend!.when("POST", syncUrl).check(pushTxn).respond(200, { pos: "B" }); // missing txn_id
await httpBackend.flushAllExpected(); await httpBackend!.flushAllExpected();
// attach rejection handlers now else if we do it later Jest treats that as an unhandled rejection // attach rejection handlers now else if we do it later Jest treats that as an unhandled rejection
// which is a fail. // which is a fail.
@ -885,14 +885,14 @@ describe("SlidingSync", () => {
C.finally(() => { C.finally(() => {
pendingC = false; 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 // include the txn_id for B, so C's promise is outstanding
return { return {
pos: "C", pos: "C",
txn_id: gotTxnIds[1], txn_id: gotTxnIds[1],
}; };
}); });
await httpBackend.flushAllExpected(); await httpBackend!.flushAllExpected();
// A is rejected, see above // A is rejected, see above
expect(B).resolves.toEqual(gotTxnIds[1]); // B is resolved expect(B).resolves.toEqual(gotTxnIds[1]); // B is resolved
expect(pendingC).toBe(true); // C is pending still expect(pendingC).toBe(true); // C is pending still
@ -904,7 +904,7 @@ describe("SlidingSync", () => {
pending = false; pending = false;
}); });
let txnId; let txnId;
httpBackend.when("POST", syncUrl).check(function(req) { httpBackend!.when("POST", syncUrl).check(function(req) {
const body = req.data; const body = req.data;
logger.debug("got ", body); logger.debug("got ", body);
expect(body.room_subscriptions).toBeFalsy(); expect(body.room_subscriptions).toBeFalsy();
@ -921,7 +921,7 @@ describe("SlidingSync", () => {
extensions: {}, extensions: {},
}; };
}); });
await httpBackend.flushAllExpected(); await httpBackend!.flushAllExpected();
expect(txnId).toBeDefined(); expect(txnId).toBeDefined();
expect(pending).toBe(true); expect(pending).toBe(true);
slidingSync.stop(); slidingSync.stop();
@ -963,10 +963,10 @@ describe("SlidingSync", () => {
}; };
it("should be able to register an extension", async () => { it("should be able to register an extension", async () => {
slidingSync = new SlidingSync(proxyBaseUrl, [], {}, client, 1); slidingSync = new SlidingSync(proxyBaseUrl, [], {}, client!, 1);
slidingSync.registerExtension(extPre); slidingSync.registerExtension(extPre);
const callbackOrder = []; const callbackOrder: string[] = [];
let extensionOnResponseCalled = false; let extensionOnResponseCalled = false;
onPreExtensionRequest = () => { onPreExtensionRequest = () => {
return extReq; return extReq;
@ -977,7 +977,7 @@ describe("SlidingSync", () => {
expect(resp).toEqual(extResp); expect(resp).toEqual(extResp);
}; };
httpBackend.when("POST", syncUrl).check(function(req) { httpBackend!.when("POST", syncUrl).check(function(req) {
const body = req.data; const body = req.data;
logger.log("ext req", body); logger.log("ext req", body);
expect(body.extensions).toBeTruthy(); expect(body.extensions).toBeTruthy();
@ -998,7 +998,7 @@ describe("SlidingSync", () => {
} }
}); });
slidingSync.start(); slidingSync.start();
await httpBackend.flushAllExpected(); await httpBackend!.flushAllExpected();
await p; await p;
expect(extensionOnResponseCalled).toBe(true); expect(extensionOnResponseCalled).toBe(true);
expect(callbackOrder).toEqual(["onPreExtensionResponse", "Lifecycle"]); expect(callbackOrder).toEqual(["onPreExtensionResponse", "Lifecycle"]);
@ -1012,7 +1012,7 @@ describe("SlidingSync", () => {
onPreExtensionResponse = (resp) => { onPreExtensionResponse = (resp) => {
responseCalled = true; responseCalled = true;
}; };
httpBackend.when("POST", syncUrl).check(function(req) { httpBackend!.when("POST", syncUrl).check(function(req) {
const body = req.data; const body = req.data;
logger.log("ext req nothing", body); logger.log("ext req nothing", body);
expect(body.extensions).toBeTruthy(); expect(body.extensions).toBeTruthy();
@ -1030,7 +1030,7 @@ describe("SlidingSync", () => {
const p = listenUntil(slidingSync, "SlidingSync.Lifecycle", (state, resp, err) => { const p = listenUntil(slidingSync, "SlidingSync.Lifecycle", (state, resp, err) => {
return state === SlidingSyncState.Complete; return state === SlidingSyncState.Complete;
}); });
await httpBackend.flushAllExpected(); await httpBackend!.flushAllExpected();
await p; await p;
expect(responseCalled).toBe(false); expect(responseCalled).toBe(false);
}); });
@ -1041,13 +1041,13 @@ describe("SlidingSync", () => {
return extReq; return extReq;
}; };
let responseCalled = false; let responseCalled = false;
const callbackOrder = []; const callbackOrder: string[] = [];
onPostExtensionResponse = (resp) => { onPostExtensionResponse = (resp) => {
expect(resp).toEqual(extResp); expect(resp).toEqual(extResp);
responseCalled = true; responseCalled = true;
callbackOrder.push("onPostExtensionResponse"); callbackOrder.push("onPostExtensionResponse");
}; };
httpBackend.when("POST", syncUrl).check(function(req) { httpBackend!.when("POST", syncUrl).check(function(req) {
const body = req.data; const body = req.data;
logger.log("ext req after start", body); logger.log("ext req after start", body);
expect(body.extensions).toBeTruthy(); expect(body.extensions).toBeTruthy();
@ -1071,7 +1071,7 @@ describe("SlidingSync", () => {
return true; return true;
} }
}); });
await httpBackend.flushAllExpected(); await httpBackend!.flushAllExpected();
await p; await p;
expect(responseCalled).toBe(true); expect(responseCalled).toBe(true);
expect(callbackOrder).toEqual(["Lifecycle", "onPostExtensionResponse"]); expect(callbackOrder).toEqual(["Lifecycle", "onPostExtensionResponse"]);
@ -1079,7 +1079,7 @@ describe("SlidingSync", () => {
}); });
it("is not possible to register the same extension name twice", async () => { 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); slidingSync.registerExtension(extPre);
expect(() => { slidingSync.registerExtension(extPre); }).toThrow(); expect(() => { slidingSync.registerExtension(extPre); }).toThrow();
}); });
@ -1106,7 +1106,7 @@ function listenUntil<T>(
callback: (...args: any[]) => T, callback: (...args: any[]) => T,
timeoutMs = 500, timeoutMs = 500,
): Promise<T> { ): Promise<T> {
const trace = new Error().stack.split(`\n`)[2]; const trace = new Error().stack?.split(`\n`)[2];
return Promise.race([new Promise<T>((resolve, reject) => { return Promise.race([new Promise<T>((resolve, reject) => {
const wrapper = (...args) => { const wrapper = (...args) => {
try { try {