Deflake spotlight when homeserver is reused

Signed-off-by: Michael Telatynski <7t3chguy@gmail.com>
This commit is contained in:
Michael Telatynski
2025-01-10 11:38:12 +00:00
parent 3eeb2216f9
commit b7b650e0fc

View File

@@ -6,8 +6,8 @@ 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 type { AccountDataEvents } from "matrix-js-sdk/src/matrix"; import type { AccountDataEvents, Visibility } from "matrix-js-sdk/src/matrix";
import { test, expect } from "../../element-web-test"; import { test as base, expect } from "../../element-web-test";
import { Filter } from "../../pages/Spotlight"; import { Filter } from "../../pages/Spotlight";
import { Bot } from "../../pages/bot"; import { Bot } from "../../pages/bot";
import type { Locator, Page } from "@playwright/test"; import type { Locator, Page } from "@playwright/test";
@@ -38,41 +38,37 @@ async function startDM(app: ElementAppPage, page: Page, name: string): Promise<v
await expect(page.getByRole("group", { name: "People" }).getByText(name)).toBeAttached(); await expect(page.getByRole("group", { name: "People" }).getByText(name)).toBeAttached();
} }
test.describe("Spotlight", () => { type RoomRef = { name: string; roomId: string };
const bot1Name = "BotBob"; const test = base.extend<{
let bot1: Bot; bot1: Bot;
bot2: Bot;
const bot2Name = "ByteBot"; room1: RoomRef;
let bot2: Bot; room2: RoomRef;
room3: RoomRef;
const room1Name = "247"; }>({
let room1Id: string; bot1: async ({ page, homeserver }, use, testInfo) => {
const bot = new Bot(page, homeserver, { displayName: `BotBob_${testInfo.testId}`, autoAcceptInvites: true });
const room2Name = "Lounge"; await use(bot);
let room2Id: string; },
bot2: async ({ page, homeserver }, use, testInfo) => {
const room3Name = "Public"; const bot = new Bot(page, homeserver, { displayName: `ByteBot_${testInfo.testId}`, autoAcceptInvites: true });
let room3Id: string; await use(bot);
},
test.use({ room1: async ({ app }, use) => {
displayName: "Jim", const name = "247";
}); const roomId = await app.client.createRoom({ name, visibility: "public" as Visibility });
await use({ name, roomId });
test.beforeEach(async ({ page, homeserver, app, user }) => { },
bot1 = new Bot(page, homeserver, { displayName: bot1Name, autoAcceptInvites: true }); room2: async ({ bot2 }, use) => {
bot2 = new Bot(page, homeserver, { displayName: bot2Name, autoAcceptInvites: true }); const name = "Lounge";
const Visibility = await page.evaluate(() => (window as any).matrixcs.Visibility); const roomId = await bot2.createRoom({ name, visibility: "public" as Visibility });
await use({ name, roomId });
room1Id = await app.client.createRoom({ name: room1Name, visibility: Visibility.Public }); },
room3: async ({ bot2 }, use) => {
await bot1.joinRoom(room1Id); const name = "Public";
const bot1UserId = await bot1.evaluate((client) => client.getUserId()); const roomId = await bot2.createRoom({
room2Id = await bot2.createRoom({ name: room2Name, visibility: Visibility.Public }); name,
await bot2.inviteUser(room2Id, bot1UserId); visibility: "public" as Visibility,
room3Id = await bot2.createRoom({
name: room3Name,
visibility: Visibility.Public,
initial_state: [ initial_state: [
{ {
type: "m.room.history_visibility", type: "m.room.history_visibility",
@@ -83,9 +79,21 @@ test.describe("Spotlight", () => {
}, },
], ],
}); });
await bot2.inviteUser(room3Id, bot1UserId); await use({ name, roomId });
},
});
await page.goto("/#/room/" + room1Id); test.describe("Spotlight", () => {
test.use({
displayName: "Jim",
});
test.beforeEach(async ({ page, user, bot1, bot2, room1, room2, room3 }) => {
await bot1.joinRoom(room1.roomId);
await bot2.inviteUser(room2.roomId, bot1.credentials.userId);
await bot2.inviteUser(room3.roomId, bot1.credentials.userId);
await page.goto(`/#/room/${room1.roomId}`);
await expect(page.locator(".mx_RoomSublist_skeletonUI")).not.toBeAttached(); await expect(page.locator(".mx_RoomSublist_skeletonUI")).not.toBeAttached();
}); });
@@ -117,69 +125,69 @@ test.describe("Spotlight", () => {
await expect(spotlight.dialog.locator(".mx_SpotlightDialog_filter")).not.toBeAttached(); await expect(spotlight.dialog.locator(".mx_SpotlightDialog_filter")).not.toBeAttached();
}); });
test("should find joined rooms", async ({ page, app }) => { test("should find joined rooms", async ({ page, app, room1 }) => {
const spotlight = await app.openSpotlight(); const spotlight = await app.openSpotlight();
await page.waitForTimeout(500); // wait for the dialog to settle await page.waitForTimeout(500); // wait for the dialog to settle
await spotlight.search(room1Name); await spotlight.search(room1.name);
const resultLocator = spotlight.results; const resultLocator = spotlight.results;
await expect(resultLocator).toHaveCount(1); await expect(resultLocator).toHaveCount(1);
await expect(resultLocator.first()).toContainText(room1Name); await expect(resultLocator.first()).toContainText(room1.name);
await resultLocator.first().click(); await resultLocator.first().click();
await expect(page).toHaveURL(new RegExp(`#/room/${room1Id}`)); await expect(page).toHaveURL(new RegExp(`#/room/${room1.roomId}`));
await expect(roomHeaderName(page)).toContainText(room1Name); await expect(roomHeaderName(page)).toContainText(room1.name);
}); });
test("should find known public rooms", async ({ page, app }) => { test("should find known public rooms", async ({ page, app, room1 }) => {
const spotlight = await app.openSpotlight(); const spotlight = await app.openSpotlight();
await page.waitForTimeout(500); // wait for the dialog to settle await page.waitForTimeout(500); // wait for the dialog to settle
await spotlight.filter(Filter.PublicRooms); await spotlight.filter(Filter.PublicRooms);
await spotlight.search(room1Name); await spotlight.search(room1.name);
const resultLocator = spotlight.results; const resultLocator = spotlight.results;
await expect(resultLocator).toHaveCount(1); await expect(resultLocator).toHaveCount(1);
await expect(resultLocator.first()).toContainText(room1Name); await expect(resultLocator.first()).toContainText(room1.name);
await expect(resultLocator.first()).toContainText("View"); await expect(resultLocator.first()).toContainText("View");
await resultLocator.first().click(); await resultLocator.first().click();
await expect(page).toHaveURL(new RegExp(`#/room/${room1Id}`)); await expect(page).toHaveURL(new RegExp(`#/room/${room1.roomId}`));
await expect(roomHeaderName(page)).toContainText(room1Name); await expect(roomHeaderName(page)).toContainText(room1.name);
}); });
test("should find unknown public rooms", async ({ page, app }) => { test("should find unknown public rooms", async ({ page, app, room2 }) => {
const spotlight = await app.openSpotlight(); const spotlight = await app.openSpotlight();
await page.waitForTimeout(500); // wait for the dialog to settle await page.waitForTimeout(500); // wait for the dialog to settle
await spotlight.filter(Filter.PublicRooms); await spotlight.filter(Filter.PublicRooms);
await spotlight.search(room2Name); await spotlight.search(room2.name);
const resultLocator = spotlight.results; const resultLocator = spotlight.results;
await expect(resultLocator).toHaveCount(1); await expect(resultLocator).toHaveCount(1);
await expect(resultLocator.first()).toContainText(room2Name); await expect(resultLocator.first()).toContainText(room2.name);
await expect(resultLocator.first()).toContainText("Join"); await expect(resultLocator.first()).toContainText("Join");
await resultLocator.first().click(); await resultLocator.first().click();
await expect(page).toHaveURL(new RegExp(`#/room/${room2Id}`)); await expect(page).toHaveURL(new RegExp(`#/room/${room2.roomId}`));
await expect(page.locator(".mx_RoomView_MessageList")).toHaveCount(1); await expect(page.locator(".mx_RoomView_MessageList")).toHaveCount(1);
await expect(roomHeaderName(page)).toContainText(room2Name); await expect(roomHeaderName(page)).toContainText(room2.name);
}); });
test("should find unknown public world readable rooms", async ({ page, app }) => { test("should find unknown public world readable rooms", async ({ page, app, room3 }) => {
const spotlight = await app.openSpotlight(); const spotlight = await app.openSpotlight();
await page.waitForTimeout(500); // wait for the dialog to settle await page.waitForTimeout(500); // wait for the dialog to settle
await spotlight.filter(Filter.PublicRooms); await spotlight.filter(Filter.PublicRooms);
await spotlight.search(room3Name); await spotlight.search(room3.name);
const resultLocator = spotlight.results; const resultLocator = spotlight.results;
await expect(resultLocator).toHaveCount(1); await expect(resultLocator).toHaveCount(1);
await expect(resultLocator.first()).toContainText(room3Name); await expect(resultLocator.first()).toContainText(room3.name);
await expect(resultLocator.first()).toContainText("View"); await expect(resultLocator.first()).toContainText("View");
await resultLocator.first().click(); await resultLocator.first().click();
await expect(page).toHaveURL(new RegExp(`#/room/${room3Id}`)); await expect(page).toHaveURL(new RegExp(`#/room/${room3.roomId}`));
await page.getByRole("button", { name: "Join the discussion" }).click(); await page.getByRole("button", { name: "Join the discussion" }).click();
await expect(roomHeaderName(page)).toHaveText(room3Name); await expect(roomHeaderName(page)).toHaveText(room3.name);
}); });
// TODO: We currently cant test finding rooms on other homeservers/other protocols // TODO: We currently cant test finding rooms on other homeservers/other protocols
// We obviously dont have federation or bridges in local e2e tests // We obviously dont have federation or bridges in local e2e tests
test.skip("should find unknown public rooms on other homeservers", async ({ page, app }) => { test.skip("should find unknown public rooms on other homeservers", async ({ page, app, room3 }) => {
const spotlight = await app.openSpotlight(); const spotlight = await app.openSpotlight();
await page.waitForTimeout(500); // wait for the dialog to settle await page.waitForTimeout(500); // wait for the dialog to settle
await spotlight.filter(Filter.PublicRooms); await spotlight.filter(Filter.PublicRooms);
await spotlight.search(room3Name); await spotlight.search(room3.name);
await page.locator("[aria-haspopup=true][role=button]").click(); await page.locator("[aria-haspopup=true][role=button]").click();
await page await page
@@ -194,20 +202,20 @@ test.describe("Spotlight", () => {
const resultLocator = spotlight.results; const resultLocator = spotlight.results;
await expect(resultLocator).toHaveCount(1); await expect(resultLocator).toHaveCount(1);
await expect(resultLocator.first()).toContainText(room3Name); await expect(resultLocator.first()).toContainText(room3.name);
await expect(resultLocator.first()).toContainText(room3Id); await expect(resultLocator.first()).toContainText(room3.roomId);
}); });
test("should find known people", async ({ page, app }) => { test("should find known people", async ({ page, app, bot1 }) => {
const spotlight = await app.openSpotlight(); const spotlight = await app.openSpotlight();
await page.waitForTimeout(500); // wait for the dialog to settle await page.waitForTimeout(500); // wait for the dialog to settle
await spotlight.filter(Filter.People); await spotlight.filter(Filter.People);
await spotlight.search(bot1Name); await spotlight.search(bot1.credentials.displayName);
const resultLocator = spotlight.results; const resultLocator = spotlight.results;
await expect(resultLocator).toHaveCount(1); await expect(resultLocator).toHaveCount(1);
await expect(resultLocator.first()).toContainText(bot1Name); await expect(resultLocator.first()).toContainText(bot1.credentials.displayName);
await resultLocator.first().click(); await resultLocator.first().click();
await expect(roomHeaderName(page)).toHaveText(bot1Name); await expect(roomHeaderName(page)).toHaveText(bot1.credentials.displayName);
}); });
/** /**
@@ -217,42 +225,41 @@ test.describe("Spotlight", () => {
* *
* https://github.com/matrix-org/synapse/issues/16472 * https://github.com/matrix-org/synapse/issues/16472
*/ */
test("should find unknown people", async ({ page, app }) => { test("should find unknown people", async ({ page, app, bot2 }) => {
const spotlight = await app.openSpotlight(); const spotlight = await app.openSpotlight();
await page.waitForTimeout(500); // wait for the dialog to settle await page.waitForTimeout(500); // wait for the dialog to settle
await spotlight.filter(Filter.People); await spotlight.filter(Filter.People);
await spotlight.search(bot2Name); await spotlight.search(bot2.credentials.displayName);
const resultLocator = spotlight.results; const resultLocator = spotlight.results;
await expect(resultLocator).toHaveCount(1); await expect(resultLocator).toHaveCount(1);
await expect(resultLocator.first()).toContainText(bot2Name); await expect(resultLocator.first()).toContainText(bot2.credentials.displayName);
await resultLocator.first().click(); await resultLocator.first().click();
await expect(roomHeaderName(page)).toHaveText(bot2Name); await expect(roomHeaderName(page)).toHaveText(bot2.credentials.displayName);
}); });
test("should find group DMs by usernames or user ids", async ({ page, app }) => { test("should find group DMs by usernames or user ids", async ({ page, app, bot1, bot2, room1 }) => {
// First we want to share a room with both bots to ensure weve got their usernames cached // First we want to share a room with both bots to ensure weve got their usernames cached
const bot2UserId = await bot2.evaluate((client) => client.getUserId()); await app.client.inviteUser(room1.roomId, bot2.credentials.userId);
await app.client.inviteUser(room1Id, bot2UserId);
// Starting a DM with ByteBot (will be turned into a group dm later) // Starting a DM with ByteBot (will be turned into a group dm later)
let spotlight = await app.openSpotlight(); let spotlight = await app.openSpotlight();
await page.waitForTimeout(500); // wait for the dialog to settle await page.waitForTimeout(500); // wait for the dialog to settle
await spotlight.filter(Filter.People); await spotlight.filter(Filter.People);
await spotlight.search(bot2Name); await spotlight.search(bot2.credentials.displayName);
let resultLocator = spotlight.results; let resultLocator = spotlight.results;
await expect(resultLocator).toHaveCount(1); await expect(resultLocator).toHaveCount(1);
await expect(resultLocator.first()).toContainText(bot2Name); await expect(resultLocator.first()).toContainText(bot2.credentials.displayName);
await resultLocator.first().click(); await resultLocator.first().click();
// Send first message to actually start DM // Send first message to actually start DM
await expect(roomHeaderName(page)).toHaveText(bot2Name); await expect(roomHeaderName(page)).toHaveText(bot2.credentials.displayName);
const locator = page.getByRole("textbox", { name: "Send a message…" }); const locator = page.getByRole("textbox", { name: "Send a message…" });
await locator.fill("Hey!"); await locator.fill("Hey!");
await locator.press("Enter"); await locator.press("Enter");
// Assert DM exists by checking for the first message and the room being in the room list // Assert DM exists by checking for the first message and the room being in the room list
await expect(page.locator(".mx_EventTile_body").filter({ hasText: "Hey!" })).toBeAttached({ timeout: 3000 }); await expect(page.locator(".mx_EventTile_body").filter({ hasText: "Hey!" })).toBeAttached({ timeout: 3000 });
await expect(page.getByRole("group", { name: "People" })).toContainText(bot2Name); await expect(page.getByRole("group", { name: "People" })).toContainText(bot2.credentials.displayName);
// Invite BotBob into existing DM with ByteBot // Invite BotBob into existing DM with ByteBot
const dmRooms = await app.client.evaluate((client, userId) => { const dmRooms = await app.client.evaluate((client, userId) => {
@@ -260,18 +267,17 @@ test.describe("Spotlight", () => {
.getAccountData("m.direct" as keyof AccountDataEvents) .getAccountData("m.direct" as keyof AccountDataEvents)
?.getContent<Record<string, string[]>>(); ?.getContent<Record<string, string[]>>();
return map[userId] ?? []; return map[userId] ?? [];
}, bot2UserId); }, bot2.credentials.userId);
expect(dmRooms).toHaveLength(1); expect(dmRooms).toHaveLength(1);
const groupDmName = await app.client.evaluate((client, id) => client.getRoom(id).name, dmRooms[0]); const groupDmName = await app.client.evaluate((client, id) => client.getRoom(id).name, dmRooms[0]);
const bot1UserId = await bot1.evaluate((client) => client.getUserId()); await app.client.inviteUser(dmRooms[0], bot1.credentials.userId);
await app.client.inviteUser(dmRooms[0], bot1UserId);
await expect(roomHeaderName(page).first()).toContainText(groupDmName); await expect(roomHeaderName(page).first()).toContainText(groupDmName);
await expect(page.getByRole("group", { name: "People" }).first()).toContainText(groupDmName); await expect(page.getByRole("group", { name: "People" }).first()).toContainText(groupDmName);
// Search for BotBob by id, should return group DM and user // Search for BotBob by id, should return group DM and user
spotlight = await app.openSpotlight(); spotlight = await app.openSpotlight();
await spotlight.filter(Filter.People); await spotlight.filter(Filter.People);
await spotlight.search(bot1UserId); await spotlight.search(bot1.credentials.userId);
await page.waitForTimeout(1000); // wait for the dialog to settle await page.waitForTimeout(1000); // wait for the dialog to settle
resultLocator = spotlight.results; resultLocator = spotlight.results;
await expect(resultLocator).toHaveCount(2); await expect(resultLocator).toHaveCount(2);
@@ -284,7 +290,7 @@ test.describe("Spotlight", () => {
// Search for ByteBot by id, should return group DM and user // Search for ByteBot by id, should return group DM and user
spotlight = await app.openSpotlight(); spotlight = await app.openSpotlight();
await spotlight.filter(Filter.People); await spotlight.filter(Filter.People);
await spotlight.search(bot2UserId); await spotlight.search(bot2.credentials.userId);
await page.waitForTimeout(1000); // wait for the dialog to settle await page.waitForTimeout(1000); // wait for the dialog to settle
resultLocator = spotlight.results; resultLocator = spotlight.results;
await expect(resultLocator).toHaveCount(2); await expect(resultLocator).toHaveCount(2);
@@ -297,11 +303,10 @@ test.describe("Spotlight", () => {
}); });
// Test against https://github.com/vector-im/element-web/issues/22851 // Test against https://github.com/vector-im/element-web/issues/22851
test("should show each person result only once", async ({ page, app }) => { test("should show each person result only once", async ({ page, app, bot1 }) => {
const spotlight = await app.openSpotlight(); const spotlight = await app.openSpotlight();
await page.waitForTimeout(500); // wait for the dialog to settle await page.waitForTimeout(500); // wait for the dialog to settle
await spotlight.filter(Filter.People); await spotlight.filter(Filter.People);
const bot1UserId = await bot1.evaluate((client) => client.getUserId());
// 2 rounds of search to simulate the bug conditions. Specifically, the first search // 2 rounds of search to simulate the bug conditions. Specifically, the first search
// should have 1 result (not 2) and the second search should also have 1 result (instead // should have 1 result (not 2) and the second search should also have 1 result (instead
@@ -310,24 +315,24 @@ test.describe("Spotlight", () => {
// We search for user ID to trigger the profile lookup within the dialog. // We search for user ID to trigger the profile lookup within the dialog.
for (let i = 0; i < 2; i++) { for (let i = 0; i < 2; i++) {
console.log("Iteration: " + i); console.log("Iteration: " + i);
await spotlight.search(bot1UserId); await spotlight.search(bot1.credentials.userId);
await page.waitForTimeout(1000); // wait for the dialog to settle await page.waitForTimeout(1000); // wait for the dialog to settle
const resultLocator = spotlight.results; const resultLocator = spotlight.results;
await expect(resultLocator).toHaveCount(1); await expect(resultLocator).toHaveCount(1);
await expect(resultLocator.first()).toContainText(bot1UserId); await expect(resultLocator.first()).toContainText(bot1.credentials.userId);
} }
}); });
test("should allow opening group chat dialog", async ({ page, app }) => { test("should allow opening group chat dialog", async ({ page, app, bot2 }) => {
const spotlight = await app.openSpotlight(); const spotlight = await app.openSpotlight();
await page.waitForTimeout(500); // wait for the dialog to settle await page.waitForTimeout(500); // wait for the dialog to settle
await spotlight.filter(Filter.People); await spotlight.filter(Filter.People);
await spotlight.search(bot2Name); await spotlight.search(bot2.credentials.displayName);
await page.waitForTimeout(3000); // wait for the dialog to settle await page.waitForTimeout(3000); // wait for the dialog to settle
const resultLocator = spotlight.results; const resultLocator = spotlight.results;
await expect(resultLocator).toHaveCount(1); await expect(resultLocator).toHaveCount(1);
await expect(resultLocator.first()).toContainText(bot2Name); await expect(resultLocator.first()).toContainText(bot2.credentials.displayName);
await expect(spotlight.dialog.locator(".mx_SpotlightDialog_startGroupChat")).toContainText( await expect(spotlight.dialog.locator(".mx_SpotlightDialog_startGroupChat")).toContainText(
"Start a group chat", "Start a group chat",
@@ -336,18 +341,18 @@ test.describe("Spotlight", () => {
await expect(page.getByRole("dialog")).toContainText("Direct Messages"); await expect(page.getByRole("dialog")).toContainText("Direct Messages");
}); });
test("should close spotlight after starting a DM", async ({ page, app }) => { test("should close spotlight after starting a DM", async ({ page, app, bot1 }) => {
await startDM(app, page, bot1Name); await startDM(app, page, bot1.credentials.displayName);
await expect(page.locator(".mx_SpotlightDialog")).toHaveCount(0); await expect(page.locator(".mx_SpotlightDialog")).toHaveCount(0);
}); });
test("should show the same user only once", async ({ page, app }) => { test("should show the same user only once", async ({ page, app, bot1 }) => {
await startDM(app, page, bot1Name); await startDM(app, page, bot1.credentials.displayName);
await page.goto("/#/home"); await page.goto("/#/home");
const spotlight = await app.openSpotlight(); const spotlight = await app.openSpotlight();
await page.waitForTimeout(500); // wait for the dialog to settle await page.waitForTimeout(500); // wait for the dialog to settle
await spotlight.filter(Filter.People); await spotlight.filter(Filter.People);
await spotlight.search(bot1Name); await spotlight.search(bot1.credentials.displayName);
await page.waitForTimeout(3000); // wait for the dialog to settle await page.waitForTimeout(3000); // wait for the dialog to settle
await expect(spotlight.dialog.locator(".mx_Spinner")).not.toBeAttached(); await expect(spotlight.dialog.locator(".mx_Spinner")).not.toBeAttached();
const resultLocator = spotlight.results; const resultLocator = spotlight.results;