|
67dab81…
|
lmata
|
1 |
"""Tests for shell completions — module API and CLI command.""" |
|
67dab81…
|
lmata
|
2 |
|
|
67dab81…
|
lmata
|
3 |
from pathlib import Path |
|
67dab81…
|
lmata
|
4 |
|
|
67dab81…
|
lmata
|
5 |
import pytest |
|
67dab81…
|
lmata
|
6 |
from click.testing import CliRunner |
|
67dab81…
|
lmata
|
7 |
|
|
67dab81…
|
lmata
|
8 |
from navegador.cli.commands import main |
|
67dab81…
|
lmata
|
9 |
from navegador.completions import ( |
|
67dab81…
|
lmata
|
10 |
SUPPORTED_SHELLS, |
|
67dab81…
|
lmata
|
11 |
get_eval_line, |
|
67dab81…
|
lmata
|
12 |
get_rc_path, |
|
67dab81…
|
lmata
|
13 |
install_completion, |
|
67dab81…
|
lmata
|
14 |
) |
|
67dab81…
|
lmata
|
15 |
|
|
67dab81…
|
lmata
|
16 |
# ── get_eval_line ───────────────────────────────────────────────────────────── |
|
67dab81…
|
lmata
|
17 |
|
|
67dab81…
|
lmata
|
18 |
|
|
67dab81…
|
lmata
|
19 |
class TestGetEvalLine: |
|
67dab81…
|
lmata
|
20 |
def test_bash_contains_bash_source(self): |
|
67dab81…
|
lmata
|
21 |
line = get_eval_line("bash") |
|
67dab81…
|
lmata
|
22 |
assert "bash_source" in line |
|
67dab81…
|
lmata
|
23 |
assert "navegador" in line |
|
67dab81…
|
lmata
|
24 |
|
|
67dab81…
|
lmata
|
25 |
def test_zsh_contains_zsh_source(self): |
|
67dab81…
|
lmata
|
26 |
line = get_eval_line("zsh") |
|
67dab81…
|
lmata
|
27 |
assert "zsh_source" in line |
|
67dab81…
|
lmata
|
28 |
assert "navegador" in line |
|
67dab81…
|
lmata
|
29 |
|
|
67dab81…
|
lmata
|
30 |
def test_fish_contains_fish_source(self): |
|
67dab81…
|
lmata
|
31 |
line = get_eval_line("fish") |
|
67dab81…
|
lmata
|
32 |
assert "fish_source" in line |
|
67dab81…
|
lmata
|
33 |
assert "navegador" in line |
|
67dab81…
|
lmata
|
34 |
|
|
67dab81…
|
lmata
|
35 |
def test_bash_uses_eval(self): |
|
67dab81…
|
lmata
|
36 |
line = get_eval_line("bash") |
|
67dab81…
|
lmata
|
37 |
assert line.startswith("eval ") |
|
67dab81…
|
lmata
|
38 |
|
|
67dab81…
|
lmata
|
39 |
def test_zsh_uses_eval(self): |
|
67dab81…
|
lmata
|
40 |
line = get_eval_line("zsh") |
|
67dab81…
|
lmata
|
41 |
assert line.startswith("eval ") |
|
67dab81…
|
lmata
|
42 |
|
|
67dab81…
|
lmata
|
43 |
def test_fish_uses_pipe_source(self): |
|
67dab81…
|
lmata
|
44 |
line = get_eval_line("fish") |
|
67dab81…
|
lmata
|
45 |
assert "| source" in line |
|
67dab81…
|
lmata
|
46 |
|
|
67dab81…
|
lmata
|
47 |
def test_invalid_shell_raises_value_error(self): |
|
67dab81…
|
lmata
|
48 |
with pytest.raises(ValueError, match="Unsupported shell"): |
|
67dab81…
|
lmata
|
49 |
get_eval_line("powershell") |
|
67dab81…
|
lmata
|
50 |
|
|
67dab81…
|
lmata
|
51 |
def test_all_supported_shells_return_string(self): |
|
67dab81…
|
lmata
|
52 |
for shell in SUPPORTED_SHELLS: |
|
67dab81…
|
lmata
|
53 |
assert isinstance(get_eval_line(shell), str) |
|
67dab81…
|
lmata
|
54 |
|
|
67dab81…
|
lmata
|
55 |
|
|
67dab81…
|
lmata
|
56 |
# ── get_rc_path ─────────────────────────────────────────────────────────────── |
|
67dab81…
|
lmata
|
57 |
|
|
67dab81…
|
lmata
|
58 |
|
|
67dab81…
|
lmata
|
59 |
class TestGetRcPath: |
|
67dab81…
|
lmata
|
60 |
def test_bash_returns_bashrc(self): |
|
67dab81…
|
lmata
|
61 |
assert get_rc_path("bash") == "~/.bashrc" |
|
67dab81…
|
lmata
|
62 |
|
|
67dab81…
|
lmata
|
63 |
def test_zsh_returns_zshrc(self): |
|
67dab81…
|
lmata
|
64 |
assert get_rc_path("zsh") == "~/.zshrc" |
|
67dab81…
|
lmata
|
65 |
|
|
67dab81…
|
lmata
|
66 |
def test_fish_returns_config_fish(self): |
|
67dab81…
|
lmata
|
67 |
assert "config.fish" in get_rc_path("fish") |
|
67dab81…
|
lmata
|
68 |
|
|
67dab81…
|
lmata
|
69 |
def test_invalid_shell_raises_value_error(self): |
|
67dab81…
|
lmata
|
70 |
with pytest.raises(ValueError, match="Unsupported shell"): |
|
67dab81…
|
lmata
|
71 |
get_rc_path("ksh") |
|
67dab81…
|
lmata
|
72 |
|
|
67dab81…
|
lmata
|
73 |
def test_all_supported_shells_return_string(self): |
|
67dab81…
|
lmata
|
74 |
for shell in SUPPORTED_SHELLS: |
|
67dab81…
|
lmata
|
75 |
assert isinstance(get_rc_path(shell), str) |
|
67dab81…
|
lmata
|
76 |
|
|
67dab81…
|
lmata
|
77 |
|
|
67dab81…
|
lmata
|
78 |
# ── install_completion ──────────────────────────────────────────────────────── |
|
67dab81…
|
lmata
|
79 |
|
|
67dab81…
|
lmata
|
80 |
|
|
67dab81…
|
lmata
|
81 |
class TestInstallCompletion: |
|
67dab81…
|
lmata
|
82 |
def test_creates_file_with_eval_line(self, tmp_path): |
|
67dab81…
|
lmata
|
83 |
rc = tmp_path / ".bashrc" |
|
67dab81…
|
lmata
|
84 |
result = install_completion("bash", rc_path=str(rc)) |
|
67dab81…
|
lmata
|
85 |
assert result.exists() |
|
67dab81…
|
lmata
|
86 |
content = rc.read_text() |
|
67dab81…
|
lmata
|
87 |
assert "bash_source" in content |
|
67dab81…
|
lmata
|
88 |
assert "navegador" in content |
|
67dab81…
|
lmata
|
89 |
|
|
67dab81…
|
lmata
|
90 |
def test_creates_parent_dirs_for_fish(self, tmp_path): |
|
67dab81…
|
lmata
|
91 |
rc = tmp_path / ".config" / "fish" / "config.fish" |
|
67dab81…
|
lmata
|
92 |
result = install_completion("fish", rc_path=str(rc)) |
|
67dab81…
|
lmata
|
93 |
assert result.exists() |
|
67dab81…
|
lmata
|
94 |
assert rc.parent.is_dir() |
|
67dab81…
|
lmata
|
95 |
|
|
67dab81…
|
lmata
|
96 |
def test_returns_path_object(self, tmp_path): |
|
67dab81…
|
lmata
|
97 |
rc = tmp_path / ".zshrc" |
|
67dab81…
|
lmata
|
98 |
result = install_completion("zsh", rc_path=str(rc)) |
|
67dab81…
|
lmata
|
99 |
assert isinstance(result, Path) |
|
67dab81…
|
lmata
|
100 |
|
|
67dab81…
|
lmata
|
101 |
def test_idempotent_does_not_duplicate(self, tmp_path): |
|
67dab81…
|
lmata
|
102 |
rc = tmp_path / ".bashrc" |
|
67dab81…
|
lmata
|
103 |
install_completion("bash", rc_path=str(rc)) |
|
67dab81…
|
lmata
|
104 |
install_completion("bash", rc_path=str(rc)) |
|
67dab81…
|
lmata
|
105 |
content = rc.read_text() |
|
67dab81…
|
lmata
|
106 |
# The eval line should appear exactly once |
|
67dab81…
|
lmata
|
107 |
line = "eval \"$(_NAVEGADOR_COMPLETE=bash_source navegador)\"" |
|
67dab81…
|
lmata
|
108 |
assert content.count(line) == 1 |
|
67dab81…
|
lmata
|
109 |
|
|
67dab81…
|
lmata
|
110 |
def test_appends_to_existing_file(self, tmp_path): |
|
67dab81…
|
lmata
|
111 |
rc = tmp_path / ".zshrc" |
|
67dab81…
|
lmata
|
112 |
rc.write_text("# existing content\nexport FOO=bar\n") |
|
67dab81…
|
lmata
|
113 |
install_completion("zsh", rc_path=str(rc)) |
|
67dab81…
|
lmata
|
114 |
content = rc.read_text() |
|
67dab81…
|
lmata
|
115 |
assert "existing content" in content |
|
67dab81…
|
lmata
|
116 |
assert "zsh_source" in content |
|
67dab81…
|
lmata
|
117 |
|
|
67dab81…
|
lmata
|
118 |
def test_invalid_shell_raises_value_error(self, tmp_path): |
|
67dab81…
|
lmata
|
119 |
with pytest.raises(ValueError, match="Unsupported shell"): |
|
67dab81…
|
lmata
|
120 |
install_completion("tcsh", rc_path=str(tmp_path / ".tcshrc")) |
|
67dab81…
|
lmata
|
121 |
|
|
67dab81…
|
lmata
|
122 |
def test_zsh_eval_line_in_file(self, tmp_path): |
|
67dab81…
|
lmata
|
123 |
rc = tmp_path / ".zshrc" |
|
67dab81…
|
lmata
|
124 |
install_completion("zsh", rc_path=str(rc)) |
|
67dab81…
|
lmata
|
125 |
assert "zsh_source" in rc.read_text() |
|
67dab81…
|
lmata
|
126 |
|
|
67dab81…
|
lmata
|
127 |
def test_fish_source_line_in_file(self, tmp_path): |
|
67dab81…
|
lmata
|
128 |
rc = tmp_path / "config.fish" |
|
67dab81…
|
lmata
|
129 |
install_completion("fish", rc_path=str(rc)) |
|
67dab81…
|
lmata
|
130 |
assert "fish_source" in rc.read_text() |
|
67dab81…
|
lmata
|
131 |
assert "| source" in rc.read_text() |
|
67dab81…
|
lmata
|
132 |
|
|
67dab81…
|
lmata
|
133 |
|
|
67dab81…
|
lmata
|
134 |
# ── CLI: navegador completions <shell> ──────────────────────────────────────── |
|
67dab81…
|
lmata
|
135 |
|
|
67dab81…
|
lmata
|
136 |
|
|
67dab81…
|
lmata
|
137 |
class TestCompletionsCommand: |
|
67dab81…
|
lmata
|
138 |
# Basic output |
|
67dab81…
|
lmata
|
139 |
|
|
67dab81…
|
lmata
|
140 |
def test_bash_outputs_eval_line(self): |
|
67dab81…
|
lmata
|
141 |
runner = CliRunner() |
|
67dab81…
|
lmata
|
142 |
result = runner.invoke(main, ["completions", "bash"]) |
|
67dab81…
|
lmata
|
143 |
assert result.exit_code == 0 |
|
67dab81…
|
lmata
|
144 |
assert "bash_source" in result.output |
|
67dab81…
|
lmata
|
145 |
|
|
67dab81…
|
lmata
|
146 |
def test_zsh_outputs_eval_line(self): |
|
67dab81…
|
lmata
|
147 |
runner = CliRunner() |
|
67dab81…
|
lmata
|
148 |
result = runner.invoke(main, ["completions", "zsh"]) |
|
67dab81…
|
lmata
|
149 |
assert result.exit_code == 0 |
|
67dab81…
|
lmata
|
150 |
assert "zsh_source" in result.output |
|
67dab81…
|
lmata
|
151 |
|
|
67dab81…
|
lmata
|
152 |
def test_fish_outputs_source_line(self): |
|
67dab81…
|
lmata
|
153 |
runner = CliRunner() |
|
67dab81…
|
lmata
|
154 |
result = runner.invoke(main, ["completions", "fish"]) |
|
67dab81…
|
lmata
|
155 |
assert result.exit_code == 0 |
|
67dab81…
|
lmata
|
156 |
assert "fish_source" in result.output |
|
67dab81…
|
lmata
|
157 |
|
|
67dab81…
|
lmata
|
158 |
def test_output_mentions_rc_file(self): |
|
67dab81…
|
lmata
|
159 |
runner = CliRunner() |
|
67dab81…
|
lmata
|
160 |
result = runner.invoke(main, ["completions", "bash"]) |
|
67dab81…
|
lmata
|
161 |
assert result.exit_code == 0 |
|
67dab81…
|
lmata
|
162 |
assert ".bashrc" in result.output |
|
67dab81…
|
lmata
|
163 |
|
|
67dab81…
|
lmata
|
164 |
def test_output_mentions_install_hint(self): |
|
67dab81…
|
lmata
|
165 |
runner = CliRunner() |
|
67dab81…
|
lmata
|
166 |
result = runner.invoke(main, ["completions", "zsh"]) |
|
67dab81…
|
lmata
|
167 |
assert result.exit_code == 0 |
|
67dab81…
|
lmata
|
168 |
assert "--install" in result.output |
|
67dab81…
|
lmata
|
169 |
|
|
67dab81…
|
lmata
|
170 |
# Invalid shell |
|
67dab81…
|
lmata
|
171 |
|
|
67dab81…
|
lmata
|
172 |
def test_invalid_shell_exits_nonzero(self): |
|
67dab81…
|
lmata
|
173 |
runner = CliRunner() |
|
67dab81…
|
lmata
|
174 |
result = runner.invoke(main, ["completions", "powershell"]) |
|
67dab81…
|
lmata
|
175 |
assert result.exit_code != 0 |
|
67dab81…
|
lmata
|
176 |
|
|
67dab81…
|
lmata
|
177 |
def test_invalid_shell_shows_error(self): |
|
67dab81…
|
lmata
|
178 |
runner = CliRunner() |
|
67dab81…
|
lmata
|
179 |
result = runner.invoke(main, ["completions", "powershell"]) |
|
67dab81…
|
lmata
|
180 |
assert result.exit_code != 0 |
|
67dab81…
|
lmata
|
181 |
# Click's Choice type reports the invalid value |
|
67dab81…
|
lmata
|
182 |
assert "powershell" in result.output or "invalid" in result.output.lower() |
|
67dab81…
|
lmata
|
183 |
|
|
67dab81…
|
lmata
|
184 |
# --install flag |
|
67dab81…
|
lmata
|
185 |
|
|
67dab81…
|
lmata
|
186 |
def test_install_bash_creates_file(self): |
|
67dab81…
|
lmata
|
187 |
runner = CliRunner() |
|
67dab81…
|
lmata
|
188 |
with runner.isolated_filesystem() as tmp: |
|
67dab81…
|
lmata
|
189 |
rc = str(Path(tmp) / ".bashrc") |
|
67dab81…
|
lmata
|
190 |
result = runner.invoke(main, ["completions", "bash", "--install", "--rc-path", rc]) |
|
67dab81…
|
lmata
|
191 |
assert result.exit_code == 0 |
|
67dab81…
|
lmata
|
192 |
assert Path(rc).exists() |
|
67dab81…
|
lmata
|
193 |
assert "bash_source" in Path(rc).read_text() |
|
67dab81…
|
lmata
|
194 |
|
|
67dab81…
|
lmata
|
195 |
def test_install_zsh_creates_file(self): |
|
67dab81…
|
lmata
|
196 |
runner = CliRunner() |
|
67dab81…
|
lmata
|
197 |
with runner.isolated_filesystem() as tmp: |
|
67dab81…
|
lmata
|
198 |
rc = str(Path(tmp) / ".zshrc") |
|
67dab81…
|
lmata
|
199 |
result = runner.invoke(main, ["completions", "zsh", "--install", "--rc-path", rc]) |
|
67dab81…
|
lmata
|
200 |
assert result.exit_code == 0 |
|
67dab81…
|
lmata
|
201 |
assert Path(rc).exists() |
|
67dab81…
|
lmata
|
202 |
|
|
67dab81…
|
lmata
|
203 |
def test_install_fish_creates_file(self): |
|
67dab81…
|
lmata
|
204 |
runner = CliRunner() |
|
67dab81…
|
lmata
|
205 |
with runner.isolated_filesystem() as tmp: |
|
67dab81…
|
lmata
|
206 |
rc = str(Path(tmp) / "config.fish") |
|
67dab81…
|
lmata
|
207 |
result = runner.invoke(main, ["completions", "fish", "--install", "--rc-path", rc]) |
|
67dab81…
|
lmata
|
208 |
assert result.exit_code == 0 |
|
67dab81…
|
lmata
|
209 |
assert Path(rc).exists() |
|
67dab81…
|
lmata
|
210 |
|
|
67dab81…
|
lmata
|
211 |
def test_install_shows_success_message(self): |
|
67dab81…
|
lmata
|
212 |
runner = CliRunner() |
|
67dab81…
|
lmata
|
213 |
with runner.isolated_filesystem() as tmp: |
|
67dab81…
|
lmata
|
214 |
rc = str(Path(tmp) / ".bashrc") |
|
67dab81…
|
lmata
|
215 |
result = runner.invoke(main, ["completions", "bash", "--install", "--rc-path", rc]) |
|
67dab81…
|
lmata
|
216 |
assert result.exit_code == 0 |
|
67dab81…
|
lmata
|
217 |
assert "installed" in result.output.lower() or "Completion" in result.output |
|
67dab81…
|
lmata
|
218 |
|
|
67dab81…
|
lmata
|
219 |
def test_install_shows_restart_hint(self): |
|
67dab81…
|
lmata
|
220 |
runner = CliRunner() |
|
67dab81…
|
lmata
|
221 |
with runner.isolated_filesystem() as tmp: |
|
67dab81…
|
lmata
|
222 |
rc = str(Path(tmp) / ".zshrc") |
|
67dab81…
|
lmata
|
223 |
result = runner.invoke(main, ["completions", "zsh", "--install", "--rc-path", rc]) |
|
67dab81…
|
lmata
|
224 |
assert result.exit_code == 0 |
|
67dab81…
|
lmata
|
225 |
assert "source" in result.output or "restart" in result.output.lower() |
|
67dab81…
|
lmata
|
226 |
|
|
67dab81…
|
lmata
|
227 |
def test_install_idempotent_via_cli(self): |
|
67dab81…
|
lmata
|
228 |
runner = CliRunner() |
|
67dab81…
|
lmata
|
229 |
with runner.isolated_filesystem() as tmp: |
|
67dab81…
|
lmata
|
230 |
rc = str(Path(tmp) / ".zshrc") |
|
67dab81…
|
lmata
|
231 |
runner.invoke(main, ["completions", "zsh", "--install", "--rc-path", rc]) |
|
67dab81…
|
lmata
|
232 |
runner.invoke(main, ["completions", "zsh", "--install", "--rc-path", rc]) |
|
67dab81…
|
lmata
|
233 |
content = Path(rc).read_text() |
|
67dab81…
|
lmata
|
234 |
line = 'eval "$(_NAVEGADOR_COMPLETE=zsh_source navegador)"' |
|
67dab81…
|
lmata
|
235 |
assert content.count(line) == 1 |