Add Pin/Unpin action in quick access of the message action bar (#12897)
* Add Pin/Unpin action in quick access of the message action bar * Add tests for `MessageActionBar` * Add tests for `PinningUtils` * Fix `MessageContextMenu-test` * Add e2e test to pin/unpin from message action bar
This commit is contained in:
parent
4064db1d02
commit
3d80eff65b
9 changed files with 503 additions and 105 deletions
|
@ -15,7 +15,7 @@ limitations under the License.
|
|||
*/
|
||||
|
||||
import React from "react";
|
||||
import { fireEvent, render, RenderResult } from "@testing-library/react";
|
||||
import { fireEvent, render, RenderResult, screen, waitFor } from "@testing-library/react";
|
||||
import {
|
||||
EventStatus,
|
||||
MatrixEvent,
|
||||
|
@ -28,9 +28,11 @@ import {
|
|||
FeatureSupport,
|
||||
Thread,
|
||||
M_POLL_KIND_DISCLOSED,
|
||||
EventTimeline,
|
||||
} from "matrix-js-sdk/src/matrix";
|
||||
import { PollStartEvent } from "matrix-js-sdk/src/extensible_events_v1/PollStartEvent";
|
||||
import { mocked } from "jest-mock";
|
||||
import userEvent from "@testing-library/user-event";
|
||||
|
||||
import { MatrixClientPeg } from "../../../../src/MatrixClientPeg";
|
||||
import RoomContext, { TimelineRenderingType } from "../../../../src/contexts/RoomContext";
|
||||
|
@ -83,8 +85,16 @@ describe("MessageContextMenu", () => {
|
|||
});
|
||||
|
||||
describe("message pinning", () => {
|
||||
let room: Room;
|
||||
|
||||
beforeEach(() => {
|
||||
room = makeDefaultRoom();
|
||||
|
||||
jest.spyOn(SettingsStore, "getValue").mockReturnValue(true);
|
||||
jest.spyOn(
|
||||
room.getLiveTimeline().getState(EventTimeline.FORWARDS)!,
|
||||
"mayClientSendStateEvent",
|
||||
).mockReturnValue(true);
|
||||
});
|
||||
|
||||
afterAll(() => {
|
||||
|
@ -95,25 +105,23 @@ describe("MessageContextMenu", () => {
|
|||
const eventContent = createMessageEventContent("hello");
|
||||
const event = new MatrixEvent({ type: EventType.RoomMessage, content: eventContent });
|
||||
|
||||
const room = makeDefaultRoom();
|
||||
// mock permission to disallow adding pinned messages to room
|
||||
jest.spyOn(room.currentState, "mayClientSendStateEvent").mockReturnValue(false);
|
||||
jest.spyOn(
|
||||
room.getLiveTimeline().getState(EventTimeline.FORWARDS)!,
|
||||
"mayClientSendStateEvent",
|
||||
).mockReturnValue(false);
|
||||
|
||||
createMenu(event, {}, {}, undefined, room);
|
||||
createMenu(event, { rightClick: true }, {}, undefined, room);
|
||||
|
||||
expect(document.querySelector('li[aria-label="Pin"]')).toBeFalsy();
|
||||
expect(screen.queryByRole("menuitem", { name: "Pin" })).toBeFalsy();
|
||||
});
|
||||
|
||||
it("does not show pin option for beacon_info event", () => {
|
||||
const deadBeaconEvent = makeBeaconInfoEvent("@alice:server.org", roomId, { isLive: false });
|
||||
|
||||
const room = makeDefaultRoom();
|
||||
// mock permission to allow adding pinned messages to room
|
||||
jest.spyOn(room.currentState, "mayClientSendStateEvent").mockReturnValue(true);
|
||||
createMenu(deadBeaconEvent, { rightClick: true }, {}, undefined, room);
|
||||
|
||||
createMenu(deadBeaconEvent, {}, {}, undefined, room);
|
||||
|
||||
expect(document.querySelector('li[aria-label="Pin"]')).toBeFalsy();
|
||||
expect(screen.queryByRole("menuitem", { name: "Pin" })).toBeFalsy();
|
||||
});
|
||||
|
||||
it("does not show pin option when pinning feature is disabled", () => {
|
||||
|
@ -124,15 +132,12 @@ describe("MessageContextMenu", () => {
|
|||
room_id: roomId,
|
||||
});
|
||||
|
||||
const room = makeDefaultRoom();
|
||||
// mock permission to allow adding pinned messages to room
|
||||
jest.spyOn(room.currentState, "mayClientSendStateEvent").mockReturnValue(true);
|
||||
// disable pinning feature
|
||||
jest.spyOn(SettingsStore, "getValue").mockReturnValue(false);
|
||||
|
||||
createMenu(pinnableEvent, {}, {}, undefined, room);
|
||||
createMenu(pinnableEvent, { rightClick: true }, {}, undefined, room);
|
||||
|
||||
expect(document.querySelector('li[aria-label="Pin"]')).toBeFalsy();
|
||||
expect(screen.queryByRole("menuitem", { name: "Pin" })).toBeFalsy();
|
||||
});
|
||||
|
||||
it("shows pin option when pinning feature is enabled", () => {
|
||||
|
@ -143,16 +148,12 @@ describe("MessageContextMenu", () => {
|
|||
room_id: roomId,
|
||||
});
|
||||
|
||||
const room = makeDefaultRoom();
|
||||
// mock permission to allow adding pinned messages to room
|
||||
jest.spyOn(room.currentState, "mayClientSendStateEvent").mockReturnValue(true);
|
||||
createMenu(pinnableEvent, { rightClick: true }, {}, undefined, room);
|
||||
|
||||
createMenu(pinnableEvent, {}, {}, undefined, room);
|
||||
|
||||
expect(document.querySelector('li[aria-label="Pin"]')).toBeTruthy();
|
||||
expect(screen.getByRole("menuitem", { name: "Pin" })).toBeTruthy();
|
||||
});
|
||||
|
||||
it("pins event on pin option click", () => {
|
||||
it("pins event on pin option click", async () => {
|
||||
const onFinished = jest.fn();
|
||||
const eventContent = createMessageEventContent("hello");
|
||||
const pinnableEvent = new MatrixEvent({
|
||||
|
@ -162,43 +163,48 @@ describe("MessageContextMenu", () => {
|
|||
});
|
||||
pinnableEvent.event.event_id = "!3";
|
||||
const client = MatrixClientPeg.safeGet();
|
||||
const room = makeDefaultRoom();
|
||||
|
||||
// mock permission to allow adding pinned messages to room
|
||||
jest.spyOn(room.currentState, "mayClientSendStateEvent").mockReturnValue(true);
|
||||
jest.spyOn(room.getLiveTimeline().getState(EventTimeline.FORWARDS)!, "getStateEvents").mockReturnValue({
|
||||
// @ts-ignore
|
||||
getContent: () => ({ pinned: ["!1", "!2"] }),
|
||||
});
|
||||
|
||||
// mock read pins account data
|
||||
const pinsAccountData = new MatrixEvent({ content: { event_ids: ["!1", "!2"] } });
|
||||
jest.spyOn(room, "getAccountData").mockReturnValue(pinsAccountData);
|
||||
|
||||
createMenu(pinnableEvent, { onFinished }, {}, undefined, room);
|
||||
createMenu(pinnableEvent, { onFinished, rightClick: true }, {}, undefined, room);
|
||||
|
||||
fireEvent.click(document.querySelector('li[aria-label="Pin"]')!);
|
||||
await userEvent.click(screen.getByRole("menuitem", { name: "Pin" }));
|
||||
|
||||
// added to account data
|
||||
expect(client.setRoomAccountData).toHaveBeenCalledWith(roomId, ReadPinsEventId, {
|
||||
event_ids: [
|
||||
// from account data
|
||||
"!1",
|
||||
"!2",
|
||||
pinnableEvent.getId(),
|
||||
],
|
||||
});
|
||||
await waitFor(() =>
|
||||
expect(client.setRoomAccountData).toHaveBeenCalledWith(roomId, ReadPinsEventId, {
|
||||
event_ids: [
|
||||
// from account data
|
||||
"!1",
|
||||
"!2",
|
||||
pinnableEvent.getId(),
|
||||
],
|
||||
}),
|
||||
);
|
||||
|
||||
// add to room's pins
|
||||
expect(client.sendStateEvent).toHaveBeenCalledWith(
|
||||
roomId,
|
||||
EventType.RoomPinnedEvents,
|
||||
{
|
||||
pinned: [pinnableEvent.getId()],
|
||||
},
|
||||
"",
|
||||
await waitFor(() =>
|
||||
expect(client.sendStateEvent).toHaveBeenCalledWith(
|
||||
roomId,
|
||||
EventType.RoomPinnedEvents,
|
||||
{
|
||||
pinned: ["!1", "!2", pinnableEvent.getId()],
|
||||
},
|
||||
"",
|
||||
),
|
||||
);
|
||||
|
||||
expect(onFinished).toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it("unpins event on pin option click when event is pinned", () => {
|
||||
it("unpins event on pin option click when event is pinned", async () => {
|
||||
const eventContent = createMessageEventContent("hello");
|
||||
const pinnableEvent = new MatrixEvent({
|
||||
type: EventType.RoomMessage,
|
||||
|
@ -207,7 +213,6 @@ describe("MessageContextMenu", () => {
|
|||
});
|
||||
pinnableEvent.event.event_id = "!3";
|
||||
const client = MatrixClientPeg.safeGet();
|
||||
const room = makeDefaultRoom();
|
||||
|
||||
// make the event already pinned in the room
|
||||
const pinEvent = new MatrixEvent({
|
||||
|
@ -216,18 +221,15 @@ describe("MessageContextMenu", () => {
|
|||
state_key: "",
|
||||
content: { pinned: [pinnableEvent.getId(), "!another-event"] },
|
||||
});
|
||||
room.currentState.setStateEvents([pinEvent]);
|
||||
|
||||
// mock permission to allow adding pinned messages to room
|
||||
jest.spyOn(room.currentState, "mayClientSendStateEvent").mockReturnValue(true);
|
||||
room.getLiveTimeline().getState(EventTimeline.FORWARDS)!.setStateEvents([pinEvent]);
|
||||
|
||||
// mock read pins account data
|
||||
const pinsAccountData = new MatrixEvent({ content: { event_ids: ["!1", "!2"] } });
|
||||
jest.spyOn(room, "getAccountData").mockReturnValue(pinsAccountData);
|
||||
|
||||
createMenu(pinnableEvent, {}, {}, undefined, room);
|
||||
createMenu(pinnableEvent, { rightClick: true }, {}, undefined, room);
|
||||
|
||||
fireEvent.click(document.querySelector('li[aria-label="Unpin"]')!);
|
||||
await userEvent.click(screen.getByRole("menuitem", { name: "Unpin" }));
|
||||
|
||||
expect(client.setRoomAccountData).not.toHaveBeenCalled();
|
||||
|
||||
|
|
|
@ -25,6 +25,7 @@ import {
|
|||
Room,
|
||||
FeatureSupport,
|
||||
Thread,
|
||||
EventTimeline,
|
||||
} from "matrix-js-sdk/src/matrix";
|
||||
|
||||
import MessageActionBar from "../../../../src/components/views/messages/MessageActionBar";
|
||||
|
@ -51,6 +52,8 @@ describe("<MessageActionBar />", () => {
|
|||
...mockClientMethodsUser(userId),
|
||||
...mockClientMethodsEvents(),
|
||||
getRoom: jest.fn(),
|
||||
setRoomAccountData: jest.fn(),
|
||||
sendStateEvent: jest.fn(),
|
||||
});
|
||||
const room = new Room(roomId, client, userId);
|
||||
|
||||
|
@ -442,10 +445,10 @@ describe("<MessageActionBar />", () => {
|
|||
});
|
||||
});
|
||||
|
||||
it.each([["React"], ["Reply"], ["Reply in thread"], ["Edit"]])(
|
||||
it.each([["React"], ["Reply"], ["Reply in thread"], ["Edit"], ["Pin"]])(
|
||||
"does not show context menu when right-clicking",
|
||||
(buttonLabel: string) => {
|
||||
// For favourite button
|
||||
// For favourite and pin buttons
|
||||
jest.spyOn(SettingsStore, "getValue").mockReturnValue(true);
|
||||
|
||||
const event = new MouseEvent("contextmenu", {
|
||||
|
@ -468,4 +471,33 @@ describe("<MessageActionBar />", () => {
|
|||
fireEvent.contextMenu(queryByLabelText("Options")!);
|
||||
expect(queryByTestId("mx_MessageContextMenu")).toBeTruthy();
|
||||
});
|
||||
|
||||
describe("pin button", () => {
|
||||
beforeEach(() => {
|
||||
// enable pin button
|
||||
jest.spyOn(SettingsStore, "getValue").mockReturnValue(true);
|
||||
});
|
||||
|
||||
afterEach(() => {
|
||||
jest.spyOn(
|
||||
room.getLiveTimeline().getState(EventTimeline.FORWARDS)!,
|
||||
"mayClientSendStateEvent",
|
||||
).mockRestore();
|
||||
});
|
||||
|
||||
it("should not render pin button when user can't send state event", () => {
|
||||
jest.spyOn(
|
||||
room.getLiveTimeline().getState(EventTimeline.FORWARDS)!,
|
||||
"mayClientSendStateEvent",
|
||||
).mockReturnValue(false);
|
||||
|
||||
const { queryByLabelText } = getComponent({ mxEvent: alicesMessageEvent });
|
||||
expect(queryByLabelText("Pin")).toBeFalsy();
|
||||
});
|
||||
|
||||
it("should render pin button", () => {
|
||||
const { queryByLabelText } = getComponent({ mxEvent: alicesMessageEvent });
|
||||
expect(queryByLabelText("Pin")).toBeTruthy();
|
||||
});
|
||||
});
|
||||
});
|
||||
|
|
252
test/utils/PinningUtils-test.ts
Normal file
252
test/utils/PinningUtils-test.ts
Normal file
|
@ -0,0 +1,252 @@
|
|||
/*
|
||||
* Copyright 2024 The Matrix.org Foundation C.I.C.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import { EventTimeline, EventType, IEvent, MatrixClient, MatrixEvent, Room } from "matrix-js-sdk/src/matrix";
|
||||
import { mocked } from "jest-mock";
|
||||
|
||||
import { createTestClient } from "../test-utils";
|
||||
import PinningUtils from "../../src/utils/PinningUtils";
|
||||
import SettingsStore from "../../src/settings/SettingsStore";
|
||||
import { canPinEvent, isContentActionable } from "../../src/utils/EventUtils";
|
||||
import { ReadPinsEventId } from "../../src/components/views/right_panel/types";
|
||||
|
||||
jest.mock("../../src/utils/EventUtils", () => {
|
||||
return {
|
||||
isContentActionable: jest.fn(),
|
||||
canPinEvent: jest.fn(),
|
||||
};
|
||||
});
|
||||
|
||||
describe("PinningUtils", () => {
|
||||
const roomId = "!room:example.org";
|
||||
const userId = "@alice:example.org";
|
||||
|
||||
const mockedIsContentActionable = mocked(isContentActionable);
|
||||
const mockedCanPinEvent = mocked(canPinEvent);
|
||||
|
||||
let matrixClient: MatrixClient;
|
||||
let room: Room;
|
||||
|
||||
/**
|
||||
* Create a pinned event with the given content.
|
||||
* @param content
|
||||
*/
|
||||
function makePinEvent(content?: Partial<IEvent>) {
|
||||
return new MatrixEvent({
|
||||
type: EventType.RoomMessage,
|
||||
sender: userId,
|
||||
content: {
|
||||
body: "First pinned message",
|
||||
msgtype: "m.text",
|
||||
},
|
||||
room_id: roomId,
|
||||
origin_server_ts: 0,
|
||||
event_id: "$eventId",
|
||||
...content,
|
||||
});
|
||||
}
|
||||
|
||||
beforeEach(() => {
|
||||
// Enable feature pinning
|
||||
jest.spyOn(SettingsStore, "getValue").mockReturnValue(true);
|
||||
mockedIsContentActionable.mockImplementation(() => true);
|
||||
mockedCanPinEvent.mockImplementation(() => true);
|
||||
|
||||
matrixClient = createTestClient();
|
||||
room = new Room(roomId, matrixClient, userId);
|
||||
matrixClient.getRoom = jest.fn().mockReturnValue(room);
|
||||
|
||||
jest.spyOn(
|
||||
matrixClient.getRoom(roomId)!.getLiveTimeline().getState(EventTimeline.FORWARDS)!,
|
||||
"mayClientSendStateEvent",
|
||||
).mockReturnValue(true);
|
||||
});
|
||||
|
||||
describe("isPinnable", () => {
|
||||
test.each(PinningUtils.PINNABLE_EVENT_TYPES)("should return true for pinnable event types", (eventType) => {
|
||||
const event = makePinEvent({ type: eventType });
|
||||
expect(PinningUtils.isPinnable(event)).toBe(true);
|
||||
});
|
||||
|
||||
test("should return false for a non pinnable event type", () => {
|
||||
const event = makePinEvent({ type: EventType.RoomCreate });
|
||||
expect(PinningUtils.isPinnable(event)).toBe(false);
|
||||
});
|
||||
|
||||
test("should return false for a redacted event", () => {
|
||||
const event = makePinEvent({ unsigned: { redacted_because: "because" as unknown as IEvent } });
|
||||
expect(PinningUtils.isPinnable(event)).toBe(false);
|
||||
});
|
||||
});
|
||||
|
||||
describe("isPinned", () => {
|
||||
test("should return false if no room", () => {
|
||||
matrixClient.getRoom = jest.fn().mockReturnValue(undefined);
|
||||
const event = makePinEvent();
|
||||
|
||||
expect(PinningUtils.isPinned(matrixClient, event)).toBe(false);
|
||||
});
|
||||
|
||||
test("should return false if no pinned event", () => {
|
||||
jest.spyOn(
|
||||
matrixClient.getRoom(roomId)!.getLiveTimeline().getState(EventTimeline.FORWARDS)!,
|
||||
"getStateEvents",
|
||||
).mockReturnValue(null);
|
||||
|
||||
const event = makePinEvent();
|
||||
expect(PinningUtils.isPinned(matrixClient, event)).toBe(false);
|
||||
});
|
||||
|
||||
test("should return false if pinned events do not contain the event id", () => {
|
||||
jest.spyOn(
|
||||
matrixClient.getRoom(roomId)!.getLiveTimeline().getState(EventTimeline.FORWARDS)!,
|
||||
"getStateEvents",
|
||||
).mockReturnValue({
|
||||
// @ts-ignore
|
||||
getContent: () => ({ pinned: ["$otherEventId"] }),
|
||||
});
|
||||
|
||||
const event = makePinEvent();
|
||||
expect(PinningUtils.isPinned(matrixClient, event)).toBe(false);
|
||||
});
|
||||
|
||||
test("should return true if pinned events contains the event id", () => {
|
||||
const event = makePinEvent();
|
||||
jest.spyOn(
|
||||
matrixClient.getRoom(roomId)!.getLiveTimeline().getState(EventTimeline.FORWARDS)!,
|
||||
"getStateEvents",
|
||||
).mockReturnValue({
|
||||
// @ts-ignore
|
||||
getContent: () => ({ pinned: [event.getId()] }),
|
||||
});
|
||||
|
||||
expect(PinningUtils.isPinned(matrixClient, event)).toBe(true);
|
||||
});
|
||||
});
|
||||
|
||||
describe("canPinOrUnpin", () => {
|
||||
test("should return false if pinning is disabled", () => {
|
||||
// Disable feature pinning
|
||||
jest.spyOn(SettingsStore, "getValue").mockReturnValue(false);
|
||||
const event = makePinEvent();
|
||||
|
||||
expect(PinningUtils.canPinOrUnpin(matrixClient, event)).toBe(false);
|
||||
});
|
||||
|
||||
test("should return false if event is not actionable", () => {
|
||||
mockedIsContentActionable.mockImplementation(() => false);
|
||||
const event = makePinEvent();
|
||||
|
||||
expect(PinningUtils.canPinOrUnpin(matrixClient, event)).toBe(false);
|
||||
});
|
||||
|
||||
test("should return false if no room", () => {
|
||||
matrixClient.getRoom = jest.fn().mockReturnValue(undefined);
|
||||
const event = makePinEvent();
|
||||
|
||||
expect(PinningUtils.canPinOrUnpin(matrixClient, event)).toBe(false);
|
||||
});
|
||||
|
||||
test("should return false if client cannot send state event", () => {
|
||||
jest.spyOn(
|
||||
matrixClient.getRoom(roomId)!.getLiveTimeline().getState(EventTimeline.FORWARDS)!,
|
||||
"mayClientSendStateEvent",
|
||||
).mockReturnValue(false);
|
||||
const event = makePinEvent();
|
||||
|
||||
expect(PinningUtils.canPinOrUnpin(matrixClient, event)).toBe(false);
|
||||
});
|
||||
|
||||
test("should return false if event is not pinnable", () => {
|
||||
mockedCanPinEvent.mockReturnValue(false);
|
||||
const event = makePinEvent();
|
||||
|
||||
expect(PinningUtils.canPinOrUnpin(matrixClient, event)).toBe(false);
|
||||
});
|
||||
|
||||
test("should return true if all conditions are met", () => {
|
||||
const event = makePinEvent();
|
||||
|
||||
expect(PinningUtils.canPinOrUnpin(matrixClient, event)).toBe(true);
|
||||
});
|
||||
});
|
||||
|
||||
describe("pinOrUnpinEvent", () => {
|
||||
test("should do nothing if no room", async () => {
|
||||
matrixClient.getRoom = jest.fn().mockReturnValue(undefined);
|
||||
const event = makePinEvent();
|
||||
|
||||
await PinningUtils.pinOrUnpinEvent(matrixClient, event);
|
||||
expect(matrixClient.sendStateEvent).not.toHaveBeenCalled();
|
||||
});
|
||||
|
||||
test("should do nothing if no event id", async () => {
|
||||
const event = makePinEvent({ event_id: undefined });
|
||||
|
||||
await PinningUtils.pinOrUnpinEvent(matrixClient, event);
|
||||
expect(matrixClient.sendStateEvent).not.toHaveBeenCalled();
|
||||
});
|
||||
|
||||
test("should pin the event if not pinned", async () => {
|
||||
jest.spyOn(
|
||||
matrixClient.getRoom(roomId)!.getLiveTimeline().getState(EventTimeline.FORWARDS)!,
|
||||
"getStateEvents",
|
||||
).mockReturnValue({
|
||||
// @ts-ignore
|
||||
getContent: () => ({ pinned: ["$otherEventId"] }),
|
||||
});
|
||||
|
||||
jest.spyOn(room, "getAccountData").mockReturnValue({
|
||||
getContent: jest.fn().mockReturnValue({
|
||||
event_ids: ["$otherEventId"],
|
||||
}),
|
||||
} as unknown as MatrixEvent);
|
||||
|
||||
const event = makePinEvent();
|
||||
await PinningUtils.pinOrUnpinEvent(matrixClient, event);
|
||||
|
||||
expect(matrixClient.setRoomAccountData).toHaveBeenCalledWith(roomId, ReadPinsEventId, {
|
||||
event_ids: ["$otherEventId", event.getId()],
|
||||
});
|
||||
expect(matrixClient.sendStateEvent).toHaveBeenCalledWith(
|
||||
roomId,
|
||||
EventType.RoomPinnedEvents,
|
||||
{ pinned: ["$otherEventId", event.getId()] },
|
||||
"",
|
||||
);
|
||||
});
|
||||
|
||||
test("should unpin the event if already pinned", async () => {
|
||||
const event = makePinEvent();
|
||||
|
||||
jest.spyOn(
|
||||
matrixClient.getRoom(roomId)!.getLiveTimeline().getState(EventTimeline.FORWARDS)!,
|
||||
"getStateEvents",
|
||||
).mockReturnValue({
|
||||
// @ts-ignore
|
||||
getContent: () => ({ pinned: [event.getId(), "$otherEventId"] }),
|
||||
});
|
||||
|
||||
await PinningUtils.pinOrUnpinEvent(matrixClient, event);
|
||||
expect(matrixClient.sendStateEvent).toHaveBeenCalledWith(
|
||||
roomId,
|
||||
EventType.RoomPinnedEvents,
|
||||
{ pinned: ["$otherEventId"] },
|
||||
"",
|
||||
);
|
||||
});
|
||||
});
|
||||
});
|
Loading…
Add table
Add a link
Reference in a new issue