ソースを参照

add fastapi test option to pytest

dinhlongviolin1 1 ヶ月 前
コミット
ce078c477d

+ 3 - 0
.github/actions/gui-test/e2e/action.yml

@@ -11,3 +11,6 @@ runs:
     - name: run tests
       shell: bash
       run: pipenv run pytest -m "teste2e" tests/gui
+    - name: run tests for fastapi
+      shell: bash
+      run: pipenv run pytest -m "teste2e" tests/gui --gui-server="fastapi"

+ 3 - 0
.github/actions/gui-test/prefix/action.yml

@@ -12,3 +12,6 @@ runs:
     - name: run pytest with prefix configuration
       shell: bash
       run: pipenv run pytest -m "teste2e" tests/gui/e2e --e2e-base-url="/prefix/" --e2e-port="4000"
+    - name: run pytest with prefix configuration with fastapi
+      shell: bash
+      run: pipenv run pytest -m "teste2e" tests/gui/e2e --e2e-base-url="/prefix/" --e2e-port="4000" --gui-server="fastapi"

+ 4 - 4
taipy/gui/servers/fastapi/server.py

@@ -123,12 +123,13 @@ class FastAPIServer(_Server):
 
         # Define your event handlers and routes
         @self._ws.event
-        async def connect(sid, environ):
+        async def connect(sid, *args):
             with get_request_meta_ctx(), set_sid_ctx(sid):
                 gui._handle_connect()
 
         @self._ws.event
-        async def message(sid, message, *args):
+        async def message(sid, *args):
+            message = args[0]
             with get_request_meta_ctx(), set_sid_ctx(sid):
                 if "status" in message:
                     _TaipyLogger._get_logger().info(message["status"])
@@ -193,9 +194,9 @@ class FastAPIServer(_Server):
                 if not path or path == "index.html" or "." not in path:
                     try:
                         return templates.TemplateResponse(
+                            request,
                             "index.html",
                             {
-                                "request": request,
                                 "title": title,
                                 "favicon": f"{favicon}?version={version}",
                                 "root_margin": root_margin,
@@ -216,7 +217,6 @@ class FastAPIServer(_Server):
 
                 if path == "taipy.status.json":
                     return JSONResponse(content=self._gui._serve_status(pathlib.Path(template_folder) / path))
-
                 if (file_path := str(os.path.normpath((base_path := static_folder + os.path.sep) + path))).startswith(
                     base_path
                 ) and os.path.isfile(file_path):

+ 16 - 11
taipy/gui/servers/fastapi/utils.py

@@ -39,17 +39,22 @@ def send_from_directory(
     return FileResponse(joined_path, **kwargs)
 
 
-def exec_async(async_func: t.Callable, *args, **kwargs):
-    loop = asyncio.get_event_loop()
-    if loop is not None:
-        asyncio.run_coroutine_threadsafe(async_func(*args, **kwargs), loop)
-    else:
-        asyncio.run(async_func(*args, **kwargs))
+def exec_async(async_func: t.Callable, *args, **kwargs) -> None:
+    try:
+        # future = asyncio.run_coroutine_threadsafe(async_func(*args, **kwargs), asyncio.get_event_loop())
+        # return future.result()
+        asyncio.get_event_loop().create_task(async_func(*args, **kwargs))
+    except RuntimeError as ex:
+        if "There is no current event loop in thread" in str(ex):
+            asyncio.run(async_func(*args, **kwargs))
+            return
+        raise ex
 
 
 def run_async(async_func: t.Callable, *args, **kwargs):
-    loop = asyncio.get_event_loop()
-    if loop is not None:
-        return loop.run_until_complete(async_func(*args, **kwargs))
-    else:
-        return asyncio.run(async_func(*args, **kwargs))
+    try:
+        return asyncio.get_event_loop().run_until_complete(async_func(*args, **kwargs))
+    except RuntimeError as ex:
+        if "There is no current event loop in thread" in str(ex):
+            return asyncio.run(async_func(*args, **kwargs))
+        raise ex

+ 8 - 0
tests/conftest.py

@@ -30,6 +30,7 @@ def pytest_addoption(parser: pytest.Parser) -> None:
     """Add custom command line options for pytest."""
     parser.addoption("--e2e-base-url", action="store", default="/", help="base url for e2e testing")
     parser.addoption("--e2e-port", action="store", default="5000", help="port for e2e testing")
+    parser.addoption("--gui-server", action="store", default="flask", help="server for e2e testing")
 
 
 @pytest.fixture(scope="session")
@@ -44,6 +45,12 @@ def e2e_port(request: pytest.FixtureRequest) -> str:
     return request.config.getoption("--e2e-port")
 
 
+@pytest.fixture(scope="session")
+def gui_server(request: pytest.FixtureRequest) -> str:
+    """Fixture to get the server for e2e testing."""
+    return request.config.getoption("--gui-server")
+
+
 def remove_subparser(name: str) -> None:
     """Remove a subparser from argparse."""
     _TaipyParser._sub_taipyparsers.pop(name, None)
@@ -162,6 +169,7 @@ def inject_core_sections() -> t.Callable:
 
     return _inject_core_sections
 
+
 @pytest.fixture
 def inject_rest_sections() -> t.Callable:
     """Fixture to inject core sections into the configuration."""

+ 2 - 2
tests/gui/e2e/conftest.py

@@ -22,10 +22,10 @@ def browser_context_args(browser_context_args, e2e_port, e2e_base_url):
 
 
 @pytest.fixture(scope="function")
-def gui(helpers, e2e_base_url):
+def gui(helpers, e2e_base_url, gui_server):
     from taipy.gui import Gui
 
-    gui = Gui()
+    gui = Gui(server=gui_server)
     gui.load_config({"base_url": e2e_base_url, "host": "0.0.0.0" if e2e_base_url != "/" else "127.0.0.1"})
     yield gui
     # Delete Gui instance and state of some classes after each test

+ 22 - 8
tests/gui/e2e/renderers/test_html_rendering.py

@@ -18,11 +18,14 @@ from urllib.request import urlopen
 
 import pytest
 
+from taipy.gui.servers.utils import get_server_type
+
 if util.find_spec("playwright"):
     from playwright._impl._page import Page
 
 from taipy.gui import Gui, Html
-from taipy.gui.servers.flask import FlaskServer as _Server
+from taipy.gui.servers.fastapi import FastAPIServer
+from taipy.gui.servers.flask import FlaskServer
 
 
 @pytest.mark.teste2e
@@ -99,15 +102,26 @@ def test_html_render_bind_assets(page: "Page", gui: Gui, helpers, e2e_base_url,
 
 @pytest.mark.teste2e
 def test_html_render_path_mapping(page: "Page", gui: Gui, helpers, e2e_base_url, e2e_port):
-    gui._server = _Server(
-        gui,
-        path_mapping={"style": f"{Path(Path(__file__).parent.resolve())}{os.path.sep}test-assets{os.path.sep}style"},
-        server=gui._server_instance,
-        async_mode="gevent",
-    )
+    if get_server_type() == "flask":
+        gui._server = FlaskServer(
+            gui,
+            path_mapping={
+                "style": f"{Path(Path(__file__).parent.resolve())}{os.path.sep}test-assets{os.path.sep}style"
+            },
+            server=gui._server_instance,
+            async_mode="gevent",
+        )
+    else:
+        gui._server = FastAPIServer(
+            gui,
+            path_mapping={
+                "style": f"{Path(Path(__file__).parent.resolve())}{os.path.sep}test-assets{os.path.sep}style"
+            },
+            server=gui._server_instance,
+        )
     gui.add_page("page1", Html(f"{Path(Path(__file__).parent.resolve())}{os.path.sep}page1.html"))
     helpers.run_e2e(gui)
-    assert ".taipy-text" in urlopen(f"http://127.0.0.1:{e2e_port}{e2e_base_url}/style/style.css").read().decode("utf-8")
+    assert ".taipy-text" in urlopen(f"http://127.0.0.1:{e2e_port}{e2e_base_url}style/style.css").read().decode("utf-8")
     page.goto("./page1")
     page.expect_websocket()
     page.wait_for_selector("#text1")