Skip to content
This repository was archived by the owner on Sep 11, 2024. It is now read-only.

Commit 4866e4f

Browse files
committed
Add tests for unread notification facilities
Add some tests to guarantee some consistency in `useUnreadNotifications` and `RoomNotificationState`. Signed-off-by: Clark Fischer <clark.fischer@gmail.com>
1 parent 056fec8 commit 4866e4f

File tree

4 files changed

+307
-54
lines changed

4 files changed

+307
-54
lines changed

test/RoomNotifs-test.ts

Lines changed: 12 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
Copyright 2022 The Matrix.org Foundation C.I.C.
2+
Copyright 2022 - 2023 The Matrix.org Foundation C.I.C.
33
44
Licensed under the Apache License, Version 2.0 (the "License");
55
you may not use this file except in compliance with the License.
@@ -15,10 +15,11 @@ limitations under the License.
1515
*/
1616

1717
import { mocked } from "jest-mock";
18-
import { ConditionKind, PushRuleActionName, TweakName } from "matrix-js-sdk/src/@types/PushRules";
18+
import { PushRuleActionName, TweakName } from "matrix-js-sdk/src/@types/PushRules";
1919
import { NotificationCountType, Room } from "matrix-js-sdk/src/models/room";
20+
import { MatrixClient } from "matrix-js-sdk/src/matrix";
2021

21-
import { mkEvent, stubClient } from "./test-utils";
22+
import { mkEvent, mkRoom, muteRoom, stubClient } from "./test-utils";
2223
import { MatrixClientPeg } from "../src/MatrixClientPeg";
2324
import { getRoomNotifsState, RoomNotifState, getUnreadNotificationCount } from "../src/RoomNotifs";
2425

@@ -52,26 +53,9 @@ describe("RoomNotifs test", () => {
5253

5354
it("getRoomNotifsState handles mute state", () => {
5455
const cli = MatrixClientPeg.get();
55-
cli.pushRules = {
56-
global: {
57-
override: [
58-
{
59-
rule_id: "!roomId:server",
60-
enabled: true,
61-
default: false,
62-
conditions: [
63-
{
64-
kind: ConditionKind.EventMatch,
65-
key: "room_id",
66-
pattern: "!roomId:server",
67-
},
68-
],
69-
actions: [PushRuleActionName.DontNotify],
70-
},
71-
],
72-
},
73-
};
74-
expect(getRoomNotifsState(cli, "!roomId:server")).toBe(RoomNotifState.Mute);
56+
const room = mkRoom(cli, "!roomId:server");
57+
muteRoom(room);
58+
expect(getRoomNotifsState(cli, room.roomId)).toBe(RoomNotifState.Mute);
7559
});
7660

7761
it("getRoomNotifsState handles mentions only", () => {
@@ -100,11 +84,11 @@ describe("RoomNotifs test", () => {
10084
const ROOM_ID = "!roomId:example.org";
10185
const THREAD_ID = "$threadId";
10286

103-
let cli;
87+
let cli: jest.Mocked<MatrixClient>;
10488
let room: Room;
10589
beforeEach(() => {
106-
cli = MatrixClientPeg.get();
107-
room = new Room(ROOM_ID, cli, cli.getUserId());
90+
cli = MatrixClientPeg.get() as jest.Mocked<MatrixClient>;
91+
room = new Room(ROOM_ID, cli, cli.getUserId()!);
10892
});
10993

11094
it("counts room notification type", () => {
@@ -125,7 +109,7 @@ describe("RoomNotifs test", () => {
125109
room.setUnreadNotificationCount(NotificationCountType.Highlight, 1);
126110

127111
const OLD_ROOM_ID = "!oldRoomId:example.org";
128-
const oldRoom = new Room(OLD_ROOM_ID, cli, cli.getUserId());
112+
const oldRoom = new Room(OLD_ROOM_ID, cli, cli.getUserId()!);
129113
oldRoom.setUnreadNotificationCount(NotificationCountType.Total, 10);
130114
oldRoom.setUnreadNotificationCount(NotificationCountType.Highlight, 6);
131115

@@ -135,7 +119,7 @@ describe("RoomNotifs test", () => {
135119
event: true,
136120
type: "m.room.create",
137121
room: ROOM_ID,
138-
user: cli.getUserId(),
122+
user: cli.getUserId()!,
139123
content: {
140124
creator: cli.getUserId(),
141125
room_version: "5",
Lines changed: 110 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,110 @@
1+
/*
2+
Copyright 2023 The Matrix.org Foundation C.I.C.
3+
4+
Licensed under the Apache License, Version 2.0 (the "License");
5+
you may not use this file except in compliance with the License.
6+
You may obtain a copy of the License at
7+
8+
http://www.apache.org/licenses/LICENSE-2.0
9+
10+
Unless required by applicable law or agreed to in writing, software
11+
distributed under the License is distributed on an "AS IS" BASIS,
12+
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
See the License for the specific language governing permissions and
14+
limitations under the License.
15+
*/
16+
17+
import { renderHook } from "@testing-library/react-hooks";
18+
import { EventStatus, NotificationCountType, PendingEventOrdering } from "matrix-js-sdk/src/matrix";
19+
import { Room } from "matrix-js-sdk/src/matrix";
20+
21+
import type { MatrixClient } from "matrix-js-sdk/src/matrix";
22+
import { useUnreadNotifications } from "../../src/hooks/useUnreadNotifications";
23+
import { NotificationColor } from "../../src/stores/notifications/NotificationColor";
24+
import { mkEvent, muteRoom, stubClient } from "../test-utils";
25+
26+
describe("useUnreadNotifications", () => {
27+
let client: MatrixClient;
28+
let room: Room;
29+
30+
beforeEach(() => {
31+
client = stubClient();
32+
room = new Room("!room:example.org", client, "@user:example.org", {
33+
pendingEventOrdering: PendingEventOrdering.Detached,
34+
});
35+
});
36+
37+
function setUnreads(greys: number, reds: number): void {
38+
room.setUnreadNotificationCount(NotificationCountType.Highlight, reds);
39+
room.setUnreadNotificationCount(NotificationCountType.Total, greys);
40+
}
41+
42+
it("shows nothing by default", async () => {
43+
const { result } = renderHook(() => useUnreadNotifications(room));
44+
const { color, symbol, count } = result.current;
45+
46+
expect(symbol).toBe(null);
47+
expect(color).toBe(NotificationColor.None);
48+
expect(count).toBe(0);
49+
});
50+
51+
it("indicates if there are unsent messages", async () => {
52+
const event = mkEvent({
53+
event: true,
54+
type: "m.message",
55+
user: "@user:example.org",
56+
content: {},
57+
});
58+
event.status = EventStatus.NOT_SENT;
59+
room.addPendingEvent(event, "txn");
60+
61+
const { result } = renderHook(() => useUnreadNotifications(room));
62+
const { color, symbol, count } = result.current;
63+
64+
expect(symbol).toBe("!");
65+
expect(color).toBe(NotificationColor.Unsent);
66+
expect(count).toBeGreaterThan(0);
67+
});
68+
69+
it("indicates the user has been invited to a channel", async () => {
70+
room.updateMyMembership("invite");
71+
72+
const { result } = renderHook(() => useUnreadNotifications(room));
73+
const { color, symbol, count } = result.current;
74+
75+
expect(symbol).toBe("!");
76+
expect(color).toBe(NotificationColor.Red);
77+
expect(count).toBeGreaterThan(0);
78+
});
79+
80+
it("shows nothing for muted channels", async () => {
81+
setUnreads(999, 999);
82+
muteRoom(room);
83+
84+
const { result } = renderHook(() => useUnreadNotifications(room));
85+
const { color, count } = result.current;
86+
87+
expect(color).toBe(NotificationColor.None);
88+
expect(count).toBe(0);
89+
});
90+
91+
it("uses the correct number of unreads", async () => {
92+
setUnreads(999, 0);
93+
94+
const { result } = renderHook(() => useUnreadNotifications(room));
95+
const { color, count } = result.current;
96+
97+
expect(color).toBe(NotificationColor.Grey);
98+
expect(count).toBe(999);
99+
});
100+
101+
it("uses the correct number of highlights", async () => {
102+
setUnreads(0, 888);
103+
104+
const { result } = renderHook(() => useUnreadNotifications(room));
105+
const { color, count } = result.current;
106+
107+
expect(color).toBe(NotificationColor.Red);
108+
expect(count).toBe(888);
109+
});
110+
});
Lines changed: 140 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
Copyright 2022 The Matrix.org Foundation C.I.C.
2+
Copyright 2022 - 2023 The Matrix.org Foundation C.I.C.
33
44
Licensed under the Apache License, Version 2.0 (the "License");
55
you may not use this file except in compliance with the License.
@@ -15,38 +15,165 @@ limitations under the License.
1515
*/
1616

1717
import { Room } from "matrix-js-sdk/src/models/room";
18-
import { MatrixEventEvent, MatrixEvent, MatrixClient } from "matrix-js-sdk/src/matrix";
18+
import {
19+
MatrixEventEvent,
20+
PendingEventOrdering,
21+
EventStatus,
22+
NotificationCountType,
23+
EventType,
24+
} from "matrix-js-sdk/src/matrix";
25+
import { MatrixEvent } from "matrix-js-sdk/src/matrix";
1926

20-
import { stubClient } from "../../test-utils";
21-
import { MatrixClientPeg } from "../../../src/MatrixClientPeg";
27+
import type { MatrixClient } from "matrix-js-sdk/src/matrix";
28+
import { mkEvent, muteRoom, stubClient } from "../../test-utils";
2229
import { RoomNotificationState } from "../../../src/stores/notifications/RoomNotificationState";
23-
import * as testUtils from "../../test-utils";
2430
import { NotificationStateEvents } from "../../../src/stores/notifications/NotificationState";
31+
import { NotificationColor } from "../../../src/stores/notifications/NotificationColor";
32+
import { createMessageEventContent } from "../../test-utils/events";
2533

2634
describe("RoomNotificationState", () => {
27-
let testRoom: Room;
35+
let room: Room;
2836
let client: MatrixClient;
2937

3038
beforeEach(() => {
31-
stubClient();
32-
client = MatrixClientPeg.get();
33-
testRoom = testUtils.mkStubRoom("$aroomid", "Test room", client);
39+
client = stubClient();
40+
room = new Room("!room:example.com", client, "@user:example.org", {
41+
pendingEventOrdering: PendingEventOrdering.Detached,
42+
});
3443
});
3544

45+
function addThread(room: Room): void {
46+
const threadId = "thread_id";
47+
jest.spyOn(room, "eventShouldLiveIn").mockReturnValue({
48+
shouldLiveInRoom: true,
49+
shouldLiveInThread: true,
50+
threadId,
51+
});
52+
const thread = room.createThread(
53+
threadId,
54+
new MatrixEvent({
55+
room_id: room.roomId,
56+
event_id: "event_root_1",
57+
type: EventType.RoomMessage,
58+
sender: "userId",
59+
content: createMessageEventContent("RootEvent"),
60+
}),
61+
[],
62+
true,
63+
);
64+
for (let i = 0; i < 10; i++) {
65+
thread.addEvent(
66+
new MatrixEvent({
67+
room_id: room.roomId,
68+
event_id: "event_reply_1" + i,
69+
type: EventType.RoomMessage,
70+
sender: "userId",
71+
content: createMessageEventContent("ReplyEvent" + 1),
72+
}),
73+
false,
74+
);
75+
}
76+
}
77+
78+
function setUnreads(room: Room, greys: number, reds: number): void {
79+
room.setUnreadNotificationCount(NotificationCountType.Highlight, reds);
80+
room.setUnreadNotificationCount(NotificationCountType.Total, greys);
81+
}
82+
3683
it("Updates on event decryption", () => {
37-
const roomNotifState = new RoomNotificationState(testRoom as any as Room);
84+
const roomNotifState = new RoomNotificationState(room);
3885
const listener = jest.fn();
3986
roomNotifState.addListener(NotificationStateEvents.Update, listener);
4087
const testEvent = {
41-
getRoomId: () => testRoom.roomId,
88+
getRoomId: () => room.roomId,
4289
} as unknown as MatrixEvent;
43-
testRoom.getUnreadNotificationCount = jest.fn().mockReturnValue(1);
90+
room.getUnreadNotificationCount = jest.fn().mockReturnValue(1);
4491
client.emit(MatrixEventEvent.Decrypted, testEvent);
4592
expect(listener).toHaveBeenCalled();
4693
});
4794

4895
it("removes listeners", () => {
49-
const roomNotifState = new RoomNotificationState(testRoom as any as Room);
96+
const roomNotifState = new RoomNotificationState(room);
5097
expect(() => roomNotifState.destroy()).not.toThrow();
5198
});
99+
100+
it("suggests an 'unread' ! if there are unsent messages", () => {
101+
const roomNotifState = new RoomNotificationState(room);
102+
103+
const event = mkEvent({
104+
event: true,
105+
type: "m.message",
106+
user: "@user:example.org",
107+
content: {},
108+
});
109+
event.status = EventStatus.NOT_SENT;
110+
room.addPendingEvent(event, "txn");
111+
112+
expect(roomNotifState.color).toBe(NotificationColor.Unsent);
113+
expect(roomNotifState.symbol).toBe("!");
114+
expect(roomNotifState.count).toBeGreaterThan(0);
115+
});
116+
117+
it("suggests nothing if the room is muted", () => {
118+
const roomNotifState = new RoomNotificationState(room);
119+
120+
muteRoom(room);
121+
setUnreads(room, 1234, 0);
122+
room.updateMyMembership("join"); // emit
123+
124+
expect(roomNotifState.color).toBe(NotificationColor.None);
125+
expect(roomNotifState.symbol).toBe(null);
126+
expect(roomNotifState.count).toBe(0);
127+
});
128+
129+
it("suggests a red ! if the user has been invited to a room", () => {
130+
const roomNotifState = new RoomNotificationState(room);
131+
132+
room.updateMyMembership("invite"); // emit
133+
134+
expect(roomNotifState.color).toBe(NotificationColor.Red);
135+
expect(roomNotifState.symbol).toBe("!");
136+
expect(roomNotifState.count).toBeGreaterThan(0);
137+
});
138+
139+
it("returns a proper count and color for regular unreads", () => {
140+
const roomNotifState = new RoomNotificationState(room);
141+
142+
setUnreads(room, 4321, 0);
143+
room.updateMyMembership("join"); // emit
144+
145+
expect(roomNotifState.color).toBe(NotificationColor.Grey);
146+
expect(roomNotifState.symbol).toBe(null);
147+
expect(roomNotifState.count).toBe(4321);
148+
});
149+
150+
it("returns a proper count and color for highlights", () => {
151+
const roomNotifState = new RoomNotificationState(room);
152+
153+
setUnreads(room, 0, 69);
154+
room.updateMyMembership("join"); // emit
155+
156+
expect(roomNotifState.color).toBe(NotificationColor.Red);
157+
expect(roomNotifState.symbol).toBe(null);
158+
expect(roomNotifState.count).toBe(69);
159+
});
160+
161+
it("includes threads", async () => {
162+
const roomNotifState = new RoomNotificationState(room);
163+
164+
room.timeline.push(
165+
new MatrixEvent({
166+
room_id: room.roomId,
167+
type: EventType.RoomMessage,
168+
sender: "userId",
169+
content: createMessageEventContent("timeline event"),
170+
}),
171+
);
172+
173+
addThread(room);
174+
room.updateMyMembership("join"); // emit
175+
176+
expect(roomNotifState.color).toBe(NotificationColor.Bold);
177+
expect(roomNotifState.symbol).toBe(null);
178+
});
52179
});

0 commit comments

Comments
 (0)