repositories / dotfiles
dotfiles
bugabingas dorkfiles
owned by admin
quickshell/nuguland/vault-runtime.test.mjs
Rawimport assert from "node:assert/strict";
import { spawnSync } from "node:child_process";
import { copyFileSync, mkdtempSync, mkdirSync, readFileSync, rmSync, writeFileSync, existsSync } from "node:fs";
import { tmpdir } from "node:os";
import { join, resolve } from "node:path";
// Real QML Process/FileView lifecycle, isolated from the desktop, clipboard and user settings.
const directory = mkdtempSync(join(tmpdir(), "nuguland-vault-runtime-test-"));
const profile = {
name: "personal", serverUrl: "https://vault.example.com", account: "initial@example.com",
unlockTimeoutSeconds: 900, clipboardTimeoutSeconds: 30,
};
try {
for (const file of ["Vault.qml", "vault.js", "VaultPanel.qml", "Theme.qml", "NuguText.qml", "PopupCard.qml", "PopupButton.qml", "PopupTextField.qml", "PopupToolTip.qml", "SetupField.qml"])
copyFileSync(new URL(file, import.meta.url), join(directory, file));
mkdirSync(join(directory, "state"));
mkdirSync(join(directory, "vault-native"));
writeFileSync(join(directory, "state", "vault.json"), JSON.stringify(profile));
writeFileSync(join(directory, "Clipboard.qml"), `import QtQuick
Item {
signal vaultCapturePaused
function pauseForVaultCopy(seconds) {}
function resumeAfterVaultCopy() {}
}
`);
writeFileSync(join(directory, "old.cjs"), `const fs = require("node:fs");
const lines = require("node:readline").createInterface({ input: process.stdin });
lines.on("line", line => {
if (JSON.parse(line).op === "configure-lock") {
fs.writeFileSync(${JSON.stringify(join(directory, "obsolete-request"))}, "rejected");
console.log(JSON.stringify({ event: "protocol-error" }));
}
});
`);
const binary = resolve("vault-native/target/debug/nuguland-vault");
writeFileSync(join(directory, "vault-native", "run.sh"), `#!/bin/sh
printf x >> '${directory}/launches'
if [ ! -f '${directory}/old-started' ]; then
touch '${directory}/old-started'
exec node '${directory}/old.cjs'
fi
exec '${binary}'
`, { mode: 0o700 });
writeFileSync(join(directory, "shell.qml"), `//@ pragma StateDir ${directory}/state
import QtQuick
import Quickshell
import Quickshell.Io
ShellRoot {
id: test
property int phase: 0
property string savedAccount: ""
property var retainedField: null
property var fixtureDetail: ({ event: "detail", id: "fixture", fields: [{ id: "login.password", label: "password", value: "synthetic", sensitive: true, kind: "text" }], totp: { current: "123456", next: "654321", remaining: 1, currentSlot: 0, nextSlot: 1 } })
function find(item, name) {
if (item.objectName === name) return item;
for (const child of item.children) {
const found = find(child, name);
if (found) return found;
}
return null;
}
Clipboard { id: clipboard }
Vault { id: vault; clipboardService: clipboard }
Theme { id: theme }
Window {
visible: true
width: 980
height: 620
VaultPanel { id: panel; anchors.fill: parent; theme: theme; vaultService: vault }
}
FileView {
id: saved
path: vault.configPath
watchChanges: true
onFileChanged: reload()
onLoaded: test.savedAccount = JSON.parse(text()).account
}
Timer {
interval: 20
running: true
repeat: true
onTriggered: {
if (vault.setupError) {
console.error("vault runtime test phase " + test.phase + ": " + vault.setupError);
Qt.quit();
} else if (test.phase === 0 && !vault.setupSaving && vault.profile.account === "initial@example.com") {
vault.beginSetup();
test.phase = 1;
} else if (test.phase === 1 && !vault.lockPending) {
vault.setupUrlDraft = "other.example.com";
vault.setupAccountDraft = "saved@example.com";
vault.saveSetup();
test.phase = 2;
} else if (test.phase === 2 && !vault.setupSaving && !vault.setupEditing
&& vault.status === "locked" && test.savedAccount === "saved@example.com") {
vault.beginSetup();
test.phase = 3;
} else if (test.phase === 3 && !vault.lockPending) {
vault.setupUrlDraft = "http://invalid.example.com";
vault.testConnection();
test.phase = 4;
} else if (test.phase === 4 && !vault.connectionBusy && vault.connectionResult.indexOf("https") >= 0) {
if (vault.profile.serverUrl !== "https://other.example.com" || test.savedAccount !== "saved@example.com")
console.error("connection test changed saved settings");
else {
vault.handleLine(JSON.stringify({ event: "state", status: "second-factor", items: [],
challenge: { providers: [{ id: 1, label: "email code", codeSupported: true }] } }));
vault.panelOpen = true;
vault.selectSecondFactor(1);
vault.secondFactorDraft = "123";
vault.panelOpen = false;
test.phase = 5;
}
} else if (test.phase === 5) {
vault.panelOpen = true;
if (vault.status !== "second-factor" || vault.secondFactorProvider !== 1 || vault.secondFactorDraft !== "123"
|| vault.secondFactorHint().indexOf("check your inbox") < 0)
console.error("closing lost verification state");
else
console.info("vault runtime test: saved");
vault.cancelUnlock();
if (vault.secondFactorDraft !== "")
console.error("cancel retained verification code");
test.phase = 6;
} else if (test.phase === 6 && !vault.lockPending && vault.status === "locked") {
vault.cancelSetup();
vault.status = "unlocked";
vault.selectedId = "fixture";
vault.handleLine(JSON.stringify(test.fixtureDetail));
test.phase = 7;
} else if (test.phase === 7) {
test.retainedField = test.find(panel, "vault-field-login.password");
if (!test.retainedField) {
console.error("fixture field delegate missing");
Qt.quit();
return;
}
test.retainedField.forceActiveFocus();
const update = Object.assign({}, test.fixtureDetail, { totp: { current: "654321", next: "987654", remaining: 30, currentSlot: 1, nextSlot: 2 } });
vault.handleLine(JSON.stringify(update));
test.phase = 8;
} else if (test.phase === 8) {
if (test.find(panel, "vault-field-login.password") !== test.retainedField || !test.retainedField.activeFocus)
console.error("TOTP rollover replaced the field delegate or lost keyboard focus");
else
console.info("vault runtime test: field focus retained");
vault.lock();
test.phase = 9;
} else if (test.phase === 9 && !vault.lockPending) {
if (!test.find(panel, "vaultMasterPassword").input.activeFocus)
console.error("locking failed to focus the master password input");
else
console.info("vault runtime test: unlock focused");
Qt.quit();
}
}
}
}
`);
mkdirSync(join(directory, "runtime"), { mode: 0o700 });
const environment = {
...process.env,
QT_QPA_PLATFORM: "offscreen", QT_QUICK_BACKEND: "software", QT_QPA_PLATFORMTHEME: "",
QT_NO_XDG_DESKTOP_PORTAL: "1", QS_NO_RELOAD_POPUP: "1",
XDG_STATE_HOME: join(directory, "state"), XDG_CACHE_HOME: join(directory, "cache"),
XDG_DATA_HOME: join(directory, "data"), XDG_RUNTIME_DIR: join(directory, "runtime"),
};
delete environment.WAYLAND_DISPLAY;
delete environment.DISPLAY;
const result = spawnSync("qs", ["--no-color", "-p", join(directory, "shell.qml")], {
cwd: directory,
env: environment,
encoding: "utf8", timeout: 15_000,
});
const output = result.stdout + result.stderr + "\nlaunches: " + (existsSync(join(directory, "launches")) ? readFileSync(join(directory, "launches"), "utf8") : "none");
assert.equal(result.error, undefined, output);
assert.equal(result.status, 0, output);
assert.doesNotMatch(output, /WARN|ERROR|Error:|TypeError|ReferenceError/, output);
assert.match(output, /vault runtime test: saved/);
assert.match(output, /vault runtime test: field focus retained/);
assert.match(output, /vault runtime test: unlock focused/);
assert.equal(existsSync(join(directory, "obsolete-request")), false, "old protocol must not receive configuration requests");
assert.equal(readFileSync(join(directory, "launches"), "utf8"), "xxxx", "replace obsolete boundary, replace on save, test in a separate boundary");
assert.deepEqual(JSON.parse(readFileSync(join(directory, "state", "vault.json"), "utf8")), {
...profile, serverUrl: "https://other.example.com", account: "saved@example.com",
});
console.log("vault-runtime.test.mjs: ok");
} finally {
rmSync(directory, { recursive: true, force: true });
}