diff --git a/tools/web-ux/browser-tests/memory_document_fixture_server.ts b/tools/web-ux/browser-tests/memory_document_fixture_server.ts index c27cc1d75..7bf6b63ab 100644 --- a/tools/web-ux/browser-tests/memory_document_fixture_server.ts +++ b/tools/web-ux/browser-tests/memory_document_fixture_server.ts @@ -15,11 +15,11 @@ if ( throw new Error("usage: server.ts [section-count: 3..100]"); } -// Opt-in keeps the existing Memory scenarios and their pagination unchanged. +// 既存Memory scenarioとpaginationを変えないよう、明示選択時だけ有効にする。 const permissionsMode = Deno.args[3] === "permissions"; if (permissionsMode) { - // Permission evidence must serve a parent-supplied actual production build. - // Do not make /health succeed for a missing build or substitute fixture HTML. + // 権限の証拠は親から渡された実製品buildを使って採取する。 + // build欠落時に/healthを成功させたり、fixture HTMLで代替したりしない。 await Deno.stat(join(buildRoot, "index.html")); await Deno.stat(join(buildRoot, "_app/immutable")); } diff --git a/tools/web-ux/browser-tests/subject_permissions_before.ts b/tools/web-ux/browser-tests/subject_permissions_before.ts index 5ba18304b..2d8b3307e 100644 --- a/tools/web-ux/browser-tests/subject_permissions_before.ts +++ b/tools/web-ux/browser-tests/subject_permissions_before.ts @@ -1,5 +1,5 @@ #!/usr/bin/env -S deno run -A -/** Capture the actual built Subject detail before UI changes; never builds Web. */ +/** 変更前の実Web buildでSubject詳細を撮影する。Web自体はbuildしない。 */ import { dirname, fromFileUrl, join, resolve } from "@std/path"; import { capture } from "../src/capture.ts"; import type { Scenario } from "../src/types.ts"; @@ -10,7 +10,7 @@ const outputRoot = join(repositoryRoot, "target/web-ux/t-802"); const buildRoot = resolve(Deno.args[0] ?? join(repositoryRoot, "web/workspace/build")); const runPrefix = Deno.args[1] ?? `before-${new Date().toISOString().replaceAll(/[:.]/g, "-")}`; if (!/^[a-zA-Z0-9._-]+$/.test(runPrefix)) throw new Error("run id must be a safe filename"); -// Preflight is mandatory. A missing build must not be replaced by fixture HTML. +// Preflightは必須。buildの欠落をfixture HTMLで代替しない。 for (const filename of ["index.html", "_app/immutable"]) { await Deno.stat(join(buildRoot, filename)).catch(() => { throw new Error( diff --git a/tools/web-ux/browser-tests/subject_permissions_fixture.ts b/tools/web-ux/browser-tests/subject_permissions_fixture.ts index 9efd6de6a..5195e52e7 100644 --- a/tools/web-ux/browser-tests/subject_permissions_fixture.ts +++ b/tools/web-ux/browser-tests/subject_permissions_fixture.ts @@ -1,7 +1,6 @@ /** - * Synthetic HTTP state for T-802 browser UX only. NOT the real Backend, - * authentication, authorization, operation catalog, or cancellation implementation. - * Keep real Backend integration evidence separate from these browser fixtures. + * Browser UXだけの合成HTTP状態。実Backend、認証、認可、操作catalog、取消の実装ではない。 + * 実Backendの結合証拠は、このbrowser fixtureと分けて保持する。 */ export const permissionFixtureSubjects = [ ["permissions-empty", "New Subject — permissions not configured"], @@ -31,7 +30,7 @@ export const fixtureLegacyPermission: FixturePermission = { operation: "worker.notify", resource: { kind: "worker", runtime_id: "legacy-runtime", worker_id: "legacy-recipient" }, }; -// Deliberately a small synthetic catalog, not a copy/second authority for production. +// 小さな合成catalogに限定する。製品catalogの複製や第二正本ではない。 export const fixturePermissionOperations: { operation: string; label: string; @@ -105,8 +104,8 @@ export function createSubjectPermissionsFixture() { return async (request: Request): Promise => { const url = new URL(request.url); if (url.pathname === "/api/auth/whoami") { - // Exercise the real Web's admission gate with a synthetic HTTP session projection. - // No cookies/tokens or real Backend session are created or bypassed. + // 合成HTTP session projectionで実Webの画面入場判定を通す。 + // 実Backendのsessionやcookie/tokenは作成・迂回しない。 return json({ actor: { user_id: "synthetic-owner", @@ -156,7 +155,7 @@ export function createSubjectPermissionsFixture() { requests.push({ subject_id: id, request: payload, outcome: "conflict" }); return error("Policy changed in another browser; reload and review", 409); } - // Reject only the combinations used by this fixture. This is NOT Backend validation evidence. + // fixtureで使う組合せだけを拒否する。Backend検証の証拠にはしない。 for (const p of [...payload.execution, ...payload.delegation]) { const legacy = JSON.stringify(p) === JSON.stringify(fixtureLegacyPermission) && id === "permissions-legacy"; diff --git a/tools/web-ux/browser-tests/subject_permissions_review.ts b/tools/web-ux/browser-tests/subject_permissions_review.ts index 74d678869..5af94bd8f 100644 --- a/tools/web-ux/browser-tests/subject_permissions_review.ts +++ b/tools/web-ux/browser-tests/subject_permissions_review.ts @@ -1,5 +1,5 @@ #!/usr/bin/env -S deno run -A -/** Actual built Web + synthetic HTTP. No real Backend auth/Worker success claim. */ +/** 実Web buildを合成HTTPで操作する。実Backend認証やWorker動作の証拠ではない。 */ import { assert, assertEquals } from "@std/assert"; import { dirname, fromFileUrl, join, resolve } from "@std/path"; import { chromium, type Page } from "playwright"; @@ -11,7 +11,7 @@ const root = resolve(dirname(fromFileUrl(import.meta.url)), "../../.."); const buildRoot = resolve(Deno.args[0] ?? join(root, "web/workspace/build")); const runId = Deno.args[1] ?? `after-${new Date().toISOString().replaceAll(/[:.]/g, "-")}`; if (!/^[a-zA-Z0-9._-]+$/.test(runId)) throw new Error("run id must be a safe filename"); -// Fail fast; the parent owns building Web. Never replace it with a synthetic shell. +// Web buildは親の責務。欠落時は即失敗させ、合成shellで代替しない。 for (const name of ["index.html", "_app/immutable"]) { await Deno.stat(join(buildRoot, name)).catch(() => { throw new Error(`Actual Web build missing: ${join(buildRoot, name)}; parent must supply it.`); @@ -19,7 +19,7 @@ for (const name of ["index.html", "_app/immutable"]) { } const output = join(root, "target/web-ux/t-802", runId); await Deno.mkdir(dirname(output), { recursive: true }); -await Deno.mkdir(output); // A repeated run ID must not overwrite prior evidence. +await Deno.mkdir(output); // 同じrun IDで既存の証拠を上書きしない。 const sourceCommit = await new Deno.Command("git", { args: ["rev-parse", "HEAD"], cwd: root, @@ -113,12 +113,12 @@ function observe(page: Page) { }); } async function capturePoint(page: Page, id: string, expectedErrors: string[] = []) { - // Show the decision being captured, without changing product CSS or layout. + // 製品CSSやlayoutを変えず、確認対象の判断が見える位置へ移動する。 if (id.includes("preview")) await page.locator(".diff").scrollIntoViewIfNeeded(); else if (/saved|re-read|unknown|recovered|conflict/.test(id)) { await page.locator(".permissions > header").scrollIntoViewIfNeeded(); } - // Always retain the image first, even when an overflow assertion will fail. + // overflow検査が失敗する場合も、先に画像を保存して根拠を残す。 const image = join(output, `${id}.png`); const snapshot = join(output, `${id}.aria.txt`); await page.screenshot({ path: image }); @@ -169,7 +169,7 @@ async function capturePoint(page: Page, id: string, expectedErrors: string[] = [ imageSha256: await sha256File(image), accessibilitySnapshot: snapshot, expectedErrors, - // Per-page errors are cumulative so reload does not erase recovery evidence. + // 再読込みで復旧の証拠を消さないよう、pageごとにerrorを累積する。 errors: [...diagnostics.get(page) ?? []], errorSummary: { observed: errorCounts.get(page) ?? 0, @@ -206,7 +206,7 @@ async function add( ) { await page.getByRole("combobox", { name: "Permission scope", exact: true }).selectOption(lane); await page.getByRole("combobox", { name: "Operation", exact: true }).selectOption(operation); - // Exercise the actual name/description/resource input UI, not internal JSON entry. + // 内部JSON入力ではなく、実UIの名称・説明・資源入力を操作する。 await page.getByText( operation === "ticket.read" ? "Read the selected ticket. Does not allow editing or closing it." @@ -267,7 +267,7 @@ try { try { const page = await context.newPage(); observe(page); - // Enter through the ordinary Subject detail navigation. + // 通常のSubject詳細から管理導線へ進む。 await page.goto(`${base}/w/memory-review/memory/permissions-empty`); await page.locator(".subject-overview").waitFor(); await page.locator('main a[href$="/permissions"]').click(); @@ -282,7 +282,7 @@ try { `${prefix}-operation-description-resource-input`, ); await add(page, "delegation", "ticket.comment", "T-802"); - // Use named lane scopes so one grant cannot silently appear in both lanes. + // 名前付きlaneで検査し、一つのgrantが両方へ黙って追加されないことを確認する。 assertEquals( await page.locator('.lane[aria-label="Direct operations"]').getByText("Read ticket", { exact: true, @@ -310,7 +310,7 @@ try { await page.locator("[data-permission-state=ready]").waitFor(); await capturePoint(page, `${prefix}-re-read`); - // Real browser transport loss AFTER the synthetic HTTP fixture accepts the POST. + // 合成HTTP fixtureがPOSTを受け付けた後、実browserで応答を消失させる。 await add(page, "execution", "ticket.comment", "T-803"); let lost = false; await page.route("**/subjektiv/subjects/permissions-empty/permissions", async (route) => { @@ -323,7 +323,7 @@ try { }); await save(page, "unknown"); assert(lost); - // Fault injection is one-shot; restore ordinary browser networking before evidence capture. + // 障害注入は一度だけとし、撮影前に通常のbrowser通信へ戻す。 await page.unroute("**/subjektiv/subjects/permissions-empty/permissions"); await capturePoint(page, `${prefix}-unknown`, [ "Intentional POST response loss after synthetic save", @@ -343,7 +343,7 @@ try { "Intentional earlier POST response loss", ]); - // Two browser pages observe the same synthetic change identity before either save. + // 二つのbrowser画面で同じ合成change identityを観測してから保存する。 const other = await context.newPage(); observe(other); await open(page, base, "permissions-edit"); @@ -397,7 +397,7 @@ try { await capturePoint(page, `${prefix}-legacy-preserved`, [ "Intentional earlier service unavailable GET", ]); - // Revoke a supported direct permission; this fixture proves UI intent, not real cancellation. + // 直接操作の失効を確認する。ここで証明するのはUIの入力であり、実取消ではない。 await page.getByRole("button", { name: "Remove Read ticket from direct operations", exact: true,