Add tests for usePresence
and move the slightly weird test about it working when no member is linked onto the hook test which is what it's actually trying to test.
This commit is contained in:
@@ -6,17 +6,20 @@ SPDX-License-Identifier: AGPL-3.0-only OR GPL-3.0-only OR LicenseRef-Element-Com
|
|||||||
Please see LICENSE files in the repository root for full details.
|
Please see LICENSE files in the repository root for full details.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import { render, waitFor } from "jest-matrix-react";
|
import { act, render, renderHook, waitFor } from "jest-matrix-react";
|
||||||
import { mocked } from "jest-mock";
|
import { mocked } from "jest-mock";
|
||||||
import { type MatrixClient, PendingEventOrdering, Room, RoomMember, User } from "matrix-js-sdk/src/matrix";
|
import { type MatrixClient, PendingEventOrdering, Room, RoomMember, User, UserEvent } from "matrix-js-sdk/src/matrix";
|
||||||
import React from "react";
|
import React from "react";
|
||||||
import userEvent from "@testing-library/user-event";
|
|
||||||
|
|
||||||
import { MatrixClientPeg } from "../../../../../src/MatrixClientPeg";
|
import { MatrixClientPeg } from "../../../../../src/MatrixClientPeg";
|
||||||
import { stubClient } from "../../../../test-utils";
|
import { getMockClientWithEventEmitter, stubClient } from "../../../../test-utils";
|
||||||
import DMRoomMap from "../../../../../src/utils/DMRoomMap";
|
import DMRoomMap from "../../../../../src/utils/DMRoomMap";
|
||||||
import WithPresenceIndicator from "../../../../../src/components/views/avatars/WithPresenceIndicator";
|
import WithPresenceIndicator, {
|
||||||
|
Presence,
|
||||||
|
usePresence,
|
||||||
|
} from "../../../../../src/components/views/avatars/WithPresenceIndicator";
|
||||||
import { isPresenceEnabled } from "../../../../../src/utils/presence";
|
import { isPresenceEnabled } from "../../../../../src/utils/presence";
|
||||||
|
import { getJoinedNonFunctionalMembers } from "../../../../../src/utils/room/getJoinedNonFunctionalMembers";
|
||||||
|
|
||||||
jest.mock("../../../../../src/utils/presence");
|
jest.mock("../../../../../src/utils/presence");
|
||||||
|
|
||||||
@@ -87,51 +90,136 @@ describe("WithPresenceIndicator", () => {
|
|||||||
|
|
||||||
expect(asFragment()).toMatchSnapshot();
|
expect(asFragment()).toMatchSnapshot();
|
||||||
});
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe("usePresence", () => {
|
||||||
|
const ROOM_ID = "roomId";
|
||||||
|
const DM_USER_ID = "@bob:foo.bar";
|
||||||
|
|
||||||
|
let mockClient: ReturnType<typeof getMockClientWithEventEmitter>;
|
||||||
|
let room: Room;
|
||||||
|
let member: RoomMember;
|
||||||
|
let user: User;
|
||||||
|
|
||||||
|
beforeEach(() => {
|
||||||
|
mockClient = getMockClientWithEventEmitter({
|
||||||
|
getUserId: jest.fn().mockReturnValue("@alice:foo.bar"),
|
||||||
|
getUser: jest.fn().mockReturnValue(null),
|
||||||
|
store: { getPendingEvents: jest.fn().mockResolvedValue([]) },
|
||||||
|
});
|
||||||
|
room = new Room(ROOM_ID, mockClient as unknown as MatrixClient, mockClient.getUserId() ?? "");
|
||||||
|
|
||||||
|
mocked(isPresenceEnabled).mockReturnValue(true);
|
||||||
|
mocked(getJoinedNonFunctionalMembers).mockReturnValue([1, 2] as any);
|
||||||
|
|
||||||
|
user = new User(DM_USER_ID);
|
||||||
|
user.presence = "online";
|
||||||
|
member = new RoomMember(ROOM_ID, DM_USER_ID);
|
||||||
|
member.user = user;
|
||||||
|
});
|
||||||
|
|
||||||
|
afterEach(() => {
|
||||||
|
jest.restoreAllMocks();
|
||||||
|
});
|
||||||
|
|
||||||
|
it("returns null when presence is disabled", () => {
|
||||||
|
mocked(isPresenceEnabled).mockReturnValue(false);
|
||||||
|
const { result } = renderHook(() => usePresence(room, member));
|
||||||
|
expect(result.current).toBeNull();
|
||||||
|
});
|
||||||
|
|
||||||
|
it("returns null when room does not have exactly 2 members", () => {
|
||||||
|
mocked(getJoinedNonFunctionalMembers).mockReturnValue([1] as any);
|
||||||
|
const { result } = renderHook(() => usePresence(room, member));
|
||||||
|
expect(result.current).toBeNull();
|
||||||
|
});
|
||||||
|
|
||||||
|
it("returns null when member is null", () => {
|
||||||
|
const { result } = renderHook(() => usePresence(room, null));
|
||||||
|
expect(result.current).toBeNull();
|
||||||
|
});
|
||||||
|
|
||||||
it.each([
|
it.each([
|
||||||
["online", "Online"],
|
["online", Presence.Online],
|
||||||
["offline", "Offline"],
|
["offline", Presence.Offline],
|
||||||
["unavailable", "Away"],
|
["unavailable", Presence.Away],
|
||||||
])(
|
["busy", Presence.Busy],
|
||||||
"renders presence indicator when member.user is not linked but client has user data",
|
])("returns correct presence for user with '%s' presence state", (presenceStr, expectedPresence) => {
|
||||||
async (presenceStr, renderedStr) => {
|
user.presence = presenceStr;
|
||||||
mocked(isPresenceEnabled).mockReturnValue(true);
|
const { result } = renderHook(() => usePresence(room, member));
|
||||||
|
expect(result.current).toBe(expectedPresence);
|
||||||
|
});
|
||||||
|
|
||||||
const DM_USER_ID = "@bob:foo.bar";
|
it("returns Online when user.currentlyActive is true regardless of presence string", () => {
|
||||||
const dmRoomMap = {
|
user.presence = "offline";
|
||||||
getUserIdForRoomId: () => {
|
user.currentlyActive = true;
|
||||||
return DM_USER_ID;
|
const { result } = renderHook(() => usePresence(room, member));
|
||||||
},
|
expect(result.current).toBe(Presence.Online);
|
||||||
} as unknown as DMRoomMap;
|
});
|
||||||
|
|
||||||
jest.spyOn(DMRoomMap, "shared").mockReturnValue(dmRoomMap);
|
it("updates when UserEvent.Presence fires on member.user", async () => {
|
||||||
|
user.presence = "online";
|
||||||
|
const { result } = renderHook(() => usePresence(room, member));
|
||||||
|
expect(result.current).toBe(Presence.Online);
|
||||||
|
|
||||||
// member.user is not set: simulates the race condition on fresh login with no cache
|
act(() => {
|
||||||
// where the room list renders before member.user is linked
|
user.presence = "offline";
|
||||||
room.getMember = jest.fn((userId) => {
|
user.emit(UserEvent.Presence, null as any, user);
|
||||||
return new RoomMember(room.roomId, userId);
|
});
|
||||||
});
|
|
||||||
|
|
||||||
// But client.getUser() has the presence data
|
await waitFor(() => expect(result.current).toBe(Presence.Offline));
|
||||||
const user = new User(DM_USER_ID);
|
});
|
||||||
user.presence = presenceStr;
|
|
||||||
mockClient.getUser = jest.fn((userId) => (userId === DM_USER_ID ? user : null));
|
|
||||||
|
|
||||||
const { container } = renderComponent();
|
it("updates when UserEvent.CurrentlyActive fires on member.user", async () => {
|
||||||
|
user.presence = "offline";
|
||||||
|
user.currentlyActive = false;
|
||||||
|
const { result } = renderHook(() => usePresence(room, member));
|
||||||
|
expect(result.current).toBe(Presence.Offline);
|
||||||
|
|
||||||
const presence = container.querySelector(".mx_WithPresenceIndicator_icon")!;
|
act(() => {
|
||||||
expect(presence).toBeVisible();
|
user.currentlyActive = true;
|
||||||
await userEvent.hover(presence!);
|
user.emit(UserEvent.CurrentlyActive, null as any, user);
|
||||||
|
});
|
||||||
|
|
||||||
const tooltip = await waitFor(() => {
|
await waitFor(() => expect(result.current).toBe(Presence.Online));
|
||||||
const tooltip = document.getElementById(presence.getAttribute("aria-labelledby")!);
|
});
|
||||||
expect(tooltip).toBeVisible();
|
|
||||||
return tooltip;
|
|
||||||
});
|
|
||||||
|
|
||||||
// component should fall back to reading client.getUser() which does have the presence data
|
it("returns correct presence when member.user is not linked but client has user data", () => {
|
||||||
// so it should render correctly
|
member.user = undefined;
|
||||||
expect(tooltip).toHaveTextContent(renderedStr);
|
mocked(mockClient.getUser).mockImplementation((userId) => (userId === DM_USER_ID ? user : null));
|
||||||
},
|
|
||||||
);
|
user.presence = "online";
|
||||||
|
const { result } = renderHook(() => usePresence(room, member));
|
||||||
|
expect(result.current).toBe("online");
|
||||||
|
});
|
||||||
|
|
||||||
|
it("updates via client-level UserEvent.Presence when member.user is not yet linked", async () => {
|
||||||
|
member.user = undefined;
|
||||||
|
mocked(mockClient.getUser).mockImplementation((userId) => (userId === DM_USER_ID ? user : null));
|
||||||
|
user.presence = "online";
|
||||||
|
|
||||||
|
const { result } = renderHook(() => usePresence(room, member));
|
||||||
|
expect(result.current).toBe(Presence.Online);
|
||||||
|
|
||||||
|
act(() => {
|
||||||
|
user.presence = "offline";
|
||||||
|
mockClient.emit(UserEvent.Presence, null as any, user);
|
||||||
|
});
|
||||||
|
|
||||||
|
await waitFor(() => expect(result.current).toBe(Presence.Offline));
|
||||||
|
});
|
||||||
|
|
||||||
|
it("does not update when client emits UserEvent.Presence for a different user", async () => {
|
||||||
|
user.presence = "online";
|
||||||
|
const { result } = renderHook(() => usePresence(room, member));
|
||||||
|
expect(result.current).toBe(Presence.Online);
|
||||||
|
|
||||||
|
act(() => {
|
||||||
|
const otherUser = new User("@other:foo.bar");
|
||||||
|
otherUser.presence = "offline";
|
||||||
|
mockClient.emit(UserEvent.Presence, null as any, otherUser);
|
||||||
|
});
|
||||||
|
|
||||||
|
expect(result.current).toBe(Presence.Online);
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|||||||
Reference in New Issue
Block a user