-
Notifications
You must be signed in to change notification settings - Fork 23
Expand file tree
/
Copy pathtest_additional_commands.py
More file actions
311 lines (236 loc) · 12.6 KB
/
Copy pathtest_additional_commands.py
File metadata and controls
311 lines (236 loc) · 12.6 KB
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
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
"""Tests for additional management commands with missing coverage.
This file focuses on testing commands that were missing from the main test suite,
specifically targeting the config, troubleshoot, and optimize commands.
Also includes tests for error handling and edge cases.
"""
from pathlib import Path
from collections.abc import Callable
from typing import Any
import pytest
from django.conf import LazySettings
from django.core.management import CommandError, call_command
from pytest import CaptureFixture
from pytest_mock import MockerFixture
from django_tailwind_cli.config import get_config
from django_tailwind_cli.management.commands.tailwind import handle_command_errors
def _call_directly(func: Any, *args: Any, **kwargs: Any) -> Any:
"""Helper that bypasses django.utils.autoreload.run_with_reloader in tests."""
return func(*args, **kwargs)
@pytest.fixture(autouse=True)
def configure_test_settings(settings: LazySettings, tmp_path: Path, mocker: MockerFixture):
"""Configure settings for all tests in this module."""
settings.BASE_DIR = tmp_path
settings.TAILWIND_CLI_PATH = tmp_path / "tailwindcss"
settings.TAILWIND_CLI_VERSION = "4.0.0"
settings.TAILWIND_CLI_SRC_CSS = tmp_path / "assets" / "css" / "input.css"
settings.STATICFILES_DIRS = (tmp_path / "assets",)
settings.TAILWIND_CLI_USE_DAISY_UI = False
settings.TAILWIND_CLI_AUTOMATIC_DOWNLOAD = True
# Mock subprocess to avoid actual CLI calls
mocker.patch("subprocess.run")
def mock_download(
url: str, filepath: Path, timeout: int = 30, progress_callback: Callable[[int, int, float], None] | None = None
) -> None:
filepath.parent.mkdir(parents=True, exist_ok=True)
filepath.write_bytes(b"fake binary content")
mocker.patch("django_tailwind_cli.utils.http.download_with_progress", side_effect=mock_download)
class TestConfigCommand:
"""Test the config command that displays configuration information."""
def test_config_command_basic_output(self, capsys: CaptureFixture[str]):
"""Test that config command displays basic configuration information."""
call_command("tailwind", "config")
captured = capsys.readouterr()
# Check for main sections
assert "🔧 Django Tailwind CLI Configuration" in captured.out
assert "📦 Version Information:" in captured.out
assert "📁 File Paths:" in captured.out
assert "⚙️ Django Settings:" in captured.out
assert "💻 Platform Information:" in captured.out
assert "🔗 Command URLs:" in captured.out
assert "📊 Status Summary:" in captured.out
def test_config_command_version_info(self, capsys: CaptureFixture[str]):
"""Test that config command shows version information."""
call_command("tailwind", "config")
captured = capsys.readouterr()
assert "Tailwind CSS Version: 4.0.0" in captured.out
assert "DaisyUI Enabled: No" in captured.out
assert "Auto Download: Yes" in captured.out
def test_config_command_with_daisy_ui(self, settings: LazySettings, capsys: CaptureFixture[str]):
"""Test config command shows correct DaisyUI status when enabled."""
settings.TAILWIND_CLI_USE_DAISY_UI = True
call_command("tailwind", "config")
captured = capsys.readouterr()
assert "DaisyUI Enabled: Yes" in captured.out
def test_config_command_with_auto_download_disabled(self, settings: LazySettings, capsys: CaptureFixture[str]):
"""Test config command shows correct auto download status when disabled."""
settings.TAILWIND_CLI_AUTOMATIC_DOWNLOAD = False
call_command("tailwind", "config")
captured = capsys.readouterr()
assert "Auto Download: No" in captured.out
def test_config_command_file_paths(self, capsys: CaptureFixture[str]):
"""Test that config command shows file paths and existence status."""
config = get_config()
# Create some files to test existence checks
config.src_css.parent.mkdir(parents=True, exist_ok=True)
config.src_css.write_text("@import 'tailwindcss';")
call_command("tailwind", "config")
captured = capsys.readouterr()
assert "CLI Binary:" in captured.out
assert "CSS Entries" in captured.out
assert "Source:" in captured.out
assert "Output:" in captured.out
assert "✅" in captured.out # At least one file exists
assert "❌" in captured.out # Some files don't exist
def test_config_command_django_settings(self, settings: LazySettings, capsys: CaptureFixture[str]):
"""Test that config command displays Django settings."""
settings.TAILWIND_CLI_PATH = "/custom/path"
settings.TAILWIND_CLI_SRC_CSS = "custom/input.css"
settings.TAILWIND_CLI_DIST_CSS = "custom/output.css"
call_command("tailwind", "config")
captured = capsys.readouterr()
assert "STATICFILES_DIRS:" in captured.out
assert "TAILWIND_CLI_VERSION: 4.0.0" in captured.out
assert "TAILWIND_CLI_PATH: /custom/path" in captured.out
assert "TAILWIND_CLI_SRC_CSS: custom/input.css" in captured.out
assert "TAILWIND_CLI_DIST_CSS: custom/output.css" in captured.out
def test_config_command_platform_info(self, capsys: CaptureFixture[str]):
"""Test that config command displays platform information."""
call_command("tailwind", "config")
captured = capsys.readouterr()
assert "Operating System:" in captured.out
assert "Architecture:" in captured.out
assert "Binary Extension:" in captured.out
def test_config_command_download_url(self, capsys: CaptureFixture[str]):
"""Test that config command displays download URL."""
call_command("tailwind", "config")
captured = capsys.readouterr()
assert "Download URL:" in captured.out
assert "github.com" in captured.out
def test_config_command_status_summary_ready(self, capsys: CaptureFixture[str]):
"""Test config command shows ready status when files exist."""
config = get_config()
# Create CLI binary and source CSS
config.cli_path.parent.mkdir(parents=True, exist_ok=True)
config.cli_path.write_text("fake binary")
config.src_css.parent.mkdir(parents=True, exist_ok=True)
config.src_css.write_text("@import 'tailwindcss';")
call_command("tailwind", "config")
captured = capsys.readouterr()
assert "✅ Ready to build CSS" in captured.out
def test_config_command_status_summary_setup_required(self, capsys: CaptureFixture[str]):
"""Test config command shows setup required when files missing."""
call_command("tailwind", "config")
captured = capsys.readouterr()
assert "⚠️ Setup required" in captured.out
assert "python manage.py tailwind download_cli" in captured.out
assert "python manage.py tailwind build" in captured.out
class TestTroubleshootCommand:
"""Test the troubleshoot command that provides debugging help."""
def test_troubleshoot_command_basic_output(self, capsys: CaptureFixture[str]):
"""Test that troubleshoot command displays help information."""
call_command("tailwind", "troubleshoot")
captured = capsys.readouterr()
# Check for main troubleshooting sections
assert "🔍 Django Tailwind CLI Troubleshooting Guide" in captured.out
assert "❓ Issue 1: CSS not updating in browser" in captured.out
assert "❓ Issue 2: Build/watch command fails" in captured.out
assert "🔧 Diagnostic Commands" in captured.out
class TestOptimizeCommand:
"""Test the optimize command that provides performance tips."""
def test_optimize_command_basic_output(self, capsys: CaptureFixture[str]):
"""Test that optimize command displays optimization tips."""
call_command("tailwind", "optimize")
captured = capsys.readouterr()
# Check for optimization content
assert "⚡ Django Tailwind CLI Performance Optimization" in captured.out
assert "🏗️ Build Performance" in captured.out
assert "👀 File Watching Efficiency" in captured.out
assert "🚀 Production Deployment" in captured.out
class TestErrorHandling:
"""Test error handling decorator and error scenarios."""
@pytest.fixture(autouse=True)
def _bypass_autoreload(self, mocker: MockerFixture):
"""Bypass django autoreload so watch tests run in-process."""
mocker.patch(
"django.utils.autoreload.run_with_reloader",
side_effect=_call_directly,
)
def test_handle_command_errors_decorator_command_error(self, mocker: MockerFixture):
"""Test error decorator handles CommandError properly."""
mock_exit = mocker.patch("sys.exit")
mock_secho = mocker.patch("typer.secho")
@handle_command_errors
def failing_function():
raise CommandError("Test command error")
failing_function()
mock_secho.assert_called()
mock_exit.assert_called_with(1)
# Check that error message is displayed
error_calls = [call for call in mock_secho.call_args_list if "❌ Command error:" in str(call)]
assert len(error_calls) > 0
def test_handle_command_errors_decorator_file_not_found(self, mocker: MockerFixture):
"""Test error decorator handles FileNotFoundError properly."""
mock_exit = mocker.patch("sys.exit")
mock_secho = mocker.patch("typer.secho")
@handle_command_errors
def failing_function():
raise FileNotFoundError("Test file not found")
failing_function()
mock_secho.assert_called()
mock_exit.assert_called_with(1)
# Check that error message is displayed
error_calls = [call for call in mock_secho.call_args_list if "❌ File not found:" in str(call)]
assert len(error_calls) > 0
def test_handle_command_errors_decorator_permission_error(self, mocker: MockerFixture):
"""Test error decorator handles PermissionError properly."""
mock_exit = mocker.patch("sys.exit")
mock_secho = mocker.patch("typer.secho")
@handle_command_errors
def failing_function():
raise PermissionError("Test permission denied")
failing_function()
mock_secho.assert_called()
mock_exit.assert_called_with(1)
# Check that error message is displayed
error_calls = [call for call in mock_secho.call_args_list if "❌ Permission denied:" in str(call)]
assert len(error_calls) > 0
def test_handle_command_errors_decorator_generic_exception(self, mocker: MockerFixture):
"""Test error decorator handles generic exceptions properly."""
mock_exit = mocker.patch("sys.exit")
mock_secho = mocker.patch("typer.secho")
@handle_command_errors
def failing_function():
raise ValueError("Test generic error")
failing_function()
mock_secho.assert_called()
mock_exit.assert_called_with(1)
# Check that error message is displayed
error_calls = [call for call in mock_secho.call_args_list if "❌ Unexpected error:" in str(call)]
assert len(error_calls) > 0
def test_handle_command_errors_decorator_success(self, mocker: MockerFixture):
"""Test error decorator doesn't interfere with successful execution."""
mock_exit = mocker.patch("sys.exit")
@handle_command_errors
def successful_function():
return "success"
result = successful_function()
assert result == "success"
mock_exit.assert_not_called()
def test_build_verbose_flag(self, capsys: CaptureFixture[str]):
"""Test build command with verbose flag shows additional output."""
config = get_config()
config.cli_path.parent.mkdir(parents=True, exist_ok=True)
config.cli_path.write_text("fake binary")
call_command("tailwind", "build", "--verbose")
captured = capsys.readouterr()
# Should show verbose output about build process
assert "Built production stylesheet" in captured.out
def test_watch_verbose_flag(self, capsys: CaptureFixture[str]):
"""Test watch command with verbose flag shows additional output."""
config = get_config()
config.cli_path.parent.mkdir(parents=True, exist_ok=True)
config.cli_path.write_text("fake binary")
call_command("tailwind", "watch", "--verbose")
captured = capsys.readouterr()
# Should show verbose output about watching process
assert "Watching for changes" in captured.out or "watch" in captured.out.lower()