Skip to content

Commit 4d440eb

Browse files
authored
Merge pull request #3 from idom-team/archmonger
add basic test of IDOM
2 parents 6b64aa2 + 4e1a970 commit 4d440eb

File tree

9 files changed

+104
-26
lines changed

9 files changed

+104
-26
lines changed

.github/workflows/test.yml

+1-1
Original file line numberDiff line numberDiff line change
@@ -32,4 +32,4 @@ jobs:
3232
run: |
3333
npm install -g npm@latest
3434
npm --version
35-
nox -s test
35+
nox -s test -- --headless

noxfile.py

+7-4
Original file line numberDiff line numberDiff line change
@@ -39,18 +39,21 @@ def format(session: Session) -> None:
3939
def test(session: Session) -> None:
4040
"""Run the complete test suite"""
4141
session.install("--upgrade", "pip", "setuptools", "wheel")
42-
session.notify("test_suite")
42+
session.notify("test_suite", posargs=session.posargs)
4343
session.notify("test_style")
4444

4545

4646
@nox.session
4747
def test_suite(session: Session) -> None:
4848
"""Run the Python-based test suite"""
49-
session.env["IDOM_DEBUG_MODE"] = "1"
5049
install_requirements_file(session, "test-env")
5150
session.install(".[all]")
52-
session.chdir("tests")
53-
session.run("figure-it-out")
51+
52+
session.chdir(HERE / "tests")
53+
session.env["IDOM_DEBUG_MODE"] = "1"
54+
session.env["SELENIUM_HEADLESS"] = str(int("--headless" in session.posargs))
55+
session.run("python", "manage.py", "build_js")
56+
session.run("python", "manage.py", "test")
5457

5558

5659
@nox.session

requirements/test-env.txt

+5
Original file line numberDiff line numberDiff line change
@@ -1 +1,6 @@
11
django
2+
selenium
3+
4+
# required due issue with channels:
5+
# https://github.com/django/channels/issues/1639#issuecomment-817994671
6+
twisted<21

src/django_idom/websocket_consumer.py

+18-14
Original file line numberDiff line numberDiff line change
@@ -3,9 +3,9 @@
33
from typing import Any
44

55
from channels.generic.websocket import AsyncJsonWebsocketConsumer
6-
from idom.core.component import ComponentConstructor
76
from idom.core.dispatcher import dispatch_single_view
8-
from idom.core.layout import Layout
7+
from idom.core.layout import Layout, LayoutEvent
8+
from idom.core.proto import ComponentConstructor
99

1010

1111
class IdomAsyncWebSocketConsumer(AsyncJsonWebsocketConsumer):
@@ -19,22 +19,26 @@ def __init__(
1919

2020
async def connect(self) -> None:
2121
await super().connect()
22-
self._idom_recv_queue = recv_queue = asyncio.Queue()
23-
self._idom_dispatcher_future = asyncio.ensure_future(
24-
dispatch_single_view(
25-
Layout(self._idom_component_constructor()),
26-
self.send_json,
27-
recv_queue.get,
28-
)
29-
)
22+
self._idom_dispatcher_future = asyncio.ensure_future(self._run_dispatch_loop())
3023

31-
async def close(self, *args: Any, **kwargs: Any) -> None:
24+
async def disconnect(self, code: int) -> None:
3225
if self._idom_dispatcher_future.done():
3326
await self._idom_dispatcher_future
3427
else:
3528
self._idom_dispatcher_future.cancel()
36-
await asyncio.wait([self._idom_dispatcher_future])
37-
await super().close(*args, **kwargs)
29+
await super().disconnect(code)
3830

3931
async def receive_json(self, content: Any, **kwargs: Any) -> None:
40-
await self._idom_recv_queue.put(content)
32+
await self._idom_recv_queue.put(LayoutEvent(**content))
33+
34+
async def _run_dispatch_loop(self):
35+
self._idom_recv_queue = recv_queue = asyncio.Queue()
36+
try:
37+
await dispatch_single_view(
38+
Layout(self._idom_component_constructor()),
39+
self.send_json,
40+
recv_queue.get,
41+
)
42+
except Exception:
43+
await self.close()
44+
raise

tests/js/rollup.config.js

+11
Original file line numberDiff line numberDiff line change
@@ -19,4 +19,15 @@ export default {
1919
),
2020
}),
2121
],
22+
onwarn: function (warning) {
23+
// Skip certain warnings
24+
25+
// should intercept ... but doesn't in some rollup versions
26+
if (warning.code === "THIS_IS_UNDEFINED") {
27+
return;
28+
}
29+
30+
// console.warn everything else
31+
console.warn(warning.message);
32+
},
2233
};

tests/test_app/asgi.py

+2-2
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@
1414

1515
from django_idom import IdomAsyncWebSocketConsumer # noqa: E402
1616

17-
from .views import HelloWorld
17+
from .views import Root
1818

1919

2020
os.environ.setdefault("DJANGO_SETTINGS_MODULE", "test_app.settings")
@@ -29,7 +29,7 @@
2929
{
3030
"http": http_asgi_app,
3131
"websocket": URLRouter(
32-
[url("", IdomAsyncWebSocketConsumer.as_asgi(component=HelloWorld))]
32+
[url("", IdomAsyncWebSocketConsumer.as_asgi(component=Root))]
3333
),
3434
}
3535
)

tests/test_app/settings.py

+5-2
Original file line numberDiff line numberDiff line change
@@ -72,8 +72,11 @@
7272
DATABASES = {
7373
"default": {
7474
"ENGINE": "django.db.backends.sqlite3",
75-
"NAME": BASE_DIR / "db.sqlite3",
76-
}
75+
"NAME": os.path.join(BASE_DIR, "db.sqlite3"),
76+
"TEST": {
77+
"NAME": os.path.join(BASE_DIR, "db_test.sqlite3"),
78+
},
79+
},
7780
}
7881

7982
# Password validation

tests/test_app/tests.py

+35-3
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,37 @@
1-
from django.test import TestCase
1+
import os
22

3+
from channels.testing import ChannelsLiveServerTestCase
4+
from selenium import webdriver
5+
from selenium.webdriver.support.ui import WebDriverWait
36

4-
class Temp(TestCase):
5-
pass
7+
8+
class TestIdomCapabilities(ChannelsLiveServerTestCase):
9+
def setUp(self):
10+
self.driver = make_driver(5, 5)
11+
self.driver.get(self.live_server_url)
12+
13+
def tearDown(self) -> None:
14+
self.driver.quit()
15+
16+
def wait_until(self, condition, timeout=5):
17+
WebDriverWait(self.driver, timeout).until(lambda driver: condition())
18+
19+
def test_hello_world(self):
20+
self.driver.find_element_by_id("hello-world")
21+
22+
def test_counter(self):
23+
button = self.driver.find_element_by_id("counter-inc")
24+
count = self.driver.find_element_by_id("counter-num")
25+
26+
for i in range(5):
27+
self.wait_until(lambda: count.get_attribute("data-count") == str(i))
28+
button.click()
29+
30+
31+
def make_driver(page_load_timeout, implicit_wait_timeout):
32+
options = webdriver.ChromeOptions()
33+
options.headless = bool(int(os.environ.get("SELENIUM_HEADLESS", 0)))
34+
driver = webdriver.Chrome(options=options)
35+
driver.set_page_load_timeout(page_load_timeout)
36+
driver.implicitly_wait(implicit_wait_timeout)
37+
return driver

tests/test_app/views.py

+20
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,26 @@ def base_template(request):
99
return HttpResponse(template.render(context, request))
1010

1111

12+
@idom.component
13+
def Root():
14+
return idom.html.div(HelloWorld(), Counter())
15+
16+
1217
@idom.component
1318
def HelloWorld():
1419
return idom.html.h1({"id": "hello-world"}, "Hello World!")
20+
21+
22+
@idom.component
23+
def Counter():
24+
count, set_count = idom.hooks.use_state(0)
25+
return idom.html.div(
26+
idom.html.button(
27+
{"id": "counter-inc", "onClick": lambda event: set_count(count + 1)},
28+
"Click me!",
29+
),
30+
idom.html.p(
31+
{"id": "counter-num", "data-count": count},
32+
f"Current count is: {count}",
33+
),
34+
)

0 commit comments

Comments
 (0)