import { expect, test } from "@playwright/test"; test.describe("Authentication", () => { test.beforeEach(async ({ page }) => { // 1. Force state await page.addInitScript(() => { window.localStorage.setItem("locale", "ko"); window.localStorage.setItem("admin_session", "fake-token"); ( window as Window & typeof globalThis & { _IS_TEST_MODE?: boolean } )._IS_TEST_MODE = true; const authority = "http://localhost:5000/oidc"; const client_id = "adminfront"; const key = `oidc.user:${authority}:${client_id}`; const authData = { access_token: "fake-token", token_type: "Bearer", profile: { sub: "admin-user", name: "Admin", email: "admin@test.com", role: "super_admin", }, expires_at: Math.floor(Date.now() / 1000) + 36000, }; window.localStorage.setItem(key, JSON.stringify(authData)); }); // 2. High-priority Mocks await page.route("**/api/v1/user/me", async (route) => { await route.fulfill({ json: { id: "admin-user", name: "Admin", role: "super_admin", manageableTenants: [], }, headers: { "Access-Control-Allow-Origin": "*" }, }); }); await page.route("**/oidc/**", async (route) => { const url = route.request().url(); if (url.includes(".well-known/openid-configuration")) { await route.fulfill({ json: { issuer: "http://localhost:5000/oidc", authorization_endpoint: "http://localhost:5000/oidc/auth", token_endpoint: "http://localhost:5000/oidc/token", jwks_uri: "http://localhost:5000/oidc/jwks", userinfo_endpoint: "http://localhost:5000/oidc/userinfo", end_session_endpoint: "http://localhost:5000/oidc/session/end", }, headers: { "Access-Control-Allow-Origin": "*" }, }); } else if (url.includes("/jwks")) { await route.fulfill({ json: { keys: [] }, headers: { "Access-Control-Allow-Origin": "*" }, }); } else { await route.fulfill({ status: 200, body: "ok", headers: { "Access-Control-Allow-Origin": "*" }, }); } }); // 3. Catch-all for others await page.route(/.*\/api\/v1\/.*/, async (route) => { if (route.request().method() === "GET") { await route.fulfill({ json: { items: [], total: 0 } }); } else { await route.fulfill({ status: 200, json: {} }); } }); }); test("should redirect unauthorized users to login page", async ({ page }) => { await page.addInitScript(() => { window.localStorage.clear(); ( window as Window & typeof globalThis & { _IS_TEST_MODE?: boolean } )._IS_TEST_MODE = false; }); await page.goto("/"); await expect(page).toHaveURL(/.*\/login.*/, { timeout: 15000 }); }); test("should render an actionable SSO button on login route with returnTo", async ({ page, }) => { await page.addInitScript(() => { window.localStorage.clear(); ( window as Window & typeof globalThis & { _IS_TEST_MODE?: boolean } )._IS_TEST_MODE = false; }); await page.goto("/login?returnTo=%2F"); const loginButton = page.getByRole("button", { name: /SSO 계정으로 로그인/i, }); await expect(loginButton).toBeVisible(); await expect(loginButton).toBeEnabled(); await expect(loginButton).not.toContainText("로그인 진행 중"); }); test("should allow access to dashboard when authenticated", async ({ page, }) => { await page.goto("/"); // strict mode violation 피하기 위해 .last() 사용하거나 더 구체적인 셀렉터 사용 await expect(page.locator("h1").last()).toContainText( /Admin Control|운영 도구/i, { timeout: 15000 }, ); }); test("should link org chart navigation through the auto login entry", async ({ page, }) => { await page.goto("/"); await expect(page.getByRole("link", { name: "조직도" })).toHaveAttribute( "href", /\/login\?auto=1&returnTo=%2Fchart%3FincludeInternal%3Dtrue$/, ); }); test("should logout and redirect to login page", async ({ page }) => { await page.goto("/"); page.on("dialog", (dialog) => dialog.accept()); const logoutBtn = page .locator("button") .filter({ hasText: /Logout|로그아웃/i }) .first(); await logoutBtn.waitFor({ state: "visible" }); await logoutBtn.click(); await expect(page).toHaveURL(/.*\/login.*/, { timeout: 15000 }); }); });