11# TextFrame - ASCII Frame Simulator
22
3- :::{warning}
4- This is a testing utility in ` tests/textframe/ ` . It is ** not** part of the public API.
5- :::
6-
73TextFrame provides a fixed-size ASCII frame simulator for visualizing terminal content with overflow detection and diagnostic rendering. It integrates with [ syrupy] ( https://github.com/tophat/syrupy ) for snapshot testing and pytest for rich assertion output.
84
5+ ## Installation
6+
7+ TextFrame is available as an optional extra:
8+
9+ ``` bash
10+ pip install libtmux[textframe]
11+ ```
12+
13+ This installs syrupy and registers the pytest plugin automatically.
14+
15+ ## Quick Start
16+
17+ After installation, the ` textframe_snapshot ` fixture and ` pytest_assertrepr_compare ` hook are auto-discovered by pytest:
18+
19+ ``` python
20+ from libtmux.textframe import TextFrame
21+
22+ def test_my_terminal_ui (textframe_snapshot ):
23+ frame = TextFrame(content_width = 20 , content_height = 5 )
24+ frame.set_content([" Hello" , " World" ])
25+ assert frame == textframe_snapshot
26+ ```
27+
928## Overview
1029
1130TextFrame is designed for testing terminal UI components. It provides:
@@ -20,7 +39,7 @@ TextFrame is designed for testing terminal UI components. It provides:
2039### TextFrame Dataclass
2140
2241``` python
23- from tests .textframe.core import TextFrame, ContentOverflowError
42+ from libtmux .textframe import TextFrame, ContentOverflowError
2443
2544# Create a frame with fixed dimensions
2645frame = TextFrame(content_width = 10 , content_height = 2 )
@@ -79,7 +98,7 @@ TextFrame uses syrupy's `SingleFileSnapshotExtension` to store each snapshot in
7998### Extension Implementation
8099
81100``` python
82- # tests /textframe/plugin.py
101+ # src/libtmux /textframe/plugin.py
83102from syrupy.extensions.single_file import SingleFileSnapshotExtension, WriteMode
84103
85104class TextFrameExtension (SingleFileSnapshotExtension ):
@@ -100,36 +119,34 @@ Key design decisions:
1001192 . ** ` _write_mode = WriteMode.TEXT ` ** : Stores snapshots as text (not binary)
1011203 . ** Custom serialization** : Renders TextFrame objects and ContentOverflowError exceptions as ASCII art
102121
103- ### Fixture Override Pattern
122+ ### Auto-Discovered Fixtures
104123
105- The snapshot fixture is overridden in ` conftest.py ` using syrupy's ` use_extension() ` pattern :
124+ When ` libtmux[textframe] ` is installed, the following fixture is available :
106125
107126``` python
108- # tests/textframe/conftest.py
109127@pytest.fixture
110- def snapshot (snapshot : SnapshotAssertion) -> SnapshotAssertion:
128+ def textframe_snapshot (snapshot : SnapshotAssertion) -> SnapshotAssertion:
129+ """ Snapshot fixture configured with TextFrameExtension."""
111130 return snapshot.use_extension(TextFrameExtension)
112131```
113132
114- This pattern works because pytest fixtures that request themselves receive the parent scope's version.
115-
116133### Snapshot Directory Structure
117134
118135```
119- tests/textframe/ __snapshots__/
120- test_core /
136+ __snapshots__/
137+ test_module /
121138 test_frame_rendering[basic_success].frame
122139 test_frame_rendering[overflow_width].frame
123140 test_frame_rendering[empty_frame].frame
124141 ...
125142```
126143
127- ## Pure pytest Assertion Hook
144+ ## pytest Assertion Hook
128145
129- For TextFrame-to-TextFrame comparisons (without syrupy), a ` pytest_assertrepr_compare ` hook provides rich diff output:
146+ The ` pytest_assertrepr_compare ` hook provides rich diff output for TextFrame comparisons :
130147
131148``` python
132- # tests/textframe/conftest.py
149+ # Auto-registered via pytest11 entry point
133150def pytest_assertrepr_compare (config , op , left , right ):
134151 if not isinstance (left, TextFrame) or not isinstance (right, TextFrame):
135152 return None
@@ -159,6 +176,25 @@ This hook intercepts `assert frame1 == frame2` comparisons and shows:
159176- Dimension mismatches (width/height)
160177- Line-by-line diff using ` difflib.ndiff `
161178
179+ ## Plugin Discovery
180+
181+ The textframe plugin is registered via pytest's entry point mechanism:
182+
183+ ``` toml
184+ # pyproject.toml
185+ [project .entry-points .pytest11 ]
186+ libtmux-textframe = " libtmux.textframe.plugin"
187+
188+ [project .optional-dependencies ]
189+ textframe = [" syrupy>=4.0.0" ]
190+ ```
191+
192+ When installed with ` pip install libtmux[textframe] ` :
193+ 1 . syrupy is installed as a dependency
194+ 2 . The pytest11 entry point is registered
195+ 3 . pytest auto-discovers the plugin on startup
196+ 4 . ` textframe_snapshot ` fixture and assertion hooks are available
197+
162198## Architecture Patterns
163199
164200### From syrupy
@@ -170,8 +206,8 @@ This hook intercepts `assert frame1 == frame2` comparisons and shows:
170206### From pytest
171207
172208- ** ` pytest_assertrepr_compare ` hook** : Return ` list[str] ` for custom assertion output
173- - ** Fixture override pattern ** : Request same-named fixture to get parent scope's version
174- - ** ` ndiff ` for diffs ** : Character-level diff with ` + ` / ` - ` prefixes
209+ - ** pytest11 entry points ** : Auto-discovery of installed plugins
210+ - ** Fixture auto-discovery ** : Fixtures defined in plugins are globally available
175211
176212### From CPython dataclasses
177213
@@ -183,8 +219,16 @@ This hook intercepts `assert frame1 == frame2` comparisons and shows:
183219
184220| File | Purpose |
185221| ------| ---------|
186- | ` tests/textframe/core.py ` | ` TextFrame ` dataclass and ` ContentOverflowError ` |
187- | ` tests/textframe/plugin.py ` | Syrupy ` TextFrameExtension ` |
188- | ` tests/textframe/conftest.py ` | Fixture override and ` pytest_assertrepr_compare ` hook |
189- | ` tests/textframe/test_core.py ` | Parametrized tests with snapshot assertions |
190- | ` tests/textframe/__snapshots__/test_core/*.frame ` | Snapshot baselines |
222+ | ` src/libtmux/textframe/core.py ` | ` TextFrame ` dataclass and ` ContentOverflowError ` |
223+ | ` src/libtmux/textframe/plugin.py ` | Syrupy extension, pytest hooks, and fixtures |
224+ | ` src/libtmux/textframe/__init__.py ` | Public API exports |
225+
226+ ## Public API
227+
228+ ``` python
229+ from libtmux.textframe import (
230+ TextFrame, # Core dataclass
231+ ContentOverflowError, # Exception with visual diagnostic
232+ TextFrameExtension, # Syrupy extension for custom usage
233+ )
234+ ```
0 commit comments