238 lines
		
	
	
		
			8.3 KiB
		
	
	
	
		
			TypeScript
		
	
	
			
		
		
	
	
			238 lines
		
	
	
		
			8.3 KiB
		
	
	
	
		
			TypeScript
		
	
	
/*
 | 
						|
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 } from "jest-mock";
 | 
						|
import { EventType, IContent, MatrixClient, MatrixEvent, Room } from "matrix-js-sdk/src/matrix";
 | 
						|
import { KnownMembership } from "matrix-js-sdk/src/types";
 | 
						|
 | 
						|
import SdkConfig from "../../../src/SdkConfig";
 | 
						|
import SettingsStore from "../../../src/settings/SettingsStore";
 | 
						|
import { MemberListStore } from "../../../src/stores/MemberListStore";
 | 
						|
import { stubClient } from "../../test-utils";
 | 
						|
import { TestSdkContext } from "../TestSdkContext";
 | 
						|
 | 
						|
describe("MemberListStore", () => {
 | 
						|
    const alice = "@alice:bar";
 | 
						|
    const bob = "@bob:bar";
 | 
						|
    const charlie = "@charlie:bar";
 | 
						|
    const roomId = "!foo:bar";
 | 
						|
    let store: MemberListStore;
 | 
						|
    let client: MatrixClient;
 | 
						|
    let room: Room;
 | 
						|
 | 
						|
    beforeEach(() => {
 | 
						|
        const context = new TestSdkContext();
 | 
						|
        client = stubClient();
 | 
						|
        client.baseUrl = "https://invalid.base.url.here";
 | 
						|
        context.client = client;
 | 
						|
        store = new MemberListStore(context);
 | 
						|
        // alice is joined to the room.
 | 
						|
        room = new Room(roomId, client, client.getUserId()!);
 | 
						|
        room.currentState.setStateEvents([
 | 
						|
            new MatrixEvent({
 | 
						|
                type: EventType.RoomCreate,
 | 
						|
                state_key: "",
 | 
						|
                content: {
 | 
						|
                    creator: alice,
 | 
						|
                },
 | 
						|
                sender: alice,
 | 
						|
                room_id: roomId,
 | 
						|
                event_id: "$1",
 | 
						|
            }),
 | 
						|
            new MatrixEvent({
 | 
						|
                type: EventType.RoomMember,
 | 
						|
                state_key: alice,
 | 
						|
                content: {
 | 
						|
                    membership: KnownMembership.Join,
 | 
						|
                },
 | 
						|
                sender: alice,
 | 
						|
                room_id: roomId,
 | 
						|
                event_id: "$2",
 | 
						|
            }),
 | 
						|
        ]);
 | 
						|
        room.recalculate();
 | 
						|
        mocked(client.getRoom).mockImplementation((r: string): Room | null => {
 | 
						|
            if (r === roomId) {
 | 
						|
                return room;
 | 
						|
            }
 | 
						|
            return null;
 | 
						|
        });
 | 
						|
        SdkConfig.put({
 | 
						|
            enable_presence_by_hs_url: {
 | 
						|
                [client.baseUrl]: false,
 | 
						|
            },
 | 
						|
        });
 | 
						|
    });
 | 
						|
 | 
						|
    it("loads members in a room", async () => {
 | 
						|
        addMember(room, bob, KnownMembership.Invite);
 | 
						|
        addMember(room, charlie, KnownMembership.Leave);
 | 
						|
 | 
						|
        const { invited, joined } = await store.loadMemberList(roomId);
 | 
						|
        expect(invited).toEqual([room.getMember(bob)]);
 | 
						|
        expect(joined).toEqual([room.getMember(alice)]);
 | 
						|
    });
 | 
						|
 | 
						|
    it("fails gracefully for invalid rooms", async () => {
 | 
						|
        const { invited, joined } = await store.loadMemberList("!idontexist:bar");
 | 
						|
        expect(invited).toEqual([]);
 | 
						|
        expect(joined).toEqual([]);
 | 
						|
    });
 | 
						|
 | 
						|
    it("sorts by power level", async () => {
 | 
						|
        addMember(room, bob, KnownMembership.Join);
 | 
						|
        addMember(room, charlie, KnownMembership.Join);
 | 
						|
        setPowerLevels(room, {
 | 
						|
            users: {
 | 
						|
                [alice]: 100,
 | 
						|
                [charlie]: 50,
 | 
						|
            },
 | 
						|
            users_default: 10,
 | 
						|
        });
 | 
						|
 | 
						|
        const { invited, joined } = await store.loadMemberList(roomId);
 | 
						|
        expect(invited).toEqual([]);
 | 
						|
        expect(joined).toEqual([room.getMember(alice), room.getMember(charlie), room.getMember(bob)]);
 | 
						|
    });
 | 
						|
 | 
						|
    it("sorts by name if power level is equal", async () => {
 | 
						|
        const doris = "@doris:bar";
 | 
						|
        addMember(room, bob, KnownMembership.Join);
 | 
						|
        addMember(room, charlie, KnownMembership.Join);
 | 
						|
        setPowerLevels(room, {
 | 
						|
            users_default: 10,
 | 
						|
        });
 | 
						|
 | 
						|
        let { invited, joined } = await store.loadMemberList(roomId);
 | 
						|
        expect(invited).toEqual([]);
 | 
						|
        expect(joined).toEqual([room.getMember(alice), room.getMember(bob), room.getMember(charlie)]);
 | 
						|
 | 
						|
        // Ensure it sorts by display name if they are set
 | 
						|
        addMember(room, doris, KnownMembership.Join, "AAAAA");
 | 
						|
        ({ invited, joined } = await store.loadMemberList(roomId));
 | 
						|
        expect(invited).toEqual([]);
 | 
						|
        expect(joined).toEqual([
 | 
						|
            room.getMember(doris),
 | 
						|
            room.getMember(alice),
 | 
						|
            room.getMember(bob),
 | 
						|
            room.getMember(charlie),
 | 
						|
        ]);
 | 
						|
    });
 | 
						|
 | 
						|
    it("filters based on a search query", async () => {
 | 
						|
        const mice = "@mice:bar";
 | 
						|
        const zorro = "@zorro:bar";
 | 
						|
        addMember(room, bob, KnownMembership.Join);
 | 
						|
        addMember(room, mice, KnownMembership.Join);
 | 
						|
 | 
						|
        let { invited, joined } = await store.loadMemberList(roomId, "ice");
 | 
						|
        expect(invited).toEqual([]);
 | 
						|
        expect(joined).toEqual([room.getMember(alice), room.getMember(mice)]);
 | 
						|
 | 
						|
        // Ensure it filters by display name if they are set
 | 
						|
        addMember(room, zorro, KnownMembership.Join, "ice ice baby");
 | 
						|
        ({ invited, joined } = await store.loadMemberList(roomId, "ice"));
 | 
						|
        expect(invited).toEqual([]);
 | 
						|
        expect(joined).toEqual([room.getMember(alice), room.getMember(zorro), room.getMember(mice)]);
 | 
						|
    });
 | 
						|
 | 
						|
    describe("lazy loading", () => {
 | 
						|
        beforeEach(() => {
 | 
						|
            mocked(client.hasLazyLoadMembersEnabled).mockReturnValue(true);
 | 
						|
            room.loadMembersIfNeeded = jest.fn();
 | 
						|
            mocked(room.loadMembersIfNeeded).mockResolvedValue(true);
 | 
						|
        });
 | 
						|
 | 
						|
        it("calls Room.loadMembersIfNeeded once when enabled", async () => {
 | 
						|
            let { joined } = await store.loadMemberList(roomId);
 | 
						|
            expect(joined).toEqual([room.getMember(alice)]);
 | 
						|
            expect(room.loadMembersIfNeeded).toHaveBeenCalledTimes(1);
 | 
						|
            ({ joined } = await store.loadMemberList(roomId));
 | 
						|
            expect(joined).toEqual([room.getMember(alice)]);
 | 
						|
            expect(room.loadMembersIfNeeded).toHaveBeenCalledTimes(1);
 | 
						|
        });
 | 
						|
    });
 | 
						|
 | 
						|
    describe("sliding sync", () => {
 | 
						|
        beforeEach(() => {
 | 
						|
            jest.spyOn(SettingsStore, "getValue").mockImplementation((settingName, roomId, value) => {
 | 
						|
                return settingName === "feature_sliding_sync"; // this is enabled, everything else is disabled.
 | 
						|
            });
 | 
						|
            client.members = jest.fn();
 | 
						|
        });
 | 
						|
 | 
						|
        it("calls /members when lazy loading", async () => {
 | 
						|
            mocked(client.members).mockResolvedValue({
 | 
						|
                chunk: [
 | 
						|
                    {
 | 
						|
                        type: EventType.RoomMember,
 | 
						|
                        state_key: bob,
 | 
						|
                        content: {
 | 
						|
                            membership: KnownMembership.Join,
 | 
						|
                            displayname: "Bob",
 | 
						|
                        },
 | 
						|
                        sender: bob,
 | 
						|
                        room_id: room.roomId,
 | 
						|
                        event_id: "$" + Math.random(),
 | 
						|
                        origin_server_ts: 2,
 | 
						|
                    },
 | 
						|
                ],
 | 
						|
            });
 | 
						|
            const { joined } = await store.loadMemberList(roomId);
 | 
						|
            expect(joined).toEqual([room.getMember(alice), room.getMember(bob)]);
 | 
						|
            expect(client.members).toHaveBeenCalled();
 | 
						|
        });
 | 
						|
 | 
						|
        it("does not use lazy loading on encrypted rooms", async () => {
 | 
						|
            client.isRoomEncrypted = jest.fn();
 | 
						|
            mocked(client.isRoomEncrypted).mockReturnValue(true);
 | 
						|
 | 
						|
            const { joined } = await store.loadMemberList(roomId);
 | 
						|
            expect(joined).toEqual([room.getMember(alice)]);
 | 
						|
            expect(client.members).not.toHaveBeenCalled();
 | 
						|
        });
 | 
						|
    });
 | 
						|
});
 | 
						|
 | 
						|
function addEventToRoom(room: Room, ev: MatrixEvent) {
 | 
						|
    room.getLiveTimeline().addEvent(ev, {
 | 
						|
        toStartOfTimeline: false,
 | 
						|
    });
 | 
						|
}
 | 
						|
 | 
						|
function setPowerLevels(room: Room, pl: IContent) {
 | 
						|
    addEventToRoom(
 | 
						|
        room,
 | 
						|
        new MatrixEvent({
 | 
						|
            type: EventType.RoomPowerLevels,
 | 
						|
            state_key: "",
 | 
						|
            content: pl,
 | 
						|
            sender: room.getCreator()!,
 | 
						|
            room_id: room.roomId,
 | 
						|
            event_id: "$" + Math.random(),
 | 
						|
        }),
 | 
						|
    );
 | 
						|
}
 | 
						|
 | 
						|
function addMember(room: Room, userId: string, membership: string, displayName?: string) {
 | 
						|
    addEventToRoom(
 | 
						|
        room,
 | 
						|
        new MatrixEvent({
 | 
						|
            type: EventType.RoomMember,
 | 
						|
            state_key: userId,
 | 
						|
            content: {
 | 
						|
                membership: membership,
 | 
						|
                displayname: displayName,
 | 
						|
            },
 | 
						|
            sender: userId,
 | 
						|
            room_id: room.roomId,
 | 
						|
            event_id: "$" + Math.random(),
 | 
						|
        }),
 | 
						|
    );
 | 
						|
}
 |