Jelajahi Sumber

Fix JavaScript error caused by new `ui.notify` (#3457)

* fix `ui.notify`

* fix user tests involving notifications

* reset ui.notify during teardown
Falko Schindler 9 bulan lalu
induk
melakukan
fca2b5503c

+ 3 - 2
nicegui/functions/notify.py

@@ -1,6 +1,6 @@
 from typing import Any, Literal, Optional, Union
 from typing import Any, Literal, Optional, Union
 
 
-from ..elements.notification import Notification
+from ..context import context
 
 
 ARG_MAP = {
 ARG_MAP = {
     'close_button': 'closeBtn',
     'close_button': 'closeBtn',
@@ -49,4 +49,5 @@ 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 = {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['message'] = str(message)
     options.update(kwargs)
     options.update(kwargs)
-    Notification(options=options)
+    client = context.client
+    client.outbox.enqueue_message('notify', options, client.id)

+ 1 - 0
nicegui/static/nicegui.js

@@ -358,6 +358,7 @@ function createApp(elements, options) {
           window.open(url, target);
           window.open(url, target);
         },
         },
         download: (msg) => download(msg.src, msg.filename, msg.media_type, options.prefix),
         download: (msg) => download(msg.src, msg.filename, msg.media_type, options.prefix),
+        notify: (msg) => Quasar.Notify.create(msg),
       };
       };
       const socketMessageQueue = [];
       const socketMessageQueue = [];
       let isProcessingSocketMessage = false;
       let isProcessingSocketMessage = false;

+ 3 - 0
nicegui/testing/plugin.py

@@ -14,6 +14,7 @@ from starlette.routing import Route
 import nicegui.storage
 import nicegui.storage
 from nicegui import Client, app, binding, core, run, ui
 from nicegui import Client, app, binding, core, run, ui
 from nicegui.functions.navigate import Navigate
 from nicegui.functions.navigate import Navigate
+from nicegui.functions.notify import notify
 from nicegui.page import page
 from nicegui.page import page
 
 
 from .screen import Screen
 from .screen import Screen
@@ -143,6 +144,7 @@ async def user(nicegui_reset_globals,  # pylint: disable=unused-argument
         async with httpx.AsyncClient(app=core.app, base_url='http://test') as client:
         async with httpx.AsyncClient(app=core.app, base_url='http://test') as client:
             yield User(client)
             yield User(client)
     ui.navigate = Navigate()
     ui.navigate = Navigate()
+    ui.notify = notify
 
 
 
 
 @pytest.fixture
 @pytest.fixture
@@ -155,6 +157,7 @@ async def create_user(nicegui_reset_globals,  # pylint: disable=unused-argument
     async with core.app.router.lifespan_context(core.app):
     async with core.app.router.lifespan_context(core.app):
         yield lambda: User(httpx.AsyncClient(app=core.app, base_url='http://test'))
         yield lambda: User(httpx.AsyncClient(app=core.app, base_url='http://test'))
     ui.navigate = Navigate()
     ui.navigate = Navigate()
+    ui.notify = notify
 
 
 
 
 @pytest.fixture()
 @pytest.fixture()

+ 8 - 5
nicegui/testing/user.py

@@ -2,7 +2,7 @@ from __future__ import annotations
 
 
 import asyncio
 import asyncio
 import re
 import re
-from typing import List, Optional, Set, Type, TypeVar, Union, overload
+from typing import Any, List, Optional, Set, Type, TypeVar, Union, overload
 from uuid import uuid4
 from uuid import uuid4
 
 
 import httpx
 import httpx
@@ -14,6 +14,7 @@ from nicegui.nicegui import _on_handshake
 
 
 from .user_interaction import UserInteraction
 from .user_interaction import UserInteraction
 from .user_navigate import UserNavigate
 from .user_navigate import UserNavigate
+from .user_notify import UserNotify
 
 
 # pylint: disable=protected-access
 # pylint: disable=protected-access
 
 
@@ -31,10 +32,12 @@ class User:
         self.back_history: List[str] = []
         self.back_history: List[str] = []
         self.forward_history: List[str] = []
         self.forward_history: List[str] = []
         self.navigate = UserNavigate(self)
         self.navigate = UserNavigate(self)
+        self.notify = UserNotify()
 
 
-    def __getattribute__(self, name: str) -> asyncio.Any:
-        if name != 'navigate':  # NOTE: avoid infinite recursion
+    def __getattribute__(self, name: str) -> Any:
+        if name not in {'notify', 'navigate'}:  # NOTE: avoid infinite recursion
             ui.navigate = self.navigate
             ui.navigate = self.navigate
+            ui.notify = self.notify
         return super().__getattribute__(name)
         return super().__getattribute__(name)
 
 
     async def open(self, path: str, *, clear_forward_history: bool = True) -> None:
     async def open(self, path: str, *, clear_forward_history: bool = True) -> None:
@@ -91,7 +94,7 @@ class User:
         assert self.client
         assert self.client
         for _ in range(retries):
         for _ in range(retries):
             with self.client:
             with self.client:
-                if self._gather_elements(target, kind, marker, content):
+                if self.notify.contains(target) or self._gather_elements(target, kind, marker, content):
                     return
                     return
                 await asyncio.sleep(0.1)
                 await asyncio.sleep(0.1)
         raise AssertionError('expected to see at least one ' + self._build_error_message(target, kind, marker, content))
         raise AssertionError('expected to see at least one ' + self._build_error_message(target, kind, marker, content))
@@ -126,7 +129,7 @@ class User:
         assert self.client
         assert self.client
         for _ in range(retries):
         for _ in range(retries):
             with self.client:
             with self.client:
-                if not self._gather_elements(target, kind, marker, content):
+                if not self.notify.contains(target) and not self._gather_elements(target, kind, marker, content):
                     return
                     return
                 await asyncio.sleep(0.05)
                 await asyncio.sleep(0.05)
         raise AssertionError('expected not to see any ' + self._build_error_message(target, kind, marker, content))
         raise AssertionError('expected not to see any ' + self._build_error_message(target, kind, marker, content))

+ 14 - 0
nicegui/testing/user_notify.py

@@ -0,0 +1,14 @@
+from typing import Any, List
+
+
+class UserNotify:
+
+    def __init__(self) -> None:
+        self.messages: List[str] = []
+
+    def __call__(self, message: str, **kwargs) -> None:
+        self.messages.append(message)
+
+    def contains(self, needle: Any) -> bool:
+        """Check if any of the messages contain the given substring."""
+        return isinstance(needle, str) and any(needle in message for message in self.messages)