committed by
GitHub
5 changed files with 222 additions and 2 deletions
@ -0,0 +1,119 @@ |
|||
import { expect, test } from "@playwright/test" |
|||
import { findLastEmail } from "./utils/mailcatcher" |
|||
import { randomEmail } from "./utils/random" |
|||
import { logInUser, signUpNewUser } from "./utils/user" |
|||
|
|||
test.use({ storageState: { cookies: [], origins: [] } }) |
|||
|
|||
test("Password Recovery title is visible", async ({ page }) => { |
|||
await page.goto("/recover-password") |
|||
|
|||
await expect( |
|||
page.getByRole("heading", { name: "Password Recovery" }), |
|||
).toBeVisible() |
|||
}) |
|||
|
|||
test("Input is visible, empty and editable", async ({ page }) => { |
|||
await page.goto("/recover-password") |
|||
|
|||
await expect(page.getByPlaceholder("Email")).toBeVisible() |
|||
await expect(page.getByPlaceholder("Email")).toHaveText("") |
|||
await expect(page.getByPlaceholder("Email")).toBeEditable() |
|||
}) |
|||
|
|||
test("Continue button is visible", async ({ page }) => { |
|||
await page.goto("/recover-password") |
|||
|
|||
await expect(page.getByRole("button", { name: "Continue" })).toBeVisible() |
|||
}) |
|||
|
|||
test("User can reset password successfully using the link", async ({ |
|||
page, |
|||
request, |
|||
}) => { |
|||
const full_name = "Test User" |
|||
const email = randomEmail() |
|||
const password = "changethis" |
|||
const new_password = "changethat" |
|||
|
|||
// Sign up a new user
|
|||
await signUpNewUser(page, full_name, email, password) |
|||
|
|||
await page.goto("/recover-password") |
|||
await page.getByPlaceholder("Email").fill(email) |
|||
|
|||
await page.getByRole("button", { name: "Continue" }).click() |
|||
|
|||
const emailData = await findLastEmail({ |
|||
request, |
|||
filter: (e) => e.recipients.includes(`<${email}>`), |
|||
timeout: 5000, |
|||
}) |
|||
|
|||
await page.goto(`http://localhost:1080/messages/${emailData.id}.html`) |
|||
|
|||
const selector = 'a[href*="/reset-password?token="]' |
|||
|
|||
let url = await page.getAttribute(selector, "href") |
|||
|
|||
// TODO: update var instead of doing a replace
|
|||
url = url!.replace("http://localhost/", "http://localhost:5173/") |
|||
|
|||
// Set the new password and confirm it
|
|||
await page.goto(url) |
|||
|
|||
await page.getByLabel("Set Password").fill(new_password) |
|||
await page.getByLabel("Confirm Password").fill(new_password) |
|||
await page.getByRole("button", { name: "Reset Password" }).click() |
|||
await expect(page.getByText("Password updated successfully")).toBeVisible() |
|||
|
|||
// Check if the user is able to login with the new password
|
|||
await logInUser(page, email, new_password) |
|||
}) |
|||
|
|||
test("Expired or invalid reset link", async ({ page }) => { |
|||
const invalidUrl = "/reset-password?token=invalidtoken" |
|||
|
|||
await page.goto(invalidUrl) |
|||
|
|||
await page.getByLabel("Set Password").fill("newpassword") |
|||
await page.getByLabel("Confirm Password").fill("newpassword") |
|||
await page.getByRole("button", { name: "Reset Password" }).click() |
|||
|
|||
await expect(page.getByText("Invalid token")).toBeVisible() |
|||
}) |
|||
|
|||
test("Weak new password validation", async ({ page, request }) => { |
|||
const full_name = "Test User" |
|||
const email = randomEmail() |
|||
const password = "password" |
|||
|
|||
// Sign up a new user
|
|||
await signUpNewUser(page, full_name, email, password) |
|||
|
|||
await page.goto("/recover-password") |
|||
await page.getByPlaceholder("Email").fill(email) |
|||
await page.getByRole("button", { name: "Continue" }).click() |
|||
|
|||
const emailData = await findLastEmail({ |
|||
request, |
|||
filter: (e) => e.recipients.includes(`<${email}>`), |
|||
timeout: 5000, |
|||
}) |
|||
|
|||
await page.goto(`http://localhost:1080/messages/${emailData.id}.html`) |
|||
|
|||
const selector = 'a[href*="/reset-password?token="]' |
|||
let url = await page.getAttribute(selector, "href") |
|||
url = url!.replace("http://localhost/", "http://localhost:5173/") |
|||
|
|||
// Set a weak new password
|
|||
await page.goto(url) |
|||
await page.getByLabel("Set Password").fill("123") |
|||
await page.getByLabel("Confirm Password").fill("123") |
|||
await page.getByRole("button", { name: "Reset Password" }).click() |
|||
|
|||
await expect( |
|||
page.getByText("Password must be at least 8 characters"), |
|||
).toBeVisible() |
|||
}) |
@ -0,0 +1,59 @@ |
|||
import type { APIRequestContext } from "@playwright/test" |
|||
|
|||
type Email = { |
|||
id: number |
|||
recipients: string[] |
|||
subject: string |
|||
} |
|||
|
|||
async function findEmail({ |
|||
request, |
|||
filter, |
|||
}: { request: APIRequestContext; filter?: (email: Email) => boolean }) { |
|||
const response = await request.get("http://localhost:1080/messages") |
|||
|
|||
let emails = await response.json() |
|||
|
|||
if (filter) { |
|||
emails = emails.filter(filter) |
|||
} |
|||
|
|||
const email = emails[emails.length - 1] |
|||
|
|||
if (email) { |
|||
return email as Email |
|||
} |
|||
|
|||
return null |
|||
} |
|||
|
|||
export function findLastEmail({ |
|||
request, |
|||
filter, |
|||
timeout = 5000, |
|||
}: { |
|||
request: APIRequestContext |
|||
filter?: (email: Email) => boolean |
|||
timeout?: number |
|||
}) { |
|||
const timeoutPromise = new Promise<never>((_, reject) => |
|||
setTimeout( |
|||
() => reject(new Error("Timeout while trying to get latest email")), |
|||
timeout, |
|||
), |
|||
) |
|||
|
|||
const checkEmails = async () => { |
|||
while (true) { |
|||
const emailData = await findEmail({ request, filter }) |
|||
|
|||
if (emailData) { |
|||
return emailData |
|||
} |
|||
// Wait for 100ms before checking again
|
|||
await new Promise((resolve) => setTimeout(resolve, 100)) |
|||
} |
|||
} |
|||
|
|||
return Promise.race([timeoutPromise, checkEmails()]) |
|||
} |
@ -0,0 +1,38 @@ |
|||
import { type Page, expect } from "@playwright/test" |
|||
|
|||
export async function signUpNewUser( |
|||
page: Page, |
|||
name: string, |
|||
email: string, |
|||
password: string, |
|||
) { |
|||
await page.goto("/signup") |
|||
|
|||
await page.getByPlaceholder("Full Name").fill(name) |
|||
await page.getByPlaceholder("Email").fill(email) |
|||
await page.getByPlaceholder("Password", { exact: true }).fill(password) |
|||
await page.getByPlaceholder("Repeat Password").fill(password) |
|||
await page.getByRole("button", { name: "Sign Up" }).click() |
|||
await expect( |
|||
page.getByText("Your account has been created successfully"), |
|||
).toBeVisible() |
|||
await page.goto("/login") |
|||
} |
|||
|
|||
export async function logInUser(page: Page, email: string, password: string) { |
|||
await page.goto("/login") |
|||
|
|||
await page.getByPlaceholder("Email").fill(email) |
|||
await page.getByPlaceholder("Password", { exact: true }).fill(password) |
|||
await page.getByRole("button", { name: "Log In" }).click() |
|||
await page.waitForURL("/") |
|||
await expect( |
|||
page.getByText("Welcome back, nice to see you again!"), |
|||
).toBeVisible() |
|||
} |
|||
|
|||
export async function logOutUser(page: Page, name: string) { |
|||
await page.getByRole("button", { name: name }).click() |
|||
await page.getByRole("menuitem", { name: "Log out" }).click() |
|||
await page.goto("/login") |
|||
} |
Loading…
Reference in new issue