Skip to content

tui: improve testability and simplify code#2551

Merged
dgageot merged 3 commits intodocker:mainfrom
dgageot:board/tui-test-improvements-and-code-refactori-87c7b4fe
Apr 28, 2026
Merged

tui: improve testability and simplify code#2551
dgageot merged 3 commits intodocker:mainfrom
dgageot:board/tui-test-improvements-and-code-refactori-87c7b4fe

Conversation

@dgageot
Copy link
Copy Markdown
Member

@dgageot dgageot commented Apr 27, 2026

Summary

This PR improves the TUI's testability and simplifies its code without removing any feature.

Testability

  • New pkg/tui/internal/editorname package: hosts FromEnv(visual, editorEnv) string (previously duplicated verbatim in pkg/tui/tui.go and pkg/tui/page/chat/chat.go — the chat copy was dead code with the only test). Added a regression test for non-ASCII editor names.
  • New Transcriber interface in pkg/tui with a WithTranscriber option, decoupling appModel from the concrete *transcribe.Transcriber. Tests can now drive the speech handlers via a fake without audio hardware or network.
  • New tests for previously-untested helpers:
    • parseCtrlNumberKey
    • mouse hit-testing (hitTestLeanRegion, hitTestFullRegion — extracted as pure free functions)
    • terminal window title (formatWindowTitle — extracted as a pure helper)
    • speech-to-text handlers (handleStartSpeak, handleStopSpeak)

Simplification

  • New pkg/tui/dispatch.go: centralises the Update → type-assert → reassign boilerplate behind updateChatCmd/updateEditorCmd/updateDialogCmd/updateCompletionsCmd and the matching forwardX shortcuts. ~30 repetitions of the 5-line pattern in tui.go/handlers.go collapse to one-liners.
  • New pkg/tui/tabs.go: tab persistence and lookup logic (persistActiveTab, persistFreshTab, restoreTabs, persistedSessionID, findTabByPersistedID) lifted out of tui.go.
  • copyToClipboard(text, successMsg) helper deduplicates the two clipboard handlers.
  • isErrTitleGenerating (string-comparison) replaced with errors.Is(err, app.ErrTitleGenerating).
  • persistSplitDiffView(enabled bool) replaces an anonymous goroutine in handleToggleSplitDiff.
  • applyThemeChanged shrinks from 12 lines of boilerplate to a 3-line tea.Batch.

Bug fixes (found during self-review)

  • UTF-8 corruption in editorname.FromEnv (carried over from the original duplicated copy): the fallback capitalisation strings.ToUpper(baseName[:1]) + baseName[1:] slices by byte, not rune, mangling editor names that begin with multi-byte UTF-8 characters (e.g. édit → garbled output). Now uses utf8.DecodeRuneInString + unicode.ToUpper. Regression test added.
  • Data race in tui_exit_test.go (pre-existing, exposed by go test -race): neutralizeExitFunc mutated the package-level exitFunc while the safety-net goroutine spawned by cleanupAll still read from it, and two parallel tests both mutated the global. Fix: shrink shutdownTimeout in tests, wait for the safety-net goroutine before restoring originals, and remove t.Parallel() from tests that mutate package globals. go test -race ./pkg/tui/... now passes for the whole subtree.

Stats

  • pkg/tui/tui.go: 2544 → 2301 lines (-243).
  • pkg/tui/handlers.go: 674 → 660 lines.
  • New: pkg/tui/dispatch.go (82 lines), pkg/tui/tabs.go (171 lines), pkg/tui/internal/editorname/ (76 + 130 lines test).
  • New tests: pkg/tui/tui_helpers_test.go (243 lines), pkg/tui/tui_speak_test.go (~130 lines).

Validation

  • go build ./...
  • go vet ./pkg/tui/...
  • go test ./pkg/tui/...
  • go test -race ./pkg/tui/...
  • golangci-lint run ./pkg/tui/... ✅ (0 issues)

Assisted-By: docker-agent

dgageot added 3 commits April 27, 2026 17:53
…yout/title helpers, inject transcriber via interface
… own file

- centralise the chatPage/editor/dialog/completions Update boilerplate

  in dispatch.go (forwardX returns (m, cmd); updateXCmd returns just cmd

  for batching)

- move tab restore/persist/lookup logic from tui.go to tabs.go

- extract copyToClipboard helper to deduplicate clipboard handlers

- replace isErrTitleGenerating string comparison with errors.Is

- inline persistSplitDiffView helper for the userconfig save
- editorname.FromEnv: capitalise the first rune (not byte) so editor

  binary names beginning with multi-byte UTF-8 characters survive

  the round-trip; add a regression test.

- tui_exit_test.go: fix data race between the safety-net goroutine

  spawned by cleanupAll and subsequent tests by shrinking

  shutdownTimeout in neutralizeExitFunc, waiting for the goroutine to

  observe the no-op exitFunc, and removing t.Parallel() from tests

  that mutate the package-level exitFunc/shutdownTimeout. go test -race

  now passes for the whole pkg/tui tree.

- tui_speak_test.go: drop the misleading containsErrorNotification

  helper and the unused fakeTranscriber.lastHandler/mu; assert the

  produced message is a notification.ShowMsg with TypeError instead.

- tui_helpers_test.go: drop hand-rolled contains() helper; use

  strings.Contains.
@dgageot dgageot requested a review from a team as a code owner April 27, 2026 16:32
@dgageot dgageot merged commit 88a2a37 into docker:main Apr 28, 2026
9 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants