Skip to content

Commit a3f21cc

Browse files
committed
test(coverage): improve test coverage from 77% to 88%
- Add comprehensive error handling tests in test_error_scenarios.py - Test error suggestion functions for better user guidance - Add setup command functionality tests with proper mocking - Fix hanging test by mocking CLI download and subprocess operations - Remove unused imports and fix linting issues - Add tests for file error solutions and permission handling
1 parent 4e1204d commit a3f21cc

File tree

5 files changed

+167
-15
lines changed

5 files changed

+167
-15
lines changed

src/django_tailwind_cli/management/commands/tailwind.py

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -130,7 +130,7 @@ def _suggest_file_error_solutions(error_msg: str) -> None:
130130
typer.secho(" • Verify file permissions", fg=typer.colors.BLUE)
131131

132132

133-
def _suggest_permission_error_solutions(error_msg: str) -> None:
133+
def _suggest_permission_error_solutions(_error_msg: str) -> None:
134134
"""Provide actionable suggestions for permission errors."""
135135
typer.secho("\n💡 Solutions:", fg=typer.colors.YELLOW)
136136
typer.secho(" • Check file/directory permissions:", fg=typer.colors.BLUE)
@@ -535,7 +535,7 @@ def show_config():
535535
"""
536536
from django.core.management.color import color_style
537537

538-
style = color_style()
538+
color_style() # Initialize color styling
539539
config = get_config()
540540

541541
typer.secho("\n🔧 Django Tailwind CLI Configuration", fg=typer.colors.CYAN, bold=True)
@@ -643,7 +643,7 @@ def setup_guide():
643643

644644
typer.secho(f" ✅ django-tailwind-cli is installed (version: {__version__})", fg=typer.colors.GREEN)
645645
except ImportError:
646-
typer.secho(f" ❌ django-tailwind-cli not found", fg=typer.colors.RED)
646+
typer.secho(" ❌ django-tailwind-cli not found", fg=typer.colors.RED)
647647
typer.secho(" Run: pip install django-tailwind-cli", fg=typer.colors.BLUE)
648648
return
649649

@@ -677,7 +677,7 @@ def setup_guide():
677677
typer.secho("\n🔧 Step 3: Configuration Status", fg=typer.colors.YELLOW, bold=True)
678678
try:
679679
config = get_config()
680-
typer.secho(f" ✅ Configuration loaded successfully", fg=typer.colors.GREEN)
680+
typer.secho(" ✅ Configuration loaded successfully", fg=typer.colors.GREEN)
681681
typer.secho(f" Version: {config.version_str}", fg=typer.colors.BLUE)
682682
typer.secho(f" CLI Path: {config.cli_path}", fg=typer.colors.BLUE)
683683
typer.secho(f" CSS Output: {config.dist_css}", fg=typer.colors.BLUE)

tests/.gitignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
11
tailwind.config.js
22
assets/css/source.css
33
assets/css/tailwind.css
4+
.django_tailwind_cli

tests/test_additional_commands.py

Lines changed: 4 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -5,9 +5,7 @@
55
Also includes tests for error handling and edge cases.
66
"""
77

8-
import sys
98
from pathlib import Path
10-
from unittest.mock import Mock
119

1210
import pytest
1311
from django.conf import LazySettings
@@ -77,7 +75,7 @@ def test_config_command_with_auto_download_disabled(self, settings: LazySettings
7775

7876
assert "Auto Download: No" in captured.out
7977

80-
def test_config_command_file_paths(self, capsys: CaptureFixture[str], tmp_path: Path):
78+
def test_config_command_file_paths(self, capsys: CaptureFixture[str]):
8179
"""Test that config command shows file paths and existence status."""
8280
config = get_config()
8381

@@ -126,7 +124,7 @@ def test_config_command_download_url(self, capsys: CaptureFixture[str]):
126124
assert "Download URL:" in captured.out
127125
assert "github.com" in captured.out
128126

129-
def test_config_command_status_summary_ready(self, capsys: CaptureFixture[str], tmp_path: Path):
127+
def test_config_command_status_summary_ready(self, capsys: CaptureFixture[str]):
130128
"""Test config command shows ready status when files exist."""
131129
config = get_config()
132130

@@ -265,7 +263,7 @@ def successful_function():
265263
assert result == "success"
266264
mock_exit.assert_not_called()
267265

268-
def test_build_verbose_flag(self, mocker: MockerFixture, capsys: CaptureFixture[str]):
266+
def test_build_verbose_flag(self, capsys: CaptureFixture[str]):
269267
"""Test build command with verbose flag shows additional output."""
270268
config = get_config()
271269
config.cli_path.parent.mkdir(parents=True, exist_ok=True)
@@ -277,7 +275,7 @@ def test_build_verbose_flag(self, mocker: MockerFixture, capsys: CaptureFixture[
277275
# Should show verbose output about build process
278276
assert "Built production stylesheet" in captured.out
279277

280-
def test_watch_verbose_flag(self, mocker: MockerFixture, capsys: CaptureFixture[str]):
278+
def test_watch_verbose_flag(self, capsys: CaptureFixture[str]):
281279
"""Test watch command with verbose flag shows additional output."""
282280
config = get_config()
283281
config.cli_path.parent.mkdir(parents=True, exist_ok=True)

tests/test_error_scenarios.py

Lines changed: 154 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -692,3 +692,157 @@ def test_command_error_handling_decorator_edge_cases(self, settings: LazySetting
692692
# Test with unknown command - should raise SystemExit or CommandError
693693
with pytest.raises((CommandError, SystemExit)):
694694
call_command("tailwind", "nonexistent_command")
695+
696+
697+
class TestErrorSuggestionScenarios:
698+
"""Test error suggestion functions that provide user guidance."""
699+
700+
def test_suggest_command_error_solutions_staticfiles_dirs(self, capsys: CaptureFixture[str]):
701+
"""Test error suggestions for STATICFILES_DIRS issues."""
702+
from django_tailwind_cli.management.commands.tailwind import _suggest_command_error_solutions
703+
704+
_suggest_command_error_solutions("Error: STATICFILES_DIRS is not configured properly")
705+
706+
captured = capsys.readouterr()
707+
assert "💡 Solution:" in captured.out
708+
assert "STATICFILES_DIRS" in captured.out
709+
assert "BASE_DIR / 'assets'" in captured.out
710+
711+
def test_suggest_command_error_solutions_base_dir(self, capsys: CaptureFixture[str]):
712+
"""Test error suggestions for BASE_DIR issues."""
713+
from django_tailwind_cli.management.commands.tailwind import _suggest_command_error_solutions
714+
715+
_suggest_command_error_solutions("Error: BASE_DIR is not properly configured")
716+
717+
captured = capsys.readouterr()
718+
assert "💡 Solution:" in captured.out
719+
assert "BASE_DIR" in captured.out
720+
assert "Path(__file__).resolve().parent.parent" in captured.out
721+
722+
def test_suggest_command_error_solutions_tailwind_css_3x(self, capsys: CaptureFixture[str]):
723+
"""Test error suggestions for Tailwind CSS 3.x issues."""
724+
from django_tailwind_cli.management.commands.tailwind import _suggest_command_error_solutions
725+
726+
_suggest_command_error_solutions("Error: Tailwind CSS 3.x is not supported")
727+
728+
captured = capsys.readouterr()
729+
assert "💡 Solution:" in captured.out
730+
assert "django-tailwind-cli v2.21.1" in captured.out
731+
assert "Tailwind CSS 3.x" in captured.out
732+
733+
def test_suggest_command_error_solutions_version(self, capsys: CaptureFixture[str]):
734+
"""Test error suggestions for version issues."""
735+
from django_tailwind_cli.management.commands.tailwind import _suggest_command_error_solutions
736+
737+
_suggest_command_error_solutions("Error: invalid version specified")
738+
739+
captured = capsys.readouterr()
740+
assert "💡 Solution:" in captured.out
741+
assert "TAILWIND_CLI_VERSION" in captured.out
742+
assert "'latest'" in captured.out
743+
744+
def test_suggest_command_error_solutions_no_match(self, capsys: CaptureFixture[str]):
745+
"""Test error suggestions when no specific match is found."""
746+
from django_tailwind_cli.management.commands.tailwind import _suggest_command_error_solutions
747+
748+
_suggest_command_error_solutions("Some random error message")
749+
750+
captured = capsys.readouterr()
751+
# Should not print any suggestions for unknown errors
752+
assert captured.out == ""
753+
754+
def test_suggest_file_error_solutions_file_not_found(self, capsys: CaptureFixture[str]):
755+
"""Test file error suggestions for file not found issues."""
756+
from django_tailwind_cli.management.commands.tailwind import _suggest_file_error_solutions
757+
758+
_suggest_file_error_solutions("Error: file not found: /path/to/missing/file.css")
759+
760+
captured = capsys.readouterr()
761+
assert "💡 Suggestions:" in captured.out
762+
assert "CSS input file" in captured.out
763+
764+
def test_suggest_file_error_solutions_permission_denied(self, capsys: CaptureFixture[str]):
765+
"""Test file error suggestions for permission issues."""
766+
from django_tailwind_cli.management.commands.tailwind import _suggest_file_error_solutions
767+
768+
_suggest_file_error_solutions("Error: permission denied accessing file")
769+
770+
captured = capsys.readouterr()
771+
assert "💡 Suggestions:" in captured.out
772+
assert "file path is correct" in captured.out
773+
774+
def test_suggest_file_error_solutions_directory_not_found(self, capsys: CaptureFixture[str]):
775+
"""Test file error suggestions for directory issues."""
776+
from django_tailwind_cli.management.commands.tailwind import _suggest_file_error_solutions
777+
778+
_suggest_file_error_solutions("Error: directory not found or invalid")
779+
780+
captured = capsys.readouterr()
781+
assert "💡 Suggestions:" in captured.out
782+
assert "directory exists" in captured.out
783+
784+
785+
class TestSetupCommandScenarios:
786+
"""Test the setup command functionality."""
787+
788+
def test_setup_command_import_error_handling(
789+
self, settings: LazySettings, tmp_path: Path, capsys: CaptureFixture[str]
790+
):
791+
"""Test setup command when django-tailwind-cli cannot be imported."""
792+
settings.STATICFILES_DIRS = [tmp_path / "assets"]
793+
794+
# Mock the import of __version__ within the setup function
795+
with patch("django_tailwind_cli.__version__", side_effect=ImportError):
796+
call_command("tailwind", "setup")
797+
798+
captured = capsys.readouterr()
799+
assert "django-tailwind-cli not found" in captured.out or "Installation Check" in captured.out
800+
801+
def test_setup_command_missing_staticfiles_dirs(self, settings: LazySettings, capsys: CaptureFixture[str]):
802+
"""Test setup command when STATICFILES_DIRS is not configured."""
803+
settings.STATICFILES_DIRS = []
804+
settings.INSTALLED_APPS = ["django_tailwind_cli"]
805+
806+
call_command("tailwind", "setup")
807+
808+
captured = capsys.readouterr()
809+
assert "STATICFILES_DIRS not configured" in captured.out
810+
811+
def test_setup_command_configuration_error(
812+
self, settings: LazySettings, tmp_path: Path, capsys: CaptureFixture[str]
813+
):
814+
"""Test setup command when configuration loading fails."""
815+
settings.STATICFILES_DIRS = [tmp_path / "assets"]
816+
settings.INSTALLED_APPS = ["django_tailwind_cli"]
817+
818+
with patch(
819+
"django_tailwind_cli.management.commands.tailwind.get_config",
820+
side_effect=Exception("Config error"),
821+
):
822+
call_command("tailwind", "setup")
823+
824+
captured = capsys.readouterr()
825+
assert "Configuration error" in captured.out
826+
827+
def test_setup_command_success(self, settings: LazySettings, tmp_path: Path, capsys: CaptureFixture[str]):
828+
"""Test successful setup command execution."""
829+
settings.BASE_DIR = tmp_path
830+
settings.STATICFILES_DIRS = [tmp_path / "assets"]
831+
settings.INSTALLED_APPS = ["django_tailwind_cli"]
832+
833+
# Create the assets directory
834+
(tmp_path / "assets").mkdir(parents=True, exist_ok=True)
835+
836+
# Mock the CLI download and subprocess operations to prevent hanging
837+
with patch("django_tailwind_cli.management.commands.tailwind._download_cli"):
838+
with patch("subprocess.run") as mock_subprocess:
839+
# Mock successful subprocess run
840+
mock_result = Mock()
841+
mock_result.returncode = 0
842+
mock_subprocess.return_value = mock_result
843+
844+
call_command("tailwind", "setup")
845+
846+
captured = capsys.readouterr()
847+
assert "Django Tailwind CLI Setup Guide" in captured.out
848+
assert "Configuration loaded successfully" in captured.out

tests/test_management_commands.py

Lines changed: 4 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -157,9 +157,7 @@ def setup_process_tests(self, settings: LazySettings, tmp_path: Path, mocker: Mo
157157
mocker.patch("requests.get").return_value.content = b"fake-cli-binary"
158158

159159
# Mock the ProcessManager entirely to prevent real process creation
160-
self.mock_process_manager = mocker.patch(
161-
"django_tailwind_cli.management.commands.tailwind.ProcessManager"
162-
)
160+
self.mock_process_manager = mocker.patch("django_tailwind_cli.management.commands.tailwind.ProcessManager")
163161
mock_manager_instance = Mock()
164162
mock_manager_instance.start_concurrent_processes.return_value = None
165163
self.mock_process_manager.return_value = mock_manager_instance
@@ -182,10 +180,11 @@ def test_runserver_without_django_extensions(self):
182180
@pytest.mark.timeout(3)
183181
def test_runserver_with_django_extensions(self):
184182
"""Test runserver when django-extensions is available."""
183+
185184
# Mock both django-extensions and werkzeug as available
186185
def mock_find_spec(name):
187186
return Mock() if name in ["django_extensions", "werkzeug"] else None
188-
187+
189188
self.mock_find_spec.side_effect = mock_find_spec
190189

191190
call_command("tailwind", "runserver")
@@ -245,4 +244,4 @@ def test_list_templates_with_verbose(self, capsys: CaptureFixture[str]):
245244
pytestmark = [
246245
pytest.mark.filterwarnings("ignore::DeprecationWarning"),
247246
pytest.mark.filterwarnings("ignore::PendingDeprecationWarning"),
248-
]
247+
]

0 commit comments

Comments
 (0)