Skip to content

Commit 7b10927

Browse files
committed
Pushing a flavor with a broken test, where the mock does self.document.values["calc"]. It's a good place to PR.
1 parent 0f986d3 commit 7b10927

File tree

11 files changed

+513
-148
lines changed

11 files changed

+513
-148
lines changed

poetry.lock

Lines changed: 94 additions & 148 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

pyproject.toml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -50,6 +50,7 @@ typeguard = ">=2.13.3"
5050
xdoctest = { extras = ["colors"], version = ">=0.15.10" }
5151
myst-parser = { version = ">=0.16.1" }
5252
requests = "^2.28.1"
53+
playwright = "^1.27"
5354
pytest-playwright = "^0.3.0"
5455
pytest-asyncio-cooperative = "^0.28.0"
5556
nox-poetry = "^1.0.1"

src/psc/fixtures.py

Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
"""Automate some testing."""
22
from __future__ import annotations
33

4+
import builtins
45
from dataclasses import dataclass
56
from dataclasses import field
67
from mimetypes import guess_type
@@ -180,3 +181,51 @@ def _route_handler(route: Route) -> None:
180181
# Don't spend 30 seconds on timeout
181182
page.set_default_timeout(12000)
182183
return page
184+
185+
186+
@dataclass
187+
class FakeDocument:
188+
"""Pretend to be a DOM that holds values at id's."""
189+
190+
values: dict[str, str] = field(default_factory=dict)
191+
log: list[str] = field(default_factory=list)
192+
193+
194+
@pytest.fixture
195+
def fake_document() -> FakeDocument:
196+
"""Yield a document that cleans up."""
197+
yield FakeDocument()
198+
199+
200+
@dataclass
201+
class ElementNode:
202+
value: str
203+
document: FakeDocument
204+
205+
def write(self, value: str) -> None:
206+
"""Collect anything that is written to the node."""
207+
self.document.log.append(value)
208+
209+
def removeAttribute(self, name) -> None:
210+
"""Pretend to remove an attribute from this node."""
211+
pass
212+
213+
214+
@dataclass
215+
class ElementCallable:
216+
document: FakeDocument
217+
218+
def __call__(self, key: str) -> ElementNode:
219+
value = self.document.values[key]
220+
node = ElementNode(value, self.document)
221+
return node
222+
223+
224+
@pytest.fixture
225+
def fake_element(fake_document) -> None:
226+
"""Install the stateful Element into builtins."""
227+
try:
228+
builtins.Element = ElementCallable(fake_document)
229+
yield
230+
finally:
231+
delattr(builtins, "Element")
Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
def interest(*args, **kwargs):
2+
# Signal that PyScript is alive by setting the ``Calculate``
3+
# button away from disabled.
4+
ec = Element("calc") # noqa
5+
6+
# Now get the various inputs
7+
ep = Element("principal") # noqa
8+
er = Element("interest_rate") # noqa
9+
et = Element("time") # noqa
10+
p = float(ep.value)
11+
r = float(er.value)
12+
t = float(et.value)
13+
output1 = Element("simple_interest") # noqa
14+
output2 = Element("compound_interest") # noqa
15+
res1 = round(p + (p * r * t))
16+
res2 = round(p * ((1 + r) ** t))
17+
output1.write("simple interest: " + str(res1))
18+
output2.write("compound interest: " + str(res2))
19+
20+
21+
def setup():
22+
"""When Pyodide starts up, enable the Calculate button."""
23+
ec = Element("calc") # noqa
24+
ec.element.removeAttribute("disabled")
Loading
Lines changed: 96 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,96 @@
1+
<!DOCTYPE html>
2+
<html lang="en" xmlns="http://www.w3.org/1999/xhtml">
3+
4+
<head>
5+
<meta charset="utf-8"/>
6+
<title>Interest Calculator</title>
7+
<link rel="stylesheet" href="styles.css"/>
8+
<script defer src="../../../pyscript/pyscript.js"></script>
9+
</head>
10+
11+
<body>
12+
13+
<header>
14+
<h1 id="header_h1">Welcome to the Compound Calculator!</h1>
15+
</header>
16+
17+
<main style="">
18+
19+
<div class="flexelement" id="first_div">
20+
<p id="first_p">
21+
Welcome to the "Simple and Compound Interest Calculator!"
22+
<br/>
23+
<strong>"how does it work?"</strong>
24+
to use the "Simple and Compound Interest Calculator", please enter the input data into the form.
25+
after clicking "Calculate", your result will be shown at the bottom of the form.
26+
</p>
27+
28+
<div style="margin-left: 15%;">
29+
<div style="width: 100%;">
30+
<input type="radio" name='expander' id="expander-1">
31+
<label class="expander_label" for="expander-1">Compound Interest Formula &#187;</label>
32+
<img src="compound-interest.png" alt="Compound Interest"
33+
style="height: 200px;"/>
34+
</div>
35+
36+
<div style="margin-top: 25px;">
37+
<input type="radio" name='expander' id="expander-2">
38+
<label class="expander_label" for="expander-2">Simple Interest Formula &#187;</label>
39+
<img src="simple-interest.png" alt="Simple Interest"
40+
style="height: 150px;"/>
41+
</div>
42+
</div>
43+
</div>
44+
45+
46+
<div class="flexelement">
47+
48+
<div id="form" style="">
49+
50+
<div>
51+
<label>Principal
52+
<input id="principal" type="number" style="color: black; min-height: 60px;"/></label>
53+
<br/><br>
54+
<label>Interest rate
55+
<input id="interest_rate" type="number" style="color: black; min-height: 60px;"
56+
placeholder="Decimal, f.e. 0.8"/>
57+
</label>
58+
<br>
59+
<br/>
60+
61+
<label>Time
62+
<input id="time" type="number" style="color: black; min-height: 60px;"
63+
placeholder="in years"/></label>
64+
<br> <br/>
65+
66+
<button py-click="interest()" id="calc" style="min-height: 60px;" disabled>Calculate</button>
67+
68+
<div style="margin-top: 2%;">
69+
<span id="simple_interest"></span>
70+
<br/>
71+
<span id="compound_interest"></span>
72+
</div>
73+
74+
</div>
75+
76+
</div>
77+
78+
</div>
79+
80+
<py-config src="../py_config.toml"></py-config>
81+
<py-script src="calculator.py"></py-script>
82+
<py-script>
83+
setup()
84+
</py-script>
85+
</main>
86+
87+
<footer>
88+
<p id="footer_p">
89+
thank you for using the "Simple and Compound
90+
Interest Calculator!", powered by PyScript!
91+
</p>
92+
</footer>
93+
94+
</body>
95+
96+
</html>
Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
title: Compound Interest Calculator
3+
subtitle: The classic hello world, but in Python -- in a browser!
4+
---
5+
The *body* description.
Loading
Lines changed: 165 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,165 @@
1+
body {
2+
background-color: #F3F6F8;
3+
min-height: 100vh;
4+
display: flex;
5+
flex-direction: column;
6+
}
7+
8+
main {
9+
display: flex;
10+
flex-direction: row;
11+
}
12+
13+
img {
14+
flex: 1;
15+
margin: 5%;
16+
margin-left: 20%;
17+
}
18+
19+
#first_div {
20+
align-content: center;
21+
margin: 1%;
22+
flex: 1.25;
23+
}
24+
25+
#first_p {
26+
margin: 3.5%;
27+
margin-left: 15%;
28+
font-family: Tahoma, sans-serif;
29+
}
30+
31+
.flexelement {
32+
flex: 1;
33+
padding: 2%;
34+
}
35+
36+
37+
38+
#form {
39+
margin-top: 7%;
40+
height: 100%;
41+
display: flex;
42+
align-content: center;
43+
justify-content: center;
44+
}
45+
46+
#form input[type=number] {
47+
border: 1px solid white;
48+
border-radius: 0.3em;
49+
height: 12%;
50+
width: 100%;
51+
margin-bottom: 5px;
52+
}
53+
54+
55+
#form button {
56+
width: 100%;
57+
height: 12%;
58+
margin-bottom: 5px;
59+
}
60+
61+
header {
62+
height: 100px;
63+
}
64+
65+
footer {
66+
margin-top: auto;
67+
height: 57px;
68+
}
69+
70+
header, footer {
71+
background-color: #1A1A27;
72+
}
73+
74+
#header_h1 {
75+
padding-top: 2.2%;
76+
margin-left: 40%;
77+
font-weight: bold;
78+
color: white;
79+
}
80+
81+
input[type=number] {
82+
padding: 1%;
83+
}
84+
85+
#principal, #interest_rate, #time {
86+
width: 25%;
87+
}
88+
89+
#calc {
90+
width: 25%;
91+
padding: 1%;
92+
font-weight: bold;
93+
margin-top: 2%;
94+
color: white;
95+
background-color: #0095E8;
96+
}
97+
98+
#simple_interest, #compound_interest {
99+
color: black;
100+
font-weight: bold;
101+
}
102+
103+
input[type=radio] {
104+
display: none;
105+
}
106+
107+
label {
108+
cursor: pointer;
109+
}
110+
111+
input[type=radio] ~ img {
112+
animation: close 1.5;
113+
display: none;
114+
height: 0;
115+
max-height: 500px;
116+
overflow: hidden;
117+
}
118+
119+
input[type=radio]:checked ~ img {
120+
animation: open 1.5s;
121+
display: block;
122+
height: auto;
123+
max-height: 500px;
124+
}
125+
126+
@keyframes open {
127+
from {
128+
max-height: 0;
129+
}
130+
131+
to {
132+
max-height: auto;
133+
}
134+
}
135+
136+
@keyframes close {
137+
from {
138+
display: block;
139+
max-height: auto;
140+
}
141+
142+
to {
143+
display: none;
144+
height: 0;
145+
}
146+
}
147+
148+
.expander_label {
149+
border: 1px solid #0095E8;
150+
border-radius: 0.1em;
151+
padding: 1%;
152+
font-weight: 500;
153+
color: white;
154+
background-color: #0095E8;
155+
background-color: #0095E8;
156+
}
157+
158+
#footer_p {
159+
font-weight: bold;
160+
font-size: 13px;
161+
text-align: center;
162+
color: white;
163+
vertical-align: central;
164+
padding: 1.2%;
165+
}

0 commit comments

Comments
 (0)