Phase 6, the last of the fresh-install spec. 159 scripts lose their .sh: 110 contracts, 47 Vicinae commands, 2 compositor contracts. A shebang and the executable bit already select the interpreter. The extension only ever added something that had to stay in sync, and the rename proved the point twice over in the space of an hour. The spec's stated risk was Vicinae's script discovery. One script was renamed and reloaded on its own before the other 46 followed; it came back as scripts:panama.capture and all 47 resolve. What the probe turned up instead is that the extension was never only a filename: Vicinae's command IDs embed it, so every ID changed. Nothing in this repository refers to them, so nothing breaks. The only trace is Vicinae's metadata.json, whose visited map had two Panama entries that are now orphaned -- two commands lost their usage ranking and will earn it back. Worth knowing before anyone renames these again on a machine that has a keybind pointing at one. Rewriting the references by exact filename missed two things it structurally could not see: a name built from a variable, settings-$page.sh, and a glob, -name '*.sh'. Both were in the contract that counts the generated commands, which promptly reported 47 expected and 0 found. The mechanical part of a rename is the part that looks finished. The three subcommands. panama doctor fronts a health check that already existed and already ran at the end of every install but could not be reached from a terminal. panama upgrade re-runs the installer from anywhere. panama test runs the suite, which had no entry point at all -- 121 files that were the main safety net in this repository and were invisible in it. Writing that runner found three tests nothing was running. calendar_agenda_bridge_test, home_assistant_bridge_test and kdeconnect_bridge_test are unittest suites without the executable bit, so no contract invoked them and the first draft of the runner skipped them silently. All three pass, and have passed unobserved for weeks. The runner collects *_test.py as well now, because a runner with a blind spot is worse than no runner for the same reason a dependency checker with one is: it reports PASS. Six worktrees pruned. Each was re-checked rather than trusted to the spec's list, and two needed it: panama-commands is not on feat/panama-commands but on feat/gnome-tweaks-parity, and fix/panama-displays-review reads [ahead 3] -- ahead of its remote, not of main, with every commit patch-equivalent to landed work. roadmap-completion stays; it has five commits that are genuinely unlanded. The branches are left alone: pruning a worktree costs nothing, deleting a branch is a decision. 121 contracts pass. Claude-Session: https://claude.ai/code/session_01NvgBuSWB5sE43yWmg21ozj
431 lines
20 KiB
Bash
Executable File
431 lines
20 KiB
Bash
Executable File
#!/usr/bin/env bash
|
|
|
|
# The approved Diagnostic Ledger is exercised in an isolated Quickshell
|
|
# harness. It never maps or reloads the user's production shell.
|
|
|
|
set -euo pipefail
|
|
|
|
repo_dir="$(cd "$(dirname "${BASH_SOURCE[0]}")/../.." && pwd)"
|
|
settings_dir="$repo_dir/config/dot/quickshell/modules/settings"
|
|
bar_dir="$repo_dir/config/dot/quickshell/modules/bar"
|
|
|
|
fail() {
|
|
printf 'health UI contract: %s\n' "$1" >&2
|
|
exit 1
|
|
}
|
|
|
|
for file in HealthPage.qml HealthSummary.qml HealthCheckRow.qml; do
|
|
[[ -f "$settings_dir/$file" ]] || fail "$file is missing"
|
|
done
|
|
[[ -f "$bar_dir/HealthIndicator.qml" ]] || fail 'HealthIndicator.qml is missing'
|
|
|
|
python3 - "$bar_dir/Bar.qml" <<'PY' || fail 'health indicator is not immediately before activity in the bar'
|
|
import re
|
|
import sys
|
|
|
|
source = open(sys.argv[1], encoding="utf-8").read()
|
|
match = re.search(r'HealthIndicator\s*\{.*?\}\s*ActivityIndicator\s*\{', source, re.S)
|
|
assert match is not None
|
|
PY
|
|
|
|
! rg -n '#[0-9a-fA-F]{3,8}' "$bar_dir/HealthIndicator.qml" >/dev/null \
|
|
|| fail 'health indicator introduced colors outside Theme'
|
|
! rg -n 'Behavior|Animation|Transition|pulse|shimmer' "$bar_dir/HealthIndicator.qml" >/dev/null \
|
|
|| fail 'health indicator introduced motion'
|
|
[[ ! -e "$settings_dir/ServicesPage.qml" ]] || fail 'ServicesPage.qml still exists'
|
|
rg -Fq 'HealthPage 1.0 HealthPage.qml' "$settings_dir/qmldir" \
|
|
|| fail 'HealthPage is not registered in the Settings QML module'
|
|
rg -Fq 'HealthSummary 1.0 HealthSummary.qml' "$settings_dir/qmldir" \
|
|
|| fail 'HealthSummary is not registered in the Settings QML module'
|
|
rg -Fq 'HealthCheckRow 1.0 HealthCheckRow.qml' "$settings_dir/qmldir" \
|
|
|| fail 'HealthCheckRow is not registered in the Settings QML module'
|
|
! rg -Fq 'ServicesPage 1.0 ServicesPage.qml' "$settings_dir/qmldir" \
|
|
|| fail 'retired ServicesPage remains registered in the Settings QML module'
|
|
|
|
rg -Fq 'label: "System Health"' "$settings_dir/SettingsSidebar.qml" \
|
|
|| fail 'sidebar does not label the stable services route System Health'
|
|
rg -Fq 'onTapped: root.pageRequested("services")' "$settings_dir/SettingsSidebar.qml" \
|
|
|| fail 'health footer does not open the stable services route'
|
|
rg -Fq 'height: 54' "$settings_dir/SettingsSidebar.qml" \
|
|
|| fail 'health footer lost its 54px target'
|
|
rg -Fq 'onClicked: Health.copyReport()' "$settings_dir/HealthSummary.qml" \
|
|
|| fail 'Copy Report does not use the redacted Health report path'
|
|
rg -Fq 'text: "Checking…"' "$settings_dir/HealthSummary.qml" \
|
|
|| fail 'refresh state is not expressed in text'
|
|
rg -Fq 'implicitHeight: 126' "$settings_dir/HealthSummary.qml" \
|
|
|| fail 'summary hero is not the approved stable 126px height'
|
|
rg -Fq 'implicitHeight: 62' "$settings_dir/HealthCheckRow.qml" \
|
|
|| fail 'health rows are below the approved 62px target'
|
|
rg -Fq 'Health.refresh()' "$settings_dir/HealthPage.qml" \
|
|
|| fail 'opening System Health does not request a fresh scan'
|
|
rg -Fq 'SystemSettings.openGnomePanel("network")' "$settings_dir/HealthPage.qml" \
|
|
|| fail 'Fedora ownership boundary does not open GNOME Settings'
|
|
# Users and Sharing are Panama pages now. A handoff here would send someone to
|
|
# GNOME for a panel this app owns, which is the opposite of the point -- so the
|
|
# assertion is inverted rather than deleted.
|
|
rg -Fq 'SystemSettings.openGnomePanel("system", "users")' "$settings_dir/HealthPage.qml" \
|
|
&& fail 'System Health still hands Users to GNOME, but Panama owns that page'
|
|
rg -Fq 'SystemSettings.openGnomePanel("sharing")' "$settings_dir/HealthPage.qml" \
|
|
&& fail 'System Health still hands Sharing to GNOME, but Panama owns that page'
|
|
rg -Fq 'SystemSettings.openGnomePanel("color")' "$settings_dir/HealthPage.qml" \
|
|
|| fail 'Fedora ownership boundary lost the Color profiles handoff'
|
|
rg -Fq 'SystemSettings.openGnomePanel("wellbeing")' "$settings_dir/HealthPage.qml" \
|
|
|| fail 'Fedora ownership boundary lost the Digital wellbeing handoff'
|
|
# Exact authored handoffs are asserted above. Also prove every panel named by
|
|
# this boundary is accepted by SystemSettings, so a typo cannot ship a dead
|
|
# button even if its copy still looks correct.
|
|
rg -Fq 'title: "Fedora system settings"' "$settings_dir/HealthPage.qml" \
|
|
|| fail 'the Fedora ownership boundary card is gone'
|
|
|
|
allowed="$(rg -o '"[a-z-]+"' "$repo_dir/config/dot/quickshell/services/SystemSettings.qml" \
|
|
| sed -n '/"\(applications\|background\|bluetooth\|color\|display\|keyboard\|mouse\|multitasking\|network\|notifications\|online-accounts\|power\|printers\|privacy\|search\|sharing\|sound\|system\|universal-access\|wacom\|wellbeing\|wifi\|wwan\)"/p' \
|
|
| tr -d '"' | sort -u)"
|
|
|
|
while read -r panel; do
|
|
[[ -n "$panel" ]] || continue
|
|
grep -qx "$panel" <<<"$allowed" \
|
|
|| fail "the Fedora card opens \"$panel\", which openGnomePanel does not allow -- that button does nothing"
|
|
done < <(rg -o 'openGnomePanel\("([a-z-]+)"' -r '$1' "$settings_dir/HealthPage.qml" | sort -u)
|
|
|
|
rg -q 'openGnomePanel\(' "$settings_dir/HealthPage.qml" \
|
|
|| fail 'the Fedora ownership boundary does not open GNOME Settings at all'
|
|
rg -Fq 'Health.repair(check.id, false)' "$settings_dir/HealthPage.qml" \
|
|
|| fail 'Settings repair does not stay inline/non-external'
|
|
rg -Fq 'ShellState.openSettings(check.action.target)' "$settings_dir/HealthPage.qml" \
|
|
|| fail 'authored Settings targets are not routed directly'
|
|
! rg -n '#[0-9a-fA-F]{3,8}' \
|
|
"$settings_dir/HealthPage.qml" \
|
|
"$settings_dir/HealthSummary.qml" \
|
|
"$settings_dir/HealthCheckRow.qml" >/dev/null \
|
|
|| fail 'health UI introduced colors outside Theme'
|
|
|
|
python3 - "$settings_dir/HealthPage.qml" "$settings_dir/HealthSummary.qml" \
|
|
"$settings_dir/HealthCheckRow.qml" <<'PY' || fail 'approved health structure or accessibility contract is missing'
|
|
import sys
|
|
|
|
page, summary, row = [open(path, encoding="utf-8").read() for path in sys.argv[1:]]
|
|
|
|
labels = (
|
|
'if (status === "ok") return "Healthy";',
|
|
'if (status === "warning") return "Needs attention";',
|
|
'if (status === "error") return "Action required";',
|
|
'return "Not set up";',
|
|
)
|
|
assert all(label in page for label in labels)
|
|
assert 'group: "desktop-foundation"' in page
|
|
assert 'group: "input-media"' in page
|
|
assert 'group: "integrations"' in page
|
|
assert 'group: "panama-tools"' in page
|
|
assert 'SettingsCard {' in page and 'SettingsCard {' in summary
|
|
assert 'activeFocusOnTab: enabled' in summary
|
|
assert 'Keys.onReturnPressed' in summary and 'Keys.onSpacePressed' in summary
|
|
assert 'activeFocusOnTab: enabled' in row
|
|
assert 'Keys.onReturnPressed' in row and 'Keys.onSpacePressed' in row
|
|
assert 'border.width: activeFocus ? 2 : 1' in summary
|
|
assert 'border.width: activeFocus ? 2 : 1' in row
|
|
assert 'Health.diagnosticUnavailable ? "Retry"' in summary
|
|
assert 'Health.lastCopyResult' in summary
|
|
assert 'pendingConfirmation' in page
|
|
assert 'ddc-permissions' in page
|
|
PY
|
|
|
|
fixture='{"schemaVersion":1,"generatedAt":"2026-08-18T12:00:00Z","summary":{"status":"error","healthy":2,"warnings":2,"errors":1,"unconfigured":1},"context":{"session":"hyprland","versions":[{"id":"quickshell","version":"0.3.0"}]},"checks":[{"id":"desktop.vicinae","group":"desktop-foundation","title":"Vicinae","status":"warning","detail":"The launcher service is stopped.","action":{"kind":"repair","label":"Restart Vicinae","confirm":false}},{"id":"desktop.quickshell","group":"desktop-foundation","title":"Quickshell","status":"error","detail":"Panama shell needs to restart.","action":{"kind":"repair","label":"Restart Panama","confirm":true}},{"id":"input.pipewire","group":"input-media","title":"PipeWire","status":"ok","detail":"Audio graph is responding."},{"id":"integration.bluebubbles","group":"integrations","title":"BlueBubbles","status":"unconfigured","detail":"Messaging integration has not been enabled."},{"id":"integration.calendar","group":"integrations","title":"Calendar","status":"warning","detail":"Calendar probe timed out.","action":{"kind":"open","label":"Open Date & Time","confirm":false,"target":"datetime"}},{"id":"panama.caffeine","group":"panama-tools","title":"Caffeine","status":"ok","detail":"No duplicate sleep inhibitors."}]}'
|
|
|
|
state_home="$(mktemp -d /tmp/panama-health-ui.XXXXXX)"
|
|
config_path="$state_home/quickshell"
|
|
harness="$config_path/health-ui-harness.qml"
|
|
helper="$state_home/panama-doctor"
|
|
shell_log="$state_home/quickshell.log"
|
|
fixture_home="$state_home/home"
|
|
mkdir -p "$fixture_home"
|
|
cp -a "$repo_dir/config/dot/quickshell" "$config_path"
|
|
|
|
cat >"$helper" <<'EOF'
|
|
#!/usr/bin/env bash
|
|
set -euo pipefail
|
|
if [[ "${1:-}" == "--repair" ]]; then
|
|
sleep 0.35
|
|
printf '{"schemaVersion":1,"checkId":"%s","accepted":true,"exitCode":7,"message":"The authored repair failed."}\n' "$2"
|
|
exit 7
|
|
fi
|
|
sleep 1.5
|
|
printf '%s\n' "$PANAMA_HEALTH_FIXTURE"
|
|
EOF
|
|
chmod +x "$helper"
|
|
|
|
cat >"$harness" <<'QML'
|
|
import Quickshell
|
|
import Quickshell.Io
|
|
import QtQuick
|
|
|
|
import "modules/bar" as BarModule
|
|
import qs.config
|
|
import qs.modules.settings
|
|
import qs.services
|
|
|
|
ShellRoot {
|
|
id: root
|
|
|
|
Component.onCompleted: {
|
|
Health.startupScanEnabled = false;
|
|
ShellState.settingsPage = "services";
|
|
Health.consumeSnapshot(Quickshell.env("PANAMA_HEALTH_FIXTURE"), 100);
|
|
}
|
|
|
|
FloatingWindow {
|
|
title: "Panama Health UI Contract"
|
|
visible: true
|
|
implicitWidth: 980
|
|
implicitHeight: 820
|
|
|
|
SettingsShell {
|
|
id: settingsShell
|
|
anchors.fill: parent
|
|
}
|
|
|
|
BarModule.HealthIndicator {
|
|
id: healthIndicator
|
|
objectName: "health-indicator"
|
|
}
|
|
}
|
|
|
|
IpcHandler {
|
|
target: "health-ui-test"
|
|
|
|
function state(): string {
|
|
return JSON.stringify(settingsShell.healthDiagnostics);
|
|
}
|
|
|
|
function request(id: string): bool {
|
|
return settingsShell.requestHealthAction(id);
|
|
}
|
|
|
|
function indicatorMode(mode: string): bool {
|
|
const checkStatus = mode === "healthy" ? "ok" : (mode === "unconfigured" ? "unconfigured" : mode);
|
|
const overallStatus = mode === "warning" || mode === "error" ? mode : "healthy";
|
|
const snapshot = {
|
|
schemaVersion: 1,
|
|
generatedAt: "2026-08-18T12:00:00Z",
|
|
summary: {
|
|
status: overallStatus,
|
|
healthy: mode === "healthy" ? 1 : 0,
|
|
warnings: mode === "warning" ? 1 : 0,
|
|
errors: mode === "error" ? 1 : 0,
|
|
unconfigured: mode === "unconfigured" ? 1 : 0
|
|
},
|
|
context: { session: "hyprland", versions: [] },
|
|
checks: [{
|
|
id: "desktop.vicinae",
|
|
group: "desktop-foundation",
|
|
title: "Vicinae",
|
|
status: checkStatus,
|
|
detail: "Fixture observation."
|
|
}]
|
|
};
|
|
return Health.consumeSnapshot(JSON.stringify(snapshot), Health.acceptedGeneration + 1);
|
|
}
|
|
|
|
function indicatorState(): string {
|
|
return JSON.stringify({
|
|
visible: healthIndicator.visible,
|
|
width: healthIndicator.width,
|
|
implicitWidth: healthIndicator.implicitWidth,
|
|
issueCount: healthIndicator.issueCount,
|
|
statusText: healthIndicator.statusText,
|
|
accessibleLabel: healthIndicator.accessibleLabel,
|
|
tooltipText: healthIndicator.tooltipText,
|
|
warningTone: healthIndicator.tone === Theme.warn,
|
|
errorTone: healthIndicator.tone === Theme.danger,
|
|
activeFocusOnTab: healthIndicator.activeFocusOnTab
|
|
});
|
|
}
|
|
|
|
function activateIndicator(): string {
|
|
ShellState.settingsPage = "home";
|
|
ShellState.settingsOpen = false;
|
|
const generation = Health.generation;
|
|
healthIndicator.activated();
|
|
return JSON.stringify({
|
|
page: ShellState.settingsPage,
|
|
settingsOpen: ShellState.settingsOpen,
|
|
refreshRequested: Health.generation > generation || Health.queuedRefresh
|
|
});
|
|
}
|
|
}
|
|
}
|
|
QML
|
|
|
|
run() {
|
|
PANAMA_HEALTH_FIXTURE="$fixture" PANAMA_HEALTH_HELPER="$helper" \
|
|
HOME="$fixture_home" XDG_STATE_HOME="$state_home" \
|
|
qs -p "$harness" "$@"
|
|
}
|
|
|
|
harness_pid=""
|
|
cleanup() {
|
|
if [[ -n "$harness_pid" ]]; then
|
|
kill "$harness_pid" >/dev/null 2>&1 || true
|
|
for _ in $(seq 1 40); do
|
|
kill -0 "$harness_pid" >/dev/null 2>&1 || break
|
|
sleep 0.1
|
|
done
|
|
else
|
|
run kill >/dev/null 2>&1 || true
|
|
fi
|
|
rm -rf "$state_home"
|
|
}
|
|
trap cleanup EXIT
|
|
|
|
PANAMA_HEALTH_FIXTURE="$fixture" PANAMA_HEALTH_HELPER="$helper" \
|
|
HOME="$fixture_home" XDG_STATE_HOME="$state_home" \
|
|
qs -p "$harness" --daemonize >"$shell_log" 2>&1
|
|
for _ in $(seq 1 40); do
|
|
harness_pid="$(qs list --all 2>/dev/null | awk -v path="$harness" '
|
|
/Process ID:/ { pid = $3 }
|
|
index($0, "Config path: " path) { print pid; exit }
|
|
')"
|
|
[[ "$harness_pid" =~ ^[0-9]+$ ]] && break
|
|
sleep 0.1
|
|
done
|
|
for _ in $(seq 1 60); do
|
|
run ipc show 2>/dev/null | rg -q '^target health-ui-test$' && break
|
|
sleep 0.1
|
|
done
|
|
run ipc show 2>/dev/null | rg -q '^target health-ui-test$' \
|
|
|| { sed -n '1,200p' "$shell_log" >&2; fail 'isolated fixture did not start'; }
|
|
|
|
checking_state="$(run ipc call health-ui-test state)"
|
|
jq -e '
|
|
.renderedRows == [
|
|
{objectName:"health-check-row:issue:desktop.vicinae", id:"desktop.vicinae", section:"issue", statusText:"Needs attention"},
|
|
{objectName:"health-check-row:issue:desktop.quickshell", id:"desktop.quickshell", section:"issue", statusText:"Action required"},
|
|
{objectName:"health-check-row:issue:integration.calendar", id:"integration.calendar", section:"issue", statusText:"Needs attention"},
|
|
{objectName:"health-check-row:quiet:input.pipewire", id:"input.pipewire", section:"quiet", statusText:"Healthy"},
|
|
{objectName:"health-check-row:quiet:integration.bluebubbles", id:"integration.bluebubbles", section:"quiet", statusText:"Not set up"},
|
|
{objectName:"health-check-row:quiet:panama.caffeine", id:"panama.caffeine", section:"quiet", statusText:"Healthy"}
|
|
]
|
|
and (.renderedRows | map(.id) | length) == 6
|
|
and (.renderedRows | map(.id) | unique | length) == 6
|
|
and .emptyQuietGroups == ["desktop-foundation"]
|
|
and .fedoraHandoffs == [
|
|
{id:"color", label:"Color profiles", action:"Open color"},
|
|
{id:"wellbeing", label:"Digital wellbeing", action:"Open wellbeing"}
|
|
]
|
|
and .summaryHeight == 126
|
|
and (.rowHeights | length) == 6
|
|
and (.rowHeights | all(. >= 62))
|
|
and .checking == true
|
|
and .checkingText == "Checking…"
|
|
' >/dev/null <<<"$checking_state" || fail "checking fixture did not render the approved state: $checking_state"
|
|
|
|
checking_heights="$(jq -c .rowHeights <<<"$checking_state")"
|
|
for _ in $(seq 1 40); do
|
|
settled_state="$(run ipc call health-ui-test state)"
|
|
[[ "$(jq -r .checking <<<"$settled_state")" == "false" ]] && break
|
|
sleep 0.1
|
|
done
|
|
sleep 0.1
|
|
settled_state="$(run ipc call health-ui-test state)"
|
|
[[ "$(jq -c .rowHeights <<<"$settled_state")" == "$checking_heights" ]] \
|
|
|| fail 'row geometry changed after refresh settled'
|
|
jq -e '
|
|
(.focusChain | index("health-copy-report-button")) != null
|
|
and (.focusChain | index("health-refresh-button")) != null
|
|
and (.focusChain | any(startswith("health-row-action:")))
|
|
' >/dev/null <<<"$settled_state" || fail "actual focus-chain traversal does not reach hero and row actions: $settled_state"
|
|
|
|
settled_heights="$(jq -c .rowHeights <<<"$settled_state")"
|
|
[[ "$(run ipc call health-ui-test request desktop.vicinae)" == "true" ]] \
|
|
|| fail 'inline repair fixture could not be requested'
|
|
working_repair_state="$(run ipc call health-ui-test state)"
|
|
jq -e '.renderedRows[] | select(.id == "desktop.vicinae") | .statusText == "Working…"' \
|
|
>/dev/null <<<"$working_repair_state" || fail "repair row did not show Working state: $working_repair_state"
|
|
[[ "$(jq -c .rowHeights <<<"$working_repair_state")" == "$settled_heights" ]] \
|
|
|| fail 'repair Working state changed row geometry'
|
|
for _ in $(seq 1 40); do
|
|
failed_repair_state="$(run ipc call health-ui-test state)"
|
|
jq -e '.renderedRows[] | select(.id == "desktop.vicinae") | .statusText == "Repair failed"' \
|
|
>/dev/null <<<"$failed_repair_state" && break
|
|
sleep 0.1
|
|
done
|
|
jq -e '.renderedRows[] | select(.id == "desktop.vicinae") | .statusText == "Repair failed"' \
|
|
>/dev/null <<<"$failed_repair_state" || fail "repair failure was not shown inline: $failed_repair_state"
|
|
[[ "$(jq -c .rowHeights <<<"$failed_repair_state")" == "$settled_heights" ]] \
|
|
|| fail 'repair failure changed row geometry'
|
|
[[ "$(jq -r '.renderedRows | map(.id) | unique | length' <<<"$failed_repair_state")" == 6 ]] \
|
|
|| fail 'repair state duplicated a health action row'
|
|
for _ in $(seq 1 40); do
|
|
failed_repair_state="$(run ipc call health-ui-test state)"
|
|
[[ "$(jq -r .checking <<<"$failed_repair_state")" == "false" ]] && break
|
|
sleep 0.1
|
|
done
|
|
[[ "$(jq -r .checking <<<"$failed_repair_state")" == "false" ]] \
|
|
|| fail 'post-repair scan did not settle before the next action'
|
|
|
|
[[ "$(run ipc call health-ui-test request desktop.quickshell)" == "true" ]] \
|
|
|| fail 'restart confirmation fixture could not be requested'
|
|
confirmation_state="$(run ipc call health-ui-test state)"
|
|
jq -e '
|
|
.confirmationVisible == true
|
|
and .confirmationId == "desktop.quickshell"
|
|
and (.activatedRows | index("health-check-row:issue:desktop.quickshell")) != null
|
|
and (.activatedRows | map(select(endswith(":desktop.quickshell"))) | length) == 1
|
|
' \
|
|
>/dev/null <<<"$confirmation_state" || fail 'Quickshell restart did not open confirmation sheet'
|
|
|
|
for hidden_mode in healthy unconfigured; do
|
|
[[ "$(run ipc call health-ui-test indicatorMode "$hidden_mode")" == "true" ]] \
|
|
|| fail "$hidden_mode indicator fixture was rejected"
|
|
hidden_state="$(run ipc call health-ui-test indicatorState)"
|
|
jq -e '
|
|
.visible == false
|
|
and .width == 0
|
|
and .implicitWidth == 0
|
|
and .issueCount == 0
|
|
' >/dev/null <<<"$hidden_state" \
|
|
|| fail "$hidden_mode state reserved bar space: $hidden_state"
|
|
done
|
|
|
|
[[ "$(run ipc call health-ui-test indicatorMode warning)" == "true" ]] \
|
|
|| fail 'warning indicator fixture was rejected'
|
|
warning_state="$(run ipc call health-ui-test indicatorState)"
|
|
jq -e '
|
|
.visible == true
|
|
and .width > 0
|
|
and .implicitWidth > 0
|
|
and .issueCount == 1
|
|
and .statusText == "1 system health issue"
|
|
and .accessibleLabel == "System Health: 1 issue needs attention"
|
|
and .tooltipText == "System Health: 1 issue needs attention"
|
|
and .warningTone == true
|
|
and .errorTone == false
|
|
and .activeFocusOnTab == true
|
|
' >/dev/null <<<"$warning_state" || fail "warning indicator is not the approved amber accessible capsule: $warning_state"
|
|
|
|
[[ "$(run ipc call health-ui-test indicatorMode error)" == "true" ]] \
|
|
|| fail 'error indicator fixture was rejected'
|
|
error_state="$(run ipc call health-ui-test indicatorState)"
|
|
jq -e '
|
|
.visible == true
|
|
and .issueCount == 1
|
|
and .accessibleLabel == "System Health: 1 issue requires action"
|
|
and .tooltipText == "System Health: 1 issue requires action"
|
|
and .warningTone == false
|
|
and .errorTone == true
|
|
' >/dev/null <<<"$error_state" || fail "error indicator is not the approved red accessible capsule: $error_state"
|
|
|
|
activation_state="$(run ipc call health-ui-test activateIndicator)"
|
|
jq -e '
|
|
.page == "services"
|
|
and .settingsOpen == true
|
|
and .refreshRequested == true
|
|
' >/dev/null <<<"$activation_state" || fail "indicator activation did not open and refresh System Health: $activation_state"
|
|
|
|
if rg -i 'QQml|ReferenceError|TypeError|binding loop|failed to load component' "$shell_log"; then
|
|
fail 'isolated fixture emitted QML errors or warnings'
|
|
fi
|
|
|
|
trap - EXIT
|
|
cleanup
|
|
printf 'health UI contract: PASS\n'
|