forked from baron/baron-sso
240 lines
7.4 KiB
TypeScript
240 lines
7.4 KiB
TypeScript
import { expect, test } from "@playwright/test";
|
|
import {
|
|
type AuditLog,
|
|
type Consent,
|
|
installDevApiMock,
|
|
makeClient,
|
|
seedAuth,
|
|
} from "./helpers/devfront-fixtures";
|
|
import { captureEvidence } from "./helpers/evidence";
|
|
|
|
const appNamePlaceholder = /My Awesome Application|예: 멋진 애플리케이션/i;
|
|
|
|
test.describe("DevFront role report", () => {
|
|
test.beforeEach(async ({ page }) => {
|
|
page.on("dialog", async (dialog) => {
|
|
await dialog.accept();
|
|
});
|
|
});
|
|
|
|
test("user can enter and sees empty RP list", async ({ page }, testInfo) => {
|
|
await seedAuth(page, "user");
|
|
await installDevApiMock(page, {
|
|
clients: [],
|
|
consents: [] as Consent[],
|
|
auditLogs: [] as AuditLog[],
|
|
});
|
|
|
|
await page.goto("/clients");
|
|
await expect(
|
|
page.getByText(/조회 가능한 RP가 없습니다|No RPs are available/i),
|
|
).toBeVisible();
|
|
await expect(
|
|
page.getByRole("heading", {
|
|
name: /^연동 앱$|^Connected Application$/i,
|
|
}),
|
|
).toBeVisible();
|
|
await captureEvidence(page, testInfo, "role-user-empty-rps");
|
|
});
|
|
|
|
test("user sees developer request entry point on overview", async ({
|
|
page,
|
|
}, testInfo) => {
|
|
await seedAuth(page, "user");
|
|
await installDevApiMock(page, {
|
|
clients: [],
|
|
consents: [] as Consent[],
|
|
auditLogs: [] as AuditLog[],
|
|
auditLogsByCursor: undefined,
|
|
developerRequests: [],
|
|
});
|
|
|
|
await page.goto("/");
|
|
await expect(
|
|
page.getByText(
|
|
/대시보드는 개발자 권한이 있어야 볼 수 있습니다|개발자 권한 신청을 검토 중입니다./,
|
|
),
|
|
).toBeVisible();
|
|
const requestBtn = page.getByRole("button", {
|
|
name: /개발자 권한 신청/,
|
|
});
|
|
await expect(requestBtn).toBeVisible();
|
|
await requestBtn.click();
|
|
await expect(page).toHaveURL(/\/developer-requests$/);
|
|
await captureEvidence(page, testInfo, "role-user-overview-request-entry");
|
|
});
|
|
|
|
test("user with approved developer request sees overview without CTA", async ({
|
|
page,
|
|
}, testInfo) => {
|
|
await seedAuth(page, "user");
|
|
await installDevApiMock(page, {
|
|
clients: [],
|
|
consents: [] as Consent[],
|
|
auditLogs: [] as AuditLog[],
|
|
auditLogsByCursor: undefined,
|
|
developerRequests: [
|
|
{
|
|
id: "req-approved",
|
|
userId: "playwright-user",
|
|
userName: "Playwright User",
|
|
name: "Playwright User",
|
|
userEmail: "playwright@example.com",
|
|
organization: "Tenant A",
|
|
reason: "Need access",
|
|
status: "approved",
|
|
createdAt: "2026-05-29T00:00:00.000Z",
|
|
updatedAt: "2026-05-29T00:00:00.000Z",
|
|
approvedAt: "2026-05-29T00:10:00.000Z",
|
|
},
|
|
],
|
|
});
|
|
|
|
await page.goto("/");
|
|
await expect(
|
|
page.getByRole("heading", { name: /운영 현황/ }),
|
|
).toBeVisible();
|
|
await expect(
|
|
page.getByRole("button", { name: /개발자 권한 신청/ }),
|
|
).toHaveCount(0);
|
|
await captureEvidence(page, testInfo, "role-user-overview-approved");
|
|
});
|
|
|
|
test("rp_admin sees only assigned Gitea app and its logs", async ({
|
|
page,
|
|
}, testInfo) => {
|
|
await seedAuth(page, "rp_admin");
|
|
const state = {
|
|
clients: [makeClient("gitea-client", { name: "Gitea" })],
|
|
consents: [] as Consent[],
|
|
auditLogs: [
|
|
{
|
|
event_id: "evt-rp-1",
|
|
timestamp: "2026-03-04T01:00:00.000Z",
|
|
user_id: "rp-admin-user",
|
|
event_type: "CLIENT_UPDATE",
|
|
status: "success" as const,
|
|
ip_address: "127.0.0.1",
|
|
user_agent: "playwright",
|
|
details: JSON.stringify({
|
|
action: "UPDATE_CLIENT",
|
|
target_id: "gitea-client",
|
|
tenant_id: "tenant-a",
|
|
}),
|
|
},
|
|
] as AuditLog[],
|
|
};
|
|
await installDevApiMock(page, state);
|
|
|
|
await page.goto("/clients");
|
|
await expect(
|
|
page.getByRole("link", { name: "Gitea", exact: true }),
|
|
).toBeVisible();
|
|
await expect(
|
|
page.getByRole("cell", { name: "gitea-client" }),
|
|
).toBeVisible();
|
|
await captureEvidence(page, testInfo, "role-rp-admin-clients");
|
|
|
|
await page.goto("/audit-logs");
|
|
await expect(page.getByText("UPDATE_CLIENT")).toBeVisible();
|
|
await expect(page.getByText("gitea-client")).toBeVisible();
|
|
await captureEvidence(page, testInfo, "role-rp-admin-audit");
|
|
});
|
|
|
|
test("tenant_admin can manage tenant apps and see tenant logs", async ({
|
|
page,
|
|
}, testInfo) => {
|
|
await seedAuth(page, "tenant_admin");
|
|
const state = {
|
|
clients: [
|
|
makeClient("tenant-a-app-1", { name: "Tenant A CRM" }),
|
|
makeClient("tenant-a-app-2", { name: "Tenant A ERP" }),
|
|
],
|
|
consents: [] as Consent[],
|
|
auditLogs: [] as AuditLog[],
|
|
auditLogsByCursor: undefined,
|
|
};
|
|
await installDevApiMock(page, state);
|
|
|
|
await page.goto("/clients");
|
|
await expect(page.getByText("Tenant A CRM")).toBeVisible();
|
|
await expect(page.getByText("Tenant A ERP")).toBeVisible();
|
|
await captureEvidence(page, testInfo, "role-tenant-admin-clients");
|
|
|
|
await page.goto("/clients/tenant-a-app-1/settings");
|
|
await page
|
|
.getByPlaceholder(appNamePlaceholder)
|
|
.fill("Tenant A CRM Updated");
|
|
|
|
const updatePromise = page.waitForResponse(
|
|
(r) =>
|
|
r.url().includes("/api/v1/dev/clients") &&
|
|
r.request().method() === "PUT",
|
|
);
|
|
await page.getByRole("button", { name: /^저장$|^Save$/i }).click();
|
|
await updatePromise;
|
|
|
|
await page.goto("/audit-logs");
|
|
await expect(page.getByText("UPDATE_CLIENT")).toBeVisible({
|
|
timeout: 30000,
|
|
});
|
|
await expect(page.getByText("tenant-a-app-1")).toBeVisible();
|
|
await captureEvidence(page, testInfo, "role-tenant-admin-audit");
|
|
});
|
|
|
|
test("super_admin sees all and can generate log entries", async ({
|
|
page,
|
|
}, testInfo) => {
|
|
await seedAuth(page, "super_admin");
|
|
const state = {
|
|
clients: [
|
|
makeClient("tenant-a-app", { name: "Tenant A App" }),
|
|
makeClient("tenant-b-app", { name: "Tenant B App" }),
|
|
],
|
|
consents: [] as Consent[],
|
|
auditLogs: [] as AuditLog[],
|
|
auditLogsByCursor: undefined,
|
|
};
|
|
await installDevApiMock(page, state);
|
|
|
|
await page.goto("/clients");
|
|
await expect(page.getByText("Tenant A App")).toBeVisible();
|
|
await expect(page.getByText("Tenant B App")).toBeVisible();
|
|
await captureEvidence(page, testInfo, "role-super-admin-clients");
|
|
|
|
await page.goto("/clients/new");
|
|
await page
|
|
.getByPlaceholder(appNamePlaceholder)
|
|
.fill("Super Admin Created App");
|
|
await page
|
|
.getByPlaceholder(/https:\/\/app\.example\.com\/callback/i)
|
|
.fill("https://super-admin.example.com/callback");
|
|
|
|
const createPromise = page.waitForResponse(
|
|
(r) =>
|
|
r.url().includes("/api/v1/dev/clients") &&
|
|
r.request().method() === "POST",
|
|
);
|
|
await page.getByRole("button", { name: /앱 생성|Create/i }).click();
|
|
await createPromise;
|
|
await expect(page).toHaveURL(/\/clients\/client-\d+\/settings$/);
|
|
await expect
|
|
.poll(() =>
|
|
state.auditLogs.some((item) => {
|
|
try {
|
|
return JSON.parse(item.details)?.action === "CREATE_CLIENT";
|
|
} catch {
|
|
return false;
|
|
}
|
|
}),
|
|
)
|
|
.toBe(true);
|
|
|
|
await page.goto("/audit-logs");
|
|
await expect(page.getByText("CREATE_CLIENT")).toBeVisible({
|
|
timeout: 30000,
|
|
});
|
|
await captureEvidence(page, testInfo, "role-super-admin-audit");
|
|
});
|
|
});
|