summaryrefslogtreecommitdiff
path: root/Scripts
diff options
context:
space:
mode:
Diffstat (limited to 'Scripts')
-rwxr-xr-xScripts/audit-a11y.sh129
1 files changed, 129 insertions, 0 deletions
diff --git a/Scripts/audit-a11y.sh b/Scripts/audit-a11y.sh
new file mode 100755
index 0000000..9ac3aa0
--- /dev/null
+++ b/Scripts/audit-a11y.sh
@@ -0,0 +1,129 @@
+#!/usr/bin/env bash
+#
+# Run the accessibility audit suite against real simulator runtimes.
+#
+# ./Scripts/audit-a11y.sh # floor + current
+# ./Scripts/audit-a11y.sh floor # oldest supported runtime only
+# ./Scripts/audit-a11y.sh current # newest installed runtime only
+#
+# Why this exists rather than living entirely in CI: audit coverage is NOT
+# nested across OS versions — each runtime reports findings the others miss, in
+# both directions. Measured on Tracked Domains, iOS 18.6 reported 2 findings and
+# iOS 27.0 reported 6; at accessibility text sizes the Dashboard produced a
+# hit-region finding on 18.6 that 27.0 did not. The GitHub macos-26 image ships
+# only iOS 26.x runtimes, so it structurally cannot cover the floor. This machine
+# can.
+#
+# CI covers "current" on a clean checkout (which a local run cannot, since it
+# would miss an uncommitted file). This script covers "floor" (which CI cannot).
+# Together they cover both; neither duplicates the other.
+#
+# The deployment target is read from the project rather than hard-coded, so it
+# cannot drift out of sync.
+
+set -euo pipefail
+
+cd "$(dirname "$0")/.."
+
+PROJECT="DomainDig.xcodeproj"
+SCHEME="DomainDig"
+TIER="${1:-both}"
+
+case "$TIER" in
+ floor | current | both) ;;
+ *)
+ echo "usage: $0 [floor|current|both]" >&2
+ exit 2
+ ;;
+esac
+
+echo "==> Reading deployment target from $PROJECT"
+DEPLOYMENT_TARGET=$(
+ xcodebuild -project "$PROJECT" -scheme "$SCHEME" -showBuildSettings 2>/dev/null \
+ | awk -F' = ' '/ IPHONEOS_DEPLOYMENT_TARGET = /{print $2; exit}'
+)
+
+if [ -z "${DEPLOYMENT_TARGET:-}" ]; then
+ echo "error: could not read IPHONEOS_DEPLOYMENT_TARGET" >&2
+ exit 1
+fi
+
+DT_MAJOR="${DEPLOYMENT_TARGET%%.*}"
+DT_MINOR="${DEPLOYMENT_TARGET##*.}"
+[ "$DT_MINOR" = "$DEPLOYMENT_TARGET" ] && DT_MINOR=0
+FLOOR_RANK=$(( DT_MAJOR * 1000 + DT_MINOR ))
+
+echo " deployment target: $DEPLOYMENT_TARGET (rank $FLOOR_RANK)"
+
+# Pick an iPhone simulator at or above the deployment target. A runtime BELOW it
+# is useless — the app cannot install there — which is why this filters rather
+# than just taking the oldest installed runtime.
+select_sim() {
+ local which="$1"
+ xcrun simctl list devices available --json \
+ | jq -c --argjson floor "$FLOOR_RANK" --arg which "$which" '
+ [ .devices | to_entries[]
+ | (.key | capture("SimRuntime\\.iOS-(?<maj>[0-9]+)-(?<min>[0-9]+)$")) as $v
+ | (($v.maj | tonumber) * 1000 + ($v.min | tonumber)) as $rank
+ | select($rank >= $floor)
+ | .value[]
+ | select(.name | startswith("iPhone"))
+ | { rank: $rank, udid: .udid, name: .name, os: "\($v.maj).\($v.min)" }
+ ]
+ | sort_by(.rank, .name)
+ | if length == 0 then empty
+ elif $which == "floor" then first
+ else last end
+ '
+}
+
+run_tier() {
+ local which="$1"
+ local sim udid label
+
+ sim=$(select_sim "$which")
+ if [ -z "$sim" ]; then
+ echo "error: no iPhone simulator at or above iOS $DEPLOYMENT_TARGET installed" >&2
+ echo "hint: install one with 'xcodebuild -downloadPlatform iOS'" >&2
+ return 1
+ fi
+
+ udid=$(echo "$sim" | jq -r .udid)
+ label=$(echo "$sim" | jq -r '"\(.name) (iOS \(.os))"')
+
+ echo
+ echo "==> $which: $label"
+
+ if [ "$which" = "floor" ] && [ "$(echo "$sim" | jq -r .rank)" -ge $(( (DT_MAJOR + 1) * 1000 )) ]; then
+ echo " NOTE: nearest installed runtime is a major version above the $DEPLOYMENT_TARGET"
+ echo " deployment target, so this is not true floor coverage."
+ fi
+
+ # Findings are printed by the suite itself; surface them plus the verdict.
+ set -o pipefail
+ xcodebuild test \
+ -project "$PROJECT" \
+ -scheme "$SCHEME" \
+ -destination "id=$udid" \
+ CODE_SIGNING_ALLOWED=NO 2>&1 \
+ | grep -E 'finding\(s\)|no accessibility findings|^ • |did not complete in time|Executed [0-9]+ test|\*\* TEST (SUCCEEDED|FAILED)' \
+ || true
+
+ return 0
+}
+
+status=0
+if [ "$TIER" = "both" ]; then
+ run_tier floor || status=1
+ run_tier current || status=1
+else
+ run_tier "$TIER" || status=1
+fi
+
+echo
+if [ "$status" -eq 0 ]; then
+ echo "==> Done. Findings above are the burndown list for issue #21."
+ echo " They are reported, not enforced — widen"
+ echo " AccessibilityAuditHarness.enforcedAuditTypes as each phase lands."
+fi
+exit "$status"