|
8 | 8 |
|
9 | 9 | import types |
10 | 10 |
|
| 11 | +import pytest |
| 12 | + |
11 | 13 | from devbase.tui import actions_plugin, actions_project, app, menu |
12 | 14 |
|
13 | 15 |
|
@@ -139,11 +141,18 @@ def test_run_interactive_opens_top_menu(tmp_path, monkeypatch): |
139 | 141 |
|
140 | 142 |
|
141 | 143 | def _patch_loop(monkeypatch, selects, rows=None): |
142 | | - """_top_menu_loop の入力 (一覧と選択値) を注入する共通ヘルパ。""" |
| 144 | + """_top_menu_loop の入力 (一覧と選択値) を注入する共通ヘルパ。 |
| 145 | +
|
| 146 | + 操作実行後の Enter 待ち (`_pause_for_review`) は即継続にスタブし、 |
| 147 | + 呼び出し回数を返す (pause 自体の挙動は専用テストで検証する)。 |
| 148 | + """ |
143 | 149 | monkeypatch.setattr(app, "list_projects", |
144 | 150 | lambda projects_dir: list(_ROWS) if rows is None else rows) |
145 | 151 | it = iter(selects) |
146 | 152 | monkeypatch.setattr(app, "_select_top", lambda r: next(it)) |
| 153 | + pauses = [] |
| 154 | + monkeypatch.setattr(app, "_pause_for_review", lambda: pauses.append(1) or True) |
| 155 | + return pauses |
147 | 156 |
|
148 | 157 |
|
149 | 158 | def test_select_top_appends_categories_after_projects(monkeypatch): |
@@ -243,6 +252,63 @@ def test_top_loop_empty_projects_still_offers_categories(monkeypatch, tmp_path): |
243 | 252 | assert ran == [1], "プロジェクト無しでもカテゴリへ遷移できる" |
244 | 253 |
|
245 | 254 |
|
| 255 | +# --------------------------------------------------------------------------- |
| 256 | +# 操作実行後の Enter 待ち (_pause_for_review): 出力が流れる前に読めるようにする |
| 257 | +# --------------------------------------------------------------------------- |
| 258 | + |
| 259 | +def test_top_loop_pauses_after_execution(monkeypatch, tmp_path): |
| 260 | + """操作を実行したら一覧の再表示前に Enter を待つ (出力を読めるようにする)。""" |
| 261 | + pauses = _patch_loop(monkeypatch, ["plugin", None]) |
| 262 | + from devbase.tui import actions_plugin |
| 263 | + monkeypatch.setattr(actions_plugin, "run", lambda root: 0) |
| 264 | + |
| 265 | + assert app._top_menu_loop(tmp_path) == 0 |
| 266 | + assert pauses == [1], "実行後は一覧再表示の前に Enter を待つ" |
| 267 | + |
| 268 | + |
| 269 | +def test_top_loop_no_pause_on_menu_back(monkeypatch, tmp_path): |
| 270 | + """操作なし (MENU_BACK) で戻ったときは Enter を待たない (出力がないため)。""" |
| 271 | + pauses = _patch_loop(monkeypatch, ["plugin", None]) |
| 272 | + from devbase.tui import actions_plugin |
| 273 | + monkeypatch.setattr(actions_plugin, "run", lambda root: menu.MENU_BACK) |
| 274 | + |
| 275 | + assert app._top_menu_loop(tmp_path) == 0 |
| 276 | + assert pauses == [], "MENU_BACK では Enter を待たない" |
| 277 | + |
| 278 | + |
| 279 | +def test_top_loop_pause_ctrl_c_exits_with_last_rc(monkeypatch, tmp_path): |
| 280 | + """Enter 待ちで Ctrl-C (False) を受けたら直近の実行 rc で全体中止する。""" |
| 281 | + _patch_loop(monkeypatch, ["plugin"]) |
| 282 | + from devbase.tui import actions_plugin |
| 283 | + monkeypatch.setattr(actions_plugin, "run", lambda root: 1) |
| 284 | + monkeypatch.setattr(app, "_pause_for_review", lambda: False) |
| 285 | + |
| 286 | + assert app._top_menu_loop(tmp_path) == 1 |
| 287 | + |
| 288 | + |
| 289 | +def test_pause_for_review_enter_returns_true(monkeypatch): |
| 290 | + monkeypatch.setattr("builtins.input", lambda *a: "") |
| 291 | + assert app._pause_for_review() is True |
| 292 | + |
| 293 | + |
| 294 | +def test_pause_for_review_ctrl_c_returns_false(monkeypatch): |
| 295 | + def _interrupt(*a): |
| 296 | + raise KeyboardInterrupt |
| 297 | + |
| 298 | + monkeypatch.setattr("builtins.input", _interrupt) |
| 299 | + assert app._pause_for_review() is False |
| 300 | + |
| 301 | + |
| 302 | +@pytest.mark.parametrize("exc", [EOFError, OSError]) |
| 303 | +def test_pause_for_review_unreadable_stdin_returns_true(monkeypatch, exc): |
| 304 | + """非 TTY 等で stdin を読めない場合は待たずに一覧へ戻る (ハングしない)。""" |
| 305 | + def _unreadable(*a): |
| 306 | + raise exc |
| 307 | + |
| 308 | + monkeypatch.setattr("builtins.input", _unreadable) |
| 309 | + assert app._pause_for_review() is True |
| 310 | + |
| 311 | + |
246 | 312 | def test_route_plugin_delegates(monkeypatch, tmp_path): |
247 | 313 | """PR4: plugin カテゴリは actions_plugin.run へ routing される。""" |
248 | 314 | monkeypatch.setattr(actions_plugin, "run", lambda root: "RESULT") |
|
0 commit comments