Files
UmbraEdit/tests/extensions/test_versioninfo.py
B.D. Murphy f421136f75 house: update website URLs to umbratools.com/umbraedit
- Update all website references from umbraedit.io to umbratools.com/umbraedit
- Update documentation URLs from docs.umbraedit.io to umbratools.com/umbraedit/docs
- Update release URLs from releases.umbraedit.io to umbratools.com/umbraedit/releases
- Update __domain__ to umbratools.com for Qt organization domain
- Update README first image to reference UmbraEdit icon SVG
- Update README link text from umbraedit.io to UmbraEdit
- Update test versioninfo expected text
2026-11-06 07:26:55 -08:00

221 lines
6.7 KiB
Python

"""
UmbraEdit - Version Info Widget Tests
=======================================
This file is a part of UmbraEdit.
Copyright (C) 2024 Veronica Berglyd Olsen and UmbraEdit contributors.
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful, but
WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <https://www.gnu.org/licenses/>.
""" # noqa
from __future__ import annotations
from typing import Any
from urllib.error import HTTPError
import pytest
from PyQt6.QtCore import QObject, Qt, QUrl, pyqtBoundSignal, pyqtSignal
from PyQt6.QtWidgets import QWidget
from pytestqt.qtbot import QtBot
from umbraedit import SHARED
from umbraedit.constants import nwConst
from umbraedit.extensions.versioninfo import VersionInfoWidget, _Retriever
from umbraedit.types import QtAlignAbsolute, QtAlignLeftMiddle, QtAlignRightMiddle
from tests.helpers import SimpleDialog
from tests.mocked import MockGuiMain
class MockSignalHolder(QObject):
"""Mock object holding the data ready signal."""
dataReady = pyqtSignal(str, str)
class MockRetriever:
"""Mock retriever that only stores the signal."""
def __init__(self, signal: pyqtBoundSignal) -> None:
self.signal = signal
class MockDesktopServices:
"""Mock desktop services that record the opened URL."""
url = None
@staticmethod
def openUrl(url: QUrl) -> None:
"""Record the URL instead of opening it."""
MockDesktopServices.url = url
class MockData:
"""Mock release data."""
def decode(self, *a: Any) -> str:
"""Return a release as JSON."""
return '{"tag_name": "v1.0"}'
class MockPayload:
"""Mock release payload."""
def __enter__(self):
"""Enter the payload context."""
return self
def __exit__(self, *args: Any) -> None:
"""Exit the payload context."""
return
def read(self):
"""Return the release data."""
return MockData()
class MockListData:
"""Mock empty release list data."""
def decode(self, *a: Any) -> str:
"""Return an empty JSON list."""
return "[]"
class MockListPayload:
"""Mock empty release list payload."""
def __enter__(self):
"""Enter the payload context."""
return self
def __exit__(self, *args: Any) -> None:
"""Exit the payload context."""
return
def read(self):
"""Return the empty list data."""
return MockListData()
class MockHTTPError:
"""Mock payload that raises an HTTP error."""
def __enter__(self):
"""Raise an HTTP error."""
raise HTTPError("http://example.com", 403, "Rate limit", None, None) # type: ignore
def __exit__(self, *args: Any) -> None:
"""Exit the payload context."""
return
class MockException:
"""Mock payload that raises a generic exception."""
def __enter__(self):
"""Raise a generic exception."""
raise Exception("Oh noes!")
def __exit__(self, *args: Any) -> None:
"""Exit the payload context."""
return
@pytest.mark.gui
def testVersionInfoWidget_Main(qtbot: QtBot, monkeypatch: pytest.MonkeyPatch, mockGUI: MockGuiMain):
"""Test the VersionInfoWidget class."""
version = VersionInfoWidget(None) # type: ignore
dialog = SimpleDialog(version)
dialog.show()
with monkeypatch.context() as mp:
mp.setattr("umbraedit.extensions.versioninfo.QDesktopServices", MockDesktopServices)
version._processLink("#notes")
assert MockDesktopServices.url == QUrl(nwConst.URL_RELEASES)
with monkeypatch.context() as mp:
mp.setattr("umbraedit.extensions.versioninfo.QDesktopServices", MockDesktopServices)
version._processLink("#website")
assert MockDesktopServices.url == QUrl(nwConst.URL_WEB)
with monkeypatch.context() as mp:
mp.setattr("umbraedit.extensions.versioninfo._Retriever", MockRetriever)
mp.setattr(SHARED, "runInThreadPool", lambda *a: None)
version._processLink("#update")
assert version._lblRelease.text() == "Latest Version: Checking ..."
# Unknown links do nothing
version._processLink("#unknown")
version._updateReleaseInfo("v2.0", "")
assert version._lblRelease.text() == (
"Latest Version: 2.0 \u2013 Download from <a href='#website'>UmbraEdit</a>"
)
version._updateReleaseInfo("", "")
assert version._lblRelease.text() == "Latest Version: Failed"
# The release label aligns with the layout direction
assert version._lblRelease.alignment() == QtAlignAbsolute | QtAlignLeftMiddle
rtlParent = QWidget()
rtlParent.setLayoutDirection(Qt.LayoutDirection.RightToLeft)
rtlVersion = VersionInfoWidget(rtlParent)
assert rtlVersion._lblRelease.alignment() == QtAlignAbsolute | QtAlignRightMiddle
dialog.close()
# qtbot.stop()
@pytest.mark.gui
def testVersionInfoWidget_Retriever(qtbot: QtBot, monkeypatch: pytest.MonkeyPatch):
"""Test the _Retriever class."""
holder = MockSignalHolder()
task = _Retriever(holder.dataReady)
# Valid Data
with monkeypatch.context() as mp:
mp.setattr("umbraedit.extensions.versioninfo.urlopen", lambda *a, **k: MockPayload())
with qtbot.waitSignal(holder.dataReady) as signal:
task.run()
assert signal.args == ["v1.0", ""]
def httpErr():
raise HTTPError("http://example.com", 403, "Rate limit") # type: ignore
# Non-Dict Response
with monkeypatch.context() as mp:
mp.setattr("umbraedit.extensions.versioninfo.urlopen", lambda *a, **k: MockListPayload())
received = []
holder.dataReady.connect(lambda tag, reason: received.append((tag, reason)))
task.run()
assert received == []
# HTTP Error
with monkeypatch.context() as mp:
mp.setattr("umbraedit.extensions.versioninfo.urlopen", lambda *a, **k: MockHTTPError())
with qtbot.waitSignal(holder.dataReady) as signal:
task.run()
assert signal.args == ["", "Rate limit (HTTP 403)"]
# Other Error
with monkeypatch.context() as mp:
mp.setattr("umbraedit.extensions.versioninfo.urlopen", lambda *a, **k: MockException())
with qtbot.waitSignal(holder.dataReady) as signal:
task.run()
assert signal.args == ["", "Oh noes!"]