import unittest from unittest import mock from sbp.tui import frame from sbp.tui.term import Term class FakeScreen: def __init__(self, height: int = 24, width: int = 80) -> None: self.height = height self.width = width self.writes: list[tuple[int, int, str]] = [] def getmaxyx(self): return self.height, self.width def addstr(self, y, x, text, attr=0): if y >= self.height or x + len(text) > self.width: raise AssertionError(f"write out of bounds: y={y} x={x} len={len(text)}") self.writes.append((y, x, text)) def erase(self): self.writes.clear() def refresh(self): pass def keypad(self, flag): pass def getch(self): return -1 def text(self) -> str: return "\n".join(text for _, _, text in self.writes) def make_term(screen: FakeScreen) -> Term: with mock.patch.object(frame_term_curses(), "has_colors", return_value=False), \ mock.patch.object(frame_term_curses(), "curs_set", return_value=None): return Term(screen) def frame_term_curses(): import sbp.tui.term as module return module.curses class WindowOffsetTest(unittest.TestCase): def test_no_scroll_when_everything_fits(self) -> None: self.assertEqual(frame.window_offset(3, 5, 10), 0) def test_keeps_selection_visible(self) -> None: self.assertEqual(frame.window_offset(7, 20, 5), 3) def test_clamps_to_last_page(self) -> None: self.assertEqual(frame.window_offset(19, 20, 5), 15) class SidebarWidthTest(unittest.TestCase): def test_minimum(self) -> None: self.assertEqual(frame.sidebar_width(["A"]), frame.SIDEBAR_MIN) def test_grows_with_titles(self) -> None: self.assertGreater( frame.sidebar_width(["VeryLongSectionName"]), frame.sidebar_width(["A"]) ) def test_capped(self) -> None: self.assertEqual(frame.sidebar_width(["x" * 100]), frame.SIDEBAR_MAX) class DrawTest(unittest.TestCase): def setUp(self) -> None: self.screen = FakeScreen() self.term = make_term(self.screen) def draw(self, rows=None, index=0, width=80): rows = rows or [frame.Row("first", action=lambda app: None), frame.Row("second")] frame.draw( self.term, title="sbp 2.0.0", subtitle="host active", sidebar=["Dashboard", "Protocols", "Users", "Settings", "Logs"], sidebar_index=1, sidebar_focused=False, content_title="Protocols", rows=rows, index=index, content_focused=True, footer="keys", ) def test_draws_both_panes(self) -> None: self.draw() text = self.screen.text() for word in ("Dashboard", "Protocols", "Users", "Settings", "Logs", "first", "second", "keys"): self.assertIn(word, text) def test_selected_row_is_padded_for_highlight(self) -> None: self.draw(index=1) row = [text for y, _, text in self.screen.writes if text.startswith("second")] self.assertTrue(row) self.assertGreater(len(row[0]), len("second")) def test_small_terminal_shows_hint(self) -> None: self.screen = FakeScreen(height=8, width=40) self.term.screen = self.screen self.draw() self.assertIn("terminal too small", self.screen.text()) if __name__ == "__main__": unittest.main()