Skip to content

Commit 3197580

Browse files
committed
re-organize tests to support testing multiple servers
1 parent 29bac3a commit 3197580

19 files changed

+131
-89
lines changed

idom/server/__init__.py

Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -49,8 +49,7 @@ def multiview_server(
4949
"""
5050
mount, element = multiview()
5151
server_instance = server(element)
52-
if server_options:
53-
server_instance.configure(server_options)
52+
server_instance.configure(server_options)
5453
server_instance.daemon(host, port, **(run_options or {}))
5554
return mount, server_instance
5655

@@ -81,7 +80,6 @@ def hotswap_server(
8180
"""
8281
mount, element = hotswap(shared=True)
8382
server_instance = server(element)
84-
if server_options:
85-
server_instance.configure(server_options)
83+
server_instance.configure(server_options)
8684
server_instance.daemon(host, port, **(run_options or {}))
8785
return mount, server_instance

idom/server/base.py

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -71,11 +71,17 @@ def register(self: _Self, app: _App) -> _Self:
7171
self._app = app
7272
return self
7373

74-
def configure(self: _Self, config: _Config) -> _Self:
74+
def configure(self: _Self, config: Optional[_Config] = None) -> _Self:
7575
"""Configure this extension."""
76-
self._config = self._update_config(self._config, config)
76+
if config is not None:
77+
self._config = self._update_config(self._config, config)
7778
return self
7879

80+
@abc.abstractmethod
81+
def stop(self) -> None:
82+
"""Stop the running application"""
83+
raise NotImplementedError()
84+
7985
@abc.abstractmethod
8086
def _init_config(self) -> _Config:
8187
"""Return the default configuration options."""

idom/server/sanic.py

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,9 @@ class Config(TypedDict, total=False):
3030
class SanicRenderServer(AbstractRenderServer[Sanic, Config]):
3131
"""Base ``sanic`` extension."""
3232

33+
def stop(self) -> None:
34+
self.application.stop()
35+
3336
def _init_config(self) -> Config:
3437
return Config(cors=False, url_prefix="", webpage_route=True)
3538

tests/conftest.py

Lines changed: 115 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,16 +1,34 @@
11
import logging
22
import inspect
3+
import time
4+
from socket import socket
35

46
from loguru import logger
57
import pytest
68
from _pytest.logging import caplog as _caplog # noqa
9+
from selenium.webdriver import Chrome
10+
from selenium.webdriver.chrome.options import Options
11+
12+
import pyalect.builtins.pytest # noqa
13+
14+
import idom
15+
from idom.server import hotswap_server
16+
from idom.server.sanic import PerClientState
17+
18+
19+
# Default is an error because we want to know whether we are setting the last
20+
# error while testing. A refactor could miss the code path that catches serve
21+
# errors.
22+
default_error = NotImplementedError()
723

824

925
def pytest_collection_modifyitems(items):
1026
for item in items:
1127
if isinstance(item, pytest.Function):
1228
if inspect.iscoroutinefunction(item.function):
1329
item.add_marker(pytest.mark.asyncio)
30+
if "driver" in item.fixturenames:
31+
item.add_marker("slow")
1432

1533

1634
def pytest_addoption(parser):
@@ -30,3 +48,100 @@ def emit(self, record):
3048
handler_id = logger.add(PropogateHandler(), format="{message}")
3149
yield _caplog
3250
logger.remove(handler_id)
51+
52+
53+
@pytest.fixture(scope="session", autouse=True)
54+
def fresh_client():
55+
idom.client.restore()
56+
yield
57+
idom.client.restore()
58+
59+
60+
@pytest.fixture
61+
def display(driver, server, mount, host, port, last_server_error):
62+
def display(element, query=""):
63+
mount(element)
64+
driver.get(f"http://{host}:{port}/client/index.html")
65+
66+
try:
67+
yield display
68+
finally:
69+
last_error = last_server_error.get()
70+
if last_error is default_error:
71+
msg = f"The server {server} never ran or did not set the 'last_server_error' fixture."
72+
raise NotImplementedError(msg)
73+
elif last_error is not None:
74+
raise last_error
75+
76+
77+
@pytest.fixture(scope="session")
78+
def driver(pytestconfig):
79+
chrome_options = Options()
80+
81+
if getattr(pytestconfig.option, "headless", False):
82+
chrome_options.headless = True
83+
84+
driver = Chrome(options=chrome_options)
85+
86+
driver.set_window_size(1080, 800)
87+
driver.set_page_load_timeout(3)
88+
driver.implicitly_wait(3)
89+
90+
try:
91+
yield driver
92+
finally:
93+
driver.quit()
94+
95+
96+
@pytest.fixture(scope="module")
97+
def mount(mount_and_server):
98+
return mount_and_server[0]
99+
100+
101+
@pytest.fixture(scope="module")
102+
def server(mount_and_server):
103+
server = mount_and_server[1]
104+
time.sleep(1)
105+
yield server
106+
server.stop()
107+
108+
109+
@pytest.fixture(scope="module")
110+
def mount_and_server(server_type, host, port):
111+
return hotswap_server(server_type, host, port, run_options={"debug": True})
112+
113+
114+
@pytest.fixture(scope="module")
115+
def server_type(last_server_error):
116+
class ServerWithErrorCatch(PerClientState):
117+
async def _stream_route(self, request, socket):
118+
last_server_error.set(None)
119+
try:
120+
await super()._stream_route(request, socket)
121+
except Exception as e:
122+
last_server_error.set(e)
123+
124+
return ServerWithErrorCatch
125+
126+
127+
@pytest.fixture(scope="session")
128+
def host():
129+
return "127.0.0.1"
130+
131+
132+
@pytest.fixture(scope="module")
133+
def port(host):
134+
sock = socket()
135+
sock.bind((host, 0))
136+
return sock.getsockname()[1]
137+
138+
139+
@pytest.fixture(scope="session")
140+
def last_server_error():
141+
return idom.Var(default_error)
142+
143+
144+
@pytest.fixture(autouse=True)
145+
def _clean_last_server_error(last_server_error):
146+
last_server_error.set(default_error)
147+
yield
File renamed without changes.

tests/test_browser/conftest.py

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

tests/test_browser/test_client.py renamed to tests/test_client.py

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@ def victory():
1515
yield Import("victory", install=True)
1616

1717

18+
@pytest.mark.slow
1819
def test_install(driver, display, victory):
1920
display(victory.VictoryBar)
2021

@@ -29,6 +30,7 @@ def test_raise_on_missing_import_path():
2930
client.import_path("module/that/does/not/exist")
3031

3132

33+
@pytest.mark.slow
3234
def test_custom_module(driver, display, victory):
3335
my_chart = define_module("my/chart", HERE / "my_chart.js")
3436

@@ -40,6 +42,7 @@ def test_custom_module(driver, display, victory):
4042
driver.find_element_by_class_name("VictoryContainer")
4143

4244

45+
@pytest.mark.slow
4346
def test_delete_module(victory):
4447
client.delete_module("victory")
4548
assert not client.module_exists("victory")
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.

tests/test_idom/__init__.py

Whitespace-only changes.

tests/test_idom/conftest.py

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

tests/test_idom/test_core/__init__.py

Whitespace-only changes.
File renamed without changes.
File renamed without changes.

0 commit comments

Comments
 (0)