Skip to content
This repository was archived by the owner on Sep 11, 2024. It is now read-only.

Commit 0611769

Browse files
authored
Fix close button on forgot password flow (#12732)
* Fix close button on forgot password flow The 'x' had escaped out the right of the button for some reason * Add test that actually opens the dialog in question * Actually screenshot the right thing * Unnecessary screenshot
1 parent 2f953f1 commit 0611769

File tree

8 files changed

+141
-17
lines changed

8 files changed

+141
-17
lines changed
Lines changed: 77 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,77 @@
1+
/*
2+
Copyright 2024 The Matrix.org Foundation C.I.C.
3+
4+
Licensed under the Apache License, Version 2.0 (the "License");
5+
you may not use this file except in compliance with the License.
6+
You may obtain a copy of the License at
7+
8+
http://www.apache.org/licenses/LICENSE-2.0
9+
10+
Unless required by applicable law or agreed to in writing, software
11+
distributed under the License is distributed on an "AS IS" BASIS,
12+
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
See the License for the specific language governing permissions and
14+
limitations under the License.
15+
*/
16+
17+
import { expect, test } from "../../element-web-test";
18+
import { selectHomeserver } from "../utils";
19+
20+
const username = "user1234";
21+
// this has to be password-like enough to please zxcvbn. Needless to say it's just from pwgen.
22+
const password = "oETo7MPf0o";
23+
const email = "[email protected]";
24+
25+
test.describe("Forgot Password", () => {
26+
test.use({
27+
startHomeserverOpts: ({ mailhog }, use) =>
28+
use({
29+
template: "email",
30+
variables: {
31+
SMTP_HOST: "host.containers.internal",
32+
SMTP_PORT: mailhog.instance.smtpPort,
33+
},
34+
}),
35+
});
36+
37+
test("renders properly", async ({ page, homeserver }) => {
38+
await page.goto("/");
39+
40+
await page.getByRole("link", { name: "Sign in" }).click();
41+
42+
// need to select a homeserver at this stage, before entering the forgot password flow
43+
await selectHomeserver(page, homeserver.config.baseUrl);
44+
45+
await page.getByRole("button", { name: "Forgot password?" }).click();
46+
47+
await expect(page.getByRole("main")).toMatchScreenshot("forgot-password.png");
48+
});
49+
50+
test("renders email verification dialog properly", async ({ page, homeserver }) => {
51+
const user = await homeserver.registerUser(username, password);
52+
53+
await homeserver.setThreepid(user.userId, "email", email);
54+
55+
await page.goto("/");
56+
57+
await page.getByRole("link", { name: "Sign in" }).click();
58+
await selectHomeserver(page, homeserver.config.baseUrl);
59+
60+
await page.getByRole("button", { name: "Forgot password?" }).click();
61+
62+
await page.getByRole("textbox", { name: "Email address" }).fill(email);
63+
64+
await page.getByRole("button", { name: "Send email" }).click();
65+
66+
await page.getByRole("button", { name: "Next" }).click();
67+
68+
await page.getByRole("textbox", { name: "New Password", exact: true }).fill(password);
69+
await page.getByRole("textbox", { name: "Confirm new password", exact: true }).fill(password);
70+
71+
await page.getByRole("button", { name: "Reset password" }).click();
72+
73+
await expect(page.getByRole("button", { name: "Resend" })).toBeInViewport();
74+
75+
await expect(page.locator(".mx_Dialog")).toMatchScreenshot("forgot-password-verify-email.png");
76+
});
77+
});

playwright/e2e/login/login.spec.ts

Lines changed: 1 addition & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -14,11 +14,10 @@ See the License for the specific language governing permissions and
1414
limitations under the License.
1515
*/
1616

17-
import { Page } from "@playwright/test";
18-
1917
import { expect, test } from "../../element-web-test";
2018
import { doTokenRegistration } from "./utils";
2119
import { isDendrite } from "../../plugins/homeserver/dendrite";
20+
import { selectHomeserver } from "../utils";
2221

2322
test.describe("Login", () => {
2423
test.describe("Password login", () => {
@@ -85,17 +84,6 @@ test.describe("Login", () => {
8584
await expect(page).toHaveURL(/\/#\/room\/!room:id$/);
8685
await expect(page.getByRole("button", { name: "Join the discussion" })).toBeVisible();
8786
});
88-
89-
async function selectHomeserver(page: Page, homeserverUrl: string) {
90-
await page.getByRole("button", { name: "Edit" }).click();
91-
await page.getByRole("textbox", { name: "Other homeserver" }).fill(homeserverUrl);
92-
await page.getByRole("button", { name: "Continue", exact: true }).click();
93-
// wait for the dialog to go away
94-
await expect(page.locator(".mx_ServerPickerDialog")).toHaveCount(0);
95-
96-
await expect(page.locator(".mx_Spinner")).toHaveCount(0);
97-
await expect(page.locator(".mx_ServerPicker_server")).toHaveText(homeserverUrl);
98-
}
9987
});
10088

10189
// tests for old-style SSO login, in which we exchange tokens with Synapse, and Synapse talks to an auth server

playwright/e2e/utils.ts

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,8 +17,8 @@ limitations under the License.
1717
*/
1818

1919
import { uniqueId } from "lodash";
20+
import { expect, type Page } from "@playwright/test";
2021

21-
import type { Page } from "@playwright/test";
2222
import type { ClientEvent, MatrixEvent, Room } from "matrix-js-sdk/src/matrix";
2323
import { Client } from "../pages/client";
2424

@@ -63,4 +63,15 @@ export async function waitForRoom(
6363
);
6464
}
6565

66+
export async function selectHomeserver(page: Page, homeserverUrl: string) {
67+
await page.getByRole("button", { name: "Edit" }).click();
68+
await page.getByRole("textbox", { name: "Other homeserver" }).fill(homeserverUrl);
69+
await page.getByRole("button", { name: "Continue", exact: true }).click();
70+
// wait for the dialog to go away
71+
await expect(page.locator(".mx_ServerPickerDialog")).toHaveCount(0);
72+
73+
await expect(page.locator(".mx_Spinner")).toHaveCount(0);
74+
await expect(page.locator(".mx_ServerPicker_server")).toHaveText(homeserverUrl);
75+
}
76+
6677
export const CommandOrControl = process.platform === "darwin" ? "Meta" : "Control";

playwright/plugins/homeserver/index.ts

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,15 @@ export interface HomeserverInstance {
3939
* @param password login password
4040
*/
4141
loginUser(userId: string, password: string): Promise<Credentials>;
42+
43+
/**
44+
* Sets a third party identifier for the given user. This only supports setting a single 3pid and will
45+
* replace any others.
46+
* @param userId The full ID of the user to edit (as returned from registerUser)
47+
* @param medium The medium of the 3pid to set
48+
* @param address The address of the 3pid to set
49+
*/
50+
setThreepid(userId: string, medium: string, address: string): Promise<void>;
4251
}
4352

4453
export interface StartHomeserverOpts {

playwright/plugins/homeserver/synapse/index.ts

Lines changed: 40 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -94,6 +94,8 @@ export class Synapse implements Homeserver, HomeserverInstance {
9494
protected docker: Docker = new Docker();
9595
public config: HomeserverConfig & { serverId: string };
9696

97+
private adminToken?: string;
98+
9799
public constructor(private readonly request: APIRequestContext) {}
98100

99101
/**
@@ -152,20 +154,25 @@ export class Synapse implements Homeserver, HomeserverInstance {
152154
return [path.join(synapseLogsPath, "stdout.log"), path.join(synapseLogsPath, "stderr.log")];
153155
}
154156

155-
public async registerUser(username: string, password: string, displayName?: string): Promise<Credentials> {
157+
private async registerUserInternal(
158+
username: string,
159+
password: string,
160+
displayName?: string,
161+
admin = false,
162+
): Promise<Credentials> {
156163
const url = `${this.config.baseUrl}/_synapse/admin/v1/register`;
157164
const { nonce } = await this.request.get(url).then((r) => r.json());
158165
const mac = crypto
159166
.createHmac("sha1", this.config.registrationSecret)
160-
.update(`${nonce}\0${username}\0${password}\0notadmin`)
167+
.update(`${nonce}\0${username}\0${password}\0${admin ? "" : "not"}admin`)
161168
.digest("hex");
162169
const res = await this.request.post(url, {
163170
data: {
164171
nonce,
165172
username,
166173
password,
167174
mac,
168-
admin: false,
175+
admin,
169176
displayname: displayName,
170177
},
171178
});
@@ -185,6 +192,10 @@ export class Synapse implements Homeserver, HomeserverInstance {
185192
};
186193
}
187194

195+
public registerUser(username: string, password: string, displayName?: string): Promise<Credentials> {
196+
return this.registerUserInternal(username, password, displayName, false);
197+
}
198+
188199
public async loginUser(userId: string, password: string): Promise<Credentials> {
189200
const url = `${this.config.baseUrl}/_matrix/client/v3/login`;
190201
const res = await this.request.post(url, {
@@ -207,4 +218,30 @@ export class Synapse implements Homeserver, HomeserverInstance {
207218
homeServer: json.home_server,
208219
};
209220
}
221+
222+
public async setThreepid(userId: string, medium: string, address: string): Promise<void> {
223+
if (this.adminToken === undefined) {
224+
const result = await this.registerUserInternal("admin", "totalyinsecureadminpassword", undefined, true);
225+
this.adminToken = result.accessToken;
226+
}
227+
228+
const url = `${this.config.baseUrl}/_synapse/admin/v2/users/${userId}`;
229+
const res = await this.request.put(url, {
230+
data: {
231+
threepids: [
232+
{
233+
medium,
234+
address,
235+
},
236+
],
237+
},
238+
headers: {
239+
Authorization: `Bearer ${this.adminToken}`,
240+
},
241+
});
242+
243+
if (!res.ok()) {
244+
throw await res.json();
245+
}
246+
}
210247
}
17.4 KB
Loading
22.1 KB
Loading

res/css/_common.pcss

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -522,6 +522,8 @@ legend {
522522
content: "";
523523
width: 28px;
524524
height: 28px;
525+
left: 0;
526+
top: 0;
525527
position: absolute;
526528
mask-image: url("@vector-im/compound-design-tokens/icons/close.svg");
527529
mask-repeat: no-repeat;

0 commit comments

Comments
 (0)