- hypr/apps.lua - hypr/autostart.lua - hypr/envs.lua - hypr/hyprland.lua - hypr/hyprsunset.conf - hypr/input.lua - hypr/looknfeel.lua - hypr/omasettings.lua - hypr/xdph.conf - omarchy/branding/about.txt - omarchy/branding/screensaver.txt - omarchy/extensions/omarchy-menu.jsonc - omarchy/hooks/battery-low.d/play-warning-sound.sample - omarchy/hooks/font-set.d/show-font-notification.sample - omarchy/hooks/post-boot.d/weather.sample - omarchy/hooks/post-update.d/install-voxtype.hook - omarchy/hooks/post-update.d/setup-agent.hook - omarchy/hooks/post-update.d/setup-fingerprint.hook - omarchy/hooks/post-update.d/show-update-notification.sample - omarchy/hooks/pre-refresh-pacman.d/add-custom-repo.sample - omarchy/hooks/theme-set.d/show-theme-notification.sample - omarchy/shell.json - omarchy/shell.toml - omarchy/theme.name - omarchy/themes/azure-glow/README.md - omarchy/themes/azure-glow/alacritty.toml - omarchy/themes/azure-glow/btop.theme - omarchy/themes/azure-glow/hyprland.conf - omarchy/themes/azure-glow/hyprlock.conf - omarchy/themes/azure-glow/icons.theme - … 269 more
115 lines
5.5 KiB
JavaScript
115 lines
5.5 KiB
JavaScript
#!/usr/bin/env node
|
|
// The first post-authentication bw process is the item list. Organization and
|
|
// folder metadata must not compete with it; they begin only after items have
|
|
// reached the model and had an event-loop turn to paint.
|
|
//
|
|
// node tests/initial-load.test.js
|
|
|
|
const fs = require("fs")
|
|
const path = require("path")
|
|
|
|
const panelSrc = fs.readFileSync(path.join(__dirname, "..", "Panel.qml"), "utf8")
|
|
let pass = 0
|
|
const failures = []
|
|
const check = (label, ok, detail) => ok ? pass++ : failures.push(`${label}\n ${detail}`)
|
|
|
|
const bodyOf = name => {
|
|
const start = panelSrc.indexOf(`function ${name}(`)
|
|
if (start === -1) return ""
|
|
let depth = 0
|
|
for (let i = panelSrc.indexOf("{", start); i < panelSrc.length; i++) {
|
|
if (panelSrc[i] === "{") depth++
|
|
else if (panelSrc[i] === "}" && --depth === 0) return panelSrc.slice(start, i + 1)
|
|
}
|
|
return ""
|
|
}
|
|
|
|
const initial = bodyOf("beginInitialVaultLoad")
|
|
check("initial loading starts the item list", /loadItems\(/.test(initial), initial)
|
|
check("initial loading does not start organizations concurrently",
|
|
!/loadOrganizations\(/.test(initial), initial)
|
|
check("initial loading does not start folders concurrently",
|
|
!/loadFolders\(/.test(initial), initial)
|
|
|
|
for (const source of ["onUnlockSuccess", "onSessionHandoff"]) {
|
|
const body = bodyOf(source)
|
|
check(`${source} uses the items-first entry point`, /beginInitialVaultLoad\(/.test(body), body)
|
|
check(`${source} does not launch organization metadata directly`, !/loadOrganizations\(/.test(body), body)
|
|
check(`${source} does not launch folder metadata directly`, !/loadFolders\(/.test(body), body)
|
|
}
|
|
|
|
const listFinished = bodyOf("onListFinished")
|
|
check("metadata deferral begins only after the item result is accepted",
|
|
/items\s*=\s*Model\.parseSanitizedItems/.test(listFinished)
|
|
&& !/items\s*=\s*Model\.parseItems/.test(listFinished)
|
|
&& /deferredMetadataTimer\.restart\(\)/.test(listFinished)
|
|
&& listFinished.indexOf("items = Model.parseSanitizedItems") < listFinished.indexOf("deferredMetadataTimer.restart()"),
|
|
listFinished)
|
|
|
|
const listExited = bodyOf("onListProcessExited")
|
|
check("item output is accepted only after the process exit status is known",
|
|
/exitCode\s*===\s*0/.test(listExited) && /onListFinished\(/.test(listExited), listExited)
|
|
check("a failed item refresh clears all loading and deferred-work state",
|
|
/isLoading\s*=\s*false/.test(listExited)
|
|
&& /isSyncing\s*=\s*false/.test(listExited)
|
|
&& /metadataLoadPending\s*=\s*false/.test(listExited)
|
|
&& /syncReloadPending\s*=\s*false/.test(listExited),
|
|
listExited)
|
|
check("a failed item refresh does not run the post-load status refresh",
|
|
!/statusRefreshAfterItems[\s\S]{0,140}runStatusCheck\(/.test(listExited),
|
|
listExited)
|
|
|
|
const timerStart = panelSrc.indexOf("id: deferredMetadataTimer")
|
|
const timer = timerStart === -1 ? "" : panelSrc.slice(timerStart, timerStart + 700)
|
|
check("deferred metadata loads both organizations and folders", /loadOrganizations\(/.test(timer) && /loadFolders\(/.test(timer), timer)
|
|
check("metadata waits long enough for an item-list frame",
|
|
/interval:\s*(?:[2-9][0-9]|[1-9][0-9]{2,})/.test(timer), timer)
|
|
check("post-load status refresh is metadata-only",
|
|
/runStatusCheck\(false\)/.test(timer)
|
|
&& /function runStatusCheck\(authoritative\)/.test(panelSrc)
|
|
&& /statusCheckAuthoritative\s*=\s*authoritative\s*!==\s*false/.test(panelSrc)
|
|
&& /if\s*\(!authoritative\)\s*\{[\s\S]{0,220}return/.test(bodyOf("onStatusFinished")),
|
|
bodyOf("runStatusCheck") + "\n" + bodyOf("onStatusFinished") + "\n" + timer)
|
|
|
|
const sync = bodyOf("onSyncFinished")
|
|
check("a successful server sync also reloads items before metadata",
|
|
/beginInitialVaultLoad\(/.test(sync) && !/loadOrganizations\(/.test(sync) && !/loadFolders\(/.test(sync), sync)
|
|
|
|
check("the empty list says when items are loading", panelSrc.includes('"Loading items..."'), "missing loading label")
|
|
const syncButton = panelSrc.slice(panelSrc.indexOf("// Sync Vault Button"), panelSrc.indexOf("// Send Button"))
|
|
check("the compact sync control reports progress using supported PanelActionButton properties",
|
|
/tooltipText:\s*root\.isSyncing\s*\?\s*"Syncing\.\.\."/.test(syncButton)
|
|
&& /enabled:\s*!root\.isSyncing/.test(syncButton)
|
|
&& !/iconSpinning/.test(syncButton),
|
|
syncButton)
|
|
check("the panel header reports sync progress in text",
|
|
/if\s*\(root\.isSyncing\)\s*return\s*"Syncing\.\.\."/.test(panelSrc),
|
|
"missing Syncing... header state")
|
|
|
|
const listProcessStart = panelSrc.indexOf("id: listProc")
|
|
const listProcess = listProcessStart === -1 ? "" : panelSrc.slice(listProcessStart, listProcessStart + 900)
|
|
check("the item process waits for onExited instead of racing its stdout and stderr handlers",
|
|
/onExited:[\s\S]*onListProcessExited/.test(listProcess)
|
|
&& !/onStreamFinished:[\s\S]*onListFinished/.test(listProcess),
|
|
listProcess)
|
|
|
|
const processBlock = id => {
|
|
const idAt = panelSrc.indexOf(`id: ${id}`)
|
|
if (idAt === -1) return ""
|
|
const next = panelSrc.indexOf("\n Process {", idAt)
|
|
return panelSrc.slice(idAt, next === -1 ? panelSrc.length : next)
|
|
}
|
|
for (const id of ["statusProc", "sessionHandoffProc", "listOrgsProc", "listFoldersProc",
|
|
"orgCollectionsProc", "listSendsProc", "keyringLookupMasterProc",
|
|
"getItemProc", "getTotpProc"]) {
|
|
const block = processBlock(id)
|
|
check(`${id} accepts output only after its exit status is known`,
|
|
/onExited:/.test(block) && !/onStreamFinished:/.test(block), block)
|
|
}
|
|
|
|
console.log(`${pass} passed, ${failures.length} failed`)
|
|
if (failures.length) {
|
|
console.error("\nFAILURES:\n " + failures.join("\n "))
|
|
process.exit(1)
|
|
}
|