feat: separate login registration and own account flows

This commit is contained in:
2026-10-16 09:43:57 +09:00
parent d22899d39e
commit b8b12442a0
26 changed files with 3040 additions and 632 deletions
@@ -4,7 +4,10 @@
import { assert, assertEquals } from "@std/assert";
import { resolve } from "@std/path";
import { chromium, type Page } from "playwright";
import { startOwnedProcesses, stopOwnedProcesses } from "../../../../tools/web-ux/src/processes.ts";
import {
startOwnedProcesses,
stopOwnedProcesses,
} from "../../../../tools/web-ux/src/processes.ts";
const root = resolve(import.meta.dirname!, "../../../..");
const evidence = `${root}/target/web-ux/hosting/interactions`;
async function noOverflow(page: Page) {
@@ -63,7 +66,9 @@ Deno.test("hosting UI keyboard navigation, policy editing, denial and unknown sa
});
page.on("response", (response) => {
if (response.status() >= 400) {
httpErrors.push(`${response.status()} ${new URL(response.url()).pathname}`);
httpErrors.push(
`${response.status()} ${new URL(response.url()).pathname}`,
);
}
});
page.on(
@@ -80,18 +85,34 @@ Deno.test("hosting UI keyboard navigation, policy editing, denial and unknown sa
settingsRequests.push(request.method());
}
});
await page.goto("http://127.0.0.1:15379/account?fixture=admin-on");
await page.getByRole("button", { name: "Add passkey to alice" }).waitFor();
await page.goto("http://127.0.0.1:15379/?fixture=admin-on");
await page.getByRole("heading", { name: "Workspaces", exact: true })
.waitFor();
if (
await page.getByRole("button", { name: "Show sidebar", exact: true }).isVisible()
) await page.getByRole("button", { name: "Show sidebar", exact: true }).click();
await page.getByRole("button", {
name: "Show sidebar",
exact: true,
}).isVisible()
) {
await page.getByRole("button", {
name: "Show sidebar",
exact: true,
}).click();
}
const nav = page.getByRole("navigation", { name: "Global pages" });
await nav.getByRole("link", { name: "Hosting settings", exact: true }).waitFor();
await nav.getByRole("link", { name: "Hosting settings", exact: true })
.waitFor();
await nav.getByRole("link", { name: "Account", exact: true }).focus();
await page.keyboard.press("Tab");
assertEquals(await page.locator(":focus").innerText(), "Device Login");
assertEquals(
await page.locator(":focus").innerText(),
"Device Login",
);
await page.keyboard.press("Tab");
assertEquals(await page.locator(":focus").innerText(), "Hosting settings");
assertEquals(
await page.locator(":focus").innerText(),
"Hosting settings",
);
assertEquals(
await page.locator(":focus").evaluate((element) =>
getComputedStyle(element).outlineStyle !== "none"
@@ -99,42 +120,80 @@ Deno.test("hosting UI keyboard navigation, policy editing, denial and unknown sa
true,
);
await page.keyboard.press("Enter");
const edit = page.getByRole("button", { name: "Edit registration policy" });
const edit = page.getByRole("button", {
name: "Edit registration policy",
});
await edit.waitFor();
// Globalの入口からhostingへ進んだ後はWorkspace shellを離れる。
// Workspace未作成でも管理できることと整合させる。
assertEquals(await nav.count(), 0);
await page.getByRole("heading", { name: "Hosting settings", exact: true }).waitFor();
await page.getByRole("heading", {
name: "Hosting settings",
exact: true,
}).waitFor();
if (
await page.getByRole("button", { name: "Hide sidebar", exact: true }).isVisible() &&
await page.getByRole("button", {
name: "Hide sidebar",
exact: true,
}).isVisible() &&
width < 768
) await page.getByRole("button", { name: "Hide sidebar", exact: true }).click();
) {
await page.getByRole("button", {
name: "Hide sidebar",
exact: true,
}).click();
}
await noOverflow(page);
await edit.focus();
await page.keyboard.press("Enter");
const checkbox = page.getByRole("checkbox", { name: "Allow new account registration" });
const checkbox = page.getByRole("checkbox", {
name: "Allow new account registration",
});
assertEquals(
await checkbox.evaluate((element) => element === document.activeElement),
await checkbox.evaluate((element) =>
element === document.activeElement
),
true,
);
assertEquals(await checkbox.isChecked(), true);
await page.keyboard.press("Space");
await page.getByRole("button", { name: "Cancel", exact: true }).click();
assertEquals(await edit.evaluate((element) => element === document.activeElement), true);
await page.getByRole("button", { name: "Cancel", exact: true })
.click();
assertEquals(
await edit.evaluate((element) =>
element === document.activeElement
),
true,
);
await page.keyboard.press("Enter");
assertEquals(await checkbox.isChecked(), true);
await page.keyboard.press("Space");
await page.keyboard.press("Tab");
assertEquals(await page.locator(":focus").innerText(), "Save settings");
assertEquals(
await page.locator(":focus").innerText(),
"Save settings",
);
await page.keyboard.press("Enter");
await page.getByRole("status").filter({ hasText: "Hosting settings saved." }).waitFor();
assertEquals(await edit.evaluate((element) => element === document.activeElement), true);
await page.getByRole("status").filter({
hasText: "Hosting settings saved.",
}).waitFor();
assertEquals(
await edit.evaluate((element) =>
element === document.activeElement
),
true,
);
await page.getByText("Disabled", { exact: true }).waitFor();
assertEquals(settingsRequests.filter((method) => method === "PUT").length, 1);
assertEquals(
settingsRequests.filter((method) => method === "PUT").length,
1,
);
await noOverflow(page);
for (const mode of ["ordinary", "anonymous"]) {
settingsRequests.length = 0;
await page.goto(`http://127.0.0.1:15379/hosting/settings?fixture=${mode}`);
await page.goto(
`http://127.0.0.1:15379/hosting/settings?fixture=${mode}`,
);
await page.getByText(
mode === "ordinary"
? "Hosting administrator access is required."
@@ -144,41 +203,62 @@ Deno.test("hosting UI keyboard navigation, policy editing, denial and unknown sa
assertEquals(await edit.count(), 0);
assertEquals(await checkbox.count(), 0);
assertEquals(
await nav.getByRole("link", { name: "Hosting settings", exact: true }).count(),
await nav.getByRole("link", {
name: "Hosting settings",
exact: true,
}).count(),
0,
);
assertEquals(settingsRequests, []);
await noOverflow(page);
}
await page.goto("http://127.0.0.1:15379/account?fixture=account-off");
await page.goto(
"http://127.0.0.1:15379/register?fixture=account-off",
);
await page.getByText(
"New account registration is disabled. Existing users can still log in.",
).waitFor();
assertEquals(await page.getByRole("button", { name: "Register new account" }).count(), 0);
assertEquals(
await page.getByRole("button", { name: "Log in with passkey" }).isEnabled(),
true,
await page.getByRole("button", { name: "Register new account" })
.count(),
0,
);
await page.getByRole("link", {
name: "Log in with an existing account",
}).waitFor();
await noOverflow(page);
await page.goto("http://127.0.0.1:15379/account?fixture=ordinary-account-off");
await page.getByRole("button", { name: "Add passkey to alice" }).waitFor();
await page.goto(
"http://127.0.0.1:15379/account?fixture=ordinary-account-off",
);
await page.getByRole("button", { name: "Add passkey to alice" })
.waitFor();
assertEquals(
await page.getByRole("button", { name: "Add passkey to alice" }).isEnabled(),
await page.getByRole("button", { name: "Add passkey to alice" })
.isEnabled(),
true,
);
await page.goto("http://127.0.0.1:15379/hosting/settings?fixture=admin-save-error");
await page.goto(
"http://127.0.0.1:15379/hosting/settings?fixture=admin-save-error",
);
await edit.click();
await checkbox.press("Space");
await page.getByRole("button", { name: "Save settings", exact: true }).click();
await page.getByRole("button", { name: "Save settings", exact: true })
.click();
await page.getByRole("alert").waitFor();
assertEquals(await checkbox.isChecked(), false);
assertEquals(await checkbox.isDisabled(), true);
assertEquals(
await page.getByRole("button", { name: "Save settings", exact: true }).isDisabled(),
await page.getByRole("button", {
name: "Save settings",
exact: true,
}).isDisabled(),
true,
);
await noOverflow(page);
await page.getByRole("button", { name: "Reload settings", exact: true }).click();
await page.getByRole("button", {
name: "Reload settings",
exact: true,
}).click();
await edit.waitFor();
assertEquals(await page.getByRole("alert").count(), 0);
assertEquals(await edit.isEnabled(), true);
@@ -189,7 +269,11 @@ Deno.test("hosting UI keyboard navigation, policy editing, denial and unknown sa
"/hosting/settings",
'button:has-text("Retry loading settings")',
],
["account-error", "/account", 'button:has-text("Retry registration availability")'],
[
"account-error",
"/register",
'button:has-text("Retry registration availability")',
],
[
"admin-loading",
"/hosting/settings",
@@ -197,7 +281,7 @@ Deno.test("hosting UI keyboard navigation, policy editing, denial and unknown sa
],
[
"account-loading",
"/account",
"/register",
'[role="status"]:has-text("Checking registration availability")',
],
]
@@ -242,7 +326,10 @@ Deno.test("hosting UI keyboard navigation, policy editing, denial and unknown sa
}
}
}
await Deno.writeTextFile(`${evidence}/results.json`, JSON.stringify(results, null, 2));
await Deno.writeTextFile(
`${evidence}/results.json`,
JSON.stringify(results, null, 2),
);
} finally {
await browser.close();
await stopOwnedProcesses(processes);