Add support for onclose callbacks on notifications.
This commit is contained in:
parent
755fafc9ce
commit
77d4d3f4d4
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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:
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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_)
|
||||
|
|
|
|||
|
|
@ -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:
|
||||
|
|
|
|||
Loading…
Reference in New Issue