ae3ec0ef86
It was trying to click the room before it had turned the invite filter off so it was relying on clicking it before the rescind made its way to the UI.
378 lines
16 KiB
TypeScript
378 lines
16 KiB
TypeScript
/*
|
|
Copyright 2024 New Vector Ltd.
|
|
Copyright 2023 The Matrix.org Foundation C.I.C.
|
|
|
|
SPDX-License-Identifier: AGPL-3.0-only OR GPL-3.0-only OR LicenseRef-Element-Commercial
|
|
Please see LICENSE files in the repository root for full details.
|
|
*/
|
|
|
|
import { type Locator, type Page, type Request } from "@playwright/test";
|
|
import { rejectToast } from "@element-hq/element-web-playwright-common";
|
|
|
|
import { test as base, expect } from "../../element-web-test";
|
|
import type { ElementAppPage } from "../../pages/ElementAppPage";
|
|
import type { Bot } from "../../pages/bot";
|
|
|
|
const test = base.extend<{
|
|
testRoom: { roomId: string; name: string };
|
|
joinedBot: Bot;
|
|
}>({
|
|
testRoom: async ({ user, app }, use) => {
|
|
const name = "Test Room";
|
|
const roomId = await app.client.createRoom({ name });
|
|
await use({ roomId, name });
|
|
},
|
|
joinedBot: async ({ app, bot, testRoom }, use) => {
|
|
const roomId = testRoom.roomId;
|
|
await bot.prepareClient();
|
|
const bobUserId = await bot.evaluate((client) => client.getUserId());
|
|
await app.client.evaluate(
|
|
async (client, { bobUserId, roomId }) => {
|
|
await client.invite(roomId, bobUserId);
|
|
},
|
|
{ bobUserId, roomId },
|
|
);
|
|
await bot.joinRoom(roomId);
|
|
await use(bot);
|
|
},
|
|
});
|
|
|
|
test.describe("Sliding Sync", () => {
|
|
const checkOrder = async (wantOrder: string[], page: Page) => {
|
|
await expect(page.getByTestId("room-list").getByTestId("room-name")).toHaveText(wantOrder);
|
|
};
|
|
|
|
const bumpRoom = async (roomId: string, app: ElementAppPage) => {
|
|
// Send a message into the given room, this should bump the room to the top
|
|
console.log("sendEvent", app.client.sendEvent);
|
|
await app.client.sendEvent(roomId, null, "m.room.message", {
|
|
body: "Hello world",
|
|
msgtype: "m.text",
|
|
});
|
|
};
|
|
|
|
function getPrimaryFilters(page: Page): Locator {
|
|
return page.getByTestId("primary-filters");
|
|
}
|
|
|
|
function getRoomOptionsMenu(page: Page): Locator {
|
|
return page.getByRole("button", { name: "Room Options" });
|
|
}
|
|
|
|
function getFilterExpandButton(page: Page): Locator {
|
|
return getPrimaryFilters(page).getByRole("button", { name: "Expand filter list" });
|
|
}
|
|
|
|
test.use({
|
|
config: {
|
|
features: {
|
|
feature_simplified_sliding_sync: true,
|
|
},
|
|
},
|
|
});
|
|
|
|
// Load the user fixture for all tests
|
|
test.beforeEach(async ({ app, page, user }) => {
|
|
await rejectToast(page, "Verify this device");
|
|
await rejectToast(page, "Notifications");
|
|
});
|
|
|
|
test("should render the Rooms list in reverse chronological order by default and allowing sorting A-Z", async ({
|
|
page,
|
|
app,
|
|
testRoom,
|
|
}) => {
|
|
// create rooms and check room names are correct
|
|
for (const fruit of ["Apple", "Pineapple", "Orange"]) {
|
|
await app.client.createRoom({ name: fruit });
|
|
await expect(page.getByRole("option", { name: `Open room ${fruit}` })).toBeVisible();
|
|
}
|
|
const roomList = page.getByTestId("room-list");
|
|
// Check count, 3 fruits + 1 testRoom = 4
|
|
await expect(roomList.getByRole("option")).toHaveCount(4);
|
|
await checkOrder(["Orange", "Pineapple", "Apple", "Test Room"], page);
|
|
|
|
await getRoomOptionsMenu(page).click();
|
|
await page.getByRole("menuitemradio", { name: "A-Z" }).click();
|
|
|
|
await checkOrder(["Apple", "Orange", "Pineapple", "Test Room"], page);
|
|
});
|
|
|
|
test("should move rooms around as new events arrive", async ({ page, app, testRoom }) => {
|
|
// create rooms and check room names are correct
|
|
const roomIds: string[] = [];
|
|
for (const fruit of ["Apple", "Pineapple", "Orange"]) {
|
|
const id = await app.client.createRoom({ name: fruit });
|
|
roomIds.push(id);
|
|
await expect(page.getByRole("option", { name: `Open room ${fruit}` })).toBeVisible();
|
|
}
|
|
|
|
// Select the Test Room
|
|
await page.getByRole("option", { name: "Open room Test Room" }).click();
|
|
const [apple, pineapple, orange] = roomIds;
|
|
await checkOrder(["Orange", "Pineapple", "Apple", "Test Room"], page);
|
|
await bumpRoom(apple, app);
|
|
await checkOrder(["Apple", "Orange", "Pineapple", "Test Room"], page);
|
|
await bumpRoom(orange, app);
|
|
await checkOrder(["Orange", "Apple", "Pineapple", "Test Room"], page);
|
|
await bumpRoom(orange, app);
|
|
await checkOrder(["Orange", "Apple", "Pineapple", "Test Room"], page);
|
|
await bumpRoom(pineapple, app);
|
|
await checkOrder(["Pineapple", "Orange", "Apple", "Test Room"], page);
|
|
});
|
|
|
|
test("should not move the selected room: it should be sticky", async ({ page, app, testRoom }) => {
|
|
// create rooms and check room names are correct
|
|
const roomIds: string[] = [];
|
|
for (const fruit of ["Apple", "Pineapple", "Orange"]) {
|
|
const id = await app.client.createRoom({ name: fruit });
|
|
roomIds.push(id);
|
|
await expect(page.getByRole("option", { name: `Open room ${fruit}` })).toBeVisible();
|
|
}
|
|
|
|
// Given a list of Orange, Pineapple, Apple - if Pineapple is active and a message is sent in Apple, the list should
|
|
// turn into Apple, Pineapple, Orange - the index position of Pineapple never changes even though the list should technically
|
|
// be Apple, Orange Pineapple - only when you click on a different room do things reshuffle.
|
|
|
|
// Select the Pineapple room
|
|
await page.getByRole("option", { name: "Open room Pineapple" }).click();
|
|
await checkOrder(["Orange", "Pineapple", "Apple", "Test Room"], page);
|
|
|
|
// Move Apple
|
|
await bumpRoom(roomIds[0], app);
|
|
await checkOrder(["Apple", "Pineapple", "Orange", "Test Room"], page);
|
|
|
|
// Select the Test Room
|
|
await page.getByRole("option", { name: "Open room Test Room" }).click();
|
|
|
|
// the rooms reshuffle to match reality
|
|
await checkOrder(["Apple", "Orange", "Pineapple", "Test Room"], page);
|
|
});
|
|
|
|
test.skip("should show the right unread notifications", async ({ page, user, joinedBot: bob, testRoom }) => {
|
|
// send a message in the test room: unread notification count should increment
|
|
await bob.sendMessage(testRoom.roomId, "Hello World");
|
|
|
|
const itemLocator1 = page.getByRole("option", { name: "Open room David Langley with 1 unread message." });
|
|
await expect(itemLocator1.getByTestId("notification-decoration")).toHaveText("1");
|
|
|
|
// send an @mention: highlight count (red) should be 2.
|
|
await bob.sendMessage(testRoom.roomId, `Hello ${user.displayName}`);
|
|
const itemLocator2 = page.getByRole("treeitem", {
|
|
name: "Open room Test Room 2 unread messages including mentions.",
|
|
});
|
|
await expect(itemLocator2.getByTestId("notification-decoration")).toHaveText("2");
|
|
|
|
// click on the room, the notif counts should disappear
|
|
await page.getByRole("option", { name: "Open room Test Room 2 unread messages including mentions." }).click();
|
|
await expect(
|
|
page.getByRole("option", { name: "Open room Test Room" }).getByTestId("notification-decoration"),
|
|
).not.toBeAttached();
|
|
});
|
|
|
|
test("should show unread indicators", async ({ page, app, joinedBot: bot, testRoom }) => {
|
|
// create a new room so we know when the message has been received as it'll re-shuffle the room list
|
|
await app.client.createRoom({ name: "Dummy" });
|
|
|
|
await checkOrder(["Dummy", "Test Room"], page);
|
|
|
|
await bot.sendMessage(testRoom.roomId, "Do you read me?");
|
|
|
|
// wait for this message to arrive, tell by the room list resorting
|
|
await checkOrder(["Test Room", "Dummy"], page);
|
|
|
|
await expect(
|
|
page.getByRole("option", { name: "Open room Test Room" }).getByTestId("notification-decoration"),
|
|
).toBeAttached();
|
|
});
|
|
|
|
test("should update user settings promptly", async ({ page, app }) => {
|
|
await app.settings.openUserSettings("Preferences");
|
|
const locator = page.getByRole("switch", { name: "Show timestamps in 12 hour format" });
|
|
await expect(locator).toBeVisible();
|
|
await locator.check();
|
|
});
|
|
|
|
test("should send subscribe_rooms on room switch if room not already subscribed", async ({ page, app }) => {
|
|
// create rooms and check room names are correct
|
|
const roomIds: string[] = [];
|
|
for (const fruit of ["Apple", "Pineapple", "Orange"]) {
|
|
const id = await app.client.createRoom({ name: fruit });
|
|
roomIds.push(id);
|
|
await expect(page.getByRole("option", { name: `Open room ${fruit}` })).toBeVisible();
|
|
}
|
|
const [roomAId, roomPId] = roomIds;
|
|
|
|
const matchRoomSubRequest = (subRoomId: string) => (request: Request) => {
|
|
if (!request.url().includes("/sync")) return false;
|
|
const body = request.postDataJSON();
|
|
return body.room_subscriptions?.[subRoomId];
|
|
};
|
|
|
|
// Select the Test Room and wait for playwright to get the request
|
|
const [request] = await Promise.all([
|
|
page.waitForRequest(matchRoomSubRequest(roomAId)),
|
|
page.getByRole("option", { name: "Open room Apple", exact: true }).click(),
|
|
]);
|
|
const roomSubscriptions = request.postDataJSON().room_subscriptions;
|
|
expect(roomSubscriptions, "room_subscriptions is object").toBeDefined();
|
|
|
|
// Switch to another room and wait for playwright to get the request
|
|
await Promise.all([
|
|
page.waitForRequest(matchRoomSubRequest(roomPId)),
|
|
page.getByRole("option", { name: "Open room Pineapple", exact: true }).click(),
|
|
]);
|
|
});
|
|
|
|
test("should show and be able to accept/reject/rescind invites", async ({
|
|
page,
|
|
app,
|
|
joinedBot: bot,
|
|
testRoom,
|
|
}) => {
|
|
const clientUserId = await app.client.evaluate((client) => client.getUserId());
|
|
|
|
// invite bot into 3 rooms:
|
|
// - roomJoin: will join this room
|
|
// - roomReject: will reject the invite
|
|
// - roomRescind: will make Bob rescind the invite
|
|
const roomNames = ["Room to Join", "Room to Reject", "Room to Rescind"];
|
|
const roomRescind = await bot.evaluate(
|
|
async (client, { roomNames, clientUserId }) => {
|
|
const rooms = await Promise.all(roomNames.map((name) => client.createRoom({ name })));
|
|
await Promise.all(rooms.map((room) => client.invite(room.room_id, clientUserId)));
|
|
return rooms[2].room_id;
|
|
},
|
|
{ roomNames, clientUserId },
|
|
);
|
|
|
|
await getFilterExpandButton(page).click();
|
|
const primaryFilters = getPrimaryFilters(page);
|
|
await primaryFilters.getByRole("option", { name: "Invites" }).click();
|
|
|
|
await expect(page.getByTestId("room-list").getByRole("option")).toHaveCount(3);
|
|
|
|
// Select the room to join
|
|
await page.getByRole("option", { name: "Open room Room to Join" }).click();
|
|
|
|
// Accept the invite
|
|
await page.locator(".mx_RoomView").getByRole("button", { name: "Accept" }).click();
|
|
|
|
await checkOrder(["Room to Rescind", "Room to Reject"], page);
|
|
|
|
// Select the room to reject
|
|
await page.getByRole("option", { name: "Open room Room to Reject" }).click();
|
|
|
|
// Decline the invite
|
|
await page.locator(".mx_RoomView").getByRole("button", { name: "Decline", exact: true }).click();
|
|
|
|
await expect(page.getByTestId("room-list").getByRole("option")).toHaveCount(1);
|
|
|
|
await expect(page.getByRole("option", { name: "Open room Room to Rescind" })).toBeVisible();
|
|
|
|
// now rescind the invite
|
|
await bot.evaluate(
|
|
async (client, { roomRescind, clientUserId }) => {
|
|
await client.kick(roomRescind, clientUserId);
|
|
},
|
|
{ roomRescind, clientUserId },
|
|
);
|
|
|
|
// toggle the invites filter off again so we see all the rooms again
|
|
await primaryFilters.getByRole("option", { name: "Invites" }).click();
|
|
|
|
await page.getByRole("option", { name: "Open room Room to Rescind" }).click();
|
|
|
|
await page.locator(".mx_RoomView").getByRole("button", { name: "Forget this room", exact: true }).click();
|
|
|
|
// Wait for the rescind to take effect and check the joined list once more
|
|
await expect(page.getByTestId("room-list").getByRole("option")).toHaveCount(2);
|
|
|
|
await checkOrder(["Room to Join", "Test Room"], page);
|
|
});
|
|
|
|
test("should show a favourite DM only in the favourite sublist", async ({ page, app }) => {
|
|
const roomId = await app.client.createRoom({
|
|
name: "Favourite DM",
|
|
is_direct: true,
|
|
});
|
|
await app.client.evaluate(async (client, roomId) => {
|
|
await client.setRoomTag(roomId, "m.favourite", { order: 0.5 });
|
|
}, roomId);
|
|
|
|
await getFilterExpandButton(page).click();
|
|
const primaryFilters = getPrimaryFilters(page);
|
|
await primaryFilters.getByRole("option", { name: "Favourites" }).click();
|
|
|
|
await expect(page.getByRole("option", { name: "Favourite DM" })).toBeVisible();
|
|
|
|
await primaryFilters.getByRole("option", { name: "People" }).click();
|
|
|
|
await expect(page.getByRole("option", { name: "Favourite DM" })).not.toBeAttached();
|
|
});
|
|
|
|
// Regression test for a bug in SS mode, but would be useful to have in non-SS mode too.
|
|
// This ensures we are setting RoomViewStore state correctly.
|
|
test("should clear the reply to field when swapping rooms", async ({ page, app, testRoom }) => {
|
|
await app.client.createRoom({ name: "Other Room" });
|
|
await expect(page.getByRole("option", { name: "Open room Other Room" })).toBeVisible();
|
|
await app.client.sendMessage(testRoom.roomId, "Hello world");
|
|
|
|
// select the room
|
|
await page.getByRole("option", { name: "Open room Test Room" }).click();
|
|
|
|
await expect(page.locator(".mx_ReplyPreview")).not.toBeAttached();
|
|
|
|
// click reply-to on the Hello World message
|
|
const locator = page.locator(".mx_EventTile_last");
|
|
await locator.getByText("Hello world").hover();
|
|
await locator.getByRole("button", { name: "Reply", exact: true }).click({});
|
|
|
|
// check it's visible
|
|
await expect(page.locator(".mx_ReplyPreview")).toBeVisible();
|
|
|
|
// now click Other Room
|
|
await page.getByRole("option", { name: "Open room Other Room" }).click();
|
|
|
|
// ensure the reply-to disappears
|
|
await expect(page.locator(".mx_ReplyPreview")).not.toBeAttached();
|
|
|
|
// click back
|
|
await page.getByRole("option", { name: "Open room Test Room" }).click();
|
|
|
|
// ensure the reply-to reappears
|
|
await expect(page.locator(".mx_ReplyPreview")).toBeVisible();
|
|
});
|
|
|
|
// Regression test for https://github.com/vector-im/element-web/issues/21462
|
|
test("should not cancel replies when permalinks are clicked", async ({ page, app, testRoom }) => {
|
|
// we require a first message as you cannot click the permalink text with the avatar in the way
|
|
await app.client.sendMessage(testRoom.roomId, "First message");
|
|
await app.client.sendMessage(testRoom.roomId, "Permalink me");
|
|
await app.client.sendMessage(testRoom.roomId, "Reply to me");
|
|
|
|
// select the room
|
|
await page.getByRole("option", { name: "Open room Test Room" }).click();
|
|
await expect(page.locator(".mx_ReplyPreview")).not.toBeAttached();
|
|
|
|
// click reply-to on the Reply to me message
|
|
const locator = page.locator(".mx_EventTile").last();
|
|
await locator.getByText("Reply to me").hover();
|
|
await locator.getByRole("button", { name: "Reply", exact: true }).click();
|
|
|
|
// check it's visible
|
|
await expect(page.locator(".mx_ReplyPreview")).toBeVisible();
|
|
|
|
// now click on the permalink for Permalink me
|
|
const tile = page.locator(".mx_EventTile").filter({ hasText: "Permalink me" });
|
|
await tile.hover();
|
|
await tile.locator("a").dispatchEvent("click");
|
|
|
|
// make sure it is now selected with the little green |
|
|
await expect(page.locator(".mx_EventTile_selected").filter({ hasText: "Permalink me" })).toBeVisible();
|
|
|
|
// ensure the reply-to does not disappear
|
|
await expect(page.locator(".mx_ReplyPreview")).toBeVisible();
|
|
});
|
|
});
|