TextFrame: Terminal UI testing primitive with pytest/syrupy integration#613
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 45.39% 45.97% +0.57%
==========================================
Files 22 25 +3
Lines 2249 2397 +148
Branches 360 387 +27
==========================================
+ Hits 1021 1102 +81
- Misses 1082 1142 +60
- Partials 146 153 +7 ☔ View full report in Codecov by Sentry. 🚀 New features to boost your workflow:
|
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
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: 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+
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+
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+
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: 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)
Code reviewNo issues found. Checked for bugs and CLAUDE.md compliance. 🤖 Generated with Claude Code |
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: 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
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 👎. |
why: Enable snapshot testing for ASCII frame visualization what: - Add syrupy to dev dependencies
why: Validate Syrupy snapshot testing for terminal frame visualization what: - Add TextFrame dataclass with content overflow detection - Add ContentOverflowError with Reality vs Mask visual - Add TextFrameSerializer extending AmberDataSerializer - Add TextFrameExtension for Syrupy integration - Add parametrized tests for rendering and nested serialization
why: Store expected ASCII frame output for regression testing what: - Add snapshots for basic, empty, and overflow frame rendering - Add snapshot for nested TextFrame serialization
why: Prevent invalid TextFrame instances from being created with zero/negative dimensions or multi-character fill strings. what: - Add __post_init__ to validate content_width > 0 - Add __post_init__ to validate content_height > 0 - Add __post_init__ to validate fill_char is single character
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: 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: 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: Show actual frame output to help users understand the feature. what: - Add basic usage example with rendered ASCII frame output - Add multiline output example demonstrating printf capture - Add truncation example showing long lines clipped to frame width - Reorganize into sections: Basic, Multiline, Truncation, Snapshot testing
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: Document the new capture_frame() parameters for users. what: - Add flag forwarding bullet point to capture_frame() feature list
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: Include display() in 0.53.x feature list what: - Add interactive curses viewer to TextFrame features
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
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