mirror of
https://github.com/Novaloop-AG/healthchat-web.git
synced 2026-11-03 16:10:05 +00:00
Prepare for repo merge
Signed-off-by: Michael Telatynski <7t3chguy@gmail.com>
This commit is contained in:
@@ -0,0 +1,101 @@
|
||||
/*
|
||||
Copyright 2024 New Vector Ltd.
|
||||
Copyright 2022 The Matrix.org Foundation C.I.C.
|
||||
|
||||
SPDX-License-Identifier: AGPL-3.0-only OR GPL-3.0-only
|
||||
Please see LICENSE files in the repository root for full details.
|
||||
*/
|
||||
|
||||
import { mocked, MockedObject } from "jest-mock";
|
||||
import { PendingEventOrdering, Room, RoomStateEvent } from "matrix-js-sdk/src/matrix";
|
||||
import { KnownMembership } from "matrix-js-sdk/src/types";
|
||||
import { Widget } from "matrix-widget-api";
|
||||
|
||||
import type { MatrixClient } from "matrix-js-sdk/src/matrix";
|
||||
import type { ClientWidgetApi } from "matrix-widget-api";
|
||||
import {
|
||||
stubClient,
|
||||
setupAsyncStoreWithClient,
|
||||
useMockedCalls,
|
||||
MockedCall,
|
||||
useMockMediaDevices,
|
||||
} from "../../../test-utils";
|
||||
import { MatrixClientPeg } from "../../../../src/MatrixClientPeg";
|
||||
import DMRoomMap from "../../../../src/utils/DMRoomMap";
|
||||
import { DefaultTagID } from "../../../../src/stores/room-list/models";
|
||||
import { SortAlgorithm, ListAlgorithm } from "../../../../src/stores/room-list/algorithms/models";
|
||||
import "../../../../src/stores/room-list/RoomListStore"; // must be imported before Algorithm to avoid cycles
|
||||
import { Algorithm } from "../../../../src/stores/room-list/algorithms/Algorithm";
|
||||
import { CallStore } from "../../../../src/stores/CallStore";
|
||||
import { WidgetMessagingStore } from "../../../../src/stores/widgets/WidgetMessagingStore";
|
||||
|
||||
describe("Algorithm", () => {
|
||||
useMockedCalls();
|
||||
|
||||
let client: MockedObject<MatrixClient>;
|
||||
let algorithm: Algorithm;
|
||||
|
||||
beforeEach(() => {
|
||||
useMockMediaDevices();
|
||||
stubClient();
|
||||
client = mocked(MatrixClientPeg.safeGet());
|
||||
DMRoomMap.makeShared(client);
|
||||
|
||||
algorithm = new Algorithm();
|
||||
algorithm.start();
|
||||
algorithm.populateTags(
|
||||
{ [DefaultTagID.Untagged]: SortAlgorithm.Alphabetic },
|
||||
{ [DefaultTagID.Untagged]: ListAlgorithm.Natural },
|
||||
);
|
||||
});
|
||||
|
||||
afterEach(() => {
|
||||
algorithm.stop();
|
||||
});
|
||||
|
||||
it("sticks rooms with calls to the top when they're connected", async () => {
|
||||
const room = new Room("!1:example.org", client, "@alice:example.org", {
|
||||
pendingEventOrdering: PendingEventOrdering.Detached,
|
||||
});
|
||||
const roomWithCall = new Room("!2:example.org", client, "@alice:example.org", {
|
||||
pendingEventOrdering: PendingEventOrdering.Detached,
|
||||
});
|
||||
|
||||
client.getRoom.mockImplementation((roomId) => {
|
||||
switch (roomId) {
|
||||
case room.roomId:
|
||||
return room;
|
||||
case roomWithCall.roomId:
|
||||
return roomWithCall;
|
||||
default:
|
||||
return null;
|
||||
}
|
||||
});
|
||||
client.getRooms.mockReturnValue([room, roomWithCall]);
|
||||
client.reEmitter.reEmit(room, [RoomStateEvent.Events]);
|
||||
client.reEmitter.reEmit(roomWithCall, [RoomStateEvent.Events]);
|
||||
|
||||
for (const room of client.getRooms()) jest.spyOn(room, "getMyMembership").mockReturnValue(KnownMembership.Join);
|
||||
algorithm.setKnownRooms(client.getRooms());
|
||||
|
||||
setupAsyncStoreWithClient(CallStore.instance, client);
|
||||
setupAsyncStoreWithClient(WidgetMessagingStore.instance, client);
|
||||
|
||||
MockedCall.create(roomWithCall, "1");
|
||||
const call = CallStore.instance.getCall(roomWithCall.roomId);
|
||||
if (call === null) throw new Error("Failed to create call");
|
||||
|
||||
const widget = new Widget(call.widget);
|
||||
WidgetMessagingStore.instance.storeMessaging(widget, roomWithCall.roomId, {
|
||||
stop: () => {},
|
||||
} as unknown as ClientWidgetApi);
|
||||
|
||||
// End of setup
|
||||
|
||||
expect(algorithm.getOrderedRooms()[DefaultTagID.Untagged]).toEqual([room, roomWithCall]);
|
||||
await call.start();
|
||||
expect(algorithm.getOrderedRooms()[DefaultTagID.Untagged]).toEqual([roomWithCall, room]);
|
||||
await call.disconnect();
|
||||
expect(algorithm.getOrderedRooms()[DefaultTagID.Untagged]).toEqual([room, roomWithCall]);
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,177 @@
|
||||
/*
|
||||
Copyright 2024 New Vector Ltd.
|
||||
Copyright 2022 The Matrix.org Foundation C.I.C.
|
||||
|
||||
SPDX-License-Identifier: AGPL-3.0-only OR GPL-3.0-only
|
||||
Please see LICENSE files in the repository root for full details.
|
||||
*/
|
||||
|
||||
import { Room, MatrixClient } from "matrix-js-sdk/src/matrix";
|
||||
import { KnownMembership } from "matrix-js-sdk/src/types";
|
||||
|
||||
import { mkMessage, mkRoom, stubClient } from "../../../test-utils";
|
||||
import { MatrixClientPeg } from "../../../../src/MatrixClientPeg";
|
||||
import "../../../../src/stores/room-list/RoomListStore";
|
||||
import { RecentAlgorithm } from "../../../../src/stores/room-list/algorithms/tag-sorting/RecentAlgorithm";
|
||||
import { makeThreadEvent, mkThread } from "../../../test-utils/threads";
|
||||
import { DefaultTagID } from "../../../../src/stores/room-list/models";
|
||||
|
||||
describe("RecentAlgorithm", () => {
|
||||
let algorithm: RecentAlgorithm;
|
||||
let cli: MatrixClient;
|
||||
|
||||
beforeEach(() => {
|
||||
stubClient();
|
||||
cli = MatrixClientPeg.safeGet();
|
||||
algorithm = new RecentAlgorithm();
|
||||
});
|
||||
|
||||
describe("getLastTs", () => {
|
||||
it("returns the last ts", () => {
|
||||
const room = new Room("room123", cli, "@john:matrix.org");
|
||||
|
||||
const event1 = mkMessage({
|
||||
room: room.roomId,
|
||||
msg: "Hello world!",
|
||||
user: "@alice:matrix.org",
|
||||
ts: 5,
|
||||
event: true,
|
||||
});
|
||||
const event2 = mkMessage({
|
||||
room: room.roomId,
|
||||
msg: "Howdy!",
|
||||
user: "@bob:matrix.org",
|
||||
ts: 10,
|
||||
event: true,
|
||||
});
|
||||
|
||||
room.getMyMembership = () => KnownMembership.Join;
|
||||
|
||||
room.addLiveEvents([event1]);
|
||||
expect(algorithm.getLastTs(room, "@jane:matrix.org")).toBe(5);
|
||||
expect(algorithm.getLastTs(room, "@john:matrix.org")).toBe(5);
|
||||
|
||||
room.addLiveEvents([event2]);
|
||||
|
||||
expect(algorithm.getLastTs(room, "@jane:matrix.org")).toBe(10);
|
||||
expect(algorithm.getLastTs(room, "@john:matrix.org")).toBe(10);
|
||||
});
|
||||
|
||||
it("returns a fake ts for rooms without a timeline", () => {
|
||||
const room = mkRoom(cli, "!new:example.org");
|
||||
// @ts-ignore
|
||||
room.timeline = undefined;
|
||||
expect(algorithm.getLastTs(room, "@john:matrix.org")).toBe(Number.MAX_SAFE_INTEGER);
|
||||
});
|
||||
|
||||
it("works when not a member", () => {
|
||||
const room = mkRoom(cli, "!new:example.org");
|
||||
room.getMyMembership.mockReturnValue(KnownMembership.Invite);
|
||||
expect(algorithm.getLastTs(room, "@john:matrix.org")).toBe(Number.MAX_SAFE_INTEGER);
|
||||
});
|
||||
});
|
||||
|
||||
describe("sortRooms", () => {
|
||||
it("orders rooms per last message ts", () => {
|
||||
const room1 = new Room("room1", cli, "@bob:matrix.org");
|
||||
const room2 = new Room("room2", cli, "@bob:matrix.org");
|
||||
|
||||
room1.getMyMembership = () => KnownMembership.Join;
|
||||
room2.getMyMembership = () => KnownMembership.Join;
|
||||
|
||||
const evt = mkMessage({
|
||||
room: room1.roomId,
|
||||
msg: "Hello world!",
|
||||
user: "@alice:matrix.org",
|
||||
ts: 5,
|
||||
event: true,
|
||||
});
|
||||
const evt2 = mkMessage({
|
||||
room: room2.roomId,
|
||||
msg: "Hello world!",
|
||||
user: "@alice:matrix.org",
|
||||
ts: 2,
|
||||
event: true,
|
||||
});
|
||||
|
||||
room1.addLiveEvents([evt]);
|
||||
room2.addLiveEvents([evt2]);
|
||||
|
||||
expect(algorithm.sortRooms([room2, room1], DefaultTagID.Untagged)).toEqual([room1, room2]);
|
||||
});
|
||||
|
||||
it("orders rooms without messages first", () => {
|
||||
const room1 = new Room("room1", cli, "@bob:matrix.org");
|
||||
const room2 = new Room("room2", cli, "@bob:matrix.org");
|
||||
|
||||
room1.getMyMembership = () => KnownMembership.Join;
|
||||
room2.getMyMembership = () => KnownMembership.Join;
|
||||
|
||||
const evt = mkMessage({
|
||||
room: room1.roomId,
|
||||
msg: "Hello world!",
|
||||
user: "@alice:matrix.org",
|
||||
ts: 5,
|
||||
event: true,
|
||||
});
|
||||
|
||||
room1.addLiveEvents([evt]);
|
||||
|
||||
expect(algorithm.sortRooms([room2, room1], DefaultTagID.Untagged)).toEqual([room2, room1]);
|
||||
|
||||
const { events } = mkThread({
|
||||
room: room1,
|
||||
client: cli,
|
||||
authorId: "@bob:matrix.org",
|
||||
participantUserIds: ["@bob:matrix.org"],
|
||||
ts: 12,
|
||||
});
|
||||
|
||||
room1.addLiveEvents(events);
|
||||
});
|
||||
|
||||
it("orders rooms based on thread replies too", () => {
|
||||
const room1 = new Room("room1", cli, "@bob:matrix.org");
|
||||
const room2 = new Room("room2", cli, "@bob:matrix.org");
|
||||
|
||||
room1.getMyMembership = () => KnownMembership.Join;
|
||||
room2.getMyMembership = () => KnownMembership.Join;
|
||||
|
||||
const { rootEvent, events: events1 } = mkThread({
|
||||
room: room1,
|
||||
client: cli,
|
||||
authorId: "@bob:matrix.org",
|
||||
participantUserIds: ["@bob:matrix.org"],
|
||||
ts: 12,
|
||||
length: 5,
|
||||
});
|
||||
room1.addLiveEvents(events1);
|
||||
|
||||
const { events: events2 } = mkThread({
|
||||
room: room2,
|
||||
client: cli,
|
||||
authorId: "@bob:matrix.org",
|
||||
participantUserIds: ["@bob:matrix.org"],
|
||||
ts: 14,
|
||||
length: 10,
|
||||
});
|
||||
room2.addLiveEvents(events2);
|
||||
|
||||
expect(algorithm.sortRooms([room1, room2], DefaultTagID.Untagged)).toEqual([room2, room1]);
|
||||
|
||||
const threadReply = makeThreadEvent({
|
||||
user: "@bob:matrix.org",
|
||||
room: room1.roomId,
|
||||
event: true,
|
||||
msg: `hello world`,
|
||||
rootEventId: rootEvent.getId()!,
|
||||
replyToEventId: rootEvent.getId()!,
|
||||
// replies are 1ms after each other
|
||||
ts: 50,
|
||||
});
|
||||
room1.addLiveEvents([threadReply]);
|
||||
|
||||
expect(algorithm.sortRooms([room1, room2], DefaultTagID.Untagged)).toEqual([room1, room2]);
|
||||
});
|
||||
});
|
||||
});
|
||||
+425
@@ -0,0 +1,425 @@
|
||||
/*
|
||||
Copyright 2024 New Vector Ltd.
|
||||
Copyright 2023 The Matrix.org Foundation C.I.C.
|
||||
|
||||
SPDX-License-Identifier: AGPL-3.0-only OR GPL-3.0-only
|
||||
Please see LICENSE files in the repository root for full details.
|
||||
*/
|
||||
|
||||
import { ConditionKind, MatrixEvent, PushRuleActionName, Room, RoomEvent } from "matrix-js-sdk/src/matrix";
|
||||
import { logger } from "matrix-js-sdk/src/logger";
|
||||
|
||||
import { RoomNotificationStateStore } from "../../../../../src/stores/notifications/RoomNotificationStateStore";
|
||||
import { ImportanceAlgorithm } from "../../../../../src/stores/room-list/algorithms/list-ordering/ImportanceAlgorithm";
|
||||
import { SortAlgorithm } from "../../../../../src/stores/room-list/algorithms/models";
|
||||
import * as RoomNotifs from "../../../../../src/RoomNotifs";
|
||||
import { DefaultTagID, RoomUpdateCause } from "../../../../../src/stores/room-list/models";
|
||||
import { NotificationLevel } from "../../../../../src/stores/notifications/NotificationLevel";
|
||||
import { AlphabeticAlgorithm } from "../../../../../src/stores/room-list/algorithms/tag-sorting/AlphabeticAlgorithm";
|
||||
import { getMockClientWithEventEmitter, mockClientMethodsUser } from "../../../../test-utils";
|
||||
import { RecentAlgorithm } from "../../../../../src/stores/room-list/algorithms/tag-sorting/RecentAlgorithm";
|
||||
import { DEFAULT_PUSH_RULES, makePushRule } from "../../../../test-utils/pushRules";
|
||||
|
||||
describe("ImportanceAlgorithm", () => {
|
||||
const userId = "@alice:server.org";
|
||||
const tagId = DefaultTagID.Favourite;
|
||||
|
||||
const makeRoom = (id: string, name: string, order?: number): Room => {
|
||||
const room = new Room(id, client, userId);
|
||||
room.name = name;
|
||||
const tagEvent = new MatrixEvent({
|
||||
type: "m.tag",
|
||||
content: {
|
||||
tags: {
|
||||
[tagId]: {
|
||||
order,
|
||||
},
|
||||
},
|
||||
},
|
||||
});
|
||||
room.addTags(tagEvent);
|
||||
return room;
|
||||
};
|
||||
|
||||
const client = getMockClientWithEventEmitter({
|
||||
...mockClientMethodsUser(userId),
|
||||
});
|
||||
const roomA = makeRoom("!aaa:server.org", "Alpha", 2);
|
||||
const roomB = makeRoom("!bbb:server.org", "Bravo", 5);
|
||||
const roomC = makeRoom("!ccc:server.org", "Charlie", 1);
|
||||
const roomD = makeRoom("!ddd:server.org", "Delta", 4);
|
||||
const roomE = makeRoom("!eee:server.org", "Echo", 3);
|
||||
const roomX = makeRoom("!xxx:server.org", "Xylophone", 99);
|
||||
|
||||
const muteRoomARule = makePushRule(roomA.roomId, {
|
||||
actions: [PushRuleActionName.DontNotify],
|
||||
conditions: [{ kind: ConditionKind.EventMatch, key: "room_id", pattern: roomA.roomId }],
|
||||
});
|
||||
const muteRoomBRule = makePushRule(roomB.roomId, {
|
||||
actions: [PushRuleActionName.DontNotify],
|
||||
conditions: [{ kind: ConditionKind.EventMatch, key: "room_id", pattern: roomB.roomId }],
|
||||
});
|
||||
client.pushRules = {
|
||||
global: {
|
||||
...DEFAULT_PUSH_RULES.global,
|
||||
override: [...DEFAULT_PUSH_RULES.global.override!, muteRoomARule, muteRoomBRule],
|
||||
},
|
||||
};
|
||||
|
||||
const unreadStates: Record<string, ReturnType<(typeof RoomNotifs)["determineUnreadState"]>> = {
|
||||
red: { symbol: null, count: 1, level: NotificationLevel.Highlight },
|
||||
grey: { symbol: null, count: 1, level: NotificationLevel.Notification },
|
||||
none: { symbol: null, count: 0, level: NotificationLevel.None },
|
||||
};
|
||||
|
||||
beforeEach(() => {
|
||||
jest.spyOn(RoomNotifs, "determineUnreadState").mockReturnValue({
|
||||
symbol: null,
|
||||
count: 0,
|
||||
level: NotificationLevel.None,
|
||||
});
|
||||
});
|
||||
|
||||
const setupAlgorithm = (sortAlgorithm: SortAlgorithm, rooms?: Room[]) => {
|
||||
const algorithm = new ImportanceAlgorithm(tagId, sortAlgorithm);
|
||||
algorithm.setRooms(rooms || [roomA, roomB, roomC]);
|
||||
return algorithm;
|
||||
};
|
||||
|
||||
describe("When sortAlgorithm is manual", () => {
|
||||
const sortAlgorithm = SortAlgorithm.Manual;
|
||||
it("orders rooms by tag order without categorizing", () => {
|
||||
jest.spyOn(RoomNotificationStateStore.instance, "getRoomState");
|
||||
const algorithm = setupAlgorithm(sortAlgorithm);
|
||||
|
||||
// didn't check notif state
|
||||
expect(RoomNotificationStateStore.instance.getRoomState).not.toHaveBeenCalled();
|
||||
// sorted according to room tag order
|
||||
expect(algorithm.orderedRooms).toEqual([roomC, roomA, roomB]);
|
||||
});
|
||||
|
||||
describe("handleRoomUpdate", () => {
|
||||
// XXX: This doesn't work because manual ordered rooms dont get categoryindices
|
||||
// possibly related https://github.com/vector-im/element-web/issues/25099
|
||||
it.skip("removes a room", () => {
|
||||
const algorithm = setupAlgorithm(sortAlgorithm);
|
||||
|
||||
const shouldTriggerUpdate = algorithm.handleRoomUpdate(roomA, RoomUpdateCause.RoomRemoved);
|
||||
|
||||
expect(shouldTriggerUpdate).toBe(true);
|
||||
expect(algorithm.orderedRooms).toEqual([roomC, roomB]);
|
||||
});
|
||||
|
||||
// XXX: This doesn't work because manual ordered rooms dont get categoryindices
|
||||
it.skip("adds a new room", () => {
|
||||
const algorithm = setupAlgorithm(sortAlgorithm);
|
||||
|
||||
const shouldTriggerUpdate = algorithm.handleRoomUpdate(roomD, RoomUpdateCause.NewRoom);
|
||||
|
||||
expect(shouldTriggerUpdate).toBe(true);
|
||||
expect(algorithm.orderedRooms).toEqual([roomC, roomB, roomD, roomE]);
|
||||
});
|
||||
|
||||
it("does nothing and returns false for a timeline update", () => {
|
||||
const algorithm = setupAlgorithm(sortAlgorithm);
|
||||
|
||||
const beforeRooms = algorithm.orderedRooms;
|
||||
const shouldTriggerUpdate = algorithm.handleRoomUpdate(roomA, RoomUpdateCause.Timeline);
|
||||
|
||||
expect(shouldTriggerUpdate).toBe(false);
|
||||
// strict equal
|
||||
expect(algorithm.orderedRooms).toBe(beforeRooms);
|
||||
});
|
||||
|
||||
it("does nothing and returns false for a read receipt update", () => {
|
||||
const algorithm = setupAlgorithm(sortAlgorithm);
|
||||
|
||||
const beforeRooms = algorithm.orderedRooms;
|
||||
const shouldTriggerUpdate = algorithm.handleRoomUpdate(roomA, RoomUpdateCause.ReadReceipt);
|
||||
|
||||
expect(shouldTriggerUpdate).toBe(false);
|
||||
// strict equal
|
||||
expect(algorithm.orderedRooms).toBe(beforeRooms);
|
||||
});
|
||||
|
||||
it("throws for an unhandle update cause", () => {
|
||||
const algorithm = setupAlgorithm(sortAlgorithm);
|
||||
|
||||
expect(() =>
|
||||
algorithm.handleRoomUpdate(roomA, "something unexpected" as unknown as RoomUpdateCause),
|
||||
).toThrow("Unsupported update cause: something unexpected");
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe("When sortAlgorithm is alphabetical", () => {
|
||||
const sortAlgorithm = SortAlgorithm.Alphabetic;
|
||||
|
||||
beforeEach(async () => {
|
||||
// destroy roomMap so we can start fresh
|
||||
// @ts-ignore private property
|
||||
RoomNotificationStateStore.instance.roomMap = new Map<Room, RoomNotificationState>();
|
||||
|
||||
jest.spyOn(AlphabeticAlgorithm.prototype, "sortRooms").mockClear();
|
||||
jest.spyOn(RoomNotifs, "determineUnreadState")
|
||||
.mockClear()
|
||||
.mockImplementation((room) => {
|
||||
switch (room) {
|
||||
// b and e have red notifs
|
||||
case roomB:
|
||||
case roomE:
|
||||
return unreadStates.red;
|
||||
// c is grey
|
||||
case roomC:
|
||||
return unreadStates.grey;
|
||||
default:
|
||||
return unreadStates.none;
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
it("orders rooms by alpha when they have the same notif state", () => {
|
||||
jest.spyOn(RoomNotifs, "determineUnreadState").mockReturnValue({
|
||||
symbol: null,
|
||||
count: 0,
|
||||
level: NotificationLevel.None,
|
||||
});
|
||||
const algorithm = setupAlgorithm(sortAlgorithm);
|
||||
|
||||
// sorted according to alpha
|
||||
expect(algorithm.orderedRooms).toEqual([roomA, roomB, roomC]);
|
||||
});
|
||||
|
||||
it("orders rooms by notification state then alpha", () => {
|
||||
const algorithm = setupAlgorithm(sortAlgorithm, [roomC, roomB, roomE, roomD, roomA]);
|
||||
|
||||
expect(algorithm.orderedRooms).toEqual([
|
||||
// alpha within red
|
||||
roomB,
|
||||
roomE,
|
||||
// grey
|
||||
roomC,
|
||||
// alpha within none
|
||||
roomA,
|
||||
roomD,
|
||||
]);
|
||||
});
|
||||
|
||||
describe("handleRoomUpdate", () => {
|
||||
it("removes a room", () => {
|
||||
const algorithm = setupAlgorithm(sortAlgorithm);
|
||||
jest.spyOn(AlphabeticAlgorithm.prototype, "sortRooms").mockClear();
|
||||
|
||||
const shouldTriggerUpdate = algorithm.handleRoomUpdate(roomA, RoomUpdateCause.RoomRemoved);
|
||||
|
||||
expect(shouldTriggerUpdate).toBe(true);
|
||||
expect(algorithm.orderedRooms).toEqual([roomB, roomC]);
|
||||
// no re-sorting on a remove
|
||||
expect(AlphabeticAlgorithm.prototype.sortRooms).not.toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it("warns and returns without change when removing a room that is not indexed", () => {
|
||||
jest.spyOn(logger, "warn").mockReturnValue(undefined);
|
||||
const algorithm = setupAlgorithm(sortAlgorithm);
|
||||
|
||||
const shouldTriggerUpdate = algorithm.handleRoomUpdate(roomD, RoomUpdateCause.RoomRemoved);
|
||||
|
||||
expect(shouldTriggerUpdate).toBe(false);
|
||||
expect(logger.warn).toHaveBeenCalledWith(`Tried to remove unknown room from ${tagId}: ${roomD.roomId}`);
|
||||
});
|
||||
|
||||
it("adds a new room", () => {
|
||||
const algorithm = setupAlgorithm(sortAlgorithm);
|
||||
jest.spyOn(AlphabeticAlgorithm.prototype, "sortRooms").mockClear();
|
||||
|
||||
const shouldTriggerUpdate = algorithm.handleRoomUpdate(roomE, RoomUpdateCause.NewRoom);
|
||||
|
||||
expect(shouldTriggerUpdate).toBe(true);
|
||||
// inserted according to notif state
|
||||
expect(algorithm.orderedRooms).toEqual([roomB, roomE, roomC, roomA]);
|
||||
// only sorted within category
|
||||
expect(AlphabeticAlgorithm.prototype.sortRooms).toHaveBeenCalledWith([roomB, roomE], tagId);
|
||||
});
|
||||
|
||||
it("throws for an unhandled update cause", () => {
|
||||
const algorithm = setupAlgorithm(sortAlgorithm);
|
||||
|
||||
expect(() =>
|
||||
algorithm.handleRoomUpdate(roomA, "something unexpected" as unknown as RoomUpdateCause),
|
||||
).toThrow("Unsupported update cause: something unexpected");
|
||||
});
|
||||
|
||||
it("ignores a mute change", () => {
|
||||
// muted rooms are not pushed to the bottom when sort is alpha
|
||||
const algorithm = setupAlgorithm(sortAlgorithm, [roomC, roomB, roomE, roomD, roomA]);
|
||||
jest.spyOn(AlphabeticAlgorithm.prototype, "sortRooms").mockClear();
|
||||
|
||||
const shouldTriggerUpdate = algorithm.handleRoomUpdate(roomE, RoomUpdateCause.PossibleMuteChange);
|
||||
|
||||
expect(shouldTriggerUpdate).toBe(false);
|
||||
// no sorting
|
||||
expect(AlphabeticAlgorithm.prototype.sortRooms).not.toHaveBeenCalled();
|
||||
});
|
||||
|
||||
describe("time and read receipt updates", () => {
|
||||
it("throws for when a room is not indexed", () => {
|
||||
const algorithm = setupAlgorithm(sortAlgorithm, [roomC, roomB, roomE, roomD, roomA]);
|
||||
|
||||
expect(() => algorithm.handleRoomUpdate(roomX, RoomUpdateCause.Timeline)).toThrow(
|
||||
`Room ${roomX.roomId} has no index in ${tagId}`,
|
||||
);
|
||||
});
|
||||
|
||||
it("re-sorts category when updated room has not changed category", () => {
|
||||
const algorithm = setupAlgorithm(sortAlgorithm, [roomC, roomB, roomE, roomD, roomA]);
|
||||
jest.spyOn(AlphabeticAlgorithm.prototype, "sortRooms").mockClear();
|
||||
|
||||
const shouldTriggerUpdate = algorithm.handleRoomUpdate(roomE, RoomUpdateCause.Timeline);
|
||||
|
||||
expect(shouldTriggerUpdate).toBe(true);
|
||||
expect(algorithm.orderedRooms).toEqual([roomB, roomE, roomC, roomA, roomD]);
|
||||
// only sorted within category
|
||||
expect(AlphabeticAlgorithm.prototype.sortRooms).toHaveBeenCalledTimes(1);
|
||||
expect(AlphabeticAlgorithm.prototype.sortRooms).toHaveBeenCalledWith([roomB, roomE], tagId);
|
||||
});
|
||||
|
||||
it("re-sorts category when updated room has changed category", () => {
|
||||
const algorithm = setupAlgorithm(sortAlgorithm, [roomC, roomB, roomE, roomD, roomA]);
|
||||
jest.spyOn(AlphabeticAlgorithm.prototype, "sortRooms").mockClear();
|
||||
|
||||
// change roomE to unreadState.none
|
||||
jest.spyOn(RoomNotifs, "determineUnreadState").mockImplementation((room) => {
|
||||
switch (room) {
|
||||
// b and e have red notifs
|
||||
case roomB:
|
||||
return unreadStates.red;
|
||||
// c is grey
|
||||
case roomC:
|
||||
return unreadStates.grey;
|
||||
case roomE:
|
||||
default:
|
||||
return unreadStates.none;
|
||||
}
|
||||
});
|
||||
// @ts-ignore don't bother mocking rest of emit properties
|
||||
roomE.emit(RoomEvent.Timeline, new MatrixEvent({ type: "whatever", room_id: roomE.roomId }));
|
||||
|
||||
const shouldTriggerUpdate = algorithm.handleRoomUpdate(roomE, RoomUpdateCause.Timeline);
|
||||
|
||||
expect(shouldTriggerUpdate).toBe(true);
|
||||
expect(algorithm.orderedRooms).toEqual([roomB, roomC, roomA, roomD, roomE]);
|
||||
|
||||
// only sorted within roomE's new category
|
||||
expect(AlphabeticAlgorithm.prototype.sortRooms).toHaveBeenCalledTimes(1);
|
||||
expect(AlphabeticAlgorithm.prototype.sortRooms).toHaveBeenCalledWith([roomA, roomD, roomE], tagId);
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe("When sortAlgorithm is recent", () => {
|
||||
const sortAlgorithm = SortAlgorithm.Recent;
|
||||
|
||||
// mock recent algorithm sorting
|
||||
const fakeRecentOrder = [roomC, roomB, roomE, roomD, roomA];
|
||||
|
||||
beforeEach(async () => {
|
||||
// destroy roomMap so we can start fresh
|
||||
// @ts-ignore private property
|
||||
RoomNotificationStateStore.instance.roomMap = new Map<Room, RoomNotificationState>();
|
||||
|
||||
jest.spyOn(RecentAlgorithm.prototype, "sortRooms")
|
||||
.mockClear()
|
||||
.mockImplementation((rooms: Room[]) =>
|
||||
fakeRecentOrder.filter((sortedRoom) => rooms.includes(sortedRoom)),
|
||||
);
|
||||
jest.spyOn(RoomNotifs, "determineUnreadState")
|
||||
.mockClear()
|
||||
.mockImplementation((room) => {
|
||||
switch (room) {
|
||||
// b, c and e have red notifs
|
||||
case roomB:
|
||||
case roomE:
|
||||
case roomC:
|
||||
return unreadStates.red;
|
||||
default:
|
||||
return unreadStates.none;
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
it("orders rooms by recent when they have the same notif state", () => {
|
||||
jest.spyOn(RoomNotifs, "determineUnreadState").mockReturnValue({
|
||||
symbol: null,
|
||||
count: 0,
|
||||
level: NotificationLevel.None,
|
||||
});
|
||||
const algorithm = setupAlgorithm(sortAlgorithm);
|
||||
|
||||
// sorted according to recent
|
||||
expect(algorithm.orderedRooms).toEqual([roomC, roomB, roomA]);
|
||||
});
|
||||
|
||||
it("orders rooms by notification state then recent", () => {
|
||||
const algorithm = setupAlgorithm(sortAlgorithm, [roomC, roomB, roomE, roomD, roomA]);
|
||||
|
||||
expect(algorithm.orderedRooms).toEqual([
|
||||
// recent within red
|
||||
roomC,
|
||||
roomE,
|
||||
// recent within none
|
||||
roomD,
|
||||
// muted
|
||||
roomB,
|
||||
roomA,
|
||||
]);
|
||||
});
|
||||
|
||||
describe("handleRoomUpdate", () => {
|
||||
it("removes a room", () => {
|
||||
const algorithm = setupAlgorithm(sortAlgorithm);
|
||||
jest.spyOn(RecentAlgorithm.prototype, "sortRooms").mockClear();
|
||||
|
||||
const shouldTriggerUpdate = algorithm.handleRoomUpdate(roomA, RoomUpdateCause.RoomRemoved);
|
||||
|
||||
expect(shouldTriggerUpdate).toBe(true);
|
||||
expect(algorithm.orderedRooms).toEqual([roomC, roomB]);
|
||||
// no re-sorting on a remove
|
||||
expect(RecentAlgorithm.prototype.sortRooms).not.toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it("warns and returns without change when removing a room that is not indexed", () => {
|
||||
jest.spyOn(logger, "warn").mockReturnValue(undefined);
|
||||
const algorithm = setupAlgorithm(sortAlgorithm);
|
||||
|
||||
const shouldTriggerUpdate = algorithm.handleRoomUpdate(roomD, RoomUpdateCause.RoomRemoved);
|
||||
|
||||
expect(shouldTriggerUpdate).toBe(false);
|
||||
expect(logger.warn).toHaveBeenCalledWith(`Tried to remove unknown room from ${tagId}: ${roomD.roomId}`);
|
||||
});
|
||||
|
||||
it("adds a new room", () => {
|
||||
const algorithm = setupAlgorithm(sortAlgorithm);
|
||||
jest.spyOn(RecentAlgorithm.prototype, "sortRooms").mockClear();
|
||||
|
||||
const shouldTriggerUpdate = algorithm.handleRoomUpdate(roomE, RoomUpdateCause.NewRoom);
|
||||
|
||||
expect(shouldTriggerUpdate).toBe(true);
|
||||
// inserted according to notif state and mute
|
||||
expect(algorithm.orderedRooms).toEqual([roomC, roomE, roomB, roomA]);
|
||||
// only sorted within category
|
||||
expect(RecentAlgorithm.prototype.sortRooms).toHaveBeenCalledWith([roomE, roomC], tagId);
|
||||
});
|
||||
|
||||
it("re-sorts on a mute change", () => {
|
||||
const algorithm = setupAlgorithm(sortAlgorithm, [roomC, roomB, roomE, roomD, roomA]);
|
||||
jest.spyOn(RecentAlgorithm.prototype, "sortRooms").mockClear();
|
||||
|
||||
const shouldTriggerUpdate = algorithm.handleRoomUpdate(roomE, RoomUpdateCause.PossibleMuteChange);
|
||||
|
||||
expect(shouldTriggerUpdate).toBe(true);
|
||||
expect(RecentAlgorithm.prototype.sortRooms).toHaveBeenCalledWith([roomC, roomE], tagId);
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,280 @@
|
||||
/*
|
||||
Copyright 2024 New Vector Ltd.
|
||||
Copyright 2023 The Matrix.org Foundation C.I.C.
|
||||
|
||||
SPDX-License-Identifier: AGPL-3.0-only OR GPL-3.0-only
|
||||
Please see LICENSE files in the repository root for full details.
|
||||
*/
|
||||
|
||||
import { ConditionKind, EventType, MatrixEvent, PushRuleActionName, Room, ClientEvent } from "matrix-js-sdk/src/matrix";
|
||||
import { logger } from "matrix-js-sdk/src/logger";
|
||||
|
||||
import { NaturalAlgorithm } from "../../../../../src/stores/room-list/algorithms/list-ordering/NaturalAlgorithm";
|
||||
import { SortAlgorithm } from "../../../../../src/stores/room-list/algorithms/models";
|
||||
import { DefaultTagID, RoomUpdateCause } from "../../../../../src/stores/room-list/models";
|
||||
import { AlphabeticAlgorithm } from "../../../../../src/stores/room-list/algorithms/tag-sorting/AlphabeticAlgorithm";
|
||||
import { RecentAlgorithm } from "../../../../../src/stores/room-list/algorithms/tag-sorting/RecentAlgorithm";
|
||||
import { RoomNotificationStateStore } from "../../../../../src/stores/notifications/RoomNotificationStateStore";
|
||||
import * as RoomNotifs from "../../../../../src/RoomNotifs";
|
||||
import { getMockClientWithEventEmitter, mockClientMethodsUser } from "../../../../test-utils";
|
||||
import { DEFAULT_PUSH_RULES, makePushRule } from "../../../../test-utils/pushRules";
|
||||
import { NotificationLevel } from "../../../../../src/stores/notifications/NotificationLevel";
|
||||
|
||||
describe("NaturalAlgorithm", () => {
|
||||
const userId = "@alice:server.org";
|
||||
const tagId = DefaultTagID.Favourite;
|
||||
|
||||
const makeRoom = (id: string, name: string): Room => {
|
||||
const room = new Room(id, client, userId);
|
||||
room.name = name;
|
||||
return room;
|
||||
};
|
||||
|
||||
const client = getMockClientWithEventEmitter({
|
||||
...mockClientMethodsUser(userId),
|
||||
});
|
||||
const roomA = makeRoom("!aaa:server.org", "Alpha");
|
||||
const roomB = makeRoom("!bbb:server.org", "Bravo");
|
||||
const roomC = makeRoom("!ccc:server.org", "Charlie");
|
||||
const roomD = makeRoom("!ddd:server.org", "Delta");
|
||||
const roomE = makeRoom("!eee:server.org", "Echo");
|
||||
const roomX = makeRoom("!xxx:server.org", "Xylophone");
|
||||
|
||||
const muteRoomARule = makePushRule(roomA.roomId, {
|
||||
actions: [PushRuleActionName.DontNotify],
|
||||
conditions: [{ kind: ConditionKind.EventMatch, key: "room_id", pattern: roomA.roomId }],
|
||||
});
|
||||
const muteRoomDRule = makePushRule(roomD.roomId, {
|
||||
actions: [PushRuleActionName.DontNotify],
|
||||
conditions: [{ kind: ConditionKind.EventMatch, key: "room_id", pattern: roomD.roomId }],
|
||||
});
|
||||
client.pushRules = {
|
||||
global: {
|
||||
...DEFAULT_PUSH_RULES.global,
|
||||
override: [...DEFAULT_PUSH_RULES.global!.override!, muteRoomARule, muteRoomDRule],
|
||||
},
|
||||
};
|
||||
|
||||
const setupAlgorithm = (sortAlgorithm: SortAlgorithm, rooms?: Room[]) => {
|
||||
const algorithm = new NaturalAlgorithm(tagId, sortAlgorithm);
|
||||
algorithm.setRooms(rooms || [roomA, roomB, roomC]);
|
||||
return algorithm;
|
||||
};
|
||||
|
||||
describe("When sortAlgorithm is alphabetical", () => {
|
||||
const sortAlgorithm = SortAlgorithm.Alphabetic;
|
||||
|
||||
beforeEach(async () => {
|
||||
jest.spyOn(AlphabeticAlgorithm.prototype, "sortRooms").mockClear();
|
||||
});
|
||||
|
||||
it("orders rooms by alpha", () => {
|
||||
const algorithm = setupAlgorithm(sortAlgorithm);
|
||||
|
||||
// sorted according to alpha
|
||||
expect(algorithm.orderedRooms).toEqual([roomA, roomB, roomC]);
|
||||
});
|
||||
|
||||
describe("handleRoomUpdate", () => {
|
||||
it("removes a room", () => {
|
||||
const algorithm = setupAlgorithm(sortAlgorithm);
|
||||
jest.spyOn(AlphabeticAlgorithm.prototype, "sortRooms").mockClear();
|
||||
|
||||
const shouldTriggerUpdate = algorithm.handleRoomUpdate(roomA, RoomUpdateCause.RoomRemoved);
|
||||
|
||||
expect(shouldTriggerUpdate).toBe(true);
|
||||
expect(algorithm.orderedRooms).toEqual([roomB, roomC]);
|
||||
});
|
||||
|
||||
it("warns when removing a room that is not indexed", () => {
|
||||
jest.spyOn(logger, "warn").mockReturnValue(undefined);
|
||||
const algorithm = setupAlgorithm(sortAlgorithm);
|
||||
|
||||
const shouldTriggerUpdate = algorithm.handleRoomUpdate(roomD, RoomUpdateCause.RoomRemoved);
|
||||
|
||||
expect(shouldTriggerUpdate).toBe(false);
|
||||
expect(logger.warn).toHaveBeenCalledWith(`Tried to remove unknown room from ${tagId}: ${roomD.roomId}`);
|
||||
});
|
||||
|
||||
it("adds a new room", () => {
|
||||
const algorithm = setupAlgorithm(sortAlgorithm);
|
||||
jest.spyOn(AlphabeticAlgorithm.prototype, "sortRooms").mockClear();
|
||||
|
||||
const shouldTriggerUpdate = algorithm.handleRoomUpdate(roomE, RoomUpdateCause.NewRoom);
|
||||
|
||||
expect(shouldTriggerUpdate).toBe(true);
|
||||
expect(algorithm.orderedRooms).toEqual([roomA, roomB, roomC, roomE]);
|
||||
// only sorted within category
|
||||
expect(AlphabeticAlgorithm.prototype.sortRooms).toHaveBeenCalledWith(
|
||||
[roomA, roomB, roomC, roomE],
|
||||
tagId,
|
||||
);
|
||||
});
|
||||
|
||||
it("adds a new muted room", () => {
|
||||
const algorithm = setupAlgorithm(sortAlgorithm, [roomA, roomB, roomE]);
|
||||
jest.spyOn(AlphabeticAlgorithm.prototype, "sortRooms").mockClear();
|
||||
|
||||
const shouldTriggerUpdate = algorithm.handleRoomUpdate(roomD, RoomUpdateCause.NewRoom);
|
||||
|
||||
expect(shouldTriggerUpdate).toBe(true);
|
||||
// muted room mixed in main category
|
||||
expect(algorithm.orderedRooms).toEqual([roomA, roomB, roomD, roomE]);
|
||||
// only sorted within category
|
||||
expect(AlphabeticAlgorithm.prototype.sortRooms).toHaveBeenCalledTimes(1);
|
||||
});
|
||||
|
||||
it("ignores a mute change update", () => {
|
||||
const algorithm = setupAlgorithm(sortAlgorithm);
|
||||
jest.spyOn(AlphabeticAlgorithm.prototype, "sortRooms").mockClear();
|
||||
|
||||
const shouldTriggerUpdate = algorithm.handleRoomUpdate(roomA, RoomUpdateCause.PossibleMuteChange);
|
||||
|
||||
expect(shouldTriggerUpdate).toBe(false);
|
||||
expect(AlphabeticAlgorithm.prototype.sortRooms).not.toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it("throws for an unhandled update cause", () => {
|
||||
const algorithm = setupAlgorithm(sortAlgorithm);
|
||||
|
||||
expect(() =>
|
||||
algorithm.handleRoomUpdate(roomA, "something unexpected" as unknown as RoomUpdateCause),
|
||||
).toThrow("Unsupported update cause: something unexpected");
|
||||
});
|
||||
|
||||
describe("time and read receipt updates", () => {
|
||||
it("handles when a room is not indexed", () => {
|
||||
const algorithm = setupAlgorithm(sortAlgorithm);
|
||||
|
||||
const shouldTriggerUpdate = algorithm.handleRoomUpdate(roomX, RoomUpdateCause.Timeline);
|
||||
|
||||
// for better or worse natural alg sets this to true
|
||||
expect(shouldTriggerUpdate).toBe(true);
|
||||
expect(algorithm.orderedRooms).toEqual([roomA, roomB, roomC]);
|
||||
});
|
||||
|
||||
it("re-sorts rooms when timeline updates", () => {
|
||||
const algorithm = setupAlgorithm(sortAlgorithm);
|
||||
jest.spyOn(AlphabeticAlgorithm.prototype, "sortRooms").mockClear();
|
||||
|
||||
const shouldTriggerUpdate = algorithm.handleRoomUpdate(roomA, RoomUpdateCause.Timeline);
|
||||
|
||||
expect(shouldTriggerUpdate).toBe(true);
|
||||
expect(algorithm.orderedRooms).toEqual([roomA, roomB, roomC]);
|
||||
// only sorted within category
|
||||
expect(AlphabeticAlgorithm.prototype.sortRooms).toHaveBeenCalledTimes(1);
|
||||
expect(AlphabeticAlgorithm.prototype.sortRooms).toHaveBeenCalledWith([roomA, roomB, roomC], tagId);
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe("When sortAlgorithm is recent", () => {
|
||||
const sortAlgorithm = SortAlgorithm.Recent;
|
||||
|
||||
// mock recent algorithm sorting
|
||||
const fakeRecentOrder = [roomC, roomA, roomB, roomD, roomE];
|
||||
|
||||
beforeEach(async () => {
|
||||
// destroy roomMap so we can start fresh
|
||||
// @ts-ignore private property
|
||||
RoomNotificationStateStore.instance.roomMap = new Map<Room, RoomNotificationState>();
|
||||
|
||||
jest.spyOn(RecentAlgorithm.prototype, "sortRooms")
|
||||
.mockClear()
|
||||
.mockImplementation((rooms: Room[]) =>
|
||||
fakeRecentOrder.filter((sortedRoom) => rooms.includes(sortedRoom)),
|
||||
);
|
||||
|
||||
jest.spyOn(RoomNotifs, "determineUnreadState").mockReturnValue({
|
||||
symbol: null,
|
||||
count: 0,
|
||||
level: NotificationLevel.None,
|
||||
});
|
||||
});
|
||||
|
||||
it("orders rooms by recent with muted rooms to the bottom", () => {
|
||||
const algorithm = setupAlgorithm(sortAlgorithm);
|
||||
|
||||
// sorted according to recent
|
||||
expect(algorithm.orderedRooms).toEqual([roomC, roomB, roomA]);
|
||||
});
|
||||
|
||||
describe("handleRoomUpdate", () => {
|
||||
it("removes a room", () => {
|
||||
const algorithm = setupAlgorithm(sortAlgorithm);
|
||||
jest.spyOn(RecentAlgorithm.prototype, "sortRooms").mockClear();
|
||||
|
||||
const shouldTriggerUpdate = algorithm.handleRoomUpdate(roomA, RoomUpdateCause.RoomRemoved);
|
||||
|
||||
expect(shouldTriggerUpdate).toBe(true);
|
||||
expect(algorithm.orderedRooms).toEqual([roomC, roomB]);
|
||||
// no re-sorting on a remove
|
||||
expect(RecentAlgorithm.prototype.sortRooms).not.toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it("warns and returns without change when removing a room that is not indexed", () => {
|
||||
jest.spyOn(logger, "warn").mockReturnValue(undefined);
|
||||
const algorithm = setupAlgorithm(sortAlgorithm);
|
||||
|
||||
const shouldTriggerUpdate = algorithm.handleRoomUpdate(roomD, RoomUpdateCause.RoomRemoved);
|
||||
|
||||
expect(shouldTriggerUpdate).toBe(false);
|
||||
expect(logger.warn).toHaveBeenCalledWith(`Tried to remove unknown room from ${tagId}: ${roomD.roomId}`);
|
||||
});
|
||||
|
||||
it("adds a new room", () => {
|
||||
const algorithm = setupAlgorithm(sortAlgorithm);
|
||||
jest.spyOn(RecentAlgorithm.prototype, "sortRooms").mockClear();
|
||||
|
||||
const shouldTriggerUpdate = algorithm.handleRoomUpdate(roomE, RoomUpdateCause.NewRoom);
|
||||
|
||||
expect(shouldTriggerUpdate).toBe(true);
|
||||
// inserted according to mute then recentness
|
||||
expect(algorithm.orderedRooms).toEqual([roomC, roomB, roomE, roomA]);
|
||||
// only sorted within category, muted roomA is not resorted
|
||||
expect(RecentAlgorithm.prototype.sortRooms).toHaveBeenCalledWith([roomC, roomB, roomE], tagId);
|
||||
});
|
||||
|
||||
it("does not re-sort on possible mute change when room did not change effective mutedness", () => {
|
||||
const algorithm = setupAlgorithm(sortAlgorithm, [roomC, roomB, roomE, roomD, roomA]);
|
||||
jest.spyOn(RecentAlgorithm.prototype, "sortRooms").mockClear();
|
||||
|
||||
const shouldTriggerUpdate = algorithm.handleRoomUpdate(roomE, RoomUpdateCause.PossibleMuteChange);
|
||||
|
||||
expect(shouldTriggerUpdate).toBe(false);
|
||||
expect(RecentAlgorithm.prototype.sortRooms).not.toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it("re-sorts on a mute change", () => {
|
||||
const algorithm = setupAlgorithm(sortAlgorithm, [roomC, roomB, roomE, roomD, roomA]);
|
||||
jest.spyOn(RecentAlgorithm.prototype, "sortRooms").mockClear();
|
||||
|
||||
// mute roomE
|
||||
const muteRoomERule = makePushRule(roomE.roomId, {
|
||||
actions: [PushRuleActionName.DontNotify],
|
||||
conditions: [{ kind: ConditionKind.EventMatch, key: "room_id", pattern: roomE.roomId }],
|
||||
});
|
||||
const pushRulesEvent = new MatrixEvent({ type: EventType.PushRules });
|
||||
client.pushRules!.global!.override!.push(muteRoomERule);
|
||||
client.emit(ClientEvent.AccountData, pushRulesEvent);
|
||||
|
||||
const shouldTriggerUpdate = algorithm.handleRoomUpdate(roomE, RoomUpdateCause.PossibleMuteChange);
|
||||
|
||||
expect(shouldTriggerUpdate).toBe(true);
|
||||
expect(algorithm.orderedRooms).toEqual([
|
||||
// unmuted, sorted by recent
|
||||
roomC,
|
||||
roomB,
|
||||
// muted, sorted by recent
|
||||
roomA,
|
||||
roomD,
|
||||
roomE,
|
||||
]);
|
||||
// only sorted muted category
|
||||
expect(RecentAlgorithm.prototype.sortRooms).toHaveBeenCalledTimes(1);
|
||||
expect(RecentAlgorithm.prototype.sortRooms).toHaveBeenCalledWith([roomA, roomD, roomE], tagId);
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
Reference in New Issue
Block a user