@@ -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