Skip to content

Commit 17570c0

Browse files
committed
tests(pane): Add exhaustive capture_frame() snapshot tests
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
1 parent e80229e commit 17570c0

File tree

1 file changed

+318
-0
lines changed

1 file changed

+318
-0
lines changed

tests/test_pane_capture_frame.py

Lines changed: 318 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -367,3 +367,321 @@ def output_appeared() -> bool:
367367
truncated = line[: frame.content_width]
368368
if truncated.strip(): # Non-empty lines
369369
assert truncated in frame.render()
370+
371+
372+
# =============================================================================
373+
# Exhaustive Snapshot Tests
374+
# =============================================================================
375+
376+
377+
class SnapshotCase(t.NamedTuple):
378+
"""Snapshot test case for exhaustive capture_frame() variations.
379+
380+
This NamedTuple defines the parameters for parametrized snapshot tests
381+
that cover all combinations of capture_frame() options.
382+
383+
Attributes
384+
----------
385+
test_id : str
386+
Unique identifier for the test case, used in snapshot filenames.
387+
command : str
388+
Shell command to execute (empty string for prompt-only tests).
389+
content_width : int
390+
Frame width in characters.
391+
content_height : int
392+
Frame height in lines.
393+
start : t.Literal["-"] | int | None
394+
Starting line for capture (None = default).
395+
end : t.Literal["-"] | int | None
396+
Ending line for capture (None = default).
397+
overflow_behavior : t.Literal["error", "truncate"]
398+
How to handle content exceeding frame dimensions.
399+
wait_for : str
400+
String to wait for before capturing (ensures output is ready).
401+
"""
402+
403+
test_id: str
404+
command: str
405+
content_width: int
406+
content_height: int
407+
start: t.Literal["-"] | int | None
408+
end: t.Literal["-"] | int | None
409+
overflow_behavior: t.Literal["error", "truncate"]
410+
wait_for: str
411+
412+
413+
SNAPSHOT_CASES: list[SnapshotCase] = [
414+
# --- Dimension Variations ---
415+
SnapshotCase(
416+
test_id="prompt_only",
417+
command="",
418+
content_width=20,
419+
content_height=3,
420+
start=None,
421+
end=None,
422+
overflow_behavior="truncate",
423+
wait_for="$",
424+
),
425+
SnapshotCase(
426+
test_id="echo_simple",
427+
command='echo "hello"',
428+
content_width=25,
429+
content_height=4,
430+
start=None,
431+
end=None,
432+
overflow_behavior="truncate",
433+
wait_for="hello",
434+
),
435+
SnapshotCase(
436+
test_id="echo_multiline",
437+
command='printf "a\\nb\\nc\\n"',
438+
content_width=20,
439+
content_height=6,
440+
start=None,
441+
end=None,
442+
overflow_behavior="truncate",
443+
wait_for="c",
444+
),
445+
SnapshotCase(
446+
test_id="wide_frame",
447+
command='echo "test"',
448+
content_width=60,
449+
content_height=3,
450+
start=None,
451+
end=None,
452+
overflow_behavior="truncate",
453+
wait_for="test",
454+
),
455+
SnapshotCase(
456+
test_id="narrow_frame",
457+
command='echo "test"',
458+
content_width=10,
459+
content_height=3,
460+
start=None,
461+
end=None,
462+
overflow_behavior="truncate",
463+
wait_for="test",
464+
),
465+
SnapshotCase(
466+
test_id="tall_frame",
467+
command='echo "x"',
468+
content_width=20,
469+
content_height=10,
470+
start=None,
471+
end=None,
472+
overflow_behavior="truncate",
473+
wait_for="x",
474+
),
475+
SnapshotCase(
476+
test_id="short_frame",
477+
command='echo "x"',
478+
content_width=20,
479+
content_height=2,
480+
start=None,
481+
end=None,
482+
overflow_behavior="truncate",
483+
wait_for="x",
484+
),
485+
# --- Start/End Parameter Variations ---
486+
SnapshotCase(
487+
test_id="start_zero",
488+
command='echo "line"',
489+
content_width=30,
490+
content_height=5,
491+
start=0,
492+
end=None,
493+
overflow_behavior="truncate",
494+
wait_for="line",
495+
),
496+
SnapshotCase(
497+
test_id="end_zero",
498+
command='echo "line"',
499+
content_width=30,
500+
content_height=3,
501+
start=None,
502+
end=0,
503+
overflow_behavior="truncate",
504+
wait_for="line",
505+
),
506+
SnapshotCase(
507+
test_id="end_dash",
508+
command='echo "line"',
509+
content_width=30,
510+
content_height=5,
511+
start=None,
512+
end="-",
513+
overflow_behavior="truncate",
514+
wait_for="line",
515+
),
516+
SnapshotCase(
517+
test_id="start_end_range",
518+
command='printf "L1\\nL2\\nL3\\nL4\\n"',
519+
content_width=30,
520+
content_height=5,
521+
start=0,
522+
end=2,
523+
overflow_behavior="truncate",
524+
wait_for="L4",
525+
),
526+
# --- Truncation Behavior ---
527+
SnapshotCase(
528+
test_id="truncate_width",
529+
command='echo "' + "x" * 50 + '"',
530+
content_width=15,
531+
content_height=4,
532+
start=None,
533+
end=None,
534+
overflow_behavior="truncate",
535+
wait_for="xxxx",
536+
),
537+
SnapshotCase(
538+
test_id="truncate_height",
539+
command='printf "L1\\nL2\\nL3\\nL4\\nL5\\nL6\\nL7\\nL8\\nL9\\nL10\\n"',
540+
content_width=30,
541+
content_height=3,
542+
start=None,
543+
end=None,
544+
overflow_behavior="truncate",
545+
wait_for="L10",
546+
),
547+
# --- Special Characters ---
548+
SnapshotCase(
549+
test_id="special_chars",
550+
command='echo "!@#$%"',
551+
content_width=25,
552+
content_height=4,
553+
start=None,
554+
end=None,
555+
overflow_behavior="truncate",
556+
wait_for="!@#$%",
557+
),
558+
SnapshotCase(
559+
test_id="unicode_basic",
560+
command='echo "cafe"', # Using ASCII to avoid shell encoding issues
561+
content_width=25,
562+
content_height=4,
563+
start=None,
564+
end=None,
565+
overflow_behavior="truncate",
566+
wait_for="cafe",
567+
),
568+
# --- Edge Cases ---
569+
SnapshotCase(
570+
test_id="empty_lines",
571+
command='printf "\\n\\n\\n"',
572+
content_width=20,
573+
content_height=6,
574+
start=None,
575+
end=None,
576+
overflow_behavior="truncate",
577+
wait_for="$",
578+
),
579+
SnapshotCase(
580+
test_id="spaces_only",
581+
command='echo " "',
582+
content_width=20,
583+
content_height=4,
584+
start=None,
585+
end=None,
586+
overflow_behavior="truncate",
587+
wait_for="$",
588+
),
589+
SnapshotCase(
590+
test_id="mixed_content",
591+
command='echo "abc 123 !@#"',
592+
content_width=30,
593+
content_height=4,
594+
start=None,
595+
end=None,
596+
overflow_behavior="truncate",
597+
wait_for="abc 123 !@#",
598+
),
599+
]
600+
601+
602+
@pytest.mark.parametrize(
603+
list(SnapshotCase._fields),
604+
SNAPSHOT_CASES,
605+
ids=[case.test_id for case in SNAPSHOT_CASES],
606+
)
607+
def test_capture_frame_snapshot_parametrized(
608+
test_id: str,
609+
command: str,
610+
content_width: int,
611+
content_height: int,
612+
start: t.Literal["-"] | int | None,
613+
end: t.Literal["-"] | int | None,
614+
overflow_behavior: t.Literal["error", "truncate"],
615+
wait_for: str,
616+
session: Session,
617+
snapshot: SnapshotAssertion,
618+
) -> None:
619+
"""Exhaustive snapshot tests for capture_frame() parameter variations.
620+
621+
This parametrized test covers all combinations of capture_frame() options
622+
including dimensions, start/end parameters, truncation, special characters,
623+
and edge cases.
624+
625+
Parameters
626+
----------
627+
test_id : str
628+
Unique identifier for the test case.
629+
command : str
630+
Shell command to execute (empty for prompt-only).
631+
content_width : int
632+
Frame width in characters.
633+
content_height : int
634+
Frame height in lines.
635+
start : t.Literal["-"] | int | None
636+
Starting line for capture.
637+
end : t.Literal["-"] | int | None
638+
Ending line for capture.
639+
overflow_behavior : t.Literal["error", "truncate"]
640+
How to handle overflow.
641+
wait_for : str
642+
String to wait for before capturing.
643+
session : Session
644+
pytest fixture providing tmux session.
645+
snapshot : SnapshotAssertion
646+
syrupy snapshot fixture with TextFrameExtension.
647+
"""
648+
env = shutil.which("env")
649+
assert env is not None, "Cannot find usable `env` in PATH."
650+
651+
window = session.new_window(
652+
attach=True,
653+
window_name=f"snap_{test_id}",
654+
window_shell=f"{env} PS1='$ ' sh",
655+
)
656+
pane = window.active_pane
657+
assert pane is not None
658+
659+
# Wait for shell prompt to appear
660+
def prompt_ready() -> bool:
661+
return "$" in "\n".join(pane.capture_pane())
662+
663+
retry_until(prompt_ready, 2, raises=True)
664+
665+
# Send command if provided
666+
if command:
667+
pane.send_keys(command, literal=True, suppress_history=False)
668+
669+
# Wait for expected content
670+
if wait_for:
671+
672+
def content_ready() -> bool:
673+
return wait_for in "\n".join(pane.capture_pane())
674+
675+
retry_until(content_ready, 2, raises=True)
676+
677+
# Capture frame with specified parameters
678+
frame = pane.capture_frame(
679+
start=start,
680+
end=end,
681+
content_width=content_width,
682+
content_height=content_height,
683+
overflow_behavior=overflow_behavior,
684+
)
685+
686+
# Compare against snapshot
687+
assert frame == snapshot

0 commit comments

Comments
 (0)