diff --git a/app/gateway-frontend/e2e/admin-runtime-actions.spec.ts b/app/gateway-frontend/e2e/admin-runtime-actions.spec.ts index 5f8a9f19..fbe6f66f 100644 --- a/app/gateway-frontend/e2e/admin-runtime-actions.spec.ts +++ b/app/gateway-frontend/e2e/admin-runtime-actions.spec.ts @@ -1,4 +1,5 @@ import { expect, test, type Page, type Route } from '@playwright/test'; +import { mkdir, writeFile } from 'node:fs/promises'; const response = (data: unknown) => ({ result: { data } }); const operationNames = (route: Route): string[] => { @@ -43,6 +44,7 @@ const installFixture = async ( gameIsUnited?: number; openerOnly?: boolean; diagnosticsFixture?: boolean; + auditScopes?: string[]; } = {} ) => { let requested = false; @@ -86,6 +88,10 @@ const installFixture = async ( installActive = true; } const results = operations.map((operation) => { + if (operation === 'auth.issueGameSession') { + requestBodies.push(body); + return response({ profile: 'hwe:default', gameToken: 'audit-fixture-token' }); + } if (operation === 'admin.profiles.diagnostics' && options.diagnosticsFixture) { diagnosticReads += 1; return response({ @@ -154,6 +160,18 @@ const installFixture = async ( } if (operation === 'admin.capabilities.list') { const capabilities = [ + ...(options.auditScopes + ? [ + { + permission: 'admin.playAudit.read', + label: '플레이 감사', + description: '조회', + risk: 'HIGH', + scope: 'PROFILE', + scopes: options.auditScopes, + }, + ] + : []), { permission: 'admin.users.manage', label: '사용자·제재 관리', @@ -683,3 +701,76 @@ test('runtime diagnostics shows expired lease and retains history after recovery await expect(panel).toContainText('턴 프로세스와 실행 권한 정상'); await expect(panel).toContainText('Heartbeat deadline exceeded'); }); + +for (const width of [1280, 390]) { + test(`play audit entry uses scoped capability and private session transfer at ${width}px`, async ({ page }) => { + await page.setViewportSize({ width, height: 900 }); + const fixture = await installFixture(page, { auditScopes: ['hwe:default'] }); + await page.route('**/hwe/play-audit', (route) => + route.fulfill({ contentType: 'text/html', body: '
+ {{ auditEntryError[profile.profileName] }} +
+