New room list: fix incorrect decoration (#29770)

* fix(call): reset call value when the roomId changes

* fix(call): reset presence indicator when the room changes

* refactor: use existing `usePresence`

* test: fix room avatar view test

* test: update snapshots
This commit is contained in:
Florian Duros
2025-04-15 18:35:37 +02:00
committed by GitHub
parent 7e5f96c85d
commit 18a7250cf9
7 changed files with 111 additions and 228 deletions

View File

@@ -5,32 +5,18 @@
* Please see LICENSE files in the repository root for full details.
*/
import { renderHook, waitFor, act } from "jest-matrix-react";
import {
JoinRule,
type MatrixClient,
MatrixEvent,
type Room,
type RoomMember,
User,
UserEvent,
} from "matrix-js-sdk/src/matrix";
import { mocked } from "jest-mock";
import { renderHook, waitFor } from "jest-matrix-react";
import { JoinRule, type MatrixClient, type Room, RoomMember, User } from "matrix-js-sdk/src/matrix";
import { useRoomAvatarViewModel } from "../../../../../src/components/viewmodels/avatars/RoomAvatarViewModel";
import { createTestClient, mkStubRoom } from "../../../../test-utils";
import DMRoomMap from "../../../../../src/utils/DMRoomMap";
import { getJoinedNonFunctionalMembers } from "../../../../../src/utils/room/getJoinedNonFunctionalMembers";
import { isPresenceEnabled } from "../../../../../src/utils/presence";
import * as PresenceIndicatorModule from "../../../../../src/components/views/avatars/WithPresenceIndicator";
jest.mock("../../../../../src/utils/room/getJoinedNonFunctionalMembers", () => ({
getJoinedNonFunctionalMembers: jest.fn().mockReturnValue([]),
}));
jest.mock("../../../../../src/utils/presence", () => ({
isPresenceEnabled: jest.fn().mockReturnValue(false),
}));
describe("RoomAvatarViewModel", () => {
let matrixClient: MatrixClient;
let room: Room;
@@ -41,6 +27,9 @@ describe("RoomAvatarViewModel", () => {
DMRoomMap.makeShared(matrixClient);
jest.spyOn(DMRoomMap.shared(), "getUserIdForRoomId").mockReturnValue(null);
jest.spyOn(PresenceIndicatorModule, "useDmMember").mockReturnValue(null);
jest.spyOn(PresenceIndicatorModule, "usePresence").mockReturnValue(null);
});
it("should has hasDecoration to false", async () => {
@@ -74,62 +63,14 @@ describe("RoomAvatarViewModel", () => {
await waitFor(() => expect(vm.current.isPublic).toBe(true));
});
describe("presence", () => {
let user: User;
it("should return presence", async () => {
const user = User.createUser("userId", matrixClient);
const roomMember = new RoomMember(room.roomId, "userId");
roomMember.user = user;
jest.spyOn(PresenceIndicatorModule, "useDmMember").mockReturnValue(roomMember);
jest.spyOn(PresenceIndicatorModule, "usePresence").mockReturnValue(PresenceIndicatorModule.Presence.Online);
beforeEach(() => {
jest.spyOn(DMRoomMap.shared(), "getUserIdForRoomId").mockReturnValue("userId");
mocked(getJoinedNonFunctionalMembers).mockReturnValue([{}, {}] as RoomMember[]);
mocked(isPresenceEnabled).mockReturnValue(true);
user = User.createUser("userId", matrixClient);
jest.spyOn(matrixClient, "getUser").mockReturnValue(user);
});
it("should has presence set to null", () => {
jest.spyOn(DMRoomMap.shared(), "getUserIdForRoomId").mockReturnValue(null);
const { result: vm } = renderHook(() => useRoomAvatarViewModel(room));
expect(vm.current.presence).toBe(null);
});
it("should has online presence", async () => {
const { result: vm } = renderHook(() => useRoomAvatarViewModel(room));
expect(vm.current.presence).toBe("offline");
user.presence = "online";
await act(() => user.emit(UserEvent.Presence, new MatrixEvent(), user));
await waitFor(() => expect(vm.current.presence).toBe("online"));
user.currentlyActive = true;
user.presence = "offline";
await act(() => user.emit(UserEvent.CurrentlyActive, new MatrixEvent(), user));
await waitFor(() => expect(vm.current.presence).toBe("online"));
});
it("should has busy presence", async () => {
user.presence = "busy";
const { result: vm } = renderHook(() => useRoomAvatarViewModel(room));
expect(vm.current.presence).toBe("busy");
});
it("should has offline presence", async () => {
user.presence = "offline";
const { result: vm } = renderHook(() => useRoomAvatarViewModel(room));
expect(vm.current.presence).toBe("offline");
});
it("should has unavailable presence", async () => {
user.presence = "unavailable";
const { result: vm } = renderHook(() => useRoomAvatarViewModel(room));
expect(vm.current.presence).toBe("unavailable");
});
it("should has hasDecoration to true", async () => {
const { result: vm } = renderHook(() => useRoomAvatarViewModel(room));
expect(vm.current.hasDecoration).toBe(true);
});
const { result: vm } = renderHook(() => useRoomAvatarViewModel(room));
expect(vm.current.presence).toBe(PresenceIndicatorModule.Presence.Online);
});
});

View File

@@ -12,11 +12,11 @@ import { mocked } from "jest-mock";
import { RoomAvatarView } from "../../../../../src/components/views/avatars/RoomAvatarView";
import { mkStubRoom, stubClient } from "../../../../test-utils";
import {
type Presence,
type RoomAvatarViewState,
useRoomAvatarViewModel,
} from "../../../../../src/components/viewmodels/avatars/RoomAvatarViewModel";
import DMRoomMap from "../../../../../src/utils/DMRoomMap";
import { Presence } from "../../../../../src/components/views/avatars/WithPresenceIndicator";
jest.mock("../../../../../src/components/viewmodels/avatars/RoomAvatarViewModel", () => ({
useRoomAvatarViewModel: jest.fn(),
@@ -83,10 +83,10 @@ describe("<RoomAvatarView />", () => {
});
it.each([
{ presence: "online" as Presence, label: "Online" },
{ presence: "offline" as Presence, label: "Offline" },
{ presence: "busy" as Presence, label: "Busy" },
{ presence: "unavailable" as Presence, label: "Away" },
{ presence: Presence.Online, label: "Online" },
{ presence: Presence.Offline, label: "Offline" },
{ presence: Presence.Busy, label: "Busy" },
{ presence: Presence.Away, label: "Away" },
])("should render the $presence presence", ({ presence, label }) => {
mocked(useRoomAvatarViewModel).mockReturnValue({
...defaultValue,

View File

@@ -108,7 +108,76 @@ exports[`<RoomAvatarView /> should render a video room decoration 1`] = `
</DocumentFragment>
`;
exports[`<RoomAvatarView /> should render the busy presence 1`] = `
exports[`<RoomAvatarView /> should render the AWAY presence 1`] = `
<DocumentFragment>
<div
class="mx_RoomAvatarView"
>
<span
aria-label="Avatar"
class="_avatar_1qbcf_8 mx_BaseAvatar mx_RoomAvatarView_RoomAvatar mx_RoomAvatarView_RoomAvatar_icon mx_RoomAvatarView_RoomAvatar_presence"
data-color="1"
data-testid="avatar-img"
data-type="round"
style="--cpd-avatar-size: 32px;"
>
<img
alt=""
class="_image_1qbcf_41"
data-type="round"
height="32px"
loading="lazy"
referrerpolicy="no-referrer"
src="http://this.is.a.url/avatar.url/room.png"
width="32px"
/>
</span>
<svg
aria-label="This room is a video room"
class="mx_RoomAvatarView_icon"
color="var(--cpd-color-icon-tertiary)"
fill="currentColor"
height="16px"
viewBox="0 0 24 24"
width="16px"
xmlns="http://www.w3.org/2000/svg"
>
<path
d="M6 4h10a2 2 0 0 1 2 2v4.286l3.35-2.871a1 1 0 0 1 1.65.76v7.65a1 1 0 0 1-1.65.76L18 13.715V18a2 2 0 0 1-2 2H6a4 4 0 0 1-4-4V8a4 4 0 0 1 4-4"
/>
</svg>
<svg
aria-label="Away"
class="mx_RoomAvatarView_PresenceDecoration"
color="var(--cpd-color-icon-quaternary)"
fill="currentColor"
height="8px"
viewBox="0 0 8 8"
width="8px"
xmlns="http://www.w3.org/2000/svg"
>
<g
clip-path="url(#a)"
>
<path
d="M8 4a4 4 0 1 1-8 0 4 4 0 0 1 8 0"
/>
</g>
<defs>
<clippath
id="a"
>
<path
d="M0 0h8v8H0z"
/>
</clippath>
</defs>
</svg>
</div>
</DocumentFragment>
`;
exports[`<RoomAvatarView /> should render the BUSY presence 1`] = `
<DocumentFragment>
<div
class="mx_RoomAvatarView"
@@ -179,7 +248,7 @@ exports[`<RoomAvatarView /> should render the busy presence 1`] = `
</DocumentFragment>
`;
exports[`<RoomAvatarView /> should render the offline presence 1`] = `
exports[`<RoomAvatarView /> should render the OFFLINE presence 1`] = `
<DocumentFragment>
<div
class="mx_RoomAvatarView"
@@ -250,7 +319,7 @@ exports[`<RoomAvatarView /> should render the offline presence 1`] = `
</DocumentFragment>
`;
exports[`<RoomAvatarView /> should render the online presence 1`] = `
exports[`<RoomAvatarView /> should render the ONLINE presence 1`] = `
<DocumentFragment>
<div
class="mx_RoomAvatarView"
@@ -318,72 +387,3 @@ exports[`<RoomAvatarView /> should render the online presence 1`] = `
</div>
</DocumentFragment>
`;
exports[`<RoomAvatarView /> should render the unavailable presence 1`] = `
<DocumentFragment>
<div
class="mx_RoomAvatarView"
>
<span
aria-label="Avatar"
class="_avatar_1qbcf_8 mx_BaseAvatar mx_RoomAvatarView_RoomAvatar mx_RoomAvatarView_RoomAvatar_icon mx_RoomAvatarView_RoomAvatar_presence"
data-color="1"
data-testid="avatar-img"
data-type="round"
style="--cpd-avatar-size: 32px;"
>
<img
alt=""
class="_image_1qbcf_41"
data-type="round"
height="32px"
loading="lazy"
referrerpolicy="no-referrer"
src="http://this.is.a.url/avatar.url/room.png"
width="32px"
/>
</span>
<svg
aria-label="This room is a video room"
class="mx_RoomAvatarView_icon"
color="var(--cpd-color-icon-tertiary)"
fill="currentColor"
height="16px"
viewBox="0 0 24 24"
width="16px"
xmlns="http://www.w3.org/2000/svg"
>
<path
d="M6 4h10a2 2 0 0 1 2 2v4.286l3.35-2.871a1 1 0 0 1 1.65.76v7.65a1 1 0 0 1-1.65.76L18 13.715V18a2 2 0 0 1-2 2H6a4 4 0 0 1-4-4V8a4 4 0 0 1 4-4"
/>
</svg>
<svg
aria-label="Away"
class="mx_RoomAvatarView_PresenceDecoration"
color="var(--cpd-color-icon-quaternary)"
fill="currentColor"
height="8px"
viewBox="0 0 8 8"
width="8px"
xmlns="http://www.w3.org/2000/svg"
>
<g
clip-path="url(#a)"
>
<path
d="M8 4a4 4 0 1 1-8 0 4 4 0 0 1 8 0"
/>
</g>
<defs>
<clippath
id="a"
>
<path
d="M0 0h8v8H0z"
/>
</clippath>
</defs>
</svg>
</div>
</DocumentFragment>
`;