Skip to content

Commit eade32a

Browse files
authored
Playwright test: withheld sessions for MSC4268 (#31153)
* Playwright test: withheld sessions for MSC4268 Test what happens when a session is withheld as part of MSC4268 * Add comment on `openRoomInfoPanel`
1 parent a0de60a commit eade32a

File tree

2 files changed

+98
-2
lines changed

2 files changed

+98
-2
lines changed

playwright/e2e/crypto/history-sharing.spec.ts

Lines changed: 81 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -56,4 +56,85 @@ test.describe("History sharing", function () {
5656
});
5757
},
5858
);
59+
60+
test("Messages sent when we believed the room history was unshared should not be visible", async ({
61+
labsFlags,
62+
browser,
63+
page: alicePage,
64+
user: aliceCredentials,
65+
app: aliceElementApp,
66+
homeserver,
67+
}, testInfo) => {
68+
test.setTimeout(60000);
69+
70+
// In this test:
71+
// 1. Alice creates an encrypted room with Bob.
72+
// 2. She sets the history visibility to "shared", but Bob doesn't receive the memo
73+
// 3. Bob sends a message
74+
// 4. Alice invites Charlie
75+
// 5. Charlie can't see the message.
76+
77+
await aliceElementApp.client.bootstrapCrossSigning(aliceCredentials);
78+
await createRoom(alicePage, "TestRoom", true);
79+
80+
// Register a second user, and open it in a second instance of the app
81+
const bobCredentials = await homeserver.registerUser(`user_${testInfo.testId}_bob`, "password", "Bob");
82+
const bobPage = await createNewInstance(browser, bobCredentials, {}, labsFlags);
83+
const bobElementApp = new ElementAppPage(bobPage);
84+
await bobElementApp.client.bootstrapCrossSigning(bobCredentials);
85+
86+
// ... and a third
87+
const charlieCredentials = await homeserver.registerUser(
88+
`user_${testInfo.testId}_charlie`,
89+
"password",
90+
"Charlie",
91+
);
92+
const charliePage = await createNewInstance(browser, charlieCredentials, {}, labsFlags);
93+
const charlieElementApp = new ElementAppPage(charliePage);
94+
await charlieElementApp.client.bootstrapCrossSigning(charlieCredentials);
95+
96+
// Alice invites Bob, and Bob accepts
97+
const roomId = await aliceElementApp.getCurrentRoomIdFromUrl();
98+
await aliceElementApp.inviteUserToCurrentRoom(bobCredentials.userId);
99+
await bobPage.getByRole("option", { name: "TestRoom" }).click();
100+
await bobPage.getByRole("button", { name: "Accept" }).click();
101+
102+
// Bob sends a message with "shared" visibility
103+
await sendMessageInCurrentRoom(bobPage, "Message1: 'shared' visibility");
104+
await expect(alicePage.getByText("Message1")).toBeVisible();
105+
106+
// Alice sets the history visibility to "joined"
107+
await aliceElementApp.client.sendStateEvent(roomId, "m.room.history_visibility", {
108+
history_visibility: "joined",
109+
});
110+
await expect(
111+
bobPage.getByText(
112+
"Alice made future room history visible to all room members, from the point they joined.",
113+
),
114+
).toBeVisible();
115+
116+
// Bob stops syncing, and sends a message with "joined" visibility.
117+
// (Stopping syncing *before* sending the message means that the active sync will be flushed by sending the
118+
// message, so that Alice's change to the history viz below won't be seen by Bob.)
119+
await bobPage.route(`**/sync*`, (route) => route.fulfill({}));
120+
await sendMessageInCurrentRoom(bobPage, "Message2: 'joined' visibility");
121+
await expect(alicePage.getByText("Message2")).toBeVisible();
122+
123+
// Alice changes the history viz, but Bob doesn't receive the memo
124+
await aliceElementApp.client.sendStateEvent(roomId, "m.room.history_visibility", {
125+
history_visibility: "shared",
126+
});
127+
await sendMessageInCurrentRoom(bobPage, "Message3: 'shared' visibility, but Bob thinks it is still 'joined'");
128+
129+
// Alice now invites Charlie
130+
await aliceElementApp.inviteUserToCurrentRoom(charlieCredentials.userId);
131+
await charliePage.getByRole("option", { name: "TestRoom" }).click();
132+
await charliePage.getByRole("button", { name: "Accept" }).click();
133+
134+
// Message1 should be visible
135+
// Message2 should be invisible
136+
// Message3 should be undecryptable
137+
await expect(charliePage.getByText("Message1")).toBeVisible();
138+
await expect(charliePage.getByText("You don't have access to this message")).toBeVisible();
139+
});
59140
});

playwright/pages/ElementAppPage.ts

Lines changed: 17 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -199,6 +199,21 @@ export class ElementAppPage {
199199
return this.page.locator(".mx_RightPanel");
200200
}
201201

202+
/**
203+
* Opens the room info panel if it is not already open.
204+
*
205+
* TODO: fix this so that it works correctly if, say, the member list was open instead of the room info panel.
206+
*
207+
* @returns locator to the right panel
208+
*/
209+
public async openRoomInfoPanel(): Promise<Locator> {
210+
const locator = this.page.getByTestId("right-panel");
211+
if (!(await locator.isVisible())) {
212+
await this.page.getByRole("button", { name: "Room info" }).first().click();
213+
}
214+
return locator;
215+
}
216+
202217
/**
203218
* Opens/closes the memberlist panel
204219
* @returns locator to the memberlist panel
@@ -217,8 +232,8 @@ export class ElementAppPage {
217232
* @param userId - The user to invite to the room.
218233
*/
219234
public async inviteUserToCurrentRoom(userId: string): Promise<void> {
220-
await this.toggleRoomInfoPanel(); // TODO skip this if the room info panel is already open
221-
await this.page.getByTestId("right-panel").getByRole("menuitem", { name: "Invite" }).click();
235+
const rightPanel = await this.openRoomInfoPanel();
236+
await rightPanel.getByRole("menuitem", { name: "Invite" }).click();
222237

223238
const input = this.page.getByRole("dialog").getByTestId("invite-dialog-input");
224239
await input.fill(userId);

0 commit comments

Comments
 (0)