-
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.68% 46.28% +0.59%
==========================================
Files 22 25 +3
Lines 2250 2398 +148
Branches 360 387 +27
==========================================
+ Hits 1028 1110 +82
- Misses 1079 1138 +59
- Partials 143 150 +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: 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
…rflow why: Allow flexible handling of oversized content - either error with visual diagnostic or silently truncate to fit. what: - Add OverflowBehavior type alias for "error" | "truncate" - Add overflow_behavior parameter with default "error" (backward compatible) - Implement truncate logic to clip width and height - Update docstrings to reflect new behavior
why: Verify the new overflow_behavior="truncate" mode works correctly for width, height, and combined overflow scenarios. what: - Add overflow_behavior field to Case NamedTuple with default "error" - Add truncate_width test case (clips horizontal overflow) - Add truncate_height test case (clips vertical overflow) - Add truncate_both test case (clips both dimensions) - Update test to pass overflow_behavior to TextFrame constructor
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: 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
c043d03 to
94a6f8f
Compare
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
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: 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