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