webwidgets 1.0.0__tar.gz → 1.1.0__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {webwidgets-1.0.0 → webwidgets-1.1.0}/.github/workflows/ci-full.yml +2 -3
- {webwidgets-1.0.0 → webwidgets-1.1.0}/.github/workflows/ci-quick.yml +2 -3
- {webwidgets-1.0.0 → webwidgets-1.1.0}/PKG-INFO +6 -1
- {webwidgets-1.0.0 → webwidgets-1.1.0}/pyproject.toml +8 -0
- {webwidgets-1.0.0 → webwidgets-1.1.0}/tests/compilation/test_css.py +144 -137
- webwidgets-1.1.0/tests/compilation/test_css_rule.py +108 -0
- webwidgets-1.1.0/tests/compilation/test_css_sections.py +191 -0
- {webwidgets-1.0.0 → webwidgets-1.1.0}/tests/compilation/test_html_tags.py +4 -0
- webwidgets-1.1.0/tests/conftest.py +20 -0
- {webwidgets-1.0.0 → webwidgets-1.1.0}/tests/utility/test_validation.py +92 -27
- {webwidgets-1.0.0 → webwidgets-1.1.0}/tests/website/test_website.py +68 -62
- webwidgets-1.1.0/tests/widgets/conftest.py +48 -0
- webwidgets-1.1.0/tests/widgets/containers/__init__.py +11 -0
- webwidgets-1.1.0/tests/widgets/containers/test_box.py +110 -0
- {webwidgets-1.0.0/tests/widgets → webwidgets-1.1.0/tests/widgets/containers}/test_page.py +1 -1
- webwidgets-1.1.0/tests/widgets/render_page.py +58 -0
- webwidgets-1.1.0/tests/widgets/test_render_page.py +61 -0
- webwidgets-1.1.0/tests/wrap_core_css.py +37 -0
- {webwidgets-1.0.0 → webwidgets-1.1.0}/webwidgets/__init__.py +1 -1
- {webwidgets-1.0.0 → webwidgets-1.1.0}/webwidgets/compilation/css/__init__.py +3 -2
- {webwidgets-1.0.0 → webwidgets-1.1.0}/webwidgets/compilation/css/css.py +105 -122
- webwidgets-1.1.0/webwidgets/compilation/css/css_rule.py +104 -0
- webwidgets-1.1.0/webwidgets/compilation/css/sections/__init__.py +14 -0
- webwidgets-1.1.0/webwidgets/compilation/css/sections/css_section.py +106 -0
- webwidgets-1.1.0/webwidgets/compilation/css/sections/preamble.py +40 -0
- webwidgets-1.1.0/webwidgets/compilation/css/sections/rule_section.py +43 -0
- {webwidgets-1.0.0 → webwidgets-1.1.0}/webwidgets/compilation/html/__init__.py +1 -1
- {webwidgets-1.0.0 → webwidgets-1.1.0}/webwidgets/compilation/html/html_tags.py +5 -0
- {webwidgets-1.0.0 → webwidgets-1.1.0}/webwidgets/utility/validation.py +68 -1
- {webwidgets-1.0.0 → webwidgets-1.1.0}/webwidgets/website/website.py +5 -5
- {webwidgets-1.0.0 → webwidgets-1.1.0}/webwidgets/widgets/containers/__init__.py +1 -0
- webwidgets-1.1.0/webwidgets/widgets/containers/box.py +70 -0
- {webwidgets-1.0.0 → webwidgets-1.1.0}/webwidgets/widgets/widget.py +1 -2
- {webwidgets-1.0.0 → webwidgets-1.1.0}/.github/workflows/cd.yml +0 -0
- {webwidgets-1.0.0 → webwidgets-1.1.0}/.gitignore +0 -0
- {webwidgets-1.0.0 → webwidgets-1.1.0}/LICENSE +0 -0
- {webwidgets-1.0.0 → webwidgets-1.1.0}/README.md +0 -0
- {webwidgets-1.0.0 → webwidgets-1.1.0}/tests/__init__.py +0 -0
- {webwidgets-1.0.0 → webwidgets-1.1.0}/tests/compilation/__init__.py +0 -0
- {webwidgets-1.0.0 → webwidgets-1.1.0}/tests/compilation/test_html_node.py +0 -0
- {webwidgets-1.0.0 → webwidgets-1.1.0}/tests/utility/__init__.py +0 -0
- {webwidgets-1.0.0 → webwidgets-1.1.0}/tests/utility/test_indentation.py +0 -0
- {webwidgets-1.0.0 → webwidgets-1.1.0}/tests/utility/test_representation.py +0 -0
- {webwidgets-1.0.0 → webwidgets-1.1.0}/tests/utility/test_sanitizing.py +0 -0
- {webwidgets-1.0.0 → webwidgets-1.1.0}/tests/website/__init__.py +0 -0
- {webwidgets-1.0.0 → webwidgets-1.1.0}/tests/widgets/__init__.py +0 -0
- {webwidgets-1.0.0 → webwidgets-1.1.0}/webwidgets/compilation/__init__.py +0 -0
- {webwidgets-1.0.0 → webwidgets-1.1.0}/webwidgets/compilation/html/html_node.py +0 -0
- {webwidgets-1.0.0 → webwidgets-1.1.0}/webwidgets/utility/__init__.py +0 -0
- {webwidgets-1.0.0 → webwidgets-1.1.0}/webwidgets/utility/indentation.py +0 -0
- {webwidgets-1.0.0 → webwidgets-1.1.0}/webwidgets/utility/representation.py +0 -0
- {webwidgets-1.0.0 → webwidgets-1.1.0}/webwidgets/utility/sanitizing.py +0 -0
- {webwidgets-1.0.0 → webwidgets-1.1.0}/webwidgets/website/__init__.py +0 -0
- {webwidgets-1.0.0 → webwidgets-1.1.0}/webwidgets/website/compiled_website.py +0 -0
- {webwidgets-1.0.0 → webwidgets-1.1.0}/webwidgets/widgets/__init__.py +0 -0
- {webwidgets-1.0.0 → webwidgets-1.1.0}/webwidgets/widgets/containers/container.py +0 -0
- {webwidgets-1.0.0 → webwidgets-1.1.0}/webwidgets/widgets/containers/page.py +0 -0
@@ -64,15 +64,14 @@ jobs:
|
|
64
64
|
uses: actions/setup-python@v3
|
65
65
|
with:
|
66
66
|
python-version: ${{ matrix.python-version }}
|
67
|
-
- name:
|
67
|
+
- name: Upgrading pip
|
68
68
|
run: |
|
69
69
|
python -c "import platform; print('OS', platform.system())"
|
70
70
|
python -c "import sys; print('Python version', sys.version)"
|
71
71
|
python -m pip install --upgrade pip
|
72
|
-
pip install pytest
|
73
72
|
- name: Build and install
|
74
73
|
run: |
|
75
|
-
pip install .
|
74
|
+
pip install .[dev]
|
76
75
|
# Removing webwidgets directory so imports come from build
|
77
76
|
rm -r webwidgets
|
78
77
|
- name: Test with pytest
|
@@ -59,16 +59,15 @@ jobs:
|
|
59
59
|
uses: actions/setup-python@v3
|
60
60
|
with:
|
61
61
|
python-version: ${{ matrix.python-version }}
|
62
|
-
- name:
|
62
|
+
- name: Upgrading pip
|
63
63
|
run: |
|
64
64
|
python -c "import sys; print('Python version', sys.version)"
|
65
65
|
python -m pip install --upgrade pip
|
66
|
-
pip install pytest
|
67
66
|
- name: Build and install
|
68
67
|
run: |
|
69
68
|
echo "Current directory:"
|
70
69
|
ls -la
|
71
|
-
pip install .
|
70
|
+
pip install .[dev]
|
72
71
|
# Removing webwidgets directory so imports come from build
|
73
72
|
rm -r webwidgets
|
74
73
|
echo "Removed webwidgets directory. New content:"
|
@@ -1,6 +1,6 @@
|
|
1
1
|
Metadata-Version: 2.4
|
2
2
|
Name: webwidgets
|
3
|
-
Version: 1.
|
3
|
+
Version: 1.1.0
|
4
4
|
Summary: A Python package for designing web UIs.
|
5
5
|
Project-URL: Source code, https://github.com/mlaasri/WebWidgets
|
6
6
|
Author: mlaasri
|
@@ -9,6 +9,11 @@ Keywords: design,webui
|
|
9
9
|
Classifier: Operating System :: OS Independent
|
10
10
|
Classifier: Programming Language :: Python :: 3
|
11
11
|
Requires-Python: >=3.9
|
12
|
+
Provides-Extra: dev
|
13
|
+
Requires-Dist: numpy; extra == 'dev'
|
14
|
+
Requires-Dist: pillow; extra == 'dev'
|
15
|
+
Requires-Dist: pytest; extra == 'dev'
|
16
|
+
Requires-Dist: selenium; extra == 'dev'
|
12
17
|
Description-Content-Type: text/markdown
|
13
18
|
|
14
19
|
# WebWidgets
|
@@ -21,6 +21,14 @@ classifiers = [
|
|
21
21
|
[project.urls]
|
22
22
|
"Source code" = "https://github.com/mlaasri/WebWidgets"
|
23
23
|
|
24
|
+
[project.optional-dependencies]
|
25
|
+
dev = [
|
26
|
+
"numpy",
|
27
|
+
"pillow",
|
28
|
+
"pytest",
|
29
|
+
"selenium"
|
30
|
+
]
|
31
|
+
|
24
32
|
[tool.hatch.version]
|
25
33
|
path = "webwidgets/__init__.py"
|
26
34
|
|
@@ -14,8 +14,10 @@ import pytest
|
|
14
14
|
from typing import Any, Dict, List
|
15
15
|
from webwidgets.compilation.html.html_node import HTMLNode
|
16
16
|
from webwidgets.compilation.html.html_tags import TextNode
|
17
|
-
from webwidgets.compilation.css.css import
|
18
|
-
|
17
|
+
from webwidgets.compilation.css.css import apply_css, compile_css, CompiledCSS, \
|
18
|
+
default_class_namer
|
19
|
+
from webwidgets.compilation.css.css_rule import ClassRule, CSSRule
|
20
|
+
from webwidgets.compilation.css.sections import RuleSection
|
19
21
|
|
20
22
|
|
21
23
|
class TestCompileCSS:
|
@@ -29,19 +31,20 @@ class TestCompileCSS:
|
|
29
31
|
:return: List of the member variables of each :py:class:`CSSRule`.
|
30
32
|
:rtype: Dict[int, Any]
|
31
33
|
"""
|
32
|
-
return [
|
34
|
+
return [{a: getattr(rule, a) for a in ("selector", "declarations")}
|
35
|
+
for rule in rules]
|
33
36
|
|
34
37
|
@staticmethod
|
35
|
-
def _serialize_mapping(mapping: Dict[int, List[
|
38
|
+
def _serialize_mapping(mapping: Dict[int, List[ClassRule]]) -> Dict[int, List[str]]:
|
36
39
|
"""Utility function to convert a :py:attr:`CompiledCSS.mapping` object
|
37
40
|
into a dictionary that can be used in testing.
|
38
41
|
|
39
42
|
:param mapping: :py:attr:`CompiledCSS.mapping` object.
|
40
|
-
:type mapping: Dict[int, List[
|
41
|
-
:return: Dictionary mapping each node ID to the
|
42
|
-
achieve the same style.
|
43
|
+
:type mapping: Dict[int, List[ClassRule]]
|
44
|
+
:return: Dictionary mapping each node ID to the selectors of the rules
|
45
|
+
that achieve the same style.
|
43
46
|
"""
|
44
|
-
return {i: [r.
|
47
|
+
return {i: [r.selector for r in rules] for i, rules in mapping.items()}
|
45
48
|
|
46
49
|
def test_argument_type(self):
|
47
50
|
"""Compares compilation when given a node object versus a list of
|
@@ -57,12 +60,12 @@ class TestCompileCSS:
|
|
57
60
|
|
58
61
|
# Define expected compilation results
|
59
62
|
expected_rules = [
|
60
|
-
{"
|
61
|
-
{"
|
63
|
+
{"selector": ".c0", "declarations": {"a": "5"}},
|
64
|
+
{"selector": ".c1", "declarations": {"b": "4"}}
|
62
65
|
]
|
63
66
|
expected_mapping = {
|
64
|
-
id(tree): ['
|
65
|
-
id(tree.children[0]): ['
|
67
|
+
id(tree): ['.c0', '.c1'],
|
68
|
+
id(tree.children[0]): ['.c0']
|
66
69
|
}
|
67
70
|
|
68
71
|
# Compile tree as single node object
|
@@ -72,7 +75,7 @@ class TestCompileCSS:
|
|
72
75
|
assert compiled_css.trees == [tree]
|
73
76
|
assert [id(t) for t in compiled_css.trees] == [id(tree)]
|
74
77
|
assert TestCompileCSS._serialize_rules(
|
75
|
-
compiled_css.rules) == expected_rules
|
78
|
+
compiled_css.core.rules) == expected_rules
|
76
79
|
assert TestCompileCSS._serialize_mapping(
|
77
80
|
compiled_css.mapping) == expected_mapping
|
78
81
|
|
@@ -83,7 +86,7 @@ class TestCompileCSS:
|
|
83
86
|
assert compiled_css2.trees == [tree]
|
84
87
|
assert [id(t) for t in compiled_css2.trees] == [id(tree)]
|
85
88
|
assert TestCompileCSS._serialize_rules(
|
86
|
-
compiled_css2.rules) == expected_rules
|
89
|
+
compiled_css2.core.rules) == expected_rules
|
87
90
|
assert TestCompileCSS._serialize_mapping(
|
88
91
|
compiled_css2.mapping) == expected_mapping
|
89
92
|
|
@@ -103,16 +106,16 @@ class TestCompileCSS:
|
|
103
106
|
|
104
107
|
# Check that the rules are correctly generated
|
105
108
|
expected_rules = [
|
106
|
-
{"
|
107
|
-
{"
|
108
|
-
{"
|
109
|
+
{"selector": ".c0", "declarations": {"color": "blue"}},
|
110
|
+
{"selector": ".c1", "declarations": {"margin": "0"}},
|
111
|
+
{"selector": ".c2", "declarations": {"padding": "0"}}
|
109
112
|
]
|
110
113
|
assert TestCompileCSS._serialize_rules(
|
111
|
-
compiled_css.rules) == expected_rules
|
114
|
+
compiled_css.core.rules) == expected_rules
|
112
115
|
|
113
116
|
# Check that the mapping is correctly generated
|
114
|
-
expected_mapping = {id(node1): ['
|
115
|
-
node2): ['
|
117
|
+
expected_mapping = {id(node1): ['.c1', '.c2'], id(
|
118
|
+
node2): ['.c0', '.c1'], id(node3): ['.c1', '.c2']}
|
116
119
|
assert TestCompileCSS._serialize_mapping(
|
117
120
|
compiled_css.mapping) == expected_mapping
|
118
121
|
|
@@ -137,19 +140,19 @@ class TestCompileCSS:
|
|
137
140
|
|
138
141
|
# Check that the rules are correctly generated
|
139
142
|
expected_rules = [
|
140
|
-
{"
|
141
|
-
{"
|
142
|
-
{"
|
143
|
-
{"
|
143
|
+
{"selector": ".c0", "declarations": {"color": "blue"}},
|
144
|
+
{"selector": ".c1", "declarations": {"margin": "0"}},
|
145
|
+
{"selector": ".c2", "declarations": {"margin": "5"}},
|
146
|
+
{"selector": ".c3", "declarations": {"padding": "0"}}
|
144
147
|
]
|
145
148
|
assert TestCompileCSS._serialize_rules(
|
146
|
-
compiled_css.rules) == expected_rules
|
149
|
+
compiled_css.core.rules) == expected_rules
|
147
150
|
|
148
151
|
# Check that the mapping is correctly generated
|
149
152
|
expected_mapping = {
|
150
|
-
id(tree): ['
|
151
|
-
id(tree.children[0]): ['
|
152
|
-
id(tree.children[1]): ['
|
153
|
+
id(tree): ['.c1', '.c3'],
|
154
|
+
id(tree.children[0]): ['.c0', '.c2'],
|
155
|
+
id(tree.children[1]): ['.c0', '.c3'],
|
153
156
|
id(tree.children[0].children[0]): [],
|
154
157
|
id(tree.children[1].children[0]): []
|
155
158
|
}
|
@@ -181,20 +184,20 @@ class TestCompileCSS:
|
|
181
184
|
|
182
185
|
# Check that the rules are correctly generated
|
183
186
|
expected_rules = [
|
184
|
-
{"
|
185
|
-
{"
|
186
|
-
{"
|
187
|
-
{"
|
187
|
+
{"selector": ".c0", "declarations": {"color": "red"}},
|
188
|
+
{"selector": ".c1", "declarations": {"margin": "10"}},
|
189
|
+
{"selector": ".c2", "declarations": {"margin": "5"}},
|
190
|
+
{"selector": ".c3", "declarations": {"padding": "0"}}
|
188
191
|
]
|
189
192
|
assert TestCompileCSS._serialize_rules(
|
190
|
-
compiled_css.rules) == expected_rules
|
193
|
+
compiled_css.core.rules) == expected_rules
|
191
194
|
|
192
195
|
# Check that the mapping is correctly generated
|
193
196
|
expected_mapping = {
|
194
|
-
id(tree1): ['
|
195
|
-
id(tree1.children[0]): ['
|
196
|
-
id(tree2): ['
|
197
|
-
id(tree2.children[0]): ['
|
197
|
+
id(tree1): ['.c1', '.c3'],
|
198
|
+
id(tree1.children[0]): ['.c0'],
|
199
|
+
id(tree2): ['.c2', '.c3'],
|
200
|
+
id(tree2.children[0]): ['.c1']
|
198
201
|
}
|
199
202
|
assert TestCompileCSS._serialize_mapping(
|
200
203
|
compiled_css.mapping) == expected_mapping
|
@@ -211,14 +214,14 @@ class TestCompileCSS:
|
|
211
214
|
)
|
212
215
|
compiled_css = compile_css(tree)
|
213
216
|
expected_rules = [
|
214
|
-
{"
|
215
|
-
{"
|
216
|
-
{"
|
217
|
-
{"
|
218
|
-
{"
|
217
|
+
{"selector": ".c0", "declarations": {"a": "10"}},
|
218
|
+
{"selector": ".c1", "declarations": {"a": "5"}},
|
219
|
+
{"selector": ".c2", "declarations": {"b": "10"}},
|
220
|
+
{"selector": ".c3", "declarations": {"b": "4"}},
|
221
|
+
{"selector": ".c4", "declarations": {"c": "5"}},
|
219
222
|
]
|
220
223
|
assert TestCompileCSS._serialize_rules(
|
221
|
-
compiled_css.rules) == expected_rules
|
224
|
+
compiled_css.core.rules) == expected_rules
|
222
225
|
|
223
226
|
def test_duplicate_node(self):
|
224
227
|
"""Test that adding the same node twice does not impact compilation"""
|
@@ -231,20 +234,20 @@ class TestCompileCSS:
|
|
231
234
|
]
|
232
235
|
)
|
233
236
|
expected_rules = [
|
234
|
-
{"
|
235
|
-
{"
|
236
|
-
{"
|
237
|
+
{"selector": ".c0", "declarations": {"a": "5"}},
|
238
|
+
{"selector": ".c1", "declarations": {"b": "10"}},
|
239
|
+
{"selector": ".c2", "declarations": {"b": "4"}}
|
237
240
|
]
|
238
241
|
expected_mapping = {
|
239
|
-
id(tree): ['
|
240
|
-
id(tree.children[0]): ['
|
241
|
-
id(tree.children[1]): ['
|
242
|
+
id(tree): ['.c0', '.c2'],
|
243
|
+
id(tree.children[0]): ['.c0'],
|
244
|
+
id(tree.children[1]): ['.c1']
|
242
245
|
}
|
243
246
|
compiled_css = compile_css([tree])
|
244
247
|
assert compiled_css.trees == [tree]
|
245
248
|
assert [id(t) for t in compiled_css.trees] == [id(tree)]
|
246
249
|
assert TestCompileCSS._serialize_rules(
|
247
|
-
compiled_css.rules) == expected_rules
|
250
|
+
compiled_css.core.rules) == expected_rules
|
248
251
|
assert TestCompileCSS._serialize_mapping(
|
249
252
|
compiled_css.mapping) == expected_mapping
|
250
253
|
|
@@ -256,21 +259,21 @@ class TestCompileCSS:
|
|
256
259
|
assert [id(t) for t in compiled_css2.trees] == [
|
257
260
|
id(tree), id(tree.children[0])]
|
258
261
|
assert TestCompileCSS._serialize_rules(
|
259
|
-
compiled_css2.rules) == expected_rules
|
262
|
+
compiled_css2.core.rules) == expected_rules
|
260
263
|
assert TestCompileCSS._serialize_mapping(
|
261
264
|
compiled_css2.mapping) == expected_mapping
|
262
265
|
|
263
|
-
@pytest.mark.parametrize("
|
264
|
-
(lambda _, i: f"rule{i}", ["rule0", "rule1", "rule2"]),
|
265
|
-
(lambda _, i: f"rule-{i + 1}", ["rule-1", "rule-2", "rule-3"]),
|
266
|
+
@pytest.mark.parametrize("class_namer, selectors", [
|
267
|
+
(lambda _, i: f"rule{i}", [".rule0", ".rule1", ".rule2"]),
|
268
|
+
(lambda _, i: f"rule-{i + 1}", [".rule-1", ".rule-2", ".rule-3"]),
|
266
269
|
(lambda r, i: f"{list(r[i].declarations.items())[0][0]}{i}", [
|
267
|
-
"az0", "bz1", "bz2"]),
|
270
|
+
".az0", ".bz1", ".bz2"]),
|
268
271
|
(lambda r, i: f"{list(r[i].declarations.items())[0][0][0]}{i}", [
|
269
|
-
"a0", "b1", "b2"]),
|
270
|
-
(lambda r, i: f"
|
271
|
-
"
|
272
|
+
".a0", ".b1", ".b2"]),
|
273
|
+
(lambda r, i: f"c{list(r[i].declarations.items())[0][1]}-{i}", [
|
274
|
+
".c10-1", ".c4-2", ".c5-0"]),
|
272
275
|
])
|
273
|
-
def
|
276
|
+
def test_custom_class_names(self, class_namer, selectors):
|
274
277
|
tree = HTMLNode(
|
275
278
|
style={"az": "5", "bz": "4"},
|
276
279
|
children=[
|
@@ -278,39 +281,43 @@ class TestCompileCSS:
|
|
278
281
|
HTMLNode(style={"bz": "10"}),
|
279
282
|
]
|
280
283
|
)
|
281
|
-
compiled_css = compile_css(tree,
|
282
|
-
assert [r.
|
284
|
+
compiled_css = compile_css(tree, class_namer=class_namer)
|
285
|
+
assert [r.selector for r in compiled_css.core.rules] == selectors
|
283
286
|
|
284
287
|
|
285
288
|
class TestCompiledCSS:
|
286
|
-
def test_export_custom_compiled_css(self):
|
287
|
-
|
288
|
-
|
289
|
-
|
290
|
-
|
289
|
+
def test_export_custom_compiled_css(self, wrap_core_css):
|
290
|
+
core = RuleSection(
|
291
|
+
rules=[
|
292
|
+
CSSRule(selector=".c0", declarations={
|
293
|
+
"margin": "0", "padding": "0"}),
|
294
|
+
CSSRule(selector=".c1", declarations={"color": "blue"}),
|
295
|
+
CSSRule(selector=".c2", declarations={
|
291
296
|
"background-color": "white", "font-size": "16px"})
|
292
|
-
|
297
|
+
],
|
298
|
+
title="Core"
|
299
|
+
)
|
293
300
|
compiled_css = CompiledCSS(trees=None,
|
294
|
-
|
301
|
+
core=core,
|
295
302
|
mapping=None)
|
296
|
-
|
297
|
-
".
|
303
|
+
expected_core_css = '\n'.join([
|
304
|
+
".c0 {",
|
298
305
|
" margin: 0;",
|
299
306
|
" padding: 0;",
|
300
307
|
"}",
|
301
308
|
"",
|
302
|
-
".
|
309
|
+
".c1 {",
|
303
310
|
" color: blue;",
|
304
311
|
"}",
|
305
312
|
"",
|
306
|
-
".
|
313
|
+
".c2 {",
|
307
314
|
" background-color: white;",
|
308
315
|
" font-size: 16px;",
|
309
316
|
"}"
|
310
317
|
])
|
311
|
-
assert compiled_css.to_css() ==
|
318
|
+
assert compiled_css.to_css() == wrap_core_css(expected_core_css)
|
312
319
|
|
313
|
-
def test_export_real_compiled_css(self):
|
320
|
+
def test_export_real_compiled_css(self, wrap_core_css):
|
314
321
|
tree = HTMLNode(
|
315
322
|
style={"margin": "0", "padding": "0"},
|
316
323
|
children=[
|
@@ -319,33 +326,33 @@ class TestCompiledCSS:
|
|
319
326
|
]
|
320
327
|
)
|
321
328
|
compiled_css = compile_css(tree)
|
322
|
-
|
323
|
-
".
|
329
|
+
expected_core_css = '\n'.join([
|
330
|
+
".c0 {",
|
324
331
|
" color: blue;",
|
325
332
|
"}",
|
326
333
|
"",
|
327
|
-
".
|
334
|
+
".c1 {",
|
328
335
|
" color: green;",
|
329
336
|
"}",
|
330
337
|
"",
|
331
|
-
".
|
338
|
+
".c2 {",
|
332
339
|
" margin: 0;",
|
333
340
|
"}",
|
334
341
|
"",
|
335
|
-
".
|
342
|
+
".c3 {",
|
336
343
|
" padding: 0;",
|
337
344
|
"}"
|
338
345
|
])
|
339
|
-
assert compiled_css.to_css() ==
|
346
|
+
assert compiled_css.to_css() == wrap_core_css(expected_core_css)
|
340
347
|
|
341
|
-
def test_export_empty_style(self):
|
348
|
+
def test_export_empty_style(self, wrap_core_css):
|
342
349
|
node = HTMLNode()
|
343
350
|
css = compile_css(node).to_css()
|
344
|
-
assert css == ""
|
351
|
+
assert css == wrap_core_css("")
|
345
352
|
other_css = CompiledCSS(trees=None,
|
346
|
-
|
353
|
+
core=RuleSection(title="Core"),
|
347
354
|
mapping=None).to_css()
|
348
|
-
assert other_css == ""
|
355
|
+
assert other_css == wrap_core_css("")
|
349
356
|
|
350
357
|
def test_export_invalid_style(self):
|
351
358
|
node = HTMLNode(style={"marg!in": "0", "padding": "0"})
|
@@ -354,29 +361,29 @@ class TestCompiledCSS:
|
|
354
361
|
compiled_css.to_css()
|
355
362
|
|
356
363
|
@pytest.mark.parametrize("indent_size", [0, 2, 4, 8])
|
357
|
-
def test_css_indentation(self, indent_size):
|
364
|
+
def test_css_indentation(self, indent_size, wrap_core_css):
|
358
365
|
node = HTMLNode(style={"a": "0", "b": "1"})
|
359
|
-
|
360
|
-
".
|
366
|
+
expected_core_css = '\n'.join([
|
367
|
+
".c0 {",
|
361
368
|
f"{' ' * indent_size}a: 0;",
|
362
369
|
"}",
|
363
370
|
"",
|
364
|
-
".
|
371
|
+
".c1 {",
|
365
372
|
f"{' ' * indent_size}b: 1;",
|
366
373
|
"}"
|
367
374
|
])
|
368
375
|
css = compile_css(node).to_css(indent_size=indent_size)
|
369
|
-
assert css ==
|
376
|
+
assert css == wrap_core_css(expected_core_css, indent_size=indent_size)
|
370
377
|
|
371
378
|
|
372
379
|
class TestApplyCSS:
|
373
380
|
@pytest.mark.parametrize("class_in, class_out", [
|
374
|
-
(None, "
|
375
|
-
("", "
|
376
|
-
("z", "z
|
377
|
-
("
|
378
|
-
("z
|
379
|
-
("
|
381
|
+
(None, "c0 c1"), # No class attribute
|
382
|
+
("", "c0 c1"), # Empty class
|
383
|
+
("z", "z c0 c1"), # Existing class
|
384
|
+
("c1", "c1 c0"), # Existing rule
|
385
|
+
("z c1", "z c1 c0"), # Existing class and rule
|
386
|
+
("c1 z", "c1 z c0") # Existing rule and class
|
380
387
|
])
|
381
388
|
def test_apply_css_to_node(self, class_in, class_out):
|
382
389
|
tree = HTMLNode(attributes=None if class_in is None else {"class": class_in},
|
@@ -386,22 +393,22 @@ class TestApplyCSS:
|
|
386
393
|
assert tree.to_html() == f'<htmlnode class="{class_out}"></htmlnode>'
|
387
394
|
|
388
395
|
@pytest.mark.parametrize("cl1_in, cl1_out", [
|
389
|
-
(None, "
|
390
|
-
("", "
|
391
|
-
("c", "c
|
392
|
-
("
|
393
|
-
("c
|
394
|
-
("
|
395
|
-
("
|
396
|
+
(None, "c2 c3"), # No class attribute
|
397
|
+
("", "c2 c3"), # Empty class
|
398
|
+
("c", "c c2 c3"), # Existing class
|
399
|
+
("c3", "c3 c2"), # Existing rule
|
400
|
+
("c c3", "c c3 c2"), # Existing class and rule
|
401
|
+
("c3 c", "c3 c c2"), # Existing rule and class
|
402
|
+
("rc3", "rc3 c2 c3") # Rule decoy
|
396
403
|
])
|
397
404
|
@pytest.mark.parametrize("cl2_in, cl2_out", [
|
398
|
-
(None, "
|
399
|
-
("", "
|
400
|
-
("z", "z
|
401
|
-
("
|
402
|
-
("z
|
403
|
-
("
|
404
|
-
("
|
405
|
+
(None, "c1 c2"), # No class attribute
|
406
|
+
("", "c1 c2"), # Empty class
|
407
|
+
("z", "z c1 c2"), # Existing class
|
408
|
+
("c1", "c1 c2"), # Existing rule
|
409
|
+
("z c1", "z c1 c2"), # Existing class and rule
|
410
|
+
("c1 z", "c1 z c2"), # Existing rule and class
|
411
|
+
("cc1", "cc1 c1 c2") # Rule decoy
|
405
412
|
])
|
406
413
|
@pytest.mark.parametrize("mix", [False, True])
|
407
414
|
def test_apply_css_to_tree(self, cl1_in, cl1_out, cl2_in, cl2_out, mix):
|
@@ -419,22 +426,22 @@ class TestApplyCSS:
|
|
419
426
|
|
420
427
|
# Compiling and applying CSS to the tree
|
421
428
|
compiled_css = compile_css(tree)
|
422
|
-
assert TestCompileCSS._serialize_rules(compiled_css.rules) == [
|
423
|
-
{"
|
424
|
-
{"
|
425
|
-
{"
|
426
|
-
{"
|
429
|
+
assert TestCompileCSS._serialize_rules(compiled_css.core.rules) == [
|
430
|
+
{"selector": ".c0", "declarations": {"color": "blue"}},
|
431
|
+
{"selector": ".c1", "declarations": {"color": "green"}},
|
432
|
+
{"selector": ".c2", "declarations": {"margin": "0"}},
|
433
|
+
{"selector": ".c3", "declarations": {"padding": "0"}}
|
427
434
|
]
|
428
435
|
apply_css(compiled_css, tree)
|
429
436
|
|
430
437
|
# Checking the tree's new classes
|
431
438
|
assert tree.attributes["class"] == cl1_out
|
432
|
-
assert tree.children[0].attributes["class"] == "
|
439
|
+
assert tree.children[0].attributes["class"] == "c0 c2"
|
433
440
|
assert tree.children[1].attributes["class"] == cl2_out
|
434
441
|
|
435
442
|
# Checking the final HTML code
|
436
|
-
mix_node = '<textnode class="
|
437
|
-
'<htmlnode class="
|
443
|
+
mix_node = '<textnode class="c0 c2">a</textnode>' if mix else \
|
444
|
+
'<htmlnode class="c0 c2"></htmlnode>'
|
438
445
|
expected_html = '\n'.join([
|
439
446
|
f'<htmlnode class="{cl1_out}">',
|
440
447
|
f' {mix_node}',
|
@@ -453,7 +460,7 @@ class TestApplyCSS:
|
|
453
460
|
)
|
454
461
|
html_before = tree.to_html()
|
455
462
|
compiled_css = compile_css(tree)
|
456
|
-
assert compiled_css.rules == []
|
463
|
+
assert compiled_css.core.rules == []
|
457
464
|
apply_css(compiled_css, tree)
|
458
465
|
html_after = tree.to_html()
|
459
466
|
|
@@ -486,24 +493,24 @@ class TestApplyCSS:
|
|
486
493
|
|
487
494
|
# Checking the tree's new classes
|
488
495
|
assert "class" not in tree.attributes
|
489
|
-
assert tree.children[0].attributes["class"] == "
|
496
|
+
assert tree.children[0].attributes["class"] == "c0 c1"
|
490
497
|
assert tree.children[1].attributes["class"] == "z"
|
491
498
|
|
492
499
|
# Checking the final HTML code
|
493
500
|
expected_html = '\n'.join([
|
494
501
|
'<htmlnode>',
|
495
|
-
' <textnode class="
|
502
|
+
' <textnode class="c0 c1">a</textnode>',
|
496
503
|
' <htmlnode class="z"></htmlnode>',
|
497
504
|
'</htmlnode>'
|
498
505
|
])
|
499
506
|
assert tree.to_html() == expected_html
|
500
507
|
|
501
508
|
@pytest.mark.parametrize("class_in, class_out", [
|
502
|
-
(None, "
|
503
|
-
("", "
|
504
|
-
("z", "z
|
505
|
-
("
|
506
|
-
("
|
509
|
+
(None, "c0 c1"),
|
510
|
+
("", "c0 c1"),
|
511
|
+
("z", "z c0 c1"),
|
512
|
+
("c0", "c0 c1"),
|
513
|
+
("c1", "c1 c0"),
|
507
514
|
])
|
508
515
|
def test_apply_css_multiple_times(self, class_in, class_out):
|
509
516
|
tree = HTMLNode(style={"a": "0", "b": "1"}) if class_in is None else \
|
@@ -533,20 +540,20 @@ class TestApplyCSS:
|
|
533
540
|
|
534
541
|
|
535
542
|
class TestDefaultRuleNamer:
|
536
|
-
def
|
537
|
-
rules = [
|
538
|
-
|
543
|
+
def test_default_class_namer(self):
|
544
|
+
rules = [ClassRule(None, {"color": "red"}),
|
545
|
+
ClassRule(None, {"margin": "0"})]
|
539
546
|
for i, rule in enumerate(rules):
|
540
|
-
rule.name =
|
541
|
-
assert rules[0].name == "
|
542
|
-
assert rules[1].name == "
|
547
|
+
rule.name = default_class_namer(rules=rules, index=i)
|
548
|
+
assert rules[0].name == "c0"
|
549
|
+
assert rules[1].name == "c1"
|
543
550
|
|
544
|
-
def
|
545
|
-
rules = [
|
546
|
-
|
551
|
+
def test_default_class_namer_override(self):
|
552
|
+
rules = [ClassRule("first", {"color": "red"}),
|
553
|
+
ClassRule("second", {"margin": "0"})]
|
547
554
|
assert rules[0].name == "first"
|
548
555
|
assert rules[1].name == "second"
|
549
556
|
for i, rule in enumerate(rules):
|
550
|
-
rule.name =
|
551
|
-
assert rules[0].name == "
|
552
|
-
assert rules[1].name == "
|
557
|
+
rule.name = default_class_namer(rules=rules, index=i)
|
558
|
+
assert rules[0].name == "c0"
|
559
|
+
assert rules[1].name == "c1"
|