diff options
-rw-r--r-- | README.asciidoc | 9 | ||||
-rw-r--r-- | doc/changelog.asciidoc | 8 | ||||
-rw-r--r-- | misc/requirements/requirements-dev.txt | 10 | ||||
-rw-r--r-- | misc/requirements/requirements-flake8.txt | 6 | ||||
-rw-r--r-- | misc/requirements/requirements-mypy.txt | 6 | ||||
-rw-r--r-- | misc/requirements/requirements-pylint.txt | 8 | ||||
-rw-r--r-- | misc/requirements/requirements-pyroma.txt | 2 | ||||
-rw-r--r-- | misc/requirements/requirements-sphinx.txt | 4 | ||||
-rw-r--r-- | misc/requirements/requirements-tests.txt | 8 | ||||
-rw-r--r-- | misc/requirements/requirements-tox.txt | 8 | ||||
-rw-r--r-- | misc/requirements/requirements-yamllint.txt | 2 | ||||
-rw-r--r-- | qutebrowser/api/cmdutils.py | 31 | ||||
-rw-r--r-- | qutebrowser/browser/webengine/interceptor.py | 7 | ||||
-rw-r--r-- | qutebrowser/browser/webengine/notification.py | 14 | ||||
-rw-r--r-- | qutebrowser/mainwindow/tabbedbrowser.py | 2 | ||||
-rw-r--r-- | qutebrowser/utils/version.py | 7 | ||||
-rw-r--r-- | requirements.txt | 2 | ||||
-rw-r--r-- | tests/helpers/testutils.py | 2 | ||||
-rw-r--r-- | tests/unit/browser/webengine/test_webengineinterceptor.py | 97 | ||||
-rw-r--r-- | tests/unit/utils/test_version.py | 8 |
20 files changed, 185 insertions, 56 deletions
diff --git a/README.asciidoc b/README.asciidoc index 910a6b987..2b6bdfdd6 100644 --- a/README.asciidoc +++ b/README.asciidoc @@ -220,6 +220,7 @@ Active * https://nyxt.atlas.engineer/[Nyxt browser] (formerly "Next browser", Lisp, Emacs-like but also offers Vim bindings, QtWebEngine or GTK+/WebKit2 - note there was a https://jgkamat.gitlab.io/blog/next-rce.html[critical remote code execution in 2019] which was handled quite badly) * https://vieb.dev/[Vieb] (JavaScript, Electron) * https://surf.suckless.org/[surf] (C, GTK+ with WebKit1/WebKit2) +* https://github.com/jun7/wyeb[wyeb] (C, GTK+ with WebKit2) * Chrome/Chromium addons: https://vimium.github.io/[Vimium] * Firefox addons (based on WebExtensions): @@ -227,9 +228,8 @@ Active https://addons.mozilla.org/en-GB/firefox/addon/vimium-ff/[Vimium-FF] * Addons for Firefox and Chrome: https://github.com/brookhong/Surfingkeys[Surfingkeys], - https://lydell.github.io/LinkHints/[Link Hints] (hinting only) -* Addons for Safari: - https://televator.net/vimari/[Vimari] + https://lydell.github.io/LinkHints/[Link Hints] (hinting only), + https://github.com/ueokande/vimmatic[Vimmatic] Inactive ~~~~~~~~ @@ -246,7 +246,6 @@ main inspiration for qutebrowser) * https://www.uzbl.org/[uzbl] (C, GTK+ with WebKit1/WebKit2) * https://github.com/conformal/xombrero[xombrero] (C, GTK+ with WebKit1) * https://github.com/linkdd/cream-browser[Cream Browser] (C, GTK+ with WebKit1) -* https://github.com/jun7/wyeb[wyeb] (C, GTK+ with WebKit2) * Firefox addons (not based on WebExtensions or no recent activity): http://www.vimperator.org/[Vimperator], http://bug.5digits.org/pentadactyl/index[Pentadactyl], @@ -263,6 +262,8 @@ main inspiration for qutebrowser) https://github.com/1995eaton/chromium-vim[cVim], https://github.com/dcchambers/vb4c[vb4c] (fork of cVim, https://github.com/dcchambers/vb4c/issues/23#issuecomment-810694017[unmaintained]), https://glee.github.io/[GleeBox] +* Addons for Safari: + https://televator.net/vimari/[Vimari] License ------- diff --git a/doc/changelog.asciidoc b/doc/changelog.asciidoc index 03dbbeeae..b2b392a4c 100644 --- a/doc/changelog.asciidoc +++ b/doc/changelog.asciidoc @@ -202,8 +202,16 @@ Fixed - Crash when using QtWebKit with PAC and the file has an invalid encoding. - Crash with the "tiramisu" notification server. - Crash when the "herbe" notification presenter doesn't start correctly. +- Crash when no notification server is installed/available. +- Warning with recent versions of the "deadd" (aka "linux notification center") notification server. - Crash when using `:print --pdf` with a directory where its parent directory did not exist. +- The `PyQt{5,6}.sip` version is now shown correctly in the :version|--version + output. Previously that showed the version from the standalone `sip` module + which was only set for PyQt5. (#7805) +- When a `config.py` calls `.redirect()` via a request interceptor (which is + unsupported) and supplies an invalid redirect target URL, an exception is now + raised for the `.redirect()` call instead of later inside qutebrowser. [[v2.5.4]] v2.5.4 (2023-03-13) diff --git a/misc/requirements/requirements-dev.txt b/misc/requirements/requirements-dev.txt index 6655cbfde..c9590f1c5 100644 --- a/misc/requirements/requirements-dev.txt +++ b/misc/requirements/requirements-dev.txt @@ -6,7 +6,7 @@ bump2version==1.0.1 certifi==2023.7.22 cffi==1.15.1 charset-normalizer==3.2.0 -cryptography==41.0.2 +cryptography==41.0.3 docutils==0.20.1 github3.py==4.0.1 hunter==3.6.1 @@ -19,12 +19,12 @@ keyring==24.2.0 manhole==1.8.0 markdown-it-py==3.0.0 mdurl==0.1.2 -more-itertools==9.1.0 +more-itertools==10.1.0 packaging==23.1 pkginfo==1.9.6 ply==3.11 pycparser==2.21 -Pygments==2.15.1 +Pygments==2.16.1 PyJWT==2.8.0 Pympler==1.0.1 pyproject_hooks==1.0.0 @@ -34,9 +34,9 @@ readme-renderer==40.0 requests==2.31.0 requests-toolbelt==1.0.0 rfc3986==2.0.0 -rich==13.4.2 +rich==13.5.2 SecretStorage==3.3.3 -sip==6.7.10 +sip==6.7.11 six==1.16.0 tomli==2.0.1 twine==4.0.2 diff --git a/misc/requirements/requirements-flake8.txt b/misc/requirements/requirements-flake8.txt index 685542224..e16d6860f 100644 --- a/misc/requirements/requirements-flake8.txt +++ b/misc/requirements/requirements-flake8.txt @@ -1,7 +1,7 @@ # This file is automatically generated by scripts/dev/recompile_requirements.py attrs==23.1.0 -flake8==6.0.0 +flake8==6.1.0 flake8-bugbear==23.7.10 flake8-builtins==2.1.0 flake8-comprehensions==3.14.0 @@ -16,8 +16,8 @@ flake8-tidy-imports==4.10.0 flake8-tuple==0.4.1 mccabe==0.7.0 pep8-naming==0.13.3 -pycodestyle==2.10.0 +pycodestyle==2.11.0 pydocstyle==6.3.0 -pyflakes==3.0.1 +pyflakes==3.1.0 six==1.16.0 snowballstemmer==2.2.0 diff --git a/misc/requirements/requirements-mypy.txt b/misc/requirements/requirements-mypy.txt index a47f25d3f..1e18a7ab2 100644 --- a/misc/requirements/requirements-mypy.txt +++ b/misc/requirements/requirements-mypy.txt @@ -1,6 +1,6 @@ # This file is automatically generated by scripts/dev/recompile_requirements.py -chardet==5.1.0 +chardet==5.2.0 diff-cover==7.7.0 importlib-resources==6.0.0 Jinja2==3.1.2 @@ -9,12 +9,12 @@ MarkupSafe==2.1.3 mypy==1.4.1 mypy-extensions==1.0.0 pluggy==1.2.0 -Pygments==2.15.1 +Pygments==2.16.1 PyQt5-stubs==5.15.6.0 tomli==2.0.1 types-colorama==0.4.15.12 types-docutils==0.20.0.1 -types-Pygments==2.15.0.2 +types-Pygments==2.16.0.0 types-PyYAML==6.0.12.11 types-setuptools==68.0.0.3 typing_extensions==4.7.1 diff --git a/misc/requirements/requirements-pylint.txt b/misc/requirements/requirements-pylint.txt index 62d7696eb..cd09e4bf2 100644 --- a/misc/requirements/requirements-pylint.txt +++ b/misc/requirements/requirements-pylint.txt @@ -4,7 +4,7 @@ astroid==2.15.6 certifi==2023.7.22 cffi==1.15.1 charset-normalizer==3.2.0 -cryptography==41.0.2 +cryptography==41.0.3 dill==0.3.7 github3.py==4.0.1 idna==3.4 @@ -12,16 +12,16 @@ isort==5.12.0 lazy-object-proxy==1.9.0 mccabe==0.7.0 pefile==2023.2.7 -platformdirs==3.9.1 +platformdirs==3.10.0 pycparser==2.21 PyJWT==2.8.0 -pylint==2.17.4 +pylint==2.17.5 python-dateutil==2.8.2 ./scripts/dev/pylint_checkers requests==2.31.0 six==1.16.0 tomli==2.0.1 -tomlkit==0.11.8 +tomlkit==0.12.1 typing_extensions==4.7.1 uritemplate==4.1.1 # urllib3==2.0.4 diff --git a/misc/requirements/requirements-pyroma.txt b/misc/requirements/requirements-pyroma.txt index 50078baeb..f574b4c26 100644 --- a/misc/requirements/requirements-pyroma.txt +++ b/misc/requirements/requirements-pyroma.txt @@ -6,7 +6,7 @@ charset-normalizer==3.2.0 docutils==0.20.1 idna==3.4 packaging==23.1 -Pygments==2.15.1 +Pygments==2.16.1 pyproject_hooks==1.0.0 pyroma==4.2 requests==2.31.0 diff --git a/misc/requirements/requirements-sphinx.txt b/misc/requirements/requirements-sphinx.txt index 35b0e6257..3557eab6c 100644 --- a/misc/requirements/requirements-sphinx.txt +++ b/misc/requirements/requirements-sphinx.txt @@ -11,11 +11,11 @@ importlib-metadata==6.8.0 Jinja2==3.1.2 MarkupSafe==2.1.3 packaging==23.1 -Pygments==2.15.1 +Pygments==2.16.1 pytz==2023.3 requests==2.31.0 snowballstemmer==2.2.0 -Sphinx==7.0.1 +Sphinx==7.1.2 sphinxcontrib-applehelp==1.0.4 sphinxcontrib-devhelp==1.0.2 sphinxcontrib-htmlhelp==2.0.1 diff --git a/misc/requirements/requirements-tests.txt b/misc/requirements/requirements-tests.txt index 9b99a577a..abd6ea727 100644 --- a/misc/requirements/requirements-tests.txt +++ b/misc/requirements/requirements-tests.txt @@ -13,23 +13,23 @@ execnet==2.0.2 filelock==3.12.2 Flask==2.3.2 hunter==3.6.1 -hypothesis==6.82.0 +hypothesis==6.82.2 idna==3.4 importlib-metadata==6.8.0 iniconfig==2.0.0 itsdangerous==2.1.2 -jaraco.functools==3.8.0 +jaraco.functools==3.8.1 # Jinja2==3.1.2 Mako==1.2.4 manhole==1.8.0 # MarkupSafe==2.1.3 -more-itertools==9.1.0 +more-itertools==10.1.0 packaging==23.1 parse==1.19.1 parse-type==0.6.2 pluggy==1.2.0 py-cpuinfo==9.0.0 -Pygments==2.15.1 +Pygments==2.16.1 pytest==7.4.0 pytest-bdd==6.1.1 pytest-benchmark==4.0.0 diff --git a/misc/requirements/requirements-tox.txt b/misc/requirements/requirements-tox.txt index a4c7c4948..ae8fce6ff 100644 --- a/misc/requirements/requirements-tox.txt +++ b/misc/requirements/requirements-tox.txt @@ -1,17 +1,17 @@ # This file is automatically generated by scripts/dev/recompile_requirements.py cachetools==5.3.1 -chardet==5.1.0 +chardet==5.2.0 colorama==0.4.6 distlib==0.3.7 filelock==3.12.2 packaging==23.1 pip==23.2.1 -platformdirs==3.9.1 +platformdirs==3.10.0 pluggy==1.2.0 pyproject-api==1.5.3 setuptools==68.0.0 tomli==2.0.1 tox==4.6.4 -virtualenv==20.24.1 -wheel==0.41.0 +virtualenv==20.24.2 +wheel==0.41.1 diff --git a/misc/requirements/requirements-yamllint.txt b/misc/requirements/requirements-yamllint.txt index a35c0ff58..fd9ea256f 100644 --- a/misc/requirements/requirements-yamllint.txt +++ b/misc/requirements/requirements-yamllint.txt @@ -1,5 +1,5 @@ # This file is automatically generated by scripts/dev/recompile_requirements.py -pathspec==0.11.1 +pathspec==0.11.2 PyYAML==6.0.1 yamllint==1.32.0 diff --git a/qutebrowser/api/cmdutils.py b/qutebrowser/api/cmdutils.py index 067ebca92..0c367c6bf 100644 --- a/qutebrowser/api/cmdutils.py +++ b/qutebrowser/api/cmdutils.py @@ -35,7 +35,7 @@ Possible values: import inspect -from typing import Any, Callable, Iterable +from typing import Any, Callable, Iterable, Protocol, Optional, Dict, cast from qutebrowser.utils import qtutils from qutebrowser.commands import command, cmdexc @@ -90,7 +90,21 @@ def check_exclusive(flags: Iterable[bool], names: Iterable[str]) -> None: raise CommandError("Only one of {} can be given!".format(argstr)) -_CmdHandlerType = Callable[..., Any] +_CmdHandlerFunc = Callable[..., Any] + + +class _CmdHandlerType(Protocol): + + """A qutebrowser command function, which had qute_args patched on it. + + Applying @cmdutils.argument to a function will patch it with a qute_args attribute. + Below, we cast the decorated function to _CmdHandlerType to make mypy aware of this. + """ + + qute_args: Optional[Dict[str, command.ArgInfo]] + + def __call__(self, *args: Any, **kwargs: Any) -> Any: + ... class register: # noqa: N801,N806 pylint: disable=invalid-name @@ -118,7 +132,7 @@ class register: # noqa: N801,N806 pylint: disable=invalid-name # The arguments to pass to Command. self._kwargs = kwargs - def __call__(self, func: _CmdHandlerType) -> _CmdHandlerType: + def __call__(self, func: _CmdHandlerFunc) -> _CmdHandlerType: """Register the command before running the function. Gets called when a function should be decorated. @@ -158,7 +172,8 @@ class register: # noqa: N801,N806 pylint: disable=invalid-name # This is checked by future @cmdutils.argument calls so they fail # (as they'd be silently ignored otherwise) - func.qute_args = None # type: ignore[attr-defined] + func = cast(_CmdHandlerType, func) + func.qute_args = None return func @@ -210,19 +225,21 @@ class argument: # noqa: N801,N806 pylint: disable=invalid-name self._argname = argname # The name of the argument to handle. self._kwargs = kwargs # Valid ArgInfo members. - def __call__(self, func: _CmdHandlerType) -> _CmdHandlerType: + def __call__(self, func: _CmdHandlerFunc) -> _CmdHandlerType: funcname = func.__name__ if self._argname not in inspect.signature(func).parameters: raise ValueError("{} has no argument {}!".format(funcname, self._argname)) + + func = cast(_CmdHandlerType, func) if not hasattr(func, 'qute_args'): - func.qute_args = {} # type: ignore[attr-defined] + func.qute_args = {} elif func.qute_args is None: raise ValueError("@cmdutils.argument got called above (after) " "@cmdutils.register for {}!".format(funcname)) arginfo = command.ArgInfo(**self._kwargs) - func.qute_args[self._argname] = arginfo # type: ignore[attr-defined] + func.qute_args[self._argname] = arginfo return func diff --git a/qutebrowser/browser/webengine/interceptor.py b/qutebrowser/browser/webengine/interceptor.py index ac0795803..161f5ffab 100644 --- a/qutebrowser/browser/webengine/interceptor.py +++ b/qutebrowser/browser/webengine/interceptor.py @@ -10,7 +10,7 @@ from qutebrowser.qt.webenginecore import (QWebEngineUrlRequestInterceptor, from qutebrowser.config import websettings, config from qutebrowser.browser import shared -from qutebrowser.utils import debug, log +from qutebrowser.utils import debug, log, qtutils from qutebrowser.extensions import interceptors from qutebrowser.misc import objects @@ -35,6 +35,11 @@ class WebEngineRequest(interceptors.Request): if self._webengine_info is None: raise interceptors.RedirectException("Request improperly initialized.") + try: + qtutils.ensure_valid(url) + except qtutils.QtValueError as e: + raise interceptors.RedirectException(f"Redirect to invalid URL: {e}") + # Redirecting a request that contains payload data is not allowed. # To be safe, abort on any request not in a whitelist. verb = self._webengine_info.requestMethod() diff --git a/qutebrowser/browser/webengine/notification.py b/qutebrowser/browser/webengine/notification.py index 2edb2d538..e8b2e27f1 100644 --- a/qutebrowser/browser/webengine/notification.py +++ b/qutebrowser/browser/webengine/notification.py @@ -113,6 +113,9 @@ class DBusError(Error): # https://crashes.qutebrowser.org/view/de62220a # after "Notification daemon did quit!" "org.freedesktop.DBus.Error.UnknownObject", + + # notmuch-sha1-ef7b6e9e79e5f2f6cba90224122288895c1fe0d8 + "org.freedesktop.DBus.Error.ServiceUnknown", } def __init__(self, msg: QDBusMessage) -> None: @@ -856,12 +859,15 @@ class DBusNotificationAdapter(AbstractNotificationAdapter): log.misc.debug(f"Enabling quirks {quirks}") self._quirks = quirks - expected_spec_version = self._quirks.spec_version or self.SPEC_VERSION - if spec_version != expected_spec_version: + expected_spec_versions = [self.SPEC_VERSION] + if self._quirks.spec_version is not None: + expected_spec_versions.append(self._quirks.spec_version) + + if spec_version not in expected_spec_versions: log.misc.warning( f"Notification server ({name} {ver} by {vendor}) implements " - f"spec {spec_version}, but {expected_spec_version} was expected. " - f"If {name} is up to date, please report a qutebrowser bug.") + f"spec {spec_version}, but {'/'.join(expected_spec_versions)} was " + f"expected. If {name} is up to date, please report a qutebrowser bug.") # https://specifications.freedesktop.org/notification-spec/latest/ar01s08.html icon_key_overrides = { diff --git a/qutebrowser/mainwindow/tabbedbrowser.py b/qutebrowser/mainwindow/tabbedbrowser.py index 85f683133..98cb67cb2 100644 --- a/qutebrowser/mainwindow/tabbedbrowser.py +++ b/qutebrowser/mainwindow/tabbedbrowser.py @@ -316,6 +316,8 @@ class TabbedBrowser(QWidget): fields['id'] = self._win_id title = title_format.format(**fields) + # prevent hanging WMs and similar issues with giant URLs + title = utils.elide(title, 1024) self._window().setWindowTitle(title) diff --git a/qutebrowser/utils/version.py b/qutebrowser/utils/version.py index fdaa12efb..ce816b9fd 100644 --- a/qutebrowser/utils/version.py +++ b/qutebrowser/utils/version.py @@ -381,7 +381,6 @@ class ModuleInfo: def _create_module_info() -> Dict[str, ModuleInfo]: packages = [ - ('sip', ['SIP_VERSION_STR']), ('colorama', ['VERSION', '__version__']), ('jinja2', ['__version__']), ('pygments', ['__version__']), @@ -395,9 +394,13 @@ def _create_module_info() -> Dict[str, ModuleInfo]: ('PyQt5.QtWebEngineWidgets', []), ('PyQt5.QtWebEngine', ['PYQT_WEBENGINE_VERSION_STR']), ('PyQt5.QtWebKitWidgets', []), + ('PyQt5.sip', ['SIP_VERSION_STR']), ] elif machinery.IS_QT6: - packages.append(('PyQt6.QtWebEngineCore', ['PYQT_WEBENGINE_VERSION_STR'])) + packages += [ + ('PyQt6.QtWebEngineCore', ['PYQT_WEBENGINE_VERSION_STR']), + ('PyQt6.sip', ['SIP_VERSION_STR']), + ] else: raise utils.Unreachable() diff --git a/requirements.txt b/requirements.txt index b5bab3296..f10ab6f9b 100644 --- a/requirements.txt +++ b/requirements.txt @@ -5,7 +5,7 @@ colorama==0.4.6 importlib-resources==6.0.0 ; python_version=="3.8.*" Jinja2==3.1.2 MarkupSafe==2.1.3 -Pygments==2.15.1 +Pygments==2.16.1 PyYAML==6.0.1 zipp==3.16.2 # Unpinned due to recompile_requirements.py limitations diff --git a/tests/helpers/testutils.py b/tests/helpers/testutils.py index f6d7fcc4b..dc6fdac32 100644 --- a/tests/helpers/testutils.py +++ b/tests/helpers/testutils.py @@ -150,7 +150,7 @@ def partial_compare(val1, val2, *, indent=0): if val2 is Ellipsis: print_i("Ignoring ellipsis comparison", indent, error=True) return PartialCompareOutcome() - elif type(val1) != type(val2): # pylint: disable=unidiomatic-typecheck + elif type(val1) is not type(val2): outcome = PartialCompareOutcome( "Different types ({}, {}) -> False".format(type(val1).__name__, type(val2).__name__)) diff --git a/tests/unit/browser/webengine/test_webengineinterceptor.py b/tests/unit/browser/webengine/test_webengineinterceptor.py index d579d60f7..099ba69d7 100644 --- a/tests/unit/browser/webengine/test_webengineinterceptor.py +++ b/tests/unit/browser/webengine/test_webengineinterceptor.py @@ -6,12 +6,15 @@ import pytest +import pytest_mock -pytest.importorskip('qutebrowser.qt.webenginecore') +pytest.importorskip("qutebrowser.qt.webenginecore") +from qutebrowser.qt.core import QUrl, QByteArray from qutebrowser.qt.webenginecore import QWebEngineUrlRequestInfo from qutebrowser.browser.webengine import interceptor +from qutebrowser.extensions import interceptors from qutebrowser.utils import qtutils from helpers import testutils @@ -19,10 +22,12 @@ from helpers import testutils def test_no_missing_resource_types(): request_interceptor = interceptor.RequestInterceptor() qb_keys = set(request_interceptor._resource_types.keys()) - qt_keys = set(testutils.enum_members( - QWebEngineUrlRequestInfo, - QWebEngineUrlRequestInfo.ResourceType, - ).values()) + qt_keys = set( + testutils.enum_members( + QWebEngineUrlRequestInfo, + QWebEngineUrlRequestInfo.ResourceType, + ).values() + ) assert qt_keys == qb_keys @@ -30,3 +35,85 @@ def test_resource_type_values(): request_interceptor = interceptor.RequestInterceptor() for qt_value, qb_item in request_interceptor._resource_types.items(): assert qtutils.extract_enum_val(qt_value) == qb_item.value + + +@pytest.fixture +def we_request( # a shrubbery! + mocker: pytest_mock.MockerFixture, +) -> interceptor.WebEngineRequest: + qt_info = mocker.Mock(spec=QWebEngineUrlRequestInfo) + qt_info.requestMethod.return_value = QByteArray(b"GET") + first_party_url = QUrl("https://firstparty.example.org/") + request_url = QUrl("https://request.example.org/") + return interceptor.WebEngineRequest( + first_party_url=first_party_url, + request_url=request_url, + webengine_info=qt_info, + ) + + +def test_block(we_request: interceptor.WebEngineRequest): + assert not we_request.is_blocked + we_request.block() + assert we_request.is_blocked + + +class TestRedirect: + REDIRECT_URL = QUrl("https://redirect.example.com/") + + def test_redirect(self, we_request: interceptor.WebEngineRequest): + assert not we_request._redirected + we_request.redirect(self.REDIRECT_URL) + assert we_request._redirected + we_request._webengine_info.redirect.assert_called_once_with(self.REDIRECT_URL) + + def test_twice(self, we_request: interceptor.WebEngineRequest): + we_request.redirect(self.REDIRECT_URL) + with pytest.raises( + interceptors.RedirectException, + match=r"Request already redirected.", + ): + we_request.redirect(self.REDIRECT_URL) + we_request._webengine_info.redirect.assert_called_once_with(self.REDIRECT_URL) + + def test_invalid_method(self, we_request: interceptor.WebEngineRequest): + we_request._webengine_info.requestMethod.return_value = QByteArray(b"POST") + with pytest.raises( + interceptors.RedirectException, + match=( + r"Request method b'POST' for https://request.example.org/ does not " + r"support redirection." + ), + ): + we_request.redirect(self.REDIRECT_URL) + assert not we_request._webengine_info.redirect.called + + def test_invalid_method_ignore_unsupported( + self, + we_request: interceptor.WebEngineRequest, + caplog: pytest.LogCaptureFixture, + ): + we_request._webengine_info.requestMethod.return_value = QByteArray(b"POST") + we_request.redirect(self.REDIRECT_URL, ignore_unsupported=True) + assert caplog.messages == [ + "Request method b'POST' for https://request.example.org/ does not support " + "redirection." + ] + assert not we_request._webengine_info.redirect.called + + def test_improperly_initialized(self, we_request: interceptor.WebEngineRequest): + we_request._webengine_info = None + with pytest.raises( + interceptors.RedirectException, + match=r"Request improperly initialized.", + ): + we_request.redirect(self.REDIRECT_URL) + + def test_invalid_url(self, we_request: interceptor.WebEngineRequest): + url = QUrl() + assert not url.isValid() + with pytest.raises( + interceptors.RedirectException, + match=r"Redirect to invalid URL: PyQt\d\.QtCore\.QUrl\(''\) is not valid", + ): + we_request.redirect(url) diff --git a/tests/unit/utils/test_version.py b/tests/unit/utils/test_version.py index f78a6f12d..486270d70 100644 --- a/tests/unit/utils/test_version.py +++ b/tests/unit/utils/test_version.py @@ -644,8 +644,8 @@ class TestModuleVersions: assert version._module_versions() == expected @pytest.mark.parametrize('module, idx, expected', [ - ('colorama', 1, 'colorama: no'), - ('adblock', 5, 'adblock: no'), + ('colorama', 0, 'colorama: no'), + ('adblock', 4, 'adblock: no'), ]) def test_missing_module(self, module, idx, expected, import_fake): """Test with a module missing. @@ -693,11 +693,11 @@ class TestModuleVersions: assert not mod_info.is_usable() expected = f"adblock: {fake_version} (< {mod_info.min_version}, outdated)" - assert version._module_versions()[5] == expected + assert version._module_versions()[4] == expected @pytest.mark.parametrize('attribute, expected_modules', [ ('VERSION', ['colorama']), - ('SIP_VERSION_STR', ['sip']), + ('SIP_VERSION_STR', ['PyQt5.sip', 'PyQt6.sip']), (None, []), ]) def test_version_attribute(self, attribute, expected_modules, import_fake): |