From ddbbec0ffa7877dfe47f42889967b427897bdfea Mon Sep 17 00:00:00 2001 From: Michael Telatynski <7t3chguy@gmail.com> Date: Mon, 13 Jun 2022 12:56:48 +0100 Subject: [PATCH 1/6] Fix test message utils using overload --- spec/test-utils/test-utils.ts | 24 +++- spec/unit/event-timeline-set.spec.ts | 6 +- spec/unit/filter-component.spec.ts | 21 ++-- spec/unit/room.spec.ts | 174 +++++++++++++-------------- 4 files changed, 117 insertions(+), 108 deletions(-) diff --git a/spec/test-utils/test-utils.ts b/spec/test-utils/test-utils.ts index 0a42578de60..84a9662e419 100644 --- a/spec/test-utils/test-utils.ts +++ b/spec/test-utils/test-utils.ts @@ -74,7 +74,6 @@ interface IEventOpts { sender?: string; skey?: string; content: IContent; - event?: boolean; user?: string; unsigned?: IUnsigned; redacts?: string; @@ -93,7 +92,9 @@ let testEventIndex = 1; // counter for events, easier for comparison of randomly * @param {MatrixClient} client If passed along with opts.event=true will be used to set up re-emitters. * @return {Object} a JSON object representing this event. */ -export function mkEvent(opts: IEventOpts, client?: MatrixClient): object | MatrixEvent { +export function mkEvent(opts: IEventOpts & { event: true }, client?: MatrixClient): MatrixEvent; +export function mkEvent(opts: IEventOpts & { event?: false }, client?: MatrixClient): object; +export function mkEvent(opts: IEventOpts & { event?: boolean }, client?: MatrixClient): object | MatrixEvent { if (!opts.type || !opts.content) { throw new Error("Missing .type or .content =>" + JSON.stringify(opts)); } @@ -143,7 +144,9 @@ interface IPresenceOpts { * @param {Object} opts Values for the presence. * @return {Object|MatrixEvent} The event */ -export function mkPresence(opts: IPresenceOpts): object | MatrixEvent { +export function mkPresence(opts: IPresenceOpts & { event: true }): MatrixEvent; +export function mkPresence(opts: IPresenceOpts & { event?: false }): object; +export function mkPresence(opts: IPresenceOpts & { event?: boolean }): object | MatrixEvent { const event = { event_id: "$" + Math.random() + "-" + Math.random(), type: "m.presence", @@ -182,7 +185,9 @@ interface IMembershipOpts { * @param {boolean} opts.event True to make a MatrixEvent. * @return {Object|MatrixEvent} The event */ -export function mkMembership(opts: IMembershipOpts): object | MatrixEvent { +export function mkMembership(opts: IMembershipOpts & { event: true }): MatrixEvent; +export function mkMembership(opts: IMembershipOpts & { event?: false }): object; +export function mkMembership(opts: IMembershipOpts & { event?: boolean }): object | MatrixEvent { const eventOpts: IEventOpts = { ...opts, type: EventType.RoomMember, @@ -220,7 +225,9 @@ interface IMessageOpts { * @param {MatrixClient} client If passed along with opts.event=true will be used to set up re-emitters. * @return {Object|MatrixEvent} The event */ -export function mkMessage(opts: IMessageOpts, client?: MatrixClient): object | MatrixEvent { +export function mkMessage(opts: IMessageOpts & { event: true }, client?: MatrixClient): MatrixEvent; +export function mkMessage(opts: IMessageOpts & { event?: false }, client?: MatrixClient): object; +export function mkMessage(opts: IMessageOpts & { event?: boolean }, client?: MatrixClient): object | MatrixEvent { const eventOpts: IEventOpts = { ...opts, type: EventType.RoomMessage, @@ -252,7 +259,12 @@ interface IReplyMessageOpts extends IMessageOpts { * @param {MatrixClient} client If passed along with opts.event=true will be used to set up re-emitters. * @return {Object|MatrixEvent} The event */ -export function mkReplyMessage(opts: IReplyMessageOpts, client?: MatrixClient): object | MatrixEvent { +export function mkReplyMessage(opts: IReplyMessageOpts & { event: true }, client?: MatrixClient): MatrixEvent; +export function mkReplyMessage(opts: IReplyMessageOpts & { event?: false }, client?: MatrixClient): object; +export function mkReplyMessage( + opts: IReplyMessageOpts & { event?: boolean }, + client?: MatrixClient, +): object | MatrixEvent { const eventOpts: IEventOpts = { ...opts, type: EventType.RoomMessage, diff --git a/spec/unit/event-timeline-set.spec.ts b/spec/unit/event-timeline-set.spec.ts index 053a78e46c5..ba4ff9fd9aa 100644 --- a/spec/unit/event-timeline-set.spec.ts +++ b/spec/unit/event-timeline-set.spec.ts @@ -62,14 +62,14 @@ describe('EventTimelineSet', () => { user: userA, msg: 'Hi!', event: true, - }) as MatrixEvent; + }); replyEvent = utils.mkReplyMessage({ room: roomId, user: userA, msg: 'Hoo!', event: true, replyToMessage: messageEvent, - }) as MatrixEvent; + }); }); describe('addLiveEvent', () => { @@ -91,7 +91,7 @@ describe('EventTimelineSet', () => { // make a duplicate const duplicateMessageEvent = utils.mkMessage({ room: roomId, user: userA, msg: "dupe", event: true, - }) as MatrixEvent; + }); duplicateMessageEvent.event.event_id = messageEvent.getId(); // Adding the duplicate event should replace the `messageEvent` diff --git a/spec/unit/filter-component.spec.ts b/spec/unit/filter-component.spec.ts index 47ffb37cf50..a0a337cd17d 100644 --- a/spec/unit/filter-component.spec.ts +++ b/spec/unit/filter-component.spec.ts @@ -1,7 +1,4 @@ -import { - MatrixEvent, - RelationType, -} from "../../src"; +import { RelationType } from "../../src"; import { FilterComponent } from "../../src/filter-component"; import { mkEvent } from '../test-utils/test-utils'; @@ -14,7 +11,7 @@ describe("Filter Component", function() { content: { }, room: 'roomId', event: true, - }) as MatrixEvent; + }); const checkResult = filter.check(event); @@ -28,7 +25,7 @@ describe("Filter Component", function() { content: { }, room: 'roomId', event: true, - }) as MatrixEvent; + }); const checkResult = filter.check(event); @@ -55,7 +52,7 @@ describe("Filter Component", function() { }, }, }, - }) as MatrixEvent; + }); expect(filter.check(threadRootNotParticipated)).toBe(false); }); @@ -80,7 +77,7 @@ describe("Filter Component", function() { user: '@someone-else:server.org', room: 'roomId', event: true, - }) as MatrixEvent; + }); expect(filter.check(threadRootParticipated)).toBe(true); }); @@ -100,7 +97,7 @@ describe("Filter Component", function() { [RelationType.Reference]: {}, }, }, - }) as MatrixEvent; + }); expect(filter.check(referenceRelationEvent)).toBe(false); }); @@ -123,7 +120,7 @@ describe("Filter Component", function() { }, room: 'roomId', event: true, - }) as MatrixEvent; + }); const eventWithMultipleRelations = mkEvent({ "type": "m.room.message", @@ -148,7 +145,7 @@ describe("Filter Component", function() { }, "room": 'roomId', "event": true, - }) as MatrixEvent; + }); const noMatchEvent = mkEvent({ "type": "m.room.message", @@ -160,7 +157,7 @@ describe("Filter Component", function() { }, "room": 'roomId', "event": true, - }) as MatrixEvent; + }); expect(filter.check(threadRootEvent)).toBe(true); expect(filter.check(eventWithMultipleRelations)).toBe(true); diff --git a/spec/unit/room.spec.ts b/spec/unit/room.spec.ts index 3807795d1f3..7c13ad48905 100644 --- a/spec/unit/room.spec.ts +++ b/spec/unit/room.spec.ts @@ -52,7 +52,7 @@ describe("Room", function() { event: true, user: userA, room: roomId, - }, room.client) as MatrixEvent; + }, room.client); const mkReply = (target: MatrixEvent) => utils.mkEvent({ event: true, @@ -67,7 +67,7 @@ describe("Room", function() { }, }, }, - }, room.client) as MatrixEvent; + }, room.client); const mkEdit = (target: MatrixEvent, salt = Math.random()) => utils.mkEvent({ event: true, @@ -84,7 +84,7 @@ describe("Room", function() { event_id: target.getId(), }, }, - }, room.client) as MatrixEvent; + }, room.client); const mkThreadResponse = (root: MatrixEvent) => utils.mkEvent({ event: true, @@ -101,7 +101,7 @@ describe("Room", function() { "rel_type": "m.thread", }, }, - }, room.client) as MatrixEvent; + }, room.client); const mkReaction = (target: MatrixEvent) => utils.mkEvent({ event: true, @@ -115,7 +115,7 @@ describe("Room", function() { "key": Math.random().toString(), }, }, - }, room.client) as MatrixEvent; + }, room.client); const mkRedaction = (target: MatrixEvent) => utils.mkEvent({ event: true, @@ -124,7 +124,7 @@ describe("Room", function() { room: roomId, redacts: target.getId(), content: {}, - }, room.client) as MatrixEvent; + }, room.client); beforeEach(function() { room = new Room(roomId, new TestClient(userA, "device").client, userA); @@ -210,11 +210,11 @@ describe("Room", function() { const events: MatrixEvent[] = [ utils.mkMessage({ room: roomId, user: userA, msg: "changing room name", event: true, - }) as MatrixEvent, + }), utils.mkEvent({ type: EventType.RoomName, room: roomId, user: userA, event: true, content: { name: "New Room Name" }, - }) as MatrixEvent, + }), ]; it("Make sure legacy overload passing options directly as parameters still works", () => { @@ -235,7 +235,7 @@ describe("Room", function() { // make a duplicate const dupe = utils.mkMessage({ room: roomId, user: userA, msg: "dupe", event: true, - }) as MatrixEvent; + }); dupe.event.event_id = events[0].getId(); room.addLiveEvents(events); expect(room.timeline[0]).toEqual(events[0]); @@ -249,7 +249,7 @@ describe("Room", function() { // make a duplicate const dupe = utils.mkMessage({ room: roomId, user: userA, msg: "dupe", event: true, - }) as MatrixEvent; + }); dupe.event.event_id = events[0].getId(); room.addLiveEvents(events); expect(room.timeline[0]).toEqual(events[0]); @@ -277,13 +277,13 @@ describe("Room", function() { const events: MatrixEvent[] = [ utils.mkMembership({ room: roomId, mship: "invite", user: userB, skey: userA, event: true, - }) as MatrixEvent, + }), utils.mkEvent({ type: EventType.RoomName, room: roomId, user: userB, event: true, content: { name: "New room", }, - }) as MatrixEvent, + }), ]; room.addLiveEvents(events); expect(room.currentState.setStateEvents).toHaveBeenCalledWith( @@ -318,13 +318,13 @@ describe("Room", function() { it("should emit Room.localEchoUpdated when a local echo is updated", function() { const localEvent = utils.mkMessage({ room: roomId, user: userA, event: true, - }) as MatrixEvent; + }); localEvent.status = EventStatus.SENDING; const localEventId = localEvent.getId(); const remoteEvent = utils.mkMessage({ room: roomId, user: userA, event: true, - }) as MatrixEvent; + }); remoteEvent.event.unsigned = { transaction_id: "TXN_ID" }; const remoteEventId = remoteEvent.getId(); @@ -445,11 +445,11 @@ describe("Room", function() { const newEv = utils.mkEvent({ type: EventType.RoomName, room: roomId, user: userA, event: true, content: { name: "New Room Name" }, - }) as MatrixEvent; + }); const oldEv = utils.mkEvent({ type: EventType.RoomName, room: roomId, user: userA, event: true, content: { name: "Old Room Name" }, - }) as MatrixEvent; + }); room.addLiveEvents([newEv]); expect(newEv.sender).toEqual(sentinel); room.addEventsToTimeline([oldEv], true, room.getLiveTimeline()); @@ -482,10 +482,10 @@ describe("Room", function() { const newEv = utils.mkMembership({ room: roomId, mship: "invite", user: userB, skey: userA, event: true, - }) as MatrixEvent; + }); const oldEv = utils.mkMembership({ room: roomId, mship: "ban", user: userB, skey: userA, event: true, - }) as MatrixEvent; + }); room.addLiveEvents([newEv]); expect(newEv.target).toEqual(sentinel); room.addEventsToTimeline([oldEv], true, room.getLiveTimeline()); @@ -497,13 +497,13 @@ describe("Room", function() { const events: MatrixEvent[] = [ utils.mkMembership({ room: roomId, mship: "invite", user: userB, skey: userA, event: true, - }) as MatrixEvent, + }), utils.mkEvent({ type: EventType.RoomName, room: roomId, user: userB, event: true, content: { name: "New room", }, - }) as MatrixEvent, + }), ]; room.addEventsToTimeline(events, true, room.getLiveTimeline()); @@ -631,13 +631,13 @@ describe("Room", function() { const events: MatrixEvent[] = [ utils.mkMessage({ room: roomId, user: userA, msg: "1111", event: true, - }) as MatrixEvent, + }), utils.mkMessage({ room: roomId, user: userA, msg: "2222", event: true, - }) as MatrixEvent, + }), utils.mkMessage({ room: roomId, user: userA, msg: "3333", event: true, - }) as MatrixEvent, + }), ]; it("should handle events in the same timeline", function() { @@ -778,26 +778,26 @@ describe("Room", function() { type: EventType.RoomJoinRules, room: roomId, user: userA, content: { join_rule: rule, }, event: true, - }) as MatrixEvent]); + })]); }; const setAltAliases = function(aliases: string[]) { room.addLiveEvents([utils.mkEvent({ type: EventType.RoomCanonicalAlias, room: roomId, skey: "", content: { alt_aliases: aliases, }, event: true, - }) as MatrixEvent]); + })]); }; const setAlias = function(alias: string) { room.addLiveEvents([utils.mkEvent({ type: EventType.RoomCanonicalAlias, room: roomId, skey: "", content: { alias }, event: true, - }) as MatrixEvent]); + })]); }; const setRoomName = function(name: string) { room.addLiveEvents([utils.mkEvent({ type: EventType.RoomName, room: roomId, user: userA, content: { name: name, }, event: true, - }) as MatrixEvent]); + })]); }; const addMember = function(userId: string, state = "join", opts: any = {}) { opts.room = roomId; @@ -805,7 +805,7 @@ describe("Room", function() { opts.user = opts.user || userId; opts.skey = userId; opts.event = true; - const event = utils.mkMembership(opts) as MatrixEvent; + const event = utils.mkMembership(opts); room.addLiveEvents([event]); return event; }; @@ -1113,7 +1113,7 @@ describe("Room", function() { const eventToAck = utils.mkMessage({ room: roomId, user: userA, msg: "PLEASE ACKNOWLEDGE MY EXISTENCE", event: true, - }) as MatrixEvent; + }); function mkReceipt(roomId: string, records) { const content = {}; @@ -1179,7 +1179,7 @@ describe("Room", function() { const nextEventToAck = utils.mkMessage({ room: roomId, user: userA, msg: "I AM HERE YOU KNOW", event: true, - }) as MatrixEvent; + }); const ts = 13787898424; room.addReceipt(mkReceipt(roomId, [ mkRecord(eventToAck.getId(), "m.read", userB, ts), @@ -1214,11 +1214,11 @@ describe("Room", function() { const eventTwo = utils.mkMessage({ room: roomId, user: userA, msg: "2222", event: true, - }) as MatrixEvent; + }); const eventThree = utils.mkMessage({ room: roomId, user: userA, msg: "3333", event: true, - }) as MatrixEvent; + }); const ts = 13787898424; room.addReceipt(mkReceipt(roomId, [ mkRecord(eventToAck.getId(), "m.read", userB, ts), @@ -1266,15 +1266,15 @@ describe("Room", function() { utils.mkMessage({ room: roomId, user: userA, msg: "1111", event: true, - }) as MatrixEvent, + }), utils.mkMessage({ room: roomId, user: userA, msg: "2222", event: true, - }) as MatrixEvent, + }), utils.mkMessage({ room: roomId, user: userA, msg: "3333", event: true, - }) as MatrixEvent, + }), ]; room.addLiveEvents(events); @@ -1304,15 +1304,15 @@ describe("Room", function() { utils.mkMessage({ room: roomId, user: userA, msg: "1111", event: true, - }) as MatrixEvent, + }), utils.mkMessage({ room: roomId, user: userA, msg: "2222", event: true, - }) as MatrixEvent, + }), utils.mkMessage({ room: roomId, user: userA, msg: "3333", event: true, - }) as MatrixEvent, + }), ]; room.addLiveEvents(events); @@ -1404,14 +1404,14 @@ describe("Room", function() { }); const eventA = utils.mkMessage({ room: roomId, user: userA, msg: "remote 1", event: true, - }) as MatrixEvent; + }); const eventB = utils.mkMessage({ room: roomId, user: userA, msg: "local 1", event: true, - }) as MatrixEvent; + }); eventB.status = EventStatus.SENDING; const eventC = utils.mkMessage({ room: roomId, user: userA, msg: "remote 2", event: true, - }) as MatrixEvent; + }); room.addLiveEvents([eventA]); room.addPendingEvent(eventB, "TXN1"); room.addLiveEvents([eventC]); @@ -1430,14 +1430,14 @@ describe("Room", function() { }); const eventA = utils.mkMessage({ room: roomId, user: userA, msg: "remote 1", event: true, - }) as MatrixEvent; + }); const eventB = utils.mkMessage({ room: roomId, user: userA, msg: "local 1", event: true, - }) as MatrixEvent; + }); eventB.status = EventStatus.SENDING; const eventC = utils.mkMessage({ room: roomId, user: userA, msg: "remote 2", event: true, - }) as MatrixEvent; + }); room.addLiveEvents([eventA]); room.addPendingEvent(eventB, "TXN1"); room.addLiveEvents([eventC]); @@ -1457,7 +1457,7 @@ describe("Room", function() { }); const eventA = utils.mkMessage({ room: roomId, user: userA, event: true, - }) as MatrixEvent; + }); eventA.status = EventStatus.SENDING; const eventId = eventA.getId(); @@ -1490,7 +1490,7 @@ describe("Room", function() { const room = new Room(roomId, null, userA); const eventA = utils.mkMessage({ room: roomId, user: userA, event: true, - }) as MatrixEvent; + }); eventA.status = EventStatus.SENDING; const eventId = eventA.getId(); @@ -1565,7 +1565,7 @@ describe("Room", function() { room: roomId, event: true, name: "User A", - }) as MatrixEvent; + }); it("should load members from server on first call", async function() { const client = createClientMock([memberEvent]); @@ -1585,7 +1585,7 @@ describe("Room", function() { room: roomId, event: true, name: "Ms A", - }) as MatrixEvent; + }); const client = createClientMock([memberEvent2], [memberEvent]); const room = new Room(roomId, client as any, null, { lazyLoadMembers: true }); @@ -1656,7 +1656,7 @@ describe("Room", function() { mship: "join", room: roomId, event: true, - }) as MatrixEvent]); + })]); expect(room.guessDMUserId()).toEqual(userB); }); it("should return self if only member present", function() { @@ -1689,11 +1689,11 @@ describe("Room", function() { utils.mkMembership({ user: userA, mship: "join", room: roomId, event: true, name: "User A", - }) as MatrixEvent, + }), utils.mkMembership({ user: userB, mship: "join", room: roomId, event: true, name: "User B", - }) as MatrixEvent, + }), ]); expect(room.getDefaultRoomName(userA)).toEqual("User B"); }); @@ -1704,11 +1704,11 @@ describe("Room", function() { utils.mkMembership({ user: userA, mship: "join", room: roomId, event: true, name: "User A", - }) as MatrixEvent, + }), utils.mkMembership({ user: userB, mship: "ban", room: roomId, event: true, name: "User B", - }) as MatrixEvent, + }), ]); expect(room.getDefaultRoomName(userA)).toEqual("Empty room (was User B)"); }); @@ -1719,11 +1719,11 @@ describe("Room", function() { utils.mkMembership({ user: userA, mship: "join", room: roomId, event: true, name: "User A", - }) as MatrixEvent, + }), utils.mkMembership({ user: userB, mship: "invite", room: roomId, event: true, name: "User B", - }) as MatrixEvent, + }), ]); expect(room.getDefaultRoomName(userA)).toEqual("User B"); }); @@ -1734,11 +1734,11 @@ describe("Room", function() { utils.mkMembership({ user: userA, mship: "join", room: roomId, event: true, name: "User A", - }) as MatrixEvent, + }), utils.mkMembership({ user: userB, mship: "leave", room: roomId, event: true, name: "User B", - }) as MatrixEvent, + }), ]); expect(room.getDefaultRoomName(userA)).toEqual("Empty room (was User B)"); }); @@ -1749,15 +1749,15 @@ describe("Room", function() { utils.mkMembership({ user: userA, mship: "join", room: roomId, event: true, name: "User A", - }) as MatrixEvent, + }), utils.mkMembership({ user: userB, mship: "join", room: roomId, event: true, name: "User B", - }) as MatrixEvent, + }), utils.mkMembership({ user: userC, mship: "join", room: roomId, event: true, name: "User C", - }) as MatrixEvent, + }), ]); expect(room.getDefaultRoomName(userA)).toEqual("User B and User C"); }); @@ -1768,19 +1768,19 @@ describe("Room", function() { utils.mkMembership({ user: userA, mship: "join", room: roomId, event: true, name: "User A", - }) as MatrixEvent, + }), utils.mkMembership({ user: userB, mship: "join", room: roomId, event: true, name: "User B", - }) as MatrixEvent, + }), utils.mkMembership({ user: userC, mship: "join", room: roomId, event: true, name: "User C", - }) as MatrixEvent, + }), utils.mkMembership({ user: userD, mship: "join", room: roomId, event: true, name: "User D", - }) as MatrixEvent, + }), ]); expect(room.getDefaultRoomName(userA)).toEqual("User B and 2 others"); }); @@ -1793,18 +1793,18 @@ describe("Room", function() { utils.mkMembership({ user: userA, mship: "join", room: roomId, event: true, name: "User A", - }) as MatrixEvent, + }), utils.mkMembership({ user: userB, mship: "join", room: roomId, event: true, name: "User B", - }) as MatrixEvent, + }), utils.mkEvent({ type: UNSTABLE_ELEMENT_FUNCTIONAL_USERS.name, skey: "", room: roomId, event: true, content: { service_members: [], }, - }) as MatrixEvent, + }), ]); expect(room.getDefaultRoomName(userA)).toEqual("User B"); }); @@ -1815,11 +1815,11 @@ describe("Room", function() { utils.mkMembership({ user: userA, mship: "join", room: roomId, event: true, name: "User A", - }) as MatrixEvent, + }), utils.mkMembership({ user: userB, mship: "join", room: roomId, event: true, name: "User B", - }) as MatrixEvent, + }), utils.mkEvent({ type: UNSTABLE_ELEMENT_FUNCTIONAL_USERS.name, skey: "", @@ -1828,7 +1828,7 @@ describe("Room", function() { content: { service_members: 1, }, - }) as MatrixEvent, + }), ]); expect(room.getDefaultRoomName(userA)).toEqual("User B"); }); @@ -1839,18 +1839,18 @@ describe("Room", function() { utils.mkMembership({ user: userA, mship: "join", room: roomId, event: true, name: "User A", - }) as MatrixEvent, + }), utils.mkMembership({ user: userB, mship: "join", room: roomId, event: true, name: "User B", - }) as MatrixEvent, + }), utils.mkEvent({ type: UNSTABLE_ELEMENT_FUNCTIONAL_USERS.name, skey: "", room: roomId, event: true, content: { service_members: userB, }, - }) as MatrixEvent, + }), ]); expect(room.getDefaultRoomName(userA)).toEqual("User B"); }); @@ -1861,18 +1861,18 @@ describe("Room", function() { utils.mkMembership({ user: userA, mship: "join", room: roomId, event: true, name: "User A", - }) as MatrixEvent, + }), utils.mkMembership({ user: userB, mship: "join", room: roomId, event: true, name: "User B", - }) as MatrixEvent, + }), utils.mkEvent({ type: UNSTABLE_ELEMENT_FUNCTIONAL_USERS.name, skey: "", room: roomId, event: true, content: { service_members: [userB], }, - }) as MatrixEvent, + }), ]); expect(room.getDefaultRoomName(userA)).toEqual("Empty room"); }); @@ -1883,22 +1883,22 @@ describe("Room", function() { utils.mkMembership({ user: userA, mship: "join", room: roomId, event: true, name: "User A", - }) as MatrixEvent, + }), utils.mkMembership({ user: userB, mship: "join", room: roomId, event: true, name: "User B", - }) as MatrixEvent, + }), utils.mkMembership({ user: userC, mship: "join", room: roomId, event: true, name: "User C", - }) as MatrixEvent, + }), utils.mkEvent({ type: UNSTABLE_ELEMENT_FUNCTIONAL_USERS.name, skey: "", room: roomId, event: true, user: userA, content: { service_members: [userC], }, - }) as MatrixEvent, + }), ]); expect(room.getDefaultRoomName(userA)).toEqual("User B"); }); @@ -1909,22 +1909,22 @@ describe("Room", function() { utils.mkMembership({ user: userA, mship: "join", room: roomId, event: true, name: "User A", - }) as MatrixEvent, + }), utils.mkMembership({ user: userB, mship: "join", room: roomId, event: true, name: "User B", - }) as MatrixEvent, + }), utils.mkMembership({ user: userC, mship: "join", room: roomId, event: true, name: "User C", - }) as MatrixEvent, + }), utils.mkEvent({ type: UNSTABLE_ELEMENT_FUNCTIONAL_USERS.name, skey: "", room: roomId, event: true, user: userA, content: { service_members: [userB, userC], }, - }) as MatrixEvent, + }), ]); expect(room.getDefaultRoomName(userA)).toEqual("Empty room"); }); @@ -1935,18 +1935,18 @@ describe("Room", function() { utils.mkMembership({ user: userA, mship: "join", room: roomId, event: true, name: "User A", - }) as MatrixEvent, + }), utils.mkMembership({ user: userB, mship: "join", room: roomId, event: true, name: "User B", - }) as MatrixEvent, + }), utils.mkEvent({ type: UNSTABLE_ELEMENT_FUNCTIONAL_USERS.name, skey: "", room: roomId, event: true, user: userA, content: { service_members: [userC], }, - }) as MatrixEvent, + }), ]); expect(room.getDefaultRoomName(userA)).toEqual("User B"); }); From 052f04deeb96a07ec0718d431027c25f383cd18c Mon Sep 17 00:00:00 2001 From: Michael Telatynski <7t3chguy@gmail.com> Date: Mon, 13 Jun 2022 12:59:31 +0100 Subject: [PATCH 2/6] Tweak existing tests --- .../matrix-client-event-timeline.spec.js | 22 +++++++++---------- 1 file changed, 10 insertions(+), 12 deletions(-) diff --git a/spec/integ/matrix-client-event-timeline.spec.js b/spec/integ/matrix-client-event-timeline.spec.js index 420fee6e588..5a5d1d036e7 100644 --- a/spec/integ/matrix-client-event-timeline.spec.js +++ b/spec/integ/matrix-client-event-timeline.spec.js @@ -540,7 +540,7 @@ describe("MatrixClient event timelines", function() { expect(timeline.getEvents().find(e => e.getId() === THREAD_REPLY.event_id)).toBeTruthy(); }); - it("should return undefined when event is not within a thread but timelineSet is", async () => { + it("should return undefined when event is not in the thread that the given timelineSet is representing", () => { client.clientOpts.experimentalThreadSupport = true; Thread.setServerSideSupport(true); client.stopClient(); // we don't need the client to be syncing at this time @@ -566,14 +566,13 @@ describe("MatrixClient event timelines", function() { }; }); - const timelinePromise = client.getEventTimeline(timelineSet, EVENTS[0].event_id); - await httpBackend.flushAllExpected(); - - const timeline = await timelinePromise; - expect(timeline).toBeUndefined(); + return Promise.all([ + expect(client.getEventTimeline(timelineSet, EVENTS[0].event_id)).resolves.toBeUndefined(), + httpBackend.flushAllExpected(), + ]); }); - it("should return undefined when event is within a thread but timelineSet is not", async () => { + it("should return undefined when event is within a thread but timelineSet is not", () => { client.clientOpts.experimentalThreadSupport = true; Thread.setServerSideSupport(true); client.stopClient(); // we don't need the client to be syncing at this time @@ -592,11 +591,10 @@ describe("MatrixClient event timelines", function() { }; }); - const timelinePromise = client.getEventTimeline(timelineSet, THREAD_REPLY.event_id); - await httpBackend.flushAllExpected(); - - const timeline = await timelinePromise; - expect(timeline).toBeUndefined(); + return Promise.all([ + expect(client.getEventTimeline(timelineSet, THREAD_REPLY.event_id)).resolves.toBeUndefined(), + httpBackend.flushAllExpected(), + ]); }); it("should should add lazy loading filter when requested", async () => { From 5d5fa944d2215e4cc0487b0fe80e894672b7c3ed Mon Sep 17 00:00:00 2001 From: Michael Telatynski <7t3chguy@gmail.com> Date: Mon, 13 Jun 2022 14:06:33 +0100 Subject: [PATCH 3/6] Add test around `MatrixClient::getEventTimeline` --- .../matrix-client-event-timeline.spec.js | 36 +++++++++++++++++++ src/models/event-timeline.ts | 2 +- 2 files changed, 37 insertions(+), 1 deletion(-) diff --git a/spec/integ/matrix-client-event-timeline.spec.js b/spec/integ/matrix-client-event-timeline.spec.js index 5a5d1d036e7..60c0b66e9a6 100644 --- a/spec/integ/matrix-client-event-timeline.spec.js +++ b/spec/integ/matrix-client-event-timeline.spec.js @@ -540,6 +540,42 @@ describe("MatrixClient event timelines", function() { 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 () => { + client.clientOpts.experimentalThreadSupport = true; + Thread.setServerSideSupport(true); + client.stopClient(); // we don't need the client to be syncing at this time + const room = client.getRoom(roomId); + const threadRoot = new MatrixEvent(THREAD_ROOT); + const thread = room.createThread(THREAD_ROOT.event_id, threadRoot, [threadRoot], false); + const timelineSet = room.getTimelineSets()[0]; + + httpBackend.when("GET", "/rooms/!foo%3Abar/event/" + encodeURIComponent(THREAD_ROOT.event_id)) + .respond(200, function() { + return THREAD_ROOT; + }); + + httpBackend.when("GET", "/rooms/!foo%3Abar/context/" + encodeURIComponent(THREAD_ROOT.event_id)) + .respond(200, function() { + return { + start: "start_token0", + events_before: [], + event: THREAD_ROOT, + events_after: [], + end: "end_token0", + state: [], + }; + }); + + const [timeline] = await Promise.all([ + client.getEventTimeline(timelineSet, THREAD_ROOT.event_id), + httpBackend.flushAllExpected(), + ]); + + expect(timeline).not.toBe(thread.liveTimeline); + expect(timelineSet.getTimelines()).toContain(timeline); + 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", () => { client.clientOpts.experimentalThreadSupport = true; Thread.setServerSideSupport(true); diff --git a/src/models/event-timeline.ts b/src/models/event-timeline.ts index 6ec1aed9561..c73086ec725 100644 --- a/src/models/event-timeline.ts +++ b/src/models/event-timeline.ts @@ -437,7 +437,7 @@ export class EventTimeline { } } - let insertIndex; + let insertIndex: number; if (toStartOfTimeline) { insertIndex = 0; From 1b13d248ce05bfe71b96a138be6bb0b0156bb379 Mon Sep 17 00:00:00 2001 From: Michael Telatynski <7t3chguy@gmail.com> Date: Mon, 13 Jun 2022 14:19:06 +0100 Subject: [PATCH 4/6] Fix test to actually exercise the faulty behaviour --- .../matrix-client-event-timeline.spec.js | 29 +++++++++++-------- 1 file changed, 17 insertions(+), 12 deletions(-) diff --git a/spec/integ/matrix-client-event-timeline.spec.js b/spec/integ/matrix-client-event-timeline.spec.js index 60c0b66e9a6..c165a7057ed 100644 --- a/spec/integ/matrix-client-event-timeline.spec.js +++ b/spec/integ/matrix-client-event-timeline.spec.js @@ -70,10 +70,23 @@ const EVENTS = [ }), ]; -const THREAD_ROOT = utils.mkMessage({ +const THREAD_ROOT = utils.mkEvent({ room: roomId, user: userId, - msg: "thread root", + type: "m.room.message", + content: { + "body": "thread root", + "msgtype": "m.text", + }, + unsigned: { + "m.relations": { + "io.element.thread": { + "latest_event": undefined, + "count": 1, + "current_user_participated": true, + }, + }, + }, }); const THREAD_REPLY = utils.mkEvent({ @@ -91,6 +104,8 @@ const THREAD_REPLY = utils.mkEvent({ }, }); +THREAD_ROOT.unsigned["m.relations"]["io.element.thread"].latest_event = THREAD_REPLY; + // start the client, and wait for it to initialise function startClient(httpBackend, client) { httpBackend.when("GET", "/versions").respond(200, {}); @@ -549,11 +564,6 @@ describe("MatrixClient event timelines", function() { const thread = room.createThread(THREAD_ROOT.event_id, threadRoot, [threadRoot], false); const timelineSet = room.getTimelineSets()[0]; - httpBackend.when("GET", "/rooms/!foo%3Abar/event/" + encodeURIComponent(THREAD_ROOT.event_id)) - .respond(200, function() { - return THREAD_ROOT; - }); - httpBackend.when("GET", "/rooms/!foo%3Abar/context/" + encodeURIComponent(THREAD_ROOT.event_id)) .respond(200, function() { return { @@ -585,11 +595,6 @@ describe("MatrixClient event timelines", function() { const thread = room.createThread(THREAD_ROOT.event_id, threadRoot, [threadRoot], false); const timelineSet = thread.timelineSet; - httpBackend.when("GET", "/rooms/!foo%3Abar/event/" + encodeURIComponent(THREAD_ROOT.event_id)) - .respond(200, function() { - return THREAD_ROOT; - }); - httpBackend.when("GET", "/rooms/!foo%3Abar/context/" + encodeURIComponent(EVENTS[0].event_id)) .respond(200, function() { return { From dcde91007d182f207b9d0cd46b6022232dc73afc Mon Sep 17 00:00:00 2001 From: Michael Telatynski <7t3chguy@gmail.com> Date: Mon, 13 Jun 2022 14:22:52 +0100 Subject: [PATCH 5/6] Extract timelineSet thread belongs logic and test it --- spec/unit/event-timeline-set.spec.ts | 71 ++++++++++++++++++++++++++++ src/client.ts | 10 +--- src/models/event-timeline-set.ts | 25 ++++++++++ 3 files changed, 97 insertions(+), 9 deletions(-) diff --git a/spec/unit/event-timeline-set.spec.ts b/spec/unit/event-timeline-set.spec.ts index ba4ff9fd9aa..689eca0a674 100644 --- a/spec/unit/event-timeline-set.spec.ts +++ b/spec/unit/event-timeline-set.spec.ts @@ -25,6 +25,8 @@ import { Room, DuplicateStrategy, } from '../../src'; +import { Thread } from "../../src/models/thread"; +import { ReEmitter } from "../../src/ReEmitter"; describe('EventTimelineSet', () => { const roomId = '!foo:bar'; @@ -54,6 +56,7 @@ describe('EventTimelineSet', () => { beforeEach(() => { client = utils.mock(MatrixClient, 'MatrixClient'); + client.reEmitter = utils.mock(ReEmitter, 'ReEmitter'); room = new Room(roomId, client, userA); eventTimelineSet = new EventTimelineSet(room); eventTimeline = new EventTimeline(eventTimelineSet); @@ -220,4 +223,72 @@ describe('EventTimelineSet', () => { }); }); }); + + describe("eventBelongs", () => { + const mkThreadResponse = (root: MatrixEvent) => utils.mkEvent({ + event: true, + type: EventType.RoomMessage, + user: userA, + room: roomId, + content: { + "body": "Thread response :: " + Math.random(), + "m.relates_to": { + "event_id": root.getId(), + "m.in_reply_to": { + "event_id": root.getId(), + }, + "rel_type": "m.thread", + }, + }, + }, room.client); + + let thread: Thread; + + beforeEach(() => { + (client.supportsExperimentalThreads as jest.Mock).mockReturnValue(true); + thread = new Thread("!thread_id:server", messageEvent, { room, client }); + }); + + it("should throw if timeline set has no room", () => { + const eventTimelineSet = new EventTimelineSet(undefined, {}, client); + expect(() => eventTimelineSet.eventBelongs(messageEvent)).toThrowError(); + }); + + it("should return false if timeline set is for thread but event is not threaded", () => { + const eventTimelineSet = new EventTimelineSet(room, {}, client, thread); + expect(eventTimelineSet.eventBelongs(replyEvent)).toBeFalsy(); + }); + + it("should return false if timeline set it for thread but event it for a different thread", () => { + const eventTimelineSet = new EventTimelineSet(room, {}, client, thread); + const event = mkThreadResponse(replyEvent); + expect(eventTimelineSet.eventBelongs(event)).toBeFalsy(); + }); + + it("should return false if timeline set is not for a thread but event is a thread response", () => { + const eventTimelineSet = new EventTimelineSet(room, {}, client); + const event = mkThreadResponse(replyEvent); + expect(eventTimelineSet.eventBelongs(event)).toBeFalsy(); + }); + + it("should return true if the timeline set is not for a thread and the event is a thread root", () => { + const eventTimelineSet = new EventTimelineSet(room, {}, client); + expect(eventTimelineSet.eventBelongs(messageEvent)).toBeTruthy(); + }); + + it("should return true if the timeline set is for a thread and the event is its thread root", () => { + const thread = new Thread(messageEvent.getId(), messageEvent, { room, client }); + const eventTimelineSet = new EventTimelineSet(room, {}, client, thread); + messageEvent.setThread(thread); + expect(eventTimelineSet.eventBelongs(messageEvent)).toBeTruthy(); + }); + + it("should return true if the timeline set is for a thread and the event is a response to it", () => { + const thread = new Thread(messageEvent.getId(), messageEvent, { room, client }); + const eventTimelineSet = new EventTimelineSet(room, {}, client, thread); + messageEvent.setThread(thread); + const event = mkThreadResponse(messageEvent); + expect(eventTimelineSet.eventBelongs(event)).toBeTruthy(); + }); + }); }); diff --git a/src/client.ts b/src/client.ts index c29578d20ac..5d7009d4e94 100644 --- a/src/client.ts +++ b/src/client.ts @@ -5299,15 +5299,7 @@ export class MatrixClient extends TypedEventEmitter Date: Wed, 15 Jun 2022 15:37:13 +0100 Subject: [PATCH 6/6] tweak method name --- spec/unit/event-timeline-set.spec.ts | 16 ++++++++-------- src/client.ts | 2 +- src/models/event-timeline-set.ts | 4 ++-- 3 files changed, 11 insertions(+), 11 deletions(-) diff --git a/spec/unit/event-timeline-set.spec.ts b/spec/unit/event-timeline-set.spec.ts index 689eca0a674..42f4bca4de2 100644 --- a/spec/unit/event-timeline-set.spec.ts +++ b/spec/unit/event-timeline-set.spec.ts @@ -224,7 +224,7 @@ describe('EventTimelineSet', () => { }); }); - describe("eventBelongs", () => { + describe("canContain", () => { const mkThreadResponse = (root: MatrixEvent) => utils.mkEvent({ event: true, type: EventType.RoomMessage, @@ -251,36 +251,36 @@ describe('EventTimelineSet', () => { it("should throw if timeline set has no room", () => { const eventTimelineSet = new EventTimelineSet(undefined, {}, client); - expect(() => eventTimelineSet.eventBelongs(messageEvent)).toThrowError(); + expect(() => eventTimelineSet.canContain(messageEvent)).toThrowError(); }); it("should return false if timeline set is for thread but event is not threaded", () => { const eventTimelineSet = new EventTimelineSet(room, {}, client, thread); - expect(eventTimelineSet.eventBelongs(replyEvent)).toBeFalsy(); + expect(eventTimelineSet.canContain(replyEvent)).toBeFalsy(); }); it("should return false if timeline set it for thread but event it for a different thread", () => { const eventTimelineSet = new EventTimelineSet(room, {}, client, thread); const event = mkThreadResponse(replyEvent); - expect(eventTimelineSet.eventBelongs(event)).toBeFalsy(); + expect(eventTimelineSet.canContain(event)).toBeFalsy(); }); it("should return false if timeline set is not for a thread but event is a thread response", () => { const eventTimelineSet = new EventTimelineSet(room, {}, client); const event = mkThreadResponse(replyEvent); - expect(eventTimelineSet.eventBelongs(event)).toBeFalsy(); + expect(eventTimelineSet.canContain(event)).toBeFalsy(); }); it("should return true if the timeline set is not for a thread and the event is a thread root", () => { const eventTimelineSet = new EventTimelineSet(room, {}, client); - expect(eventTimelineSet.eventBelongs(messageEvent)).toBeTruthy(); + expect(eventTimelineSet.canContain(messageEvent)).toBeTruthy(); }); it("should return true if the timeline set is for a thread and the event is its thread root", () => { const thread = new Thread(messageEvent.getId(), messageEvent, { room, client }); const eventTimelineSet = new EventTimelineSet(room, {}, client, thread); messageEvent.setThread(thread); - expect(eventTimelineSet.eventBelongs(messageEvent)).toBeTruthy(); + expect(eventTimelineSet.canContain(messageEvent)).toBeTruthy(); }); it("should return true if the timeline set is for a thread and the event is a response to it", () => { @@ -288,7 +288,7 @@ describe('EventTimelineSet', () => { const eventTimelineSet = new EventTimelineSet(room, {}, client, thread); messageEvent.setThread(thread); const event = mkThreadResponse(messageEvent); - expect(eventTimelineSet.eventBelongs(event)).toBeTruthy(); + expect(eventTimelineSet.canContain(event)).toBeTruthy(); }); }); }); diff --git a/src/client.ts b/src/client.ts index 5d7009d4e94..9a335c1debf 100644 --- a/src/client.ts +++ b/src/client.ts @@ -5299,7 +5299,7 @@ export class MatrixClient extends TypedEventEmitter