xhtm 0.2.0__tar.gz → 0.3.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.
- {xhtm-0.2.0 → xhtm-0.3.0}/PKG-INFO +1 -1
- {xhtm-0.2.0 → xhtm-0.3.0}/xhtm.egg-info/PKG-INFO +1 -1
- {xhtm-0.2.0 → xhtm-0.3.0}/xhtm.egg-info/SOURCES.txt +8 -1
- xhtm-0.3.0/xhtml/__init__.py +11 -0
- {xhtm-0.2.0 → xhtm-0.3.0}/xhtml/attribute.py +1 -1
- xhtm-0.3.0/xhtml/element/__init__.py +20 -0
- {xhtm-0.2.0 → xhtm-0.3.0}/xhtml/element/css.py +3 -3
- {xhtm-0.2.0 → xhtm-0.3.0}/xhtml/element/doc.py +2 -2
- {xhtm-0.2.0 → xhtm-0.3.0}/xhtml/element/tag.py +6 -6
- xhtm-0.3.0/xhtml/flask/__init__.py +3 -0
- xhtm-0.3.0/xhtml/header/__init__.py +4 -0
- xhtm-0.3.0/xhtml/request/__init__.py +3 -0
- xhtm-0.3.0/xhtml/template/__init__.py +32 -0
- xhtm-0.3.0/xhtml/unittest/__init__.py +0 -0
- xhtm-0.3.0/xhtml/unittest/test_element.py +108 -0
- xhtm-0.3.0/xhtml/unittest/test_flask.py +78 -0
- xhtm-0.3.0/xhtml/unittest/test_header.py +58 -0
- xhtm-0.3.0/xhtml/unittest/test_request.py +44 -0
- xhtm-0.3.0/xhtml/unittest/test_resource.py +59 -0
- xhtm-0.3.0/xhtml/unittest/test_template.py +33 -0
- xhtm-0.2.0/xhtml/__init__.py +0 -10
- xhtm-0.2.0/xhtml/element/__init__.py +0 -20
- xhtm-0.2.0/xhtml/flask/__init__.py +0 -3
- xhtm-0.2.0/xhtml/header/__init__.py +0 -4
- xhtm-0.2.0/xhtml/request/__init__.py +0 -3
- xhtm-0.2.0/xhtml/template/__init__.py +0 -17
- {xhtm-0.2.0 → xhtm-0.3.0}/LICENSE +0 -0
- {xhtm-0.2.0 → xhtm-0.3.0}/README.md +0 -0
- {xhtm-0.2.0 → xhtm-0.3.0}/setup.cfg +0 -0
- {xhtm-0.2.0 → xhtm-0.3.0}/setup.py +0 -0
- {xhtm-0.2.0 → xhtm-0.3.0}/xhtm.egg-info/dependency_links.txt +0 -0
- {xhtm-0.2.0 → xhtm-0.3.0}/xhtm.egg-info/requires.txt +0 -0
- {xhtm-0.2.0 → xhtm-0.3.0}/xhtm.egg-info/top_level.txt +0 -0
- {xhtm-0.2.0 → xhtm-0.3.0}/xhtm.egg-info/zip-safe +0 -0
- {xhtm-0.2.0 → xhtm-0.3.0}/xhtml/element/attr.py +0 -0
- {xhtm-0.2.0 → xhtm-0.3.0}/xhtml/flask/proxy.py +0 -0
- {xhtm-0.2.0 → xhtm-0.3.0}/xhtml/header/accept.py +0 -0
- {xhtm-0.2.0 → xhtm-0.3.0}/xhtml/header/content.py +0 -0
- {xhtm-0.2.0 → xhtm-0.3.0}/xhtml/request/stream.py +0 -0
- {xhtm-0.2.0 → xhtm-0.3.0}/xhtml/resource/__init__.py +0 -0
- {xhtm-0.2.0 → xhtm-0.3.0}/xhtml/resource/favicon.ico +0 -0
- {xhtm-0.2.0 → xhtm-0.3.0}/xhtml/resource/logo.svg +0 -0
- {xhtm-0.2.0 → xhtm-0.3.0}/xhtml/template/hello.html +0 -0
|
@@ -26,4 +26,11 @@ xhtml/resource/__init__.py
|
|
|
26
26
|
xhtml/resource/favicon.ico
|
|
27
27
|
xhtml/resource/logo.svg
|
|
28
28
|
xhtml/template/__init__.py
|
|
29
|
-
xhtml/template/hello.html
|
|
29
|
+
xhtml/template/hello.html
|
|
30
|
+
xhtml/unittest/__init__.py
|
|
31
|
+
xhtml/unittest/test_element.py
|
|
32
|
+
xhtml/unittest/test_flask.py
|
|
33
|
+
xhtml/unittest/test_header.py
|
|
34
|
+
xhtml/unittest/test_request.py
|
|
35
|
+
xhtml/unittest/test_resource.py
|
|
36
|
+
xhtml/unittest/test_template.py
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
# coding:utf-8
|
|
2
|
+
|
|
3
|
+
from xhtml.element import * # noqa:F401,F403
|
|
4
|
+
from xhtml.flask import FlaskProxy # noqa:F401
|
|
5
|
+
from xhtml.header import AcceptLanguage # noqa:F401
|
|
6
|
+
from xhtml.header import LanguageQ # noqa:F401
|
|
7
|
+
from xhtml.request import StreamResponse # noqa:F401
|
|
8
|
+
from xhtml.resource import FileResource # noqa:F401
|
|
9
|
+
from xhtml.resource import Resource # noqa:F401
|
|
10
|
+
from xhtml.template import LocaleTemplate # noqa:F401
|
|
11
|
+
from xhtml.template import Template # noqa:F401
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
# coding:utf-8
|
|
2
|
+
|
|
3
|
+
from xhtml.element.attr import Attr # noqa:F401
|
|
4
|
+
from xhtml.element.css import StyleCSS # noqa:F401
|
|
5
|
+
from xhtml.element.doc import HtmlDoc # noqa:F401
|
|
6
|
+
from xhtml.element.tag import Body # noqa:F401
|
|
7
|
+
from xhtml.element.tag import Br # noqa:F401
|
|
8
|
+
from xhtml.element.tag import Div # noqa:F401
|
|
9
|
+
from xhtml.element.tag import EmptyTag # noqa:F401
|
|
10
|
+
from xhtml.element.tag import Form # noqa:F401
|
|
11
|
+
from xhtml.element.tag import FormAttrs # noqa:F401
|
|
12
|
+
from xhtml.element.tag import Head # noqa:F401
|
|
13
|
+
from xhtml.element.tag import Html # noqa:F401
|
|
14
|
+
from xhtml.element.tag import HtmlAttrs # noqa:F401
|
|
15
|
+
from xhtml.element.tag import Input # noqa:F401
|
|
16
|
+
from xhtml.element.tag import InputAttrs # noqa:F401
|
|
17
|
+
from xhtml.element.tag import Span # noqa:F401
|
|
18
|
+
from xhtml.element.tag import Tag # noqa:F401
|
|
19
|
+
from xhtml.element.tag import TextTag # noqa:F401
|
|
20
|
+
from xhtml.element.tag import Title # noqa:F401
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# coding:utf-8
|
|
2
2
|
|
|
3
|
-
from .tag import Html
|
|
3
|
+
from xhtml.element.tag import Html
|
|
4
4
|
|
|
5
5
|
|
|
6
6
|
class HtmlDoc(Html):
|
|
@@ -9,7 +9,7 @@ class HtmlDoc(Html):
|
|
|
9
9
|
super().__init__()
|
|
10
10
|
|
|
11
11
|
def __str__(self) -> str:
|
|
12
|
-
return f"<!DOCTYPE {self.
|
|
12
|
+
return f"<!DOCTYPE {self.doctype}>\n{super().__str__()}"
|
|
13
13
|
|
|
14
14
|
@property
|
|
15
15
|
def doctype(self) -> str:
|
|
@@ -6,12 +6,12 @@ from typing import List
|
|
|
6
6
|
from typing import Optional
|
|
7
7
|
from typing import TypeVar
|
|
8
8
|
|
|
9
|
-
from
|
|
10
|
-
from
|
|
11
|
-
from .attr import Args
|
|
12
|
-
from .attr import Arguments
|
|
13
|
-
from .attr import Attr
|
|
14
|
-
from .css import StyleCSS
|
|
9
|
+
from xhtml.attribute import __description__
|
|
10
|
+
from xhtml.attribute import __project__
|
|
11
|
+
from xhtml.element.attr import Args
|
|
12
|
+
from xhtml.element.attr import Arguments
|
|
13
|
+
from xhtml.element.attr import Attr
|
|
14
|
+
from xhtml.element.css import StyleCSS
|
|
15
15
|
|
|
16
16
|
|
|
17
17
|
class Attrs(Args):
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
# coding:utf-8
|
|
2
|
+
|
|
3
|
+
from os.path import abspath
|
|
4
|
+
from os.path import dirname
|
|
5
|
+
from os.path import isdir
|
|
6
|
+
from os.path import join
|
|
7
|
+
from typing import Optional
|
|
8
|
+
|
|
9
|
+
from xlc import Message
|
|
10
|
+
from xlc import Section
|
|
11
|
+
|
|
12
|
+
from xhtml.header import AcceptLanguage
|
|
13
|
+
from xhtml.resource import Resource
|
|
14
|
+
|
|
15
|
+
BASE_DIR = dirname(abspath(__file__))
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
class Template(Resource):
|
|
19
|
+
FAVICON: str = "favicon.ico"
|
|
20
|
+
|
|
21
|
+
def __init__(self, base: Optional[str] = None):
|
|
22
|
+
super().__init__(base if base and isdir(base) else BASE_DIR)
|
|
23
|
+
|
|
24
|
+
|
|
25
|
+
class LocaleTemplate(Template):
|
|
26
|
+
def __init__(self, base: str):
|
|
27
|
+
self.__message: Message = Message.load(join(base, "locale"))
|
|
28
|
+
super().__init__(base)
|
|
29
|
+
|
|
30
|
+
def search(self, accept_language: str, section: str) -> Section:
|
|
31
|
+
language: AcceptLanguage = AcceptLanguage(accept_language)
|
|
32
|
+
return language.choice(self.__message).seek(section)
|
|
File without changes
|
|
@@ -0,0 +1,108 @@
|
|
|
1
|
+
# coding:utf-8
|
|
2
|
+
|
|
3
|
+
import unittest
|
|
4
|
+
|
|
5
|
+
from xhtml.element.doc import HtmlDoc
|
|
6
|
+
from xhtml.element.tag import Br
|
|
7
|
+
from xhtml.element.tag import Div
|
|
8
|
+
from xhtml.element.tag import Form
|
|
9
|
+
from xhtml.element.tag import Input
|
|
10
|
+
from xhtml.element.tag import Span
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
class TestHtmlDoc(unittest.TestCase):
|
|
14
|
+
|
|
15
|
+
@classmethod
|
|
16
|
+
def setUpClass(cls):
|
|
17
|
+
cls.title: str = "Login"
|
|
18
|
+
cls.html: HtmlDoc = HtmlDoc()
|
|
19
|
+
cls.html.head.title.text = cls.title
|
|
20
|
+
cls.user: Input = Input({"type": "text", "name": "username", "placeholder": "Username"}) # noqa:E501
|
|
21
|
+
cls.user.attrs.std_style.width.v = "200px"
|
|
22
|
+
cls.user.attrs.std_style.height.v = "30px"
|
|
23
|
+
cls.word: Input = Input({"type": "password", "name": "password", "placeholder": "Password"}) # noqa:E501
|
|
24
|
+
cls.word.attrs.std_style.width.v = "200px"
|
|
25
|
+
cls.word.attrs.std_style.height.v = "30px"
|
|
26
|
+
cls.post: Input = Input({"type": "submit", "value": "Submit"})
|
|
27
|
+
cls.post.attrs.std_style.width.v = "200px"
|
|
28
|
+
cls.post.attrs.std_style.height.v = "30px"
|
|
29
|
+
cls.form: Form = Form({"method": "post"})
|
|
30
|
+
cls.form.add(Div(attrs={"style": "text-align: center;"}, child=[cls.user, Br()])) # noqa: E501
|
|
31
|
+
cls.form.add(Span(attrs={"style": "display: block; margin-bottom: 10px;"})) # noqa: E501
|
|
32
|
+
cls.form.add(Div(attrs={"style": "text-align: center;"}, child=[cls.word, Br()])) # noqa: E501
|
|
33
|
+
cls.form.add(Span(attrs={"style": "display: block; margin-bottom: 20px;"})) # noqa: E501
|
|
34
|
+
cls.form.add(Div(attrs={"style": "text-align: center;"}, child=[cls.post, Br()])) # noqa: E501
|
|
35
|
+
cls.root: Div = Div()
|
|
36
|
+
cls.root.add(cls.form)
|
|
37
|
+
cls.root.attrs.std_style.height.v = "60vh"
|
|
38
|
+
cls.root.attrs.std_style.display.v = "grid"
|
|
39
|
+
cls.root.attrs.std_style.place_items.v = "center"
|
|
40
|
+
cls.html.body.add(cls.root)
|
|
41
|
+
|
|
42
|
+
@classmethod
|
|
43
|
+
def tearDownClass(cls):
|
|
44
|
+
pass
|
|
45
|
+
|
|
46
|
+
def setUp(self):
|
|
47
|
+
pass
|
|
48
|
+
|
|
49
|
+
def tearDown(self):
|
|
50
|
+
pass
|
|
51
|
+
|
|
52
|
+
def test_html(self):
|
|
53
|
+
self.assertIsInstance(str(self.html), str)
|
|
54
|
+
|
|
55
|
+
def test_std_attrs(self):
|
|
56
|
+
self.assertEqual(len(self.html.attrs), 0)
|
|
57
|
+
self.assertEqual(self.html.attrs.xmlns.v, "")
|
|
58
|
+
self.assertEqual(self.html.attrs.std_accesskey.v, "")
|
|
59
|
+
self.assertEqual(self.html.attrs.std_class.v, "")
|
|
60
|
+
self.assertEqual(self.html.attrs.std_contenteditable.v, "")
|
|
61
|
+
self.assertEqual(self.html.attrs.std_contextmenu.v, "")
|
|
62
|
+
self.assertEqual(self.html.attrs.std_dir.v, "")
|
|
63
|
+
self.assertEqual(self.html.attrs.std_draggable.v, "")
|
|
64
|
+
self.assertEqual(self.html.attrs.std_dropzone.v, "")
|
|
65
|
+
self.assertEqual(self.html.attrs.std_enterkeyhint.v, "")
|
|
66
|
+
self.assertEqual(self.html.attrs.std_hidden.v, "")
|
|
67
|
+
self.assertEqual(self.html.attrs.std_id.v, "")
|
|
68
|
+
self.assertEqual(self.html.attrs.std_inert.v, "")
|
|
69
|
+
self.assertEqual(self.html.attrs.std_inputmode.v, "")
|
|
70
|
+
self.assertEqual(self.html.attrs.std_lang.v, "")
|
|
71
|
+
self.assertEqual(self.html.attrs.std_popover.v, "")
|
|
72
|
+
self.assertEqual(self.html.attrs.std_spellcheck.v, "")
|
|
73
|
+
self.assertEqual(self.html.attrs.std_tabindex.v, "")
|
|
74
|
+
self.assertEqual(self.html.attrs.std_title.v, "")
|
|
75
|
+
self.assertEqual(self.html.attrs.std_translate.v, "")
|
|
76
|
+
self.assertNotIn("test", self.html.attrs)
|
|
77
|
+
self.html.attrs["test"] = "unit"
|
|
78
|
+
self.assertEqual(self.html.attrs["test"].v, "unit")
|
|
79
|
+
self.assertEqual(len(self.html.attrs), 20)
|
|
80
|
+
self.assertIsInstance(self.html.attrs.keys(), tuple)
|
|
81
|
+
self.assertIsInstance(self.html.attrs.values(), tuple)
|
|
82
|
+
self.assertIsInstance(self.html.attrs.items(), tuple)
|
|
83
|
+
self.assertNotIn("unit", self.html.attrs)
|
|
84
|
+
self.html.attrs.hit("unit", "test")
|
|
85
|
+
self.assertEqual(self.html.attrs["unit"].v, "test")
|
|
86
|
+
self.assertEqual(len(self.html.attrs), 21)
|
|
87
|
+
|
|
88
|
+
def test_css_style(self):
|
|
89
|
+
self.assertEqual(self.root.attrs.std_style.margin.v, "")
|
|
90
|
+
self.assertEqual(self.root.attrs.std_style.margin_top.v, "")
|
|
91
|
+
self.assertEqual(self.root.attrs.std_style.margin_bottom.v, "")
|
|
92
|
+
self.assertEqual(self.root.attrs.std_style.margin_left.v, "")
|
|
93
|
+
self.assertEqual(self.root.attrs.std_style.margin_right.v, "")
|
|
94
|
+
self.assertEqual(self.root.attrs.std_style.text_align.v, "")
|
|
95
|
+
self.assertEqual(self.root.attrs.std_style.vertical_align.v, "")
|
|
96
|
+
|
|
97
|
+
def test_form(self):
|
|
98
|
+
self.assertEqual(self.form.attrs.method.v, "post")
|
|
99
|
+
|
|
100
|
+
def test_input(self):
|
|
101
|
+
self.assertEqual(self.user.attrs.name.v, "username")
|
|
102
|
+
self.assertEqual(self.user.attrs.placeholder.v, "Username")
|
|
103
|
+
self.assertEqual(self.user.attrs.type.v, "text")
|
|
104
|
+
self.assertEqual(self.user.attrs.value.v, "")
|
|
105
|
+
|
|
106
|
+
|
|
107
|
+
if __name__ == "__main__":
|
|
108
|
+
unittest.main()
|
|
@@ -0,0 +1,78 @@
|
|
|
1
|
+
# coding:utf-8
|
|
2
|
+
|
|
3
|
+
import unittest
|
|
4
|
+
from unittest.mock import MagicMock
|
|
5
|
+
from unittest.mock import patch
|
|
6
|
+
|
|
7
|
+
from flask import Request
|
|
8
|
+
from werkzeug.test import EnvironBuilder
|
|
9
|
+
|
|
10
|
+
from xhtml.flask import proxy
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
class TestFlaskProxy(unittest.TestCase):
|
|
14
|
+
|
|
15
|
+
@classmethod
|
|
16
|
+
def setUpClass(cls):
|
|
17
|
+
cls.cookie = MagicMock()
|
|
18
|
+
cls.cookie.name = "test"
|
|
19
|
+
cls.cookie.value = "unittest"
|
|
20
|
+
cls.cookie.expires = 1000
|
|
21
|
+
cls.cookie.path = "/"
|
|
22
|
+
cls.cookie.domain = "example.com"
|
|
23
|
+
cls.cookie.secure = True
|
|
24
|
+
|
|
25
|
+
@classmethod
|
|
26
|
+
def tearDownClass(cls):
|
|
27
|
+
pass
|
|
28
|
+
|
|
29
|
+
def setUp(self):
|
|
30
|
+
self.proxy = proxy.FlaskProxy("http://example.com")
|
|
31
|
+
|
|
32
|
+
def tearDown(self):
|
|
33
|
+
pass
|
|
34
|
+
|
|
35
|
+
@patch.object(proxy.requests, "get")
|
|
36
|
+
@patch.object(proxy, "stream_with_context")
|
|
37
|
+
def test_request_get_success(self, mock_stream_with_context, mock_get):
|
|
38
|
+
fake_response = MagicMock()
|
|
39
|
+
fake_response.status_code = 200
|
|
40
|
+
fake_response.raw.headers = {"Content-Type": "text/html"}
|
|
41
|
+
fake_response.cookies = [self.cookie]
|
|
42
|
+
mock_get.return_value = fake_response
|
|
43
|
+
mock_stream_with_context.side_effect = ["unittest"]
|
|
44
|
+
request = Request(EnvironBuilder().get_environ())
|
|
45
|
+
response = self.proxy.request(request)
|
|
46
|
+
self.assertEqual(response.status_code, 200)
|
|
47
|
+
self.assertEqual(response.headers["Content-Type"], "text/html")
|
|
48
|
+
|
|
49
|
+
@patch.object(proxy.requests, "post")
|
|
50
|
+
@patch.object(proxy, "stream_with_context")
|
|
51
|
+
def test_request_post_success(self, mock_stream_with_context, mock_post):
|
|
52
|
+
fake_response = MagicMock()
|
|
53
|
+
fake_response.status_code = 201
|
|
54
|
+
fake_response.raw.headers = {"Content-Type": "application/json"}
|
|
55
|
+
fake_response.cookies = [self.cookie]
|
|
56
|
+
mock_post.return_value = fake_response
|
|
57
|
+
mock_stream_with_context.side_effect = ["unittest"]
|
|
58
|
+
request = Request(EnvironBuilder(method="post").get_environ())
|
|
59
|
+
response = self.proxy.request(request)
|
|
60
|
+
self.assertEqual(response.status_code, 201)
|
|
61
|
+
self.assertEqual(response.headers["Content-Type"], "application/json")
|
|
62
|
+
|
|
63
|
+
@patch.object(proxy.requests, "get")
|
|
64
|
+
def test_request_UnsupportedMethod(self, mock_get):
|
|
65
|
+
request = Request(EnvironBuilder(method="put").get_environ())
|
|
66
|
+
response = self.proxy.request(request)
|
|
67
|
+
self.assertEqual(response.status_code, 405)
|
|
68
|
+
|
|
69
|
+
@patch.object(proxy.requests, "get")
|
|
70
|
+
def test_request_ConnectionError(self, mock_get):
|
|
71
|
+
mock_get.side_effect = proxy.requests.ConnectionError
|
|
72
|
+
request = Request(EnvironBuilder().get_environ())
|
|
73
|
+
response = self.proxy.request(request)
|
|
74
|
+
self.assertEqual(response.status_code, 502)
|
|
75
|
+
|
|
76
|
+
|
|
77
|
+
if __name__ == "__main__":
|
|
78
|
+
unittest.main()
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
# coding:utf-8
|
|
2
|
+
|
|
3
|
+
import unittest
|
|
4
|
+
|
|
5
|
+
from xhtml.header.accept import AcceptLanguage
|
|
6
|
+
from xhtml.header.accept import LanguageQ
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
class TestLanguageQ(unittest.TestCase):
|
|
10
|
+
|
|
11
|
+
@classmethod
|
|
12
|
+
def setUpClass(cls):
|
|
13
|
+
cls.q: LanguageQ = LanguageQ("zh-CN,zh", 0.9)
|
|
14
|
+
|
|
15
|
+
@classmethod
|
|
16
|
+
def tearDownClass(cls):
|
|
17
|
+
pass
|
|
18
|
+
|
|
19
|
+
def setUp(self):
|
|
20
|
+
pass
|
|
21
|
+
|
|
22
|
+
def tearDown(self):
|
|
23
|
+
pass
|
|
24
|
+
|
|
25
|
+
def test_str(self):
|
|
26
|
+
self.assertEqual(str(self.q), "zh-CN,zh;q=0.9")
|
|
27
|
+
|
|
28
|
+
def test_len(self):
|
|
29
|
+
self.assertEqual(len(self.q), 2)
|
|
30
|
+
|
|
31
|
+
|
|
32
|
+
class TestAcceptLanguage(unittest.TestCase):
|
|
33
|
+
|
|
34
|
+
@classmethod
|
|
35
|
+
def setUpClass(cls):
|
|
36
|
+
cls.lang: AcceptLanguage = AcceptLanguage("zh-CN,zh;q=0.9,en;q=0.8,en-GB;q=0.7,en-US;q=0.6") # noqa:E501
|
|
37
|
+
|
|
38
|
+
@classmethod
|
|
39
|
+
def tearDownClass(cls):
|
|
40
|
+
pass
|
|
41
|
+
|
|
42
|
+
def setUp(self):
|
|
43
|
+
pass
|
|
44
|
+
|
|
45
|
+
def tearDown(self):
|
|
46
|
+
pass
|
|
47
|
+
|
|
48
|
+
def test_len_and_contains(self):
|
|
49
|
+
self.assertEqual(len(self.lang), 5)
|
|
50
|
+
self.assertNotIn("zh-TW", self.lang)
|
|
51
|
+
self.assertNotIn("zh-HK", self.lang)
|
|
52
|
+
self.assertIn("zh-CN", self.lang)
|
|
53
|
+
self.assertIn("zh", self.lang)
|
|
54
|
+
self.assertIn("en", self.lang)
|
|
55
|
+
|
|
56
|
+
|
|
57
|
+
if __name__ == "__main__":
|
|
58
|
+
unittest.main()
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
# coding:utf-8
|
|
2
|
+
|
|
3
|
+
from typing import Generator
|
|
4
|
+
import unittest
|
|
5
|
+
from unittest.mock import MagicMock
|
|
6
|
+
|
|
7
|
+
from xhtml.request import stream
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
class TestStreamResponse(unittest.TestCase):
|
|
11
|
+
|
|
12
|
+
@classmethod
|
|
13
|
+
def setUpClass(cls):
|
|
14
|
+
cls.cookie = MagicMock()
|
|
15
|
+
cls.cookie.name = "test"
|
|
16
|
+
cls.cookie.value = "unittest"
|
|
17
|
+
cls.cookie.expires = 1000
|
|
18
|
+
cls.cookie.path = "/"
|
|
19
|
+
cls.cookie.domain = "example.com"
|
|
20
|
+
cls.cookie.secure = True
|
|
21
|
+
|
|
22
|
+
@classmethod
|
|
23
|
+
def tearDownClass(cls):
|
|
24
|
+
pass
|
|
25
|
+
|
|
26
|
+
def setUp(self):
|
|
27
|
+
fake_response = MagicMock()
|
|
28
|
+
fake_response.status_code = 200
|
|
29
|
+
fake_response.raw.headers = {"Content-Type": "text/html"}
|
|
30
|
+
fake_response.cookies = [self.cookie]
|
|
31
|
+
fake_response.iter_content.return_value = ["unittest", "unittest"]
|
|
32
|
+
self.stream = stream.StreamResponse(fake_response)
|
|
33
|
+
|
|
34
|
+
def tearDown(self):
|
|
35
|
+
pass
|
|
36
|
+
|
|
37
|
+
def test_generator(self):
|
|
38
|
+
self.assertIsInstance(self.stream.generator, Generator)
|
|
39
|
+
for chunk in self.stream.generator:
|
|
40
|
+
self.assertEqual(chunk, "unittest")
|
|
41
|
+
|
|
42
|
+
|
|
43
|
+
if __name__ == "__main__":
|
|
44
|
+
unittest.main()
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
# coding:utf-8
|
|
2
|
+
|
|
3
|
+
import unittest
|
|
4
|
+
|
|
5
|
+
from xhtml.resource import FileResource
|
|
6
|
+
from xhtml.resource import Resource
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
class TestFileResource(unittest.TestCase):
|
|
10
|
+
|
|
11
|
+
@classmethod
|
|
12
|
+
def setUpClass(cls):
|
|
13
|
+
pass
|
|
14
|
+
|
|
15
|
+
@classmethod
|
|
16
|
+
def tearDownClass(cls):
|
|
17
|
+
pass
|
|
18
|
+
|
|
19
|
+
def setUp(self):
|
|
20
|
+
pass
|
|
21
|
+
|
|
22
|
+
def tearDown(self):
|
|
23
|
+
pass
|
|
24
|
+
|
|
25
|
+
def test_file_not_found(self):
|
|
26
|
+
self.assertRaises(FileNotFoundError, FileResource, "test.txt")
|
|
27
|
+
|
|
28
|
+
|
|
29
|
+
class TestResource(unittest.TestCase):
|
|
30
|
+
|
|
31
|
+
@classmethod
|
|
32
|
+
def setUpClass(cls):
|
|
33
|
+
cls.res: Resource = Resource()
|
|
34
|
+
|
|
35
|
+
@classmethod
|
|
36
|
+
def tearDownClass(cls):
|
|
37
|
+
pass
|
|
38
|
+
|
|
39
|
+
def setUp(self):
|
|
40
|
+
pass
|
|
41
|
+
|
|
42
|
+
def tearDown(self):
|
|
43
|
+
pass
|
|
44
|
+
|
|
45
|
+
def test_favicon(self):
|
|
46
|
+
self.assertIsInstance(self.res.favicon.loadb(), bytes)
|
|
47
|
+
|
|
48
|
+
def test_favicon_ext(self):
|
|
49
|
+
self.assertEqual(self.res.favicon.ext, ".ico")
|
|
50
|
+
|
|
51
|
+
def test_seek(self):
|
|
52
|
+
self.assertIsInstance(self.res.seek("logo.svg").loads(), str)
|
|
53
|
+
|
|
54
|
+
def test_seek_file_not_found(self):
|
|
55
|
+
self.assertRaises(FileNotFoundError, self.res.seek, "test.txt")
|
|
56
|
+
|
|
57
|
+
|
|
58
|
+
if __name__ == "__main__":
|
|
59
|
+
unittest.main()
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
# coding:utf-8
|
|
2
|
+
|
|
3
|
+
import os
|
|
4
|
+
import unittest
|
|
5
|
+
|
|
6
|
+
from xhtml.template import LocaleTemplate
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
class TestLocaleTemplate(unittest.TestCase):
|
|
10
|
+
|
|
11
|
+
@classmethod
|
|
12
|
+
def setUpClass(cls):
|
|
13
|
+
cls.base: str = os.path.dirname(os.path.abspath(__file__))
|
|
14
|
+
cls.template: LocaleTemplate = LocaleTemplate(cls.base)
|
|
15
|
+
|
|
16
|
+
@classmethod
|
|
17
|
+
def tearDownClass(cls):
|
|
18
|
+
pass
|
|
19
|
+
|
|
20
|
+
def setUp(self):
|
|
21
|
+
pass
|
|
22
|
+
|
|
23
|
+
def tearDown(self):
|
|
24
|
+
pass
|
|
25
|
+
|
|
26
|
+
def test_search(self):
|
|
27
|
+
self.assertEqual(self.template.search("zh-CN,zh;q=0.9,en;q=0.8,en-GB;q=0.7,en-US;q=0.6", "login").lang.tag, "zh-Hans") # noqa:E501
|
|
28
|
+
self.assertEqual(self.template.search("en", "login").lang.tag, "en")
|
|
29
|
+
self.assertEqual(self.template.search("fr", "login").lang.tag, "en")
|
|
30
|
+
|
|
31
|
+
|
|
32
|
+
if __name__ == "__main__":
|
|
33
|
+
unittest.main()
|
xhtm-0.2.0/xhtml/__init__.py
DELETED
|
@@ -1,10 +0,0 @@
|
|
|
1
|
-
# coding:utf-8
|
|
2
|
-
|
|
3
|
-
from .element import * # noqa:F401,F403
|
|
4
|
-
from .flask import FlaskProxy # noqa:F401
|
|
5
|
-
from .header import AcceptLanguage # noqa:F401
|
|
6
|
-
from .header import LanguageQ # noqa:F401
|
|
7
|
-
from .request import StreamResponse # noqa:F401
|
|
8
|
-
from .resource import FileResource # noqa:F401
|
|
9
|
-
from .resource import Resource # noqa:F401
|
|
10
|
-
from .template import Template # noqa:F401
|
|
@@ -1,20 +0,0 @@
|
|
|
1
|
-
# coding:utf-8
|
|
2
|
-
|
|
3
|
-
from .attr import Attr # noqa:F401
|
|
4
|
-
from .css import StyleCSS # noqa:F401
|
|
5
|
-
from .doc import HtmlDoc # noqa:F401
|
|
6
|
-
from .tag import Body # noqa:F401
|
|
7
|
-
from .tag import Br # noqa:F401
|
|
8
|
-
from .tag import Div # noqa:F401
|
|
9
|
-
from .tag import EmptyTag # noqa:F401
|
|
10
|
-
from .tag import Form # noqa:F401
|
|
11
|
-
from .tag import FormAttrs # noqa:F401
|
|
12
|
-
from .tag import Head # noqa:F401
|
|
13
|
-
from .tag import Html # noqa:F401
|
|
14
|
-
from .tag import HtmlAttrs # noqa:F401
|
|
15
|
-
from .tag import Input # noqa:F401
|
|
16
|
-
from .tag import InputAttrs # noqa:F401
|
|
17
|
-
from .tag import Span # noqa:F401
|
|
18
|
-
from .tag import Tag # noqa:F401
|
|
19
|
-
from .tag import TextTag # noqa:F401
|
|
20
|
-
from .tag import Title # noqa:F401
|
|
@@ -1,17 +0,0 @@
|
|
|
1
|
-
# coding:utf-8
|
|
2
|
-
|
|
3
|
-
from os.path import abspath
|
|
4
|
-
from os.path import dirname
|
|
5
|
-
from os.path import isdir
|
|
6
|
-
from typing import Optional
|
|
7
|
-
|
|
8
|
-
from xhtml.resource import Resource
|
|
9
|
-
|
|
10
|
-
BASE_DIR = dirname(abspath(__file__))
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
class Template(Resource):
|
|
14
|
-
FAVICON: str = "favicon.ico"
|
|
15
|
-
|
|
16
|
-
def __init__(self, base: Optional[str] = None):
|
|
17
|
-
super().__init__(base if base and isdir(base) else BASE_DIR)
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|