Some checks failed
build-packages / resolve bundled mosh-client (push) Has been cancelled
build-packages / resolve bundled et-client (push) Has been cancelled
build-packages / build-macos (push) Has been cancelled
build-packages / build-windows (push) Has been cancelled
build-packages / build-linux-x64 (push) Has been cancelled
build-packages / build-linux-arm64 (push) Has been cancelled
build-packages / release (push) Has been cancelled
build-packages / update Nix release metadata (push) Has been cancelled
build-packages / bump homebrew tap (push) Has been cancelled
test / lint-and-test (push) Has been cancelled
AI automation / Route event (push) Has been cancelled
AI automation / Hand reopened issue to maintainers (push) Has been cancelled
AI automation / Clean source issue state (push) Has been cancelled
AI automation / Reconcile handoffs (push) Has been cancelled
AI automation / Classify issue (push) Has been cancelled
AI automation / Claude Code smoke (push) Has been cancelled
AI automation / Review issue follow-up (push) Has been cancelled
AI automation / Publish issue follow-up (push) Has been cancelled
AI automation / Implement with Claude Code (push) Has been cancelled
AI automation / Publish implement PR (push) Has been cancelled
AI automation / Continue queued issue comments (push) Has been cancelled
AI automation / Codex review loop (push) Has been cancelled
AI automation / Publish Codex fix (push) Has been cancelled
AI automation / Clear Codex dispatch marker (push) Has been cancelled
AI automation / Own PR re-request Codex (push) Has been cancelled
AI automation / External PR re-request Codex (push) Has been cancelled
AI automation / Poll Codex reaction / retry (push) Has been cancelled
build-et-binaries / build-linux-x64 (push) Has been cancelled
build-et-binaries / build-linux-arm64 (push) Has been cancelled
build-et-binaries / build-macos-universal (push) Has been cancelled
build-et-binaries / build-windows-x64 (push) Has been cancelled
build-et-binaries / release (push) Has been cancelled
133 lines
5.8 KiB
TypeScript
133 lines
5.8 KiB
TypeScript
import assert from "node:assert/strict";
|
|
import { readFileSync } from "node:fs";
|
|
import test from "node:test";
|
|
|
|
const readAppLockSectionSource = () => (
|
|
readFileSync(new URL("./AppLockSettingsSection.tsx", import.meta.url), "utf8")
|
|
);
|
|
|
|
test("disabling app lock does not trigger a second renderer-side unlock request", () => {
|
|
const source = readAppLockSectionSource();
|
|
|
|
assert.doesNotMatch(source, /unlockApp\?\.\(/);
|
|
assert.doesNotMatch(source, /unlockApp\?:/);
|
|
});
|
|
|
|
test("app lock setup starts with password setup instead of a misleading enable toggle", () => {
|
|
const source = readAppLockSectionSource();
|
|
|
|
assert.match(source, /!hasPassword \? \(/);
|
|
assert.match(source, /settings\.appLock\.setupTitle/);
|
|
assert.match(source, /settings\.appLock\.setupDescription/);
|
|
assert.match(source, /settings\.appLock\.manageTitle/);
|
|
assert.doesNotMatch(
|
|
source,
|
|
/hasAppLockPassword \? t\("settings\.appLock\.enableDesc"\) : t\("settings\.appLock\.enableAfterPassword"\)/,
|
|
);
|
|
});
|
|
|
|
test("app lock section exposes settings-search anchors", () => {
|
|
const source = readAppLockSectionSource();
|
|
|
|
assert.match(source, /anchorId="system-app-lock"/);
|
|
});
|
|
|
|
test("app lock disable handler uses its modal current password field", () => {
|
|
const source = readAppLockSectionSource();
|
|
const handlerStart = source.indexOf("const handleDisable = useCallback");
|
|
const handlerEnd = source.indexOf("const handleSavePassword", handlerStart);
|
|
const handlerSource = source.slice(handlerStart, handlerEnd);
|
|
|
|
assert.match(handlerSource, /requestAppLockDisable\(disablePassword\)/);
|
|
assert.match(handlerSource, /disablePassword,/);
|
|
assert.doesNotMatch(source, /handleAppLockEnabledChange/);
|
|
});
|
|
|
|
test("app lock page uses settings rows and moves password forms into a dialog", () => {
|
|
const source = readAppLockSectionSource();
|
|
const dialogStart = source.indexOf('<Dialog');
|
|
const firstPasswordInput = source.indexOf('<Input');
|
|
|
|
assert.match(source, /<SettingCard divided>/);
|
|
assert.match(source, /<SettingRow/);
|
|
assert.match(source, /<DialogContent/);
|
|
assert.ok(firstPasswordInput > dialogStart, "password inputs should only render inside the dialog");
|
|
assert.doesNotMatch(source.slice(0, dialogStart), /<Input/);
|
|
assert.doesNotMatch(source, /settings\.appLock\.enableDesc/);
|
|
assert.match(source, /settings\.appLock\.disableTitle/);
|
|
assert.match(source, /settings\.appLock\.disableDescription/);
|
|
assert.match(source, /settings\.appLock\.disable/);
|
|
assert.match(source, /settings\.appLock\.changePasswordTitle/);
|
|
assert.match(source, /settings\.appLock\.currentPasswordForDisablePlaceholder/);
|
|
assert.match(source, /settings\.appLock\.currentPasswordForChangePlaceholder/);
|
|
});
|
|
|
|
test("app lock password setup keeps whitespace as password content", () => {
|
|
const source = readAppLockSectionSource();
|
|
|
|
assert.ok(source.includes("newPassword.length === 0"));
|
|
assert.ok(source.includes("confirmPassword.length === 0"));
|
|
assert.ok(!source.includes("newPassword.trim()"));
|
|
assert.ok(!source.includes("confirmPassword.trim()"));
|
|
});
|
|
|
|
test("app lock system unlock setting uses bridge-provided platform label", () => {
|
|
const source = readAppLockSectionSource();
|
|
|
|
assert.match(source, /showSystemUnlock/);
|
|
assert.match(source, /appLockSystemUnlockStatus\.label/);
|
|
assert.match(source, /settings\.appLock\.systemUnlock\.label/);
|
|
assert.doesNotMatch(source, /navigator\.platform/);
|
|
});
|
|
|
|
test("app lock system unlock enablement does not require current password in settings", () => {
|
|
const source = readAppLockSectionSource();
|
|
const handlerStart = source.indexOf("const handleSystemUnlockChange = useCallback");
|
|
const handlerEnd = source.indexOf("const handleAutoPromptChange", handlerStart);
|
|
const handlerSource = source.slice(handlerStart, handlerEnd);
|
|
|
|
assert.match(handlerSource, /setAppLockSystemUnlockEnabled\(\{/);
|
|
assert.doesNotMatch(handlerSource, /appLockSystemUnlockPassword/);
|
|
assert.doesNotMatch(handlerSource, /currentPassword:/);
|
|
|
|
const systemUnlockStart = source.indexOf("{showSystemUnlock");
|
|
const systemUnlockEnd = source.indexOf("settings.appLock.changePasswordTitle", systemUnlockStart);
|
|
const systemUnlockSection = source.slice(systemUnlockStart, systemUnlockEnd);
|
|
assert.doesNotMatch(systemUnlockSection, /settings\.appLock\.currentPassword/);
|
|
});
|
|
|
|
test("app lock system unlock setting hides when unavailable unless already enabled", () => {
|
|
const source = readAppLockSectionSource();
|
|
|
|
assert.match(source, /appLockSystemUnlockStatus\.available \|\| appLockSettings\.systemUnlockEnabled/);
|
|
// Already-enabled toggle stays clickable when unavailable so the user can disable.
|
|
assert.match(
|
|
source,
|
|
/disabled=\{\s*isSavingSystemUnlock\s*\|\|\s*\(!appLockSystemUnlockStatus\.available && !appLockSettings\.systemUnlockEnabled\)\s*\}/,
|
|
);
|
|
});
|
|
|
|
test("app lock system unlock exposes auto prompt as a child option", () => {
|
|
const source = readAppLockSectionSource();
|
|
|
|
assert.match(source, /settings\.appLock\.systemUnlock\.autoPrompt\.label/);
|
|
assert.match(source, /settings\.appLock\.systemUnlock\.autoPrompt\.desc/);
|
|
assert.match(source, /appLockSettings\.systemUnlockAutoPromptEnabled/);
|
|
assert.match(source, /appLockSettings\.systemUnlockEnabled/);
|
|
});
|
|
|
|
test("app lock system unlock handles native verification failures", () => {
|
|
const source = readAppLockSectionSource();
|
|
|
|
assert.match(source, /'cancelled' \| 'failed'/);
|
|
assert.match(source, /result\.error === 'cancelled'/);
|
|
assert.match(source, /case 'failed':\s*return t\('settings\.appLock\.systemUnlock\.unavailable'\)/);
|
|
});
|
|
|
|
test("app lock disable explains that turning it off removes the saved password", () => {
|
|
const englishLocale = readFileSync(new URL("../../../application/i18n/locales/en/core.ts", import.meta.url), "utf8");
|
|
|
|
assert.match(englishLocale, /saved password will be removed/);
|
|
assert.match(englishLocale, /requires creating a new one/);
|
|
});
|