From 52046e4a6627b2705a1bb9cedcaf6b9e1739e190 Mon Sep 17 00:00:00 2001 From: kyy Date: Wed, 10 Jun 2026 15:42:01 +0900 Subject: [PATCH] =?UTF-8?q?adminfront/devfront=20code-check=20=EC=88=98?= =?UTF-8?q?=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- adminfront/tests/users.spec.ts | 2 +- adminfront/tests/worksmobile.spec.ts | 4 ++ .../src/features/clients/ClientsPage.test.tsx | 16 +++++- devfront/src/features/clients/ClientsPage.tsx | 53 +------------------ .../tests/devfront-role-switch-report.spec.ts | 16 ++++++ devfront/tests/devfront-security.spec.ts | 16 ++++++ devfront/tests/helpers/devfront-fixtures.ts | 29 ++++++++-- 7 files changed, 78 insertions(+), 58 deletions(-) diff --git a/adminfront/tests/users.spec.ts b/adminfront/tests/users.spec.ts index 44c37824..3d61336f 100644 --- a/adminfront/tests/users.spec.ts +++ b/adminfront/tests/users.spec.ts @@ -411,7 +411,7 @@ test.describe("User Management", () => { }); await page - .getByRole("button", { name: /전역 Claim 저장|Save Global Claim/i }) + .getByRole("button", { name: /사용자 Claim 값 저장/i }) .click(); await expect diff --git a/adminfront/tests/worksmobile.spec.ts b/adminfront/tests/worksmobile.spec.ts index b25bada6..9e3843c5 100644 --- a/adminfront/tests/worksmobile.spec.ts +++ b/adminfront/tests/worksmobile.spec.ts @@ -605,6 +605,10 @@ test.describe("Worksmobile tenant management", () => { }, ]); + const updateRowCheckbox = userComparisonSection + .getByRole("row", { name: /이업데이트/ }) + .getByRole("checkbox"); + await expect(updateRowCheckbox).not.toBeChecked(); await page .getByRole("row", { name: /이업데이트/ }) .getByRole("checkbox") diff --git a/devfront/src/features/clients/ClientsPage.test.tsx b/devfront/src/features/clients/ClientsPage.test.tsx index e5964584..ff792cd6 100644 --- a/devfront/src/features/clients/ClientsPage.test.tsx +++ b/devfront/src/features/clients/ClientsPage.test.tsx @@ -167,6 +167,20 @@ async function renderPage() { return container; } +async function waitForTextContent(container: HTMLElement, text: string) { + for (let attempt = 0; attempt < 20; attempt += 1) { + if (container.textContent?.includes(text)) { + return; + } + + await act(async () => { + await new Promise((resolve) => setTimeout(resolve, 0)); + }); + } + + throw new Error(`Expected container text to include: ${text}`); +} + describe("ClientsPage", () => { it("expands the list and applies search filters", async () => { fetchClientsMock.mockResolvedValue({ @@ -300,7 +314,7 @@ describe("ClientsPage", () => { fetchDeveloperRequestStatusMock.mockResolvedValue({ status: "none" }); const container = await renderPage(); - expect(container.textContent).toContain("개발자 등록 신청하기"); + await waitForTextContent(container, "개발자 등록 신청하기"); const requestButton = Array.from(container.querySelectorAll("button")).find( (button) => button.textContent === "개발자 등록 신청하기", diff --git a/devfront/src/features/clients/ClientsPage.tsx b/devfront/src/features/clients/ClientsPage.tsx index 1586fd40..e183bded 100644 --- a/devfront/src/features/clients/ClientsPage.tsx +++ b/devfront/src/features/clients/ClientsPage.tsx @@ -21,7 +21,6 @@ import { commonTableShellClass, commonTableViewportClass, } from "../../../../common/ui/table"; -import { DeveloperAccessRequestCard } from "../../components/common/DeveloperAccessRequestCard"; import { ForbiddenMessage } from "../../components/common/ForbiddenMessage"; import { Badge } from "../../components/ui/badge"; import { Button } from "../../components/ui/button"; @@ -54,7 +53,6 @@ import { t } from "../../lib/i18n"; import { resolveProfileRole } from "../../lib/role"; import { cn } from "../../lib/utils"; import { fetchMe } from "../auth/authApi"; -import { useDeveloperAccessGate } from "../developer-access/developerAccessGate"; import { resolveClientCreateAccess } from "./clientCreateAccess"; import { ClientLogo } from "./components/ClientLogo"; @@ -103,19 +101,6 @@ function ClientsPage() { enabled: hasAccessToken, }); - const { - hasDeveloperAccess: hasClientsPageAccess, - isDeveloperRequestPending, - canRequestDeveloperAccess, - isLoadingDeveloperAccessGate, - } = useDeveloperAccessGate({ - hasAccessToken, - profileRole, - tenantId, - requiredPages: ["client_create"], - isLoadingIdentity: isLoadingMe, - }); - const createAccessState = resolveClientCreateAccess({ role: profileRole, accessStatus: requestStatus, @@ -204,7 +189,6 @@ function ClientsPage() { const isLoading = isLoadingClients || isLoadingRequest || - isLoadingDeveloperAccessGate || (hasAccessToken && !profileRole && isLoadingMe); const requestSort = (key: ClientSortKey) => { @@ -219,41 +203,6 @@ function ClientsPage() { ); } - if (!hasClientsPageAccess) { - return ( -
-
- navigate("/developer-requests")} - /> -
-
- ); - } - if (clientError) { const axiosError = clientError as AxiosError<{ error?: string }>; if (axiosError.response?.status === 403) { @@ -289,7 +238,7 @@ function ClientsPage() { {t("ui.dev.clients.new", "새 클라이언트")} - ) : isDeveloperRequestPending ? ( + ) : isClientCreatePending ? (

{t( diff --git a/devfront/tests/devfront-role-switch-report.spec.ts b/devfront/tests/devfront-role-switch-report.spec.ts index 433ea215..e041514d 100644 --- a/devfront/tests/devfront-role-switch-report.spec.ts +++ b/devfront/tests/devfront-role-switch-report.spec.ts @@ -107,6 +107,22 @@ test.describe("DevFront role report", () => { const state = { clients: [makeClient("gitea-client", { name: "Gitea" })], consents: [] as Consent[], + developerRequests: [ + { + id: "req-audit-approved", + userId: "playwright-user", + userName: "Playwright User", + name: "Playwright User", + userEmail: "playwright@example.com", + organization: "Tenant A", + reason: "Need access", + status: "approved", + accessPages: ["audit"], + createdAt: "2026-05-29T00:00:00.000Z", + updatedAt: "2026-05-29T00:10:00.000Z", + approvedAt: "2026-05-29T00:10:00.000Z", + }, + ], auditLogs: [ { event_id: "evt-rp-1", diff --git a/devfront/tests/devfront-security.spec.ts b/devfront/tests/devfront-security.spec.ts index 14c1e69e..8f73fc88 100644 --- a/devfront/tests/devfront-security.spec.ts +++ b/devfront/tests/devfront-security.spec.ts @@ -65,6 +65,22 @@ test.describe("DevFront security and isolation", () => { clients: [] as ReturnType[], consents: [] as Consent[], auditLogsByCursor: undefined, + developerRequests: [ + { + id: "req-audit-approved", + userId: "playwright-user", + userName: "Playwright User", + name: "Playwright User", + userEmail: "playwright@example.com", + organization: "Tenant A", + reason: "Need access", + status: "approved", + accessPages: ["audit"], + createdAt: "2026-05-29T00:00:00.000Z", + updatedAt: "2026-05-29T00:10:00.000Z", + approvedAt: "2026-05-29T00:10:00.000Z", + }, + ], }; await installDevApiMock(page, state); diff --git a/devfront/tests/helpers/devfront-fixtures.ts b/devfront/tests/helpers/devfront-fixtures.ts index 68fc3b7c..72f5931b 100644 --- a/devfront/tests/helpers/devfront-fixtures.ts +++ b/devfront/tests/helpers/devfront-fixtures.ts @@ -242,6 +242,30 @@ export async function installDevApiMock(page: Page, state: DevApiMockState) { const readMockRole = () => (state.mockRole ?? seededRoles.get(page) ?? "super_admin").trim(); + const buildDeveloperAccessStatus = () => { + const requests = state.developerRequests ?? []; + const myRequests = requests.filter((request) => request.userId === "playwright-user"); + const approvedPages = myRequests + .filter((request) => request.status === "approved") + .flatMap((request) => request.accessPages ?? ["all"]); + const pendingPages = myRequests + .filter((request) => request.status === "pending") + .flatMap((request) => request.accessPages ?? ["all"]); + + const latestRequest = myRequests[myRequests.length - 1]; + if (!latestRequest) { + return { + status: "none" as const, + }; + } + + return { + status: latestRequest.status, + approvedPages, + pendingPages, + }; + }; + const buildSelfConfigEditorRelation = (): ClientRelation => ({ relation: "config_editor", subject: "User:playwright-user", @@ -358,10 +382,7 @@ export async function installDevApiMock(page: Page, state: DevApiMockState) { pathname === "/api/v1/dev/developer-request/status") && method === "GET" ) { - const myRequest = (state.developerRequests ?? []).find( - (r) => r.userId === "playwright-user", - ); - return json(route, myRequest || null); + return json(route, buildDeveloperAccessStatus()); } if (