1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
from __future__ import annotations
from dataclasses import dataclass
from typing import Literal
import pytest
from unicurses import KEY_DOWN, KEY_ENTER, KEY_LEFT, KEY_RIGHT, KEY_UP
from pygittools.tui.changes_list import ChangesList
from pygittools.tui.input_context import InputContext, input_context, nav_context
from pygittools.tui.log_list import CommitLogList
from pygittools.tui.pages.help import HelpPage
from pygittools.tui.types import PageAction, PageContext
_HeaderFocus = Literal["changes", "repo", "branch"]
_HomeTab = Literal["status", "log"]
@dataclass
class FakeHost:
page_context: PageContext | None
changes: ChangesList
log: CommitLogList
header_focus: _HeaderFocus = "changes"
active_tab: _HomeTab = "status"
def set_header_focus(self, focus: _HeaderFocus) -> None:
self.header_focus = focus
def set_active_tab(self, tab: _HomeTab) -> None:
self.active_tab = tab
@pytest.fixture
def host(page_ctx: PageContext) -> FakeHost:
changes = ChangesList()
changes.refresh(page_ctx.repo)
log = CommitLogList()
log.refresh(page_ctx.repo)
return FakeHost(page_context=page_ctx, changes=changes, log=log)
def test_nav_context_status_hints(host: FakeHost) -> None:
nav = nav_context()
host.header_focus = "repo"
assert "browse projects" in nav.status_hint(host)
host.header_focus = "branch"
assert "switch branch" in nav.status_hint(host)
host.active_tab = "log"
host.header_focus = "changes"
assert "j/k move" in nav.status_hint(host)
def test_nav_context_opens_help(host: FakeHost) -> None:
dispatch = nav_context().dispatch_key(host, ord("?"))
assert dispatch.page_result is not None
assert dispatch.page_result.action == PageAction.PUSH
assert isinstance(dispatch.page_result.next_page, HelpPage)
def test_nav_context_repo_header(host: FakeHost) -> None:
host.header_focus = "repo"
nav = nav_context()
nav.dispatch_key(host, KEY_DOWN)
assert host.header_focus == "branch"
dispatch = nav.dispatch_key(host, KEY_ENTER)
assert dispatch.page_result is not None
assert dispatch.page_result.action == PageAction.PUSH
def test_nav_context_branch_header(host: FakeHost) -> None:
host.header_focus = "branch"
nav = nav_context()
nav.dispatch_key(host, KEY_UP)
assert host.header_focus == "repo"
nav.dispatch_key(host, KEY_DOWN)
assert host.header_focus == "branch"
nav.dispatch_key(host, KEY_DOWN)
assert host.header_focus == "changes"
host.header_focus = "branch"
dispatch = nav.dispatch_key(host, KEY_ENTER)
assert dispatch.page_result is not None
assert dispatch.page_result.action == PageAction.PUSH
def test_nav_context_tab_switch(host: FakeHost) -> None:
nav = nav_context()
host.active_tab = "log"
nav.dispatch_key(host, KEY_LEFT)
assert host.active_tab == "status"
host.active_tab = "status"
nav.dispatch_key(host, KEY_RIGHT)
assert host.active_tab == "log"
def test_nav_context_moves_to_branch_from_top(host: FakeHost) -> None:
host.changes._cursor = 0
nav_context().dispatch_key(host, KEY_UP)
assert host.header_focus == "branch"
def test_nav_context_log_tab(host: FakeHost) -> None:
host.active_tab = "log"
nav_context().dispatch_key(host, KEY_DOWN)
assert host.log._cursor >= 0
def test_nav_context_enters_input(host: FakeHost) -> None:
host.changes._cursor = 0
dispatch = nav_context().dispatch_key(host, ord("a"))
assert dispatch.next_mode == "input"
def test_input_context_esc_returns_nav(host: FakeHost) -> None:
dispatch = input_context().dispatch_key(host, 27)
assert dispatch.next_mode == "nav"
def test_input_context_commit_relinquishes(host: FakeHost, monkeypatch: pytest.MonkeyPatch) -> None:
host.changes.set_commit_message("msg")
monkeypatch.setattr(
"pygittools.tui.changes_list.commit_staged",
lambda _repo, _msg: (True, ""),
)
dispatch = input_context().dispatch_key(host, KEY_ENTER)
assert dispatch.next_mode == "nav"
def test_input_context_status_hint() -> None:
assert "Esc" in InputContext().status_hint(FakeHost(None, ChangesList(), CommitLogList()))
def test_nav_context_without_page_context() -> None:
host = FakeHost(page_context=None, changes=ChangesList(), log=CommitLogList())
assert nav_context().dispatch_key(host, KEY_DOWN).page_result is None