Files
Felis/panel/e2e/auth-sources.smoke.spec.ts
T
Lemon-miaow 61b283ae2f feat(auth): add Owner authentication source settings
Manage Yggdrasil providers from the panel using durable platform settings, protected identity namespaces and atomic revisions. Apply changes to subsequent logins and profile lookups without restarting. Return operator-host logouts to the login method selection page.
2026-10-04 22:18:37 +08:00

85 lines
5.2 KiB
TypeScript

import { test, expect, t, expectFitsScreen } from "./fixtures";
test("Owner manages durable authentication sources from the sidebar", async ({ page, signIn }) => {
await signIn("owner");
await page.goto("/");
await page.getByRole("link", { name: t("navigation:auth_sources"), exact: true }).click();
await expect(page).toHaveURL(/\/admin\/auth-sources$/);
await expect(page.getByLabel(t("authSources:tag"))).toHaveValue("littleskin");
await expect(page.getByLabel(t("authSources:tag"))).toHaveAttribute("readonly", "");
await page.getByRole("button", { name: t("authSources:add"), exact: true }).click();
await page.getByLabel(t("authSources:tag")).nth(1).fill("custom");
await page.getByLabel(t("authSources:prefix")).nth(1).fill("CS");
await page.getByLabel(t("authSources:url"), { exact: true }).nth(1).fill("https://custom.example/sessionserver/session/minecraft/hasJoined");
await page.getByRole("button", { name: t("authSources:test"), exact: true }).nth(1).click();
await expect(page.getByText(t("authSources:test_ok", { ms: 20 }), { exact: true })).toBeVisible();
await page.getByRole("button", { name: t("authSources:move_up"), exact: true }).nth(1).click();
await page.getByRole("checkbox", { name: t("authSources:enabled"), exact: true }).nth(1).uncheck();
await page.getByRole("button", { name: t("authSources:save"), exact: true }).click();
await expect(page.getByText(t("authSources:saved"), { exact: true })).toBeVisible();
await page.reload();
await expect(page.getByLabel(t("authSources:tag")).nth(0)).toHaveValue("custom");
await expect(page.getByRole("checkbox", { name: t("authSources:enabled"), exact: true }).nth(1)).not.toBeChecked();
await expect(page.getByRole("button", { name: t("authSources:remove"), exact: true })).toHaveCount(0);
const available = await page.request.get("/api/v1/account/link/sources");
expect((await available.json()).sources.map((source: { tag: string }) => source.tag)).toEqual(["mojang", "custom"]);
await expectFitsScreen(page);
await page.screenshot({ path: "/tmp/felis-auth-sources.png", fullPage: true });
});
test("source fields stay visible while initial configuration is loading", async ({ page, signIn }) => {
await signIn("owner");
let release!: () => void;
const pending = new Promise<void>((resolve) => { release = resolve; });
await page.route("**/api/v1/settings/auth-sources", async (route) => { await pending; await route.continue(); });
await page.goto("/admin/auth-sources");
await expect(page.getByLabel(t("authSources:tag"))).toBeVisible();
await expect(page.getByLabel(t("authSources:tag"))).toBeDisabled();
await expect(page.getByText(t("authSources:loading"), { exact: true }).first()).toBeVisible();
release();
await expect(page.getByLabel(t("authSources:tag"))).toBeEnabled();
});
test("a player has neither an authentication source entry nor access to its API", async ({ page, signIn }) => {
await signIn("linked");
await page.goto("/admin/auth-sources");
await expect(page.getByText(t("common:not_authorized_title"))).toBeVisible();
await expect(page.getByRole("link", { name: t("navigation:auth_sources"), exact: true })).toHaveCount(0);
const response = await page.request.get("/api/v1/settings/auth-sources");
expect(response.status()).toBe(403);
});
for (const path of ["/servers", "/account"]) {
test(`logout from ${path} on the operator host returns to login methods`, async ({ page, signIn }) => {
await page.route("**/config.json", async (route) => {
const response = await route.fetch();
await route.fulfill({ response, json: { ...await response.json(), adminHostname: "localhost" } });
});
await signIn("owner");
await page.goto(path);
const logout = page.getByRole("button", { name: t("account:sign_out"), exact: true });
await (path === "/account" ? logout.last() : logout.first()).click();
await expect(page).toHaveURL(/\/login\?/);
await expect(page.getByRole("button", { name: t("auth:passkey_btn"), exact: true })).toBeVisible();
await expect(page.getByRole("button", { name: t("auth:tab_op_btn"), exact: true })).toBeVisible();
await expect(page.getByRole("button", { name: t("auth:op_start_btn"), exact: true })).toHaveCount(0);
await page.getByRole("button", { name: t("auth:tab_op_btn"), exact: true }).click();
await expect(page.getByRole("button", { name: t("auth:op_start_btn"), exact: true })).toBeVisible();
});
}
test("Chinese authentication settings render in both themes", async ({ page, signIn }) => {
await page.setViewportSize({ width: 1440, height: 1000 });
await page.addInitScript(() => localStorage.setItem("felis-lang", "zh-CN"));
await signIn("owner");
await page.emulateMedia({ colorScheme: "dark" });
await page.goto("/admin/auth-sources");
await expect(page.getByRole("heading", { name: "认证源", exact: true })).toBeVisible();
await expect(page.getByLabel("永久标识")).toHaveValue("littleskin");
await expectFitsScreen(page);
await page.screenshot({ path: "/tmp/felis-auth-sources-zh-dark.png", fullPage: true });
await page.getByRole("button", { name: "切换主题", exact: true }).click();
await expectFitsScreen(page);
await page.screenshot({ path: "/tmp/felis-auth-sources-zh-light.png", fullPage: true });
});