import { expect, test } from "@playwright/test"; import { type AuditLog, type Consent, installDevApiMock, makeClient, seedAuth, } from "./helpers/devfront-fixtures"; test.describe("DevFront audit logs", () => { test.beforeEach(async ({ page }) => { page.on("dialog", async (dialog) => { await dialog.accept().catch(() => {}); }); await seedAuth(page); }); test("filtering and cursor pagination", async ({ page }) => { const state = { clients: [makeClient("client-audit", { name: "Audit app" })], consents: [] as Consent[], auditLogsByCursor: { "": { items: [ { event_id: "evt-1", timestamp: "2026-03-03T09:00:00.000Z", user_id: "actor-a", event_type: "CLIENT_UPDATE", status: "success", ip_address: "127.0.0.1", user_agent: "playwright", details: JSON.stringify({ action: "UPDATE_CLIENT", target_id: "client-audit", tenant_id: "tenant-a", }), }, ], next_cursor: "cursor-2", }, "cursor-2": { items: [ { event_id: "evt-2", timestamp: "2026-03-03T09:01:00.000Z", user_id: "actor-b", event_type: "CLIENT_ROTATE_SECRET", status: "success", ip_address: "127.0.0.1", user_agent: "playwright", details: JSON.stringify({ action: "ROTATE_SECRET", target_id: "client-audit", tenant_id: "tenant-a", }), }, ], }, }, }; await installDevApiMock(page, state); await page.goto("/audit-logs"); await expect(page.getByText("UPDATE_CLIENT")).toBeVisible(); await page .getByPlaceholder(/Client ID로 필터|Filter by Client ID/i) .fill("client-audit"); await page .getByPlaceholder(/액션으로 필터|Filter by Action/i) .fill("ROTATE_SECRET"); await page.getByRole("button", { name: /더 보기|Load more/i }).click(); await expect(page.getByText("ROTATE_SECRET")).toBeVisible(); }); test("realtime create/update actions should be recorded", async ({ page, }) => { const state = { clients: [makeClient("client-realtime", { name: "Realtime app" })], consents: [] as Consent[], auditLogs: [] as AuditLog[], auditLogsByCursor: undefined, }; await installDevApiMock(page, state); await page.goto("/clients/new"); await page .getByPlaceholder("My Awesome Application") .fill("Realtime New App"); await page .getByPlaceholder(/https:\/\/app\.example\.com\/callback/i) .fill("https://realtime.example.com/callback"); await page.getByRole("button", { name: /앱 생성|Create/i }).click(); await expect.poll(() => state.auditLogs.length).toBeGreaterThanOrEqual(1); await page.goto("/clients/client-realtime/settings"); await page .getByPlaceholder("My Awesome Application") .fill("Realtime Updated"); await page.getByRole("button", { name: /^저장$|^Save$/i }).click(); await expect.poll(() => state.auditLogs.length).toBeGreaterThanOrEqual(2); const actions = state.auditLogs .map((item) => { try { return JSON.parse(item.details)?.action as string | undefined; } catch { return undefined; } }) .filter((value): value is string => Boolean(value)); expect(actions).toContain("CREATE_CLIENT"); expect(actions).toContain("UPDATE_CLIENT"); }); });