From 8b519dc81e356d526c4ce82af483d5120af44e81 Mon Sep 17 00:00:00 2001 From: Rodja Trappe Date: Thu, 18 Jul 2024 18:34:42 +0200 Subject: [PATCH] Use ui.notification for ui.notify to simplify user simulation tests (#3369) * use ui.notification for ui.notify to simplify user simulation tests * code review --------- Co-authored-by: Falko Schindler --- nicegui/element_filter.py | 1 + nicegui/elements/notification.py | 41 ++++++++++++++++++-------------- nicegui/functions/notify.py | 5 ++-- nicegui/static/nicegui.js | 1 - nicegui/testing/user.py | 7 +----- tests/test_user_simulation.py | 15 ++++++++++++ 6 files changed, 42 insertions(+), 28 deletions(-) diff --git a/nicegui/element_filter.py b/nicegui/element_filter.py index 2ff7a938b..3e5449909 100644 --- a/nicegui/element_filter.py +++ b/nicegui/element_filter.py @@ -63,6 +63,7 @@ def _iterate(self, parent: Element, *, visited: Optional[List[Element]] = None) (element.source if isinstance(element, SourceElement) else ''), element._props.get('placeholder', ''), (element._props.get('value', '') or ''), # NOTE the value could be None + element._props.get('options', {}).get('message', ''), ] content = ' '.join(str(c) for c in element_contents) diff --git a/nicegui/elements/notification.py b/nicegui/elements/notification.py index f1bc03229..9a3fb1d4c 100644 --- a/nicegui/elements/notification.py +++ b/nicegui/elements/notification.py @@ -1,5 +1,5 @@ import asyncio -from typing import Any, Callable, Literal, Optional, Union +from typing import Any, Callable, Dict, Literal, Optional, Union from typing_extensions import Self @@ -41,6 +41,7 @@ def __init__(self, spinner: bool = False, timeout: Optional[float] = 5.0, on_dismiss: Optional[Callable] = None, + options: Optional[Dict] = None, **kwargs: Any, ) -> None: """Notification element @@ -59,28 +60,32 @@ def __init__(self, :param spinner: display a spinner in the notification (default: False) :param timeout: optional timeout in seconds after which the notification is dismissed (default: 5.0) :param on_dismiss: optional callback to be invoked when the notification is dismissed + :param options: optional dictionary with all options (overrides all other arguments) Note: You can pass additional keyword arguments according to `Quasar's Notify API `_. """ with context.client.layout: super().__init__() - self._props['options'] = { - 'message': str(message), - 'position': position, - 'multiLine': multi_line, - 'spinner': spinner, - 'closeBtn': close_button, - 'timeout': (timeout or 0) * 1000, - 'group': False, - 'attrs': {'data-id': f'nicegui-dialog-{self.id}'}, - } - if type is not None: - self._props['options']['type'] = type - if color is not None: - self._props['options']['color'] = color - if icon is not None: - self._props['options']['icon'] = icon - self._props['options'].update(kwargs) + if options: + self._props['options'] = options + else: + self._props['options'] = { + 'message': str(message), + 'position': position, + 'multiLine': multi_line, + 'spinner': spinner, + 'closeBtn': close_button, + 'timeout': (timeout or 0) * 1000, + 'group': False, + 'attrs': {'data-id': f'nicegui-dialog-{self.id}'}, + } + if type is not None: + self._props['options']['type'] = type + if color is not None: + self._props['options']['color'] = color + if icon is not None: + self._props['options']['icon'] = icon + self._props['options'].update(kwargs) if on_dismiss: self.on_dismiss(on_dismiss) diff --git a/nicegui/functions/notify.py b/nicegui/functions/notify.py index 91522f346..c15b67b18 100644 --- a/nicegui/functions/notify.py +++ b/nicegui/functions/notify.py @@ -1,6 +1,6 @@ from typing import Any, Literal, Optional, Union -from ..context import context +from ..elements.notification import Notification ARG_MAP = { 'close_button': 'closeBtn', @@ -49,5 +49,4 @@ def notify(message: Any, *, options = {ARG_MAP.get(key, key): value for key, value in locals().items() if key != 'kwargs' and value is not None} options['message'] = str(message) options.update(kwargs) - client = context.client - client.outbox.enqueue_message('notify', options, client.id) + Notification(options=options) diff --git a/nicegui/static/nicegui.js b/nicegui/static/nicegui.js index e42754659..77c902839 100644 --- a/nicegui/static/nicegui.js +++ b/nicegui/static/nicegui.js @@ -345,7 +345,6 @@ function createApp(elements, options) { window.open(url, target); }, download: (msg) => download(msg.src, msg.filename, msg.media_type, options.prefix), - notify: (msg) => Quasar.Notify.create(msg), }; const socketMessageQueue = []; let isProcessingSocketMessage = false; diff --git a/nicegui/testing/user.py b/nicegui/testing/user.py index 120199d81..78952d2a0 100644 --- a/nicegui/testing/user.py +++ b/nicegui/testing/user.py @@ -9,7 +9,7 @@ import socketio from typing_extensions import Self -from nicegui import Client, ElementFilter, background_tasks, context, ui +from nicegui import Client, ElementFilter, background_tasks, ui from nicegui.element import Element from nicegui.logging import log from nicegui.nicegui import Slot, _on_handshake @@ -95,11 +95,6 @@ async def should_see(self, with self.client: if self._gather_elements(target, kind, marker, content): return - if isinstance(target, str): - content = target - for _, message_type, message_data in context.client.outbox.messages: - if content is not None and message_type == 'notify' and content in message_data['message']: - return await asyncio.sleep(0.1) raise AssertionError('expected to see at least one ' + self._build_error_message(target, kind, marker, content)) diff --git a/tests/test_user_simulation.py b/tests/test_user_simulation.py index 5cf150eef..4c81ba19f 100644 --- a/tests/test_user_simulation.py +++ b/tests/test_user_simulation.py @@ -142,6 +142,21 @@ def page(): await user.should_see('Hello') +async def test_should_not_see_notification(user: User) -> None: + @ui.page('/') + def page(): + ui.button('Notify', on_click=lambda: ui.notification('Hello')) + + await user.open('/') + await user.should_not_see('Hello') + user.find('Notify').click() + await user.should_see('Hello') + with pytest.raises(AssertionError): + await user.should_not_see('Hello') + user.find('Hello').trigger('dismiss') + await user.should_not_see('Hello') + + async def test_trigger_event(user: User) -> None: @ui.page('/') def page():