Skip to content

Commit 9eee37a

Browse files
igerberclaude
andcommitted
Add positive-render coverage for display_data with text/plain fallback (R8 P3)
The extractor's shared `execute_result | display_data` branch in `_format_output` renders `data["text/plain"]` for both output types, but the prior tests only positively covered `execute_result` (in `test_extractor_handles_list_and_string_sources`) and the negative omission cases for `display_data` (html-only, image-only). A regression that accidentally split the shared branch and dropped `display_data`'s plain-text rendering would silently leak past the suite: - `execute_result` would still render (existing test passes). - `display_data` without text/plain would still be omitted (existing negative tests pass). - But `display_data` WITH text/plain — the realistic `display(df)` / `display(fig)` case where a rich format co-emits with a plain-text repr — would silently lose the repr. Two new tests close the gap by exercising the exact co-emission pattern: - `test_html_plus_text_plain_display_data_renders_text_plain` — pandas-DataFrame-via-`display(df)` shape: text/html + text/plain coexist; assert text/plain renders and text/html is still dropped. - `test_image_plus_text_plain_display_data_renders_text_plain` — matplotlib-display(fig) shape: image/png + text/plain coexist; assert text/plain renders and image base64 is still dropped. Both follow the existing inline-fixture pattern and use the canonical real-world co-emission combinations. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
1 parent f9910c2 commit 9eee37a

1 file changed

Lines changed: 79 additions & 0 deletions

File tree

tests/test_notebook_md_extract.py

Lines changed: 79 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -136,6 +136,85 @@ def test_html_only_display_data_is_omitted(tmp_path):
136136
assert "**Output:**" not in rendered
137137

138138

139+
def test_html_plus_text_plain_display_data_renders_text_plain(tmp_path):
140+
"""Real-world `display(df)` from pandas emits BOTH `text/html` (the rich
141+
table) and `text/plain` (the repr-style fallback). The extractor's
142+
shared `execute_result | display_data` branch reads `text/plain`
143+
specifically, so the plain-text repr must render even when html is
144+
co-emitted. Locks the positive-render gap that the html-only and
145+
image-only omission tests don't cover (a regression in the shared
146+
branch could silently drop display_data plain-text rendering without
147+
failing the omission tests)."""
148+
nb = _minimal_nb_with_cells(
149+
[
150+
{
151+
"cell_type": "code",
152+
"metadata": {},
153+
"execution_count": 1,
154+
"source": "display(df)",
155+
"outputs": [
156+
{
157+
"output_type": "display_data",
158+
"data": {
159+
"text/html": "<table><tr><td>1</td></tr></table>",
160+
"text/plain": " col1\n0 1",
161+
},
162+
"metadata": {},
163+
}
164+
],
165+
}
166+
]
167+
)
168+
nb_path = tmp_path / "html_plus_plain.ipynb"
169+
nb_path.write_text(json.dumps(nb))
170+
171+
rendered = _load_extractor().extract(nb_path)
172+
# Plain-text repr renders.
173+
assert "**Output:**" in rendered
174+
assert "col1" in rendered
175+
assert "0 1" in rendered
176+
# HTML form is still dropped (we never render rich text/html).
177+
assert "<table>" not in rendered
178+
assert "<td>" not in rendered
179+
180+
181+
def test_image_plus_text_plain_display_data_renders_text_plain(tmp_path):
182+
"""Real-world matplotlib `display()` (or seaborn-style) emits BOTH
183+
`image/png` (the chart) and `text/plain` (e.g., a `<Figure ...>` repr).
184+
The image is intentionally dropped (base64 noise has no review value),
185+
but the plain-text repr must still render via the shared branch.
186+
Locks the positive-render path for the image-with-fallback case."""
187+
nb = _minimal_nb_with_cells(
188+
[
189+
{
190+
"cell_type": "code",
191+
"metadata": {},
192+
"execution_count": 1,
193+
"source": "fig, ax = plt.subplots(); ax.plot([1,2,3]); display(fig)",
194+
"outputs": [
195+
{
196+
"output_type": "display_data",
197+
"data": {
198+
"image/png": "iVBORw0KGgoAAAANSUhEUg==",
199+
"text/plain": "<Figure size 640x480 with 1 Axes>",
200+
},
201+
"metadata": {},
202+
}
203+
],
204+
}
205+
]
206+
)
207+
nb_path = tmp_path / "image_plus_plain.ipynb"
208+
nb_path.write_text(json.dumps(nb))
209+
210+
rendered = _load_extractor().extract(nb_path)
211+
# Plain-text repr renders.
212+
assert "**Output:**" in rendered
213+
assert "<Figure size 640x480 with 1 Axes>" in rendered
214+
# Image base64 is still dropped.
215+
assert "iVBORw0KGgo" not in rendered
216+
217+
139218
def test_image_png_display_data_is_omitted(tmp_path):
140219
nb = _minimal_nb_with_cells(
141220
[

0 commit comments

Comments
 (0)