Files
ssh-netbox-wrapper/testenv/tests/16-render.sh
T
Sebastian UnterschützandClaude Opus 5.5 396d489187 feat(multi): tmux keys to toggle broadcast and exclude single panes
In windows opened by netssh multi, prefix S toggles broadcast for all
panes and prefix e excludes the current pane (a pane-level
synchronize-panes off, shown as [SOLO] in the border) or includes it
again. netssh sync --pane does the same from a shell.

S and e are unbound in a default tmux (E would be "spread evenly").
netssh writes the bindings to a small tmux config and sources it, binds
a key only if the user's tmux.conf does not use it (netssh's own earlier
binding is refreshed), and the bindings act only in windows marked
@netssh. multi.sync_key and multi.solo_key change the keys or turn them
off (none); also in the configurator. zellij syncs whole tabs only, so
excluding a pane is tmux-only.

testenv: the render test presses the keys through the xterm client
(send-keys -K) and checks [SOLO], that broadcast skips the excluded
pane, that S toggles, and that other windows are unaffected. The client
gets GNU grep: BusyBox grep -oF counted only one match per line.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-11-14 13:32:32 +01:00

103 lines
5.9 KiB
Bash

# What a user sees: tmux, zellij, and the TUI rendered by a real terminal
# emulator (xterm on Xvfb), checked by OCR of screenshots. The screenshots stay
# in testenv/.artifacts/ for a look by eye.
config <<'EOF'
resolver:
strategies: [management_subnet, primary_ip]
management_subnets: [10.231.1.0/24]
ssh: {default_user: ops}
groups:
pve: ["cluster:pve-fsn1"]
EOF
check "refresh" netssh cache refresh
export TMUX_TMPDIR="$HOME/tmux"
mkdir -p "$TMUX_TMPDIR" "$HOME/.config/zellij"
printf 'show_startup_tips false\nshow_release_notes false\n' >"$HOME/.config/zellij/config.kdl"
start_screen && ok "X server and xterm" || bad "X server and xterm" "Xvfb did not start"
p1=fsn/fsn1-pve1.hoster.test p2=fsn/fsn1-pve2.hoster.test p3=fsn/fsn1-pve3.hoster.test
prompts() { screen_shows "$1" "fsn1-pve1:~\$" "fsn1-pve2:~\$" "fsn1-pve3:~\$"; }
# Two hosts on one OCR line means their panes sit side by side (tiled grid).
side_by_side() { screen_text "$1" | ocr_norm | grep -F "$(printf %s "$2" | ocr_norm)" | grep -qF "$(printf %s "$3" | ocr_norm)"; }
visible() { # NAME SHOT TEXT... — wait until the screen shows every TEXT
local name=$1 shot=$2
shift 2
if wait_for 30 screen_shows "$shot" "$@"; then ok "$name"; else bad "$name" "$(screen_text "$shot")"; fi
}
# --- tmux, started from a plain terminal: new session, attached in xterm ---
term netssh multi --sync @pve
visible "tmux: three remote shells on screen" tmux-1 "fsn1-pve1:~\$" "fsn1-pve2:~\$" "fsn1-pve3:~\$"
visible "tmux: borders show host and [SYNC]" tmux-1 "$p1 [SYNC]" "$p2 [SYNC]" "$p3 [SYNC]"
side_by_side tmux-1 "$p1" "$p2" && ok "tmux: tiled, first two panes side by side" || bad "tmux: tiled, first two panes side by side" "$(screen_text tmux-1)"
tmux send-keys -t netssh-pve "echo typed-once-\$(hostname)" Enter
visible "tmux: broadcast output in every pane" tmux-2 "typed-once-fsn1-pve1" "typed-once-fsn1-pve2" "typed-once-fsn1-pve3"
# Keys as typed in xterm (send-keys -K goes through the client's prefix and
# key tables): prefix e excludes the active pane, prefix S toggles all panes.
client=$(tmux list-clients -t netssh-pve -F '#{client_name}' | head -1)
press() { tmux send-keys -K -c "$client" C-b "$1"; sleep 0.5; }
panes=($(tmux list-panes -t netssh-pve -F '#{pane_id}'))
tmux select-pane -t "${panes[2]}"
press e
visible "tmux: prefix e marks the pane [SOLO]" tmux-solo "$p3 [SOLO]" "$p1 [SYNC]" "$p2 [SYNC]"
tmux send-keys -t "${panes[2]}" "echo solo-only" Enter
tmux send-keys -t "${panes[0]}" "echo two-panes" Enter
sleep 1
shot tmux-solo-2
[ "$(screen_count tmux-solo-2 "solo-only")" = 2 ] && ok "tmux: the excluded pane types alone" || bad "tmux: the excluded pane types alone" "$(screen_text tmux-solo-2)"
[ "$(screen_count tmux-solo-2 "two-panes")" = 4 ] && ok "tmux: broadcast skips the excluded pane" || bad "tmux: broadcast skips the excluded pane" "$(screen_text tmux-solo-2)"
press e
visible "tmux: prefix e again includes it" tmux-solo-3 "$p3 [SYNC]"
press S
sync_is() { [ "$(tmux show-window-options -t netssh-pve -v synchronize-panes 2>/dev/null)" = "$1" ]; }
wait_for 5 sync_is off && ok "tmux: prefix S turns broadcast off" || bad "tmux: prefix S turns broadcast off" "$(tmux show-window-options -t netssh-pve)"
tmux send-keys -t netssh-pve "echo just-this-pane" Enter
sleep 1
shot tmux-3
[ "$(screen_count tmux-3 "just-this-pane")" = 2 ] && ok "tmux: without broadcast only one pane gets input" || bad "tmux: without broadcast only one pane gets input" "$(screen_text tmux-3)"
shows_sync() { screen_shows tmux-3 "[SYNC]"; }
shows_sync && bad "tmux: [SYNC] disappears when broadcast is off" "$(screen_text tmux-3)" || ok "tmux: [SYNC] disappears when broadcast is off"
press S
wait_for 5 sync_is on && ok "tmux: prefix S turns it on again" || bad "tmux: prefix S turns it on again" "$(tmux show-window-options -t netssh-pve)"
tmux kill-server
# --- more hosts than panes_per_window: further windows ---
sed -i 's/^groups:/multi: {panes_per_window: 2}\ngroups:/' "$XDG_CONFIG_HOME/netssh.yaml"
term netssh multi @pve
visible "tmux: hosts split over windows pve-1 and pve-2" tmux-windows "pve-1" "pve-2"
visible "tmux: status bar shows the whole session name" tmux-windows "[netssh-pve]"
tmux kill-server
sed -i '/^multi: {panes_per_window: 2}$/d' "$XDG_CONFIG_HOME/netssh.yaml"
# --- zellij: generated grid layout, broadcast through the sync tab ---
term netssh multi --zellij @pve
visible "zellij: three remote shells on screen" zellij-1 "fsn1-pve1:~\$" "fsn1-pve2:~\$" "fsn1-pve3:~\$"
visible "zellij: pane titles show the hosts" zellij-1 "$p1" "$p2" "$p3"
side_by_side zellij-1 "$p1" "$p2" && ok "zellij: grid, first two panes side by side" || bad "zellij: grid, first two panes side by side" "$(screen_text zellij-1)"
session=$(zellij list-sessions --short --no-formatting 2>/dev/null | head -1)
zellij --session "$session" action toggle-active-sync-tab
zellij --session "$session" action write-chars 'echo zellij-$(hostname)'
zellij --session "$session" action write 13
visible "zellij: broadcast output in every pane" zellij-2 "zellij-fsn1-pve1" "zellij-fsn1-pve2" "zellij-fsn1-pve3"
zellij kill-all-sessions --yes >/dev/null 2>&1
# --- TUI: mark hosts, open them together ---
term tmux new-session -s tui netssh
visible "TUI: search screen" tui-1 "Search hostname"
# The netssh keys do nothing outside netssh multi windows.
tmux source-file "$XDG_CACHE_HOME/netssh/netssh-multi.tmux.conf"
client=$(tmux list-clients -t tui -F '#{client_name}' | head -1)
tmux send-keys -K -c "$client" C-b S
sleep 0.5
[ "$(tmux show-window-options -t tui -v synchronize-panes 2>/dev/null)" != on ] && ok "tmux: prefix S leaves other windows alone" || bad "tmux: prefix S leaves other windows alone" "synchronize-panes turned on in a non-netssh window"
tmux send-keys -t tui "pve"
visible "TUI: results listed" tui-2 "$p1" "$p2" "$p3"
tmux send-keys -t tui C-a
visible "TUI: all marked" tui-3 "3 marked"
tmux send-keys -t tui Enter
visible "TUI: marked hosts open side by side" tui-4 "fsn1-pve1:~\$" "fsn1-pve2:~\$" "fsn1-pve3:~\$"
tmux kill-server
stop_screen