Merge codex's System Health and recovery work
Brings in panama-doctor (a 25-check diagnostic with fixture-backed
contracts), a Health service, a System Health page replacing Startup &
Services, and a bar indicator that stays absent until something is
actually degraded. All seven of its contracts pass on the merge.
Three things needed resolving rather than accepting:
The branch predates the debranding, so its user-visible strings still
named the product -- "Panama desktop is healthy", "Restart Panama",
"Panama tools". Rewritten to say the same thing without the name, which
is what the rest of the app now does.
Its Fedora hand-off card was a single button calling openGnomePanel
("network") under a subtitle naming five subjects. Main had already
replaced that with a row per subject, each opening the panel that owns
it, so those rows are ported into HealthPage instead. Printers and
online accounts stay on Network & Devices with the rest of the network
hardware.
That broke its own assertion, which matched the literal
openGnomePanel("network") string. Rewritten rather than reverted: it now
checks the boundary card exists and that every panel named in HealthPage
is one openGnomePanel actually allows, since a name outside the
allow-list opens nothing at all. Verified it catches a plausible-looking
wrong name.
SettingsShell and SettingsSidebar conflicted because both sides added
pages; resolved as the union, keeping its System Health page and live
footer alongside main's Mouse & Touchpad, Privacy & Security, Region &
Language and Online Accounts.
Claude-Session: https://claude.ai/code/session_01BRvzt4H8XXLPVH5MyYdk9L
This commit is contained in:
Executable
+415
@@ -0,0 +1,415 @@
|
||||
#!/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'
|
||||
# The Fedora hand-off is a row per subject, not one button that opened the
|
||||
# network panel whatever it was labelled. What matters is that each row reaches
|
||||
# the panel that owns it, so this checks the boundary still exists and that
|
||||
# every panel it names is one openGnomePanel accepts -- a name outside that
|
||||
# allow-list opens nothing and reports an error, i.e. a dead button.
|
||||
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 .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'
|
||||
Reference in New Issue
Block a user