| 1 | // Run: tsx src/__tests__/tool-approval-mode.test.ts |
| 2 | |
| 3 | import { readFileSync } from "node:fs"; |
| 4 | import { normalizeToolApprovalMode } from "../lib/types"; |
| 5 | import { en } from "../locales/en"; |
| 6 | import { zh } from "../locales/zh"; |
| 7 | import { zhTW } from "../locales/zh-TW"; |
| 8 | |
| 9 | let passed = 0; |
| 10 | let failed = 0; |
| 11 | |
| 12 | function eq(a: unknown, b: unknown, label: string) { |
| 13 | if (a === b) { |
| 14 | process.stdout.write(` PASS ${label}\n`); |
| 15 | passed += 1; |
| 16 | } else { |
| 17 | process.stdout.write(` FAIL ${label}: expected ${JSON.stringify(b)}, got ${JSON.stringify(a)}\n`); |
| 18 | failed += 1; |
| 19 | } |
| 20 | } |
| 21 | |
| 22 | console.log("\npermission presets"); |
| 23 | |
| 24 | eq(normalizeToolApprovalMode("read-only"), "read-only", "read-only remains canonical"); |
| 25 | eq(normalizeToolApprovalMode("workspace-write"), "workspace-write", "workspace-write remains canonical"); |
| 26 | eq(normalizeToolApprovalMode("danger-full-access"), "danger-full-access", "full access remains canonical"); |
| 27 | eq(normalizeToolApprovalMode("ask"), "read-only", "legacy ask migrates to read-only"); |
| 28 | eq(normalizeToolApprovalMode("auto"), "workspace-write", "legacy auto migrates to workspace-write"); |
| 29 | eq(normalizeToolApprovalMode("yolo"), "workspace-write", "legacy yolo migrates conservatively"); |
| 30 | eq(normalizeToolApprovalMode("unknown"), "read-only", "unknown values fail closed"); |
| 31 | eq(normalizeToolApprovalMode("", undefined, false, "workspace-write"), "workspace-write", "new-session fallback uses workspace access"); |
| 32 | |
| 33 | eq(en["composer.permissionReadOnly"], "Read only", "English read-only label"); |
| 34 | eq(en["composer.permissionWorkspaceWrite"], "Workspace access", "English workspace label"); |
| 35 | eq(en["composer.permissionFullAccess"], "Full access", "English full-access label"); |
| 36 | eq(zh["composer.permissionReadOnly"], "仅可查看", "Simplified Chinese read-only label"); |
| 37 | eq(zh["composer.permissionWorkspaceWrite"], "工作区内修改", "Simplified Chinese workspace label"); |
| 38 | eq(zh["composer.permissionFullAccess"], "完全权限", "Simplified Chinese full-access label"); |
| 39 | eq(zh["composer.permissionRecommended"], "推荐", "Simplified Chinese workspace recommendation"); |
| 40 | eq(zh["composer.permissionWorkspaceWriteDesc"], "可读写当前工作区;越界操作需要你授权", "Simplified Chinese workspace description"); |
| 41 | // Windows has no OS-level shell sandbox; the workspace preset copy must not |
| 42 | // promise a shell boundary the platform cannot enforce. |
| 43 | for (const [name, dict] of [["English", en], ["Simplified Chinese", zh], ["Traditional Chinese", zhTW]] as const) { |
| 44 | const desc = dict["composer.permissionWorkspaceWriteDescWindows"]; |
| 45 | eq(typeof desc === "string" && desc.length > 0 && /shell/i.test(desc), true, `${name} Windows workspace description names the shell`); |
| 46 | eq(desc !== dict["composer.permissionWorkspaceWriteDesc"], true, `${name} Windows workspace description differs from the sandboxed copy`); |
| 47 | } |
| 48 | eq(zhTW["composer.permissionReadOnly"], "僅可查看", "Traditional Chinese read-only label"); |
| 49 | eq(zhTW["composer.permissionWorkspaceWrite"], "工作區內修改", "Traditional Chinese workspace label"); |
| 50 | eq(zhTW["composer.permissionFullAccess"], "完全權限", "Traditional Chinese full-access label"); |
| 51 | |
| 52 | const styles = readFileSync(new URL("../styles.css", import.meta.url), "utf8"); |
| 53 | eq(styles.includes(".composer-choice--permission-read-only { color: var(--composer-permission-ask); }"), true, "read-only keeps the 1.38.7 Ask blue"); |
| 54 | eq(styles.includes(".composer-choice--permission-workspace-write { color: var(--ok); }"), true, "workspace-write keeps the 1.38.7 Auto green"); |
| 55 | eq(styles.includes(".composer-choice--permission-danger-full-access { color: var(--err); }"), true, "full access keeps the 1.38.7 Yolo red"); |
| 56 | |
| 57 | console.log(`\n${passed} passed, ${failed} failed, ${passed + failed} total`); |
| 58 | if (failed > 0) process.exit(1); |
| 59 |