diff options
author | Ash <relativistic.policeman@gmail.com> | 2020-06-03 22:21:22 -0700 |
---|---|---|
committer | Ash <relativistic.policeman@gmail.com> | 2020-06-03 23:43:26 -0700 |
commit | 77d4d3f4d4255ddfdd4f05ae4abda51ef306b32c (patch) | |
tree | d15e13adf4bdec9a63da9aad0ff39f5d8c501a89 | |
parent | 755fafc9ce56a02104f672722d6d1db7b243ff37 (diff) | |
download | qutebrowser-77d4d3f4d4255ddfdd4f05ae4abda51ef306b32c.tar.gz qutebrowser-77d4d3f4d4255ddfdd4f05ae4abda51ef306b32c.zip |
Add support for onclose callbacks on notifications.
-rw-r--r-- | pytest.ini | 1 | ||||
-rw-r--r-- | qutebrowser/browser/webengine/notification.py | 32 | ||||
-rw-r--r-- | tests/end2end/conftest.py | 12 | ||||
-rw-r--r-- | tests/end2end/features/notifications.feature | 14 | ||||
-rw-r--r-- | tests/end2end/features/test_notifications_bdd.py | 4 | ||||
-rw-r--r-- | tests/end2end/fixtures/notificationserver.py | 10 |
6 files changed, 65 insertions, 8 deletions
diff --git a/pytest.ini b/pytest.ini index e85f2b298..a01c456f7 100644 --- a/pytest.ini +++ b/pytest.ini @@ -23,6 +23,7 @@ markers = qtwebkit_skip: Tests not applicable with QtWebKit qtwebengine_flaky: Tests which are flaky (and currently skipped) with QtWebEngine qtwebengine_mac_xfail: Tests which fail on macOS with QtWebEngine + qtwebengine_py_5_15: Tests which require PyQtWebEngine 5.15. js_prompt: Tests needing to display a javascript prompt this: Used to mark tests during development no_invalid_lines: Don't fail on unparseable lines in end2end tests diff --git a/qutebrowser/browser/webengine/notification.py b/qutebrowser/browser/webengine/notification.py index e2bc3700d..95a084c7e 100644 --- a/qutebrowser/browser/webengine/notification.py +++ b/qutebrowser/browser/webengine/notification.py @@ -24,8 +24,9 @@ import typing from qutebrowser.utils import log from PyQt5.QtGui import QImage -from PyQt5.QtCore import QVariant, QMetaType, QByteArray, PYQT_VERSION -from PyQt5.QtDBus import QDBusConnection, QDBusInterface, QDBus, QDBusArgument +from PyQt5.QtCore import QObject, QVariant, QMetaType, QByteArray, pyqtSlot, PYQT_VERSION +from PyQt5.QtDBus import QDBusConnection, QDBusInterface, QDBus, QDBusArgument, QDBusMessage +from PyQt5.QtWebEngine import PYQT_WEBENGINE_VERSION from PyQt5.QtWebEngineCore import QWebEngineNotification from PyQt5.QtWebEngineWidgets import QWebEngineProfile @@ -34,7 +35,7 @@ class DBusException(Exception): """Raised when something goes wrong with talking to DBus.""" -class DBusNotificationPresenter: +class DBusNotificationPresenter(QObject): """Manages notifications that are sent over DBus.""" SERVICE = "org.freedesktop.Notifications" @@ -43,17 +44,29 @@ class DBusNotificationPresenter: INTERFACE = "org.freedesktop.Notifications" def __init__(self, test_service: bool = False): + super().__init__() + self._active_notifications = {} # type: typing.Dict[int, QWebEngineNotification] bus = QDBusConnection.sessionBus() if not bus.isConnected(): raise DBusException("Failed to connect to DBus session bus") + service = self.TEST_SERVICE if test_service else self.SERVICE + self.interface = QDBusInterface( - self.TEST_SERVICE if test_service else self.SERVICE, + service, self.PATH, self.INTERFACE, bus, ) + bus.connect( + service, + self.PATH, + self.INTERFACE, + "NotificationClosed", + self._handle_close + ) + if not self.interface: raise DBusException("Could not construct a DBus interface") @@ -114,6 +127,7 @@ class DBusNotificationPresenter: ) notification_id = reply.arguments()[0] + self._active_notifications[notification_id] = qt_notification log.webview.debug("Sent out notification {}".format(notification_id)) def _convert_image(self, qimage: QImage) -> QDBusArgument: @@ -141,3 +155,13 @@ class DBusNotificationPresenter: image_data.add(QByteArray(bits)) image_data.endStructure() return image_data + + @pyqtSlot(QDBusMessage) + def _handle_close(self, message: QDBusMessage) -> None: + notification_id = message.arguments()[0] + if notification_id in self._active_notifications: + try: + self._active_notifications[notification_id].close() + except RuntimeError: + # WORKAROUND for https://www.riverbankcomputing.com/pipermail/pyqt/2020-May/042918.html + pass diff --git a/tests/end2end/conftest.py b/tests/end2end/conftest.py index 7190c7ef4..84e612952 100644 --- a/tests/end2end/conftest.py +++ b/tests/end2end/conftest.py @@ -112,6 +112,7 @@ def _get_backend_tag(tag): 'qtwebengine_todo': pytest.mark.qtwebengine_todo, 'qtwebengine_skip': pytest.mark.qtwebengine_skip, 'qtwebengine_notifications': pytest.mark.qtwebengine_notifications, + 'qtwebengine_py_5_15': pytest.mark.qtwebengine_py_5_15, 'qtwebkit_skip': pytest.mark.qtwebkit_skip, } if not any(tag.startswith(t + ':') for t in pytest_marks): @@ -136,6 +137,13 @@ if not getattr(sys, 'frozen', False): return None +def _pyqt_webengine_at_least_5_15() -> bool: + try: + from PyQt5.QtWebEngine import PYQT_WEBENGINE_VERSION + return PYQT_WEBENGINE_VERSION >= 0x050F00 + except ImportError: + return False + def pytest_collection_modifyitems(config, items): """Apply @qtwebengine_* markers; skip unittests with QUTE_BDD_WEBENGINE.""" markers = [ @@ -153,6 +161,10 @@ def pytest_collection_modifyitems(config, items): config.webengine), ('qtwebengine_mac_xfail', 'Fails on macOS with QtWebEngine', pytest.mark.xfail, config.webengine and utils.is_mac), + # WORKAROUND for https://www.riverbankcomputing.com/pipermail/pyqt/2020-May/042918.html + ('qtwebengine_py_5_15', 'Skipped with PyQtWebEngine < 5.15', + pytest.mark.skipif, + config.webengine and not _pyqt_webengine_at_least_5_15()) ] for item in items: diff --git a/tests/end2end/features/notifications.feature b/tests/end2end/features/notifications.feature index fcbb8fa25..4e950c133 100644 --- a/tests/end2end/features/notifications.feature +++ b/tests/end2end/features/notifications.feature @@ -4,7 +4,8 @@ Feature: Notifications HTML5 notification API interaction Background: - Given I open data/prompt/notifications.html + Given I have a fresh instance + And I open data/prompt/notifications.html And I set content.notifications to true And I run :click-element id button @@ -14,3 +15,14 @@ Feature: Notifications Then the javascript message "notification shown" should be logged And a notification with id 1 is presented + @qtwebengine_notifications @qtwebengine_py_5_15 + Scenario: User closes presented notification + When I run :click-element id show-button + And I close the notification with id 1 + Then the javascript message "notification closed" should be logged + + @qtwebengine_notifications @qtwebengine_py_5_15 + Scenario: User closes some other application's notification + When I run :click-element id show-button + And I close the notification with id 1234 + Then the javascript message "notification closed" should not be logged diff --git a/tests/end2end/features/test_notifications_bdd.py b/tests/end2end/features/test_notifications_bdd.py index c06cb394f..1c16b4c01 100644 --- a/tests/end2end/features/test_notifications_bdd.py +++ b/tests/end2end/features/test_notifications_bdd.py @@ -27,3 +27,7 @@ from qutebrowser.utils import qtutils @bdd.then(bdd.parsers.cfparse('a notification with id {id_:d} is presented')) def notification_presented(notification_server, id_): assert id_ in notification_server.messages + +@bdd.when(bdd.parsers.cfparse('I close the notification with id {id_:d}')) +def close_notification(notification_server, id_): + notification_server.close(id_) diff --git a/tests/end2end/fixtures/notificationserver.py b/tests/end2end/fixtures/notificationserver.py index d08a34eb3..5abb8c498 100644 --- a/tests/end2end/fixtures/notificationserver.py +++ b/tests/end2end/fixtures/notificationserver.py @@ -23,6 +23,7 @@ class TestNotificationServer(QObject): super().__init__() self._service = service self._bus = QDBusConnection.sessionBus() + assert self._bus.isConnected() self._message_id = 0 # A dict mapping notification IDs to currently-displayed notifications. self.messages = {} # type: typing.Dict[int, QDBusMessage] @@ -35,6 +36,7 @@ class TestNotificationServer(QObject): QDBusConnection.ExportAllSlots) def unregister(self) -> None: + self._bus.unregisterObject(DBusNotificationPresenter.PATH) assert self._bus.unregisterService(self._service) @pyqtSlot(QDBusMessage, result="uint") @@ -58,9 +60,11 @@ class TestNotificationServer(QObject): @pytest.fixture def notification_server(qapp): server = TestNotificationServer(DBusNotificationPresenter.TEST_SERVICE) - server.register() - yield server - server.unregister() + try: + server.register() + yield server + finally: + server.unregister() def _as_uint32(x: int) -> QVariant: |