Skip to content
This repository was archived by the owner on Sep 11, 2024. It is now read-only.

Commit c0c47c4

Browse files
committed
Add e2e tests for pinned messages
1 parent 0251e40 commit c0c47c4

File tree

6 files changed

+305
-0
lines changed

6 files changed

+305
-0
lines changed
+226
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,226 @@
1+
/*
2+
* Copyright 2024 The Matrix.org Foundation C.I.C.
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
17+
import { Page } from "@playwright/test";
18+
19+
import { test as base, expect } from "../../element-web-test";
20+
import { Client } from "../../pages/client";
21+
import { ElementAppPage } from "../../pages/ElementAppPage";
22+
import { Bot } from "../../pages/bot";
23+
24+
/**
25+
* Set up for pinned message tests.
26+
*/
27+
export const test = base.extend<{
28+
room1Name?: string;
29+
room1: { name: string; roomId: string };
30+
util: Helpers;
31+
}>({
32+
displayName: "Alice",
33+
botCreateOpts: { displayName: "Other User" },
34+
35+
room1Name: "Room 1",
36+
room1: async ({ room1Name: name, app, user, bot }, use) => {
37+
const roomId = await app.client.createRoom({ name, invite: [bot.credentials.userId] });
38+
await use({ name, roomId });
39+
},
40+
41+
util: async ({ page, app, bot }, use) => {
42+
await use(new Helpers(page, app, bot));
43+
},
44+
});
45+
46+
export class Helpers {
47+
constructor(
48+
private page: Page,
49+
private app: ElementAppPage,
50+
private bot: Bot,
51+
) {}
52+
53+
/**
54+
* Sends messages into given room as a bot
55+
* @param room - the name of the room to send messages into
56+
* @param messages - the list of messages to send, these can be strings or implementations of MessageSpec like `editOf`
57+
*/
58+
async receiveMessages(room: string | { name: string }, messages: string[]) {
59+
await this.sendMessageAsClient(this.bot, room, messages);
60+
}
61+
62+
/**
63+
* Use the supplied client to send messages or perform actions as specified by
64+
* the supplied {@link Message} items.
65+
*/
66+
private async sendMessageAsClient(cli: Client, roomName: string | { name: string }, messages: string[]) {
67+
const room = await this.findRoomByName(typeof roomName === "string" ? roomName : roomName.name);
68+
const roomId = await room.evaluate((room) => room.roomId);
69+
70+
for (const message of messages) {
71+
await cli.sendMessage(roomId, { body: message, msgtype: "m.text" });
72+
73+
// TODO: without this wait, some tests that send lots of messages flake
74+
// from time to time. I (andyb) have done some investigation, but it
75+
// needs more work to figure out. The messages do arrive over sync, but
76+
// they never appear in the timeline, and they never fire a
77+
// Room.timeline event. I think this only happens with events that refer
78+
// to other events (e.g. replies), so it might be caused by the
79+
// referring event arriving before the referred-to event.
80+
await this.page.waitForTimeout(100);
81+
}
82+
}
83+
84+
/**
85+
* Find a room by its name
86+
* @param roomName
87+
* @private
88+
*/
89+
private async findRoomByName(roomName: string) {
90+
return this.app.client.evaluateHandle((cli, roomName) => {
91+
return cli.getRooms().find((r) => r.name === roomName);
92+
}, roomName);
93+
}
94+
95+
/**
96+
* Open the room with the supplied name.
97+
*/
98+
async goTo(room: string | { name: string }) {
99+
await this.app.viewRoomByName(typeof room === "string" ? room : room.name);
100+
}
101+
102+
/**
103+
* Pin the given message
104+
* @param message
105+
*/
106+
async pinMessage(message: string) {
107+
const timelineMessage = this.page.locator(".mx_MTextBody", { hasText: message });
108+
await timelineMessage.click({ button: "right" });
109+
await this.page.getByRole("menuitem", { name: "Pin" }).click();
110+
}
111+
112+
/**
113+
* Pin the given messages
114+
* @param messages
115+
*/
116+
async pinMessages(messages: string[]) {
117+
for (const message of messages) {
118+
await this.pinMessage(message);
119+
}
120+
}
121+
122+
/**
123+
* Open the room info panel
124+
*/
125+
async openRoomInfo() {
126+
await this.page.getByRole("button", { name: "Room info" }).nth(1).click();
127+
}
128+
129+
/**
130+
* Assert that the pinned count in the room info is correct
131+
* Open the room info and check the pinned count
132+
* @param count
133+
*/
134+
async assertPinnedCountInRoomInfo(count: number) {
135+
await expect(this.page.getByRole("menuitem", { name: "Pinned messages" })).toHaveText(
136+
`Pinned messages${count}`,
137+
);
138+
}
139+
140+
/**
141+
* Open the pinned messages list
142+
*/
143+
async openPinnedMessagesList() {
144+
await this.page.getByRole("menuitem", { name: "Pinned messages" }).click();
145+
}
146+
147+
/**
148+
* Return the right panel
149+
* @private
150+
*/
151+
private getRightPanel() {
152+
return this.page.locator("#mx_RightPanel");
153+
}
154+
155+
/**
156+
* Assert that the pinned message list contains the given messages
157+
* @param messages
158+
*/
159+
async assertPinnedMessagesList(messages: string[]) {
160+
const rightPanel = this.getRightPanel();
161+
await expect(rightPanel.getByRole("heading", { name: "Pinned messages" })).toHaveText(
162+
`${messages.length} Pinned messages`,
163+
);
164+
await expect(rightPanel).toMatchScreenshot(`pinned-messages-list-messages-${messages.length}.png`);
165+
166+
const list = rightPanel.getByRole("list");
167+
await expect(list.getByRole("listitem")).toHaveCount(messages.length);
168+
169+
for (const message of messages) {
170+
await expect(list.getByText(message)).toBeVisible();
171+
}
172+
}
173+
174+
/**
175+
* Assert that the pinned message list is empty
176+
*/
177+
async assertEmptyPinnedMessagesList() {
178+
const rightPanel = this.getRightPanel();
179+
await expect(rightPanel).toMatchScreenshot(`pinned-messages-list-empty.png`);
180+
}
181+
182+
/**
183+
* Open the unpin all dialog
184+
*/
185+
async openUnpinAllDialog() {
186+
await this.openRoomInfo();
187+
await this.openPinnedMessagesList();
188+
await this.page.getByRole("button", { name: "Unpin all" }).click();
189+
}
190+
191+
/**
192+
* Return the unpin all dialog
193+
*/
194+
getUnpinAllDialog() {
195+
return this.page.locator(".mx_Dialog", { hasText: "Unpin all messages?" });
196+
}
197+
198+
/**
199+
* Click on the Continue button of the unoin all dialog
200+
*/
201+
async confirmUnpinAllDialog() {
202+
await this.getUnpinAllDialog().getByRole("button", { name: "Continue" }).click();
203+
}
204+
205+
/**
206+
* Go back from the pinned messages list
207+
*/
208+
async backPinnedMessagesList() {
209+
await this.page.locator("#mx_RightPanel").getByTestId("base-card-back-button").click();
210+
}
211+
212+
/**
213+
* Open the contextual menu of a message in the pin message list and click on unpin
214+
* @param message
215+
*/
216+
async unpinMessageFromMessageList(message: string) {
217+
const item = this.getRightPanel().getByRole("list").getByRole("listitem").filter({
218+
hasText: message,
219+
});
220+
221+
await item.getByRole("button").click();
222+
await this.page.getByRole("menu", { name: "Open menu" }).getByRole("menuitem", { name: "Unpin" }).click();
223+
}
224+
}
225+
226+
export { expect };
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,79 @@
1+
/*
2+
* Copyright 2024 The Matrix.org Foundation C.I.C.
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
17+
import { test } from "./index";
18+
import { expect } from "../../element-web-test";
19+
20+
test.describe("Pinned messages", () => {
21+
test.use({
22+
labsFlags: ["feature_pinning"],
23+
});
24+
25+
test("should show the empty state when there are no pinned messages", async ({ page, app, room1, util }) => {
26+
await util.goTo(room1);
27+
await util.openRoomInfo();
28+
await util.assertPinnedCountInRoomInfo(0);
29+
await util.openPinnedMessagesList();
30+
await util.assertEmptyPinnedMessagesList();
31+
});
32+
33+
test("should pin messages and show them in the room info panel", async ({ page, app, room1, util }) => {
34+
await util.goTo(room1);
35+
await util.receiveMessages(room1, ["Msg1", "Msg2", "Msg3", "Msg4"]);
36+
37+
await util.pinMessages(["Msg1", "Msg2", "Msg4"]);
38+
await util.openRoomInfo();
39+
await util.assertPinnedCountInRoomInfo(3);
40+
});
41+
42+
test("should pin messages and show them in the pinned message panel", async ({ page, app, room1, util }) => {
43+
await util.goTo(room1);
44+
await util.receiveMessages(room1, ["Msg1", "Msg2", "Msg3", "Msg4"]);
45+
46+
// Pin the messages
47+
await util.pinMessages(["Msg1", "Msg2", "Msg4"]);
48+
await util.openRoomInfo();
49+
await util.openPinnedMessagesList();
50+
await util.assertPinnedMessagesList(["Msg1", "Msg2", "Msg4"]);
51+
});
52+
53+
test("should unpin one message", async ({ page, app, room1, util }) => {
54+
await util.goTo(room1);
55+
await util.receiveMessages(room1, ["Msg1", "Msg2", "Msg3", "Msg4"]);
56+
await util.pinMessages(["Msg1", "Msg2", "Msg4"]);
57+
58+
await util.openRoomInfo();
59+
await util.openPinnedMessagesList();
60+
await util.unpinMessageFromMessageList("Msg2");
61+
await util.assertPinnedMessagesList(["Msg1", "Msg4"]);
62+
await util.backPinnedMessagesList();
63+
await util.assertPinnedCountInRoomInfo(2);
64+
});
65+
66+
test("should unpin all messages", async ({ page, app, room1, util }) => {
67+
await util.goTo(room1);
68+
await util.receiveMessages(room1, ["Msg1", "Msg2", "Msg3", "Msg4"]);
69+
await util.pinMessages(["Msg1", "Msg2", "Msg4"]);
70+
71+
await util.openUnpinAllDialog();
72+
await expect(util.getUnpinAllDialog()).toMatchScreenshot("unpin-all-dialog.png");
73+
await util.confirmUnpinAllDialog();
74+
75+
await util.assertEmptyPinnedMessagesList();
76+
await util.backPinnedMessagesList();
77+
await util.assertPinnedCountInRoomInfo(0);
78+
});
79+
});
Loading

0 commit comments

Comments
 (0)