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

test typescriptification - autodiscovery / crypto specs (#2550)

* spec/unit/autodiscovery.spec.js -> spec/unit/autodiscovery.spec.ts

* fix ts in autodiscovery.spec

* renamed:    spec/unit/crypto.spec.js -> spec/unit/crypto.spec.ts

* fix ts in crypto.spec

* fix some strict errors
This commit is contained in:
Kerry
2022-07-29 11:11:01 +02:00
committed by GitHub
parent 3824f65d15
commit 4a4241806e
4 changed files with 152 additions and 113 deletions

View File

@ -1,6 +1,6 @@
/* /*
Copyright 2018 New Vector Ltd Copyright 2018 New Vector Ltd
Copyright 2019 The Matrix.org Foundation C.I.C. Copyright 2019, 2022 The Matrix.org Foundation C.I.C.
Licensed under the Apache License, Version 2.0 (the "License"); Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License. you may not use this file except in compliance with the License.
@ -17,19 +17,20 @@ limitations under the License.
import MockHttpBackend from "matrix-mock-request"; import MockHttpBackend from "matrix-mock-request";
import * as sdk from "../../src"; import { request } from "../../src/matrix";
import { AutoDiscovery } from "../../src/autodiscovery"; import { AutoDiscovery } from "../../src/autodiscovery";
describe("AutoDiscovery", function() { describe("AutoDiscovery", function() {
let httpBackend = null; const getHttpBackend = (): MockHttpBackend => {
const httpBackend = new MockHttpBackend();
beforeEach(function() { request(httpBackend.requestFn);
httpBackend = new MockHttpBackend(); return httpBackend;
sdk.request(httpBackend.requestFn); };
});
it("should throw an error when no domain is specified", function() { it("should throw an error when no domain is specified", function() {
getHttpBackend();
return Promise.all([ return Promise.all([
// @ts-ignore testing no args
AutoDiscovery.findClientConfig(/* no args */).then(() => { AutoDiscovery.findClientConfig(/* no args */).then(() => {
throw new Error("Expected a failure, not success with no args"); throw new Error("Expected a failure, not success with no args");
}, () => { }, () => {
@ -42,13 +43,13 @@ describe("AutoDiscovery", function() {
return true; return true;
}), }),
AutoDiscovery.findClientConfig(null).then(() => { AutoDiscovery.findClientConfig(null as any).then(() => {
throw new Error("Expected a failure, not success with null"); throw new Error("Expected a failure, not success with null");
}, () => { }, () => {
return true; return true;
}), }),
AutoDiscovery.findClientConfig(true).then(() => { AutoDiscovery.findClientConfig(true as any).then(() => {
throw new Error("Expected a failure, not success with a non-string"); throw new Error("Expected a failure, not success with a non-string");
}, () => { }, () => {
return true; return true;
@ -57,6 +58,7 @@ describe("AutoDiscovery", function() {
}); });
it("should return PROMPT when .well-known 404s", function() { it("should return PROMPT when .well-known 404s", function() {
const httpBackend = getHttpBackend();
httpBackend.when("GET", "/.well-known/matrix/client").respond(404, {}); httpBackend.when("GET", "/.well-known/matrix/client").respond(404, {});
return Promise.all([ return Promise.all([
httpBackend.flushAllExpected(), httpBackend.flushAllExpected(),
@ -80,6 +82,7 @@ describe("AutoDiscovery", function() {
}); });
it("should return FAIL_PROMPT when .well-known returns a 500 error", function() { it("should return FAIL_PROMPT when .well-known returns a 500 error", function() {
const httpBackend = getHttpBackend();
httpBackend.when("GET", "/.well-known/matrix/client").respond(500, {}); httpBackend.when("GET", "/.well-known/matrix/client").respond(500, {});
return Promise.all([ return Promise.all([
httpBackend.flushAllExpected(), httpBackend.flushAllExpected(),
@ -103,6 +106,7 @@ describe("AutoDiscovery", function() {
}); });
it("should return FAIL_PROMPT when .well-known returns a 400 error", function() { it("should return FAIL_PROMPT when .well-known returns a 400 error", function() {
const httpBackend = getHttpBackend();
httpBackend.when("GET", "/.well-known/matrix/client").respond(400, {}); httpBackend.when("GET", "/.well-known/matrix/client").respond(400, {});
return Promise.all([ return Promise.all([
httpBackend.flushAllExpected(), httpBackend.flushAllExpected(),
@ -126,6 +130,7 @@ describe("AutoDiscovery", function() {
}); });
it("should return FAIL_PROMPT when .well-known returns an empty body", function() { it("should return FAIL_PROMPT when .well-known returns an empty body", function() {
const httpBackend = getHttpBackend();
httpBackend.when("GET", "/.well-known/matrix/client").respond(200, ""); httpBackend.when("GET", "/.well-known/matrix/client").respond(200, "");
return Promise.all([ return Promise.all([
httpBackend.flushAllExpected(), httpBackend.flushAllExpected(),
@ -149,6 +154,7 @@ describe("AutoDiscovery", function() {
}); });
it("should return FAIL_PROMPT when .well-known returns not-JSON", function() { it("should return FAIL_PROMPT when .well-known returns not-JSON", function() {
const httpBackend = getHttpBackend();
httpBackend.when("GET", "/.well-known/matrix/client").respond(200, "abc"); httpBackend.when("GET", "/.well-known/matrix/client").respond(200, "abc");
return Promise.all([ return Promise.all([
httpBackend.flushAllExpected(), httpBackend.flushAllExpected(),
@ -173,6 +179,7 @@ describe("AutoDiscovery", function() {
it("should return FAIL_PROMPT when .well-known does not have a base_url for " + it("should return FAIL_PROMPT when .well-known does not have a base_url for " +
"m.homeserver (empty string)", function() { "m.homeserver (empty string)", function() {
const httpBackend = getHttpBackend();
httpBackend.when("GET", "/.well-known/matrix/client").respond(200, { httpBackend.when("GET", "/.well-known/matrix/client").respond(200, {
"m.homeserver": { "m.homeserver": {
base_url: "", base_url: "",
@ -201,6 +208,7 @@ describe("AutoDiscovery", function() {
it("should return FAIL_PROMPT when .well-known does not have a base_url for " + it("should return FAIL_PROMPT when .well-known does not have a base_url for " +
"m.homeserver (no property)", function() { "m.homeserver (no property)", function() {
const httpBackend = getHttpBackend();
httpBackend.when("GET", "/.well-known/matrix/client").respond(200, { httpBackend.when("GET", "/.well-known/matrix/client").respond(200, {
"m.homeserver": {}, "m.homeserver": {},
}); });
@ -227,6 +235,7 @@ describe("AutoDiscovery", function() {
it("should return FAIL_ERROR when .well-known has an invalid base_url for " + it("should return FAIL_ERROR when .well-known has an invalid base_url for " +
"m.homeserver (disallowed scheme)", function() { "m.homeserver (disallowed scheme)", function() {
const httpBackend = getHttpBackend();
httpBackend.when("GET", "/.well-known/matrix/client").respond(200, { httpBackend.when("GET", "/.well-known/matrix/client").respond(200, {
"m.homeserver": { "m.homeserver": {
base_url: "mxc://example.org", base_url: "mxc://example.org",
@ -255,6 +264,7 @@ describe("AutoDiscovery", function() {
it("should return FAIL_ERROR when .well-known has an invalid base_url for " + it("should return FAIL_ERROR when .well-known has an invalid base_url for " +
"m.homeserver (verification failure: 404)", function() { "m.homeserver (verification failure: 404)", function() {
const httpBackend = getHttpBackend();
httpBackend.when("GET", "/_matrix/client/versions").respond(404, {}); httpBackend.when("GET", "/_matrix/client/versions").respond(404, {});
httpBackend.when("GET", "/.well-known/matrix/client").respond(200, { httpBackend.when("GET", "/.well-known/matrix/client").respond(200, {
"m.homeserver": { "m.homeserver": {
@ -284,6 +294,7 @@ describe("AutoDiscovery", function() {
it("should return FAIL_ERROR when .well-known has an invalid base_url for " + it("should return FAIL_ERROR when .well-known has an invalid base_url for " +
"m.homeserver (verification failure: 500)", function() { "m.homeserver (verification failure: 500)", function() {
const httpBackend = getHttpBackend();
httpBackend.when("GET", "/_matrix/client/versions").respond(500, {}); httpBackend.when("GET", "/_matrix/client/versions").respond(500, {});
httpBackend.when("GET", "/.well-known/matrix/client").respond(200, { httpBackend.when("GET", "/.well-known/matrix/client").respond(200, {
"m.homeserver": { "m.homeserver": {
@ -313,6 +324,7 @@ describe("AutoDiscovery", function() {
it("should return FAIL_ERROR when .well-known has an invalid base_url for " + it("should return FAIL_ERROR when .well-known has an invalid base_url for " +
"m.homeserver (verification failure: 200 but wrong content)", function() { "m.homeserver (verification failure: 200 but wrong content)", function() {
const httpBackend = getHttpBackend();
httpBackend.when("GET", "/_matrix/client/versions").respond(200, { httpBackend.when("GET", "/_matrix/client/versions").respond(200, {
not_matrix_versions: ["r0.0.1"], not_matrix_versions: ["r0.0.1"],
}); });
@ -344,8 +356,9 @@ describe("AutoDiscovery", function() {
it("should return SUCCESS when .well-known has a verifiably accurate base_url for " + it("should return SUCCESS when .well-known has a verifiably accurate base_url for " +
"m.homeserver", function() { "m.homeserver", function() {
const httpBackend = getHttpBackend();
httpBackend.when("GET", "/_matrix/client/versions").check((req) => { httpBackend.when("GET", "/_matrix/client/versions").check((req) => {
expect(req.opts.uri).toEqual("https://example.org/_matrix/client/versions"); expect(req.path).toEqual("https://example.org/_matrix/client/versions");
}).respond(200, { }).respond(200, {
versions: ["r0.0.1"], versions: ["r0.0.1"],
}); });
@ -376,8 +389,9 @@ describe("AutoDiscovery", function() {
}); });
it("should return SUCCESS with the right homeserver URL", function() { it("should return SUCCESS with the right homeserver URL", function() {
const httpBackend = getHttpBackend();
httpBackend.when("GET", "/_matrix/client/versions").check((req) => { httpBackend.when("GET", "/_matrix/client/versions").check((req) => {
expect(req.opts.uri) expect(req.path)
.toEqual("https://chat.example.org/_matrix/client/versions"); .toEqual("https://chat.example.org/_matrix/client/versions");
}).respond(200, { }).respond(200, {
versions: ["r0.0.1"], versions: ["r0.0.1"],
@ -411,8 +425,9 @@ describe("AutoDiscovery", function() {
it("should return SUCCESS / FAIL_PROMPT when the identity server configuration " + it("should return SUCCESS / FAIL_PROMPT when the identity server configuration " +
"is wrong (missing base_url)", function() { "is wrong (missing base_url)", function() {
const httpBackend = getHttpBackend();
httpBackend.when("GET", "/_matrix/client/versions").check((req) => { httpBackend.when("GET", "/_matrix/client/versions").check((req) => {
expect(req.opts.uri) expect(req.path)
.toEqual("https://chat.example.org/_matrix/client/versions"); .toEqual("https://chat.example.org/_matrix/client/versions");
}).respond(200, { }).respond(200, {
versions: ["r0.0.1"], versions: ["r0.0.1"],
@ -451,8 +466,9 @@ describe("AutoDiscovery", function() {
it("should return SUCCESS / FAIL_PROMPT when the identity server configuration " + it("should return SUCCESS / FAIL_PROMPT when the identity server configuration " +
"is wrong (empty base_url)", function() { "is wrong (empty base_url)", function() {
const httpBackend = getHttpBackend();
httpBackend.when("GET", "/_matrix/client/versions").check((req) => { httpBackend.when("GET", "/_matrix/client/versions").check((req) => {
expect(req.opts.uri) expect(req.path)
.toEqual("https://chat.example.org/_matrix/client/versions"); .toEqual("https://chat.example.org/_matrix/client/versions");
}).respond(200, { }).respond(200, {
versions: ["r0.0.1"], versions: ["r0.0.1"],
@ -491,8 +507,9 @@ describe("AutoDiscovery", function() {
it("should return SUCCESS / FAIL_PROMPT when the identity server configuration " + it("should return SUCCESS / FAIL_PROMPT when the identity server configuration " +
"is wrong (validation error: 404)", function() { "is wrong (validation error: 404)", function() {
const httpBackend = getHttpBackend();
httpBackend.when("GET", "/_matrix/client/versions").check((req) => { httpBackend.when("GET", "/_matrix/client/versions").check((req) => {
expect(req.opts.uri) expect(req.path)
.toEqual("https://chat.example.org/_matrix/client/versions"); .toEqual("https://chat.example.org/_matrix/client/versions");
}).respond(200, { }).respond(200, {
versions: ["r0.0.1"], versions: ["r0.0.1"],
@ -532,8 +549,9 @@ describe("AutoDiscovery", function() {
it("should return SUCCESS / FAIL_PROMPT when the identity server configuration " + it("should return SUCCESS / FAIL_PROMPT when the identity server configuration " +
"is wrong (validation error: 500)", function() { "is wrong (validation error: 500)", function() {
const httpBackend = getHttpBackend();
httpBackend.when("GET", "/_matrix/client/versions").check((req) => { httpBackend.when("GET", "/_matrix/client/versions").check((req) => {
expect(req.opts.uri) expect(req.path)
.toEqual("https://chat.example.org/_matrix/client/versions"); .toEqual("https://chat.example.org/_matrix/client/versions");
}).respond(200, { }).respond(200, {
versions: ["r0.0.1"], versions: ["r0.0.1"],
@ -573,14 +591,15 @@ describe("AutoDiscovery", function() {
it("should return SUCCESS when the identity server configuration is " + it("should return SUCCESS when the identity server configuration is " +
"verifiably accurate", function() { "verifiably accurate", function() {
const httpBackend = getHttpBackend();
httpBackend.when("GET", "/_matrix/client/versions").check((req) => { httpBackend.when("GET", "/_matrix/client/versions").check((req) => {
expect(req.opts.uri) expect(req.path)
.toEqual("https://chat.example.org/_matrix/client/versions"); .toEqual("https://chat.example.org/_matrix/client/versions");
}).respond(200, { }).respond(200, {
versions: ["r0.0.1"], versions: ["r0.0.1"],
}); });
httpBackend.when("GET", "/_matrix/identity/api/v1").check((req) => { httpBackend.when("GET", "/_matrix/identity/api/v1").check((req) => {
expect(req.opts.uri) expect(req.path)
.toEqual("https://identity.example.org/_matrix/identity/api/v1"); .toEqual("https://identity.example.org/_matrix/identity/api/v1");
}).respond(200, {}); }).respond(200, {});
httpBackend.when("GET", "/.well-known/matrix/client").respond(200, { httpBackend.when("GET", "/.well-known/matrix/client").respond(200, {
@ -615,14 +634,15 @@ describe("AutoDiscovery", function() {
it("should return SUCCESS and preserve non-standard keys from the " + it("should return SUCCESS and preserve non-standard keys from the " +
".well-known response", function() { ".well-known response", function() {
const httpBackend = getHttpBackend();
httpBackend.when("GET", "/_matrix/client/versions").check((req) => { httpBackend.when("GET", "/_matrix/client/versions").check((req) => {
expect(req.opts.uri) expect(req.path)
.toEqual("https://chat.example.org/_matrix/client/versions"); .toEqual("https://chat.example.org/_matrix/client/versions");
}).respond(200, { }).respond(200, {
versions: ["r0.0.1"], versions: ["r0.0.1"],
}); });
httpBackend.when("GET", "/_matrix/identity/api/v1").check((req) => { httpBackend.when("GET", "/_matrix/identity/api/v1").check((req) => {
expect(req.opts.uri) expect(req.path)
.toEqual("https://identity.example.org/_matrix/identity/api/v1"); .toEqual("https://identity.example.org/_matrix/identity/api/v1");
}).respond(200, {}); }).respond(200, {});
httpBackend.when("GET", "/.well-known/matrix/client").respond(200, { httpBackend.when("GET", "/.well-known/matrix/client").respond(200, {

View File

@ -27,7 +27,7 @@ describe("ContentRepo", function() {
}); });
it("should return the empty string for null input", function() { it("should return the empty string for null input", function() {
expect(getHttpUriForMxc(null, null)).toEqual(""); expect(getHttpUriForMxc(null as any, '')).toEqual("");
}); });
it("should return a thumbnail URL if a width/height/resize is specified", it("should return a thumbnail URL if a width/height/resize is specified",

View File

@ -25,7 +25,7 @@ function awaitEvent(emitter, event) {
}); });
} }
async function keyshareEventForEvent(client, event, index) { async function keyshareEventForEvent(client, event, index): Promise<MatrixEvent> {
const roomId = event.getRoomId(); const roomId = event.getRoomId();
const eventContent = event.getWireContent(); const eventContent = event.getWireContent();
const key = await client.crypto.olmDevice.getInboundGroupSessionKey( const key = await client.crypto.olmDevice.getInboundGroupSessionKey(
@ -50,6 +50,7 @@ async function keyshareEventForEvent(client, event, index) {
}, },
}); });
// make onRoomKeyEvent think this was an encrypted event // make onRoomKeyEvent think this was an encrypted event
// @ts-ignore private property
ksEvent.senderCurve25519Key = "akey"; ksEvent.senderCurve25519Key = "akey";
return ksEvent; return ksEvent;
} }
@ -79,7 +80,7 @@ describe("Crypto", function() {
getId: () => "$event_id", getId: () => "$event_id",
getSenderKey: () => null, getSenderKey: () => null,
getWireContent: () => {return {};}, getWireContent: () => {return {};},
}; } as unknown as MatrixEvent;
let encryptionInfo = client.getEventEncryptionInfo(event); let encryptionInfo = client.getEventEncryptionInfo(event);
expect(encryptionInfo.encrypted).toBeFalsy(); expect(encryptionInfo.encrypted).toBeFalsy();
@ -154,12 +155,15 @@ describe("Crypto", function() {
beforeEach(async function() { beforeEach(async function() {
const mockStorage = new MockStorageApi(); const mockStorage = new MockStorageApi();
const clientStore = new MemoryStore({ localStorage: mockStorage }); const clientStore = new MemoryStore({ localStorage: mockStorage });
const cryptoStore = new MemoryCryptoStore(mockStorage); const cryptoStore = new MemoryCryptoStore();
cryptoStore.storeEndToEndDeviceData({ cryptoStore.storeEndToEndDeviceData({
devices: { devices: {
'@bob:home.server': { '@bob:home.server': {
'BOBDEVICE': { 'BOBDEVICE': {
algorithms: [],
verified: 1,
known: false,
keys: { keys: {
'curve25519:BOBDEVICE': 'this is a key', 'curve25519:BOBDEVICE': 'this is a key',
}, },
@ -167,7 +171,7 @@ describe("Crypto", function() {
}, },
}, },
trackingStatus: {}, trackingStatus: {},
}); }, {});
mockBaseApis = { mockBaseApis = {
sendToDevice: jest.fn(), sendToDevice: jest.fn(),
@ -185,6 +189,7 @@ describe("Crypto", function() {
clientStore, clientStore,
cryptoStore, cryptoStore,
mockRoomList, mockRoomList,
[],
); );
crypto.registerEventHandlers(fakeEmitter); crypto.registerEventHandlers(fakeEmitter);
await crypto.init(); await crypto.init();
@ -195,7 +200,7 @@ describe("Crypto", function() {
}); });
it("restarts wedged Olm sessions", async function() { it("restarts wedged Olm sessions", async function() {
const prom = new Promise((resolve) => { const prom = new Promise<void>((resolve) => {
mockBaseApis.claimOneTimeKeys = function() { mockBaseApis.claimOneTimeKeys = function() {
resolve(); resolve();
return otkResponse; return otkResponse;
@ -276,8 +281,12 @@ describe("Crypto", function() {
// alice encrypts each event, and then bob tries to decrypt // alice encrypts each event, and then bob tries to decrypt
// them without any keys, so that they'll be in pending // them without any keys, so that they'll be in pending
await aliceClient.crypto.encryptEvent(event, aliceRoom); await aliceClient.crypto.encryptEvent(event, aliceRoom);
// remove keys from the event
// @ts-ignore private properties
event.clearEvent = undefined; event.clearEvent = undefined;
// @ts-ignore private properties
event.senderCurve25519Key = null; event.senderCurve25519Key = null;
// @ts-ignore private properties
event.claimedEd25519Key = null; event.claimedEd25519Key = null;
try { try {
await bobClient.crypto.decryptEvent(event); await bobClient.crypto.decryptEvent(event);
@ -291,7 +300,7 @@ describe("Crypto", function() {
roomId, olmlib.MEGOLM_ALGORITHM, roomId, olmlib.MEGOLM_ALGORITHM,
); );
let eventPromise = Promise.all(events.map((ev) => { const decryptEventsPromise = Promise.all(events.map((ev) => {
return awaitEvent(ev, "Event.decrypted"); return awaitEvent(ev, "Event.decrypted");
})); }));
@ -300,7 +309,7 @@ describe("Crypto", function() {
// can // can
let ksEvent = await keyshareEventForEvent(aliceClient, events[1], 1); let ksEvent = await keyshareEventForEvent(aliceClient, events[1], 1);
await bobDecryptor.onRoomKeyEvent(ksEvent); await bobDecryptor.onRoomKeyEvent(ksEvent);
await eventPromise; await decryptEventsPromise;
expect(events[0].getContent().msgtype).toBe("m.bad.encrypted"); expect(events[0].getContent().msgtype).toBe("m.bad.encrypted");
expect(events[1].getContent().msgtype).not.toBe("m.bad.encrypted"); expect(events[1].getContent().msgtype).not.toBe("m.bad.encrypted");
@ -320,10 +329,10 @@ describe("Crypto", function() {
// keyshare the session key starting at the first message, so // keyshare the session key starting at the first message, so
// that it can now be decrypted // that it can now be decrypted
eventPromise = awaitEvent(events[0], "Event.decrypted"); const decryptEventPromise = awaitEvent(events[0], "Event.decrypted");
ksEvent = await keyshareEventForEvent(aliceClient, events[0], 0); ksEvent = await keyshareEventForEvent(aliceClient, events[0], 0);
await bobDecryptor.onRoomKeyEvent(ksEvent); await bobDecryptor.onRoomKeyEvent(ksEvent);
await eventPromise; await decryptEventPromise;
expect(events[0].getContent().msgtype).not.toBe("m.bad.encrypted"); expect(events[0].getContent().msgtype).not.toBe("m.bad.encrypted");
await sleep(1); await sleep(1);
// the room key request should be gone since we've now decrypted everything // the room key request should be gone since we've now decrypted everything
@ -354,8 +363,12 @@ describe("Crypto", function() {
// alice encrypts each event, and then bob tries to decrypt // alice encrypts each event, and then bob tries to decrypt
// them without any keys, so that they'll be in pending // them without any keys, so that they'll be in pending
await aliceClient.crypto.encryptEvent(event, aliceRoom); await aliceClient.crypto.encryptEvent(event, aliceRoom);
// remove keys from the event
// @ts-ignore private property
event.clearEvent = undefined; event.clearEvent = undefined;
// @ts-ignore private property
event.senderCurve25519Key = null; event.senderCurve25519Key = null;
// @ts-ignore private property
event.claimedEd25519Key = null; event.claimedEd25519Key = null;
try { try {
await bobClient.crypto.decryptEvent(event); await bobClient.crypto.decryptEvent(event);
@ -451,7 +464,7 @@ describe("Crypto", function() {
await client.initCrypto(); await client.initCrypto();
client.crypto.getSecretStorageKey = async () => null; client.crypto.getSecretStorageKey = async () => null;
client.crypto.isCrossSigningReady = async () => false; client.crypto.isCrossSigningReady = async () => false;
client.crypto.baseApis.uploadDeviceSigningKeys = () => null; client.crypto.baseApis.uploadDeviceSigningKeys = jest.fn().mockResolvedValue(null);
client.crypto.baseApis.setAccountData = () => null; client.crypto.baseApis.setAccountData = () => null;
client.crypto.baseApis.uploadKeySignatures = () => null; client.crypto.baseApis.uploadKeySignatures = () => null;
client.crypto.baseApis.http.authedRequest = () => null; client.crypto.baseApis.http.authedRequest = () => null;

View File

@ -1,26 +1,36 @@
import { mocked } from 'jest-mock';
import * as utils from "../test-utils/test-utils"; import * as utils from "../test-utils/test-utils";
import { EventTimeline } from "../../src/models/event-timeline"; import { EventTimeline } from "../../src/models/event-timeline";
import { RoomState } from "../../src/models/room-state"; import { RoomState } from "../../src/models/room-state";
import { MatrixClient } from "../../src/matrix";
import { Room } from "../../src/models/room";
import { RoomMember } from "../../src/models/room-member";
import { EventTimelineSet } from "../../src/models/event-timeline-set";
function mockRoomStates(timeline) { jest.mock("../../src/models/room-state");
timeline.startState = utils.mock(RoomState, "startState");
timeline.endState = utils.mock(RoomState, "endState");
}
describe("EventTimeline", function() { describe("EventTimeline", function() {
const roomId = "!foo:bar"; const roomId = "!foo:bar";
const userA = "@alice:bar"; const userA = "@alice:bar";
const userB = "@bertha:bar"; const userB = "@bertha:bar";
let timeline; let timeline: EventTimeline;
const mockClient = {} as unknown as MatrixClient;
const getTimeline = (): EventTimeline => {
const room = new Room(roomId, mockClient, userA);
const timelineSet = new EventTimelineSet(room);
jest.spyOn(timelineSet.room, 'getUnfilteredTimelineSet').mockReturnValue(timelineSet);
return new EventTimeline(timelineSet);
};
beforeEach(function() { beforeEach(function() {
// XXX: this is a horrid hack; should use sinon or something instead to mock // reset any RoomState mocks
const timelineSet = { room: { roomId: roomId } }; jest.resetAllMocks();
timelineSet.room.getUnfilteredTimelineSet = function() {
return timelineSet;
};
timeline = new EventTimeline(timelineSet); timeline = getTimeline();
}); });
describe("construction", function() { describe("construction", function() {
@ -31,10 +41,6 @@ describe("EventTimeline", function() {
}); });
describe("initialiseState", function() { describe("initialiseState", function() {
beforeEach(function() {
mockRoomStates(timeline);
});
it("should copy state events to start and end state", function() { it("should copy state events to start and end state", function() {
const events = [ const events = [
utils.mkMembership({ utils.mkMembership({
@ -48,11 +54,15 @@ describe("EventTimeline", function() {
}), }),
]; ];
timeline.initialiseState(events); timeline.initialiseState(events);
expect(timeline.startState.setStateEvents).toHaveBeenCalledWith( // @ts-ignore private prop
const timelineStartState = timeline.startState;
expect(mocked(timelineStartState).setStateEvents).toHaveBeenCalledWith(
events, events,
{ timelineWasEmpty: undefined }, { timelineWasEmpty: undefined },
); );
expect(timeline.endState.setStateEvents).toHaveBeenCalledWith( // @ts-ignore private prop
const timelineEndState = timeline.endState;
expect(mocked(timelineEndState).setStateEvents).toHaveBeenCalledWith(
events, events,
{ timelineWasEmpty: undefined }, { timelineWasEmpty: undefined },
); );
@ -103,8 +113,8 @@ describe("EventTimeline", function() {
}); });
it("setNeighbouringTimeline should set neighbour", function() { it("setNeighbouringTimeline should set neighbour", function() {
const prev = { a: "a" }; const prev = getTimeline();
const next = { b: "b" }; const next = getTimeline();
timeline.setNeighbouringTimeline(prev, EventTimeline.BACKWARDS); timeline.setNeighbouringTimeline(prev, EventTimeline.BACKWARDS);
timeline.setNeighbouringTimeline(next, EventTimeline.FORWARDS); timeline.setNeighbouringTimeline(next, EventTimeline.FORWARDS);
expect(timeline.getNeighbouringTimeline(EventTimeline.BACKWARDS)).toBe(prev); expect(timeline.getNeighbouringTimeline(EventTimeline.BACKWARDS)).toBe(prev);
@ -112,8 +122,8 @@ describe("EventTimeline", function() {
}); });
it("setNeighbouringTimeline should throw if called twice", function() { it("setNeighbouringTimeline should throw if called twice", function() {
const prev = { a: "a" }; const prev = getTimeline();
const next = { b: "b" }; const next = getTimeline();
expect(function() { expect(function() {
timeline.setNeighbouringTimeline(prev, EventTimeline.BACKWARDS); timeline.setNeighbouringTimeline(prev, EventTimeline.BACKWARDS);
}).not.toThrow(); }).not.toThrow();
@ -135,10 +145,6 @@ describe("EventTimeline", function() {
}); });
describe("addEvent", function() { describe("addEvent", function() {
beforeEach(function() {
mockRoomStates(timeline);
});
const events = [ const events = [
utils.mkMessage({ utils.mkMessage({
room: roomId, user: userA, msg: "hungry hungry hungry", room: roomId, user: userA, msg: "hungry hungry hungry",
@ -171,24 +177,22 @@ describe("EventTimeline", function() {
}); });
it("should set event.sender for new and old events", function() { it("should set event.sender for new and old events", function() {
const sentinel = { const sentinel = new RoomMember(roomId, userA);
userId: userA, sentinel.name = "Alice";
membership: "join", sentinel.membership = "join";
name: "Alice",
}; const oldSentinel = new RoomMember(roomId, userA);
const oldSentinel = { sentinel.name = "Old Alice";
userId: userA, sentinel.membership = "join";
membership: "join",
name: "Old Alice", mocked(timeline.getState(EventTimeline.FORWARDS)).getSentinelMember
};
timeline.getState(EventTimeline.FORWARDS).getSentinelMember
.mockImplementation(function(uid) { .mockImplementation(function(uid) {
if (uid === userA) { if (uid === userA) {
return sentinel; return sentinel;
} }
return null; return null;
}); });
timeline.getState(EventTimeline.BACKWARDS).getSentinelMember mocked(timeline.getState(EventTimeline.BACKWARDS)).getSentinelMember
.mockImplementation(function(uid) { .mockImplementation(function(uid) {
if (uid === userA) { if (uid === userA) {
return oldSentinel; return oldSentinel;
@ -212,43 +216,41 @@ describe("EventTimeline", function() {
}); });
it("should set event.target for new and old m.room.member events", it("should set event.target for new and old m.room.member events",
function() { function() {
const sentinel = { const sentinel = new RoomMember(roomId, userA);
userId: userA, sentinel.name = "Alice";
membership: "join", sentinel.membership = "join";
name: "Alice",
};
const oldSentinel = {
userId: userA,
membership: "join",
name: "Old Alice",
};
timeline.getState(EventTimeline.FORWARDS).getSentinelMember
.mockImplementation(function(uid) {
if (uid === userA) {
return sentinel;
}
return null;
});
timeline.getState(EventTimeline.BACKWARDS).getSentinelMember
.mockImplementation(function(uid) {
if (uid === userA) {
return oldSentinel;
}
return null;
});
const newEv = utils.mkMembership({ const oldSentinel = new RoomMember(roomId, userA);
room: roomId, mship: "invite", user: userB, skey: userA, event: true, sentinel.name = "Old Alice";
sentinel.membership = "join";
mocked(timeline.getState(EventTimeline.FORWARDS)).getSentinelMember
.mockImplementation(function(uid) {
if (uid === userA) {
return sentinel;
}
return null;
});
mocked(timeline.getState(EventTimeline.BACKWARDS)).getSentinelMember
.mockImplementation(function(uid) {
if (uid === userA) {
return oldSentinel;
}
return null;
});
const newEv = utils.mkMembership({
room: roomId, mship: "invite", user: userB, skey: userA, event: true,
});
const oldEv = utils.mkMembership({
room: roomId, mship: "ban", user: userB, skey: userA, event: true,
});
timeline.addEvent(newEv, { toStartOfTimeline: false });
expect(newEv.target).toEqual(sentinel);
timeline.addEvent(oldEv, { toStartOfTimeline: true });
expect(oldEv.target).toEqual(oldSentinel);
}); });
const oldEv = utils.mkMembership({
room: roomId, mship: "ban", user: userB, skey: userA, event: true,
});
timeline.addEvent(newEv, { toStartOfTimeline: false });
expect(newEv.target).toEqual(sentinel);
timeline.addEvent(oldEv, { toStartOfTimeline: true });
expect(oldEv.target).toEqual(oldSentinel);
});
it("should call setStateEvents on the right RoomState with the right " + it("should call setStateEvents on the right RoomState with the right " +
"forwardLooking value for new events", function() { "forwardLooking value for new events", function() {
@ -310,7 +312,11 @@ describe("EventTimeline", function() {
it("Make sure legacy overload passing options directly as parameters still works", () => { it("Make sure legacy overload passing options directly as parameters still works", () => {
expect(() => timeline.addEvent(events[0], { toStartOfTimeline: true })).not.toThrow(); expect(() => timeline.addEvent(events[0], { toStartOfTimeline: true })).not.toThrow();
expect(() => timeline.addEvent(events[0], { stateContext: new RoomState() })).not.toThrow(); // @ts-ignore stateContext is not a valid param
expect(() => timeline.addEvent(events[0], { stateContext: new RoomState(roomId) })).not.toThrow();
expect(() => timeline.addEvent(events[0],
{ toStartOfTimeline: false, roomState: new RoomState(roomId) },
)).not.toThrow();
}); });
}); });
@ -364,14 +370,14 @@ describe("EventTimeline", function() {
// - removing the last event got baseIndex into such a state that // - removing the last event got baseIndex into such a state that
// further addEvent(ev, false) calls made the index increase. // further addEvent(ev, false) calls made the index increase.
it("should not make baseIndex assplode when removing the last event", it("should not make baseIndex assplode when removing the last event",
function() { function() {
timeline.addEvent(events[0], { toStartOfTimeline: true }); timeline.addEvent(events[0], { toStartOfTimeline: true });
timeline.removeEvent(events[0].getId()); timeline.removeEvent(events[0].getId());
const initialIndex = timeline.getBaseIndex(); const initialIndex = timeline.getBaseIndex();
timeline.addEvent(events[1], { toStartOfTimeline: false }); timeline.addEvent(events[1], { toStartOfTimeline: false });
timeline.addEvent(events[2], { toStartOfTimeline: false }); timeline.addEvent(events[2], { toStartOfTimeline: false });
expect(timeline.getBaseIndex()).toEqual(initialIndex); expect(timeline.getBaseIndex()).toEqual(initialIndex);
expect(timeline.getEvents().length).toEqual(2); expect(timeline.getEvents().length).toEqual(2);
}); });
}); });
}); });