Skip to content
This repository has been archived by the owner on Oct 22, 2024. It is now read-only.

Fix flaky crypto playwright tests #143

Merged
merged 5 commits into from
Oct 11, 2024
Merged
Show file tree
Hide file tree
Changes from 3 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 6 additions & 1 deletion playwright/e2e/crypto/crypto.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,7 @@ const testMessages = async (page: Page, bob: Bot, bobRoomId: string) => {
};

const bobJoin = async (page: Page, bob: Bot) => {
// Wait for Bob to get the invite
await bob.evaluate(async (cli) => {
const bobRooms = cli.getRooms();
if (!bobRooms.length) {
Expand All @@ -55,9 +56,13 @@ const bobJoin = async (page: Page, bob: Bot) => {
});
}
});
const roomId = await bob.joinRoomByName("Alice");

const roomId = await bob.joinRoomByName("Alice");
await expect(page.getByText("Bob joined the room")).toBeVisible();

// Even though Alice has seen Bob's join event, Bob may not have done so yet. Wait for the sync to arrive.
await bob.awaitRoomMembership(roomId);

return roomId;
};

Expand Down
9 changes: 6 additions & 3 deletions playwright/e2e/crypto/event-shields.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@ test.describe("Cryptography", function () {
await app.client.bootstrapCrossSigning(aliceCredentials);
await autoJoin(bob);

// create an encrypted room
// create an encrypted room, and wait for Bob to join it.
testRoomId = await createSharedRoomWithUser(app, bob.credentials.userId, {
name: "TestRoom",
initial_state: [
Expand All @@ -46,6 +46,9 @@ test.describe("Cryptography", function () {
},
],
});

// Even though Alice has seen Bob's join event, Bob may not have done so yet. Wait for the sync to arrive.
await bob.awaitRoomMembership(testRoomId);
});

test("should show the correct shield on e2e events", async ({
Expand Down Expand Up @@ -287,9 +290,9 @@ test.describe("Cryptography", function () {
// Let our app start syncing again
await app.client.network.goOnline();

// Wait for the messages to arrive
// Wait for the messages to arrive. It can take quite a while for the sync to wake up.
const last = page.locator(".mx_EventTile_last");
await expect(last).toContainText("test encrypted from unverified");
await expect(last).toContainText("test encrypted from unverified", { timeout: 20000 });
const lastE2eIcon = last.locator(".mx_EventTile_e2eIcon");
await expect(lastE2eIcon).toHaveClass(/mx_EventTile_e2eIcon_warning/);
await lastE2eIcon.focus();
Expand Down
8 changes: 8 additions & 0 deletions playwright/e2e/utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,14 @@ import { Client } from "../pages/client";
* @param client Client instance that can be user or bot
* @param roomId room id to find room and check
* @param predicate defines condition that is used to check the room state
*
* FIXME this does not do what it is supposed to do, and I think it is unfixable.
* `page.exposeFunction` adds a function which returns a Promise. `window[predicateId](room)` therefore
* always returns a truthy value (a Promise). But even if you fix that: as far as I can tell, the Room is
* just passed to the callback function as a JSON blob: you cannot actually call any methods on it, so the
* callback is useless.
*
* @deprecated This function is broken.
*/
export async function waitForRoom(
page: Page,
Expand Down
36 changes: 36 additions & 0 deletions playwright/pages/client.ts
Original file line number Diff line number Diff line change
Expand Up @@ -289,6 +289,42 @@ export class Client {
await client.evaluate((client, { roomId, userId }) => client.unban(roomId, userId), { roomId, userId });
}

/**
* Wait for the client to have specific membership of a given room
*
* This is often useful after joining a room, when we need to wait for the sync loop to catch up.
*
* @param roomId - ID of the room to check
* @param membership - required membership.
*/
public async awaitRoomMembership(roomId: string, membership: string = "join") {
await this.evaluate(
(cli: MatrixClient, { roomId, membership }) => {
const isReady = () => {
// Fetch the room on each check, because we get a different instance before and after the join arrives.
const room = cli.getRoom(roomId);
const myMembership = room?.getMyMembership();
// @ts-ignore access to private field "logger"
cli.logger.info(`waiting for room ${roomId}: membership now ${myMembership}`);
return myMembership == membership;
richvdh marked this conversation as resolved.
Show resolved Hide resolved
};
if (isReady()) return;

return new Promise<void>((resolve) => {
richvdh marked this conversation as resolved.
Show resolved Hide resolved
async function onEvent() {
if (isReady()) {
cli.removeListener(window.matrixcs.ClientEvent.Event, onEvent);
resolve();
}
}

cli.on(window.matrixcs.ClientEvent.Event, onEvent);
});
},
{ roomId, membership },
);
}

/**
* @param {MatrixEvent} event
* @param {ReceiptType} receiptType
Expand Down
Loading