Published source
Download source ZIP
CH-J Server Manager
Browse directories and files for a specific application release.
Source is provided under the CH-J Proprietary Software License 1.14. Its availability does not change the license terms or grant additional rights.
1
"use strict";2
// Isolated UI test. Only the OS adapter is mocked; no production authentication is bypassed.3
const { app, ipcMain } = require("electron");4
const assert = require("node:assert/strict"), fs = require("node:fs"), os = require("node:os"), path = require("node:path");5
const { createMainWindow, hardenSession } = require("../src/main/bootstrap/createMainWindow");6
const { registerCoreIpc } = require("../src/main/ipc/registerCoreIpc");7
const { VaultStore } = require("../src/main/security/vaultStore");8
const { ConfigStore } = require("../src/main/config/configStore");9
const { BiometricService } = require("../src/main/security/biometrics/biometricService");10
const { VaultLockController } = require("../src/main/security/vaultLockController");11
const root = fs.mkdtempSync(path.join(os.tmpdir(), "chj-biometric-ui-")); app.setPath("userData", root);12
let window, lock, service; const errors = [], keys = new Map();13
const wait = ms => new Promise(resolve => setTimeout(resolve, ms));14
async function until(source) { for (let i = 0; i < 100; i++) { if (await window.webContents.executeJavaScript(source)) return; await wait(50); } throw new Error("UI timeout: " + source); }15
async function run() {16
await app.whenReady(); const config = new ConfigStore(root); config.load();17
const vault = new VaultStore(root); await vault.create("test-password"); vault.lock();18
const adapter = { info: { provider: "touch-id", protection: "biometry-current-set" },19
getAvailability: async () => ({ available: true, code: "BIOMETRIC_AVAILABLE", ...adapter.info }), enroll: async () => {}, authenticate: async () => {},20
storeProtectedKey: async (id, key) => { keys.set(id, Buffer.from(key)); }, retrieveProtectedKey: async id => Buffer.from(keys.get(id)), removeEnrollment: async id => keys.delete(id) };21
service = new BiometricService({ storageRoot: root, vaultStore: vault, configStore: config, adapter });22
const pluginRuntime = { notifyLanguageChanged() {}, closeAll() {} }, sessionManager = { disconnectAll: async () => {} };23
lock = new VaultLockController({ vaultStore: vault, configStore: config, biometricService: service, pluginRuntime, sessionManager });24
hardenSession(); window = createMainWindow(); window.webContents.on("console-message", event => { if (event.level === "error") errors.push(event.message); });25
lock.on("locked", status => window.webContents.send("vault:locked", status));26
registerCoreIpc({ ipcMain, app, configStore: config, vaultStore: vault, biometricService: service, vaultLockController: lock, pluginRuntime, sessionManager,27
pluginService: { getState: () => ({ installed: [], catalog: [], windows: [] }) }, profileService: { list: () => [] },28
updateService: { getState: () => ({ currentVersion: "0.0.1", status: "idle", releases: [] }) }, getMainWindow: () => window });29
await until('state.vault && biometricStatus');30
assert.equal(await window.webContents.executeJavaScript('document.querySelector("#biometricUnlockButton").hidden'), true);31
await window.webContents.executeJavaScript('document.querySelector("#vaultPassword").value="test-password"; document.querySelector("#vaultForm").requestSubmit()');32
await until('state.vault.unlocked');33
await window.webContents.executeJavaScript('document.querySelector("[data-view=settings]").click(); document.querySelector("#enableBiometricsButton").click()');34
await until('biometricStatus.enabled'); assert.equal(keys.size, 1);35
await window.webContents.executeJavaScript('document.querySelector("#autoLockEnabled").click()');36
assert.equal(await window.webContents.executeJavaScript('document.querySelector("#autoLockMinutes").value'), "15");37
await window.webContents.executeJavaScript('document.querySelector("#securityMasterPassword").value="test-password";document.querySelector("#securityForm").requestSubmit()');38
await until('state.config.security.autoLockMinutes === 15');39
const artifacts = process.env.CHJ_BIOMETRIC_SMOKE_ARTIFACT_DIR;40
if (artifacts) { await window.webContents.executeJavaScript('document.querySelector("#securityForm").scrollIntoView()'); await wait(100); fs.mkdirSync(artifacts, { recursive: true }); fs.writeFileSync(path.join(artifacts, "security-settings.png"), (await window.webContents.capturePage()).toPNG()); }41
await lock.lock(); await until('!state.vault.unlocked && !document.querySelector("#biometricUnlockButton").hidden');42
for (const [language, label] of [["cs", "Odemknout pomocí Touch ID"], ["de", "Mit Touch ID entsperren"], ["en", "Unlock with Touch ID"]]) {43
await window.webContents.executeJavaScript(`applyLanguage(${JSON.stringify(language)})`);44
assert.equal(await window.webContents.executeJavaScript('document.querySelector("#biometricUnlockButton").textContent'), label);45
}46
await window.webContents.executeJavaScript('document.querySelector("#biometricUnlockButton").click()'); await until('state.vault.unlocked');47
assert.equal(await window.webContents.executeJavaScript('typeof window.require'), "undefined");48
await window.webContents.executeJavaScript('document.querySelector("#disableBiometricsButton").click()'); await until('!biometricStatus.enabled'); assert.equal(keys.size, 0);49
assert.deepEqual(errors, []); console.log("CHJ_BIOMETRICS_UI_OK: master password, enable, lock, biometric unlock, remove, default15, CZ/DE/EN, sandbox (mock OS adapter)");50
}51
let finished = false;52
function finish(code) {53
if (finished) return; finished = true; lock?.dispose(); window?.destroy();54
for (const key of keys.values()) key.fill(0);55
try { fs.rmSync(root, { recursive: true, force: true, maxRetries: 3, retryDelay: 100 }); }56
catch (error) { console.warn("Temporary Chromium cache cleanup:", error.code); }57
app.exit(code);58
}59
run().then(() => finish(0), error => { console.error(error); finish(1); }); setTimeout(() => { console.error("Biometric UI smoke timed out"); finish(1); }, 30000).unref();SHA-256: 7ee36a0e05469668e85deece96ef2112a6702653cbaaf406c3c4bcac6960d415
Archive SHA-256: 5ac91caf4fa32a6fdb114f2430deed486fbe7489d5eea343d1f034169fafb5e0