diff --git a/backend/tests/__init__.py b/backend/tests/__init__.py
index e69de29..8033e88 100644
--- a/backend/tests/__init__.py
+++ b/backend/tests/__init__.py
@@ -0,0 +1,11 @@
+"""Redirect the suite at its own database.
+
+`app.core.db` builds the engine at import time and several modules bind that
+object, so the name has to be in the environment before anything imports the
+settings. Overriding it here — the first module pytest imports for the
+package — keeps a test run from touching the development data.
+"""
+
+import os
+
+os.environ["POSTGRES_DB"] = "app_test"
diff --git a/backend/tests/conftest.py b/backend/tests/conftest.py
index bc9f173..9c9e370 100644
--- a/backend/tests/conftest.py
+++ b/backend/tests/conftest.py
@@ -2,12 +2,13 @@ from collections.abc import Generator
import pytest
from fastapi.testclient import TestClient
-from sqlmodel import Session, delete
+from sqlalchemy import create_engine, text
+from sqlalchemy.engine import make_url
+from sqlmodel import Session, SQLModel
from app.core.config import settings
from app.core.db import engine, init_db
from app.main import app
-from app.models import User
from tests.utils.user import authentication_token_from_email
from tests.utils.utils import get_superuser_token_headers
@@ -23,12 +24,29 @@ def flow_data(tmp_path_factory: pytest.TempPathFactory) -> Generator[None, None,
@pytest.fixture(scope="session", autouse=True)
def db() -> Generator[Session, None, None]:
+ """Create the throwaway database `tests/__init__.py` points at, drop it after."""
+ url = make_url(str(settings.SQLALCHEMY_DATABASE_URI))
+ # The teardown drops this database, so refuse to run against anything but
+ # the dedicated test one.
+ assert url.database and url.database.endswith("_test"), url.database
+
+ maintenance = create_engine(
+ url.set(database="postgres"), isolation_level="AUTOCOMMIT"
+ )
+ drop = text(f'DROP DATABASE IF EXISTS "{url.database}" WITH (FORCE)')
+ with maintenance.connect() as connection:
+ connection.execute(drop)
+ connection.execute(text(f'CREATE DATABASE "{url.database}"'))
+
+ SQLModel.metadata.create_all(engine)
with Session(engine) as session:
init_db(session)
yield session
- statement = delete(User)
- session.execute(statement)
- session.commit()
+
+ engine.dispose()
+ with maintenance.connect() as connection:
+ connection.execute(drop)
+ maintenance.dispose()
@pytest.fixture(scope="module")
diff --git a/frontend/playwright.config.ts b/frontend/playwright.config.ts
index 256fb39..8618607 100644
--- a/frontend/playwright.config.ts
+++ b/frontend/playwright.config.ts
@@ -23,10 +23,10 @@ export default defineConfig({
reporter: process.env.CI ? 'blob' : 'html',
/* Shared settings for all the projects below. See https://playwright.dev/docs/api/class-testoptions. */
use: {
- /* Base URL to use in actions like `await page.goto('/')`.
- Point PLAYWRIGHT_BASE_URL at the integrated stack (http://app.localhost)
- to test what `make dev` is serving; the API only allows CORS from there. */
- baseURL: process.env.PLAYWRIGHT_BASE_URL || 'http://localhost:5173',
+ /* Base URL to use in actions like `await page.goto('/')`. Defaults to the
+ integrated stack (`make dev`), the only origin the API allows CORS from.
+ Point PLAYWRIGHT_BASE_URL elsewhere to test another running server. */
+ baseURL: process.env.PLAYWRIGHT_BASE_URL || 'http://app.localhost',
/* Collect trace when retrying the failed test. See https://playwright.dev/docs/trace-viewer */
trace: 'on-first-retry',
@@ -83,14 +83,4 @@ export default defineConfig({
// use: { ...devices['Desktop Chrome'], channel: 'chrome' },
// },
],
-
- /* Run your local dev server before starting the tests, unless we were
- pointed at an already-running one. */
- webServer: process.env.PLAYWRIGHT_BASE_URL
- ? undefined
- : {
- command: 'bun run dev',
- url: 'http://localhost:5173',
- reuseExistingServer: !process.env.CI,
- },
});
diff --git a/frontend/src/components/Sidebar/User.tsx b/frontend/src/components/Sidebar/User.tsx
deleted file mode 100644
index 12c6362..0000000
--- a/frontend/src/components/Sidebar/User.tsx
+++ /dev/null
@@ -1,97 +0,0 @@
-import { Link as RouterLink } from "@tanstack/react-router"
-import { ChevronsUpDown, LogOut, Settings } from "lucide-react"
-
-import { Avatar, AvatarFallback } from "@/components/ui/avatar"
-import {
- DropdownMenu,
- DropdownMenuContent,
- DropdownMenuItem,
- DropdownMenuLabel,
- DropdownMenuSeparator,
- DropdownMenuTrigger,
-} from "@/components/ui/dropdown-menu"
-import {
- SidebarMenu,
- SidebarMenuButton,
- SidebarMenuItem,
- useSidebar,
-} from "@/components/ui/sidebar"
-import useAuth from "@/hooks/useAuth"
-import { getInitials } from "@/utils"
-
-interface UserInfoProps {
- fullName?: string
- email?: string
-}
-
-function UserInfo({ fullName, email }: UserInfoProps) {
- return (
-
-
-
- {getInitials(fullName || "User")}
-
-
-
-
- )
-}
-
-export function User({ user }: { user: any }) {
- const { logout } = useAuth()
- const { isMobile, setOpenMobile } = useSidebar()
-
- if (!user) return null
-
- const handleMenuClick = () => {
- if (isMobile) {
- setOpenMobile(false)
- }
- }
- const handleLogout = async () => {
- logout()
- }
-
- return (
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- User Settings
-
-
-
-
- Log Out
-
-
-
-
-
- )
-}
diff --git a/frontend/tests/admin.spec.ts b/frontend/tests/admin.spec.ts
index cd73dba..490810e 100644
--- a/frontend/tests/admin.spec.ts
+++ b/frontend/tests/admin.spec.ts
@@ -93,7 +93,8 @@ test.describe("Admin user management", () => {
await page.getByRole("button", { name: "Save" }).click()
await expect(page.getByText("User updated successfully")).toBeVisible()
- await expect(page.getByText(updatedName)).toBeVisible()
+ // Scoped to this run's row: earlier runs leave their own "Updated Name".
+ await expect(userRow.getByText(updatedName)).toBeVisible()
})
test("Delete a user successfully", async ({ page }) => {
diff --git a/frontend/tests/login.spec.ts b/frontend/tests/login.spec.ts
deleted file mode 100644
index 8072ddc..0000000
--- a/frontend/tests/login.spec.ts
+++ /dev/null
@@ -1,117 +0,0 @@
-import { expect, type Page, test } from "@playwright/test"
-import { firstSuperuser, firstSuperuserPassword } from "./config.ts"
-import { randomPassword } from "./utils/random.ts"
-
-test.use({ storageState: { cookies: [], origins: [] } })
-
-const fillForm = async (page: Page, email: string, password: string) => {
- await page.getByTestId("email-input").fill(email)
- await page.getByTestId("password-input").fill(password)
-}
-
-const verifyInput = async (page: Page, testId: string) => {
- const input = page.getByTestId(testId)
- await expect(input).toBeVisible()
- await expect(input).toHaveText("")
- await expect(input).toBeEditable()
-}
-
-test("Inputs are visible, empty and editable", async ({ page }) => {
- await page.goto("/login")
-
- await verifyInput(page, "email-input")
- await verifyInput(page, "password-input")
-})
-
-test("Log In button is visible", async ({ page }) => {
- await page.goto("/login")
-
- await expect(page.getByRole("button", { name: "Log In" })).toBeVisible()
-})
-
-test("Forgot Password link is visible", async ({ page }) => {
- await page.goto("/login")
-
- await expect(
- page.getByRole("link", { name: "Forgot your password?" }),
- ).toBeVisible()
-})
-
-test("Log in with valid email and password ", async ({ page }) => {
- await page.goto("/login")
-
- await fillForm(page, firstSuperuser, firstSuperuserPassword)
- await page.getByRole("button", { name: "Log In" }).click()
-
- await page.waitForURL("/")
-
- await expect(
- page.getByText("Welcome back, nice to see you again!"),
- ).toBeVisible()
-})
-
-test("Log in with invalid email", async ({ page }) => {
- await page.goto("/login")
-
- await fillForm(page, "invalidemail", firstSuperuserPassword)
- await page.getByRole("button", { name: "Log In" }).click()
-
- await expect(page.getByText("Invalid email address")).toBeVisible()
-})
-
-test("Log in with invalid password", async ({ page }) => {
- const password = randomPassword()
-
- await page.goto("/login")
- await fillForm(page, firstSuperuser, password)
- await page.getByRole("button", { name: "Log In" }).click()
-
- await expect(page.getByText("Incorrect email or password")).toBeVisible()
-})
-
-test("Successful log out", async ({ page }) => {
- await page.goto("/login")
-
- await fillForm(page, firstSuperuser, firstSuperuserPassword)
- await page.getByRole("button", { name: "Log In" }).click()
-
- await page.waitForURL("/")
-
- await expect(
- page.getByText("Welcome back, nice to see you again!"),
- ).toBeVisible()
-
- await page.getByTestId("user-menu").click()
- await page.getByRole("menuitem", { name: "Log out" }).click()
- await page.waitForURL("/login")
-})
-
-test("Logged-out user cannot access protected routes", async ({ page }) => {
- await page.goto("/login")
-
- await fillForm(page, firstSuperuser, firstSuperuserPassword)
- await page.getByRole("button", { name: "Log In" }).click()
-
- await page.waitForURL("/")
-
- await expect(
- page.getByText("Welcome back, nice to see you again!"),
- ).toBeVisible()
-
- await page.getByTestId("user-menu").click()
- await page.getByRole("menuitem", { name: "Log out" }).click()
- await page.waitForURL("/login")
-
- await page.goto("/settings")
- await page.waitForURL("/login")
-})
-
-test("Redirects to /login when token is wrong", async ({ page }) => {
- await page.goto("/settings")
- await page.evaluate(() => {
- localStorage.setItem("access_token", "invalid_token")
- })
- await page.goto("/settings")
- await page.waitForURL("/login")
- await expect(page).toHaveURL("/login")
-})
diff --git a/frontend/tests/reset-password.spec.ts b/frontend/tests/reset-password.spec.ts
deleted file mode 100644
index 88a2fc2..0000000
--- a/frontend/tests/reset-password.spec.ts
+++ /dev/null
@@ -1,125 +0,0 @@
-import { expect, test } from "@playwright/test"
-import { findLastEmail } from "./utils/mailcatcher"
-import { randomEmail, randomPassword } 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.getByTestId("email-input")).toBeVisible()
- await expect(page.getByTestId("email-input")).toHaveText("")
- await expect(page.getByTestId("email-input")).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 fullName = "Test User"
- const email = randomEmail()
- const password = randomPassword()
- const newPassword = randomPassword()
-
- // Sign up a new user
- await signUpNewUser(page, fullName, email, password)
-
- await page.goto("/recover-password")
- await page.getByTestId("email-input").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(
- `${process.env.MAILCATCHER_HOST}/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.getByTestId("new-password-input").fill(newPassword)
- await page.getByTestId("confirm-password-input").fill(newPassword)
- 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, newPassword)
-})
-
-test("Expired or invalid reset link", async ({ page }) => {
- const password = randomPassword()
- const invalidUrl = "/reset-password?token=invalidtoken"
-
- await page.goto(invalidUrl)
-
- await page.getByTestId("new-password-input").fill(password)
- await page.getByTestId("confirm-password-input").fill(password)
- await page.getByRole("button", { name: "Reset Password" }).click()
-
- await expect(page.getByText("Invalid token")).toBeVisible()
-})
-
-test("Weak new password validation", async ({ page, request }) => {
- const fullName = "Test User"
- const email = randomEmail()
- const password = randomPassword()
- const weakPassword = "123"
-
- // Sign up a new user
- await signUpNewUser(page, fullName, email, password)
-
- await page.goto("/recover-password")
- await page.getByTestId("email-input").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(
- `${process.env.MAILCATCHER_HOST}/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.getByTestId("new-password-input").fill(weakPassword)
- await page.getByTestId("confirm-password-input").fill(weakPassword)
- await page.getByRole("button", { name: "Reset Password" }).click()
-
- await expect(
- page.getByText("Password must be at least 8 characters"),
- ).toBeVisible()
-})
diff --git a/frontend/tests/sign-up.spec.ts b/frontend/tests/sign-up.spec.ts
deleted file mode 100644
index 2a09e7c..0000000
--- a/frontend/tests/sign-up.spec.ts
+++ /dev/null
@@ -1,159 +0,0 @@
-import { expect, type Page, test } from "@playwright/test"
-
-import { randomEmail, randomPassword } from "./utils/random"
-
-test.use({ storageState: { cookies: [], origins: [] } })
-
-const fillForm = async (
- page: Page,
- full_name: string,
- email: string,
- password: string,
- confirm_password: string,
-) => {
- await page.getByTestId("full-name-input").fill(full_name)
- await page.getByTestId("email-input").fill(email)
- await page.getByTestId("password-input").fill(password)
- await page.getByTestId("confirm-password-input").fill(confirm_password)
-}
-
-const verifyInput = async (page: Page, testId: string) => {
- const input = page.getByTestId(testId)
- await expect(input).toBeVisible()
- await expect(input).toHaveText("")
- await expect(input).toBeEditable()
-}
-
-test("Inputs are visible, empty and editable", async ({ page }) => {
- await page.goto("/signup")
-
- await verifyInput(page, "full-name-input")
- await verifyInput(page, "email-input")
- await verifyInput(page, "password-input")
- await verifyInput(page, "confirm-password-input")
-})
-
-test("Sign Up button is visible", async ({ page }) => {
- await page.goto("/signup")
-
- await expect(page.getByRole("button", { name: "Sign Up" })).toBeVisible()
-})
-
-test("Log In link is visible", async ({ page }) => {
- await page.goto("/signup")
-
- await expect(page.getByRole("link", { name: "Log In" })).toBeVisible()
-})
-
-test("Sign up with valid name, email, and password", async ({ page }) => {
- const full_name = "Test User"
- const email = randomEmail()
- const password = randomPassword()
-
- await page.goto("/signup")
- await fillForm(page, full_name, email, password, password)
- await page.getByRole("button", { name: "Sign Up" }).click()
-})
-
-test("Sign up with invalid email", async ({ page }) => {
- await page.goto("/signup")
-
- await fillForm(
- page,
- "Playwright Test",
- "invalid-email",
- "changethis",
- "changethis",
- )
- await page.getByRole("button", { name: "Sign Up" }).click()
-
- await expect(page.getByText("Invalid email address")).toBeVisible()
-})
-
-test("Sign up with existing email", async ({ page }) => {
- const fullName = "Test User"
- const email = randomEmail()
- const password = randomPassword()
-
- await page.goto("/signup")
-
- await fillForm(page, fullName, email, password, password)
- await page.getByRole("button", { name: "Sign Up" }).click()
-
- await page.goto("/signup")
-
- await fillForm(page, fullName, email, password, password)
- await page.getByRole("button", { name: "Sign Up" }).click()
-
- await page
- .getByText("The user with this email already exists in the system")
- .click()
-})
-
-test("Sign up with weak password", async ({ page }) => {
- const fullName = "Test User"
- const email = randomEmail()
- const password = "weak"
-
- await page.goto("/signup")
-
- await fillForm(page, fullName, email, password, password)
- await page.getByRole("button", { name: "Sign Up" }).click()
-
- await expect(
- page.getByText("Password must be at least 8 characters"),
- ).toBeVisible()
-})
-
-test("Sign up with mismatched passwords", async ({ page }) => {
- const fullName = "Test User"
- const email = randomEmail()
- const password = randomPassword()
- const password2 = randomPassword()
-
- await page.goto("/signup")
-
- await fillForm(page, fullName, email, password, password2)
- await page.getByRole("button", { name: "Sign Up" }).click()
-
- await expect(page.getByText("The passwords don't match")).toBeVisible()
-})
-
-test("Sign up with missing full name", async ({ page }) => {
- const fullName = ""
- const email = randomEmail()
- const password = randomPassword()
-
- await page.goto("/signup")
-
- await fillForm(page, fullName, email, password, password)
- await page.getByRole("button", { name: "Sign Up" }).click()
-
- await expect(page.getByText("Full Name is required")).toBeVisible()
-})
-
-test("Sign up with missing email", async ({ page }) => {
- const fullName = "Test User"
- const email = ""
- const password = randomPassword()
-
- await page.goto("/signup")
-
- await fillForm(page, fullName, email, password, password)
- await page.getByRole("button", { name: "Sign Up" }).click()
-
- await expect(page.getByText("Invalid email address")).toBeVisible()
-})
-
-test("Sign up with missing password", async ({ page }) => {
- const fullName = ""
- const email = randomEmail()
- const password = ""
-
- await page.goto("/signup")
-
- await fillForm(page, fullName, email, password, password)
- await page.getByRole("button", { name: "Sign Up" }).click()
-
- await expect(page.getByText("Password is required")).toBeVisible()
-})
diff --git a/frontend/tests/user-settings.spec.ts b/frontend/tests/user-settings.spec.ts
deleted file mode 100644
index 533ebb6..0000000
--- a/frontend/tests/user-settings.spec.ts
+++ /dev/null
@@ -1,256 +0,0 @@
-import { expect, test } from "@playwright/test"
-import { firstSuperuser, firstSuperuserPassword } from "./config.ts"
-import { createUser } from "./utils/privateApi.ts"
-import { randomEmail, randomPassword } from "./utils/random"
-import { logInUser, logOutUser } from "./utils/user"
-
-const tabs = ["My profile", "Password", "Danger zone"]
-
-test("My profile tab is active by default", async ({ page }) => {
- await page.goto("/settings")
- await expect(page.getByRole("tab", { name: "My profile" })).toHaveAttribute(
- "aria-selected",
- "true",
- )
-})
-
-test("All tabs are visible", async ({ page }) => {
- await page.goto("/settings")
- for (const tab of tabs) {
- await expect(page.getByRole("tab", { name: tab })).toBeVisible()
- }
-})
-
-test.describe("Edit user profile", () => {
- test.use({ storageState: { cookies: [], origins: [] } })
- let email: string
- let password: string
-
- test.beforeAll(async () => {
- email = randomEmail()
- password = randomPassword()
- await createUser({ email, password })
- })
-
- test.beforeEach(async ({ page }) => {
- await logInUser(page, email, password)
- await page.goto("/settings")
- await page.getByRole("tab", { name: "My profile" }).click()
- })
-
- test("Edit user name with a valid name", async ({ page }) => {
- const updatedName = "Test User 2"
-
- await page.getByRole("button", { name: "Edit" }).click()
- await page.getByLabel("Full name").fill(updatedName)
- await page.getByRole("button", { name: "Save" }).click()
-
- await expect(page.getByText("User updated successfully")).toBeVisible()
- await expect(
- page.locator("form").getByText(updatedName, { exact: true }),
- ).toBeVisible()
- })
-
- test("Edit user email with an invalid email shows error", async ({
- page,
- }) => {
- await page.getByRole("button", { name: "Edit" }).click()
- await page.getByLabel("Email").fill("")
- await page.locator("body").click()
-
- await expect(page.getByText("Invalid email address")).toBeVisible()
- })
-})
-
-test.describe("Edit user email", () => {
- test.use({ storageState: { cookies: [], origins: [] } })
-
- test("Edit user email with a valid email", async ({ page }) => {
- const email = randomEmail()
- const password = randomPassword()
- const updatedEmail = randomEmail()
-
- await createUser({ email, password })
- await logInUser(page, email, password)
- await page.goto("/settings")
- await page.getByRole("tab", { name: "My profile" }).click()
-
- await page.getByRole("button", { name: "Edit" }).click()
- await page.getByLabel("Email").fill(updatedEmail)
- await page.getByRole("button", { name: "Save" }).click()
-
- await expect(page.getByText("User updated successfully")).toBeVisible()
- await expect(
- page.locator("form").getByText(updatedEmail, { exact: true }),
- ).toBeVisible()
- })
-})
-
-test.describe("Cancel edit actions", () => {
- test.use({ storageState: { cookies: [], origins: [] } })
-
- test("Cancel edit action restores original name", async ({ page }) => {
- const email = randomEmail()
- const password = randomPassword()
- const user = await createUser({ email, password })
-
- await logInUser(page, email, password)
- await page.goto("/settings")
- await page.getByRole("tab", { name: "My profile" }).click()
- await page.getByRole("button", { name: "Edit" }).click()
- await page.getByLabel("Full name").fill("Test User")
- await page.getByRole("button", { name: "Cancel" }).first().click()
-
- await expect(
- page.locator("form").getByText(user.full_name as string, { exact: true }),
- ).toBeVisible()
- })
-
- test("Cancel edit action restores original email", async ({ page }) => {
- const email = randomEmail()
- const password = randomPassword()
- await createUser({ email, password })
-
- await logInUser(page, email, password)
- await page.goto("/settings")
- await page.getByRole("tab", { name: "My profile" }).click()
- await page.getByRole("button", { name: "Edit" }).click()
- await page.getByLabel("Email").fill(randomEmail())
- await page.getByRole("button", { name: "Cancel" }).first().click()
-
- await expect(
- page.locator("form").getByText(email, { exact: true }),
- ).toBeVisible()
- })
-})
-
-test.describe("Change password", () => {
- test.use({ storageState: { cookies: [], origins: [] } })
-
- test("Update password successfully", async ({ page }) => {
- const email = randomEmail()
- const password = randomPassword()
- const newPassword = randomPassword()
-
- await createUser({ email, password })
- await logInUser(page, email, password)
-
- await page.goto("/settings")
- await page.getByRole("tab", { name: "Password" }).click()
- await page.getByTestId("current-password-input").fill(password)
- await page.getByTestId("new-password-input").fill(newPassword)
- await page.getByTestId("confirm-password-input").fill(newPassword)
- await page.getByRole("button", { name: "Update Password" }).click()
-
- await expect(page.getByText("Password updated successfully")).toBeVisible()
-
- await logOutUser(page)
- await logInUser(page, email, newPassword)
- })
-})
-
-test.describe("Change password validation", () => {
- test.use({ storageState: { cookies: [], origins: [] } })
- let email: string
- let password: string
-
- test.beforeAll(async () => {
- email = randomEmail()
- password = randomPassword()
- await createUser({ email, password })
- })
-
- test.beforeEach(async ({ page }) => {
- await logInUser(page, email, password)
- await page.goto("/settings")
- await page.getByRole("tab", { name: "Password" }).click()
- })
-
- test("Update password with weak passwords", async ({ page }) => {
- const weakPassword = "weak"
-
- await page.getByTestId("current-password-input").fill(password)
- await page.getByTestId("new-password-input").fill(weakPassword)
- await page.getByTestId("confirm-password-input").fill(weakPassword)
- await page.getByRole("button", { name: "Update Password" }).click()
-
- await expect(
- page.getByText("Password must be at least 8 characters"),
- ).toBeVisible()
- })
-
- test("New password and confirmation password do not match", async ({
- page,
- }) => {
- await page.getByTestId("current-password-input").fill(password)
- await page.getByTestId("new-password-input").fill(randomPassword())
- await page.getByTestId("confirm-password-input").fill(randomPassword())
- await page.getByRole("button", { name: "Update Password" }).click()
-
- await expect(page.getByText("The passwords don't match")).toBeVisible()
- })
-
- test("Current password and new password are the same", async ({ page }) => {
- await page.getByTestId("current-password-input").fill(password)
- await page.getByTestId("new-password-input").fill(password)
- await page.getByTestId("confirm-password-input").fill(password)
- await page.getByRole("button", { name: "Update Password" }).click()
-
- await expect(
- page.getByText("New password cannot be the same as the current one"),
- ).toBeVisible()
- })
-})
-
-test("Appearance button is visible in sidebar", async ({ page }) => {
- await page.goto("/settings")
- await expect(page.getByTestId("theme-button")).toBeVisible()
-})
-
-test("User can switch between theme modes", async ({ page }) => {
- await page.goto("/settings")
-
- await page.getByTestId("theme-button").click()
- await page.getByTestId("dark-mode").click()
- await expect(page.locator("html")).toHaveClass(/dark/)
-
- await expect(page.getByTestId("dark-mode")).not.toBeVisible()
-
- await page.getByTestId("theme-button").click()
- await page.getByTestId("light-mode").click()
- await expect(page.locator("html")).toHaveClass(/light/)
-})
-
-test("Selected mode is preserved across sessions", async ({ page }) => {
- await page.goto("/settings")
-
- await page.getByTestId("theme-button").click()
- if (
- await page.evaluate(() =>
- document.documentElement.classList.contains("dark"),
- )
- ) {
- await page.getByTestId("light-mode").click()
- await page.getByTestId("theme-button").click()
- }
-
- const isLightMode = await page.evaluate(() =>
- document.documentElement.classList.contains("light"),
- )
- expect(isLightMode).toBe(true)
-
- await page.getByTestId("theme-button").click()
- await page.getByTestId("dark-mode").click()
- let isDarkMode = await page.evaluate(() =>
- document.documentElement.classList.contains("dark"),
- )
- expect(isDarkMode).toBe(true)
-
- await logOutUser(page)
- await logInUser(page, firstSuperuser, firstSuperuserPassword)
-
- isDarkMode = await page.evaluate(() =>
- document.documentElement.classList.contains("dark"),
- )
- expect(isDarkMode).toBe(true)
-})
diff --git a/frontend/tests/utils/mailcatcher.ts b/frontend/tests/utils/mailcatcher.ts
deleted file mode 100644
index 8e6f78b..0000000
--- a/frontend/tests/utils/mailcatcher.ts
+++ /dev/null
@@ -1,62 +0,0 @@
-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(`${process.env.MAILCATCHER_HOST}/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((_, 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()])
-}
diff --git a/frontend/tests/utils/user.ts b/frontend/tests/utils/user.ts
index 33f86e3..65cb4f1 100644
--- a/frontend/tests/utils/user.ts
+++ b/frontend/tests/utils/user.ts
@@ -1,21 +1,5 @@
import { expect, type Page } from "@playwright/test"
-export async function signUpNewUser(
- page: Page,
- name: string,
- email: string,
- password: string,
-) {
- await page.goto("/signup")
-
- await page.getByTestId("full-name-input").fill(name)
- await page.getByTestId("email-input").fill(email)
- await page.getByTestId("password-input").fill(password)
- await page.getByTestId("confirm-password-input").fill(password)
- await page.getByRole("button", { name: "Sign Up" }).click()
- await page.goto("/login")
-}
-
export async function logInUser(page: Page, email: string, password: string) {
await page.goto("/login")
@@ -27,9 +11,3 @@ export async function logInUser(page: Page, email: string, password: string) {
page.getByText("Welcome back, nice to see you again!"),
).toBeVisible()
}
-
-export async function logOutUser(page: Page) {
- await page.getByTestId("user-menu").click()
- await page.getByRole("menuitem", { name: "Log out" }).click()
- await page.goto("/login")
-}