From a702ae192be4ca01116a2ac054b967c987b3c931 Mon Sep 17 00:00:00 2001 From: Paula Kammler Date: Thu, 1 Aug 2024 15:09:53 +0200 Subject: [PATCH 1/2] pytest configuration & fixtures (#3411) * remove pytest-prefix from plugin specification * fix requirements and settings for pytests example * rename screen and user with prefix nicegui_ * add conftest with renaming to pytests example * add internal renaming of nicegui_screen to screen and nicegui_user to user * remove unused pylint ignore * fix rename in examples * remove entry point * remove renaming * remove renaming in examples * remove renaming fixtures and add example code * double quotes * reworked as described in docs (#3413) * add pytest plugin loading to examples * run the pytest examples as part of the test_startup.sh to avoid conflicts in setup * renamed the pytest plugin from fixtures.py to plugin.py * fix todo tests * rename folder --------- Co-authored-by: Rodja Trappe Co-authored-by: Falko Schindler --- .../authentication/test_authentication.py | 2 ++ examples/chat_app/test_chat_app.py | 2 ++ examples/pytests/{ => app}/__init__.py | 0 examples/pytests/app/startup.py | 21 +++++++++++++++ examples/pytests/main.py | 27 +++---------------- examples/pytests/pytest.ini | 2 ++ examples/pytests/requirements.txt | 1 + examples/pytests/tests/conftest.py | 20 ++++++++++++++ .../tests/test_with_auto_index_page.py | 27 ------------------- ...st_with_routing.py => test_with_screen.py} | 14 ---------- ...h_user_simulation.py => test_with_user.py} | 9 ------- examples/todo_list/test_todo_list.py | 2 ++ nicegui/testing/{fixtures.py => plugin.py} | 0 pyproject.toml | 4 +-- test_startup.sh | 4 +++ tests/conftest.py | 3 +-- tests/test_download.py | 8 +++--- 17 files changed, 64 insertions(+), 82 deletions(-) rename examples/pytests/{ => app}/__init__.py (100%) create mode 100644 examples/pytests/app/startup.py create mode 100644 examples/pytests/pytest.ini create mode 100644 examples/pytests/tests/conftest.py delete mode 100644 examples/pytests/tests/test_with_auto_index_page.py rename examples/pytests/tests/{test_with_routing.py => test_with_screen.py} (81%) rename examples/pytests/tests/{test_with_user_simulation.py => test_with_user.py} (79%) rename nicegui/testing/{fixtures.py => plugin.py} (100%) diff --git a/examples/authentication/test_authentication.py b/examples/authentication/test_authentication.py index 967dfb5f9..4ba056eab 100644 --- a/examples/authentication/test_authentication.py +++ b/examples/authentication/test_authentication.py @@ -6,6 +6,8 @@ # pylint: disable=missing-function-docstring +pytest_plugins = ['nicegui.testing.plugin'] + @pytest.mark.module_under_test(main) async def test_login_logoff(user: User) -> None: diff --git a/examples/chat_app/test_chat_app.py b/examples/chat_app/test_chat_app.py index 5c83e65d9..7a0f21fff 100644 --- a/examples/chat_app/test_chat_app.py +++ b/examples/chat_app/test_chat_app.py @@ -7,6 +7,8 @@ from . import main +pytest_plugins = ['nicegui.testing.plugin'] + @pytest.mark.module_under_test(main) async def test_basic_startup_appearance(user: User) -> None: diff --git a/examples/pytests/__init__.py b/examples/pytests/app/__init__.py similarity index 100% rename from examples/pytests/__init__.py rename to examples/pytests/app/__init__.py diff --git a/examples/pytests/app/startup.py b/examples/pytests/app/startup.py new file mode 100644 index 000000000..954aa6fe0 --- /dev/null +++ b/examples/pytests/app/startup.py @@ -0,0 +1,21 @@ +from nicegui import Client, ui + +# pylint: disable=missing-function-docstring + + +def startup() -> None: + @ui.page('/') + def main_page() -> None: + ui.markdown('Try running `pytest` on this project!') + ui.button('Click me', on_click=lambda: ui.notify('Button clicked!')) + ui.link('go to subpage', '/subpage') + + @ui.page('/subpage') + def sub_page() -> None: + ui.markdown('This is a subpage') + + @ui.page('/with_connected') + async def with_connected(client: Client) -> None: + ui.markdown('This is an async connection demo') + await client.connected() + ui.markdown('Connected!') diff --git a/examples/pytests/main.py b/examples/pytests/main.py index 6df6d7fe3..e0464a729 100755 --- a/examples/pytests/main.py +++ b/examples/pytests/main.py @@ -1,27 +1,8 @@ #!/usr/bin/env python3 -from nicegui import Client, ui +from app.startup import startup -# pylint: disable=missing-function-docstring +from nicegui import app, ui +app.on_startup(startup) -@ui.page('/') -def main_page() -> None: - ui.markdown('Try running `pytest` on this project!') - ui.button('Click me', on_click=lambda: ui.notify('Button clicked!')) - ui.link('go to subpage', '/subpage') - - -@ui.page('/subpage') -def sub_page() -> None: - ui.markdown('This is a subpage') - - -@ui.page('/with_connected') -async def with_connected(client: Client) -> None: - ui.markdown('This is an async connection demo') - await client.connected() - ui.markdown('Connected!') - - -if __name__ in {'__main__', '__mp_main__'}: - ui.run() +ui.run() diff --git a/examples/pytests/pytest.ini b/examples/pytests/pytest.ini new file mode 100644 index 000000000..2f4c80e30 --- /dev/null +++ b/examples/pytests/pytest.ini @@ -0,0 +1,2 @@ +[pytest] +asyncio_mode = auto diff --git a/examples/pytests/requirements.txt b/examples/pytests/requirements.txt index 3219eace3..e97c254f8 100644 --- a/examples/pytests/requirements.txt +++ b/examples/pytests/requirements.txt @@ -1,3 +1,4 @@ +nicegui icecream pytest-asyncio pytest-selenium diff --git a/examples/pytests/tests/conftest.py b/examples/pytests/tests/conftest.py new file mode 100644 index 000000000..c1239de63 --- /dev/null +++ b/examples/pytests/tests/conftest.py @@ -0,0 +1,20 @@ +from typing import Generator + +import pytest +from app.startup import startup + +from nicegui.testing import Screen, User + +pytest_plugins = ['nicegui.testing.plugin'] + + +@pytest.fixture +def user(user: User) -> Generator[User, None, None]: + startup() + yield user + + +@pytest.fixture +def screen(screen: Screen) -> Generator[Screen, None, None]: + startup() + yield screen diff --git a/examples/pytests/tests/test_with_auto_index_page.py b/examples/pytests/tests/test_with_auto_index_page.py deleted file mode 100644 index ed37bd97a..000000000 --- a/examples/pytests/tests/test_with_auto_index_page.py +++ /dev/null @@ -1,27 +0,0 @@ -from nicegui.testing import Screen - -from ..main import main_page, sub_page - -# pylint: disable=missing-function-docstring - - -def test_markdown_message(screen: Screen) -> None: - main_page() - - screen.open('/') - screen.should_contain('Try running') - - -def test_button_click(screen: Screen) -> None: - main_page() - - screen.open('/') - screen.click('Click me') - screen.should_contain('Button clicked!') - - -def test_sub_page(screen: Screen) -> None: - sub_page() - - screen.open('/') - screen.should_contain('This is a subpage') diff --git a/examples/pytests/tests/test_with_routing.py b/examples/pytests/tests/test_with_screen.py similarity index 81% rename from examples/pytests/tests/test_with_routing.py rename to examples/pytests/tests/test_with_screen.py index 9f951a2dc..90678372d 100644 --- a/examples/pytests/tests/test_with_routing.py +++ b/examples/pytests/tests/test_with_screen.py @@ -1,45 +1,31 @@ -import importlib - from nicegui.testing import Screen -from .. import main - # pylint: disable=missing-function-docstring def test_markdown_message(screen: Screen) -> None: - importlib.reload(main) - screen.open('/') screen.should_contain('Try running') def test_button_click(screen: Screen) -> None: - importlib.reload(main) - screen.open('/') screen.click('Click me') screen.should_contain('Button clicked!') def test_sub_page(screen: Screen) -> None: - importlib.reload(main) - screen.open('/subpage') screen.should_contain('This is a subpage') def test_with_connected(screen: Screen) -> None: - importlib.reload(main) - screen.open('/with_connected') screen.should_contain('This is an async connection demo') screen.should_contain('Connected!') def test_navigation(screen: Screen) -> None: - importlib.reload(main) - screen.open('/') screen.click('go to subpage') screen.should_contain('This is a subpage') diff --git a/examples/pytests/tests/test_with_user_simulation.py b/examples/pytests/tests/test_with_user.py similarity index 79% rename from examples/pytests/tests/test_with_user_simulation.py rename to examples/pytests/tests/test_with_user.py index 4ba053f71..daf765e1a 100644 --- a/examples/pytests/tests/test_with_user_simulation.py +++ b/examples/pytests/tests/test_with_user.py @@ -1,39 +1,30 @@ -import pytest - from nicegui.testing import User -from .. import main - # pylint: disable=missing-function-docstring -@pytest.mark.module_under_test(main) async def test_markdown_message(user: User) -> None: await user.open('/') await user.should_see('Try running') -@pytest.mark.module_under_test(main) async def test_button_click(user: User) -> None: await user.open('/') user.find('Click me').click() await user.should_see('Button clicked!') -@pytest.mark.module_under_test(main) async def test_sub_page(user: User) -> None: await user.open('/subpage') await user.should_see('This is a subpage') -@pytest.mark.module_under_test(main) async def test_with_connected(user: User) -> None: await user.open('/with_connected') await user.should_see('This is an async connection demo') await user.should_see('Connected!') -@pytest.mark.module_under_test(main) async def test_navigation(user: User) -> None: await user.open('/') user.find('go to subpage').click() diff --git a/examples/todo_list/test_todo_list.py b/examples/todo_list/test_todo_list.py index b0eb272cd..b079125cd 100644 --- a/examples/todo_list/test_todo_list.py +++ b/examples/todo_list/test_todo_list.py @@ -6,6 +6,8 @@ # pylint: disable=missing-function-docstring +pytest_plugins = ['nicegui.testing.plugin'] + @pytest.mark.module_under_test(main) async def test_checking_items(user: User) -> None: diff --git a/nicegui/testing/fixtures.py b/nicegui/testing/plugin.py similarity index 100% rename from nicegui/testing/fixtures.py rename to nicegui/testing/plugin.py diff --git a/pyproject.toml b/pyproject.toml index 7addc09f8..224568caf 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -78,6 +78,7 @@ build-backend = "poetry.core.masonry.api" [tool.pytest.ini_options] addopts = "--driver Chrome" asyncio_mode = "auto" +testpaths = ["tests"] [tool.mypy] python_version = "3.8" @@ -129,6 +130,3 @@ ignore = [ exclude = [ "website/documentation/content/*", ] - -[tool.poetry.plugins.pytest11] -pytest-nicegui = "nicegui.testing.fixtures" diff --git a/test_startup.sh b/test_startup.sh index 1bd80931f..e97381be5 100755 --- a/test_startup.sh +++ b/test_startup.sh @@ -60,5 +60,9 @@ do elif test -f $path/main.py; then check $path/main.py || error=1 fi + if pytest -q --collect-only $path >/dev/null 2>&1; then + echo "running tests for $path" + pytest $path || error=1 + fi done test $error -eq 0 diff --git a/tests/conftest.py b/tests/conftest.py index 86ea344a0..b065af2cf 100644 --- a/tests/conftest.py +++ b/tests/conftest.py @@ -1,2 +1 @@ -# pylint: disable=wildcard-import,unused-wildcard-import -from nicegui.testing.fixtures import * # noqa: F403 +pytest_plugins = ['nicegui.testing.plugin'] diff --git a/tests/test_download.py b/tests/test_download.py index bfa8aaeae..93a108f46 100644 --- a/tests/test_download.py +++ b/tests/test_download.py @@ -5,7 +5,7 @@ from fastapi.responses import PlainTextResponse from nicegui import app, ui -from nicegui.testing import Screen, fixtures +from nicegui.testing import Screen, plugin @pytest.fixture @@ -25,7 +25,7 @@ def test(): screen.open('/') screen.click('Download') screen.wait(0.5) - assert (fixtures.DOWNLOAD_DIR / 'test.txt').read_text() == 'test' + assert (plugin.DOWNLOAD_DIR / 'test.txt').read_text() == 'test' def test_downloading_local_file_as_src(screen: Screen): @@ -36,7 +36,7 @@ def test_downloading_local_file_as_src(screen: Screen): route_count_before_download = len(app.routes) screen.click('download') screen.wait(0.5) - assert (fixtures.DOWNLOAD_DIR / 'slide1.jpg').exists() + assert (plugin.DOWNLOAD_DIR / 'slide1.jpg').exists() assert len(app.routes) == route_count_before_download @@ -46,4 +46,4 @@ def test_download_raw_data(screen: Screen): screen.open('/') screen.click('download') screen.wait(0.5) - assert (fixtures.DOWNLOAD_DIR / 'test.txt').read_text() == 'test' + assert (plugin.DOWNLOAD_DIR / 'test.txt').read_text() == 'test' From b54adf7fd25541a5f4ec2fc4b40ec5104919beb9 Mon Sep 17 00:00:00 2001 From: Falko Schindler Date: Fri, 2 Aug 2024 11:19:03 +0200 Subject: [PATCH 2/2] remove redundant test --- tests/test_element_filter.py | 14 -------------- 1 file changed, 14 deletions(-) diff --git a/tests/test_element_filter.py b/tests/test_element_filter.py index 7f244fe51..0f3f92706 100644 --- a/tests/test_element_filter.py +++ b/tests/test_element_filter.py @@ -100,20 +100,6 @@ def test_find_within_marker2(): assert texts(ElementFilter(kind=ui.label).not_within(marker='a')) == ['Label 3'] -def test_find_within_marker_combination(): - with ui.card().mark('a'): - ui.label('Label 1') - with ui.card().mark('b'): - ui.label('Label 2') - with ui.card().mark('a'): - ui.label('Label 3') - with ui.card().mark('b'): - ui.label('Label 4') - - assert texts(ElementFilter(kind=ui.label).within(marker='a b')) == ['Label 2'] - assert texts(ElementFilter(kind=ui.label).within(marker='a').within(marker='b')) == ['Label 2'] - - def test_find_within_instance(): ui.button('button A') ui.label('label A')