#!/bin/bash set -euo pipefail source "$(cd -- "$(dirname -- "${BASH_SOURCE[0]}")" && pwd)/base-test.sh" tmp_dir=$(mktemp -d) trap 'rm -r "$tmp_dir"' EXIT # gum reads its scripted answers from GUM_SCRIPT, one "status:output" line per # invocation, so a test spells out exactly what the human did at each screen: # "0:dhh" answers, "1:" is Esc, "130:" is Ctrl+C. Only the widgets that take a # list drain stdin, matching the real ones, so the piped layouts and timezones # can be asserted. cat >"$tmp_dir/gum" <<'EOF' #!/bin/bash count=$(($(cat "$GUM_COUNT") + 1)) printf '%s' "$count" >"$GUM_COUNT" printf '%s\n' "$*" >>"$GUM_ARGS" case $1 in choose | filter) cat >"$GUM_DIR/stdin.$count" ;; esac line=$(sed -n "${count}p" "$GUM_SCRIPT") printf '%s\n' "${line#*:}" exit "${line%%:*}" EOF cat >"$tmp_dir/tzupdate" <<'EOF' #!/bin/bash [[ -n ${TZ_GUESS:-} ]] || exit 1 printf '%s\n' "$TZ_GUESS" EOF cat >"$tmp_dir/timedatectl" <<'EOF' #!/bin/bash printf '%s\n' UTC Europe/Copenhagen America/Chicago EOF # Calls one prompt bare under `set -euo pipefail` — the shape that makes the # status capture load-bearing. A cancelled prompt is a failing assignment, so a # regression to a plain `status=$?` kills the shell before the function can # return; both cases exit with the same status, and this marker is the only # thing that tells them apart. cat >"$tmp_dir/driver" <<'EOF' #!/bin/bash set -euo pipefail set -T source "$ROOT/install/provisioning/setup-form.sh" notice() { printf '%s\n' "$1" >>"$NOTICES"; } region=${REGION:-global} if [[ -n ${TAKEN_USERS:-} ]]; then omarchy_username_taken() { [[ " $TAKEN_USERS " == *" $1 "* ]]; } fi trap 'if [[ ${FUNCNAME[0]:-} == "$PROMPT_FN" ]]; then printf "returned\n" >>"$MARKER"; fi' RETURN "$PROMPT_FN" printf 'region=%s\n' "${region:-}" printf 'keyboard=%s\n' "${keyboard:-}" printf 'keyboard_label=%s\n' "${keyboard_label:-}" printf 'username=%s\n' "${username:-}" printf 'password=%s\n' "${password:-}" printf 'password_confirmation=%s\n' "${password_confirmation:-}" printf 'full_name=%s\n' "${full_name:-}" printf 'email_address=%s\n' "${email_address:-}" printf 'hostname=%s\n' "${hostname:-}" printf 'timezone=%s\n' "${timezone:-}" EOF chmod +x "$tmp_dir/gum" "$tmp_dir/tzupdate" "$tmp_dir/timedatectl" "$tmp_dir/driver" export PATH="$tmp_dir:$PATH" export GUM_DIR="$tmp_dir" GUM_SCRIPT="$tmp_dir/script" GUM_ARGS="$tmp_dir/args" GUM_COUNT="$tmp_dir/count" export NOTICES="$tmp_dir/notices" MARKER="$tmp_dir/marker" status=0 # run_prompt ... — each response answers one gum screen run_prompt() { local prompt=$1 shift printf '%s\n' "$@" >"$GUM_SCRIPT" printf '0' >"$GUM_COUNT" : >"$GUM_ARGS" : >"$NOTICES" : >"$MARKER" rm -f "$tmp_dir"/stdin.* PROMPT_FN="$prompt" "$tmp_dir/driver" >"$tmp_dir/out" 2>"$tmp_dir/err" && status=0 || status=$? } field() { sed -n "s/^$1=//p" "$tmp_dir/out"; } assert_returned() { grep -qx returned "$MARKER" || fail "$1" "$(printf 'the prompt never returned; the shell died inside it:\n%s' "$(<"$tmp_dir/err")")" } assert_status() { ((status == $1)) || fail "$2" "expected status: $1 actual status: $status" } assert_notices() { local description=$1 expected=$2 local actual actual=$(<"$NOTICES") [[ $actual == "$expected" ]] || fail "$description" "expected notices: $expected actual notices: $actual" } # The contract both callers read, and the ordering the shared list exists to keep source "$ROOT/install/provisioning/setup-form.sh" ((OMARCHY_FORM_BACK == 1)) || fail "Esc reports status 1" ((OMARCHY_FORM_SIGNAL == 130)) || fail "Ctrl+C reports status 130" [[ $(printf '%s\n' "$OMARCHY_KEYBOARD_LAYOUTS" | head -n 1) == "English (US)|us" ]] || fail "English (US) leads the keyboard layouts so gum choose opens on the default" pass "the form publishes the 0/1/130 status contract and leads with English (US)" # Region run_prompt omarchy_prompt_region "0:China" assert_status 0 "region prompt succeeds" [[ $(field region) == "cn" ]] || fail "region prompt stores China as cn" [[ $(<"$tmp_dir/stdin.1") == $'Global\nChina' ]] || fail "region prompt offers Global then China" grep -qxF -- 'choose --selected Global --header Select region' "$GUM_ARGS" || fail "region prompt preselects Global on a fresh form" REGION=cn run_prompt omarchy_prompt_region "0:Global" [[ $(field region) == "global" ]] || fail "region prompt stores Global as global" grep -qxF -- 'choose --selected China --header Select region' "$GUM_ARGS" || fail "region prompt preselects the region chosen on an earlier visit" pass "region prompt maps Global and China and preselects the current region" run_prompt omarchy_prompt_region "1:" assert_status "$OMARCHY_FORM_BACK" "region prompt reports Esc as back" assert_returned "region prompt survives Esc under set -e" run_prompt omarchy_prompt_region "130:" assert_status "$OMARCHY_FORM_SIGNAL" "region prompt reports Ctrl+C as the caller's signal" assert_returned "region prompt survives Ctrl+C under set -e" pass "region prompt propagates Esc and Ctrl+C without dying under set -e" OMARCHY_CJK_CONSOLE=1 run_prompt omarchy_prompt_region "0:China 中国" assert_status 0 "region prompt succeeds in a CJK console" [[ $(field region) == "cn" ]] || fail "region prompt stores China 中国 as cn" [[ $(<"$tmp_dir/stdin.1") == $'Global\nChina 中国' ]] || fail "a CJK console offers the Chinese name" grep -qxF -- 'choose --selected Global --header Select region · 选择区域' "$GUM_ARGS" || fail "a CJK console asks in both languages" OMARCHY_CJK_CONSOLE=1 REGION=cn run_prompt omarchy_prompt_region "0:Global" [[ $(field region) == "global" ]] || fail "region prompt stores Global as global in a CJK console" grep -qxF -- 'choose --selected China 中国 --header Select region · 选择区域' "$GUM_ARGS" || fail "a CJK console preselects China by its shown name" pass "a CJK console names China in Chinese and maps it back to cn" # Keyboard run_prompt omarchy_prompt_keyboard "0:German" assert_status 0 "keyboard prompt succeeds" [[ $(field keyboard) == "de" ]] || fail "keyboard prompt resolves the label to a keymap" [[ $(field keyboard_label) == "German" ]] || fail "keyboard prompt keeps the label for the summary" [[ $(head -n 1 "$tmp_dir/stdin.1") == "English (US)" ]] || fail "keyboard prompt offers English (US) first" grep -qF -- '--selected English (US)' "$GUM_ARGS" || fail "keyboard prompt preselects English (US)" pass "keyboard prompt maps the chosen label to its keymap" run_prompt omarchy_prompt_keyboard "1:" assert_status "$OMARCHY_FORM_BACK" "keyboard prompt reports Esc as back" assert_returned "keyboard prompt survives Esc under set -e" run_prompt omarchy_prompt_keyboard "130:" assert_status "$OMARCHY_FORM_SIGNAL" "keyboard prompt reports Ctrl+C as the caller's signal" assert_returned "keyboard prompt survives Ctrl+C under set -e" pass "keyboard prompt propagates Esc and Ctrl+C without dying under set -e" # Username TAKEN_USERS=dhh run_prompt omarchy_prompt_username "0:Not A Username" "0:root" "0:cups-browsed" "0:dhh" "0:david" assert_status 0 "username prompt accepts a valid name" [[ $(field username) == "david" ]] || fail "username prompt keeps re-asking until the name is valid" assert_notices "username prompt explains each rejection" "Username must be alphanumeric with no spaces Username is reserved for system Username is reserved for system That username already exists on this machine" pass "username prompt rejects malformed, reserved, and taken names" run_prompt omarchy_prompt_username "1:" assert_status "$OMARCHY_FORM_BACK" "username prompt reports Esc as back" assert_returned "username prompt survives Esc under set -e" run_prompt omarchy_prompt_username "130:" assert_status "$OMARCHY_FORM_SIGNAL" "username prompt reports Ctrl+C as the caller's signal" assert_returned "username prompt survives Ctrl+C under set -e" pass "username prompt propagates Esc and Ctrl+C without dying under set -e" # Password run_prompt omarchy_prompt_password "0:one" "0:two" "0:" "0:" "0:s3cret" "0:s3cret" assert_status 0 "password prompt accepts a confirmed password" [[ $(field password) == "s3cret" ]] || fail "password prompt keeps the confirmed password" assert_notices "password prompt explains each rejection" "Passwords didn't match! Your password can't be blank!" pass "password prompt rejects mismatched and blank passwords" run_prompt omarchy_prompt_password "0:s3cret" "1:" assert_status "$OMARCHY_FORM_BACK" "password prompt reports Esc on the confirmation as back" assert_returned "password confirmation survives Esc under set -e" run_prompt omarchy_prompt_password "0:s3cret" "130:" assert_status "$OMARCHY_FORM_SIGNAL" "password prompt reports Ctrl+C on the confirmation as the caller's signal" assert_returned "password confirmation survives Ctrl+C under set -e" pass "password confirmation propagates Esc and Ctrl+C without dying under set -e" # Identity — both fields are skippable, so empty is an answer and not a cancel run_prompt omarchy_prompt_identity "0:" "0:" assert_status 0 "identity prompt treats empty fields as answers" [[ -z $(field full_name) && -z $(field email_address) ]] || fail "identity prompt leaves skipped fields empty" pass "identity prompt accepts skipped fields" run_prompt omarchy_prompt_identity "0:David" "1:" assert_status "$OMARCHY_FORM_BACK" "identity prompt reports Esc on the email as back" assert_returned "identity prompt survives Esc under set -e" pass "identity prompt propagates Esc from its second field" # Hostname run_prompt omarchy_prompt_hostname "0:-nope-" "0:workshop" assert_status 0 "hostname prompt accepts a valid hostname" [[ $(field hostname) == "workshop" ]] || fail "hostname prompt keeps re-asking until the hostname is valid" assert_notices "hostname prompt explains the rejection" "Hostname must be 1-63 letters, digits, or dashes, and cannot start or end with a dash" run_prompt omarchy_prompt_hostname "0:" assert_status 0 "hostname prompt accepts an empty hostname" [[ $(field hostname) == "$OMARCHY_HOSTNAME_DEFAULT" ]] || fail "hostname prompt falls back to the default hostname" pass "hostname prompt rejects malformed names and defaults an empty one" run_prompt omarchy_prompt_hostname "1:" assert_status "$OMARCHY_FORM_BACK" "hostname prompt reports Esc as back" assert_returned "hostname prompt survives Esc under set -e" pass "hostname prompt propagates Esc without dying under set -e" # Chinese text OMARCHY_INSTALL_LANG=zh run_prompt omarchy_prompt_username "0:Bad Name" "0:dhh" assert_status 0 "username prompt succeeds in Chinese" grep -qF -- '--prompt 用户名> ' "$GUM_ARGS" || fail "OMARCHY_INSTALL_LANG=zh asks for the username in Chinese" assert_notices "OMARCHY_INSTALL_LANG=zh explains a bad username in Chinese" "用户名只能用字母和数字,不含空格" run_prompt omarchy_prompt_username "0:dhh" grep -qF -- '--prompt Username> ' "$GUM_ARGS" || fail "the form stays English without OMARCHY_INSTALL_LANG" mapfile -t texts < <(grep -o 'omarchy_text "[^"]*"' "$ROOT/install/provisioning/setup-form.sh" | sed 's/^omarchy_text "//; s/"$//') ((${#texts[@]} > 15)) || fail "the form routes its text through omarchy_text" for text in "${texts[@]}"; do [[ -n ${OMARCHY_ZH[$text]:-} ]] || fail "every form text has a Chinese entry" "missing: $text" done pass "OMARCHY_INSTALL_LANG=zh puts every form text in Chinese" # Timezone TZ_GUESS=Europe/Copenhagen run_prompt omarchy_prompt_timezone "0:Europe/Copenhagen" assert_status 0 "timezone prompt accepts the geo guess" [[ $(field timezone) == "Europe/Copenhagen" ]] || fail "timezone prompt keeps the chosen timezone" grep -qF -- '--selected Europe/Copenhagen' "$GUM_ARGS" || fail "timezone prompt preselects the geo guess" grep -qF UTC "$tmp_dir/stdin.1" || fail "timezone prompt offers the system timezone list" pass "timezone prompt preselects the geo guess when one is available" # An unnetworked first boot has no guess, and the fallback has to survive `set -e` run_prompt omarchy_prompt_timezone "0:America/Chicago" assert_status 0 "timezone prompt survives a failed geo guess" [[ $(field timezone) == "America/Chicago" ]] || fail "timezone prompt keeps the filtered timezone" [[ $(head -n 1 "$GUM_ARGS") == filter* ]] || fail "timezone prompt filters when there is no geo guess" pass "timezone prompt falls back to filtering when the geo guess fails" run_prompt omarchy_prompt_timezone "0:" assert_status 0 "timezone prompt accepts an empty selection" [[ $(field timezone) == "UTC" ]] || fail "timezone prompt falls back to UTC" pass "timezone prompt falls back to UTC when nothing is selected" run_prompt omarchy_prompt_timezone "1:" assert_status "$OMARCHY_FORM_BACK" "timezone prompt reports Esc as back" assert_returned "timezone prompt survives Esc under set -e" TZ_GUESS=Europe/Copenhagen run_prompt omarchy_prompt_timezone "130:" assert_status "$OMARCHY_FORM_SIGNAL" "timezone prompt reports Ctrl+C as the caller's signal" assert_returned "timezone prompt survives Ctrl+C under set -e" pass "timezone prompt propagates Esc and Ctrl+C without dying under set -e"