-
Notifications
You must be signed in to change notification settings - Fork 112
TextFrame: Terminal UI testing primitive with pytest/syrupy integration
#613
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: master
Are you sure you want to change the base?
Conversation
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: 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: 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