Skip to content

Commit 09d745d

Browse files
committed
Playwright test for dehydrated device in reset flow
This should be fixed by the previous commit, so let's check it stays that way.
1 parent 5d6d36d commit 09d745d

File tree

2 files changed

+63
-3
lines changed

2 files changed

+63
-3
lines changed

playwright/e2e/crypto/dehydration.spec.ts

Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@ import {
1818
logIntoElement,
1919
logOutOfElement,
2020
} from "./utils.ts";
21+
import { Client } from "../../pages/client.ts";
2122

2223
const ROOM_NAME = "Test room";
2324
const NAME = "Alice";
@@ -93,4 +94,49 @@ test.describe("Dehydration", () => {
9394
await expect(page.locator(".mx_UserInfo_devices").getByText("Offline device enabled")).toBeVisible();
9495
await expect(page.locator(".mx_UserInfo_devices").getByText("Dehydrated device")).not.toBeVisible();
9596
});
97+
98+
test("Reset recovery key during login re-creates dehydrated device", async ({
99+
page,
100+
homeserver,
101+
app,
102+
credentials,
103+
}) => {
104+
// Set up cross-signing and recovery
105+
const { botClient } = await createBot(page, homeserver, credentials);
106+
// ... and dehydration
107+
await botClient.evaluate(async (client) => await client.getCrypto().startDehydration());
108+
109+
const initialDehydratedDeviceIds = await getDehydratedDeviceIds(botClient);
110+
expect(initialDehydratedDeviceIds.length).toBe(1);
111+
112+
await botClient.evaluate(async (client) => client.stopClient());
113+
114+
// Log in our client
115+
await logIntoElement(page, credentials);
116+
117+
// Oh no, we forgot our recovery key
118+
await page.locator(".mx_AuthPage").getByRole("button", { name: "Reset all" }).click();
119+
await page.locator(".mx_AuthPage").getByRole("button", { name: "Proceed with reset" }).click();
120+
121+
await completeCreateSecretStorageDialog(page, { accountPassword: credentials.password });
122+
123+
// There should be a brand new dehydrated device
124+
const dehydratedDeviceIds = await getDehydratedDeviceIds(app.client);
125+
expect(dehydratedDeviceIds.length).toBe(1);
126+
expect(dehydratedDeviceIds[0]).not.toEqual(initialDehydratedDeviceIds[0]);
127+
});
96128
});
129+
130+
async function getDehydratedDeviceIds(client: Client): Promise<string[]> {
131+
return await client.evaluate(async (client) => {
132+
const userId = client.getUserId();
133+
const devices = await client.getCrypto().getUserDeviceInfo([userId]);
134+
return Array.from(
135+
devices
136+
.get(userId)
137+
.values()
138+
.filter((d) => d.dehydrated)
139+
.map((d) => d.deviceId),
140+
);
141+
});
142+
}

playwright/e2e/crypto/utils.ts

Lines changed: 17 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -297,9 +297,17 @@ export async function enableKeyBackup(app: ElementAppPage): Promise<string> {
297297
*
298298
* Assumes the dialog is already open for some reason (see also {@link enableKeyBackup}).
299299
*
300+
* @param page - The playwright `Page` fixture.
301+
* @param opts - Options object
302+
* @param opts.accountPassword - The user's account password. If we are also resetting cross-signing, then we will need
303+
* to upload the public cross-signing keys, which will cause the app to prompt for the password.
304+
*
300305
* @returns the new recovery key.
301306
*/
302-
export async function completeCreateSecretStorageDialog(page: Page): Promise<string> {
307+
export async function completeCreateSecretStorageDialog(
308+
page: Page,
309+
opts?: { accountPassword?: string },
310+
): Promise<string> {
303311
const currentDialogLocator = page.locator(".mx_Dialog");
304312

305313
await expect(currentDialogLocator.getByRole("heading", { name: "Set up Secure Backup" })).toBeVisible();
@@ -311,8 +319,14 @@ export async function completeCreateSecretStorageDialog(page: Page): Promise<str
311319
const recoveryKey = await page.evaluate(() => navigator.clipboard.readText());
312320
await currentDialogLocator.getByRole("button", { name: "Continue", exact: true }).click();
313321

314-
// If the device is unverified, there should be a "Setting up keys" step; however, it
315-
// can be quite quick, and playwright can miss it, so we can't test for it.
322+
// If the device is unverified, there should be a "Setting up keys" step.
323+
// If this is not the first time we are setting up cross-signing, the app will prompt for our password; otherwise
324+
// the step is quite quick, and playwright can miss it, so we can't test for it.
325+
if (Object.hasOwn(opts, "accountPassword")) {
326+
await expect(currentDialogLocator.getByRole("heading", { name: "Setting up keys" })).toBeVisible();
327+
await page.getByPlaceholder("Password").fill(opts!.accountPassword);
328+
await currentDialogLocator.getByRole("button", { name: "Continue" }).click();
329+
}
316330

317331
// Either way, we end up at a success dialog:
318332
await expect(currentDialogLocator.getByRole("heading", { name: "Secure Backup successful" })).toBeVisible();

0 commit comments

Comments
 (0)