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")} - - -
-

{fullName}

-

{email}

-
-
- ) -} - -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") -}