1
0
forked from baron/baron-sso
Files
baron-sso/devfront/tests/devfront-clients-lifecycle.spec.ts

199 lines
6.1 KiB
TypeScript

import { expect, test } from "@playwright/test";
import {
type ClientStatus,
type Consent,
installDevApiMock,
makeClient,
seedAuth,
} from "./helpers/devfront-fixtures";
const appNamePlaceholder = /My Awesome Application|예: 멋진 애플리케이션/i;
const sshRsaPublicKey =
"ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAAABwECAwQFBgc= test@example";
test.describe("DevFront clients lifecycle", () => {
test.beforeEach(async ({ page }) => {
page.on("dialog", async (dialog) => {
await dialog.accept();
});
await seedAuth(page);
});
test("create, update status, and delete", async ({ page }) => {
const state = {
clients: [makeClient("existing-client", { name: "Existing app" })],
consents: [] as Consent[],
updatedStatus: "active" as ClientStatus,
auditLogsByCursor: undefined,
onUpdateStatus(status: ClientStatus) {
this.updatedStatus = status;
},
};
await installDevApiMock(page, state);
await page.goto("/clients");
await expect(page.getByText("Existing app")).toBeVisible();
await page
.getByRole("button", { name: /연동 앱 추가|새 클라이언트|Create/i })
.click();
await expect(page).toHaveURL(/\/clients\/new$/);
await page
.getByPlaceholder(appNamePlaceholder)
.fill("Playwright Created App");
await page
.getByPlaceholder(/https:\/\/app\.example\.com\/callback/i)
.fill("https://playwright.example.com/callback");
await page
.getByRole("button", { name: /앱 생성|클라이언트 생성|Create/i })
.click();
await expect(page).toHaveURL(/\/clients\/client-\d+\/settings$/);
await expect(
page.getByRole("heading", {
name: /연동 앱 설정|클라이언트 설정|Client Settings/i,
}),
).toBeVisible();
await page.getByRole("button", { name: /비활성|Inactive/i }).click();
await page.getByRole("button", { name: /^저장$|^Save$/i }).click();
await expect.poll(() => state.updatedStatus).toBe("inactive");
await page.getByRole("button", { name: /삭제|Delete/i }).click();
await expect(page).toHaveURL(/\/clients$/);
await expect(page.getByText("Playwright Created App")).not.toBeVisible();
});
test("rotate secret shows new value", async ({ page }) => {
let rotatedSecret = "";
const state = {
clients: [makeClient("client-rotate", { name: "Rotate app" })],
consents: [] as Consent[],
auditLogsByCursor: undefined,
onRotateSecret(newSecret: string) {
rotatedSecret = newSecret;
},
};
await installDevApiMock(page, state);
await page.goto("/clients/client-rotate");
await expect(
page.getByRole("heading", { name: "Rotate app", exact: true }),
).toBeVisible();
await page.getByTitle(/비밀키 재발급|Rotate/i).click();
await expect.poll(() => rotatedSecret).toBe("client-rotate-rotated-secret");
await expect(page.getByText("client-rotate-rotated-secret")).toBeVisible();
});
test("update name and redirect URI should be persisted", async ({ page }) => {
const state = {
clients: [
makeClient("client-edit", {
name: "Before Name",
redirectUris: ["https://before.example.com/callback"],
}),
],
consents: [] as Consent[],
auditLogsByCursor: undefined,
};
await installDevApiMock(page, state);
await page.goto("/clients/client-edit/settings");
await page.getByPlaceholder(appNamePlaceholder).fill("After Name");
await page.getByRole("button", { name: /^저장$|^Save$/i }).click();
await expect.poll(() => state.clients[0]?.name).toBe("After Name");
await page.goto("/clients/client-edit");
await page
.getByRole("textbox", { name: /인증 콜백 URL|Callback/i })
.fill("https://after.example.com/callback");
await page
.getByRole("button", { name: /Redirect URIs 저장|Save/i })
.click();
await expect
.poll(() => state.clients[0]?.redirectUris[0])
.toBe("https://after.example.com/callback");
await page.reload();
await expect(
page.getByRole("textbox", { name: /인증 콜백 URL|Callback/i }),
).toHaveValue(/https:\/\/after\.example\.com\/callback/);
});
test("pkce headless login with inline ssh-rsa key should persist mapped payload", async ({
page,
}) => {
const state = {
clients: [
makeClient("client-trusted", { name: "Trusted App", type: "pkce" }),
],
consents: [] as Consent[],
auditLogsByCursor: undefined,
};
await installDevApiMock(page, state);
await page.goto("/clients/client-trusted/settings");
await page
.getByRole("switch", {
name: /Headless Login \(자체 로그인 UI 사용\)|Headless Login \(Custom Login UI\)/i,
})
.click();
await expect(
page.getByRole("heading", {
name: /공개키 등록|Public Key Registration/i,
}),
).toBeVisible();
await page
.getByPlaceholder(
/ssh-rsa AAA\.\.\.|Paste an 'ssh-rsa AAA\.\.\.' public key first/i,
)
.fill(sshRsaPublicKey);
await page.getByRole("button", { name: /^저장$|^Save$/i }).click();
await expect
.poll(() => state.clients[0]?.tokenEndpointAuthMethod)
.toBe("private_key_jwt");
await expect
.poll(() => state.clients[0]?.metadata?.headless_login_enabled)
.toBe(true);
await expect
.poll(
() =>
(
state.clients[0]?.jwks as {
keys?: Array<{ kty?: string; alg?: string }>;
}
)?.keys?.[0]?.kty,
)
.toBe("RSA");
await expect
.poll(
() =>
(
state.clients[0]?.jwks as {
keys?: Array<{ kty?: string; alg?: string }>;
}
)?.keys?.[0]?.alg,
)
.toBe("RS256");
await page.reload();
await expect(
page.getByRole("heading", {
name: /공개키 등록|Public Key Registration/i,
}),
).toBeVisible();
await expect(
page.getByPlaceholder(
/ssh-rsa AAA\.\.\.|Paste an 'ssh-rsa AAA\.\.\.' public key first/i,
),
).toHaveValue(/"kty": "RSA"/);
});
});