From 59fd1428fc05fe2bfe9c587d9a2a368bac6aee70 Mon Sep 17 00:00:00 2001 From: Florian Duros Date: Wed, 31 Jan 2024 17:52:14 +0100 Subject: [PATCH 1/2] Add tests for `doesRoomHaveUnreadThreads` --- src/Unread.ts | 4 +- test/Unread-test.ts | 109 ++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 111 insertions(+), 2 deletions(-) diff --git a/src/Unread.ts b/src/Unread.ts index ca84ca651a7..7de9149b872 100644 --- a/src/Unread.ts +++ b/src/Unread.ts @@ -115,8 +115,8 @@ export function doesRoomHaveUnreadThreads(room: Room): boolean { return false; } - for (const withTimeline of room.getThreads()) { - if (doesTimelineHaveUnreadMessages(room, withTimeline.timeline)) { + for (const thread of room.getThreads()) { + if (doesTimelineHaveUnreadMessages(room, thread.timeline)) { // We found an unread, so the room is unread return true; } diff --git a/test/Unread-test.ts b/test/Unread-test.ts index 843490b4256..3c68b918f1b 100644 --- a/test/Unread-test.ts +++ b/test/Unread-test.ts @@ -23,6 +23,7 @@ import { makeBeaconEvent, mkEvent, stubClient } from "./test-utils"; import { makeThreadEvents, mkThread, populateThread } from "./test-utils/threads"; import { doesRoomHaveUnreadMessages, + doesRoomHaveUnreadThreads, doesRoomOrThreadHaveUnreadMessages, eventTriggersUnreadCount, } from "../src/Unread"; @@ -528,4 +529,112 @@ describe("Unread", () => { }); }); }); + + describe("doesRoomHaveUnreadThreads()", () => { + let room: Room; + const roomId = "!abc:server.org"; + const myId = client.getSafeUserId(); + + beforeAll(() => { + client.supportsThreads = () => true; + }); + + beforeEach(async () => { + room = new Room(roomId, client, myId); + jest.spyOn(logger, "warn"); + + // Don't care about the code path of hidden events. + mocked(haveRendererForEvent).mockClear().mockReturnValue(true); + }); + + it("returns false when no threads", () => { + expect(doesRoomHaveUnreadThreads(room)).toBe(false); + + // Add event to the room + const event = mkEvent({ + event: true, + type: "m.room.message", + user: aliceId, + room: roomId, + content: {}, + }); + room.addLiveEvents([event]); + + // It still returns false + expect(doesRoomHaveUnreadThreads(room)).toBe(false); + }); + + it("return true when we don't have any receipt for the thread", async () => { + await populateThread({ + room, + client, + authorId: myId, + participantUserIds: [aliceId], + }); + + // There is no receipt for the thread, it should be unread + expect(doesRoomHaveUnreadThreads(room)).toBe(true); + }); + + it("return false when we have a receipt for the thread", async () => { + const { events, rootEvent } = await populateThread({ + room, + client, + authorId: myId, + participantUserIds: [aliceId], + }); + + // Mark the thread as read. + const receipt = new MatrixEvent({ + type: "m.receipt", + room_id: "!foo:bar", + content: { + [events[events.length - 1].getId()!]: { + [ReceiptType.Read]: { + [myId]: { ts: 1, thread_id: rootEvent.getId()! }, + }, + }, + }, + }); + room.addReceipt(receipt); + + // There is a receipt for the thread, it should be read + expect(doesRoomHaveUnreadThreads(room)).toBe(false); + }); + + it("return true when only of the threads has a receipt", async () => { + // Create a first thread + await populateThread({ + room, + client, + authorId: myId, + participantUserIds: [aliceId], + }); + + // Create a second thread + const { events, rootEvent } = await populateThread({ + room, + client, + authorId: myId, + participantUserIds: [aliceId], + }); + + // Mark the thread as read. + const receipt = new MatrixEvent({ + type: "m.receipt", + room_id: "!foo:bar", + content: { + [events[events.length - 1].getId()!]: { + [ReceiptType.Read]: { + [myId]: { ts: 1, thread_id: rootEvent.getId()! }, + }, + }, + }, + }); + room.addReceipt(receipt); + + // The first thread doesn't have a receipt, it should be unread + expect(doesRoomHaveUnreadThreads(room)).toBe(true); + }); + }); }); From ce3cd54b2c8c3925494e47ddcd5619b667cf4f43 Mon Sep 17 00:00:00 2001 From: Florian Duros Date: Thu, 1 Feb 2024 14:13:03 +0100 Subject: [PATCH 2/2] Add tests for `getThreadNotificationLevel` --- test/utils/notifications-test.ts | 24 ++++++++++++++++++++++++ 1 file changed, 24 insertions(+) diff --git a/test/utils/notifications-test.ts b/test/utils/notifications-test.ts index 62200c7ef9b..30316dd5e68 100644 --- a/test/utils/notifications-test.ts +++ b/test/utils/notifications-test.ts @@ -25,6 +25,7 @@ import { clearAllNotifications, clearRoomNotification, notificationLevelToIndicator, + getThreadNotificationLevel, } from "../../src/utils/notifications"; import SettingsStore from "../../src/settings/SettingsStore"; import { getMockClientWithEventEmitter } from "../test-utils/client"; @@ -235,4 +236,27 @@ describe("notifications", () => { expect(notificationLevelToIndicator(NotificationLevel.Highlight)).toEqual("critical"); }); }); + + describe("getThreadNotificationLevel", () => { + let room: Room; + + const ROOM_ID = "123"; + const USER_ID = "@bob:example.org"; + + beforeEach(() => { + room = new Room(ROOM_ID, MatrixClientPeg.safeGet(), USER_ID); + }); + + it.each([ + { notificationCountType: NotificationCountType.Highlight, expected: NotificationLevel.Highlight }, + { notificationCountType: NotificationCountType.Total, expected: NotificationLevel.Notification }, + { notificationCountType: null, expected: NotificationLevel.Activity }, + ])( + "returns NotificationLevel $expected when notificationCountType is $expected", + ({ notificationCountType, expected }) => { + jest.spyOn(room, "threadsAggregateNotificationType", "get").mockReturnValue(notificationCountType); + expect(getThreadNotificationLevel(room)).toEqual(expected); + }, + ); + }); });