def test_select(self, patch_curses: Any, caplog: pytest.LogCaptureFixture, selection: Set[str]) -> None: """Test `cobib.tui.tui.TUI.select`. Args: patch_curses: the `tests.tui.tui_test.TUITest.patch_curses` fixture. caplog: the built-in pytest fixture. selection: the set of selected labels. """ stdscr = MockCursesPad() stdscr.size = (24, 80) tui = TUI(stdscr, debug=True) tui.selection = copy.deepcopy(selection) caplog.clear() tui.select() assert tui.selection == (set() if selection else {"knuthwebsite"}) expected_log = [ ("cobib.tui.tui", 10, "Select command triggered."), ("cobib.tui.frame", 10, 'Obtaining current label "under" cursor.'), ("cobib.tui.frame", 10, 'Current label at "0" is "knuthwebsite".'), ] if selection: expected_log.append( ("cobib.tui.tui", 20, "Removing 'knuthwebsite' from the selection.")) else: expected_log.append(("cobib.tui.tui", 20, "Adding 'knuthwebsite' to the selection.")) assert [ record for record in caplog.record_tuples if record[0] in ("cobib.tui.frame", "cobib.tui.tui") ] == expected_log
def test_loop( self, patch_curses: Any, caplog: pytest.LogCaptureFixture, keys: Union[str, List[List[int]]], ) -> None: """Test `cobib.tui.tui.TUI.loop`. Args: patch_curses: the `tests.tui.tui_test.TUITest.patch_curses` fixture. caplog: the built-in pytest fixture. keys: the keys to send to the TUI. """ stdscr = MockCursesPad() stdscr.size = (24, 80) stdscr.returned_chars = keys # type: ignore config.tui.prompt_before_quit = False tui = TUI(stdscr, debug=True) # we expect normal execution tui.loop(debug=True) # minimal assertions for key in keys: assert ("cobib.tui.tui", 10, f"Key press registered: {key}") in caplog.record_tuples assert caplog.record_tuples[-2] == ("cobib.tui.tui", 10, "Quitting from lowest level.") assert caplog.record_tuples[-1] == ("cobib.tui.tui", 10, "Stopping key event loop.")
def test_prompt_print(self, patch_curses: Any, caplog: pytest.LogCaptureFixture, text: List[str]) -> None: """Test `cobib.tui.tui.TUI.prompt_print`. Args: patch_curses: the `tests.tui.tui_test.TUITest.patch_curses` fixture. caplog: the built-in pytest fixture. text: the text to print to the prompt. """ stdscr = MockCursesPad() stdscr.size = (24, 80) tui = TUI(stdscr, debug=True) caplog.clear() tui.prompt_print("\n".join(text)) assert tui.prompt.lines == [text[0]] # type: ignore if len(text) > 1: # assert popup on multi-line text messages assert ( "cobib.tui.buffer", 10, "Appending string to text buffer: " + "\n".join(text), ) in caplog.record_tuples assert ("cobib.tui.buffer", 10, "Create popup window.") in caplog.record_tuples
def test_config_rgb_color( self, patch_curses: Any, caplog: pytest.LogCaptureFixture, monkeypatch: pytest.MonkeyPatch, can_change_color: bool, ) -> None: """Test overwriting the RGB color value. Args: patch_curses: the `tests.tui.tui_test.TUITest.patch_curses` fixture. caplog: the built-in pytest fixture. monkeypatch: the built-in pytest fixture. can_change_color: whether `curses.can_change_color` is enabled. """ monkeypatch.setattr("curses.can_change_color", lambda: can_change_color) config.tui.colors.white = "#AA0000" TUI.colors() if not can_change_color: assert ( "cobib.tui.tui", 30, "Curses cannot change the default colors. Skipping color setup.", ) in caplog.record_tuples else: assert ("TUITest", 10, "init_color: (7, 666, 0, 0)") in caplog.record_tuples
def test_loop_inactive_commands(self, patch_curses: Any, caplog: pytest.LogCaptureFixture) -> None: """Test that inactive commands are not triggered during the TUI loop. Args: patch_curses: the `tests.tui.tui_test.TUITest.patch_curses` fixture. caplog: the built-in pytest fixture. """ stdscr = MockCursesPad() stdscr.size = (24, 80) stdscr.returned_chars = [ord("q"), ord("\n")] config.tui.prompt_before_quit = False tui = TUI(stdscr, debug=True) tui.STATE.inactive_commands = ["Show"] # we expect normal execution tui.loop(debug=True) # minimal assertions assert ( "cobib.commands.show", 10, "Show command triggered from TUI.", ) not in caplog.record_tuples for key in stdscr.returned_chars: assert ("cobib.tui.tui", 10, f"Key press registered: {key}") in caplog.record_tuples assert caplog.record_tuples[-2] == ("cobib.tui.tui", 10, "Quitting from lowest level.") assert caplog.record_tuples[-1] == ("cobib.tui.tui", 10, "Stopping key event loop.")
def test_colors(self, patch_curses: Any, caplog: pytest.LogCaptureFixture) -> None: """Test `cobib.tui.tui.TUI.colors`. Args: patch_curses: the `tests.tui.tui_test.TUITest.patch_curses` fixture. caplog: the built-in pytest fixture. """ TUI.colors() assert TUI.ANSI_MAP == { "\x1b[30;43m": 2, "\x1b[34;40m": 3, "\x1b[31;40m": 4, "\x1b[37;46m": 5, "\x1b[37;42m": 6, "\x1b[37;44m": 7, "\x1b[37;41m": 8, "\x1b[37;45m": 9, } expected_log = [ ("TUITest", 10, "start_color"), ("cobib.tui.tui", 10, "Initiliazing color pair 1 for top_statusbar"), ("TUITest", 10, "init_pair: (1, 0, 3)"), ("cobib.tui.tui", 10, "Adding ANSI color code for top_statusbar"), ("cobib.tui.tui", 10, "Initiliazing color pair 2 for bottom_statusbar"), ("TUITest", 10, "init_pair: (2, 0, 3)"), ("cobib.tui.tui", 10, "Adding ANSI color code for bottom_statusbar"), ("cobib.tui.tui", 10, "Initiliazing color pair 3 for search_label"), ("TUITest", 10, "init_pair: (3, 4, 0)"), ("cobib.tui.tui", 10, "Adding ANSI color code for search_label"), ("cobib.tui.tui", 10, "Initiliazing color pair 4 for search_query"), ("TUITest", 10, "init_pair: (4, 1, 0)"), ("cobib.tui.tui", 10, "Adding ANSI color code for search_query"), ("cobib.tui.tui", 10, "Initiliazing color pair 5 for cursor_line"), ("TUITest", 10, "init_pair: (5, 7, 6)"), ("cobib.tui.tui", 10, "Adding ANSI color code for cursor_line"), ("cobib.tui.tui", 10, "Initiliazing color pair 6 for popup_help"), ("TUITest", 10, "init_pair: (6, 7, 2)"), ("cobib.tui.tui", 10, "Adding ANSI color code for popup_help"), ("cobib.tui.tui", 10, "Initiliazing color pair 7 for popup_stdout"), ("TUITest", 10, "init_pair: (7, 7, 4)"), ("cobib.tui.tui", 10, "Adding ANSI color code for popup_stdout"), ("cobib.tui.tui", 10, "Initiliazing color pair 8 for popup_stderr"), ("TUITest", 10, "init_pair: (8, 7, 1)"), ("cobib.tui.tui", 10, "Adding ANSI color code for popup_stderr"), ("cobib.tui.tui", 10, "Initiliazing color pair 9 for selection"), ("TUITest", 10, "init_pair: (9, 7, 5)"), ("cobib.tui.tui", 10, "Adding ANSI color code for selection"), ] assert caplog.record_tuples == expected_log
def test_help(self, patch_curses: Any, caplog: pytest.LogCaptureFixture) -> None: # pylint: disable=consider-using-f-string """Test `cobib.tui.tui.TUI.help`. Args: patch_curses: the `tests.tui.tui_test.TUITest.patch_curses` fixture. caplog: the built-in pytest fixture. """ stdscr = MockCursesPad() stdscr.size = (24, 80) tui = TUI(stdscr, debug=True) caplog.clear() tui.help() expected_log = [ ("cobib.tui.tui", 10, "Help command triggered."), ("cobib.tui.tui", 10, "Generating help text."), ("MockCursesPad", 10, "erase"), ("MockCursesPad", 10, "refresh: 0 0 0 0 22 80"), ("MockCursesPad", 10, "resize: 22 80"), ("MockCursesPad", 10, "addstr: 1 1 coBib TUI Help"), ("MockCursesPad", 10, "addstr: 2 1 Key Command Description"), ("MockCursesPad", 10, "bkgd: (6,)"), ("MockCursesPad", 10, "box"), ("MockCursesPad", 10, "refresh: 0 0 0 0 22 80"), ("MockCursesPad", 10, "getch"), ("MockCursesPad", 10, "clear"), ("cobib.tui.tui", 10, "Handling resize event."), ] inv_keys = {} for key, cmd in TUI.KEYDICT.items(): if cmd in TUI.HELP_DICT: inv_keys[cmd] = "ENTER" if key in (10, 13) else chr(key) for idx, (cmd, desc) in enumerate(TUI.HELP_DICT.items()): expected_log.insert( -6, ( "MockCursesPad", 10, f"addstr: {3+idx} 1 " + "{:^8} {:<8} {}".format( "[" + config.tui.key_bindings[cmd.lower()] + "]", cmd + ":", desc), ), ) for log, truth in zip( expected_log, [ record for record in caplog.record_tuples if record[0] in ("MockCursesPad", "cobib.tui.tui") ], ): assert log == truth
def test_config_bind_key(self, caplog: pytest.LogCaptureFixture) -> None: """Test `cobib.tui.tui.TUI.bind_keys` when binding a non-default key. Args: caplog: the built-in pytest fixture. """ config.tui.key_bindings.prompt = "p" TUI.bind_keys() assert ord(":") not in TUI.KEYDICT assert ord("p") in TUI.KEYDICT and TUI.KEYDICT[ord("p")] == "Prompt" assert ("cobib.tui.tui", 20, "Binding key p to the Prompt command.") in caplog.record_tuples
def test_config_color(self, patch_curses: Any, caplog: pytest.LogCaptureFixture) -> None: """Test `cobib.tui.tui.TUI.colors` when setting a non-default color value. Args: patch_curses: the `tests.tui.tui_test.TUITest.patch_curses` fixture. caplog: the built-in pytest fixture. """ config.tui.colors.selection_fg = "red" TUI.colors() assert ("TUITest", 10, "init_pair: (9, 1, 5)") in caplog.record_tuples assert ("cobib.tui.tui", 10, "Adding ANSI color code for selection") in caplog.record_tuples
def test_config_unknown_command(self, caplog: pytest.LogCaptureFixture) -> None: """Test that binding an unknown command logs a warning. Args: caplog: the built-in pytest fixture. """ config.tui.key_bindings.dummy = "p" TUI.bind_keys() assert ( "cobib.tui.tui", 30, 'Unknown command "Dummy". Ignoring key binding.', ) in caplog.record_tuples
def test_config_unknown_color(self, patch_curses: Any, caplog: pytest.LogCaptureFixture) -> None: """Test that setting an unknown color logs a warning. Args: patch_curses: the `tests.tui.tui_test.TUITest.patch_curses` fixture. caplog: the built-in pytest fixture. """ config.tui.colors.dummy_fg = "white" TUI.colors() assert ( "cobib.tui.tui", 30, "Detected unknown TUI color name specification: dummy", ) in caplog.record_tuples
def test_infoline(self) -> None: """Test `cobib.tui.tui.TUI.infoline`.""" infoline = TUI.infoline() assert ( infoline == "a:Add d:Delete e:Edit x:Export f:Filter ?:Help i:Import m:Modify o:Open ::Prompt " "q:Quit r:Redo /:Search v:Select ENTER:Show s:Sort u:Undo w:Wrap")
def test_prompt_handler(self, patch_curses: Any, keys: List[Union[int, str]], expected: str) -> None: """Test `cobib.tui.tui.TUI.prompt_handler`. Args: patch_curses: the `tests.tui.tui_test.TUITest.patch_curses` fixture. keys: the keys to send to the prompt handler. expected: the expected string to be returned from the prompt handler. """ stdscr = MockCursesPad() stdscr.size = (24, 80) config.tui.prompt_before_quit = False tui = TUI(stdscr, debug=True) tui.prompt.returned_chars = [ # type: ignore ord(k) if isinstance(k, str) else k for k in reversed(keys) ] command = tui.prompt_handler("") assert command == expected
def test_statusbar(self, attr: int, caplog: pytest.LogCaptureFixture) -> None: """Test `cobib.tui.tui.TUI.statusbar`. Args: attr: the attribute number to use for the printed text. caplog: the built-in pytest fixture. """ pad = MockCursesPad() text = "Test statusbar text" TUI.statusbar(pad, text, attr) assert pad.lines == [text] expected_log = [ ("MockCursesPad", 10, "erase"), ("MockCursesPad", 10, "getmaxyx"), ("MockCursesPad", 10, f"addnstr: 0 0 Test statusbar text -1 {attr}"), ("MockCursesPad", 10, "refresh: None None None None None None"), ] assert caplog.record_tuples == expected_log
def test_quit( self, patch_curses: Any, caplog: pytest.LogCaptureFixture, prompt_quit: bool, returned_char: int, mode: str, ) -> None: """Test `cobib.tui.tui.TUI.quit`. Args: patch_curses: the `tests.tui.tui_test.TUITest.patch_curses` fixture. caplog: the built-in pytest fixture. prompt_quit: whether to prompt before actually quitting. returned_char: the value for `tests.tui.mock_curses.MockCursesPad.returned_chars`. mode: the `cobib.tui.state.Mode` value. """ stdscr = MockCursesPad() stdscr.size = (24, 80) config.tui.prompt_before_quit = prompt_quit tui = TUI(stdscr, debug=True) STATE.mode = mode caplog.clear() tui.prompt.returned_chars = [returned_char] # type: ignore expected_log = [] if mode == Mode.LIST.value: expected_log.append( ("cobib.tui.tui", 10, "Quitting from lowest level.")) else: expected_log.append( ("cobib.tui.tui", 10, "Quitting higher menu level. Falling back to list view.")) if prompt_quit: expected_log.append(("TUITest", 10, "curs_set: (1,)")) if returned_char == ord("n"): expected_log.append( ("cobib.tui.tui", 20, "User aborted quitting.")) expected_log.append(("TUITest", 10, "curs_set: (0,)")) if mode == Mode.LIST.value and returned_char != ord("n"): with pytest.raises(StopIteration): tui.quit() else: tui.quit() assert [ record for record in caplog.record_tuples if record[0] in ("cobib.tui.tui", "TUITest") ] == expected_log
def assert_list_view(screen, current, expected): """Asserts the list view of the TUI.""" term_width = len(screen.buffer[0]) # assert default colors assert [c.bg for c in screen.buffer[0].values()] == ['brown'] * term_width assert [c.bg for c in screen.buffer[len(screen.buffer)-2].values()] == ['brown'] * term_width # the top statusline contains the version info and number of entries assert f"CoBib v{version} - {len(expected)} Entries" in screen.display[0] # check current line if current >= 0: assert [c.fg for c in screen.buffer[current].values()] == ['white'] * term_width assert [c.bg for c in screen.buffer[current].values()] == ['cyan'] * term_width # the entries per line for idx, label in enumerate(expected): # offset of 1 due to top statusline assert label in screen.display[idx+1] # the bottom statusline should contain at least parts of the information string assert screen.display[-2].strip() in TUI.infoline() # the prompt line should be empty assert screen.display[-1].strip() == ""
def test_tui_resize(setup): """Test TUI resize handling.""" # create pseudo-terminal pid, f_d = os.forkpty() if pid == 0: # child process spawns TUI curses.wrapper(TUI) else: # resize pseudo terminal fcntl.ioctl(f_d, termios.TIOCSWINSZ, array('h', [10, 120, 1200, 220])) # parent process sets up virtual screen of identical size screen = pyte.Screen(120, 10) stream = pyte.ByteStream(screen) # scrape pseudo-terminal's screen while True: try: [f_d], _, _ = select.select([f_d], [], [], 1) except (KeyboardInterrupt, ValueError): # either test was interrupted or file descriptor of child process provides nothing # to be read break else: try: # scrape screen of child process data = os.read(f_d, 1024) stream.feed(data) except OSError: # reading empty break for line in screen.display: print(line) assert_list_view(screen, 1, [ 'dummy_entry_for_scroll_testing', 'knuthwebsite', 'latexcompanion', 'einstein' ]) # the terminal should be wide enough to contain the full information text assert screen.display[-2].strip() == TUI.infoline()
def test_bind_keys(self, caplog: pytest.LogCaptureFixture) -> None: """Test `cobib.tui.tui.TUI.bind_keys`. Args: caplog: the built-in pytest fixture. """ TUI.bind_keys() assert TUI.KEYDICT == { 258: ("y", 1), 259: ("y", -1), 338: ("y", 20), 339: ("y", -20), 106: ("y", 1), 107: ("y", -1), 103: ("y", "g"), 71: ("y", "G"), 2: ("y", -20), 4: ("y", 10), 6: ("y", 20), 21: ("y", -10), 260: ("x", -1), 261: ("x", 1), 104: ("x", -1), 108: ("x", 1), 48: ("x", 0), 36: ("x", "$"), 58: "Prompt", 47: "Search", 63: "Help", 97: "Add", 100: "Delete", 101: "Edit", 102: "Filter", 105: "Import", 109: "Modify", 111: "Open", 113: "Quit", 114: "Redo", 115: "Sort", 117: "Undo", 118: "Select", 119: "Wrap", 120: "Export", 10: "Show", 13: "Show", } expected_log = [ ("cobib.tui.tui", 20, "Binding key : to the Prompt command."), ("cobib.tui.tui", 20, "Binding key / to the Search command."), ("cobib.tui.tui", 20, "Binding key ? to the Help command."), ("cobib.tui.tui", 20, "Binding key a to the Add command."), ("cobib.tui.tui", 20, "Binding key d to the Delete command."), ("cobib.tui.tui", 20, "Binding key e to the Edit command."), ("cobib.tui.tui", 20, "Binding key f to the Filter command."), ("cobib.tui.tui", 20, "Binding key i to the Import command."), ("cobib.tui.tui", 20, "Binding key m to the Modify command."), ("cobib.tui.tui", 20, "Binding key o to the Open command."), ("cobib.tui.tui", 20, "Binding key q to the Quit command."), ("cobib.tui.tui", 20, "Binding key r to the Redo command."), ("cobib.tui.tui", 20, "Binding key s to the Sort command."), ("cobib.tui.tui", 20, "Binding key u to the Undo command."), ("cobib.tui.tui", 20, "Binding key v to the Select command."), ("cobib.tui.tui", 20, "Binding key w to the Wrap command."), ("cobib.tui.tui", 20, "Binding key x to the Export command."), ("cobib.tui.tui", 20, "Binding key ENTER to the Show command."), ] assert caplog.record_tuples == expected_log
def test_resize(self, patch_curses: Any, caplog: pytest.LogCaptureFixture) -> None: """Test `cobib.tui.tui.TUI.resize_handler`. Args: patch_curses: the `tests.tui.tui_test.TUITest.patch_curses` fixture. caplog: the built-in pytest fixture. """ stdscr = MockCursesPad() stdscr.size = (24, 80) tui = TUI(stdscr, debug=True) caplog.clear() tui.height, tui.width = (12, 70) tui.resize_handler(None, None) assert tui.width == 70 assert tui.height == 12 assert tui.topbar.size[1] == 70 # type: ignore assert tui.botbar.size[1] == 70 # type: ignore assert tui.prompt.size[1] == 70 # type: ignore expected_log = [ ("TUITest", 10, "resize_term"), ("MockCursesPad", 10, "keypad: True"), ("MockCursesPad", 10, "clear"), ("MockCursesPad", 10, "refresh: None None None None None None"), ("MockCursesPad", 10, "resize: 1 70"), ("MockCursesPad", 10, "erase"), ("MockCursesPad", 10, "getmaxyx"), ("MockCursesPad", 10, "addnstr: 0 0 coBib VERSION - 3 Entries 69 0"), # will be skipped ("MockCursesPad", 10, "refresh: None None None None None None"), ("MockCursesPad", 10, "refresh: None None None None None None"), ("MockCursesPad", 10, "resize: 1 70"), ("MockCursesPad", 10, "mvwin: 10 0"), ("MockCursesPad", 10, "erase"), ("MockCursesPad", 10, "getmaxyx"), ( "MockCursesPad", 10, "addnstr: 0 0 a:Add d:Delete e:Edit x:Export f:Filter ?:Help i:Import m:Modify " "o:Open ::Prompt q:Quit r:Redo /:Search v:Select ENTER:Show s:Sort u:Undo w:Wrap " "69 0", ), ("MockCursesPad", 10, "refresh: None None None None None None"), ("MockCursesPad", 10, "refresh: None None None None None None"), ("MockCursesPad", 10, "resize: 1 70"), ("MockCursesPad", 10, "refresh: 0 0 11 0 12 69"), ("MockCursesPad", 10, "refresh: 0 0 1 0 9 69"), ] for log, truth in zip( expected_log, [ record for record in caplog.record_tuples if record[0] in ("MockCursesPad", "TUITest") ], ): assert log[0] == truth[0] assert log[1] == truth[1] if truth[2].startswith("addnstr: 0 0 coBib v"): # skip version-containing log continue assert log[2] == truth[2]
def run_tui( keys: Union[str, List[Union[str, signal.Signals]]], # pylint: disable=no-member assertion: Callable, # type: ignore assertion_kwargs: Dict, # type: ignore ) -> None: """Spawns the coBib TUI in a forked pseudo-terminal. This method attaches a pyte object to the forked terminal to allow screen scraping. It also allows passing characters to the TUI by writing to the forked processes file descriptor. Furthermore, it also takes care of gathering the log, stdout/stderr and coverage information produced by the subprocess. For more information check out this [blog post](https://mrossinek.gitlab.io/programming/testing-tui-applications-in-python/). Args: keys: a string of characters passed to the TUI process ad verbatim. assertion: a callable method to assert the TUI state. This callable must take two arguments: a pyte screen object and the caplog.record_tuples. assertion_kwargs: additional keyword arguments propagated to the assertion call. """ # "hack" the fallback terminal size os.environ["COLUMNS"] = "80" os.environ["LINES"] = "24" # create pseudo-terminal pid, f_d = os.forkpty() if pid == 0: # setup subprocess coverage collection cov = TUITest.init_subprocess_coverage() signal.signal(signal.SIGTERM, partial(TUITest.end_subprocess_coverage, cov=cov)) # redirect logging file_handler = get_file_handler(logging.DEBUG, TMP_LOGFILE) logging.getLogger().addHandler(file_handler) # child process initializes curses and spawns the TUI try: stdscr = curses.initscr() stdscr.resize(24, 80) if curses.has_colors(): curses.start_color() curses.cbreak() curses.noecho() stdscr.keypad(True) TUI(stdscr) finally: stdscr.keypad(False) curses.nocbreak() curses.echo() else: # parent process sets up virtual screen of identical size screen = pyte.Screen(80, 24) stream = pyte.ByteStream(screen) # send keys char-wise to TUI for key in keys: if key == signal.SIGWINCH: sleep(0.25) # resize pseudo terminal buf = struct.pack("HHHH", 10, 45, 0, 0) fcntl.ioctl(f_d, termios.TIOCSWINSZ, buf) # overwrite screen screen = pyte.Screen(45, 10) stream = pyte.ByteStream(screen) else: os.write(f_d, str.encode(str(key))) # scrape pseudo-terminal's screen while True: try: [f_d], _, _ = select.select([f_d], [], [], 1) except (KeyboardInterrupt, ValueError): # either test was interrupted or file descriptor of child process provides # nothing to be read break else: try: # scrape screen of child process data = os.read(f_d, 1024) stream.feed(data) except OSError: # reading empty break # send SIGTERM to child process (which is necessary in order for pytest to not show # "failed" test cases produced by the forked child processes) os.kill(pid, signal.SIGTERM) # read the child process log file logs: List[Tuple[str, int, str]] = [] print("### Captured logs from child process ###") with open(TMP_LOGFILE, "r", encoding="utf-8") as logfile: for line in logfile.readlines(): print(line.strip("\n")) if not line.startswith(str(date.today())): logs[-1] = (logs[-1][0], logs[-1][1], logs[-1][2] + line) continue split_line = line.split() logs.append( ( split_line[3], # source getattr(logging, split_line[2].strip("[]")), # log level " ".join(split_line[5:]), # message ) ) os.remove(TMP_LOGFILE) # dump screen contents for easier debugging print("### Captured screen contents from child process ###") for line in screen.display: print(line) # It is necessary to read the database here, since we are in another subprocess Database().read() assertion(screen, logs, **assertion_kwargs)
def test_init(self, patch_curses: Any, caplog: pytest.LogCaptureFixture) -> None: """Test `cobib.tui.tui.TUI.__init__`. Args: patch_curses: the `tests.tui.tui_test.TUITest.patch_curses` fixture. caplog: the built-in pytest fixture. """ stdscr = MockCursesPad() stdscr.size = (24, 80) tui = TUI(stdscr, debug=True) assert tui.stdscr == stdscr assert tui.width == 80 assert tui.height == 24 assert tui.STATE == STATE assert tui.prompt_before_quit is True assert tui.selection == set() assert isinstance(tui.topbar, MockCursesPad) assert tui.topbar.lines[0].startswith("coBib") assert tui.topbar.lines[0].endswith("3 Entries") assert isinstance(tui.botbar, MockCursesPad) assert ( tui.botbar.lines[0] == "a:Add d:Delete e:Edit x:Export f:Filter ?:Help i:Import m:Modify o:Open ::Prompt " "q:Quit r:Redo /:Search v:Select ENTER:Show s:Sort u:Undo w:Wrap") assert isinstance(tui.prompt, MockCursesPad) assert isinstance(tui.viewport, Frame) assert tui.viewport.width == 80 assert tui.viewport.height == 21 expected_lines = [ "knuthwebsite Knuth: Computers and Typesetting", "latexcompanion The \\LaTeX\\ Companion", 'einstein Zur Elektrodynamik bewegter K{\\"o}rper', ] for line, truth in zip_longest(expected_lines, tui.viewport.buffer.lines): assert line == truth.strip() expected_log = [ ("cobib.tui.tui", 20, "Initializing TUI."), ("TUITest", 10, "curs_set: (0,)"), ("cobib.tui.tui", 10, "stdscr size determined to be 80x24"), ("cobib.tui.tui", 10, "Initializing colors."), ("TUITest", 10, "use_default_colors"), ("TUITest", 10, "start_color"), ("cobib.tui.tui", 10, "Initiliazing color pair 1 for top_statusbar"), ("TUITest", 10, "init_pair: (1, 0, 3)"), ("cobib.tui.tui", 10, "Adding ANSI color code for top_statusbar"), ("cobib.tui.tui", 10, "Initiliazing color pair 2 for bottom_statusbar"), ("TUITest", 10, "init_pair: (2, 0, 3)"), ("cobib.tui.tui", 10, "Adding ANSI color code for bottom_statusbar"), ("cobib.tui.tui", 10, "Initiliazing color pair 3 for search_label"), ("TUITest", 10, "init_pair: (3, 4, 0)"), ("cobib.tui.tui", 10, "Adding ANSI color code for search_label"), ("cobib.tui.tui", 10, "Initiliazing color pair 4 for search_query"), ("TUITest", 10, "init_pair: (4, 1, 0)"), ("cobib.tui.tui", 10, "Adding ANSI color code for search_query"), ("cobib.tui.tui", 10, "Initiliazing color pair 5 for cursor_line"), ("TUITest", 10, "init_pair: (5, 7, 6)"), ("cobib.tui.tui", 10, "Adding ANSI color code for cursor_line"), ("cobib.tui.tui", 10, "Initiliazing color pair 6 for popup_help"), ("TUITest", 10, "init_pair: (6, 7, 2)"), ("cobib.tui.tui", 10, "Adding ANSI color code for popup_help"), ("cobib.tui.tui", 10, "Initiliazing color pair 7 for popup_stdout"), ("TUITest", 10, "init_pair: (7, 7, 4)"), ("cobib.tui.tui", 10, "Adding ANSI color code for popup_stdout"), ("cobib.tui.tui", 10, "Initiliazing color pair 8 for popup_stderr"), ("TUITest", 10, "init_pair: (8, 7, 1)"), ("cobib.tui.tui", 10, "Adding ANSI color code for popup_stderr"), ("cobib.tui.tui", 10, "Initiliazing color pair 9 for selection"), ("TUITest", 10, "init_pair: (9, 7, 5)"), ("cobib.tui.tui", 10, "Adding ANSI color code for selection"), ("cobib.tui.tui", 10, "Initializing key bindings."), ("cobib.tui.tui", 20, "Binding key : to the Prompt command."), ("cobib.tui.tui", 20, "Binding key / to the Search command."), ("cobib.tui.tui", 20, "Binding key ? to the Help command."), ("cobib.tui.tui", 20, "Binding key a to the Add command."), ("cobib.tui.tui", 20, "Binding key d to the Delete command."), ("cobib.tui.tui", 20, "Binding key e to the Edit command."), ("cobib.tui.tui", 20, "Binding key f to the Filter command."), ("cobib.tui.tui", 20, "Binding key i to the Import command."), ("cobib.tui.tui", 20, "Binding key m to the Modify command."), ("cobib.tui.tui", 20, "Binding key o to the Open command."), ("cobib.tui.tui", 20, "Binding key q to the Quit command."), ("cobib.tui.tui", 20, "Binding key r to the Redo command."), ("cobib.tui.tui", 20, "Binding key s to the Sort command."), ("cobib.tui.tui", 20, "Binding key u to the Undo command."), ("cobib.tui.tui", 20, "Binding key v to the Select command."), ("cobib.tui.tui", 20, "Binding key w to the Wrap command."), ("cobib.tui.tui", 20, "Binding key x to the Export command."), ("cobib.tui.tui", 20, "Binding key ENTER to the Show command."), ("cobib.tui.tui", 10, "Initializing global State"), ("cobib.tui.tui", 10, "Populating top status bar."), ("cobib.tui.tui", 10, "Populating bottom status bar."), ("cobib.tui.tui", 10, "Initializing viewport with Frame"), ("cobib.tui.tui", 10, "Populating viewport buffer."), ] assert [ record for record in caplog.record_tuples if record[0] in ("cobib.tui.tui", "TUITest") ] == expected_log