Files
2026-07-13 13:12:33 +08:00

154 lines
5.5 KiB
Python
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
from __future__ import annotations
from pathlib import Path
import pytest
from tui_real_terminal.assertions import (
assert_no_completion_menu_overlap,
assert_no_stale_completion_menu,
)
from tui_real_terminal.driver import TerminalFrame, TerminalSize
from tui_real_terminal.evidence import ScenarioResult
from tui_real_terminal.scenarios import scenario_by_id
pytestmark = pytest.mark.tui_real_terminal
def test_completion_menu_overlap_assertion_accepts_clean_menu() -> None:
frame = _frame(
"\n".join(
(
"conversation content above the composer",
" ╭ commands ──────────────────────────────╮",
" │ /compact Compact the conversation │",
" │ /resume Resume an existing session │",
" ╰────────────────────────────────────────╯",
" │ send a massage │",
)
)
)
assert_no_completion_menu_overlap(frame)
def test_completion_menu_overlap_assertion_rejects_interleaved_output() -> None:
frame = _frame(
"\n".join(
(
"conversation content above the composer",
" ╭ commands ───── fake-response:hello ───╮",
" │ /compact Compact the conversation │",
" ╰────────────────────────────────────────╯",
)
)
)
with pytest.raises(AssertionError, match="completion menu overlap"):
assert_no_completion_menu_overlap(frame)
def test_stale_completion_menu_assertion_accepts_cleared_frame() -> None:
frame = _frame(
"\n".join(
(
"conversation content remains visible",
" │ /co │",
" ╰──────────────────────────────────────╯",
)
),
checkpoint="after-close",
)
assert_no_stale_completion_menu(frame)
def test_stale_completion_menu_assertion_rejects_leftover_menu() -> None:
frame = _frame(
"\n".join(
(
"conversation content remains visible",
" ╭ commands ─────────────────────────────╮",
" │ /compact Compact the conversation │",
" ╰────────────────────────────────────────╯",
)
),
checkpoint="after-close",
)
with pytest.raises(AssertionError, match="stale completion menu"):
assert_no_stale_completion_menu(frame)
def test_slash_completion_menu_renders_without_overlap(run_real_terminal_scenario) -> None:
result = run_real_terminal_scenario(scenario_by_id("completion_slash_menu_filter"))
assert result.status == "pass"
frame = _read_frame(result, "slash-menu-filtered", TerminalSize(cols=100, rows=30))
assert " commands " in frame.text
assert "/compact" in frame.text
assert_no_completion_menu_overlap(frame)
def test_completion_menu_resize_does_not_leave_overlap(
run_real_terminal_scenario,
) -> None:
result = run_real_terminal_scenario(scenario_by_id("completion_menu_resize"))
assert result.status == "pass"
for checkpoint, size in (
("after-narrow-completion-menu", TerminalSize(cols=72, rows=24)),
("after-wide-completion-menu", TerminalSize(cols=120, rows=34)),
("after-resize-completion-menu", TerminalSize(cols=120, rows=34)),
):
frame = _read_frame(result, checkpoint, size)
assert " commands " in frame.text
assert_no_completion_menu_overlap(frame)
def test_file_completion_menu_closes_without_stale_overlay(
run_real_terminal_scenario,
) -> None:
result = run_real_terminal_scenario(scenario_by_id("completion_file_menu_escape"))
assert result.status == "pass"
file_menu = _read_frame(result, "file-menu-open", TerminalSize(cols=100, rows=30))
assert " files " in file_menu.text
assert_no_completion_menu_overlap(file_menu)
assert_no_stale_completion_menu(
_read_frame(result, "after-close-file-menu", TerminalSize(cols=100, rows=30))
)
def _frame(text: str, *, checkpoint: str = "menu") -> TerminalFrame:
rows = max(1, len(text.splitlines()))
return TerminalFrame(checkpoint, text, 1, TerminalSize(cols=80, rows=rows))
def _read_frame(
result: ScenarioResult,
checkpoint: str,
size: TerminalSize,
) -> TerminalFrame:
# ScenarioResult intentionally exposes the artifact directory; frame-level
# assertions read the checkpoint evidence without broadening run_scenario.
frame_path = _frame_path(result.run_dir, checkpoint)
return TerminalFrame(
checkpoint,
frame_path.read_text(encoding="utf-8"),
0,
size,
)
def _frame_path(run_dir: Path, checkpoint: str) -> Path:
matches = sorted((run_dir / "frames").glob(f"*-{checkpoint}.txt"))
if len(matches) == 1:
return matches[0]
available = ", ".join(
path.name for path in sorted((run_dir / "frames").glob("*.txt"))
)
raise AssertionError(
f"expected exactly one frame for checkpoint {checkpoint!r}; available: {available}"
)