1
0
forked from baron/baron-sso

chore: consolidate local integration changes

This commit is contained in:
2026-06-09 21:03:05 +09:00
parent aa2848c3b6
commit 1341f07ef9
158 changed files with 10995 additions and 1490 deletions

View File

@@ -1,4 +1,3 @@
import { readFile } from "node:fs/promises";
import { expect, test } from "@playwright/test";
test.describe("Worksmobile tenant management", () => {
@@ -32,7 +31,10 @@ test.describe("Worksmobile tenant management", () => {
page,
}) => {
const comparisonRequests: boolean[] = [];
const syncRequests: string[] = [];
const syncRequests: Array<{
userId: string;
body: Record<string, unknown>;
}> = [];
await page.route("**/api/v1/**", async (route) => {
const url = new URL(route.request().url());
@@ -218,7 +220,13 @@ test.describe("Worksmobile tenant management", () => {
isWorksmobileTenantPath("/worksmobile/users/user-missing/sync") &&
method === "POST"
) {
syncRequests.push("user-missing");
syncRequests.push({
userId: "user-missing",
body: JSON.parse(route.request().postData() ?? "{}") as Record<
string,
unknown
>,
});
return route.fulfill({
json: { id: "job-user-missing", resourceId: "user-missing" },
headers,
@@ -235,7 +243,8 @@ test.describe("Worksmobile tenant management", () => {
await expect(page.getByRole("tab", { name: "조직" })).toBeVisible();
await page.getByRole("tab", { name: "이력" }).click();
await expect(page.getByText("비밀번호 파일 히스토리")).toBeVisible();
await expect(page.getByText("비밀번호 파일 히스토리")).not.toBeVisible();
await expect(page.getByText("최근 작업")).toBeVisible();
await expect(page.getByText("domainMappings")).not.toBeVisible();
await expect(page.getByText("SCIM token")).not.toBeVisible();
await page.getByRole("tab", { name: "사용자" }).click();
@@ -246,6 +255,9 @@ test.describe("Worksmobile tenant management", () => {
"worksmobile-구성원-virtual-body",
);
await expect(userComparisonTable).toBeVisible();
await expect(page.getByTestId("worksmobile-구성원-row-count")).toHaveText(
"표시 2 / 전체 5",
);
await expect(userSyncCard).toBeVisible();
expect(
await page.evaluate(() => {
@@ -347,7 +359,17 @@ test.describe("Worksmobile tenant management", () => {
await page
.getByRole("button", { name: "선택 구성원 WORKS에 생성" })
.click();
await expect.poll(() => syncRequests).toEqual(["user-missing"]);
await expect(page.getByText("WORKS 초기 비밀번호")).toBeVisible();
await page.getByLabel("초기 비밀번호").fill("InitPass123!");
await page.getByRole("button", { name: "생성 작업 등록" }).click();
await expect
.poll(() => syncRequests)
.toEqual([
{
userId: "user-missing",
body: expect.objectContaining({ initialPassword: "InitPass123!" }),
},
]);
await page.getByRole("tab", { name: "조직" }).click();
await expect(page.getByText("조직 단건 동기화")).toBeVisible();
@@ -357,6 +379,9 @@ test.describe("Worksmobile tenant management", () => {
"worksmobile-조직/그룹-virtual-body",
);
await expect(groupComparisonTable).toBeVisible();
await expect(
page.getByTestId("worksmobile-조직/그룹-row-count"),
).toHaveText("표시 2 / 전체 2");
await expect(groupSyncCard).toBeVisible();
expect(
await page.evaluate(() => {
@@ -381,6 +406,228 @@ test.describe("Worksmobile tenant management", () => {
await expect(page.getByText("works-parent-tech")).toBeVisible();
});
test("separates selected user create and update actions", async ({
page,
}) => {
const syncRequests: Array<{
userId: string;
body: Record<string, unknown>;
}> = [];
await page.route("**/api/v1/**", async (route) => {
const url = new URL(route.request().url());
const method = route.request().method();
const headers = { "Access-Control-Allow-Origin": "*" };
const isWorksmobileTenantPath = (suffix: string) =>
url.pathname.endsWith(`/admin/tenants/hanmac-family-id${suffix}`) ||
url.pathname.endsWith(
`/admin/tenants/038326b6-954a-48a7-a85f-efd83f62b82a${suffix}`,
);
if (url.pathname.endsWith("/user/me")) {
return route.fulfill({
json: {
id: "admin-user",
name: "Admin",
role: "super_admin",
manageableTenants: [
{
id: "038326b6-954a-48a7-a85f-efd83f62b82a",
name: "한맥 가족",
slug: "hanmac-family",
type: "COMPANY_GROUP",
},
],
},
headers,
});
}
if (
url.pathname.endsWith("/admin/tenants/hanmac-family-id") &&
method === "GET"
) {
return route.fulfill({
json: {
id: "hanmac-family-id",
name: "한맥 가족",
slug: "hanmac-family",
type: "COMPANY_GROUP",
status: "active",
parentId: null,
},
headers,
});
}
if (isWorksmobileTenantPath("/worksmobile") && method === "GET") {
return route.fulfill({
json: {
tenant: {
id: "hanmac-family-id",
name: "한맥 가족",
slug: "hanmac-family",
type: "COMPANY_GROUP",
status: "active",
memberCount: 0,
createdAt: "2026-05-04T00:00:00Z",
updatedAt: "2026-05-04T00:00:00Z",
},
config: {
enabled: true,
tokenConfigured: true,
},
recentJobs: [],
},
headers,
});
}
if (
isWorksmobileTenantPath("/worksmobile/credential-batches") &&
method === "GET"
) {
return route.fulfill({ json: [], headers });
}
if (
isWorksmobileTenantPath("/worksmobile/comparison") &&
method === "GET"
) {
return route.fulfill({
json: {
users: [
{
resourceType: "USER",
baronId: "user-missing",
baronName: "김생성",
status: "missing_in_worksmobile",
},
{
resourceType: "USER",
baronId: "user-update",
baronName: "이업데이트",
baronEmail: "domain@typo.example.com",
worksmobileId: "works-user-update",
externalKey: "user-update",
worksmobileName: "이업데이트",
worksmobileEmail: "domain@example.com",
status: "needs_update",
},
],
groups: [],
},
headers,
});
}
if (
isWorksmobileTenantPath("/worksmobile/users/user-missing/sync") &&
method === "POST"
) {
syncRequests.push({
userId: "user-missing",
body: JSON.parse(route.request().postData() ?? "{}") as Record<
string,
unknown
>,
});
return route.fulfill({
json: { id: "job-user-missing", resourceId: "user-missing" },
headers,
});
}
if (
isWorksmobileTenantPath("/worksmobile/users/user-update/sync") &&
method === "POST"
) {
syncRequests.push({
userId: "user-update",
body: JSON.parse(route.request().postData() ?? "{}") as Record<
string,
unknown
>,
});
return route.fulfill({
json: { id: "job-user-update", resourceId: "user-update" },
headers,
});
}
return route.fulfill({ json: { items: [], total: 0 }, headers });
});
await page.goto("/worksmobile");
await page.getByRole("tab", { name: "사용자" }).click();
const userComparisonSection = page
.getByRole("heading", { name: "구성원" })
.locator("xpath=ancestor::div[contains(@class, 'space-y-2')][1]");
await expect(userComparisonSection.getByText("김생성")).toBeVisible();
await expect(userComparisonSection.getByText("이업데이트")).toHaveCount(2);
const statusHeader = userComparisonSection
.locator("thead th")
.filter({ hasText: "상태" })
.locator("div")
.first();
await expect
.poll(() =>
statusHeader.evaluate((element) => {
const style = window.getComputedStyle(element);
return { alignItems: style.alignItems, display: style.display };
}),
)
.toEqual({ alignItems: "center", display: "flex" });
await page
.getByRole("row", { name: /김생성/ })
.getByRole("checkbox")
.check();
await page
.getByRole("row", { name: /이업데이트/ })
.getByRole("checkbox")
.check();
await userComparisonSection
.getByRole("button", { name: "선택 구성원 WORKS에 생성" })
.click();
await expect(page.getByText("WORKS 초기 비밀번호")).toBeVisible();
await page.getByLabel("초기 비밀번호").fill("InitPass123!");
await page.getByRole("button", { name: "생성 작업 등록" }).click();
await expect
.poll(() => syncRequests)
.toEqual([
{
userId: "user-missing",
body: expect.objectContaining({ initialPassword: "InitPass123!" }),
},
]);
await page
.getByRole("row", { name: /이업데이트/ })
.getByRole("checkbox")
.check();
await userComparisonSection
.getByRole("button", { name: "선택 구성원 업데이트 적용" })
.click();
await expect
.poll(() => syncRequests)
.toEqual([
{
userId: "user-missing",
body: expect.objectContaining({ initialPassword: "InitPass123!" }),
},
{
userId: "user-update",
body: expect.not.objectContaining({
initialPassword: expect.anything(),
}),
},
]);
});
test("shows a toast when selected WORKS creation fails", async ({ page }) => {
await page.route("**/api/v1/**", async (route) => {
const url = new URL(route.request().url());
@@ -651,9 +898,7 @@ test.describe("Worksmobile tenant management", () => {
).toBeDisabled();
});
test("downloads initial password CSV and enqueues WORKS admin jobs", async ({
page,
}) => {
test("shows WORKS job history and enqueues admin jobs", async ({ page }) => {
const requests: string[] = [];
const headers = {
"Access-Control-Allow-Origin": "*",
@@ -790,24 +1035,6 @@ test.describe("Worksmobile tenant management", () => {
});
}
if (
url.pathname.endsWith(
"/admin/tenants/038326b6-954a-48a7-a85f-efd83f62b82a/worksmobile/initial-passwords.csv",
) &&
method === "GET"
) {
requests.push("download-passwords");
return route.fulfill({
body: "email,password\nuser@example.com,Secret123!\n",
contentType: "text/csv",
headers: {
...headers,
"Content-Disposition":
'attachment; filename="worksmobile-passwords.csv"',
},
});
}
if (
url.pathname.endsWith(
"/admin/tenants/038326b6-954a-48a7-a85f-efd83f62b82a/worksmobile/backfill/dry-run",
@@ -864,18 +1091,10 @@ test.describe("Worksmobile tenant management", () => {
await page.goto("/worksmobile");
await expect(page.getByText("Worksmobile 연동")).toBeVisible();
await page.getByRole("tab", { name: "이력" }).click();
const download = page.waitForEvent("download");
await page
.getByRole("button", { name: "batch-1 비밀번호 CSV 다운로드" })
.click();
const passwordCsv = await download;
expect(passwordCsv.suggestedFilename()).toBe("worksmobile-passwords.csv");
const passwordCsvPath = await passwordCsv.path();
expect(passwordCsvPath).toBeTruthy();
expect(await readFile(passwordCsvPath ?? "", "utf8")).toContain(
"user@example.com,Secret123!",
);
await expect(page.getByText("비밀번호 파일 히스토리")).not.toBeVisible();
await expect(
page.getByRole("button", { name: /비밀번호 CSV 다운로드/ }),
).toHaveCount(0);
await page.getByRole("button", { name: "Backfill Dry-run" }).click();
await expect.poll(() => requests).toContain("dry-run");
@@ -915,6 +1134,5 @@ test.describe("Worksmobile tenant management", () => {
page.once("dialog", (dialog) => dialog.accept());
await page.getByRole("button", { name: /대기중 payload 삭제/ }).click();
await expect.poll(() => requests).toContain("delete-pending");
expect(requests).toContain("download-passwords");
});
});