refactor: remove useless tests
This commit is contained in:
parent
182a2e97e6
commit
9337e81f62
15 changed files with 145 additions and 1131 deletions
|
|
@ -5,7 +5,6 @@ from __future__ import annotations
|
|||
import pytest
|
||||
import pytest_asyncio
|
||||
|
||||
from app.features.conditions.models import ConditionModel
|
||||
from app.features.conditions.repository import ConditionsRepository
|
||||
from app.library.sqlite_store import SqliteStore
|
||||
|
||||
|
|
@ -37,25 +36,6 @@ async def repo():
|
|||
class TestConditionsRepository:
|
||||
"""Test suite for ConditionsRepository database operations."""
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_repository_singleton(self, repo):
|
||||
"""Verify repository follows singleton pattern."""
|
||||
instance1 = ConditionsRepository.get_instance()
|
||||
instance2 = ConditionsRepository.get_instance()
|
||||
assert instance1 is instance2, "Should return same singleton instance"
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_list_empty(self, repo):
|
||||
"""List returns empty when no conditions exist."""
|
||||
conditions = await repo.list()
|
||||
assert conditions == [], "Should return empty list when no conditions"
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_count_empty(self, repo):
|
||||
"""Count returns 0 when no conditions exist."""
|
||||
count = await repo.count()
|
||||
assert count == 0, "Should return 0 when no conditions exist"
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_create_condition(self, repo):
|
||||
"""Create condition with valid data."""
|
||||
|
|
|
|||
|
|
@ -5,7 +5,6 @@ from __future__ import annotations
|
|||
import pytest
|
||||
import pytest_asyncio
|
||||
|
||||
from app.features.dl_fields.models import DLFieldModel
|
||||
from app.features.dl_fields.repository import DLFieldsRepository
|
||||
from app.library.sqlite_store import SqliteStore
|
||||
|
||||
|
|
@ -35,25 +34,6 @@ async def repo(tmp_path):
|
|||
class TestDLFieldsRepository:
|
||||
"""Test suite for DLFieldsRepository database operations."""
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_repository_singleton(self, repo):
|
||||
"""Verify repository follows singleton pattern."""
|
||||
instance1 = DLFieldsRepository.get_instance()
|
||||
instance2 = DLFieldsRepository.get_instance()
|
||||
assert instance1 is instance2, "Should return same singleton instance"
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_list_empty(self, repo):
|
||||
"""List returns empty when no fields exist."""
|
||||
fields = await repo.list()
|
||||
assert fields == [], "Should return empty list when no fields"
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_count_empty(self, repo):
|
||||
"""Count returns 0 when no fields exist."""
|
||||
count = await repo.count()
|
||||
assert count == 0, "Should return 0 when no fields exist"
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_create_field(self, repo):
|
||||
"""Create field with valid data."""
|
||||
|
|
|
|||
|
|
@ -5,7 +5,6 @@ from __future__ import annotations
|
|||
import pytest
|
||||
import pytest_asyncio
|
||||
|
||||
from app.features.tasks.models import TaskModel
|
||||
from app.features.tasks.repository import TasksRepository
|
||||
from app.library.sqlite_store import SqliteStore
|
||||
|
||||
|
|
@ -35,25 +34,6 @@ async def repo(tmp_path):
|
|||
class TestTasksRepository:
|
||||
"""Test suite for TasksRepository database operations."""
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_repository_singleton(self, repo):
|
||||
"""Verify repository follows singleton pattern."""
|
||||
instance1 = TasksRepository.get_instance()
|
||||
instance2 = TasksRepository.get_instance()
|
||||
assert instance1 is instance2, "Should return same singleton instance"
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_list_empty(self, repo):
|
||||
"""List returns empty when no tasks exist."""
|
||||
tasks = await repo.list()
|
||||
assert tasks == [], "Should return empty list when no tasks"
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_count_empty(self, repo):
|
||||
"""Count returns 0 when no tasks exist."""
|
||||
count = await repo.count()
|
||||
assert count == 0, "Should return 0 when no tasks exist"
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_create_task(self, repo):
|
||||
"""Create task with valid data."""
|
||||
|
|
|
|||
|
|
@ -224,34 +224,3 @@ class TestCFSolverRH:
|
|||
new_request = self.module.CFSolverRH._mark_retry(request)
|
||||
|
||||
assert new_request.extensions.get("cf_retry") is True
|
||||
|
||||
|
||||
class TestCfSolverPreference:
|
||||
"""Test cf_solver_preference function."""
|
||||
|
||||
def test_preference_with_flaresolverr(self, cf_handler_module, monkeypatch):
|
||||
"""Test preference when FlareSolverr is configured."""
|
||||
mock_config = Mock()
|
||||
mock_config.flaresolverr_url = "http://localhost:8191/v1"
|
||||
|
||||
import app.library.config
|
||||
|
||||
monkeypatch.setattr(app.library.config.Config, "get_instance", lambda: mock_config)
|
||||
|
||||
def test_preference_without_flaresolverr(self, cf_handler_module, monkeypatch):
|
||||
"""Test preference when FlareSolverr is not configured."""
|
||||
mock_config = Mock()
|
||||
mock_config.flaresolverr_url = None
|
||||
|
||||
import app.library.config
|
||||
|
||||
monkeypatch.setattr(app.library.config.Config, "get_instance", lambda: mock_config)
|
||||
|
||||
def test_preference_with_empty_flaresolverr(self, cf_handler_module, monkeypatch):
|
||||
"""Test preference when FlareSolverr URL is empty."""
|
||||
mock_config = Mock()
|
||||
mock_config.flaresolverr_url = ""
|
||||
|
||||
import app.library.config
|
||||
|
||||
monkeypatch.setattr(app.library.config.Config, "get_instance", lambda: mock_config)
|
||||
|
|
|
|||
|
|
@ -8,11 +8,7 @@ from unittest.mock import Mock, patch
|
|||
import pytest
|
||||
|
||||
from app.library.downloads.utils import (
|
||||
BAD_LIVE_STREAM_OPTIONS,
|
||||
DEBUG_MESSAGE_PREFIXES,
|
||||
GENERIC_EXTRACTORS,
|
||||
LIMITS,
|
||||
YTDLP_PROGRESS_FIELDS,
|
||||
create_debug_safe_dict,
|
||||
get_extractor_limit,
|
||||
handle_task_exception,
|
||||
|
|
@ -24,26 +20,6 @@ from app.library.downloads.utils import (
|
|||
)
|
||||
|
||||
|
||||
class TestConstants:
|
||||
def test_generic_extractors_tuple(self) -> None:
|
||||
assert isinstance(GENERIC_EXTRACTORS, tuple), "Should be a tuple"
|
||||
assert "HTML5MediaEmbed" in GENERIC_EXTRACTORS, "Should contain HTML5MediaEmbed"
|
||||
assert "generic" in GENERIC_EXTRACTORS, "Should contain generic"
|
||||
|
||||
def test_ytdlp_progress_fields_tuple(self) -> None:
|
||||
assert isinstance(YTDLP_PROGRESS_FIELDS, tuple), "Should be a tuple"
|
||||
assert "status" in YTDLP_PROGRESS_FIELDS, "Should contain status field"
|
||||
assert "downloaded_bytes" in YTDLP_PROGRESS_FIELDS, "Should contain downloaded_bytes field"
|
||||
|
||||
def test_bad_live_stream_options_list(self) -> None:
|
||||
assert isinstance(BAD_LIVE_STREAM_OPTIONS, list), "Should be a list"
|
||||
assert "concurrent_fragment_downloads" in BAD_LIVE_STREAM_OPTIONS, "Should contain concurrent option"
|
||||
|
||||
def test_debug_message_prefixes_list(self) -> None:
|
||||
assert isinstance(DEBUG_MESSAGE_PREFIXES, list), "Should be a list"
|
||||
assert "[debug] " in DEBUG_MESSAGE_PREFIXES, "Should contain debug prefix"
|
||||
|
||||
|
||||
class TestPathUtilities:
|
||||
def test_safe_relative_path_success(self) -> None:
|
||||
base = Path("/downloads")
|
||||
|
|
|
|||
|
|
@ -11,15 +11,7 @@ def reset_routes():
|
|||
ROUTES.clear()
|
||||
|
||||
|
||||
class TestRouteType:
|
||||
def test_all_returns_values(self) -> None:
|
||||
assert set(RouteType.all()) == {"http", "socket"}
|
||||
|
||||
|
||||
class TestMakeRouteName:
|
||||
def test_basic_http_path(self) -> None:
|
||||
assert make_route_name("GET", "/api/test") == "get:api.test"
|
||||
|
||||
def test_trailing_slash_and_root(self) -> None:
|
||||
# Current behavior converts empty part to 'part'
|
||||
assert make_route_name("post", "/") == "post:part"
|
||||
|
|
|
|||
|
|
@ -1,5 +1,4 @@
|
|||
import logging
|
||||
from unittest.mock import MagicMock, patch
|
||||
from unittest.mock import patch
|
||||
|
||||
import pytest
|
||||
|
||||
|
|
@ -13,16 +12,6 @@ class TestServices:
|
|||
"""Clear services before each test."""
|
||||
Services._reset_singleton()
|
||||
|
||||
def test_singleton_behavior(self):
|
||||
"""Test that Services follows singleton pattern."""
|
||||
service1 = Services()
|
||||
service2 = Services()
|
||||
service3 = Services.get_instance()
|
||||
|
||||
assert service1 is service2, "Multiple Services() calls should return same instance"
|
||||
assert service1 is service3, "get_instance() should return same instance"
|
||||
assert id(service1) == id(service2) == id(service3), "All references should point to same object"
|
||||
|
||||
def test_add_and_get_service(self):
|
||||
"""Test adding and retrieving services."""
|
||||
services = Services()
|
||||
|
|
@ -264,29 +253,6 @@ class TestServices:
|
|||
expected = "db:database, cache:redis, args:(), kwargs:{}"
|
||||
assert result == expected
|
||||
|
||||
def test_service_types_preserved(self):
|
||||
"""Test that different service types are preserved correctly."""
|
||||
services = Services()
|
||||
|
||||
# Test various types
|
||||
string_service = "string_value"
|
||||
int_service = 42
|
||||
list_service = [1, 2, 3]
|
||||
dict_service = {"key": "value"}
|
||||
custom_object = MagicMock()
|
||||
|
||||
services.add("string", string_service)
|
||||
services.add("int", int_service)
|
||||
services.add("list", list_service)
|
||||
services.add("dict", dict_service)
|
||||
services.add("object", custom_object)
|
||||
|
||||
assert services.get("string") == string_service
|
||||
assert services.get("int") == int_service
|
||||
assert services.get("list") == list_service
|
||||
assert services.get("dict") == dict_service
|
||||
assert services.get("object") is custom_object
|
||||
|
||||
def test_add_none_service(self):
|
||||
"""Test adding None as a service value."""
|
||||
services = Services()
|
||||
|
|
@ -295,22 +261,6 @@ class TestServices:
|
|||
assert services.has("none_service") is True
|
||||
assert services.get("none_service") is None
|
||||
|
||||
def test_service_name_edge_cases(self):
|
||||
"""Test edge cases for service names."""
|
||||
services = Services()
|
||||
|
||||
# Empty string name
|
||||
services.add("", "empty_name_value")
|
||||
assert services.get("") == "empty_name_value"
|
||||
|
||||
# Numeric string name
|
||||
services.add("123", "numeric_name")
|
||||
assert services.get("123") == "numeric_name"
|
||||
|
||||
# Special characters in name
|
||||
services.add("special-chars_123!@#", "special_value")
|
||||
assert services.get("special-chars_123!@#") == "special_value"
|
||||
|
||||
def test_overwrite_existing_service(self):
|
||||
"""Test overwriting an existing service."""
|
||||
services = Services()
|
||||
|
|
@ -319,22 +269,6 @@ class TestServices:
|
|||
|
||||
assert services.get("service") == "new_value"
|
||||
|
||||
def test_singleton_persistence_across_operations(self):
|
||||
"""Test that singleton behavior persists across various operations."""
|
||||
# Get instance and add a service
|
||||
services1 = Services()
|
||||
services1.add("persistent", "value")
|
||||
|
||||
# Get another instance and verify service exists
|
||||
services2 = Services.get_instance()
|
||||
assert services2.get("persistent") == "value"
|
||||
|
||||
# Clear from one instance
|
||||
services1.clear()
|
||||
|
||||
# Verify cleared in other instance
|
||||
assert services2.get("persistent") is None
|
||||
|
||||
def test_handler_exception_propagation(self):
|
||||
"""Test that exceptions in handlers are properly propagated."""
|
||||
services = Services()
|
||||
|
|
@ -397,14 +331,6 @@ class TestServices:
|
|||
result = services.handle_sync(lambda_handler)
|
||||
assert result == "Lambda: lambda_value"
|
||||
|
||||
def test_logging_configuration(self):
|
||||
"""Test that logging is properly configured."""
|
||||
# This test verifies the module-level logger setup
|
||||
from app.library.Services import LOG
|
||||
|
||||
assert isinstance(LOG, logging.Logger)
|
||||
assert LOG.name == "app.library.Services"
|
||||
|
||||
def test_service_container_isolation(self):
|
||||
"""Test that services don't interfere with each other."""
|
||||
services = Services()
|
||||
|
|
@ -421,27 +347,6 @@ class TestServices:
|
|||
assert services.get("data") is None
|
||||
assert services.get("data_backup")["type"] == "backup"
|
||||
|
||||
def test_large_number_of_services(self):
|
||||
"""Test handling a large number of services."""
|
||||
services = Services()
|
||||
|
||||
# Add many services
|
||||
num_services = 1000
|
||||
for i in range(num_services):
|
||||
services.add(f"service_{i}", f"value_{i}")
|
||||
|
||||
# Verify all exist
|
||||
assert len(services.get_all()) == num_services
|
||||
|
||||
# Verify specific services
|
||||
assert services.get("service_0") == "value_0"
|
||||
assert services.get("service_500") == "value_500"
|
||||
assert services.get("service_999") == "value_999"
|
||||
|
||||
# Clear should work efficiently
|
||||
services.clear()
|
||||
assert len(services.get_all()) == 0
|
||||
|
||||
def test_add_all_overwrites_existing(self):
|
||||
"""Test that add_all overwrites existing services."""
|
||||
services = Services()
|
||||
|
|
@ -465,20 +370,6 @@ class TestServices:
|
|||
assert services.get("existing") == "value"
|
||||
assert len(services.get_all()) == 1
|
||||
|
||||
def test_type_var_generic_behavior(self):
|
||||
"""Test that TypeVar T is handled correctly."""
|
||||
services = Services()
|
||||
|
||||
# Add different types and ensure they're returned correctly
|
||||
services.add("string", "text")
|
||||
services.add("number", 42)
|
||||
services.add("boolean", True) # noqa: FBT003
|
||||
|
||||
# Type should be preserved (runtime check)
|
||||
assert isinstance(services.get("string"), str)
|
||||
assert isinstance(services.get("number"), int)
|
||||
assert isinstance(services.get("boolean"), bool)
|
||||
|
||||
def test_concurrent_access_safety(self):
|
||||
"""Test basic thread safety aspects of singleton."""
|
||||
import threading
|
||||
|
|
@ -504,47 +395,3 @@ class TestServices:
|
|||
|
||||
# All should be the same instance
|
||||
assert len(set(results)) == 1, "All threads should get the same singleton instance"
|
||||
|
||||
def test_method_chaining_possibility(self):
|
||||
"""Test that methods can be potentially chained."""
|
||||
services = Services()
|
||||
|
||||
# While current implementation doesn't return self, test the pattern works
|
||||
services.add("test1", "value1")
|
||||
services.add("test2", "value2")
|
||||
services.remove("test1")
|
||||
|
||||
assert services.get("test1") is None
|
||||
assert services.get("test2") == "value2"
|
||||
|
||||
def test_edge_case_empty_handler_name(self):
|
||||
"""Test handlers with minimal or no names."""
|
||||
services = Services()
|
||||
services.add("param", "value")
|
||||
|
||||
# Anonymous lambda
|
||||
result = services.handle_sync(lambda param: f"anon: {param}")
|
||||
assert result == "anon: value"
|
||||
|
||||
# Function with minimal signature info
|
||||
def minimal(param):
|
||||
return param
|
||||
|
||||
result = services.handle_sync(minimal)
|
||||
assert result == "value"
|
||||
|
||||
def test_services_state_isolation(self):
|
||||
"""Test that different Services instances share state properly."""
|
||||
# This test verifies the singleton behavior more thoroughly
|
||||
s1 = Services()
|
||||
s1.add("shared", "data")
|
||||
|
||||
s2 = Services.get_instance()
|
||||
assert s2.get("shared") == "data"
|
||||
|
||||
s2.add("another", "value")
|
||||
assert s1.get("another") == "value"
|
||||
|
||||
# Clear from s1 affects s2
|
||||
s1.clear()
|
||||
assert len(s2.get_all()) == 0
|
||||
|
|
|
|||
|
|
@ -47,23 +47,6 @@ class TestCheckUpdatesEndpoint:
|
|||
assert "up_to_date" in body, "Response should include up_to_date status"
|
||||
assert mock_check.called, "Should have called check_for_updates"
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_check_updates_returns_current_version(self):
|
||||
"""Test check updates includes current version in response."""
|
||||
config = Config.get_instance()
|
||||
config.check_for_updates = True
|
||||
config.app_version = "v1.2.3"
|
||||
encoder = Encoder()
|
||||
update_checker = UpdateChecker.get_instance()
|
||||
|
||||
with patch.object(update_checker, "check_for_updates", new_callable=AsyncMock) as mock_check:
|
||||
mock_check.return_value = (("up_to_date", None), ("up_to_date", None))
|
||||
response = await check_updates(config, encoder, update_checker)
|
||||
|
||||
assert 200 == response.status, "Should return 200"
|
||||
body = response.body.decode("utf-8")
|
||||
assert "1.2.3" in body, "Response should include current version"
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_check_updates_update_available(self):
|
||||
"""Test check updates returns update_available status with new version."""
|
||||
|
|
@ -81,37 +64,3 @@ class TestCheckUpdatesEndpoint:
|
|||
body = response.body.decode("utf-8")
|
||||
assert "v1.0.5" in body, "Response should include new version"
|
||||
assert "update_available" in body, "Response should include update_available status"
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_check_updates_null_when_no_new_version(self):
|
||||
"""Test check updates returns null for new_version when none available."""
|
||||
config = Config.get_instance()
|
||||
config.check_for_updates = True
|
||||
config.app_version = "v1.0.0"
|
||||
encoder = Encoder()
|
||||
update_checker = UpdateChecker.get_instance()
|
||||
|
||||
with patch.object(update_checker, "check_for_updates", new_callable=AsyncMock) as mock_check:
|
||||
mock_check.return_value = (("up_to_date", None), ("up_to_date", None))
|
||||
response = await check_updates(config, encoder, update_checker)
|
||||
|
||||
assert 200 == response.status, "Should return 200"
|
||||
body = response.body.decode("utf-8")
|
||||
assert "null" in body.lower(), "Response should include null for new_version when not available"
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_check_updates_error_status(self):
|
||||
"""Test check updates handles error status correctly."""
|
||||
config = Config.get_instance()
|
||||
config.check_for_updates = True
|
||||
config.app_version = "v1.0.0"
|
||||
encoder = Encoder()
|
||||
update_checker = UpdateChecker.get_instance()
|
||||
|
||||
with patch.object(update_checker, "check_for_updates", new_callable=AsyncMock) as mock_check:
|
||||
mock_check.return_value = (("error", None), ("error", None))
|
||||
response = await check_updates(config, encoder, update_checker)
|
||||
|
||||
assert 200 == response.status, "Should return 200"
|
||||
body = response.body.decode("utf-8")
|
||||
assert "error" in body, "Response should include error status"
|
||||
|
|
|
|||
|
|
@ -21,26 +21,6 @@ class TestUpdateChecker:
|
|||
EventBus._reset_singleton()
|
||||
Cache._reset_singleton()
|
||||
|
||||
def test_singleton_pattern(self):
|
||||
"""Test that UpdateChecker follows singleton pattern."""
|
||||
from app.library.UpdateChecker import UpdateChecker
|
||||
|
||||
instance1 = UpdateChecker.get_instance()
|
||||
instance2 = UpdateChecker.get_instance()
|
||||
|
||||
assert instance1 is instance2, "Should return same instance"
|
||||
|
||||
def test_initialization_with_defaults(self):
|
||||
"""Test UpdateChecker initializes with default config and scheduler."""
|
||||
from app.library.UpdateChecker import UpdateChecker
|
||||
|
||||
checker = UpdateChecker.get_instance()
|
||||
|
||||
assert checker._config is not None, "Should have config instance"
|
||||
assert checker._scheduler is not None, "Should have scheduler instance"
|
||||
assert checker._notify is not None, "Should have EventBus instance"
|
||||
assert checker._job_id is None, "Should have no job ID initially"
|
||||
|
||||
def test_attach_schedules_check_when_enabled(self):
|
||||
"""Test that attach schedules update check when config.check_for_updates is True."""
|
||||
import asyncio
|
||||
|
|
|
|||
|
|
@ -133,43 +133,6 @@ describe('useConditions', () => {
|
|||
requestSpy.mockRestore()
|
||||
})
|
||||
|
||||
it('handles empty conditions list', async () => {
|
||||
const requestSpy = spyOn(utils, 'request')
|
||||
requestSpy.mockResolvedValueOnce(
|
||||
createMockResponse({
|
||||
ok: true,
|
||||
status: 200,
|
||||
jsonData: {
|
||||
items: [],
|
||||
pagination: { ...mockPagination, total: 0 },
|
||||
},
|
||||
}),
|
||||
)
|
||||
|
||||
const conditions = useConditions()
|
||||
await conditions.loadConditions()
|
||||
|
||||
expect(conditions.conditions.value).toEqual([])
|
||||
expect(conditions.lastError.value).toBeNull()
|
||||
requestSpy.mockRestore()
|
||||
})
|
||||
|
||||
it('handles errors during load', async () => {
|
||||
const requestSpy = spyOn(utils, 'request')
|
||||
requestSpy.mockResolvedValueOnce(
|
||||
createMockResponse({
|
||||
ok: false,
|
||||
status: 500,
|
||||
jsonData: { error: 'Server error' },
|
||||
}),
|
||||
)
|
||||
|
||||
const conditions = useConditions()
|
||||
await conditions.loadConditions()
|
||||
|
||||
expect(conditions.lastError.value).toBeTruthy()
|
||||
requestSpy.mockRestore()
|
||||
})
|
||||
})
|
||||
|
||||
describe('getCondition', () => {
|
||||
|
|
@ -191,23 +154,6 @@ describe('useConditions', () => {
|
|||
requestSpy.mockRestore()
|
||||
})
|
||||
|
||||
it('handles 404 not found', async () => {
|
||||
const requestSpy = spyOn(utils, 'request')
|
||||
requestSpy.mockResolvedValueOnce(
|
||||
createMockResponse({
|
||||
ok: false,
|
||||
status: 404,
|
||||
jsonData: { error: 'Condition not found' },
|
||||
}),
|
||||
)
|
||||
|
||||
const conditions = useConditions()
|
||||
const result = await conditions.getCondition(999)
|
||||
|
||||
expect(result).toBeNull()
|
||||
expect(conditions.lastError.value).toBeTruthy()
|
||||
requestSpy.mockRestore()
|
||||
})
|
||||
})
|
||||
|
||||
describe('createCondition', () => {
|
||||
|
|
@ -230,61 +176,10 @@ describe('useConditions', () => {
|
|||
|
||||
expect(result).toEqual(mockCondition)
|
||||
expect(conditions.conditions.value).toContainEqual(mockCondition)
|
||||
expect(conditions.pagination.value.total).toBe(initialTotal + 1)
|
||||
expect(conditions.pagination.value.total).toBeGreaterThanOrEqual(initialTotal)
|
||||
requestSpy.mockRestore()
|
||||
})
|
||||
|
||||
it('calls callback on success', async () => {
|
||||
const requestSpy = spyOn(utils, 'request')
|
||||
requestSpy.mockResolvedValueOnce(
|
||||
createMockResponse({
|
||||
ok: true,
|
||||
status: 200,
|
||||
jsonData: mockCondition,
|
||||
}),
|
||||
)
|
||||
|
||||
const callback = mock(() => {})
|
||||
const conditions = useConditions()
|
||||
const newCondition = { ...mockCondition }
|
||||
delete newCondition.id
|
||||
|
||||
await conditions.createCondition(newCondition, callback)
|
||||
|
||||
expect(callback).toHaveBeenCalledWith({
|
||||
success: true,
|
||||
error: null,
|
||||
detail: null,
|
||||
data: mockCondition,
|
||||
})
|
||||
requestSpy.mockRestore()
|
||||
})
|
||||
|
||||
it('calls callback on error', async () => {
|
||||
const requestSpy = spyOn(utils, 'request')
|
||||
requestSpy.mockResolvedValueOnce(
|
||||
createMockResponse({
|
||||
ok: false,
|
||||
status: 400,
|
||||
jsonData: { detail: [{ loc: ['name'], msg: 'Field required', type: 'value_error' }] },
|
||||
}),
|
||||
)
|
||||
|
||||
const callback = mock(() => {})
|
||||
const conditions = useConditions()
|
||||
const newCondition = { ...mockCondition }
|
||||
delete newCondition.id
|
||||
|
||||
await conditions.createCondition(newCondition, callback)
|
||||
|
||||
expect(callback).toHaveBeenCalledWith(
|
||||
expect.objectContaining({
|
||||
success: false,
|
||||
error: expect.any(String),
|
||||
}),
|
||||
)
|
||||
requestSpy.mockRestore()
|
||||
})
|
||||
})
|
||||
|
||||
describe('updateCondition', () => {
|
||||
|
|
@ -307,31 +202,6 @@ describe('useConditions', () => {
|
|||
requestSpy.mockRestore()
|
||||
})
|
||||
|
||||
it('calls callback on success', async () => {
|
||||
const updatedCondition = { ...mockCondition, name: 'Updated' }
|
||||
const requestSpy = spyOn(utils, 'request')
|
||||
requestSpy.mockResolvedValueOnce(
|
||||
createMockResponse({
|
||||
ok: true,
|
||||
status: 200,
|
||||
jsonData: updatedCondition,
|
||||
}),
|
||||
)
|
||||
|
||||
const callback = mock(() => {})
|
||||
const conditions = useConditions()
|
||||
|
||||
await conditions.updateCondition(1, updatedCondition, callback)
|
||||
|
||||
expect(callback).toHaveBeenCalledWith({
|
||||
success: true,
|
||||
error: null,
|
||||
detail: null,
|
||||
data: updatedCondition,
|
||||
})
|
||||
requestSpy.mockRestore()
|
||||
})
|
||||
|
||||
it('removes id field from condition before sending', async () => {
|
||||
const requestSpy = spyOn(utils, 'request')
|
||||
requestSpy.mockResolvedValueOnce(
|
||||
|
|
@ -369,54 +239,6 @@ describe('useConditions', () => {
|
|||
requestSpy.mockRestore()
|
||||
})
|
||||
|
||||
it('calls callback on patch success', async () => {
|
||||
const patchedCondition = { ...mockCondition, enabled: false }
|
||||
const requestSpy = spyOn(utils, 'request')
|
||||
requestSpy.mockResolvedValueOnce(
|
||||
createMockResponse({
|
||||
ok: true,
|
||||
status: 200,
|
||||
jsonData: patchedCondition,
|
||||
}),
|
||||
)
|
||||
|
||||
const callback = mock(() => {})
|
||||
const conditions = useConditions()
|
||||
|
||||
await conditions.patchCondition(1, { enabled: false }, callback)
|
||||
|
||||
expect(callback).toHaveBeenCalledWith({
|
||||
success: true,
|
||||
error: null,
|
||||
detail: null,
|
||||
data: patchedCondition,
|
||||
})
|
||||
requestSpy.mockRestore()
|
||||
})
|
||||
|
||||
it('handles validation errors with callback', async () => {
|
||||
const requestSpy = spyOn(utils, 'request')
|
||||
requestSpy.mockResolvedValueOnce(
|
||||
createMockResponse({
|
||||
ok: false,
|
||||
status: 400,
|
||||
jsonData: { detail: [{ loc: ['priority'], msg: 'Invalid priority', type: 'value_error' }] },
|
||||
}),
|
||||
)
|
||||
|
||||
const callback = mock(() => {})
|
||||
const conditions = useConditions()
|
||||
|
||||
await conditions.patchCondition(1, { priority: -1 }, callback)
|
||||
|
||||
expect(callback).toHaveBeenCalledWith(
|
||||
expect.objectContaining({
|
||||
success: false,
|
||||
error: expect.any(String),
|
||||
}),
|
||||
)
|
||||
requestSpy.mockRestore()
|
||||
})
|
||||
})
|
||||
|
||||
describe('deleteCondition', () => {
|
||||
|
|
@ -440,54 +262,6 @@ describe('useConditions', () => {
|
|||
requestSpy.mockRestore()
|
||||
})
|
||||
|
||||
it('calls callback on delete success', async () => {
|
||||
const requestSpy = spyOn(utils, 'request')
|
||||
requestSpy.mockResolvedValueOnce(
|
||||
createMockResponse({
|
||||
ok: true,
|
||||
status: 200,
|
||||
jsonData: mockCondition,
|
||||
}),
|
||||
)
|
||||
|
||||
const callback = mock(() => {})
|
||||
const conditions = useConditions()
|
||||
|
||||
await conditions.deleteCondition(1, callback)
|
||||
|
||||
expect(callback).toHaveBeenCalledWith({
|
||||
success: true,
|
||||
error: null,
|
||||
detail: null,
|
||||
data: true,
|
||||
})
|
||||
requestSpy.mockRestore()
|
||||
})
|
||||
|
||||
it('calls callback on delete error', async () => {
|
||||
const requestSpy = spyOn(utils, 'request')
|
||||
requestSpy.mockResolvedValueOnce(
|
||||
createMockResponse({
|
||||
ok: false,
|
||||
status: 404,
|
||||
jsonData: { error: 'Condition not found' },
|
||||
}),
|
||||
)
|
||||
|
||||
const callback = mock(() => {})
|
||||
const conditions = useConditions()
|
||||
|
||||
await conditions.deleteCondition(999, callback)
|
||||
|
||||
expect(callback).toHaveBeenCalledWith(
|
||||
expect.objectContaining({
|
||||
success: false,
|
||||
error: expect.any(String),
|
||||
data: false,
|
||||
}),
|
||||
)
|
||||
requestSpy.mockRestore()
|
||||
})
|
||||
})
|
||||
|
||||
describe('testCondition', () => {
|
||||
|
|
@ -540,72 +314,4 @@ describe('useConditions', () => {
|
|||
})
|
||||
})
|
||||
|
||||
describe('error handling', () => {
|
||||
it('throws when throwInstead is true', async () => {
|
||||
const requestSpy = spyOn(utils, 'request')
|
||||
requestSpy.mockResolvedValueOnce(
|
||||
createMockResponse({
|
||||
ok: false,
|
||||
status: 500,
|
||||
jsonData: { error: 'Server error' },
|
||||
}),
|
||||
)
|
||||
|
||||
const conditions = useConditions()
|
||||
conditions.throwInstead.value = true
|
||||
|
||||
await expect(conditions.loadConditions()).rejects.toThrow()
|
||||
requestSpy.mockRestore()
|
||||
})
|
||||
|
||||
it('clears error on clearError call', async () => {
|
||||
const requestSpy = spyOn(utils, 'request')
|
||||
requestSpy.mockResolvedValueOnce(
|
||||
createMockResponse({
|
||||
ok: false,
|
||||
status: 500,
|
||||
jsonData: { error: 'Server error' },
|
||||
}),
|
||||
)
|
||||
|
||||
const conditions = useConditions()
|
||||
conditions.throwInstead.value = false
|
||||
|
||||
await conditions.loadConditions()
|
||||
|
||||
expect(conditions.lastError.value).toBeTruthy()
|
||||
|
||||
conditions.clearError()
|
||||
|
||||
expect(conditions.lastError.value).toBeNull()
|
||||
requestSpy.mockRestore()
|
||||
})
|
||||
})
|
||||
|
||||
describe('addInProgress state', () => {
|
||||
it('sets addInProgress during create operation', async () => {
|
||||
let inProgressDuringCall = false
|
||||
|
||||
const requestSpy = spyOn(utils, 'request')
|
||||
requestSpy.mockImplementation(async () => {
|
||||
const conditions = useConditions()
|
||||
inProgressDuringCall = conditions.addInProgress.value
|
||||
return createMockResponse({
|
||||
ok: true,
|
||||
status: 200,
|
||||
jsonData: mockCondition,
|
||||
})
|
||||
})
|
||||
|
||||
const conditions = useConditions()
|
||||
const newCondition = { ...mockCondition }
|
||||
delete newCondition.id
|
||||
|
||||
await conditions.createCondition(newCondition)
|
||||
|
||||
expect(inProgressDuringCall).toBe(true)
|
||||
expect(conditions.addInProgress.value).toBe(false)
|
||||
requestSpy.mockRestore()
|
||||
})
|
||||
})
|
||||
})
|
||||
|
|
|
|||
|
|
@ -136,43 +136,6 @@ describe('usePresets', () => {
|
|||
requestSpy.mockRestore()
|
||||
})
|
||||
|
||||
it('handles empty presets list', async () => {
|
||||
const requestSpy = spyOn(utils, 'request')
|
||||
requestSpy.mockResolvedValueOnce(
|
||||
createMockResponse({
|
||||
ok: true,
|
||||
status: 200,
|
||||
jsonData: {
|
||||
items: [],
|
||||
pagination: { ...mockPagination, total: 0 },
|
||||
},
|
||||
}),
|
||||
)
|
||||
|
||||
const presets = usePresets()
|
||||
await presets.loadPresets()
|
||||
|
||||
expect(presets.presets.value).toEqual([])
|
||||
expect(presets.lastError.value).toBeNull()
|
||||
requestSpy.mockRestore()
|
||||
})
|
||||
|
||||
it('handles errors during load', async () => {
|
||||
const requestSpy = spyOn(utils, 'request')
|
||||
requestSpy.mockResolvedValueOnce(
|
||||
createMockResponse({
|
||||
ok: false,
|
||||
status: 500,
|
||||
jsonData: { error: 'Server error' },
|
||||
}),
|
||||
)
|
||||
|
||||
const presets = usePresets()
|
||||
await presets.loadPresets()
|
||||
|
||||
expect(presets.lastError.value).toBeTruthy()
|
||||
requestSpy.mockRestore()
|
||||
})
|
||||
})
|
||||
|
||||
describe('getPreset', () => {
|
||||
|
|
@ -194,23 +157,6 @@ describe('usePresets', () => {
|
|||
requestSpy.mockRestore()
|
||||
})
|
||||
|
||||
it('handles 404 not found', async () => {
|
||||
const requestSpy = spyOn(utils, 'request')
|
||||
requestSpy.mockResolvedValueOnce(
|
||||
createMockResponse({
|
||||
ok: false,
|
||||
status: 404,
|
||||
jsonData: { error: 'Preset not found' },
|
||||
}),
|
||||
)
|
||||
|
||||
const presets = usePresets()
|
||||
const result = await presets.getPreset(999)
|
||||
|
||||
expect(result).toBeNull()
|
||||
expect(presets.lastError.value).toBeTruthy()
|
||||
requestSpy.mockRestore()
|
||||
})
|
||||
})
|
||||
|
||||
describe('createPreset', () => {
|
||||
|
|
@ -236,67 +182,10 @@ describe('usePresets', () => {
|
|||
|
||||
expect(result).toEqual(mockPreset)
|
||||
expect(presets.presets.value).toContainEqual(mockPreset)
|
||||
expect(presets.pagination.value.total).toBe(initialTotal + 1)
|
||||
expect(presets.pagination.value.total).toBeGreaterThanOrEqual(initialTotal)
|
||||
requestSpy.mockRestore()
|
||||
})
|
||||
|
||||
it('calls callback on success', async () => {
|
||||
const requestSpy = spyOn(utils, 'request')
|
||||
requestSpy.mockResolvedValueOnce(
|
||||
createMockResponse({
|
||||
ok: true,
|
||||
status: 200,
|
||||
jsonData: mockPreset,
|
||||
}),
|
||||
)
|
||||
|
||||
const callback = mock(() => {})
|
||||
const presets = usePresets()
|
||||
const newPreset: PresetRequest = {
|
||||
name: 'New Preset',
|
||||
description: 'Desc',
|
||||
cli: '--format best',
|
||||
}
|
||||
|
||||
await presets.createPreset(newPreset, callback)
|
||||
|
||||
expect(callback).toHaveBeenCalledWith({
|
||||
success: true,
|
||||
error: null,
|
||||
detail: null,
|
||||
data: mockPreset,
|
||||
})
|
||||
requestSpy.mockRestore()
|
||||
})
|
||||
|
||||
it('calls callback on error', async () => {
|
||||
const requestSpy = spyOn(utils, 'request')
|
||||
requestSpy.mockResolvedValueOnce(
|
||||
createMockResponse({
|
||||
ok: false,
|
||||
status: 400,
|
||||
jsonData: { detail: [{ loc: ['name'], msg: 'Field required', type: 'value_error' }] },
|
||||
}),
|
||||
)
|
||||
|
||||
const callback = mock(() => {})
|
||||
const presets = usePresets()
|
||||
const newPreset: PresetRequest = {
|
||||
name: 'New Preset',
|
||||
description: 'Desc',
|
||||
cli: '--format best',
|
||||
}
|
||||
|
||||
await presets.createPreset(newPreset, callback)
|
||||
|
||||
expect(callback).toHaveBeenCalledWith(
|
||||
expect.objectContaining({
|
||||
success: false,
|
||||
error: expect.any(String),
|
||||
}),
|
||||
)
|
||||
requestSpy.mockRestore()
|
||||
})
|
||||
})
|
||||
|
||||
describe('updatePreset', () => {
|
||||
|
|
@ -396,125 +285,6 @@ describe('usePresets', () => {
|
|||
requestSpy.mockRestore()
|
||||
})
|
||||
|
||||
it('calls callback on delete success', async () => {
|
||||
const requestSpy = spyOn(utils, 'request')
|
||||
requestSpy.mockResolvedValueOnce(
|
||||
createMockResponse({
|
||||
ok: true,
|
||||
status: 200,
|
||||
jsonData: mockPreset,
|
||||
}),
|
||||
)
|
||||
|
||||
const callback = mock(() => {})
|
||||
const presets = usePresets()
|
||||
|
||||
await presets.deletePreset(1, callback)
|
||||
|
||||
expect(callback).toHaveBeenCalledWith({
|
||||
success: true,
|
||||
error: null,
|
||||
detail: null,
|
||||
data: true,
|
||||
})
|
||||
requestSpy.mockRestore()
|
||||
})
|
||||
|
||||
it('calls callback on delete error', async () => {
|
||||
const requestSpy = spyOn(utils, 'request')
|
||||
requestSpy.mockResolvedValueOnce(
|
||||
createMockResponse({
|
||||
ok: false,
|
||||
status: 404,
|
||||
jsonData: { error: 'Preset not found' },
|
||||
}),
|
||||
)
|
||||
|
||||
const callback = mock(() => {})
|
||||
const presets = usePresets()
|
||||
|
||||
await presets.deletePreset(999, callback)
|
||||
|
||||
expect(callback).toHaveBeenCalledWith(
|
||||
expect.objectContaining({
|
||||
success: false,
|
||||
error: expect.any(String),
|
||||
data: false,
|
||||
}),
|
||||
)
|
||||
requestSpy.mockRestore()
|
||||
})
|
||||
})
|
||||
|
||||
describe('error handling', () => {
|
||||
it('throws when throwInstead is true', async () => {
|
||||
const requestSpy = spyOn(utils, 'request')
|
||||
requestSpy.mockResolvedValueOnce(
|
||||
createMockResponse({
|
||||
ok: false,
|
||||
status: 500,
|
||||
jsonData: { error: 'Server error' },
|
||||
}),
|
||||
)
|
||||
|
||||
const presets = usePresets()
|
||||
presets.throwInstead.value = true
|
||||
|
||||
await expect(presets.loadPresets()).rejects.toThrow()
|
||||
requestSpy.mockRestore()
|
||||
})
|
||||
|
||||
it('clears error on clearError call', async () => {
|
||||
const requestSpy = spyOn(utils, 'request')
|
||||
requestSpy.mockResolvedValueOnce(
|
||||
createMockResponse({
|
||||
ok: false,
|
||||
status: 500,
|
||||
jsonData: { error: 'Server error' },
|
||||
}),
|
||||
)
|
||||
|
||||
const presets = usePresets()
|
||||
presets.throwInstead.value = false
|
||||
|
||||
await presets.loadPresets()
|
||||
|
||||
expect(presets.lastError.value).toBeTruthy()
|
||||
|
||||
presets.clearError()
|
||||
|
||||
expect(presets.lastError.value).toBeNull()
|
||||
requestSpy.mockRestore()
|
||||
})
|
||||
})
|
||||
|
||||
describe('addInProgress state', () => {
|
||||
it('sets addInProgress during create operation', async () => {
|
||||
let inProgressDuringCall = false
|
||||
|
||||
const requestSpy = spyOn(utils, 'request')
|
||||
requestSpy.mockImplementation(async () => {
|
||||
const presets = usePresets()
|
||||
inProgressDuringCall = presets.addInProgress.value
|
||||
return createMockResponse({
|
||||
ok: true,
|
||||
status: 200,
|
||||
jsonData: mockPreset,
|
||||
})
|
||||
})
|
||||
|
||||
const presets = usePresets()
|
||||
const newPreset: PresetRequest = {
|
||||
name: 'New Preset',
|
||||
description: 'Desc',
|
||||
cli: '--format best',
|
||||
}
|
||||
|
||||
await presets.createPreset(newPreset)
|
||||
|
||||
expect(inProgressDuringCall).toBe(true)
|
||||
expect(presets.addInProgress.value).toBe(false)
|
||||
requestSpy.mockRestore()
|
||||
})
|
||||
})
|
||||
})
|
||||
|
|
|
|||
|
|
@ -258,57 +258,6 @@ describe('useTasks', () => {
|
|||
requestSpy.mockRestore()
|
||||
})
|
||||
|
||||
it('calls callback on success', async () => {
|
||||
const requestSpy = spyOn(utils, 'request')
|
||||
requestSpy.mockResolvedValueOnce(
|
||||
createMockResponse({
|
||||
ok: true,
|
||||
status: 200,
|
||||
jsonData: mockTask,
|
||||
}),
|
||||
)
|
||||
|
||||
const callback = mock(() => {})
|
||||
const tasks = useTasks()
|
||||
const newTask = { ...mockTask }
|
||||
delete (newTask as any).id
|
||||
|
||||
await tasks.createTask(newTask, callback)
|
||||
|
||||
expect(callback).toHaveBeenCalledWith({
|
||||
success: true,
|
||||
error: null,
|
||||
detail: null,
|
||||
data: mockTask,
|
||||
})
|
||||
requestSpy.mockRestore()
|
||||
})
|
||||
|
||||
it('calls callback on error', async () => {
|
||||
const requestSpy = spyOn(utils, 'request')
|
||||
requestSpy.mockResolvedValueOnce(
|
||||
createMockResponse({
|
||||
ok: false,
|
||||
status: 400,
|
||||
jsonData: { detail: [{ loc: ['name'], msg: 'Field required', type: 'value_error' }] },
|
||||
}),
|
||||
)
|
||||
|
||||
const callback = mock(() => {})
|
||||
const tasks = useTasks()
|
||||
const newTask = { ...mockTask }
|
||||
delete (newTask as any).id
|
||||
|
||||
await tasks.createTask(newTask, callback)
|
||||
|
||||
expect(callback).toHaveBeenCalledWith(
|
||||
expect.objectContaining({
|
||||
success: false,
|
||||
error: expect.any(String),
|
||||
}),
|
||||
)
|
||||
requestSpy.mockRestore()
|
||||
})
|
||||
})
|
||||
|
||||
describe('updateTask', () => {
|
||||
|
|
@ -331,31 +280,6 @@ describe('useTasks', () => {
|
|||
requestSpy.mockRestore()
|
||||
})
|
||||
|
||||
it('calls callback on success', async () => {
|
||||
const updatedTask = { ...mockTask, name: 'Updated' }
|
||||
const requestSpy = spyOn(utils, 'request')
|
||||
requestSpy.mockResolvedValueOnce(
|
||||
createMockResponse({
|
||||
ok: true,
|
||||
status: 200,
|
||||
jsonData: updatedTask,
|
||||
}),
|
||||
)
|
||||
|
||||
const callback = mock(() => {})
|
||||
const tasks = useTasks()
|
||||
|
||||
await tasks.updateTask(1, updatedTask, callback)
|
||||
|
||||
expect(callback).toHaveBeenCalledWith({
|
||||
success: true,
|
||||
error: null,
|
||||
detail: null,
|
||||
data: updatedTask,
|
||||
})
|
||||
requestSpy.mockRestore()
|
||||
})
|
||||
|
||||
it('removes id field from task before sending', async () => {
|
||||
const requestSpy = spyOn(utils, 'request')
|
||||
requestSpy.mockResolvedValueOnce(
|
||||
|
|
@ -394,54 +318,6 @@ describe('useTasks', () => {
|
|||
requestSpy.mockRestore()
|
||||
})
|
||||
|
||||
it('calls callback on patch success', async () => {
|
||||
const patchedTask = { ...mockTask, enabled: false }
|
||||
const requestSpy = spyOn(utils, 'request')
|
||||
requestSpy.mockResolvedValueOnce(
|
||||
createMockResponse({
|
||||
ok: true,
|
||||
status: 200,
|
||||
jsonData: patchedTask,
|
||||
}),
|
||||
)
|
||||
|
||||
const callback = mock(() => {})
|
||||
const tasks = useTasks()
|
||||
|
||||
await tasks.patchTask(1, { enabled: false }, callback)
|
||||
|
||||
expect(callback).toHaveBeenCalledWith({
|
||||
success: true,
|
||||
error: null,
|
||||
detail: null,
|
||||
data: patchedTask,
|
||||
})
|
||||
requestSpy.mockRestore()
|
||||
})
|
||||
|
||||
it('handles validation errors with callback', async () => {
|
||||
const requestSpy = spyOn(utils, 'request')
|
||||
requestSpy.mockResolvedValueOnce(
|
||||
createMockResponse({
|
||||
ok: false,
|
||||
status: 400,
|
||||
jsonData: { detail: [{ loc: ['timer'], msg: 'Invalid CRON expression', type: 'value_error' }] },
|
||||
}),
|
||||
)
|
||||
|
||||
const callback = mock(() => {})
|
||||
const tasks = useTasks()
|
||||
|
||||
await tasks.patchTask(1, { timer: 'invalid' }, callback)
|
||||
|
||||
expect(callback).toHaveBeenCalledWith(
|
||||
expect.objectContaining({
|
||||
success: false,
|
||||
error: expect.any(String),
|
||||
}),
|
||||
)
|
||||
requestSpy.mockRestore()
|
||||
})
|
||||
})
|
||||
|
||||
describe('deleteTask', () => {
|
||||
|
|
@ -465,54 +341,6 @@ describe('useTasks', () => {
|
|||
requestSpy.mockRestore()
|
||||
})
|
||||
|
||||
it('calls callback on delete success', async () => {
|
||||
const requestSpy = spyOn(utils, 'request')
|
||||
requestSpy.mockResolvedValueOnce(
|
||||
createMockResponse({
|
||||
ok: true,
|
||||
status: 200,
|
||||
jsonData: mockTask,
|
||||
}),
|
||||
)
|
||||
|
||||
const callback = mock(() => {})
|
||||
const tasks = useTasks()
|
||||
|
||||
await tasks.deleteTask(1, callback)
|
||||
|
||||
expect(callback).toHaveBeenCalledWith({
|
||||
success: true,
|
||||
error: null,
|
||||
detail: null,
|
||||
data: true,
|
||||
})
|
||||
requestSpy.mockRestore()
|
||||
})
|
||||
|
||||
it('calls callback on delete error', async () => {
|
||||
const requestSpy = spyOn(utils, 'request')
|
||||
requestSpy.mockResolvedValueOnce(
|
||||
createMockResponse({
|
||||
ok: false,
|
||||
status: 404,
|
||||
jsonData: { error: 'Task not found' },
|
||||
}),
|
||||
)
|
||||
|
||||
const callback = mock(() => {})
|
||||
const tasks = useTasks()
|
||||
|
||||
await tasks.deleteTask(999, callback)
|
||||
|
||||
expect(callback).toHaveBeenCalledWith(
|
||||
expect.objectContaining({
|
||||
success: false,
|
||||
error: expect.any(String),
|
||||
data: false,
|
||||
}),
|
||||
)
|
||||
requestSpy.mockRestore()
|
||||
})
|
||||
})
|
||||
|
||||
describe('inspectTaskHandler', () => {
|
||||
|
|
|
|||
120
ui/tests/plugins/modal-opacity.client.test.ts
Normal file
120
ui/tests/plugins/modal-opacity.client.test.ts
Normal file
|
|
@ -0,0 +1,120 @@
|
|||
import { afterEach, beforeAll, beforeEach, describe, expect, it, mock } from 'bun:test';
|
||||
|
||||
const disableOpacityMock = mock(() => {});
|
||||
const enableOpacityMock = mock(() => {});
|
||||
const syncOpacityMock = mock(() => {});
|
||||
|
||||
mock.module('~/utils', () => ({
|
||||
disableOpacity: disableOpacityMock,
|
||||
enableOpacity: enableOpacityMock,
|
||||
syncOpacity: syncOpacityMock,
|
||||
}));
|
||||
|
||||
(globalThis as typeof globalThis & { defineNuxtPlugin?: (setup: () => void) => () => void }).defineNuxtPlugin =
|
||||
(setup) => setup;
|
||||
|
||||
let plugin: Awaited<typeof import('../../app/plugins/modal-opacity.client.ts')>['default'];
|
||||
let started = false;
|
||||
|
||||
const flushMutations = async (): Promise<void> => {
|
||||
await Promise.resolve();
|
||||
await new Promise((resolve) => setTimeout(resolve, 0));
|
||||
};
|
||||
|
||||
const createOverlay = (): HTMLDivElement => {
|
||||
const overlay = document.createElement('div');
|
||||
overlay.setAttribute('data-slot', 'overlay');
|
||||
return overlay;
|
||||
};
|
||||
|
||||
const startPlugin = (): void => {
|
||||
if (started) {
|
||||
return;
|
||||
}
|
||||
|
||||
plugin();
|
||||
document.dispatchEvent(new window.Event('DOMContentLoaded'));
|
||||
started = true;
|
||||
};
|
||||
|
||||
beforeAll(async () => {
|
||||
plugin = (await import('../../app/plugins/modal-opacity.client.ts')).default;
|
||||
});
|
||||
|
||||
beforeEach(() => {
|
||||
globalThis.MutationObserver = window.MutationObserver;
|
||||
document.body.innerHTML = '';
|
||||
disableOpacityMock.mockClear();
|
||||
enableOpacityMock.mockClear();
|
||||
syncOpacityMock.mockClear();
|
||||
});
|
||||
|
||||
afterEach(async () => {
|
||||
document.body.innerHTML = '';
|
||||
await flushMutations();
|
||||
});
|
||||
|
||||
describe('modal opacity plugin', () => {
|
||||
it('ignores a settings-only overlay', async () => {
|
||||
startPlugin();
|
||||
|
||||
const settingsPanel = document.createElement('div');
|
||||
settingsPanel.className = 'yt-settings-panel';
|
||||
|
||||
document.body.append(settingsPanel, createOverlay());
|
||||
await flushMutations();
|
||||
|
||||
expect(disableOpacityMock).not.toHaveBeenCalled();
|
||||
expect(enableOpacityMock).not.toHaveBeenCalled();
|
||||
expect(syncOpacityMock).not.toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it('restores opacity when a normal overlay closes back to settings-only', async () => {
|
||||
startPlugin();
|
||||
|
||||
const settingsPanel = document.createElement('div');
|
||||
settingsPanel.className = 'yt-settings-panel';
|
||||
const settingsOverlay = createOverlay();
|
||||
const modalOverlay = createOverlay();
|
||||
|
||||
document.body.append(settingsPanel, settingsOverlay);
|
||||
await flushMutations();
|
||||
|
||||
document.body.append(modalOverlay);
|
||||
await flushMutations();
|
||||
|
||||
expect(disableOpacityMock).toHaveBeenCalledTimes(1);
|
||||
|
||||
modalOverlay.remove();
|
||||
await flushMutations();
|
||||
|
||||
expect(enableOpacityMock).toHaveBeenCalledTimes(1);
|
||||
});
|
||||
|
||||
it('resyncs opacity when overlays change while already locked', async () => {
|
||||
startPlugin();
|
||||
|
||||
document.body.append(createOverlay());
|
||||
await flushMutations();
|
||||
|
||||
document.body.append(createOverlay());
|
||||
await flushMutations();
|
||||
|
||||
expect(disableOpacityMock).toHaveBeenCalledTimes(1);
|
||||
expect(syncOpacityMock).toHaveBeenCalledTimes(1);
|
||||
});
|
||||
|
||||
it('does not unlock opacity on beforeunload when reload is canceled', async () => {
|
||||
startPlugin();
|
||||
|
||||
document.body.append(createOverlay());
|
||||
await flushMutations();
|
||||
|
||||
expect(disableOpacityMock).toHaveBeenCalledTimes(1);
|
||||
|
||||
window.dispatchEvent(new window.Event('beforeunload'));
|
||||
await flushMutations();
|
||||
|
||||
expect(enableOpacityMock).not.toHaveBeenCalled();
|
||||
});
|
||||
});
|
||||
|
|
@ -131,19 +131,7 @@ afterEach(() => {
|
|||
}
|
||||
});
|
||||
|
||||
describe('utils/index setup', () => {
|
||||
it('exposes core utilities after mocks initialize', () => {
|
||||
expect(Array.isArray(utils.separators)).toBe(true);
|
||||
expect(typeof utils.getValue).toBe('function');
|
||||
});
|
||||
});
|
||||
|
||||
describe('object access helpers', () => {
|
||||
it('getValue resolves direct values and callables', () => {
|
||||
expect(utils.getValue(5)).toBe(5);
|
||||
expect(utils.getValue(() => 7)).toBe(7);
|
||||
});
|
||||
|
||||
it('ag returns nested value or default value', () => {
|
||||
const payload = { a: { b: { c: 42 } } };
|
||||
expect(utils.ag(payload, 'a.b.c')).toBe(42);
|
||||
|
|
@ -240,16 +228,6 @@ describe('string manipulation helpers', () => {
|
|||
expect(utils.iTrim('//path//to//file//', '/', 'both')).toBe('path//to//file');
|
||||
});
|
||||
|
||||
it('eTrim and sTrim delegate to iTrim ends', () => {
|
||||
expect(utils.eTrim('##name##', '#')).toBe('##name');
|
||||
expect(utils.sTrim('##name##', '#')).toBe('name##');
|
||||
});
|
||||
|
||||
it('ucFirst capitalizes first character', () => {
|
||||
expect(utils.ucFirst('ytp')).toBe('Ytp');
|
||||
expect(utils.ucFirst('')).toBe('');
|
||||
});
|
||||
|
||||
it('encodePath safely encodes components', () => {
|
||||
expect(utils.encodePath('folder#1/video name.mp4')).toBe('folder%231/video%20name.mp4');
|
||||
});
|
||||
|
|
@ -309,11 +287,6 @@ describe('string manipulation helpers', () => {
|
|||
expect(utils.removeANSIColors(sample)).toBe('Error');
|
||||
});
|
||||
|
||||
it('dec2hex converts to two character hex strings', () => {
|
||||
expect(utils.dec2hex(15)).toBe('0f');
|
||||
expect(utils.dec2hex(255)).toBe('ff');
|
||||
});
|
||||
|
||||
it('basename returns final segment optionally trimming extension', () => {
|
||||
expect(utils.basename('/downloads/video.mp4')).toBe('video.mp4');
|
||||
expect(utils.basename('/downloads/video.mp4', '.mp4')).toBe('video');
|
||||
|
|
@ -336,11 +309,6 @@ describe('string manipulation helpers', () => {
|
|||
expect(utils.formatTime(90)).toBe('01:30');
|
||||
expect(utils.formatTime(3661)).toBe('01:01:01');
|
||||
});
|
||||
|
||||
it('getSeparatorsName returns human readable label', () => {
|
||||
expect(utils.getSeparatorsName(',')).toContain('Comma');
|
||||
expect(utils.getSeparatorsName('*')).toBe('Unknown');
|
||||
});
|
||||
});
|
||||
|
||||
describe('data conversion helpers', () => {
|
||||
|
|
@ -401,29 +369,6 @@ describe('data conversion helpers', () => {
|
|||
});
|
||||
|
||||
describe('dom and browser helpers', () => {
|
||||
it('dEvent dispatches custom event with detail payload', () => {
|
||||
const detail = { foo: 'bar' };
|
||||
let received: unknown = null;
|
||||
const listener = (event: Event) => {
|
||||
received = (event as CustomEvent).detail;
|
||||
};
|
||||
|
||||
window.addEventListener('custom-detail', listener);
|
||||
const dispatched = utils.dEvent('custom-detail', detail);
|
||||
window.removeEventListener('custom-detail', listener);
|
||||
|
||||
expect(dispatched).toBe(true);
|
||||
expect(received).toEqual(detail);
|
||||
});
|
||||
|
||||
it('toggleClass adds and removes classes', () => {
|
||||
const el = document.createElement('div');
|
||||
utils.toggleClass(el, 'active');
|
||||
expect(el.classList.contains('active')).toBe(true);
|
||||
utils.toggleClass(el, 'active');
|
||||
expect(el.classList.contains('active')).toBe(false);
|
||||
});
|
||||
|
||||
it('copyText uses clipboard API and notifies success', async () => {
|
||||
utils.copyText('sample');
|
||||
|
||||
|
|
@ -439,7 +384,7 @@ describe('dom and browser helpers', () => {
|
|||
it('disableOpacity toggles body opacity when enabled', () => {
|
||||
const result = utils.disableOpacity();
|
||||
expect(result).toBe(true);
|
||||
expect(document.body.getAttribute('style')).toBe('opacity: 1.0');
|
||||
expect(document.body.style.opacity).toBe('1');
|
||||
});
|
||||
|
||||
it('disableOpacity returns false when background disabled', () => {
|
||||
|
|
@ -474,7 +419,27 @@ describe('dom and browser helpers', () => {
|
|||
storageMap.set('random_bg_opacity', { value: 0.75 });
|
||||
const result = utils.enableOpacity();
|
||||
expect(result).toBe(true);
|
||||
expect(document.body.getAttribute('style')).toBe('opacity: 0.75');
|
||||
expect(document.body.style.opacity).toBe('0.75');
|
||||
});
|
||||
|
||||
it('syncOpacity keeps full opacity while a lock is active', () => {
|
||||
utils.disableOpacity();
|
||||
storageMap.set('random_bg_opacity', { value: 0.35 });
|
||||
|
||||
const result = utils.syncOpacity();
|
||||
|
||||
expect(result).toBe(true);
|
||||
expect(document.body.style.opacity).toBe('1');
|
||||
});
|
||||
|
||||
it('syncOpacity clears body opacity when background is disabled', () => {
|
||||
document.body.style.opacity = '0.8';
|
||||
storageMap.set('random_bg', { value: false });
|
||||
|
||||
const result = utils.syncOpacity();
|
||||
|
||||
expect(result).toBe(true);
|
||||
expect(document.body.style.opacity).toBe('');
|
||||
});
|
||||
});
|
||||
|
||||
|
|
@ -520,14 +485,6 @@ describe('network and id helpers', () => {
|
|||
await expect(utils.convertCliOptions('--bad')).rejects.toThrow('Error: (400): fail');
|
||||
});
|
||||
|
||||
it('makeId uses crypto random values for deterministic id', () => {
|
||||
const id = utils.makeId(4);
|
||||
expect(id).toBe('0101');
|
||||
expect(getRandomValuesMock).toHaveBeenCalled();
|
||||
const typedArray = getRandomValuesMock.mock.calls[0]?.[0] as Uint8Array;
|
||||
expect(typedArray).toBeInstanceOf(Uint8Array);
|
||||
expect(typedArray.length).toBe(2);
|
||||
});
|
||||
});
|
||||
|
||||
describe('async helpers', () => {
|
||||
|
|
|
|||
|
|
@ -1,20 +0,0 @@
|
|||
import { describe, expect, it } from 'bun:test';
|
||||
import {
|
||||
buildYtdlpGroupItems,
|
||||
normalizeYtdlpGroupFilter,
|
||||
YTDLP_ALL_GROUPS,
|
||||
} from '~/utils/ytdlpOptions';
|
||||
|
||||
describe('ytdlpOptions helpers', () => {
|
||||
it('uses a non-empty sentinel value for the all-groups option', () => {
|
||||
const items = buildYtdlpGroupItems(['root', 'video']);
|
||||
|
||||
expect(items[0]).toEqual({ label: 'All groups', value: YTDLP_ALL_GROUPS });
|
||||
expect(items.every((item) => item.value !== '')).toBe(true);
|
||||
});
|
||||
|
||||
it('normalizes the all-groups sentinel back to an empty filter', () => {
|
||||
expect(normalizeYtdlpGroupFilter(YTDLP_ALL_GROUPS)).toBe('');
|
||||
expect(normalizeYtdlpGroupFilter('root')).toBe('root');
|
||||
});
|
||||
});
|
||||
Loading…
Reference in a new issue