- Improved playlist item handling with pagination, error logging, and dedicated request hooks. - Consolidated reusable helpers for track ID normalization, playlist validation, and removal logic. - Enhanced error handling by introducing `PlaylistNotFound` and `UserNotAuthenticated` exceptions. - Refined Pytest `qapp` fixture for headless CI compatibility and session-scoped reuse. - Replaced redundant fixtures in multiple test files for consistency. - Improved playlist management GUI with detailed debug logging for notifications and operations. - Added `deptry` configuration to handle transitive dependencies and ignored exclusions. - Updated Pytest and Tox configurations for consistent environment setup during CI/CD runs.
129 lines
3.6 KiB
Python
129 lines
3.6 KiB
Python
import os
|
|
import tempfile
|
|
from pathlib import Path
|
|
from unittest.mock import Mock
|
|
|
|
import pytest
|
|
from PySide6 import QtCore, QtGui, QtWidgets
|
|
from tidalapi import Album, Track, Video
|
|
from tidalapi.artist import Artist
|
|
|
|
|
|
@pytest.fixture(scope="session")
|
|
def qapp():
|
|
"""Provide a QApplication configured for headless CI (offscreen/minimal)."""
|
|
# Ensure headless backend for CI runners
|
|
os.environ.setdefault("QT_QPA_PLATFORM", "offscreen")
|
|
# Qt expects XDG_RUNTIME_DIR to exist with correct permissions
|
|
runtime_dir = Path(tempfile.mkdtemp(prefix="xdg-runtime-")).resolve()
|
|
os.environ.setdefault("XDG_RUNTIME_DIR", str(runtime_dir))
|
|
|
|
# Lower graphical requirements
|
|
QtCore.QCoreApplication.setAttribute(QtCore.Qt.AA_DisableHighDpiScaling, True)
|
|
|
|
app = QtWidgets.QApplication.instance() or QtWidgets.QApplication([])
|
|
yield app
|
|
|
|
|
|
# Backward compatible alias
|
|
qt_app = qapp
|
|
|
|
|
|
@pytest.fixture
|
|
def info_tab_widget(qt_app):
|
|
"""Create an InfoTabWidget instance for testing."""
|
|
from tidal_dl_ng.ui.info_tab_widget import InfoTabWidget
|
|
|
|
widget = InfoTabWidget()
|
|
yield widget
|
|
# InfoTabWidget inherits from QObject, not QWidget, so it doesn't have close()
|
|
widget.deleteLater()
|
|
|
|
|
|
@pytest.fixture
|
|
def mock_track():
|
|
"""Create a mock Track object with metadata."""
|
|
track = Mock(spec=Track)
|
|
track.name = "Bohemian Rhapsody"
|
|
track.title = "Bohemian Rhapsody"
|
|
track.full_name = "Bohemian Rhapsody"
|
|
track.version = "2011 Remaster"
|
|
track.duration = 354 # 5:54
|
|
track.explicit = False
|
|
track.popularity = 95
|
|
track.bpm = 72
|
|
track.isrc = "GBUM71029604"
|
|
track.bit_depth = 24
|
|
track.sample_rate = 96000
|
|
track.audio_modes = []
|
|
track.media_metadata_tags = []
|
|
track.available = True
|
|
track.id = 12345
|
|
|
|
# Mock album
|
|
mock_album = Mock(spec=Album)
|
|
mock_album.name = "A Night at the Opera"
|
|
mock_album.id = 67890
|
|
mock_album.release_date = Mock()
|
|
mock_album.release_date.strftime = Mock(return_value="1975-11-21")
|
|
track.album = mock_album
|
|
|
|
# Mock artists
|
|
mock_artist = Mock(spec=Artist)
|
|
mock_artist.name = "Queen"
|
|
mock_artist.roles = [Mock(name="main")]
|
|
track.artists = [mock_artist]
|
|
|
|
return track
|
|
|
|
|
|
@pytest.fixture
|
|
def mock_video():
|
|
"""Create a mock Video object."""
|
|
video = Mock(spec=Video)
|
|
video.name = "Thriller"
|
|
video.title = "Thriller"
|
|
video.full_name = "Thriller (Official Video)"
|
|
video.duration = 600
|
|
video.explicit = False
|
|
video.video_quality = "1080p"
|
|
video.available = True
|
|
video.id = 54321
|
|
|
|
mock_artist = Mock(spec=Artist)
|
|
mock_artist.name = "Michael Jackson"
|
|
video.artists = [mock_artist]
|
|
|
|
mock_album = Mock(spec=Album)
|
|
mock_album.name = "Thriller"
|
|
video.album = mock_album
|
|
|
|
return video
|
|
|
|
|
|
@pytest.fixture
|
|
def tree_view_setup(qt_app):
|
|
"""Create a tree view with model for testing hover manager."""
|
|
tree_view = QtWidgets.QTreeView()
|
|
source_model = QtGui.QStandardItemModel()
|
|
proxy_model = QtCore.QSortFilterProxyModel()
|
|
|
|
proxy_model.setSourceModel(source_model)
|
|
tree_view.setModel(proxy_model)
|
|
|
|
# Add some test data
|
|
source_model.setColumnCount(2)
|
|
for i in range(5):
|
|
item_index = QtGui.QStandardItem(f"Item {i}")
|
|
item_obj = QtGui.QStandardItem()
|
|
mock_track = Mock(spec=Track)
|
|
mock_track.name = f"Track {i}"
|
|
mock_track.id = i
|
|
item_obj.setData(mock_track, QtCore.Qt.ItemDataRole.UserRole)
|
|
source_model.appendRow([item_index, item_obj])
|
|
|
|
yield tree_view, proxy_model, source_model
|
|
|
|
tree_view.close()
|
|
tree_view.deleteLater()
|