Skip to content

Commit faf6b6c

Browse files
committed
refactor and cleanup tests
1 parent ac27f36 commit faf6b6c

File tree

8 files changed

+135
-105
lines changed

8 files changed

+135
-105
lines changed

Diff for: idom/testing.py

+55-23
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
import time
12
from contextlib import contextmanager, AbstractContextManager
23
from typing import Callable, Tuple, Iterator, Type, Optional, Union, Any
34

@@ -9,17 +10,18 @@
910
import idom
1011
from idom.core.element import AbstractElement, ElementConstructor
1112
from idom.server.sanic import SanicRenderServer, PerClientStateServer
12-
from idom.server.prefab import hotswap_server
13+
from idom.server.prefab import hotswap_server, multiview_server, MultiViewMount
1314
from idom.server.utils import find_available_port
1415
from idom.utils import Ref
1516

1617

1718
DisplayFunction = Callable[[Union[ElementConstructor, AbstractElement], str], None]
1819

1920

20-
def create_selenium_display_context(
21-
headless: bool, driver_timeout: float = 3.0
22-
) -> Iterator[DisplayFunction]:
21+
@contextmanager
22+
def open_selenium_chrome_driver_and_display_context(
23+
headless: bool, driver_timeout: float = 3.0, wait_for_server_start: float = 1.0
24+
) -> Iterator[Tuple[Chrome, DisplayFunction]]:
2325
host = "127.0.0.1"
2426
port = find_available_port(host)
2527
server_url = f"http://{host}:{port}"
@@ -29,17 +31,21 @@ def create_selenium_display_context(
2931
page_load_timeout=driver_timeout,
3032
implicit_wait_timeout=driver_timeout,
3133
) as driver:
32-
with open_sanic_mount_and_server(
34+
with open_sanic_hotswap_mount_and_server(
3335
server_type=PerClientStateServer, host=host, port=port
3436
) as (mount, server):
35-
return create_selenium_page_get_and_display_context(
36-
driver, server, server_url, mount
37-
)[1]
37+
time.sleep(wait_for_server_start)
38+
yield (
39+
driver,
40+
create_selenium_page_get_and_display_context(
41+
driver, server, server_url, mount
42+
)[1],
43+
)
3844

3945

4046
def create_selenium_page_get_and_display_context(
4147
driver: WebDriver,
42-
server: "_RenderServerSavesLastError",
48+
server: "SanicRenderServerWithLastError",
4349
server_url: str,
4450
element_mount_function: Callable[..., None],
4551
) -> Tuple[Callable[[str], None], "AbstractContextManager[DisplayFunction]"]:
@@ -86,24 +92,46 @@ def display_context():
8692

8793

8894
@contextmanager
89-
def open_sanic_mount_and_server(
95+
def open_sanic_multiview_mount_and_server(
9096
server_type: Type[SanicRenderServer],
9197
host: str,
9298
port: int,
99+
debug: bool = False,
100+
app: Optional[Sanic] = None,
101+
) -> Iterator[Tuple[MultiViewMount, SanicRenderServer]]:
102+
server_type = create_sanic_server_type_for_testing(server_type)
103+
try:
104+
yield multiview_server(
105+
server_type,
106+
host,
107+
port,
108+
server_options={"cors": True},
109+
run_options={"debug": debug},
110+
app=app,
111+
)
112+
finally:
113+
if server_type.last_server_error_for_idom_testing.current is not None:
114+
raise server_type.last_server_error_for_idom_testing.current # pragma: no cover
115+
116+
117+
@contextmanager
118+
def open_sanic_hotswap_mount_and_server(
119+
server_type: Type[SanicRenderServer],
120+
host: str,
121+
port: int,
122+
sync_views: bool = False,
123+
debug: bool = False,
93124
app: Optional[Sanic] = None,
94125
) -> Iterator[Tuple[Callable[..., None], SanicRenderServer]]:
126+
server_type = create_sanic_server_type_for_testing(server_type)
95127
try:
96128
yield hotswap_server(
97-
(
98-
server_type
99-
if issubclass(server_type, _RenderServerSavesLastError)
100-
else create_sanic_server_type_for_testing(server_type)
101-
),
129+
server_type,
102130
host,
103131
port,
104132
server_options={"cors": True},
105-
run_options={},
106-
sync_views=False,
133+
run_options={"debug": debug},
134+
sync_views=sync_views,
107135
app=app,
108136
)
109137
finally:
@@ -113,11 +141,15 @@ def open_sanic_mount_and_server(
113141

114142
def create_sanic_server_type_for_testing(
115143
server_type: Type[SanicRenderServer],
116-
) -> Type["_RenderServerSavesLastError"]:
117-
return type(
118-
server_type.__name__,
119-
(_RenderServerSavesLastError, server_type),
120-
{"last_server_error_for_idom_testing": Ref(None)},
144+
) -> Type["SanicRenderServerWithLastError"]:
145+
return (
146+
server_type
147+
if issubclass(server_type, SanicRenderServerWithLastError)
148+
else type(
149+
server_type.__name__,
150+
(SanicRenderServerWithLastError, server_type),
151+
{"last_server_error_for_idom_testing": Ref(None)},
152+
)
121153
)
122154

123155

@@ -143,7 +175,7 @@ def open_selenium_chrome_driver(
143175
driver.quit()
144176

145177

146-
class _RenderServerSavesLastError(SanicRenderServer):
178+
class SanicRenderServerWithLastError(SanicRenderServer):
147179
"""A server that updates the ``last_server_error`` fixture"""
148180

149181
last_server_error_for_idom_testing: Ref[Optional[Exception]]

Diff for: requirements/extras.txt

+3
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,9 @@
22
sanic <19.12.0
33
sanic-cors >=0.9.9
44

5+
# extra=testing
6+
selenium
7+
58
# extra=matplotlib
69
matplotlib
710

Diff for: tests/conftest.py

+19-38
Original file line numberDiff line numberDiff line change
@@ -2,9 +2,8 @@
22
import inspect
33
import time
44
from contextlib import ExitStack
5-
from typing import Callable, Any, Type, Tuple, Iterator, Iterable, Union
5+
from typing import Callable, Any, Tuple, Iterator, Iterable, Union
66

7-
import sanic
87
from loguru import logger
98
import pytest
109
from _pytest.logging import caplog as _caplog, LogCaptureFixture # noqa
@@ -23,18 +22,12 @@
2322
from idom.server.sanic import PerClientStateServer
2423
from idom.testing import (
2524
create_selenium_page_get_and_display_context,
26-
open_sanic_mount_and_server,
25+
open_sanic_hotswap_mount_and_server,
2726
open_selenium_chrome_driver,
28-
create_sanic_server_type_for_testing,
27+
SanicRenderServerWithLastError,
2928
)
3029

3130

32-
# Default is an error because we want to know whether we are setting the last
33-
# error while testing. A refactor could miss the code path that catches serve
34-
# errors.
35-
default_error = NotImplementedError()
36-
37-
3831
def pytest_collection_modifyitems(items: Iterable[Any]) -> None:
3932
for item in items:
4033
if isinstance(item, pytest.Function):
@@ -113,13 +106,13 @@ def driver(create_driver: Callable[[], Chrome]) -> Chrome:
113106

114107

115108
@pytest.fixture(scope="module")
116-
def create_driver(pytestconfig: Config):
109+
def create_driver(driver_is_headless):
117110
"""A Selenium web driver"""
118111
with ExitStack() as exit_stack:
119112

120113
def create():
121114
return exit_stack.enter_context(
122-
open_selenium_chrome_driver(headless=bool(pytestconfig.option.headless))
115+
open_selenium_chrome_driver(headless=driver_is_headless)
123116
)
124117

125118
yield create
@@ -139,49 +132,32 @@ def server(
139132
) -> Iterator[AbstractRenderServer]:
140133
"""An IDOM server"""
141134
server = mount_and_server[1]
135+
if not isinstance(server, SanicRenderServerWithLastError):
136+
raise TypeError(
137+
"Servers for testing must be SanicRenderServerWithLastError instances"
138+
)
142139
time.sleep(1) # wait for server to start
143140
yield server
144141
server.stop()
145142

146143

147144
@pytest.fixture(scope="module")
148145
def mount_and_server(
149-
application: sanic.Sanic,
150-
fixturized_server_type: Type[AbstractRenderServer],
151-
host: str,
152-
port: int,
153-
last_server_error: idom.Ref[Exception],
146+
host: str, port: int
154147
) -> Tuple[Callable[..., None], AbstractRenderServer]:
155148
"""An IDOM layout mount function and server as a tuple
156149
157150
The ``mount`` and ``server`` fixtures use this.
158151
"""
159-
with open_sanic_mount_and_server(
160-
fixturized_server_type, host=host, port=port, app=application
152+
with open_sanic_hotswap_mount_and_server(
153+
PerClientStateServer, host=host, port=port
161154
) as mount_and_server:
162155
yield mount_and_server
163156

164157

165158
@pytest.fixture(scope="module")
166-
def application():
167-
return sanic.Sanic()
168-
169-
170-
@pytest.fixture(scope="module")
171-
def last_server_error(fixturized_server_type):
172-
"""A ``Ref`` containing the last server error. This must be populated by ``server_type``"""
173-
return fixturized_server_type.last_server_error_for_idom_testing
174-
175-
176-
@pytest.fixture(scope="module")
177-
def fixturized_server_type(server_type):
178-
return create_sanic_server_type_for_testing(server_type)
179-
180-
181-
@pytest.fixture(scope="module")
182-
def server_type() -> Type[AbstractRenderServer]:
183-
"""The type of server the ``mount_and_server`` fixture will use to initialize a server"""
184-
return PerClientStateServer
159+
def last_server_error(server):
160+
return server.last_server_error_for_idom_testing
185161

186162

187163
@pytest.fixture(scope="module")
@@ -210,6 +186,11 @@ def client_implementation():
210186
idom.client.current = original
211187

212188

189+
@pytest.fixture(scope="session")
190+
def driver_is_headless(pytestconfig: Config):
191+
return bool(pytestconfig.option.headless)
192+
193+
213194
@pytest.fixture(scope="session", autouse=True)
214195
def _restore_client(pytestconfig: Config) -> Iterator[None]:
215196
"""Restore the client's state before and after testing

Diff for: tests/test_core/test_hooks.py

+4-4
Original file line numberDiff line numberDiff line change
@@ -120,22 +120,22 @@ async def on_click(event):
120120
count = driver.find_element_by_id("count-view")
121121

122122
driver_wait.until(lambda d: constructor_call_count.current == 1)
123-
assert count.get_attribute("innerHTML") == "0"
123+
driver_wait.until(lambda d: count.get_attribute("innerHTML") == "0")
124124

125125
inner.click()
126126

127127
driver_wait.until(lambda d: constructor_call_count.current == 1)
128-
assert count.get_attribute("innerHTML") == "1"
128+
driver_wait.until(lambda d: count.get_attribute("innerHTML") == "1")
129129

130130
outer.click()
131131

132132
driver_wait.until(lambda d: constructor_call_count.current == 2)
133-
assert count.get_attribute("innerHTML") == "0"
133+
driver_wait.until(lambda d: count.get_attribute("innerHTML") == "0")
134134

135135
inner.click()
136136

137137
driver_wait.until(lambda d: constructor_call_count.current == 2)
138-
assert count.get_attribute("innerHTML") == "1"
138+
driver_wait.until(lambda d: count.get_attribute("innerHTML") == "1")
139139

140140

141141
def test_set_state_with_reducer_instead_of_value(driver, display):

Diff for: tests/test_server/test_sanic/test_base.py

+10-8
Original file line numberDiff line numberDiff line change
@@ -1,21 +1,23 @@
11
import asyncio
22

33
import pytest
4+
from sanic import Sanic
45

56
import idom
7+
from idom.server.sanic import PerClientStateServer
8+
from idom.testing import open_sanic_multiview_mount_and_server
69

710

811
@pytest.fixture(scope="module")
9-
def mount_and_server(fixturized_server_type, host, port, last_server_error):
10-
return idom.server.multiview_server(
11-
fixturized_server_type,
12+
def mount_and_server(host, port):
13+
with open_sanic_multiview_mount_and_server(
14+
PerClientStateServer,
1215
host,
1316
port,
14-
server_options={"last_server_error": last_server_error},
15-
run_options={"debug": True},
16-
# use the default app for the server just to get test coverage there
17-
app=None,
18-
)
17+
# test that we can use a custom app instance
18+
app=Sanic(),
19+
) as mount_and_server:
20+
yield mount_and_server
1921

2022

2123
def test_serve_has_loop_attribute(server):

Diff for: tests/test_server/test_sanic/test_multiview_server.py

+7-12
Original file line numberDiff line numberDiff line change
@@ -1,23 +1,18 @@
11
import pytest
22

33
import idom
4-
from idom.server import multiview_server
4+
from idom.server.sanic import PerClientStateServer
5+
from idom.testing import open_sanic_multiview_mount_and_server
56

67
from tests.driver_utils import no_such_element
78

89

910
@pytest.fixture(scope="module")
10-
def mount_and_server(
11-
fixturized_server_type, host, port, last_server_error, application
12-
):
13-
return multiview_server(
14-
fixturized_server_type,
15-
host,
16-
port,
17-
server_options={"last_server_error": last_server_error},
18-
run_options={"debug": True},
19-
app=application,
20-
)
11+
def mount_and_server(host, port):
12+
with open_sanic_multiview_mount_and_server(
13+
PerClientStateServer, host, port
14+
) as mount_and_server:
15+
yield mount_and_server
2116

2217

2318
def test_multiview_server(driver_get, driver, mount, server):

0 commit comments

Comments
 (0)