TextFrame: Terminal UI testing primitive with pytest/syrupy integration#613
Open
TextFrame: Terminal UI testing primitive with pytest/syrupy integration#613
TextFrame: Terminal UI testing primitive with pytest/syrupy integration#613Conversation
6b00c4f to
96d1823
Compare
Codecov Report❌ Patch coverage is Additional details and impacted files@@ Coverage Diff @@
## master #613 +/- ##
==========================================
+ Coverage 46.58% 47.06% +0.47%
==========================================
Files 22 25 +3
Lines 2372 2520 +148
Branches 390 417 +27
==========================================
+ Hits 1105 1186 +81
- Misses 1098 1158 +60
- Partials 169 176 +7 ☔ View full report in Codecov by Sentry. 🚀 New features to boost your workflow:
|
tony
added a commit
that referenced
this pull request
Dec 7, 2025
why: Document new features for release notes. what: - TextFrame primitive for terminal UI testing - pytest assertion hook with rich diff output - syrupy snapshot extension with .frame files - Optional install via libtmux[textframe]
TextFrame)TextFrame: Terminal UI testing primitive with pytest/syrupy integration
tony
added a commit
that referenced
this pull request
Dec 7, 2025
why: Document the new capture_frame() method for users. what: - Add Pane.capture_frame() section under New features - Include usage example with textframe_snapshot - Document key features (default dimensions, truncate mode)
tony
added a commit
that referenced
this pull request
Dec 7, 2025
why: Document the new capture_pane() parameters for the changelog. what: - Add section for Pane.capture_pane() enhanced - Document all 5 new parameters with flag mappings - Add code examples for colored output and joined lines - Note trim_trailing requires tmux 3.4+
tony
added a commit
that referenced
this pull request
Dec 7, 2025
why: Document the new capture_pane() parameters for the changelog. what: - Add section for Pane.capture_pane() enhanced - Document all 5 new parameters with flag mappings - Add code examples for colored output and joined lines - Note trim_trailing requires tmux 3.4+
tony
added a commit
that referenced
this pull request
Dec 7, 2025
why: Document the new capture_pane() parameters for the changelog. what: - Add section for Pane.capture_pane() enhanced - Document all 5 new parameters with flag mappings - Add code examples for colored output and joined lines - Note trim_trailing requires tmux 3.4+
tony
added a commit
that referenced
this pull request
Dec 7, 2025
why: Document new features for release notes. what: - TextFrame primitive for terminal UI testing - pytest assertion hook with rich diff output - syrupy snapshot extension with .frame files - Optional install via libtmux[textframe]
tony
added a commit
that referenced
this pull request
Dec 7, 2025
why: Document the new capture_frame() method for users. what: - Add Pane.capture_frame() section under New features - Include usage example with textframe_snapshot - Document key features (default dimensions, truncate mode)
Member
Author
Code reviewNo issues found. Checked for bugs and CLAUDE.md compliance. 🤖 Generated with Claude Code |
tony
added a commit
that referenced
this pull request
Dec 7, 2025
why: Document new features for release notes. what: - TextFrame primitive for terminal UI testing - pytest assertion hook with rich diff output - syrupy snapshot extension with .frame files - Optional install via libtmux[textframe]
tony
added a commit
that referenced
this pull request
Dec 7, 2025
why: Document the new capture_frame() method for users. what: - Add Pane.capture_frame() section under New features - Include usage example with textframe_snapshot - Document key features (default dimensions, truncate mode)
94a6f8f to
d335fc9
Compare
Member
Author
Code reviewFound 1 issue:
libtmux/src/libtmux/textframe/__init__.py Lines 5 to 8 in d335fc9 The import chain is:
Suggested fix: Make the 🤖 Generated with Claude Code - If this code review was useful, please react with 👍. Otherwise, react with 👎. |
tony
added a commit
that referenced
this pull request
Dec 14, 2025
why: Document new features for release notes. what: - TextFrame primitive for terminal UI testing - pytest assertion hook with rich diff output - syrupy snapshot extension with .frame files - Optional install via libtmux[textframe]
tony
added a commit
that referenced
this pull request
Dec 14, 2025
why: Document the new capture_frame() method for users. what: - Add Pane.capture_frame() section under New features - Include usage example with textframe_snapshot - Document key features (default dimensions, truncate mode)
why: Enable snapshot testing for ASCII frame visualization what: - Add syrupy to dev dependencies
why: Capture expected output for truncate behavior test cases. what: - Add truncate_width snapshot (5x2 frame, clipped "hello") - Add truncate_height snapshot (10x1 frame, single row) - Add truncate_both snapshot (5x2 frame, both dimensions clipped)
why: Provide rich assertion output for TextFrame comparisons without requiring syrupy for basic equality checks. what: - Add pytest_assertrepr_compare hook for TextFrame == TextFrame - Show dimension mismatches (width, height) - Show content diff using difflib.ndiff
why: Individual .frame files provide cleaner git diffs and easier review than a single .ambr file with all snapshots. what: - Replace AmberSnapshotExtension with SingleFileSnapshotExtension - Set file extension to .frame - Simplify serialize() method (removed nested serializer class)
why: Replaced by individual .frame files from SingleFileSnapshotExtension. what: - Delete test_core.ambr
why: New snapshot format from SingleFileSnapshotExtension. what: - Add test_frame_rendering[basic_success].frame - Add test_frame_rendering[overflow_width].frame - Add test_frame_rendering[empty_frame].frame - Add test_frame_rendering[truncate_width].frame - Add test_frame_rendering[truncate_height].frame - Add test_frame_rendering[truncate_both].frame - Add test_nested_serialization.frame
why: Provide reference for TextFrame usage and architectural decisions. what: - Document syrupy integration (SingleFileSnapshotExtension) - Document pytest_assertrepr_compare hook pattern - Document overflow_behavior modes - Include examples and architectural insights from syrupy/pytest/CPython - Add to internals toctree
why: Enable distribution of TextFrame for downstream users. what: - Move tests/textframe/core.py → src/libtmux/textframe/core.py - Create src/libtmux/textframe/__init__.py with public API exports - Update test imports to use libtmux.textframe
why: Auto-register TextFrame assertion hooks and snapshot fixture for downstream users who install libtmux[textframe]. what: - Move tests/textframe/plugin.py → src/libtmux/textframe/plugin.py - Add pytest_assertrepr_compare hook for rich diff output - Add textframe_snapshot fixture for downstream users - Export TextFrameExtension from __init__.py - Simplify tests/textframe/conftest.py (hooks now in plugin)
why: Allow opt-in installation of textframe pytest plugin. what: - Add [project.optional-dependencies] textframe = ["syrupy>=4.0.0"] - Add [project.entry-points.pytest11] libtmux-textframe entry point - Downstream users can now: pip install libtmux[textframe]
why: Document opt-in mechanism for downstream users. what: - Update import paths from tests/ to src/libtmux/textframe/ - Add installation section: pip install libtmux[textframe] - Document auto-discovered fixtures and hooks - Add Plugin Discovery section explaining pytest11 entry points - Update file paths table
why: Lockfile reflects new optional dependency. what: - Add syrupy to textframe extras in uv.lock
why: Document new features for release notes. what: - TextFrame primitive for terminal UI testing - pytest assertion hook with rich diff output - syrupy snapshot extension with .frame files - Optional install via libtmux[textframe]
why: Enable capturing pane content as TextFrame for visualization and snapshot testing. This bridges capture_pane() with the TextFrame dataclass for a more ergonomic testing workflow. what: - Add capture_frame() method that wraps capture_pane() - Default to pane dimensions when width/height not specified - Default to truncate mode for robustness in CI environments - Add comprehensive docstring with examples
why: Verify capture_frame() works with real tmux panes and integrates properly with syrupy snapshot testing. what: - Add 12 comprehensive tests using NamedTuple parametrization - Test basic usage, custom dimensions, overflow behavior - Demonstrate retry_until integration pattern
why: Baseline snapshot for capture_frame() visual regression testing. what: - Add test_capture_frame_snapshot.frame baseline
why: Show users how to use capture_frame() for testing terminal output. what: - Add Pane.capture_frame() integration section - Document parameters with table - Explain design decisions (truncate default, refresh) - Add retry_until usage example
why: Comprehensive visual regression testing for all capture_frame() variations. what: - Add SnapshotCase NamedTuple for parametrized snapshot testing - Add 18 snapshot test cases covering: - Dimension variations: prompt_only, wide/narrow/tall/short frames - start/end parameters: start=0, end=0, end="-", start_end_range - Truncation: width and height truncation - Special characters and edge cases - Use retry_until for robust async output handling
why: Baseline snapshots for exhaustive visual regression testing. what: - Add 18 .frame snapshot files for parametrized test cases - Covers dimensions, start/end params, truncation, special chars
why: Enable doctest verification of capture_frame() output. what: - Create new pane with shell='sh' for predictable prompt - Remove # doctest: +SKIP since output is now deterministic - Follow established pattern from capture_pane() doctest
why: Allow users to control capture behavior when using capture_frame() for snapshot testing, such as capturing colored output or joining wrapped lines. what: - Add escape_sequences parameter for ANSI escape sequences - Add escape_non_printable parameter for octal escapes - Add join_wrapped parameter for joining wrapped lines - Add preserve_trailing parameter for trailing spaces - Add trim_trailing parameter with tmux 3.4+ version check - Forward all flags to capture_pane() call
why: Verify that capture_frame() correctly forwards all capture_pane() flags for proper behavior in snapshot testing scenarios. what: - Add CaptureFrameFlagCase NamedTuple for parametrized tests - Add 4 test cases covering key flag behaviors - Test escape_sequences, join_wrapped, preserve_trailing flags - Verify flag absence behavior (no_escape_sequences)
why: Enable interactive exploration of large frame content in terminal what: - Add display() method with TTY detection - Add _curses_display() with scrolling support - Navigation: arrows, WASD, vim keys (hjkl) - Page navigation: PgUp/PgDn, Home/End - Exit: q, Esc, Ctrl-C
why: Enable users to discover interactive viewer feature what: - Add Interactive Display section with usage example - Document all keyboard controls in table format - Note TTY requirement and RuntimeError behavior
why: curses KEY_RESIZE only fires on getch(), missing resize events
when terminal is resized but no key is pressed
what:
- Replace stdscr.getmaxyx() with shutil.get_terminal_size()
- Remove KEY_RESIZE handling (now redundant)
This follows Rich's approach: query terminal size directly via
ioctl(TIOCGWINSZ) on each loop iteration, which works reliably
in tmux and other terminal multiplexers.
why: Verify display() uses shutil.get_terminal_size() for resize what: - Add test_terminal_resize_via_shutil test - Mock shutil.get_terminal_size to verify it's called
why: Users without libtmux[textframe] get ImportError on capture_frame() what: - Wrap TextFrameExtension import in try/except ImportError - Only add to __all__ when syrupy is available - Core TextFrame functionality works without optional dependency
why: Follow established exception pattern for libtmux exceptions what: - Add LibTmuxException as base class alongside ValueError - Matches pattern of AdjustmentDirectionRequiresAdjustment, etc. - Enables catching all libtmux exceptions with LibTmuxException
why: Follow CLAUDE.md guideline for stdlib namespace imports what: - Change from difflib import ndiff to import difflib - Use difflib.ndiff() instead of ndiff()
why: Follow pytest best practices from CLAUDE.md guidelines what: - Use import unittest.mock namespace style - Replace patch() context managers with monkeypatch.setattr() - Document MagicMock necessity for curses window simulation
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Adds
libtmux.textframe, a fixed-size ASCII frame simulator for testing terminal UI output. Useful for validatingcapture_pane()output and terminal rendering in tests.Install:
pip install libtmux[textframe]Features
TextFrame primitive
A dataclass for creating fixed-dimension ASCII frames with overflow detection:
overflow_behavior:"error"(raises with visual diagnostic) or"truncate"(clips silently)__post_init__Pane.capture_frame()
High-level method that wraps
capture_pane()and returns aTextFrame:overflow_behavior="truncate"by default for CI robustnesspytest assertion hook
Rich diff output when comparing
TextFrameobjects:syrupy snapshot extension
TextFrameExtensionstores snapshots as.framefiles - one file per test for cleaner git diffs:Plugin discovery
Registered via
pytest11entry point - fixtures and hooks are auto-discovered whenlibtmux[textframe]is installed.Files changed
src/libtmux/pane.pycapture_frame()methodsrc/libtmux/textframe/__init__.pysrc/libtmux/textframe/core.pyTextFramedataclass,ContentOverflowErrorsrc/libtmux/textframe/plugin.pytextframe_snapshotfixturepyproject.tomldocs/internals/textframe.mdtests/textframe/tests/test_pane_capture_frame.pySee also