첫 커밋: 로컬 프로젝트 업로드
This commit is contained in:
@@ -0,0 +1,202 @@
|
||||
import { type BrowserContext, expect, type Page, test } from "@playwright/test";
|
||||
|
||||
const USERFRONT_BASE_URL =
|
||||
process.env.USERFRONT_BASE_URL ?? "https://sso.example.test";
|
||||
const ADMINFRONT_URL = process.env.ADMINFRONT_URL ?? "http://localhost:5173";
|
||||
const LOGIN_ID = process.env.E2E_LOGIN_ID ?? "";
|
||||
const PASSWORD = process.env.E2E_PASSWORD ?? "";
|
||||
|
||||
type SessionApiResponse = {
|
||||
items?: Array<{
|
||||
session_id?: string;
|
||||
client_id?: string;
|
||||
app_name?: string;
|
||||
is_current?: boolean;
|
||||
user_agent?: string;
|
||||
ip_address?: string;
|
||||
}>;
|
||||
};
|
||||
|
||||
function ensureCredentials(): void {
|
||||
if (!LOGIN_ID || !PASSWORD) {
|
||||
test.skip(true, "E2E credentials are required");
|
||||
}
|
||||
}
|
||||
|
||||
async function clickPasswordTab(page: Page): Promise<void> {
|
||||
await page.waitForTimeout(900);
|
||||
const pane = page.locator("flt-glass-pane");
|
||||
await pane.click({
|
||||
position: { x: 522, y: 158 },
|
||||
force: true,
|
||||
});
|
||||
await page.waitForTimeout(120);
|
||||
await pane.click({
|
||||
position: { x: 522, y: 158 },
|
||||
force: true,
|
||||
});
|
||||
await page.waitForTimeout(200);
|
||||
}
|
||||
|
||||
async function fillAt(
|
||||
page: Page,
|
||||
x: number,
|
||||
y: number,
|
||||
value: string,
|
||||
): Promise<void> {
|
||||
const pane = page.locator("flt-glass-pane");
|
||||
await pane.click({ position: { x, y }, force: true });
|
||||
await page.waitForTimeout(100);
|
||||
await page.keyboard.press("Control+A");
|
||||
await page.keyboard.press("Backspace");
|
||||
await page.keyboard.type(value);
|
||||
}
|
||||
|
||||
async function loginViaUserFront(page: Page): Promise<void> {
|
||||
await page.waitForURL(/\/ko\/(signin|login)/, { timeout: 30_000 });
|
||||
const loginIdInput = page.getByPlaceholder(
|
||||
/이메일 또는 휴대폰 번호|email|phone/i,
|
||||
);
|
||||
const passwordInput = page.getByPlaceholder(/비밀번호|password/i);
|
||||
const submitButton = page.getByRole("button", { name: /로그인|Login/i });
|
||||
|
||||
if ((await loginIdInput.count()) >= 1 && (await passwordInput.count()) >= 1) {
|
||||
await loginIdInput.first().fill(LOGIN_ID);
|
||||
await passwordInput.first().fill(PASSWORD);
|
||||
await submitButton.click();
|
||||
return;
|
||||
}
|
||||
|
||||
await clickPasswordTab(page);
|
||||
await fillAt(page, 640, 245, LOGIN_ID);
|
||||
await fillAt(page, 640, 311, PASSWORD);
|
||||
await page.locator("flt-glass-pane").click({
|
||||
position: { x: 640, y: 381 },
|
||||
force: true,
|
||||
});
|
||||
}
|
||||
|
||||
async function ensureConsentIfNeeded(page: Page): Promise<void> {
|
||||
if (!/\/ko\/consent/.test(page.url())) {
|
||||
return;
|
||||
}
|
||||
|
||||
const allowButton = page
|
||||
.getByRole("button")
|
||||
.filter({ hasText: /허용|동의|Accept|Allow/i })
|
||||
.first();
|
||||
|
||||
if (await allowButton.count()) {
|
||||
await allowButton.click({ force: true });
|
||||
}
|
||||
}
|
||||
|
||||
async function captureUserSessionsOnReload(
|
||||
page: Page,
|
||||
): Promise<SessionApiResponse> {
|
||||
const responsePromise = page.waitForResponse(
|
||||
(response) =>
|
||||
response.request().method() === "GET" &&
|
||||
response.url().includes("/api/v1/user/sessions"),
|
||||
{ timeout: 30_000 },
|
||||
);
|
||||
|
||||
await page.reload({ waitUntil: "domcontentloaded" });
|
||||
const response = await responsePromise;
|
||||
return (await response.json()) as SessionApiResponse;
|
||||
}
|
||||
|
||||
async function loginUserFront(context: BrowserContext): Promise<Page> {
|
||||
const page = await context.newPage();
|
||||
await page.goto(`${USERFRONT_BASE_URL}/ko/signin`, {
|
||||
waitUntil: "domcontentloaded",
|
||||
});
|
||||
await loginViaUserFront(page);
|
||||
await expect(page).toHaveURL(/\/ko\/dashboard/, { timeout: 60_000 });
|
||||
return page;
|
||||
}
|
||||
|
||||
async function loginAdminFront(context: BrowserContext): Promise<Page> {
|
||||
const page = await context.newPage();
|
||||
await page.goto(ADMINFRONT_URL, { waitUntil: "domcontentloaded" });
|
||||
const ssoButton = page.getByRole("button", {
|
||||
name: /SSO 계정으로 로그인|SSO/i,
|
||||
});
|
||||
if (await ssoButton.count()) {
|
||||
await ssoButton.click({ force: true });
|
||||
await page.waitForTimeout(1500);
|
||||
}
|
||||
if (/\/login$/.test(page.url())) {
|
||||
const authorizeUrl = await page.evaluate(() => {
|
||||
const origin = window.location.origin;
|
||||
const authority = `${USERFRONT_BASE_URL}/oidc`;
|
||||
const params = new URLSearchParams({
|
||||
client_id: "adminfront",
|
||||
redirect_uri: `${origin}/auth/callback`,
|
||||
response_type: "code",
|
||||
scope: "openid offline_access profile email",
|
||||
state: `pw-${Date.now()}`,
|
||||
nonce: `pw-${Date.now()}`,
|
||||
code_challenge: "test-code-challenge-test-code-challenge-test",
|
||||
code_challenge_method: "plain",
|
||||
});
|
||||
return `${authority}/oauth2/auth?${params.toString()}`;
|
||||
});
|
||||
await page.goto(authorizeUrl, { waitUntil: "domcontentloaded" });
|
||||
}
|
||||
await loginViaUserFront(page);
|
||||
await ensureConsentIfNeeded(page);
|
||||
await page.waitForURL(
|
||||
/localhost:5173|\/auth\/callback|\/dashboard|\/tenants/,
|
||||
{
|
||||
timeout: 60_000,
|
||||
},
|
||||
);
|
||||
return page;
|
||||
}
|
||||
|
||||
test.describe("cross-browser session debug", () => {
|
||||
test("userfront session card should map adminfront session metadata across contexts", async ({
|
||||
browser,
|
||||
}, testInfo) => {
|
||||
ensureCredentials();
|
||||
|
||||
const userfrontContext = await browser.newContext({ locale: "ko-KR" });
|
||||
const adminfrontContext = await browser.newContext({ locale: "ko-KR" });
|
||||
|
||||
const userfrontPage = await loginUserFront(userfrontContext);
|
||||
const adminfrontPage = await loginAdminFront(adminfrontContext);
|
||||
|
||||
const sessionsPayload = await captureUserSessionsOnReload(userfrontPage);
|
||||
const items = sessionsPayload.items ?? [];
|
||||
const adminfrontItems = items.filter((item) =>
|
||||
(item.client_id ?? "").toLowerCase().includes("adminfront"),
|
||||
);
|
||||
const unknownCards = await userfrontPage
|
||||
.locator("text=세션 정보")
|
||||
.allTextContents();
|
||||
const adminFrontCards = await userfrontPage
|
||||
.locator("text=AdminFront")
|
||||
.allTextContents();
|
||||
|
||||
await testInfo.attach("user-sessions.json", {
|
||||
body: JSON.stringify(sessionsPayload, null, 2),
|
||||
contentType: "application/json",
|
||||
});
|
||||
await testInfo.attach("card-summary.json", {
|
||||
body: JSON.stringify(
|
||||
{
|
||||
unknownCards,
|
||||
adminFrontCards,
|
||||
currentUrl: userfrontPage.url(),
|
||||
adminfrontUrl: adminfrontPage.url(),
|
||||
},
|
||||
null,
|
||||
2,
|
||||
),
|
||||
contentType: "application/json",
|
||||
});
|
||||
|
||||
expect(adminfrontItems.length).toBeGreaterThan(0);
|
||||
});
|
||||
});
|
||||
Reference in New Issue
Block a user