forked from baron/baron-sso
Merge origin/dev into dev
This commit is contained in:
Binary file not shown.
|
Before Width: | Height: | Size: 805 KiB |
Binary file not shown.
|
Before Width: | Height: | Size: 815 KiB |
@@ -12,6 +12,7 @@
|
||||
"lint": "biome check .",
|
||||
"preview": "vite preview",
|
||||
"test": "playwright test",
|
||||
"test:ci": "pnpm test",
|
||||
"test:coverage": "vitest run --coverage --bail 1",
|
||||
"test:unit": "vitest run --bail 1",
|
||||
"test:roles": "playwright test tests/devfront-role-switch-report.spec.ts",
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import { QueryClient, QueryClientProvider } from "@tanstack/react-query";
|
||||
import { act } from "react";
|
||||
import { act, useEffect } from "react";
|
||||
import { createRoot, type Root } from "react-dom/client";
|
||||
import { MemoryRouter, Route, Routes } from "react-router-dom";
|
||||
import { MemoryRouter, Route, Routes, useNavigate } from "react-router-dom";
|
||||
import { afterEach, beforeEach, describe, expect, it, vi } from "vitest";
|
||||
import AppLayout from "./AppLayout";
|
||||
|
||||
@@ -49,6 +49,24 @@ vi.mock("../../lib/i18n", () => ({
|
||||
|
||||
const roots: Root[] = [];
|
||||
|
||||
type TestWindow = Window & {
|
||||
__baronNavigate?: (to: string) => void;
|
||||
};
|
||||
|
||||
function RouteProbe() {
|
||||
const navigate = useNavigate();
|
||||
|
||||
useEffect(() => {
|
||||
(window as TestWindow).__baronNavigate = navigate;
|
||||
|
||||
return () => {
|
||||
delete (window as TestWindow).__baronNavigate;
|
||||
};
|
||||
}, [navigate]);
|
||||
|
||||
return <div>Client outlet</div>;
|
||||
}
|
||||
|
||||
beforeEach(() => {
|
||||
authState.isAuthenticated = true;
|
||||
authState.isLoading = false;
|
||||
@@ -89,7 +107,7 @@ async function renderLayout(initialEntry = "/clients") {
|
||||
<MemoryRouter initialEntries={[initialEntry]}>
|
||||
<Routes>
|
||||
<Route path="/" element={<AppLayout />}>
|
||||
<Route path="clients" element={<div>Client outlet</div>} />
|
||||
<Route path="clients" element={<RouteProbe />} />
|
||||
<Route path="profile" element={<div>Profile outlet</div>} />
|
||||
</Route>
|
||||
</Routes>
|
||||
@@ -181,4 +199,15 @@ describe("devfront AppLayout", () => {
|
||||
|
||||
expect(authState.signinSilent).toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it("attempts silent renewal when route changes and the session is expiring", async () => {
|
||||
authState.user.expires_at = Math.floor(Date.now() / 1000) + 60;
|
||||
await renderLayout();
|
||||
|
||||
await act(async () => {
|
||||
(window as TestWindow).__baronNavigate?.("/profile");
|
||||
});
|
||||
|
||||
expect(authState.signinSilent).toHaveBeenCalled();
|
||||
});
|
||||
});
|
||||
|
||||
@@ -201,4 +201,85 @@ describe("ClientConsentsPage RP custom claims", () => {
|
||||
}),
|
||||
);
|
||||
});
|
||||
|
||||
it("keeps date claim inputs and timezone selectors on the same row", async () => {
|
||||
fetchClientMock.mockResolvedValue({
|
||||
...clientDetail,
|
||||
client: {
|
||||
...clientDetail.client,
|
||||
metadata: {
|
||||
id_token_claims: [
|
||||
{
|
||||
namespace: "rp_claims",
|
||||
key: "contract_date",
|
||||
value: "",
|
||||
valueType: "date",
|
||||
readPermission: "admin_only",
|
||||
writePermission: "admin_only",
|
||||
},
|
||||
],
|
||||
},
|
||||
},
|
||||
});
|
||||
fetchConsentsMock.mockResolvedValue({
|
||||
items: [
|
||||
{
|
||||
subject: "user-1",
|
||||
userName: "Consent User",
|
||||
clientId: "client-a",
|
||||
clientName: "Claims App",
|
||||
grantedScopes: ["openid", "profile"],
|
||||
authenticatedAt: "2026-06-11T09:00:00Z",
|
||||
createdAt: "2026-06-10T09:00:00Z",
|
||||
status: "active",
|
||||
tenantId: "tenant-1",
|
||||
tenantName: "Hanmac",
|
||||
rpMetadata: {
|
||||
contract_date: 1781017200,
|
||||
contract_date_permissions: {
|
||||
readPermission: "admin_only",
|
||||
writePermission: "admin_only",
|
||||
},
|
||||
},
|
||||
},
|
||||
],
|
||||
});
|
||||
fetchRPUserMetadataMock.mockResolvedValue({
|
||||
clientId: "client-a",
|
||||
userId: "user-1",
|
||||
metadata: {
|
||||
contract_date: 1781017200,
|
||||
contract_date_permissions: {
|
||||
readPermission: "admin_only",
|
||||
writePermission: "admin_only",
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
const { container } = await renderPage();
|
||||
|
||||
const editButton = Array.from(container.querySelectorAll("button")).find(
|
||||
(button) =>
|
||||
button.textContent?.includes("사용자 Claim 설정") ||
|
||||
button.textContent?.includes("User Claim Settings"),
|
||||
);
|
||||
|
||||
await act(async () => {
|
||||
editButton?.dispatchEvent(new MouseEvent("click", { bubbles: true }));
|
||||
});
|
||||
await flush();
|
||||
|
||||
const dateInput = container.querySelector(
|
||||
'input[aria-label="contract_date date"]',
|
||||
);
|
||||
const timeZoneSelect = container.querySelector(
|
||||
'select[aria-label="contract_date timezone"]',
|
||||
);
|
||||
|
||||
expect(dateInput).not.toBeNull();
|
||||
expect(timeZoneSelect).not.toBeNull();
|
||||
expect(dateInput?.parentElement).toBe(timeZoneSelect?.parentElement);
|
||||
expect(dateInput?.parentElement?.className).toContain("items-center");
|
||||
expect(dateInput?.parentElement?.className).not.toContain("flex-col");
|
||||
});
|
||||
});
|
||||
|
||||
@@ -1060,7 +1060,14 @@ function ClientConsentsPage() {
|
||||
aria-label={`${row.key} ${row.valueType}`}
|
||||
/>
|
||||
) : (
|
||||
<div className="flex flex-col gap-2">
|
||||
<div
|
||||
className={cn(
|
||||
"flex gap-2",
|
||||
row.valueType === "date" || row.valueType === "datetime"
|
||||
? "items-center"
|
||||
: "flex-col",
|
||||
)}
|
||||
>
|
||||
<Input
|
||||
type={rpClaimInputType(row.valueType)}
|
||||
inputMode={rpClaimInputMode(row.valueType)}
|
||||
@@ -1087,7 +1094,7 @@ function ClientConsentsPage() {
|
||||
timeZone: event.target.value,
|
||||
})
|
||||
}
|
||||
className="h-10 rounded-md border border-input bg-background px-3 font-mono text-xs shadow-sm focus-visible:outline-none focus-visible:ring-1 focus-visible:ring-ring"
|
||||
className="h-10 min-w-[160px] rounded-md border border-input bg-background px-3 font-mono text-xs shadow-sm focus-visible:outline-none focus-visible:ring-1 focus-visible:ring-ring"
|
||||
aria-label={`${row.key} timezone`}
|
||||
>
|
||||
{timeZoneOptions.map((timeZone) => (
|
||||
|
||||
@@ -409,7 +409,7 @@ describe("ClientGeneralPage RP claims", () => {
|
||||
);
|
||||
});
|
||||
|
||||
it("shows supported scopes and custom claims without integrated offline_access from the add scope button", async () => {
|
||||
it("shows supported scopes including offline_access and custom claims from the add scope button", async () => {
|
||||
const { container } = await renderPage();
|
||||
|
||||
const addScopeButton = Array.from(
|
||||
@@ -422,7 +422,7 @@ describe("ClientGeneralPage RP claims", () => {
|
||||
});
|
||||
await flush();
|
||||
|
||||
expect(container.textContent).not.toContain("offline_access");
|
||||
expect(container.textContent).toContain("offline_access");
|
||||
expect(container.textContent).toContain("old_claim");
|
||||
|
||||
const customClaimButton = Array.from(
|
||||
|
||||
@@ -759,6 +759,15 @@ function ClientGeneralPage() {
|
||||
description: tenantScopeDescription,
|
||||
source: "standard",
|
||||
},
|
||||
{
|
||||
id: "standard-offline-access",
|
||||
name: "offline_access",
|
||||
description: t(
|
||||
"msg.dev.clients.scopes.offline_access",
|
||||
"refresh token 발급 요청",
|
||||
),
|
||||
source: "standard",
|
||||
},
|
||||
],
|
||||
[tenantScopeDescription],
|
||||
);
|
||||
@@ -2789,6 +2798,7 @@ function ClientGeneralPage() {
|
||||
) : (
|
||||
<div className="flex flex-col gap-2">
|
||||
<Input
|
||||
key={claim.valueType}
|
||||
type={claimDefaultInputType(claim.valueType)}
|
||||
inputMode={claimDefaultInputMode(
|
||||
claim.valueType,
|
||||
|
||||
@@ -99,7 +99,7 @@ test.describe("DevFront RP claim cache", () => {
|
||||
await expect(claimKeyInput).toHaveValue("new_claim");
|
||||
});
|
||||
|
||||
test("adds supported scopes and custom claim keys from the scope picker without offline_access", async ({
|
||||
test("adds supported scopes and custom claim keys from the scope picker including offline_access", async ({
|
||||
page,
|
||||
}) => {
|
||||
const state = {
|
||||
@@ -142,9 +142,9 @@ test.describe("DevFront RP claim cache", () => {
|
||||
.getByRole("button", { name: /스코프 추가|Scope 추가|Add Scope/i })
|
||||
.click();
|
||||
|
||||
await expect(page.getByText("offline_access", { exact: true })).toHaveCount(
|
||||
0,
|
||||
);
|
||||
await expect(
|
||||
page.getByText("offline_access", { exact: true }),
|
||||
).toBeVisible();
|
||||
await expect(
|
||||
page.getByRole("button", { name: /employee_code/ }),
|
||||
).toBeVisible();
|
||||
@@ -328,7 +328,11 @@ test.describe("DevFront RP claim cache", () => {
|
||||
.getByPlaceholder(/기본값을 입력하세요|Enter the default value/i)
|
||||
.first()
|
||||
.fill("3.14");
|
||||
const responsePromise = page.waitForResponse(
|
||||
"**/api/v1/dev/clients/client-claims",
|
||||
);
|
||||
await page.getByRole("button", { name: /^저장$|^Save$/i }).click();
|
||||
await responsePromise;
|
||||
|
||||
await expect
|
||||
.poll(
|
||||
@@ -357,13 +361,7 @@ test.describe("DevFront RP claim cache", () => {
|
||||
const defaultValueInput = page
|
||||
.getByPlaceholder(/기본값을 입력하세요|Enter the default value/i)
|
||||
.first();
|
||||
await expect(defaultValueInput).toHaveAttribute("inputmode", "numeric");
|
||||
await expect(defaultValueInput).toHaveAttribute("pattern", "-?[0-9]*");
|
||||
await defaultValueInput.fill("3.14");
|
||||
|
||||
await expect(
|
||||
page.getByText(/Claim 기본값이 타입과 맞지 않습니다|does not match/i),
|
||||
).toBeVisible();
|
||||
await expect(
|
||||
page.getByRole("button", { name: /^저장$|^Save$/i }),
|
||||
).toBeDisabled();
|
||||
|
||||
239
devfront/tests/devfront-login-claims.spec.ts
Normal file
239
devfront/tests/devfront-login-claims.spec.ts
Normal file
@@ -0,0 +1,239 @@
|
||||
import { expect, test } from "@playwright/test";
|
||||
import {
|
||||
getPersistedOidcUser,
|
||||
installDevApiMock,
|
||||
seedAuth,
|
||||
} from "./helpers/devfront-fixtures";
|
||||
import { captureEvidence } from "./helpers/evidence";
|
||||
|
||||
type ClaimScenario = {
|
||||
title: string;
|
||||
role: "super_admin" | "user";
|
||||
tenantName: string;
|
||||
userMeTenantId: string;
|
||||
userMeCompanyCode: string;
|
||||
profileClaims: Record<string, unknown>;
|
||||
expectedProfileAssertions: Record<string, unknown>;
|
||||
expectTenantsToBeAbsent?: boolean;
|
||||
};
|
||||
|
||||
const claimScenarios: ClaimScenario[] = [
|
||||
{
|
||||
title: "Server Side App preserves tenant and rp claims",
|
||||
role: "super_admin",
|
||||
tenantName: "Server Side Tenant",
|
||||
userMeTenantId: "tenant-server",
|
||||
userMeCompanyCode: "server-hq",
|
||||
profileClaims: {
|
||||
tenant_id: "tenant-server",
|
||||
companyCode: "server-hq",
|
||||
profile: {
|
||||
names: {
|
||||
name: "서버 앱 사용자",
|
||||
},
|
||||
emails: ["server@example.com"],
|
||||
},
|
||||
joined_tenants: ["tenant-server", "tenant-ops"],
|
||||
tenants: {
|
||||
"tenant-server": {
|
||||
department: "Platform",
|
||||
grade: "Lead",
|
||||
},
|
||||
"tenant-ops": {
|
||||
department: "Operations",
|
||||
grade: "Member",
|
||||
},
|
||||
},
|
||||
rp_claims: {
|
||||
approvalLevel: "A",
|
||||
},
|
||||
metadata: {
|
||||
rp_custom_claims: {
|
||||
"server-app": {
|
||||
approvalLevel: "A",
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
expectedProfileAssertions: {
|
||||
tenant_id: "tenant-server",
|
||||
companyCode: "server-hq",
|
||||
joined_tenants: ["tenant-server", "tenant-ops"],
|
||||
rp_claims: {
|
||||
approvalLevel: "A",
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
title: "PKCE preserves nested profile claims without tenant map expansion",
|
||||
role: "user",
|
||||
tenantName: "PKCE Tenant",
|
||||
userMeTenantId: "tenant-pkce",
|
||||
userMeCompanyCode: "pkce-hq",
|
||||
profileClaims: {
|
||||
tenant_id: "tenant-pkce",
|
||||
companyCode: "pkce-hq",
|
||||
profile: {
|
||||
names: {
|
||||
name: "PKCE 사용자",
|
||||
},
|
||||
emails: ["pkce@example.com"],
|
||||
},
|
||||
joined_tenants: ["tenant-pkce"],
|
||||
rp_claims: {
|
||||
features: ["sso", "claims"],
|
||||
},
|
||||
metadata: {
|
||||
rp_custom_claims: {
|
||||
"pkce-app": {
|
||||
features: ["sso", "claims"],
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
expectedProfileAssertions: {
|
||||
tenant_id: "tenant-pkce",
|
||||
companyCode: "pkce-hq",
|
||||
joined_tenants: ["tenant-pkce"],
|
||||
rp_claims: {
|
||||
features: ["sso", "claims"],
|
||||
},
|
||||
},
|
||||
expectTenantsToBeAbsent: true,
|
||||
},
|
||||
{
|
||||
title: "Headless login keeps session claims together with rp claims",
|
||||
role: "super_admin",
|
||||
tenantName: "Headless Tenant",
|
||||
userMeTenantId: "tenant-headless",
|
||||
userMeCompanyCode: "headless-hq",
|
||||
profileClaims: {
|
||||
tenant_id: "tenant-headless",
|
||||
companyCode: "headless-hq",
|
||||
profile: {
|
||||
names: {
|
||||
name: "헤드리스 사용자",
|
||||
},
|
||||
emails: ["headless@example.com"],
|
||||
},
|
||||
joined_tenants: ["tenant-headless", "tenant-support"],
|
||||
tenants: {
|
||||
"tenant-headless": {
|
||||
department: "Automation",
|
||||
grade: "Manager",
|
||||
},
|
||||
"tenant-support": {
|
||||
department: "Support",
|
||||
grade: "Agent",
|
||||
},
|
||||
},
|
||||
rp_claims: {
|
||||
approvalLevel: "B",
|
||||
loginMode: "headless",
|
||||
},
|
||||
sid: "session-headless-1",
|
||||
session_id: "session-headless-1",
|
||||
metadata: {
|
||||
rp_custom_claims: {
|
||||
"headless-app": {
|
||||
approvalLevel: "B",
|
||||
loginMode: "headless",
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
expectedProfileAssertions: {
|
||||
tenant_id: "tenant-headless",
|
||||
companyCode: "headless-hq",
|
||||
joined_tenants: ["tenant-headless", "tenant-support"],
|
||||
rp_claims: {
|
||||
approvalLevel: "B",
|
||||
loginMode: "headless",
|
||||
},
|
||||
sid: "session-headless-1",
|
||||
session_id: "session-headless-1",
|
||||
},
|
||||
},
|
||||
];
|
||||
|
||||
test.describe("DevFront login claims", () => {
|
||||
test.afterEach(async ({ page }, testInfo) => {
|
||||
if (testInfo.status === "passed") {
|
||||
await captureEvidence(page, testInfo, testInfo.title);
|
||||
}
|
||||
});
|
||||
|
||||
for (const scenario of claimScenarios) {
|
||||
test(scenario.title, async ({ page }) => {
|
||||
await seedAuth(page, {
|
||||
role: scenario.role,
|
||||
profile: scenario.profileClaims,
|
||||
});
|
||||
|
||||
await installDevApiMock(page, {
|
||||
clients: [],
|
||||
consents: [],
|
||||
auditLogsByCursor: undefined,
|
||||
users: [],
|
||||
tenants: [
|
||||
{
|
||||
id: scenario.userMeTenantId,
|
||||
name: scenario.tenantName,
|
||||
slug: scenario.userMeCompanyCode,
|
||||
},
|
||||
],
|
||||
});
|
||||
|
||||
await page.route("**/api/v1/user/me", async (route) => {
|
||||
await route.fulfill({
|
||||
status: 200,
|
||||
contentType: "application/json",
|
||||
body: JSON.stringify({
|
||||
id: "playwright-user",
|
||||
loginId: "playwright@example.com",
|
||||
email: "playwright@example.com",
|
||||
name: "Playwright User",
|
||||
phoneNumber: "",
|
||||
department: "QA",
|
||||
tenantId: "",
|
||||
tenantName: "",
|
||||
role: scenario.role,
|
||||
createdAt: "2026-06-01T00:00:00.000Z",
|
||||
updatedAt: "2026-06-01T00:00:00.000Z",
|
||||
}),
|
||||
});
|
||||
});
|
||||
|
||||
await page.goto("/profile");
|
||||
|
||||
await expect(
|
||||
page.getByRole("heading", { name: "내 정보" }),
|
||||
).toBeVisible();
|
||||
const storedUser = await getPersistedOidcUser(page);
|
||||
expect(storedUser).not.toBeNull();
|
||||
expect(storedUser?.profile).toMatchObject(
|
||||
scenario.expectedProfileAssertions,
|
||||
);
|
||||
if (scenario.expectTenantsToBeAbsent) {
|
||||
expect(storedUser?.profile).not.toHaveProperty("tenants");
|
||||
} else {
|
||||
expect(storedUser?.profile).toHaveProperty("tenants");
|
||||
}
|
||||
await expect(
|
||||
page.getByText(String(scenario.profileClaims.tenant_id)),
|
||||
).toBeVisible();
|
||||
await expect(page.getByText(scenario.userMeCompanyCode)).toBeVisible();
|
||||
await page.getByRole("button", { name: "권한 및 역할" }).click();
|
||||
await expect(
|
||||
page.getByRole("heading", { name: "시스템 역할" }),
|
||||
).toBeVisible();
|
||||
await expect(
|
||||
page.getByText(
|
||||
scenario.role === "super_admin"
|
||||
? /^(시스템 관리자|Super Admin|SUPER_ADMIN)$/i
|
||||
: /^(일반 사용자|General User|USER)$/i,
|
||||
),
|
||||
).toBeVisible();
|
||||
});
|
||||
}
|
||||
});
|
||||
@@ -73,6 +73,22 @@ export type DeveloperRequest = {
|
||||
adminNotes?: string; // 추가
|
||||
};
|
||||
|
||||
export type SeedAuthOptions = {
|
||||
role?: string;
|
||||
accessToken?: string;
|
||||
idToken?: string;
|
||||
refreshToken?: string;
|
||||
sessionState?: string;
|
||||
expiresInSeconds?: number;
|
||||
state?: Record<string, unknown>;
|
||||
profile?: Record<string, unknown>;
|
||||
tenantId?: string;
|
||||
companyCode?: string;
|
||||
email?: string;
|
||||
name?: string;
|
||||
phone?: string;
|
||||
};
|
||||
|
||||
export type ClientRelation = {
|
||||
relation: string;
|
||||
subject: string;
|
||||
@@ -148,30 +164,100 @@ export function makeClient(
|
||||
};
|
||||
}
|
||||
|
||||
export async function seedAuth(page: Page, role?: string) {
|
||||
function resolveSeedAuthOptions(
|
||||
roleOrOptions?: string | SeedAuthOptions,
|
||||
): Required<Pick<SeedAuthOptions, "role">> & SeedAuthOptions {
|
||||
if (typeof roleOrOptions === "string") {
|
||||
return { role: roleOrOptions };
|
||||
}
|
||||
return { role: roleOrOptions?.role ?? "super_admin", ...roleOrOptions };
|
||||
}
|
||||
|
||||
export async function getPersistedOidcUser(page: Page) {
|
||||
return page.evaluate(() => {
|
||||
const storage = window.localStorage;
|
||||
for (let index = 0; index < storage.length; index += 1) {
|
||||
const key = storage.key(index);
|
||||
if (
|
||||
key === null ||
|
||||
!key.startsWith("oidc.user:") ||
|
||||
!key.endsWith(":devfront")
|
||||
) {
|
||||
continue;
|
||||
}
|
||||
|
||||
const rawValue = storage.getItem(key);
|
||||
if (!rawValue) {
|
||||
continue;
|
||||
}
|
||||
|
||||
try {
|
||||
return JSON.parse(rawValue) as Record<string, unknown>;
|
||||
} catch {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
});
|
||||
}
|
||||
|
||||
export async function seedAuth(
|
||||
page: Page,
|
||||
roleOrOptions?: string | SeedAuthOptions,
|
||||
) {
|
||||
const options = resolveSeedAuthOptions(roleOrOptions);
|
||||
const nowInSeconds = Math.floor(Date.now() / 1000);
|
||||
seededRoles.set(page, role || "super_admin");
|
||||
const profile = {
|
||||
sub: "playwright-user",
|
||||
email: options.email ?? "playwright@example.com",
|
||||
name: options.name ?? "Playwright User",
|
||||
phone: options.phone ?? "",
|
||||
role: options.profile?.role ?? options.role,
|
||||
tenant_id: options.tenantId ?? "tenant-a",
|
||||
companyCode: options.companyCode ?? "tenant-a",
|
||||
...options.profile,
|
||||
};
|
||||
seededRoles.set(
|
||||
page,
|
||||
typeof profile.role === "string" ? profile.role : options.role,
|
||||
);
|
||||
|
||||
await page.addInitScript(
|
||||
({ issuedAt, injectedRole }) => {
|
||||
({
|
||||
issuedAt,
|
||||
injectedRole,
|
||||
injectedProfile,
|
||||
injectedState,
|
||||
injectedIdToken,
|
||||
injectedAccessToken,
|
||||
injectedRefreshToken,
|
||||
injectedSessionState,
|
||||
injectedExpiresInSeconds,
|
||||
}) => {
|
||||
(
|
||||
window as Window & typeof globalThis & { _IS_TEST_MODE?: boolean }
|
||||
)._IS_TEST_MODE = true;
|
||||
|
||||
const mockOidcUser = {
|
||||
id_token: "playwright-id-token",
|
||||
session_state: "playwright-session",
|
||||
access_token: "playwright-access-token",
|
||||
refresh_token: "playwright-refresh-token",
|
||||
id_token: injectedIdToken,
|
||||
session_state: injectedSessionState,
|
||||
access_token: injectedAccessToken,
|
||||
refresh_token: injectedRefreshToken,
|
||||
token_type: "Bearer",
|
||||
scope: "openid profile email",
|
||||
profile: {
|
||||
sub: "playwright-user",
|
||||
email: "playwright@example.com",
|
||||
name: "Playwright User",
|
||||
...(injectedRole ? { role: injectedRole } : {}),
|
||||
phone: "",
|
||||
role: injectedRole || "super_admin",
|
||||
tenant_id: "tenant-a",
|
||||
companyCode: "tenant-a",
|
||||
...(injectedProfile || {}),
|
||||
},
|
||||
expires_at: issuedAt + 3600,
|
||||
state: injectedState,
|
||||
expires_at: issuedAt + injectedExpiresInSeconds,
|
||||
};
|
||||
|
||||
const storageKeys = [
|
||||
@@ -191,9 +277,25 @@ export async function seedAuth(page: Page, role?: string) {
|
||||
}
|
||||
|
||||
window.localStorage.setItem("dev_role", injectedRole || "super_admin");
|
||||
window.localStorage.setItem("dev_tenant_id", "tenant-a");
|
||||
window.localStorage.setItem(
|
||||
"dev_tenant_id",
|
||||
typeof injectedProfile.tenant_id === "string"
|
||||
? injectedProfile.tenant_id
|
||||
: "tenant-a",
|
||||
);
|
||||
},
|
||||
{
|
||||
issuedAt: nowInSeconds,
|
||||
injectedRole:
|
||||
typeof profile.role === "string" ? profile.role : options.role,
|
||||
injectedProfile: profile,
|
||||
injectedState: options.state ?? { returnTo: "/clients" },
|
||||
injectedIdToken: options.idToken ?? "playwright-id-token",
|
||||
injectedAccessToken: options.accessToken ?? "playwright-access-token",
|
||||
injectedRefreshToken: options.refreshToken ?? "playwright-refresh-token",
|
||||
injectedSessionState: options.sessionState ?? "playwright-session",
|
||||
injectedExpiresInSeconds: options.expiresInSeconds ?? 3600,
|
||||
},
|
||||
{ issuedAt: nowInSeconds, injectedRole: role ?? "" },
|
||||
);
|
||||
|
||||
await page.route("**/oidc/**", async (route) => {
|
||||
|
||||
Reference in New Issue
Block a user