Files
fish-config/tests/test-config-settings-tui.fish
T
rootiest 078d01666e feat(config-settings): replace the ANSI renderer with a curses front-end
config-settings is now a launcher for scripts/config-settings-tui.py,
drawn with Python's stdlib curses. The seven fish files that hand-rolled
the ANSI renderer are gone, along with the golden harness that had to pin
their byte-exact output.

The TUI is a child process, so it can neither read the session's global
variables nor write them. State goes in as a dump from the new
__config_settings_state; the edits come back as a fish script that
config-settings sources, which is what lets the Session page's `set -g`
land in the caller's shell instead of in a child that is about to exit.
Every edit is emitted as a call to __config_settings_apply or
__config_settings_set_value, so list splitting, the SCROLLBACK_HISTORY_*
export mirror and the shadow-warning suppression all stay in the fish
layer that already owned them.

The consequence, and the one behaviour change: edits are applied in one
batch on exit rather than on each keypress. The status bar shows a
pending count.

New: `/` filters the current page, and on the Universal and Session pages
it reaches into every category's sub-categories, listing hits as
"Category › Sub" so a sub-category can be toggled without drilling into
its parent first. Also a `?` help overlay, mouse selection, and a
drill-down page that leads with the category's own toggle.

Gone with the renderer: the four width tiers, the wrap-aware erase
arithmetic, the stty/dd/od raw key reader, the panel-height bookkeeping
and the hand-written redraw differ. curses owns all of it, and the alt
screen plus absolute addressing makes the desync class behind 608b022,
4210f3b, 93fc5e0 and 3c4f720 unreachable.

The sub-category taxonomy is NOT duplicated in Python: it travels in the
state dump, still sourced from __config_settings_subcats. The category,
Sponge and Paths row tables move into Python, consolidating the two
copies the fish renderers kept.

Dependency: python3 with curses. Stdlib on Arch, Fedora and a full
Debian/Ubuntu python3; python3-minimal alone lacks _curses. The launcher
checks for both and names what is missing. Called out in the README.

Verified: 416/416 assertions, plus a live end-to-end in a sandbox HOME
confirming the Universal page writes universal scope only (U1/G0) and the
Session page global scope only (U0/G1).
2026-09-09 14:50:26 -04:00

57 lines
2.8 KiB
Fish
Executable File

#!/usr/bin/env fish
# Copyright (C) 2026 Rootiest
# SPDX-License-Identifier: AGPL-3.0-or-later
#
# Gate for the curses config-settings front-end (scripts/config-settings-tui.py).
#
# Runs isolated (no `# MODE:` marker). Three things are checked here:
#
# 1. python3 can import `curses`. On Arch, Fedora and a full Debian/Ubuntu
# python3 this is stdlib and always true, but python3-minimal alone does
# not carry _curses -- the one portability claim config-settings makes,
# and the reason the function checks the import rather than just `type -q`.
# 2. The TUI's own --self-test passes. That covers the state-dump parser,
# variable-name derivation, toggle cycling, the sub-category-aware filter,
# fish quoting and command emission, with no TTY.
# 3. config-settings still degrades cleanly with no terminal, rather than
# leaving a half-initialised curses screen behind.
#
# The drawing code is deliberately NOT golden-tested, and the golden harness
# the ANSI renderer needed is gone. That is the point of the rewrite: curses
# owns the cell arithmetic, so there is no hand-tuned width/pad/dash maths left
# to pin down. The seam that does need pinning -- dump in, fish script out --
# is covered here and, for session scope, in tests/test-session.fish.
source (dirname (status filename))/lib.fish
# Isolated suites run under `fish --no-config`, which autoloads nothing from
# this repo. The launcher cases below call config-settings for real, so put the
# repo's functions on the autoload path -- and only that, so nothing in conf.d
# runs and the guard variables stay unset.
set -p fish_function_path $repo_root/functions
set -l tui $repo_root/scripts/config-settings-tui.py
section "config-settings-tui: prerequisites"
check "scripts/config-settings-tui.py is executable" true (test -x $tui; and echo true; or echo false)
check "python3 is available" true (type -q python3; and echo true; or echo false)
check "python3 ships the curses module" 0 (python3 -c 'import curses' >/dev/null 2>&1; echo $status)
section "config-settings-tui: self-test"
check "--self-test passes" 0 (python3 $tui --self-test >/dev/null 2>&1; echo $status)
section "config-settings: launcher"
check "--help exits 0" 0 (config-settings --help >/dev/null 2>&1; echo $status)
check "an unknown flag exits 1" 1 (config-settings --nope >/dev/null 2>&1; echo $status)
# stdout is a pipe here, so the isatty guard fires before curses ever starts.
# Without it the TUI would fail deep inside setupterm and leave the terminal
# in whatever state it got to.
function test_no_tty_is_refused_cleanly
set -l out (config-settings 2>&1 >/dev/null | string collect)
string match -q '*needs a terminal*' -- $out
end
check "no TTY is refused with a message, not a curses crash" true (test_no_tty_is_refused_cleanly; and echo true; or echo false)
report