Skip to content

Commit 5f8788c

Browse files
committed
start to switch test suite to playwright
1 parent 6af8059 commit 5f8788c

File tree

18 files changed

+76
-129
lines changed

18 files changed

+76
-129
lines changed

.github/workflows/test.yml

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,6 @@ jobs:
1515
runs-on: ubuntu-latest
1616
steps:
1717
- uses: actions/checkout@v2
18-
- uses: nanasess/setup-chromedriver@master
1918
- uses: actions/setup-node@v2
2019
with:
2120
node-version: "14.x"
@@ -38,7 +37,6 @@ jobs:
3837
os: [ubuntu-latest, macos-latest, windows-latest]
3938
steps:
4039
- uses: actions/checkout@v2
41-
- uses: nanasess/setup-chromedriver@master
4240
- uses: actions/setup-node@v2
4341
with:
4442
node-version: "14.x"

noxfile.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -179,7 +179,7 @@ def test_python_suite(session: Session) -> None:
179179
"""Run the Python-based test suite"""
180180
session.env["IDOM_DEBUG_MODE"] = "1"
181181
install_requirements_file(session, "test-env")
182-
182+
session.run("playwright install")
183183
posargs = session.posargs
184184
posargs += ["--reruns", "3", "--reruns-delay", "1"]
185185

requirements/pkg-extras.txt

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,13 @@
11
# extra=stable,sanic
2-
sanic <19.12.0
2+
sanic
33
sanic-cors
44

55
# extra=fastapi
66
fastapi >=0.63.0
77
uvicorn[standard] >=0.13.4
88

99
# extra=starlette
10-
fastapi >=0.16.0
10+
starlette >=0.13.6
1111
uvicorn[standard] >=0.13.4
1212

1313
# extra=flask

requirements/test-env.txt

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,9 @@
11
ipython
22
pytest
3-
pytest-asyncio
3+
pytest-asyncio>=0.17
44
pytest-cov
55
pytest-mock
66
pytest-rerunfailures
77
pytest-timeout
88
responses
9-
selenium
9+
playwright

setup.cfg

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,8 @@ testpaths = tests
2424
xfail_strict = True
2525
markers =
2626
slow: marks tests as slow (deselect with '-m "not slow"')
27-
python_files = assert_*.py test_*.py
27+
python_files = *asserts.py test_*.py
28+
asyncio_mode = auto
2829

2930
[coverage:report]
3031
fail_under = 100

src/idom/sample.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@
1111

1212
@component
1313
def App() -> VdomDict:
14-
return html._(
14+
return html.div(
1515
{"style": {"padding": "15px"}},
1616
html.h1("Sample Application"),
1717
html.p(

tests/__init__.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
from .utils.browser import Display

tests/conftest.py

Lines changed: 11 additions & 84 deletions
Original file line numberDiff line numberDiff line change
@@ -1,27 +1,21 @@
11
from __future__ import annotations
22

3-
import inspect
43
import os
5-
from typing import Any, List
4+
from dataclasses import dataclass
5+
from typing import Awaitable, Callable, List
66

77
import pytest
88
from _pytest.config import Config
99
from _pytest.config.argparsing import Parser
10-
from selenium.webdriver import Chrome, ChromeOptions
11-
from selenium.webdriver.support.ui import WebDriverWait
1210

1311
import idom
14-
from idom.testing import (
15-
ServerMountPoint,
16-
clear_idom_web_modules_dir,
17-
create_simple_selenium_web_driver,
18-
)
12+
from idom.testing import ServerMountPoint, clear_idom_web_modules_dir
13+
from tests.utils.browser import launch_browser, open_display
1914

2015

2116
def pytest_collection_modifyitems(
22-
session: pytest.Session, config: pytest.config.Config, items: List[pytest.Item]
17+
session: pytest.Session, config: Config, items: List[pytest.Item]
2318
) -> None:
24-
_mark_coros_as_async_tests(items)
2519
_skip_web_driver_tests_on_windows(items)
2620

2721

@@ -32,92 +26,25 @@ def pytest_addoption(parser: Parser) -> None:
3226
action="store_true",
3327
help="Whether to run browser tests in headless mode.",
3428
)
35-
parser.addoption(
36-
"--no-restore",
37-
dest="restore_client",
38-
action="store_false",
39-
help="Whether to restore the client build before testing.",
40-
)
4129

4230

4331
@pytest.fixture
44-
def display(driver, server_mount_point):
45-
display_id = idom.Ref(0)
46-
47-
def mount_and_display(component_constructor, query=None, check_mount=True):
48-
component_id = f"display-{display_id.set_current(display_id.current + 1)}"
49-
server_mount_point.mount(
50-
lambda: idom.html.div({"id": component_id}, component_constructor())
51-
)
52-
driver.get(server_mount_point.url(query=query))
53-
if check_mount:
54-
driver.find_element("id", component_id)
55-
return component_id
56-
57-
yield mount_and_display
32+
async def display(browser):
33+
async with open_display(browser, idom.server.any) as display:
34+
yield display
5835

5936

6037
@pytest.fixture
61-
def driver_get(driver, server_mount_point):
62-
return lambda query=None: driver.get(server_mount_point.url(query=query))
63-
64-
65-
@pytest.fixture
66-
async def server_mount_point():
67-
"""An IDOM layout mount function and server as a tuple
68-
69-
The ``mount`` and ``server`` fixtures use this.
70-
"""
71-
async with ServerMountPoint() as mount_point:
72-
yield mount_point
73-
74-
75-
@pytest.fixture(scope="module")
76-
def driver_wait(driver):
77-
return WebDriverWait(driver, 3)
78-
79-
80-
@pytest.fixture(scope="module")
81-
def driver(create_driver) -> Chrome:
82-
"""A Selenium web driver"""
83-
return create_driver()
84-
85-
86-
@pytest.fixture(scope="module")
87-
def create_driver(driver_is_headless):
88-
"""A Selenium web driver"""
89-
drivers = []
90-
91-
def create(**kwargs: Any):
92-
options = ChromeOptions()
93-
options.headless = driver_is_headless
94-
driver = create_simple_selenium_web_driver(driver_options=options, **kwargs)
95-
drivers.append(driver)
96-
return driver
97-
98-
yield create
99-
100-
for d in drivers:
101-
d.quit()
102-
103-
104-
@pytest.fixture(scope="session")
105-
def driver_is_headless(pytestconfig: Config):
106-
return bool(pytestconfig.option.headless)
38+
async def browser(pytestconfig: Config):
39+
async with launch_browser(headless=bool(pytestconfig.option.headless)) as browser:
40+
yield browser
10741

10842

10943
@pytest.fixture(autouse=True)
11044
def _clear_web_modules_dir_after_test():
11145
clear_idom_web_modules_dir()
11246

11347

114-
def _mark_coros_as_async_tests(items: List[pytest.Item]) -> None:
115-
for item in items:
116-
if isinstance(item, pytest.Function):
117-
if inspect.iscoroutinefunction(item.function):
118-
item.add_marker(pytest.mark.asyncio)
119-
120-
12148
def _skip_web_driver_tests_on_windows(items: List[pytest.Item]) -> None:
12249
if os.name == "nt":
12350
for item in items:

tests/driver_utils.py

Lines changed: 0 additions & 19 deletions
This file was deleted.

tests/test_client.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@
44

55
import idom
66
from idom.testing import ServerMountPoint
7-
from tests.driver_utils import send_keys
7+
from tests.utils.browser import send_keys
88

99

1010
JS_DIR = Path(__file__).parent / "js"

tests/test_core/test_hooks.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@
1010
from idom.core.serve import render_json_patch
1111
from idom.testing import HookCatcher, assert_idom_logged
1212
from idom.utils import Ref
13-
from tests.assert_utils import assert_same_items
13+
from tests.utils.asserts import assert_same_items
1414

1515

1616
async def test_must_be_rendering_in_layout_to_use_hooks():

tests/test_core/test_layout.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,7 @@
2121
capture_idom_logs,
2222
)
2323
from idom.utils import Ref
24-
from tests.assert_utils import assert_same_items
24+
from tests.utils.asserts import assert_same_items
2525

2626

2727
@pytest.fixture(autouse=True)

tests/test_sample.py

Lines changed: 4 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,7 @@
1-
from idom.sample import run_sample_app
1+
from idom.sample import App, run_sample_app
22
from idom.server.utils import find_available_port
3+
from tests import Display
34

45

5-
def test_sample_app(driver):
6-
host = "127.0.0.1"
7-
port = find_available_port(host, allow_reuse_waiting_ports=False)
8-
9-
run_sample_app(host=host, port=port, run_in_thread=True)
10-
11-
driver.get(f"http://{host}:{port}")
12-
13-
h1 = driver.find_element("tag name", "h1")
14-
15-
assert h1.get_attribute("innerHTML") == "Sample Application"
6+
async def test_sample_app(display: Display):
7+
await display.show(App)

tests/test_server/test_common/test_multiview.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@
77
from idom.server import sanic as idom_sanic
88
from idom.server import starlette as idom_starlette
99
from idom.testing import ServerMountPoint
10-
from tests.driver_utils import no_such_element
10+
from tests.utils.browser import no_such_element
1111

1212

1313
@pytest.fixture(

tests/test_widgets.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@
44
from selenium.webdriver.common.keys import Keys
55

66
import idom
7-
from tests.driver_utils import send_keys
7+
from tests.utils.browser import send_keys
88

99

1010
HERE = Path(__file__).parent

tests/utils/__init__.py

Whitespace-only changes.
File renamed without changes.

tests/utils/browser.py

Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,47 @@
1+
from __future__ import annotations
2+
3+
from contextlib import asynccontextmanager
4+
from typing import Any
5+
6+
from playwright.async_api import Browser, Page, async_playwright
7+
8+
from idom import html
9+
from idom.server import any as any_server
10+
from idom.server.types import ServerImplementation
11+
from idom.testing import ServerMountPoint
12+
from idom.types import RootComponentConstructor
13+
14+
15+
@asynccontextmanager
16+
async def launch_browser(headless: bool) -> Browser:
17+
async with async_playwright() as p:
18+
yield await p.chromium.launch(headless=headless)
19+
20+
21+
@asynccontextmanager
22+
async def open_display(
23+
browser: Browser, implementation: ServerImplementation[Any] = any_server
24+
) -> Display:
25+
async with ServerMountPoint(implementation) as mount:
26+
page = await browser.new_page()
27+
try:
28+
yield Display(page, mount)
29+
finally:
30+
await page.close()
31+
32+
33+
class Display:
34+
def __init__(self, page: Page, mount: ServerMountPoint) -> None:
35+
self.page = page
36+
self.mount = mount
37+
self._next_id = 0
38+
39+
async def show(
40+
self, component: RootComponentConstructor, query: dict[str, Any] | None = None
41+
) -> str:
42+
self._next_id += 1
43+
component_id = f"display-{self._next_id}"
44+
self.mount.mount(lambda: html.div({"id": component_id}, component()))
45+
await self.page.goto(self.mount.url(query=query))
46+
await self.page.wait_for_selector(f"#{component_id}")
47+
return component_id

0 commit comments

Comments
 (0)