Skip to content

Commit e6d6df8

Browse files
committed
docs(textframe): Update for distributable plugin
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
1 parent e38ec01 commit e6d6df8

File tree

1 file changed

+68
-24
lines changed

1 file changed

+68
-24
lines changed

docs/internals/textframe.md

Lines changed: 68 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,30 @@
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-
73
TextFrame 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

1130
TextFrame 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
2645
frame = 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
83102
from syrupy.extensions.single_file import SingleFileSnapshotExtension, WriteMode
84103

85104
class TextFrameExtension(SingleFileSnapshotExtension):
@@ -100,36 +119,34 @@ Key design decisions:
100119
2. **`_write_mode = WriteMode.TEXT`**: Stores snapshots as text (not binary)
101120
3. **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
133150
def 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

Comments
 (0)