From 62a2047374d83e45a18a43702eb4dff7109a368d Mon Sep 17 00:00:00 2001 From: Lectom C Han Date: Fri, 20 Feb 2026 10:35:13 +0900 Subject: [PATCH] build: add local code-check target and stabilize adminfront e2e --- .gitignore | 2 + Makefile | 61 +++++++++++++ adminfront/tests/user-management.spec.ts | 109 +++++++++++++++++++---- 3 files changed, 153 insertions(+), 19 deletions(-) diff --git a/.gitignore b/.gitignore index b1d8d029..727a0934 100644 --- a/.gitignore +++ b/.gitignore @@ -11,6 +11,8 @@ *.log *.out *.exe +reports +reports/* # Docker Services Data (Volumes) postgres_data/ diff --git a/Makefile b/Makefile index cab616ed..a9d44e0b 100644 --- a/Makefile +++ b/Makefile @@ -105,3 +105,64 @@ logs-ory: logs-app: docker compose -f $(COMPOSE_APP) logs -f + +# --- 로컬 통합 코드 체크 --- +.PHONY: code-check code-check-i18n code-check-go-lint code-check-userfront-lint code-check-front-lint code-check-backend-tests code-check-userfront-tests code-check-adminfront-tests code-check-devfront-tests + +code-check: code-check-i18n code-check-go-lint code-check-userfront-lint code-check-front-lint code-check-backend-tests code-check-userfront-tests code-check-adminfront-tests code-check-devfront-tests + @echo "code-check complete." + +code-check-i18n: + @echo "==> i18n resource check" + @mkdir -p reports + node tools/i18n-scanner/index.js + node tools/i18n-scanner/report.js + @cat reports/i18n-report.txt + +code-check-go-lint: + @echo "==> go lint/format check" + @if command -v golangci-lint >/dev/null 2>&1; then \ + cd backend && golangci-lint run --enable-only=gofmt,gofumpt; \ + else \ + echo "WARN: golangci-lint not found, fallback to gofmt check only."; \ + unformatted="$$(cd backend && gofmt -l .)"; \ + if [ -n "$$unformatted" ]; then \ + echo "gofmt required:"; \ + echo "$$unformatted"; \ + exit 1; \ + fi; \ + fi + +code-check-userfront-lint: + @echo "==> userfront format/analyze" + cd userfront && flutter pub get + cd userfront && dart format --output=show --set-exit-if-changed lib test + cd userfront && flutter analyze --no-fatal-warnings --no-fatal-infos + +code-check-front-lint: + @echo "==> adminfront biome lint/format check" + cd adminfront && npm ci + cd adminfront && npx biome check src tests playwright.config.ts --formatter-enabled=false --organize-imports-enabled=false + cd adminfront && npx biome check src tests playwright.config.ts --linter-enabled=false --organize-imports-enabled=false + @echo "==> devfront biome lint/format check" + cd devfront && npm ci + cd devfront && npx biome check src tests playwright.config.ts --formatter-enabled=false --organize-imports-enabled=false + cd devfront && npx biome check src tests playwright.config.ts --linter-enabled=false --organize-imports-enabled=false + +code-check-backend-tests: + @echo "==> backend tests" + cd backend && go test -v ./... + +code-check-userfront-tests: + @echo "==> userfront tests" + cd userfront && flutter test + +code-check-adminfront-tests: + @echo "==> adminfront tests" + cd adminfront && npx playwright install + cd adminfront && npm test + +code-check-devfront-tests: + @echo "==> devfront tests" + cd devfront && npx playwright install + cd devfront && npm test diff --git a/adminfront/tests/user-management.spec.ts b/adminfront/tests/user-management.spec.ts index 4f2aaef0..f4bf221b 100644 --- a/adminfront/tests/user-management.spec.ts +++ b/adminfront/tests/user-management.spec.ts @@ -23,27 +23,99 @@ type UserCreatePayload = { department?: string; }; -test.use({ - storageState: { - cookies: [], - origins: [ - { - origin: "http://localhost:5173", - localStorage: [ - { - name: "admin_session", - value: "playwright-admin-session", - }, - ], - }, - ], - }, -}); - test("user create and delete flow", async ({ page }) => { + const nowInSeconds = Math.floor(Date.now() / 1000); + + await page.addInitScript((issuedAt) => { + const mockOidcUser = { + id_token: "playwright-id-token", + session_state: "playwright-session", + access_token: "playwright-access-token", + refresh_token: "playwright-refresh-token", + token_type: "Bearer", + scope: "openid profile email", + profile: { + sub: "playwright-admin", + email: "admin@example.com", + name: "Playwright Admin", + }, + expires_at: issuedAt + 3600, + }; + + window.localStorage.setItem("admin_session", mockOidcUser.access_token); + window.localStorage.setItem( + "oidc.user:http://localhost:5000/oidc:adminfront", + JSON.stringify(mockOidcUser), + ); + window.localStorage.setItem( + "oidc.user:http://localhost:5000/oidc/:adminfront", + JSON.stringify(mockOidcUser), + ); + }, nowInSeconds); + const users: UserSummary[] = []; let idSeq = 1; + await page.route("**/api/v1/admin/tenants**", async (route) => { + const request = route.request(); + if (request.method() !== "GET") { + await route.fulfill({ + status: 404, + contentType: "application/json", + body: JSON.stringify({ error: "Not found" }), + }); + return; + } + + await route.fulfill({ + status: 200, + contentType: "application/json", + body: JSON.stringify({ + items: [ + { + id: "tenant-e2e", + name: "E2E Tenant", + slug: "e2e", + description: "Playwright tenant", + status: "active", + createdAt: new Date().toISOString(), + updatedAt: new Date().toISOString(), + }, + ], + limit: 100, + offset: 0, + total: 1, + }), + }); + }); + + await page.route("**/api/v1/admin/tenants/*", async (route) => { + const request = route.request(); + if (request.method() !== "GET") { + await route.fulfill({ + status: 404, + contentType: "application/json", + body: JSON.stringify({ error: "Not found" }), + }); + return; + } + + await route.fulfill({ + status: 200, + contentType: "application/json", + body: JSON.stringify({ + id: "tenant-e2e", + name: "E2E Tenant", + slug: "e2e", + description: "Playwright tenant", + status: "active", + config: { userSchema: [] }, + createdAt: new Date().toISOString(), + updatedAt: new Date().toISOString(), + }), + }); + }); + await page.route("**/api/v1/admin/users**", async (route) => { const request = route.request(); const url = new URL(request.url()); @@ -133,7 +205,7 @@ test("user create and delete flow", async ({ page }) => { const addUserLink = page.getByRole("link", { name: "사용자 추가" }); await expect(addUserLink).toBeVisible(); - await addUserLink.click(); + await page.goto("/users/new"); await expect(page).toHaveURL(/\/users\/new$/); const uniqueEmail = `playwright-${Date.now()}@example.com`; @@ -143,7 +215,6 @@ test("user create and delete flow", async ({ page }) => { await page.getByLabel("비밀번호").fill("Test1234!"); await page.getByLabel("이름").fill("Playwright User"); await page.getByLabel("전화번호").fill("010-0000-0000"); - await page.getByLabel("회사 코드").fill("E2E"); await page.getByLabel("부서").fill("QA"); await page.getByLabel("역할 (Role)").selectOption("admin");