Skip to content

Commit dd09d3d

Browse files
committed
Add tests
Signed-off-by: Timo K <[email protected]>
1 parent 0588777 commit dd09d3d

File tree

3 files changed

+185
-41
lines changed

3 files changed

+185
-41
lines changed

src/toasts/IncomingCallToast.tsx

Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@ import VideoCallIcon from "@vector-im/compound-design-tokens/assets/web/icons/vi
1313
import CheckIcon from "@vector-im/compound-design-tokens/assets/web/icons/check";
1414
import CrossIcon from "@vector-im/compound-design-tokens/assets/web/icons/close";
1515
import { logger } from "matrix-js-sdk/src/logger";
16-
import { type IRTCDeclineContent, type IRTCNotificationContent } from "matrix-js-sdk/src/matrixrtc";
16+
import { type IRTCNotificationContent } from "matrix-js-sdk/src/matrixrtc";
1717

1818
import { _t } from "../languageHandler";
1919
import RoomAvatar from "../components/views/avatars/RoomAvatar";
@@ -165,14 +165,12 @@ export function IncomingCallToast({ notificationEvent }: Props): JSX.Element {
165165
// Dismiss if session got declined remotely.
166166
const onTimelineChange = useCallback(
167167
(ev: MatrixEvent) => {
168-
const content = ev.getContent() as Partial<IRTCDeclineContent>;
169168
const userId = room?.client.getUserId();
170169
if (
171170
ev.getType() === EventType.RTCDecline &&
172171
userId !== undefined &&
173172
ev.getSender() === userId && // It is our decline not someone elses
174-
content["m.relates_to"] !== undefined &&
175-
content["m.relates_to"].event_id === notificationEvent.getId() // The event declines this ringing toast.
173+
ev.relationEventId === notificationEvent.getId() // The event declines this ringing toast.
176174
) {
177175
dismissToast();
178176
}

test/unit-tests/Notifier-test.ts

Lines changed: 36 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -384,33 +384,49 @@ describe("Notifier", () => {
384384
jest.resetAllMocks();
385385
});
386386

387-
const emitCallNotifyEvent = (type?: string, roomMention = true) => {
388-
const callEvent = mkEvent({
389-
type: type ?? EventType.CallNotify,
387+
const emitCallNotificationEvent = (
388+
params: {
389+
type?: string;
390+
roomMention?: boolean;
391+
lifetime?: number;
392+
ts?: number;
393+
} = {},
394+
) => {
395+
const { type, roomMention, lifetime, ts } = {
396+
type: EventType.RTCNotification,
397+
roomMention: true,
398+
lifetime: 30000,
399+
ts: Date.now(),
400+
...params,
401+
};
402+
const notificationEvent = mkEvent({
403+
type: type,
390404
user: "@alice:foo",
391405
room: roomId,
406+
ts,
392407
content: {
393-
"application": "m.call",
408+
"notification_type": "ring",
409+
"m.relation": { rel_type: "m.reference", event_id: "$memberEventId" },
394410
"m.mentions": { user_ids: [], room: roomMention },
395-
"notify_type": "ring",
396-
"call_id": "abc123",
411+
lifetime,
412+
"sender_ts": ts,
397413
},
398414
event: true,
399415
});
400-
emitLiveEvent(callEvent);
401-
return callEvent;
416+
emitLiveEvent(notificationEvent);
417+
return notificationEvent;
402418
};
403419

404420
it("shows group call toast", () => {
405-
const notifyEvent = emitCallNotifyEvent();
421+
const notificationEvent = emitCallNotificationEvent();
406422

407423
expect(ToastStore.sharedInstance().addOrReplaceToast).toHaveBeenCalledWith(
408424
expect.objectContaining({
409-
key: getIncomingCallToastKey(notifyEvent.getContent().call_id ?? "", roomId),
425+
key: getIncomingCallToastKey(notificationEvent.getId() ?? "", roomId),
410426
priority: 100,
411427
component: IncomingCallToast,
412428
bodyClassName: "mx_IncomingCallToast",
413-
props: { notifyEvent },
429+
props: { notificationEvent },
414430
}),
415431
);
416432
});
@@ -438,13 +454,19 @@ describe("Notifier", () => {
438454
const roomSession = MatrixRTCSession.roomSessionForRoom(mockClient, testRoom);
439455

440456
mockClient.matrixRTC.getRoomSession.mockReturnValue(roomSession);
441-
emitCallNotifyEvent();
457+
emitCallNotificationEvent();
442458
expect(ToastStore.sharedInstance().addOrReplaceToast).not.toHaveBeenCalled();
443459
spyCallMemberships.mockRestore();
444460
});
445461

446-
it("should not show toast when calling with non-group call event", () => {
447-
emitCallNotifyEvent("event_type");
462+
it("should not show toast when calling with a different event type to org.matrix.msc4075.rtc.notification", () => {
463+
emitCallNotificationEvent({ type: "event_type" });
464+
465+
expect(ToastStore.sharedInstance().addOrReplaceToast).not.toHaveBeenCalled();
466+
});
467+
468+
it("should not show notification event is expired", () => {
469+
emitCallNotificationEvent({ ts: Date.now() - 40000 });
448470

449471
expect(ToastStore.sharedInstance().addOrReplaceToast).not.toHaveBeenCalled();
450472
});

test/unit-tests/toasts/IncomingCallToast-test.tsx

Lines changed: 147 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -8,17 +8,20 @@ Please see LICENSE files in the repository root for full details.
88

99
import React from "react";
1010
import { render, screen, cleanup, fireEvent, waitFor } from "jest-matrix-react";
11-
import { mocked, type Mocked } from "jest-mock";
11+
import { type Mock, mocked, type Mocked } from "jest-mock";
1212
import {
1313
Room,
1414
RoomStateEvent,
1515
type MatrixEvent,
1616
MatrixEventEvent,
1717
type MatrixClient,
1818
type RoomMember,
19+
EventType,
20+
RoomEvent,
21+
type IRoomTimelineData,
1922
} from "matrix-js-sdk/src/matrix";
2023
import { type ClientWidgetApi, Widget } from "matrix-widget-api";
21-
import { type ICallNotifyContent } from "matrix-js-sdk/src/matrixrtc";
24+
import { type IRTCNotificationContent } from "matrix-js-sdk/src/matrixrtc";
2225

2326
import {
2427
useMockedCalls,
@@ -27,6 +30,7 @@ import {
2730
mkRoomMember,
2831
setupAsyncStoreWithClient,
2932
resetAsyncStoreWithClient,
33+
mkEvent,
3034
} from "../../test-utils";
3135
import defaultDispatcher from "../../../src/dispatcher/dispatcher";
3236
import { Action } from "../../../src/dispatcher/actions";
@@ -43,7 +47,8 @@ describe("IncomingCallToast", () => {
4347

4448
let client: Mocked<MatrixClient>;
4549
let room: Room;
46-
let notifyContent: ICallNotifyContent;
50+
let notificationEvent: MatrixEvent;
51+
4752
let alice: RoomMember;
4853
let bob: RoomMember;
4954
let call: MockedCall;
@@ -64,10 +69,23 @@ describe("IncomingCallToast", () => {
6469
document.body.appendChild(audio);
6570

6671
room = new Room("!1:example.org", client, "@alice:example.org");
67-
notifyContent = {
68-
call_id: "",
69-
getRoomId: () => room.roomId,
70-
} as unknown as ICallNotifyContent;
72+
const ts = Date.now();
73+
const notificationContent = {
74+
"notification_type": "notification",
75+
"m.relation": { rel_type: "m.reference", event_id: "$memberEventId" },
76+
"m.mentions": { user_ids: [], room: true },
77+
"lifetime": 3000,
78+
"sender_ts": ts,
79+
} as unknown as IRTCNotificationContent;
80+
notificationEvent = mkEvent({
81+
type: EventType.RTCNotification,
82+
user: "@userId:matrix.org",
83+
content: notificationContent,
84+
room: room.roomId,
85+
ts,
86+
id: "$notificationEventId",
87+
event: true,
88+
});
7189
alice = mkRoomMember(room.roomId, "@alice:example.org");
7290
bob = mkRoomMember(room.roomId, "@bob:example.org");
7391

@@ -104,8 +122,12 @@ describe("IncomingCallToast", () => {
104122
});
105123

106124
const renderToast = () => {
107-
call.event.getContent = () => notifyContent as any;
108-
render(<IncomingCallToast notificationEvent={call.event} />);
125+
call.event.getContent = () =>
126+
({
127+
call_id: "",
128+
getRoomId: () => room.roomId,
129+
}) as any;
130+
render(<IncomingCallToast notificationEvent={notificationEvent} />);
109131
};
110132

111133
it("correctly shows all the information", () => {
@@ -124,14 +146,13 @@ describe("IncomingCallToast", () => {
124146
});
125147

126148
it("start ringing on ring notify event", () => {
127-
call.event.getContent = () =>
128-
({
129-
...notifyContent,
130-
notify_type: "ring",
131-
}) as any;
149+
const oldContent = notificationEvent.getContent() as IRTCNotificationContent;
150+
(notificationEvent as unknown as { getContent: () => IRTCNotificationContent }).getContent = () => {
151+
return { ...oldContent, notification_type: "ring" } as IRTCNotificationContent;
152+
};
132153

133154
const playMock = jest.spyOn(LegacyCallHandler.instance, "play");
134-
render(<IncomingCallToast notificationEvent={call.event} />);
155+
render(<IncomingCallToast notificationEvent={notificationEvent} />);
135156
expect(playMock).toHaveBeenCalled();
136157
});
137158

@@ -143,16 +164,18 @@ describe("IncomingCallToast", () => {
143164
screen.getByText("Video");
144165

145166
screen.getByRole("button", { name: "Join" });
167+
screen.getByRole("button", { name: "Decline" });
146168
screen.getByRole("button", { name: "Close" });
147169
});
148170

149-
it("joins the call and closes the toast", async () => {
171+
it("opens the lobby and closes the toast when pressing on the toast", async () => {
150172
renderToast();
151173

152174
const dispatcherSpy = jest.fn();
153175
const dispatcherRef = defaultDispatcher.register(dispatcherSpy);
154176

155-
fireEvent.click(screen.getByRole("button", { name: "Join" }));
177+
// click on the avatar (which is the example used for pressing on any area other than the buttons)
178+
fireEvent.click(screen.getByRole("presentation", { name: "" }));
156179
await waitFor(() =>
157180
expect(dispatcherSpy).toHaveBeenCalledWith({
158181
action: Action.ViewRoom,
@@ -163,12 +186,38 @@ describe("IncomingCallToast", () => {
163186
);
164187
await waitFor(() =>
165188
expect(toastStore.dismissToast).toHaveBeenCalledWith(
166-
getIncomingCallToastKey(notifyContent.call_id, room.roomId),
189+
getIncomingCallToastKey(notificationEvent.getId()!, room.roomId),
190+
),
191+
);
192+
193+
defaultDispatcher.unregister(dispatcherRef);
194+
});
195+
196+
it("opens the call directly and closes the toast when pressing on the join button", async () => {
197+
renderToast();
198+
199+
const dispatcherSpy = jest.fn();
200+
const dispatcherRef = defaultDispatcher.register(dispatcherSpy);
201+
202+
// click on the avatar (which is the example used for pressing on any area other than the buttons)
203+
fireEvent.click(screen.getByRole("button", { name: "Join" }));
204+
await waitFor(() =>
205+
expect(dispatcherSpy).toHaveBeenCalledWith({
206+
action: Action.ViewRoom,
207+
room_id: room.roomId,
208+
skipLobby: true,
209+
view_call: true,
210+
}),
211+
);
212+
await waitFor(() =>
213+
expect(toastStore.dismissToast).toHaveBeenCalledWith(
214+
getIncomingCallToastKey(notificationEvent.getId()!, room.roomId),
167215
),
168216
);
169217

170218
defaultDispatcher.unregister(dispatcherRef);
171219
});
220+
172221
it("Dismiss toast if user starts call and skips lobby when using shift key click", async () => {
173222
renderToast();
174223

@@ -186,7 +235,7 @@ describe("IncomingCallToast", () => {
186235
);
187236
await waitFor(() =>
188237
expect(toastStore.dismissToast).toHaveBeenCalledWith(
189-
getIncomingCallToastKey(notifyContent.call_id, room.roomId),
238+
getIncomingCallToastKey(notificationEvent.getId()!, room.roomId),
190239
),
191240
);
192241

@@ -202,7 +251,7 @@ describe("IncomingCallToast", () => {
202251
fireEvent.click(screen.getByRole("button", { name: "Close" }));
203252
await waitFor(() =>
204253
expect(toastStore.dismissToast).toHaveBeenCalledWith(
205-
getIncomingCallToastKey(notifyContent.call_id, room.roomId),
254+
getIncomingCallToastKey(notificationEvent.getId()!, room.roomId),
206255
),
207256
);
208257

@@ -220,7 +269,7 @@ describe("IncomingCallToast", () => {
220269

221270
await waitFor(() =>
222271
expect(toastStore.dismissToast).toHaveBeenCalledWith(
223-
getIncomingCallToastKey(notifyContent.call_id, room.roomId),
272+
getIncomingCallToastKey(notificationEvent.getId()!, room.roomId),
224273
),
225274
);
226275
});
@@ -233,7 +282,7 @@ describe("IncomingCallToast", () => {
233282

234283
await waitFor(() =>
235284
expect(toastStore.dismissToast).toHaveBeenCalledWith(
236-
getIncomingCallToastKey(notifyContent.call_id, room.roomId),
285+
getIncomingCallToastKey(notificationEvent.getId()!, room.roomId),
237286
),
238287
);
239288
});
@@ -244,7 +293,82 @@ describe("IncomingCallToast", () => {
244293

245294
await waitFor(() =>
246295
expect(toastStore.dismissToast).toHaveBeenCalledWith(
247-
getIncomingCallToastKey(notifyContent.call_id, room.roomId),
296+
getIncomingCallToastKey(notificationEvent.getId()!, room.roomId),
297+
),
298+
);
299+
});
300+
301+
it("closes toast when a decline event was received", async () => {
302+
(toastStore.dismissToast as Mock).mockReset();
303+
renderToast();
304+
305+
room.emit(
306+
RoomEvent.Timeline,
307+
mkEvent({
308+
user: "@userId:matrix.org",
309+
type: EventType.RTCDecline,
310+
content: { "m.relates_to": { event_id: notificationEvent.getId()!, rel_type: "m.reference" } },
311+
event: true,
312+
}),
313+
room,
314+
undefined,
315+
false,
316+
{} as unknown as IRoomTimelineData,
317+
);
318+
319+
await waitFor(() =>
320+
expect(toastStore.dismissToast).toHaveBeenCalledWith(
321+
getIncomingCallToastKey(notificationEvent.getId()!, room.roomId),
322+
),
323+
);
324+
});
325+
326+
it("does not close toast when a decline event for another user was received", async () => {
327+
(toastStore.dismissToast as Mock).mockReset();
328+
renderToast();
329+
330+
room.emit(
331+
RoomEvent.Timeline,
332+
mkEvent({
333+
user: "@userIdNotMe:matrix.org",
334+
type: EventType.RTCDecline,
335+
content: { "m.relates_to": { event_id: notificationEvent.getId()!, rel_type: "m.reference" } },
336+
event: true,
337+
}),
338+
room,
339+
undefined,
340+
false,
341+
{} as unknown as IRoomTimelineData,
342+
);
343+
344+
await waitFor(() =>
345+
expect(toastStore.dismissToast).not.toHaveBeenCalledWith(
346+
getIncomingCallToastKey(notificationEvent.getId()!, room.roomId),
347+
),
348+
);
349+
});
350+
351+
it("does not close toast when a decline event for another notification Event was received", async () => {
352+
(toastStore.dismissToast as Mock).mockReset();
353+
renderToast();
354+
355+
room.emit(
356+
RoomEvent.Timeline,
357+
mkEvent({
358+
user: "@userId:matrix.org",
359+
type: EventType.RTCDecline,
360+
content: { "m.relates_to": { event_id: "$otherNotificationEventRelation", rel_type: "m.reference" } },
361+
event: true,
362+
}),
363+
room,
364+
undefined,
365+
false,
366+
{} as unknown as IRoomTimelineData,
367+
);
368+
369+
await waitFor(() =>
370+
expect(toastStore.dismissToast).not.toHaveBeenCalledWith(
371+
getIncomingCallToastKey(notificationEvent.getId()!, room.roomId),
248372
),
249373
);
250374
});

0 commit comments

Comments
 (0)