From b33aabbb68573e974ee315a4de1dcff97b6834f2 Mon Sep 17 00:00:00 2001 From: chan Date: Fri, 29 May 2026 15:01:34 +0900 Subject: [PATCH 01/15] =?UTF-8?q?test:=20=EA=B0=90=EC=82=AC=20=EB=A1=9C?= =?UTF-8?q?=EA=B7=B8(Audit=20Logs)=20=ED=8E=98=EC=9D=B4=EC=A7=80=20E2E=20?= =?UTF-8?q?=ED=85=8C=EC=8A=A4=ED=8A=B8=20=EC=B6=94=EA=B0=80=20(#919)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - AuditLogsPage 내부 검색(search, action, status)에 `data-testid` 추가 - Playwright 테스트(`audit.spec.ts`) 작성하여 목록 로드, 필터 동작 확인 --- .../src/features/audit/AuditLogsPage.tsx | 3 + adminfront/tests/audit.spec.ts | 168 ++++++++++++++++++ 2 files changed, 171 insertions(+) create mode 100644 adminfront/tests/audit.spec.ts diff --git a/adminfront/src/features/audit/AuditLogsPage.tsx b/adminfront/src/features/audit/AuditLogsPage.tsx index d55540ba..52b6c248 100644 --- a/adminfront/src/features/audit/AuditLogsPage.tsx +++ b/adminfront/src/features/audit/AuditLogsPage.tsx @@ -152,6 +152,7 @@ function AuditLogsPage() { setSearchActorId(event.target.value)} placeholder={t( @@ -161,6 +162,7 @@ function AuditLogsPage() { /> setSearchAction(event.target.value.toUpperCase()) @@ -171,6 +173,7 @@ function AuditLogsPage() { )} /> setSearchActorId(event.target.value)} + placeholder={t( + "ui.common.audit.filters.user_id", + "Filter by User ID", + )} + /> + setSearchActorId(event.target.value)} + data-testid="audit-search-action" + value={searchAction} + onChange={(event) => + setSearchAction(event.target.value.toUpperCase()) + } placeholder={t( - "ui.common.audit.filters.user_id", - "Filter by User ID", + "ui.common.audit.filters.action", + "Filter by Action (e.g. ROTATE_SECRET)", )} /> - - - setSearchAction(event.target.value.toUpperCase()) - } - placeholder={t( - "ui.common.audit.filters.action", - "Filter by Action (e.g. ROTATE_SECRET)", - )} - /> - - - } - /> - fetchNextPage()} - /> - + + + } + /> + fetchNextPage()} + /> + + )} ); diff --git a/adminfront/tests/audit.spec.ts b/adminfront/tests/audit.spec.ts index 486ea6a7..f6100bb1 100644 --- a/adminfront/tests/audit.spec.ts +++ b/adminfront/tests/audit.spec.ts @@ -2,8 +2,10 @@ import { expect, test } from "@playwright/test"; test.describe("Audit Logs Management", () => { test.beforeEach(async ({ page }) => { + page.on("console", (msg) => console.log(`[PAGE] ${msg.text()}`)); + await page.addInitScript(() => { - const authority = "http://localhost:5000/oidc"; + const authority = `${window.location.origin}/oidc`; const client_id = "adminfront"; const key = `oidc.user:${authority}:${client_id}`; const authData = { @@ -30,18 +32,21 @@ test.describe("Audit Logs Management", () => { }); await page.route("**/oidc/**", async (route) => { - if (route.request().url().includes("/.well-known/openid-configuration")) { + const url = route.request().url(); + if (url.includes("/.well-known/openid-configuration")) { + const origin = new URL(url).origin; return route.fulfill({ json: { - issuer: "http://localhost:5000/oidc", - authorization_endpoint: "http://localhost:5000/oidc/auth", - token_endpoint: "http://localhost:5000/oidc/token", - userinfo_endpoint: "http://localhost:5000/oidc/userinfo", - jwks_uri: "http://localhost:5000/oidc/jwks", + issuer: `${origin}/oidc`, + authorization_endpoint: `${origin}/oidc/auth`, + token_endpoint: `${origin}/oidc/token`, + userinfo_endpoint: `${origin}/oidc/userinfo`, + jwks_uri: `${origin}/oidc/jwks`, }, }); } - await route.fulfill({ json: { issuer: "http://localhost:5000/oidc" } }); + const origin = new URL(url).origin; + await route.fulfill({ json: { issuer: `${origin}/oidc` } }); }); await page.route("**/v1/audit*", async (route) => { @@ -97,12 +102,29 @@ test.describe("Audit Logs Management", () => { console.log("[test] Navigating to /audit-logs"); await page.goto("/audit-logs"); - // Check header + // Check header - this should be visible immediately now await expect(page.getByText(/감사 로그|Audit Logs/i).first()).toBeVisible({ - timeout: 20000, + timeout: 10000, }); - // Wait for the table row to appear (retry mechanism in expect will handle the async load) + // Ensure we are not stuck in a global loading state (AppLayout) + await expect(page.locator(".animate-spin")).not.toBeVisible({ + timeout: 10000, + }); + + // Check for audit page specific error + const errorEl = page.getByTestId("audit-error"); + if (await errorEl.isVisible()) { + const errorText = await errorEl.innerText(); + throw new Error(`Audit log page showed error: ${errorText}`); + } + + // Wait for loading to finish + await expect(page.getByTestId("audit-loading")).not.toBeVisible({ + timeout: 15000, + }); + + // Wait for the table row to appear await expect(page.locator("tbody tr")).toHaveCount(20, { timeout: 15000 }); // Check specific data visible in the row @@ -114,6 +136,12 @@ test.describe("Audit Logs Management", () => { page, }) => { await page.goto("/audit-logs"); + await expect(page.locator(".animate-spin")).not.toBeVisible({ + timeout: 10000, + }); + await expect(page.getByTestId("audit-loading")).not.toBeVisible({ + timeout: 15000, + }); await expect(page.locator("tbody tr")).toHaveCount(20, { timeout: 15000 }); const loadMoreBtn = page.getByRole("button", { @@ -131,13 +159,19 @@ test.describe("Audit Logs Management", () => { test("should filter logs by Action and User ID locally", async ({ page }) => { await page.goto("/audit-logs"); + await expect(page.locator(".animate-spin")).not.toBeVisible({ + timeout: 10000, + }); + await expect(page.getByTestId("audit-loading")).not.toBeVisible({ + timeout: 15000, + }); await expect(page.locator("tbody tr")).toHaveCount(20, { timeout: 15000 }); // Search by User ID const userIdInput = page.getByTestId("audit-search-user-id"); await userIdInput.fill("user-even"); - // Playwright expect will retry, so it will wait for deferred value + // Wait for deferred value to apply await expect(page.locator("tbody tr")).toHaveCount(10, { timeout: 15000 }); await expect(page.locator("tbody")).not.toContainText("user-odd"); @@ -156,6 +190,12 @@ test.describe("Audit Logs Management", () => { test("should filter logs by Status locally", async ({ page }) => { await page.goto("/audit-logs"); + await expect(page.locator(".animate-spin")).not.toBeVisible({ + timeout: 10000, + }); + await expect(page.getByTestId("audit-loading")).not.toBeVisible({ + timeout: 15000, + }); await expect(page.locator("tbody tr")).toHaveCount(20, { timeout: 15000 }); // Select "Failure" status diff --git a/adminfront/tests/users_bulk.spec.ts b/adminfront/tests/users_bulk.spec.ts index dd0bdd25..07c27a55 100644 --- a/adminfront/tests/users_bulk.spec.ts +++ b/adminfront/tests/users_bulk.spec.ts @@ -302,9 +302,9 @@ test.describe("Users Bulk Upload", () => { const payload = JSON.parse(bulkPayload); expect(payload.users[0].tenantSlug).toBe("primary-tenant"); expect(payload.users[0].metadata.employee_id).toBe("EMP001"); - expect(payload.users[0].metadata.sub_email).toBe( + expect(payload.users[0].metadata.sub_email).toEqual([ "dual.alias@hanmaceng.co.kr", - ); + ]); expect(payload.users[0].metadata.secondary_emails).toEqual([ "dual.alias@hanmaceng.co.kr", ]); diff --git a/adminfront/vite.config.ts b/adminfront/vite.config.ts index 4c6c9d57..9f93253c 100644 --- a/adminfront/vite.config.ts +++ b/adminfront/vite.config.ts @@ -10,7 +10,10 @@ export default defineConfig({ resolve: { alias: { "lucide-react": path.resolve(process.cwd(), "node_modules/lucide-react"), + react: path.resolve(process.cwd(), "node_modules/react"), + "react-dom": path.resolve(process.cwd(), "node_modules/react-dom"), }, + dedupe: ["react", "react-dom", "react-router-dom"], }, cacheDir: process.env.ADMINFRONT_VITE_CACHE_DIR ?? diff --git a/common/package.json b/common/package.json index 186734ca..07e2b549 100644 --- a/common/package.json +++ b/common/package.json @@ -43,7 +43,7 @@ "react-dom": "^19.2.0", "react-hook-form": "^7.71.1", "react-oidc-context": "^3.3.0", - "react-router-dom": "^6.28.2", + "react-router-dom": "^7.15.1", "tailwind-merge": "^3.4.0", "zod": "^3.24.1" } diff --git a/devfront/Dockerfile b/devfront/Dockerfile index cddece4c..c18fed5b 100644 --- a/devfront/Dockerfile +++ b/devfront/Dockerfile @@ -6,7 +6,7 @@ WORKDIR /workspace ENV CI=true # Install pnpm -RUN npm install -g pnpm +RUN corepack enable && corepack prepare pnpm@10.5.2 --activate # Copy workspace configs and common package COPY common ./common diff --git a/devfront/package.json b/devfront/package.json index 8bdfe01f..0bfc6129 100644 --- a/devfront/package.json +++ b/devfront/package.json @@ -12,8 +12,8 @@ "lint": "biome check .", "preview": "vite preview", "test": "playwright test", - "test:coverage": "vitest run --coverage", - "test:unit": "vitest run", + "test:coverage": "vitest run --coverage --bail 1", + "test:unit": "vitest run --bail 1", "test:roles": "playwright test tests/devfront-role-switch-report.spec.ts", "test:ui": "playwright test --ui" }, diff --git a/orgfront/Dockerfile b/orgfront/Dockerfile index 723dc7f3..aaa6290e 100644 --- a/orgfront/Dockerfile +++ b/orgfront/Dockerfile @@ -6,7 +6,7 @@ WORKDIR /workspace ENV CI=true # Install pnpm -RUN npm install -g pnpm +RUN corepack enable && corepack prepare pnpm@10.5.2 --activate # Copy workspace configs and common package COPY common ./common diff --git a/orgfront/package.json b/orgfront/package.json index 1dd9a519..c7407d15 100644 --- a/orgfront/package.json +++ b/orgfront/package.json @@ -15,8 +15,8 @@ "lint": "biome check .", "preview": "vite preview", "test": "playwright test", - "test:coverage": "vitest run --coverage", - "test:unit": "vitest run", + "test:coverage": "vitest run --coverage --bail 1", + "test:unit": "vitest run --bail 1", "test:roles": "playwright test tests/devfront-role-switch-report.spec.ts", "test:ui": "playwright test --ui" }, diff --git a/scripts/run_adminfront_ci_tests.sh b/scripts/run_adminfront_ci_tests.sh index 903f0e37..e524b8f4 100755 --- a/scripts/run_adminfront_ci_tests.sh +++ b/scripts/run_adminfront_ci_tests.sh @@ -203,9 +203,8 @@ set +e echo "packages: ['.', '../adminfront']" > pnpm-workspace.yaml if [ "$reuse_seed_node_modules" -eq 0 ]; then - if ! command -v pnpm >/dev/null 2>&1; then - run_with_retry 3 npm install -g pnpm - fi + corepack enable + corepack prepare pnpm@10.5.2 --activate run_with_retry 3 env CI=true pnpm install --no-frozen-lockfile --shamefully-hoist --store-dir "$pnpm_store_dir" fi @@ -299,7 +298,7 @@ echo "==> adminfront using PORT=$port" ( cd "$tmp_dir/adminfront" CI=true PORT="$port" PLAYWRIGHT_WORKERS="${PLAYWRIGHT_WORKERS:-1}" \ - pnpm exec playwright test "${playwright_project_args[@]}" + pnpm exec playwright test --max-failures=1 "${playwright_project_args[@]}" ) 2>&1 | tee reports/adminfront-test.log test_exit_code=${PIPESTATUS[0]} set -e From c59ec5ce83ace0430620214a038be6498a463d49 Mon Sep 17 00:00:00 2001 From: chan Date: Fri, 29 May 2026 19:48:01 +0900 Subject: [PATCH 15/15] =?UTF-8?q?adminfront-vitest-coverage=20=EC=98=A4?= =?UTF-8?q?=EB=A5=98=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- adminfront/src/features/coverage/adminAuditAuth.test.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/adminfront/src/features/coverage/adminAuditAuth.test.tsx b/adminfront/src/features/coverage/adminAuditAuth.test.tsx index 9eb229f7..6d2a829c 100644 --- a/adminfront/src/features/coverage/adminAuditAuth.test.tsx +++ b/adminfront/src/features/coverage/adminAuditAuth.test.tsx @@ -105,7 +105,7 @@ describe("admin audit and auth coverage smoke", () => { renderWithProviders(); expect(await screen.findByText("감사 로그")).toBeInTheDocument(); - expect(screen.getByText("admin-1")).toBeInTheDocument(); + expect(await screen.findByText("admin-1")).toBeInTheDocument(); expect(screen.getByText("USER_UPDATE")).toBeInTheDocument(); });