From f46ab50ff94c4d48f2ec83b762cd2ef0e3bf445e Mon Sep 17 00:00:00 2001 From: Jeroen Schweitzer Date: Mon, 11 May 2026 18:04:48 +0200 Subject: [PATCH] test sweep: cover terminal_view IME / focus tail paths (T-91) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Three tests for the lines the existing TerminalView suite didn't quite touch: the deleteDetection backspace flow (CustomTextEdit.onDelete → scrollToBottom + Terminal.keyInput), the hardwareKeyboardOnly tap that requests focus directly (_onTapUp's else-branch when there's no CustomTextEdit), and the single-char IME insert path where the character maps to a TerminalKey (_onInsert's key != null branch). Coverage: terminal_view.dart 180/188 -> 187/188 (99.5%). The 1 remaining line is _scrollToBottom's jumpTo call — unreachable in current wiring since the tree has no Scrollable; _scrollableKey .currentState is always null and the guard short-circuits. Note: infinite_scroll_view.dart stays at 90% — the 4 uncovered lines are the position-setter's value-changed branch, only reachable when the inner Scrollable swaps its ViewportOffset. The widget doesn't expose ScrollController or physics, so there's no public surface to drive that path from a test. Co-Authored-By: Claude Opus 4.7 (1M context) --- test/terminal/terminal_view_test.dart | 56 +++++++++++++++++++++++++++ 1 file changed, 56 insertions(+) diff --git a/test/terminal/terminal_view_test.dart b/test/terminal/terminal_view_test.dart index d2950359..94e524dd 100644 --- a/test/terminal/terminal_view_test.dart +++ b/test/terminal/terminal_view_test.dart @@ -418,6 +418,62 @@ void main() { }); }); + group('TerminalView — keyboard / IME tail paths', () { + // These cover the last few lines in terminal_view.dart that the existing + // tests didn't quite touch: the onDelete callback wired into + // CustomTextEdit, the hardwareKeyboardOnly tap branch that requests + // focus directly, and the _onInsert path where the inserted character + // maps to a TerminalKey (key != null). + + testWidgets('backspace via deleteDetection runs onDelete (scroll + keyInput)', (tester) async { + final r = _OutputRecorder(); + final t = r.build(); + await tester.pumpWidget(_host(TerminalView(t, autofocus: true, deleteDetection: true))); + await tester.pump(); + // deleteDetection seeds the editing value with two spaces; shrinking + // it below that length triggers CustomTextEdit.onDelete, which fires + // _scrollToBottom + Terminal.keyInput(backspace) in TerminalView. + tester.testTextInput.updateEditingValue(const TextEditingValue( + text: '', + selection: TextSelection.collapsed(offset: 0), + )); + await tester.pump(); + // Backspace produces a non-empty escape sequence (^?). + expect(r.outputs, isNotEmpty); + }); + + testWidgets('tap on hardwareKeyboardOnly view requests focus on the focus node', (tester) async { + final t = _OutputRecorder().build(); + final focus = FocusNode(); + addTearDown(focus.dispose); + await tester.pumpWidget(_host(TerminalView( + t, + hardwareKeyboardOnly: true, + focusNode: focus, + ))); + // Tap with no selection-clear short-circuit — the else-branch of + // _onTapUp routes through _focusNode.requestFocus when + // hardwareKeyboardOnly is set (no CustomTextEdit to delegate to). + await tester.tapAt(tester.getCenter(find.byType(TerminalView))); + await tester.pump(const Duration(seconds: 1)); + expect(focus.hasFocus, isTrue); + }); + + testWidgets('single-char IME insert routes through keyInput when the char maps to a TerminalKey', (tester) async { + final r = _OutputRecorder(); + final t = r.build(); + await tester.pumpWidget(_host(TerminalView(t, autofocus: true))); + await tester.pump(); + await tester.tap(find.byType(TerminalView)); + await tester.pump(const Duration(seconds: 1)); + // 'a' is a single char that maps to TerminalKey.a — _onInsert hits + // the key != null branch and calls terminal.keyInput first. + tester.testTextInput.enterText('a'); + await tester.pump(); + expect(r.outputs, isNotEmpty); + }); + }); + group('TerminalView — keyboard visibility', () { testWidgets('platform keyboard show fires _onKeyboardShow on the focused view', (tester) async { final t = _OutputRecorder().build();