Files
medassist-ng/frontend/e2e/auth.spec.ts
T
Daniel Volz 9e8a6315e7 fix: keep topical stock non-depleting in planner flows (#359)
* fix: keep topical stock non-depleting in planner and reports

* test: stabilize e2e selectors for updated medication semantics

* fix(backend): add missing planner translation keys
2026-02-28 23:36:52 +01:00

170 lines
6.1 KiB
TypeScript

import { expect, type Page, test } from "@playwright/test";
interface AuthStateResponse {
authEnabled: boolean;
formLoginEnabled: boolean;
oidcEnabled: boolean;
oidcProviderName: string;
registrationEnabled: boolean;
}
async function getAuthState(page: Page): Promise<AuthStateResponse | null> {
try {
const response = await page.request.get("/api/auth/state");
if (!response.ok()) return null;
return (await response.json()) as AuthStateResponse;
} catch {
return null;
}
}
async function isAuthEnabled(page: Page): Promise<boolean> {
const state = await getAuthState(page);
return state?.authEnabled !== false;
}
/**
* Authentication E2E Tests
*
* Tests the login/register UI when not authenticated.
* Uses empty storage state to simulate unauthenticated access.
*
* NOTE: This file intentionally imports `test` from @playwright/test
* (not from fixtures) because auth tests use empty storageState and
* must NOT have the auth-me caching interceptor.
*/
test.describe("Authentication", () => {
test.use({ storageState: { cookies: [], origins: [] } });
test("should show login page for unauthenticated users", async ({ page }) => {
test.skip(!(await isAuthEnabled(page)), "Auth is disabled in this environment");
await page.goto("/");
await expect(page.locator(".auth-container")).toBeVisible({ timeout: 15000 });
// Should have the app title
await expect(page.locator(".auth-title")).toContainText("MedAssist-ng");
});
test("should have username and password fields", async ({ page }) => {
test.skip(!(await isAuthEnabled(page)), "Auth is disabled in this environment");
await page.goto("/");
await expect(page.locator(".auth-container")).toBeVisible({ timeout: 15000 });
const usernameField = page.locator("#username");
const passwordField = page.locator("#password");
await expect(usernameField).toBeVisible();
await expect(usernameField).toBeEnabled();
await expect(passwordField).toBeVisible();
await expect(passwordField).toBeEnabled();
});
test("should have a submit button", async ({ page }) => {
test.skip(!(await isAuthEnabled(page)), "Auth is disabled in this environment");
await page.goto("/");
await expect(page.locator(".auth-container")).toBeVisible({ timeout: 15000 });
const submitButton = page.locator('button.auth-submit[type="submit"]');
await expect(submitButton).toBeVisible();
await expect(submitButton).toBeEnabled();
});
test("should not navigate to dashboard without credentials", async ({ page }) => {
test.skip(!(await isAuthEnabled(page)), "Auth is disabled in this environment");
await page.goto("/dashboard");
// Should NOT show the app header (redirected to login)
await expect(page.locator("header.hero")).not.toBeVisible({ timeout: 10000 });
// Should show auth form instead
await expect(page.locator(".auth-container")).toBeVisible();
});
test("should show error for invalid credentials", async ({ page }) => {
test.skip(!(await isAuthEnabled(page)), "Auth is disabled in this environment");
await page.goto("/");
await expect(page.locator(".auth-container")).toBeVisible({ timeout: 15000 });
// Fill in invalid credentials
await page.locator("#username").fill("nonexistent-user");
await page.locator("#password").fill("wrongpassword");
await page.locator('button.auth-submit[type="submit"]').click();
// Should show an error message
await expect(page.locator(".auth-error")).toBeVisible({ timeout: 5000 });
});
test("should toggle between login and register forms", async ({ page }) => {
test.skip(!(await isAuthEnabled(page)), "Auth is disabled in this environment");
await page.goto("/");
await expect(page.locator(".auth-container")).toBeVisible({ timeout: 15000 });
const toggleButton = page.locator("button.auth-link-btn");
test.skip(
!(await toggleButton.isVisible().catch(() => false)),
"Registration toggle is unavailable in this environment"
);
// Check current subtitle text
const subtitle = page.locator(".auth-subtitle");
const initialText = await subtitle.textContent();
// Click the toggle link (Create account / Already have an account)
await toggleButton.click();
// Subtitle should change
const newText = await subtitle.textContent();
expect(newText).not.toBe(initialText);
});
test("should show SSO button when OIDC is enabled", async ({ page }) => {
const state = await getAuthState(page);
test.skip(!state?.authEnabled, "Auth is disabled in this environment");
test.skip(!state?.oidcEnabled, "OIDC is not enabled in this environment");
await page.goto("/");
await expect(page.locator(".auth-container")).toBeVisible({ timeout: 15000 });
const ssoButton = page.locator("button.sso-btn");
await expect(ssoButton).toBeVisible();
await expect(ssoButton).toContainText(state.oidcProviderName || "SSO");
});
test("should hide form login when formLoginEnabled is false", async ({ page }) => {
const state = await getAuthState(page);
test.skip(!state?.authEnabled, "Auth is disabled in this environment");
test.skip(state?.formLoginEnabled !== false, "Form login is enabled — cannot test hidden state");
await page.goto("/");
await expect(page.locator(".auth-container")).toBeVisible({ timeout: 15000 });
// Username/password fields should not be visible
await expect(page.locator("#username")).not.toBeVisible();
await expect(page.locator("#password")).not.toBeVisible();
// SSO button should be the only login method
await expect(page.locator("button.sso-btn")).toBeVisible();
});
test("should show both login methods when OIDC and form login are enabled", async ({ page }) => {
const state = await getAuthState(page);
test.skip(!state?.authEnabled, "Auth is disabled in this environment");
test.skip(!state?.oidcEnabled, "OIDC is not enabled");
test.skip(!state?.formLoginEnabled, "Form login is not enabled");
await page.goto("/");
await expect(page.locator(".auth-container")).toBeVisible({ timeout: 15000 });
// Both login methods visible
await expect(page.locator("#username")).toBeVisible();
await expect(page.locator("#password")).toBeVisible();
await expect(page.locator("button.sso-btn")).toBeVisible();
});
});