@@ -685,3 +685,181 @@ def content_ready() -> bool:
685685
686686 # Compare against snapshot
687687 assert frame == snapshot
688+
689+
690+ # =============================================================================
691+ # Flag Forwarding Tests
692+ # =============================================================================
693+
694+
695+ class CaptureFrameFlagCase (t .NamedTuple ):
696+ """Test case for capture_frame() flag forwarding to capture_pane()."""
697+
698+ test_id : str
699+ command : str
700+ escape_sequences : bool
701+ escape_non_printable : bool
702+ join_wrapped : bool
703+ preserve_trailing : bool
704+ trim_trailing : bool
705+ expected_pattern : str | None
706+ not_expected_pattern : str | None
707+ min_tmux_version : str | None
708+
709+
710+ CAPTURE_FRAME_FLAG_CASES : list [CaptureFrameFlagCase ] = [
711+ CaptureFrameFlagCase (
712+ test_id = "escape_sequences_color" ,
713+ command = 'printf "\\ 033[31mRED\\ 033[0m"' ,
714+ escape_sequences = True ,
715+ escape_non_printable = False ,
716+ join_wrapped = False ,
717+ preserve_trailing = False ,
718+ trim_trailing = False ,
719+ expected_pattern = r"\x1b\[31m" ,
720+ not_expected_pattern = None ,
721+ min_tmux_version = None ,
722+ ),
723+ CaptureFrameFlagCase (
724+ test_id = "no_escape_sequences" ,
725+ command = 'printf "\\ 033[31mRED\\ 033[0m"' ,
726+ escape_sequences = False ,
727+ escape_non_printable = False ,
728+ join_wrapped = False ,
729+ preserve_trailing = False ,
730+ trim_trailing = False ,
731+ expected_pattern = r"RED" ,
732+ not_expected_pattern = r"\x1b\[" ,
733+ min_tmux_version = None ,
734+ ),
735+ CaptureFrameFlagCase (
736+ test_id = "join_wrapped_long_line" ,
737+ command = "printf '%s' \" $(seq 1 30 | tr -d '\\ n')\" " ,
738+ escape_sequences = False ,
739+ escape_non_printable = False ,
740+ join_wrapped = True ,
741+ preserve_trailing = False ,
742+ trim_trailing = False ,
743+ # With join_wrapped, wrapped lines are joined - verify contiguous sequence
744+ expected_pattern = r"123456789101112131415161718192021222324252627282930" ,
745+ not_expected_pattern = None ,
746+ min_tmux_version = None ,
747+ ),
748+ CaptureFrameFlagCase (
749+ test_id = "preserve_trailing_spaces" ,
750+ command = 'printf "text \\ n"' ,
751+ escape_sequences = False ,
752+ escape_non_printable = False ,
753+ join_wrapped = False ,
754+ preserve_trailing = True ,
755+ trim_trailing = False ,
756+ expected_pattern = r"text " ,
757+ not_expected_pattern = None ,
758+ min_tmux_version = None ,
759+ ),
760+ ]
761+
762+
763+ @pytest .mark .parametrize (
764+ list (CaptureFrameFlagCase ._fields ),
765+ CAPTURE_FRAME_FLAG_CASES ,
766+ ids = [case .test_id for case in CAPTURE_FRAME_FLAG_CASES ],
767+ )
768+ def test_capture_frame_flag_forwarding (
769+ test_id : str ,
770+ command : str ,
771+ escape_sequences : bool ,
772+ escape_non_printable : bool ,
773+ join_wrapped : bool ,
774+ preserve_trailing : bool ,
775+ trim_trailing : bool ,
776+ expected_pattern : str | None ,
777+ not_expected_pattern : str | None ,
778+ min_tmux_version : str | None ,
779+ session : Session ,
780+ ) -> None :
781+ """Test that capture_frame() correctly forwards flags to capture_pane().
782+
783+ Parameters
784+ ----------
785+ test_id : str
786+ Unique identifier for the test case.
787+ command : str
788+ Shell command to execute.
789+ escape_sequences : bool
790+ Include ANSI escape sequences.
791+ escape_non_printable : bool
792+ Escape non-printable characters.
793+ join_wrapped : bool
794+ Join wrapped lines.
795+ preserve_trailing : bool
796+ Preserve trailing spaces.
797+ trim_trailing : bool
798+ Trim trailing positions.
799+ expected_pattern : str | None
800+ Regex pattern expected in output.
801+ not_expected_pattern : str | None
802+ Regex pattern that should NOT be in output.
803+ min_tmux_version : str | None
804+ Minimum tmux version required.
805+ session : Session
806+ pytest fixture providing tmux session.
807+ """
808+ import re
809+
810+ from libtmux .common import has_gte_version
811+
812+ if min_tmux_version and not has_gte_version (min_tmux_version ):
813+ pytest .skip (f"Requires tmux { min_tmux_version } +" )
814+
815+ env = shutil .which ("env" )
816+ assert env is not None
817+
818+ window = session .new_window (
819+ attach = True ,
820+ window_name = f"flag_{ test_id } " ,
821+ window_shell = f"{ env } PS1='$ ' sh" ,
822+ )
823+ pane = window .active_pane
824+ assert pane is not None
825+
826+ # Wait for shell prompt
827+ def prompt_ready () -> bool :
828+ return "$" in "\n " .join (pane .capture_pane ())
829+
830+ retry_until (prompt_ready , 2 , raises = True )
831+
832+ # Send command and wait for completion marker
833+ marker = f"__DONE_{ test_id } __"
834+ pane .send_keys (f"{ command } ; echo { marker } " , literal = True )
835+
836+ def marker_ready () -> bool :
837+ return marker in "\n " .join (pane .capture_pane ())
838+
839+ retry_until (marker_ready , 3 , raises = True )
840+
841+ # Capture frame with specified flags
842+ frame = pane .capture_frame (
843+ content_width = 80 ,
844+ content_height = 24 ,
845+ escape_sequences = escape_sequences ,
846+ escape_non_printable = escape_non_printable ,
847+ join_wrapped = join_wrapped ,
848+ preserve_trailing = preserve_trailing ,
849+ trim_trailing = trim_trailing ,
850+ )
851+
852+ # Get rendered content (without frame borders)
853+ rendered = frame .render ()
854+
855+ # Verify expected pattern
856+ if expected_pattern :
857+ assert re .search (expected_pattern , rendered , re .DOTALL ), (
858+ f"Expected pattern '{ expected_pattern } ' not found in:\n { rendered } "
859+ )
860+
861+ # Verify not_expected pattern is absent
862+ if not_expected_pattern :
863+ assert not re .search (not_expected_pattern , rendered , re .DOTALL ), (
864+ f"Unexpected pattern '{ not_expected_pattern } ' found in:\n { rendered } "
865+ )
0 commit comments