From 4a4241806e3d95af6e44d0eaea601f912fc26f6f Mon Sep 17 00:00:00 2001 From: Kerry Date: Fri, 29 Jul 2022 11:11:01 +0200 Subject: [PATCH] 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 --- ...iscovery.spec.js => autodiscovery.spec.ts} | 60 ++++--- spec/unit/content-repo.spec.ts | 2 +- spec/unit/{crypto.spec.js => crypto.spec.ts} | 33 ++-- ...imeline.spec.js => event-timeline.spec.ts} | 170 +++++++++--------- 4 files changed, 152 insertions(+), 113 deletions(-) rename spec/unit/{autodiscovery.spec.js => autodiscovery.spec.ts} (93%) rename spec/unit/{crypto.spec.js => crypto.spec.ts} (94%) rename spec/unit/{event-timeline.spec.js => event-timeline.spec.ts} (74%) diff --git a/spec/unit/autodiscovery.spec.js b/spec/unit/autodiscovery.spec.ts similarity index 93% rename from spec/unit/autodiscovery.spec.js rename to spec/unit/autodiscovery.spec.ts index 7fb9df0b7..71b48ef41 100644 --- a/spec/unit/autodiscovery.spec.js +++ b/spec/unit/autodiscovery.spec.ts @@ -1,6 +1,6 @@ /* 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"); 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 * as sdk from "../../src"; +import { request } from "../../src/matrix"; import { AutoDiscovery } from "../../src/autodiscovery"; describe("AutoDiscovery", function() { - let httpBackend = null; - - beforeEach(function() { - httpBackend = new MockHttpBackend(); - sdk.request(httpBackend.requestFn); - }); + const getHttpBackend = (): MockHttpBackend => { + const httpBackend = new MockHttpBackend(); + request(httpBackend.requestFn); + return httpBackend; + }; it("should throw an error when no domain is specified", function() { + getHttpBackend(); return Promise.all([ + // @ts-ignore testing no args AutoDiscovery.findClientConfig(/* no args */).then(() => { throw new Error("Expected a failure, not success with no args"); }, () => { @@ -42,13 +43,13 @@ describe("AutoDiscovery", function() { return true; }), - AutoDiscovery.findClientConfig(null).then(() => { + AutoDiscovery.findClientConfig(null as any).then(() => { throw new Error("Expected a failure, not success with null"); }, () => { return true; }), - AutoDiscovery.findClientConfig(true).then(() => { + AutoDiscovery.findClientConfig(true as any).then(() => { throw new Error("Expected a failure, not success with a non-string"); }, () => { return true; @@ -57,6 +58,7 @@ describe("AutoDiscovery", function() { }); it("should return PROMPT when .well-known 404s", function() { + const httpBackend = getHttpBackend(); httpBackend.when("GET", "/.well-known/matrix/client").respond(404, {}); return Promise.all([ httpBackend.flushAllExpected(), @@ -80,6 +82,7 @@ describe("AutoDiscovery", 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, {}); return Promise.all([ httpBackend.flushAllExpected(), @@ -103,6 +106,7 @@ describe("AutoDiscovery", 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, {}); return Promise.all([ httpBackend.flushAllExpected(), @@ -126,6 +130,7 @@ describe("AutoDiscovery", 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, ""); return Promise.all([ httpBackend.flushAllExpected(), @@ -149,6 +154,7 @@ describe("AutoDiscovery", 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"); return Promise.all([ httpBackend.flushAllExpected(), @@ -173,6 +179,7 @@ describe("AutoDiscovery", function() { it("should return FAIL_PROMPT when .well-known does not have a base_url for " + "m.homeserver (empty string)", function() { + const httpBackend = getHttpBackend(); httpBackend.when("GET", "/.well-known/matrix/client").respond(200, { "m.homeserver": { base_url: "", @@ -201,6 +208,7 @@ describe("AutoDiscovery", function() { it("should return FAIL_PROMPT when .well-known does not have a base_url for " + "m.homeserver (no property)", function() { + const httpBackend = getHttpBackend(); httpBackend.when("GET", "/.well-known/matrix/client").respond(200, { "m.homeserver": {}, }); @@ -227,6 +235,7 @@ describe("AutoDiscovery", function() { it("should return FAIL_ERROR when .well-known has an invalid base_url for " + "m.homeserver (disallowed scheme)", function() { + const httpBackend = getHttpBackend(); httpBackend.when("GET", "/.well-known/matrix/client").respond(200, { "m.homeserver": { 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 " + "m.homeserver (verification failure: 404)", function() { + const httpBackend = getHttpBackend(); httpBackend.when("GET", "/_matrix/client/versions").respond(404, {}); httpBackend.when("GET", "/.well-known/matrix/client").respond(200, { "m.homeserver": { @@ -284,6 +294,7 @@ describe("AutoDiscovery", function() { it("should return FAIL_ERROR when .well-known has an invalid base_url for " + "m.homeserver (verification failure: 500)", function() { + const httpBackend = getHttpBackend(); httpBackend.when("GET", "/_matrix/client/versions").respond(500, {}); httpBackend.when("GET", "/.well-known/matrix/client").respond(200, { "m.homeserver": { @@ -313,6 +324,7 @@ describe("AutoDiscovery", function() { it("should return FAIL_ERROR when .well-known has an invalid base_url for " + "m.homeserver (verification failure: 200 but wrong content)", function() { + const httpBackend = getHttpBackend(); httpBackend.when("GET", "/_matrix/client/versions").respond(200, { 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 " + "m.homeserver", function() { + const httpBackend = getHttpBackend(); 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, { versions: ["r0.0.1"], }); @@ -376,8 +389,9 @@ describe("AutoDiscovery", function() { }); it("should return SUCCESS with the right homeserver URL", function() { + const httpBackend = getHttpBackend(); httpBackend.when("GET", "/_matrix/client/versions").check((req) => { - expect(req.opts.uri) + expect(req.path) .toEqual("https://chat.example.org/_matrix/client/versions"); }).respond(200, { versions: ["r0.0.1"], @@ -411,8 +425,9 @@ describe("AutoDiscovery", function() { it("should return SUCCESS / FAIL_PROMPT when the identity server configuration " + "is wrong (missing base_url)", function() { + const httpBackend = getHttpBackend(); httpBackend.when("GET", "/_matrix/client/versions").check((req) => { - expect(req.opts.uri) + expect(req.path) .toEqual("https://chat.example.org/_matrix/client/versions"); }).respond(200, { versions: ["r0.0.1"], @@ -451,8 +466,9 @@ describe("AutoDiscovery", function() { it("should return SUCCESS / FAIL_PROMPT when the identity server configuration " + "is wrong (empty base_url)", function() { + const httpBackend = getHttpBackend(); httpBackend.when("GET", "/_matrix/client/versions").check((req) => { - expect(req.opts.uri) + expect(req.path) .toEqual("https://chat.example.org/_matrix/client/versions"); }).respond(200, { versions: ["r0.0.1"], @@ -491,8 +507,9 @@ describe("AutoDiscovery", function() { it("should return SUCCESS / FAIL_PROMPT when the identity server configuration " + "is wrong (validation error: 404)", function() { + const httpBackend = getHttpBackend(); httpBackend.when("GET", "/_matrix/client/versions").check((req) => { - expect(req.opts.uri) + expect(req.path) .toEqual("https://chat.example.org/_matrix/client/versions"); }).respond(200, { versions: ["r0.0.1"], @@ -532,8 +549,9 @@ describe("AutoDiscovery", function() { it("should return SUCCESS / FAIL_PROMPT when the identity server configuration " + "is wrong (validation error: 500)", function() { + const httpBackend = getHttpBackend(); httpBackend.when("GET", "/_matrix/client/versions").check((req) => { - expect(req.opts.uri) + expect(req.path) .toEqual("https://chat.example.org/_matrix/client/versions"); }).respond(200, { versions: ["r0.0.1"], @@ -573,14 +591,15 @@ describe("AutoDiscovery", function() { it("should return SUCCESS when the identity server configuration is " + "verifiably accurate", function() { + const httpBackend = getHttpBackend(); httpBackend.when("GET", "/_matrix/client/versions").check((req) => { - expect(req.opts.uri) + expect(req.path) .toEqual("https://chat.example.org/_matrix/client/versions"); }).respond(200, { versions: ["r0.0.1"], }); 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"); }).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 " + ".well-known response", function() { + const httpBackend = getHttpBackend(); httpBackend.when("GET", "/_matrix/client/versions").check((req) => { - expect(req.opts.uri) + expect(req.path) .toEqual("https://chat.example.org/_matrix/client/versions"); }).respond(200, { versions: ["r0.0.1"], }); 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"); }).respond(200, {}); httpBackend.when("GET", "/.well-known/matrix/client").respond(200, { diff --git a/spec/unit/content-repo.spec.ts b/spec/unit/content-repo.spec.ts index ce6da80e1..943d9f1ce 100644 --- a/spec/unit/content-repo.spec.ts +++ b/spec/unit/content-repo.spec.ts @@ -27,7 +27,7 @@ describe("ContentRepo", 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", diff --git a/spec/unit/crypto.spec.js b/spec/unit/crypto.spec.ts similarity index 94% rename from spec/unit/crypto.spec.js rename to spec/unit/crypto.spec.ts index ba74eb517..b579b7f38 100644 --- a/spec/unit/crypto.spec.js +++ b/spec/unit/crypto.spec.ts @@ -25,7 +25,7 @@ function awaitEvent(emitter, event) { }); } -async function keyshareEventForEvent(client, event, index) { +async function keyshareEventForEvent(client, event, index): Promise { const roomId = event.getRoomId(); const eventContent = event.getWireContent(); 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 + // @ts-ignore private property ksEvent.senderCurve25519Key = "akey"; return ksEvent; } @@ -79,7 +80,7 @@ describe("Crypto", function() { getId: () => "$event_id", getSenderKey: () => null, getWireContent: () => {return {};}, - }; + } as unknown as MatrixEvent; let encryptionInfo = client.getEventEncryptionInfo(event); expect(encryptionInfo.encrypted).toBeFalsy(); @@ -154,12 +155,15 @@ describe("Crypto", function() { beforeEach(async function() { const mockStorage = new MockStorageApi(); const clientStore = new MemoryStore({ localStorage: mockStorage }); - const cryptoStore = new MemoryCryptoStore(mockStorage); + const cryptoStore = new MemoryCryptoStore(); cryptoStore.storeEndToEndDeviceData({ devices: { '@bob:home.server': { 'BOBDEVICE': { + algorithms: [], + verified: 1, + known: false, keys: { 'curve25519:BOBDEVICE': 'this is a key', }, @@ -167,7 +171,7 @@ describe("Crypto", function() { }, }, trackingStatus: {}, - }); + }, {}); mockBaseApis = { sendToDevice: jest.fn(), @@ -185,6 +189,7 @@ describe("Crypto", function() { clientStore, cryptoStore, mockRoomList, + [], ); crypto.registerEventHandlers(fakeEmitter); await crypto.init(); @@ -195,7 +200,7 @@ describe("Crypto", function() { }); it("restarts wedged Olm sessions", async function() { - const prom = new Promise((resolve) => { + const prom = new Promise((resolve) => { mockBaseApis.claimOneTimeKeys = function() { resolve(); return otkResponse; @@ -276,8 +281,12 @@ describe("Crypto", function() { // alice encrypts each event, and then bob tries to decrypt // them without any keys, so that they'll be in pending await aliceClient.crypto.encryptEvent(event, aliceRoom); + // remove keys from the event + // @ts-ignore private properties event.clearEvent = undefined; + // @ts-ignore private properties event.senderCurve25519Key = null; + // @ts-ignore private properties event.claimedEd25519Key = null; try { await bobClient.crypto.decryptEvent(event); @@ -291,7 +300,7 @@ describe("Crypto", function() { roomId, olmlib.MEGOLM_ALGORITHM, ); - let eventPromise = Promise.all(events.map((ev) => { + const decryptEventsPromise = Promise.all(events.map((ev) => { return awaitEvent(ev, "Event.decrypted"); })); @@ -300,7 +309,7 @@ describe("Crypto", function() { // can let ksEvent = await keyshareEventForEvent(aliceClient, events[1], 1); await bobDecryptor.onRoomKeyEvent(ksEvent); - await eventPromise; + await decryptEventsPromise; expect(events[0].getContent().msgtype).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 // 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); await bobDecryptor.onRoomKeyEvent(ksEvent); - await eventPromise; + await decryptEventPromise; expect(events[0].getContent().msgtype).not.toBe("m.bad.encrypted"); await sleep(1); // 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 // them without any keys, so that they'll be in pending await aliceClient.crypto.encryptEvent(event, aliceRoom); + // remove keys from the event + // @ts-ignore private property event.clearEvent = undefined; + // @ts-ignore private property event.senderCurve25519Key = null; + // @ts-ignore private property event.claimedEd25519Key = null; try { await bobClient.crypto.decryptEvent(event); @@ -451,7 +464,7 @@ describe("Crypto", function() { await client.initCrypto(); client.crypto.getSecretStorageKey = async () => null; 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.uploadKeySignatures = () => null; client.crypto.baseApis.http.authedRequest = () => null; diff --git a/spec/unit/event-timeline.spec.js b/spec/unit/event-timeline.spec.ts similarity index 74% rename from spec/unit/event-timeline.spec.js rename to spec/unit/event-timeline.spec.ts index ed5047c11..f7c346b73 100644 --- a/spec/unit/event-timeline.spec.js +++ b/spec/unit/event-timeline.spec.ts @@ -1,26 +1,36 @@ +import { mocked } from 'jest-mock'; + import * as utils from "../test-utils/test-utils"; import { EventTimeline } from "../../src/models/event-timeline"; 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) { - timeline.startState = utils.mock(RoomState, "startState"); - timeline.endState = utils.mock(RoomState, "endState"); -} +jest.mock("../../src/models/room-state"); describe("EventTimeline", function() { const roomId = "!foo:bar"; const userA = "@alice: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() { - // XXX: this is a horrid hack; should use sinon or something instead to mock - const timelineSet = { room: { roomId: roomId } }; - timelineSet.room.getUnfilteredTimelineSet = function() { - return timelineSet; - }; + // reset any RoomState mocks + jest.resetAllMocks(); - timeline = new EventTimeline(timelineSet); + timeline = getTimeline(); }); describe("construction", function() { @@ -31,10 +41,6 @@ describe("EventTimeline", function() { }); describe("initialiseState", function() { - beforeEach(function() { - mockRoomStates(timeline); - }); - it("should copy state events to start and end state", function() { const events = [ utils.mkMembership({ @@ -48,11 +54,15 @@ describe("EventTimeline", function() { }), ]; timeline.initialiseState(events); - expect(timeline.startState.setStateEvents).toHaveBeenCalledWith( + // @ts-ignore private prop + const timelineStartState = timeline.startState; + expect(mocked(timelineStartState).setStateEvents).toHaveBeenCalledWith( events, { timelineWasEmpty: undefined }, ); - expect(timeline.endState.setStateEvents).toHaveBeenCalledWith( + // @ts-ignore private prop + const timelineEndState = timeline.endState; + expect(mocked(timelineEndState).setStateEvents).toHaveBeenCalledWith( events, { timelineWasEmpty: undefined }, ); @@ -103,8 +113,8 @@ describe("EventTimeline", function() { }); it("setNeighbouringTimeline should set neighbour", function() { - const prev = { a: "a" }; - const next = { b: "b" }; + const prev = getTimeline(); + const next = getTimeline(); timeline.setNeighbouringTimeline(prev, EventTimeline.BACKWARDS); timeline.setNeighbouringTimeline(next, EventTimeline.FORWARDS); expect(timeline.getNeighbouringTimeline(EventTimeline.BACKWARDS)).toBe(prev); @@ -112,8 +122,8 @@ describe("EventTimeline", function() { }); it("setNeighbouringTimeline should throw if called twice", function() { - const prev = { a: "a" }; - const next = { b: "b" }; + const prev = getTimeline(); + const next = getTimeline(); expect(function() { timeline.setNeighbouringTimeline(prev, EventTimeline.BACKWARDS); }).not.toThrow(); @@ -135,10 +145,6 @@ describe("EventTimeline", function() { }); describe("addEvent", function() { - beforeEach(function() { - mockRoomStates(timeline); - }); - const events = [ utils.mkMessage({ 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() { - const sentinel = { - userId: userA, - membership: "join", - name: "Alice", - }; - const oldSentinel = { - userId: userA, - membership: "join", - name: "Old Alice", - }; - timeline.getState(EventTimeline.FORWARDS).getSentinelMember + const sentinel = new RoomMember(roomId, userA); + sentinel.name = "Alice"; + sentinel.membership = "join"; + + const oldSentinel = new RoomMember(roomId, userA); + sentinel.name = "Old Alice"; + sentinel.membership = "join"; + + mocked(timeline.getState(EventTimeline.FORWARDS)).getSentinelMember .mockImplementation(function(uid) { if (uid === userA) { return sentinel; } return null; }); - timeline.getState(EventTimeline.BACKWARDS).getSentinelMember + mocked(timeline.getState(EventTimeline.BACKWARDS)).getSentinelMember .mockImplementation(function(uid) { if (uid === userA) { return oldSentinel; @@ -212,43 +216,41 @@ describe("EventTimeline", function() { }); it("should set event.target for new and old m.room.member events", - function() { - const sentinel = { - userId: userA, - 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; - }); + function() { + const sentinel = new RoomMember(roomId, userA); + sentinel.name = "Alice"; + sentinel.membership = "join"; - const newEv = utils.mkMembership({ - room: roomId, mship: "invite", user: userB, skey: userA, event: true, + const oldSentinel = new RoomMember(roomId, userA); + 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 " + "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", () => { 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 // further addEvent(ev, false) calls made the index increase. it("should not make baseIndex assplode when removing the last event", - function() { - timeline.addEvent(events[0], { toStartOfTimeline: true }); - timeline.removeEvent(events[0].getId()); - const initialIndex = timeline.getBaseIndex(); - timeline.addEvent(events[1], { toStartOfTimeline: false }); - timeline.addEvent(events[2], { toStartOfTimeline: false }); - expect(timeline.getBaseIndex()).toEqual(initialIndex); - expect(timeline.getEvents().length).toEqual(2); - }); + function() { + timeline.addEvent(events[0], { toStartOfTimeline: true }); + timeline.removeEvent(events[0].getId()); + const initialIndex = timeline.getBaseIndex(); + timeline.addEvent(events[1], { toStartOfTimeline: false }); + timeline.addEvent(events[2], { toStartOfTimeline: false }); + expect(timeline.getBaseIndex()).toEqual(initialIndex); + expect(timeline.getEvents().length).toEqual(2); + }); }); });