1import datetime
2
3from babel.dates import get_timezone
4from markupsafe import escape
5from markupsafe import Markup
6
7from lektor._compat import itervalues
8from lektor._compat import text_type
9from lektor.context import Context
10from lektor.datamodel import Field
11from lektor.types import BadValue
12from lektor.types import Undefined
13from lektor.types.formats import MarkdownDescriptor
14
15
16class DummySource(object):
17    url_path = "/"
18
19
20def make_field(env, type, **options):
21    return Field(env, "demo", type=env.types[type], options=options)
22
23
24def test_markdown(env, pad):
25    field = make_field(env, "markdown")
26
27    source = DummySource()
28
29    with Context(pad=pad):
30        rv = field.deserialize_value("Hello **World**!", pad=pad)
31        assert isinstance(rv, MarkdownDescriptor)
32        rv = rv.__get__(source)
33        assert rv
34        assert rv.source == "Hello **World**!"
35        assert escape(rv) == Markup("<p>Hello <strong>World</strong>!</p>\n")
36        assert rv.meta == {}
37
38        for val in "", None:
39            rv = field.deserialize_value(val, pad=pad)
40            assert isinstance(rv, MarkdownDescriptor)
41            rv = rv.__get__(source)
42            assert not rv
43            assert rv.source == ""
44            assert escape(rv) == Markup("")
45            assert rv.meta == {}
46
47
48def test_markdown_links(env, pad):
49    field = make_field(env, "markdown")
50    source = DummySource()
51
52    def md(s):
53        rv = field.deserialize_value(s, pad=pad)
54        assert isinstance(rv, MarkdownDescriptor)
55        return text_type(rv.__get__(source)).strip()
56
57    with Context(pad=pad):
58        assert md("[foo](http://example.com/)") == (
59            '<p><a href="http://example.com/">foo</a></p>'
60        )
61        assert md("[foo](javascript:foo)") == (
62            '<p><a href="javascript:foo">foo</a></p>'
63        )
64
65        img = (
66            "iVBORw0KGgoAAAANSUhEUgAAAAUAAAAFCAYAAACNbyblAAAAHElEQVQI12P4"
67            "//8/w38GIAXDIBKE0DHxgljNBAAO9TXL0Y4OHwAAAABJRU5ErkJggg=="
68        )
69        assert (
70            md("![test](data:image/png;base64,%s)" % img)
71            == ('<p><img src="data:image/png;base64,%s" alt="test"></p>') % img
72        )
73
74
75def test_markdown_linking(pad, builder):
76    blog_index = pad.get("/blog", page_num=1)
77
78    prog, _ = builder.build(blog_index)
79    with prog.artifacts[0].open("rb") as f:
80        assert (
81            b'Look at my <a href="2015/12/post1/hello.txt">' b"attachment</a>"
82        ) in f.read()
83
84    blog_post = pad.get("/blog/post1")
85
86    prog, _ = builder.build(blog_post)
87    with prog.artifacts[0].open("rb") as f:
88        assert b'Look at my <a href="hello.txt">' b"attachment</a>" in f.read()
89
90
91def test_markdown_images(pad, builder):
92    blog_index = pad.get("/blog", page_num=1)
93
94    prog, _ = builder.build(blog_index)
95    with prog.artifacts[0].open("rb") as f:
96        assert (
97            b'This is an image <img src="2015/12/' b'post1/logo.png" alt="logo">.'
98        ) in f.read()
99
100    blog_post = pad.get("/blog/post1")
101
102    prog, _ = builder.build(blog_post)
103    with prog.artifacts[0].open("rb") as f:
104        assert b'This is an image <img src="logo.png" alt="logo">.' in f.read()
105
106
107def test_string(env, pad):
108    field = make_field(env, "string")
109
110    with Context(pad=pad):
111        rv = field.deserialize_value("", pad=pad)
112        assert rv == ""
113
114        rv = field.deserialize_value(None, pad=pad)
115        assert isinstance(rv, Undefined)
116
117        rv = field.deserialize_value("foo\nbar", pad=pad)
118        assert rv == "foo"
119
120        rv = field.deserialize_value(" 123 ", pad=pad)
121        assert rv == "123"
122
123
124def test_text(env, pad):
125    field = make_field(env, "text")
126
127    with Context(pad=pad):
128        rv = field.deserialize_value("", pad=pad)
129        assert rv == ""
130
131        rv = field.deserialize_value(None, pad=pad)
132        assert isinstance(rv, Undefined)
133
134        rv = field.deserialize_value("foo\nbar", pad=pad)
135        assert rv == "foo\nbar"
136
137        rv = field.deserialize_value(" 123 ", pad=pad)
138        assert rv == " 123 "
139
140
141def test_integer(env, pad):
142    field = make_field(env, "integer")
143
144    with Context(pad=pad):
145        rv = field.deserialize_value("", pad=pad)
146        assert isinstance(rv, BadValue)
147
148        rv = field.deserialize_value(None, pad=pad)
149        assert isinstance(rv, Undefined)
150
151        rv = field.deserialize_value("42", pad=pad)
152        assert rv == 42
153
154        rv = field.deserialize_value(" 23 ", pad=pad)
155        assert rv == 23
156
157
158def test_float(env, pad):
159    field = make_field(env, "float")
160
161    with Context(pad=pad):
162        rv = field.deserialize_value("", pad=pad)
163        assert isinstance(rv, BadValue)
164
165        rv = field.deserialize_value(None, pad=pad)
166        assert isinstance(rv, Undefined)
167
168        rv = field.deserialize_value("42", pad=pad)
169        assert rv == 42.0
170
171        rv = field.deserialize_value(" 23.0 ", pad=pad)
172        assert rv == 23.0
173
174        rv = field.deserialize_value("-23.5", pad=pad)
175        assert rv == -23.5
176
177
178def test_boolean(env, pad):
179    field = make_field(env, "boolean")
180
181    with Context(pad=pad):
182        rv = field.deserialize_value("", pad=pad)
183        assert isinstance(rv, BadValue)
184
185        rv = field.deserialize_value(None, pad=pad)
186        assert isinstance(rv, Undefined)
187
188        for s in "true", "TRUE", "True", "1", "yes":
189            rv = field.deserialize_value(s, pad=pad)
190            assert rv is True
191
192        for s in "false", "FALSE", "False", "0", "no":
193            rv = field.deserialize_value(s, pad=pad)
194            assert rv is False
195
196
197def test_datetime(env, pad):
198    field = make_field(env, "datetime")
199
200    with Context(pad=pad):
201        # default
202        rv = field.deserialize_value("2016-04-30 01:02:03", pad=pad)
203        assert isinstance(rv, datetime.datetime)
204        assert rv.year == 2016
205        assert rv.month == 4
206        assert rv.day == 30
207        assert rv.hour == 1
208        assert rv.minute == 2
209        assert rv.second == 3
210        assert rv.tzinfo is None
211
212        # It is not datetime, it is None
213        rv = field.deserialize_value(None, pad=pad)
214        assert isinstance(rv, Undefined)
215
216        # It is not datetime, it is empty string
217        rv = field.deserialize_value("", pad=pad)
218        assert isinstance(rv, BadValue)
219
220        # It is not datetime, it is date
221        rv = field.deserialize_value("2016-04-30", pad=pad)
222        assert isinstance(rv, BadValue)
223
224
225def test_datetime_timezone_utc(env, pad):
226    field = make_field(env, "datetime")
227    with Context(pad=pad):
228        # Known timezone name, UTC
229        rv = field.deserialize_value("2016-04-30 01:02:03 UTC", pad=pad)
230        assert isinstance(rv, datetime.datetime)
231        assert rv.year == 2016
232        assert rv.month == 4
233        assert rv.day == 30
234        assert rv.hour == 1
235        assert rv.minute == 2
236        assert rv.second == 3
237        assert rv.tzinfo is get_timezone("UTC")
238
239
240def test_datetime_timezone_est(env, pad):
241    field = make_field(env, "datetime")
242    with Context(pad=pad):
243        # Known timezone name, EST
244        rv = field.deserialize_value("2016-04-30 01:02:03 EST", pad=pad)
245        assert isinstance(rv, datetime.datetime)
246        assert rv.year == 2016
247        assert rv.month == 4
248        assert rv.day == 30
249        assert rv.hour == 1
250        assert rv.minute == 2
251        assert rv.second == 3
252        assert rv.tzinfo is get_timezone("EST")
253
254
255def test_datetime_timezone_location(env, pad):
256    field = make_field(env, "datetime")
257    with Context(pad=pad):
258        # Known location name, Asia/Seoul
259        rv = field.deserialize_value("2016-04-30 01:02:03 Asia/Seoul", pad=pad)
260        assert isinstance(rv, datetime.datetime)
261        assert rv.year == 2016
262        assert rv.month == 4
263        assert rv.day == 30
264        assert rv.hour == 1
265        assert rv.minute == 2
266        assert rv.second == 3
267        tzinfos = get_timezone("Asia/Seoul")._tzinfos  # pylint: disable=no-member
268        assert rv.tzinfo in itervalues(tzinfos)
269
270
271def test_datetime_timezone_kst(env, pad):
272    field = make_field(env, "datetime")
273    with Context(pad=pad):
274        # KST - http://www.timeanddate.com/time/zones/kst
275        rv = field.deserialize_value("2016-04-30 01:02:03 +0900", pad=pad)
276        assert isinstance(rv, datetime.datetime)
277        assert rv.year == 2016
278        assert rv.month == 4
279        assert rv.day == 30
280        assert rv.hour == 1
281        assert rv.minute == 2
282        assert rv.second == 3
283        assert rv.tzinfo._offset == datetime.timedelta(0, 9 * 60 * 60)
284
285
286def test_datetime_timezone_acst(env, pad):
287    field = make_field(env, "datetime")
288    with Context(pad=pad):
289        # ACST - http://www.timeanddate.com/time/zones/acst
290        rv = field.deserialize_value("2016-04-30 01:02:03 +0930", pad=pad)
291        assert isinstance(rv, datetime.datetime)
292        assert rv.year == 2016
293        assert rv.month == 4
294        assert rv.day == 30
295        assert rv.hour == 1
296        assert rv.minute == 2
297        assert rv.second == 3
298        assert rv.tzinfo._offset == datetime.timedelta(0, (9 * 60 + 30) * 60)
299
300
301def test_datetime_timezone_mst(env, pad):
302    field = make_field(env, "datetime")
303    with Context(pad=pad):
304        # MST - http://www.timeanddate.com/time/zones/mst
305        rv = field.deserialize_value("2016-04-30 01:02:03 -0700", pad=pad)
306        assert isinstance(rv, datetime.datetime)
307        assert rv.year == 2016
308        assert rv.month == 4
309        assert rv.day == 30
310        assert rv.hour == 1
311        assert rv.minute == 2
312        assert rv.second == 3
313        assert rv.tzinfo._offset == datetime.timedelta(0, -7 * 60 * 60)
314
315
316def test_datetime_timezone_mart(env, pad):
317    field = make_field(env, "datetime")
318    with Context(pad=pad):
319        # MART - http://www.timeanddate.com/time/zones/mart
320        rv = field.deserialize_value("2016-04-30 01:02:03 -0930", pad=pad)
321        assert isinstance(rv, datetime.datetime)
322        assert rv.year == 2016
323        assert rv.month == 4
324        assert rv.day == 30
325        assert rv.hour == 1
326        assert rv.minute == 2
327        assert rv.second == 3
328        assert rv.tzinfo._offset == datetime.timedelta(0, -(9 * 60 + 30) * 60)
329
330
331def test_datetime_timezone_name(env, pad):
332    field = make_field(env, "datetime")
333    with Context(pad=pad):
334        # with timezone name (case 1)
335        rv = field.deserialize_value("2016-04-30 01:02:03 KST +0900", pad=pad)
336        assert isinstance(rv, datetime.datetime)
337        assert rv.year == 2016
338        assert rv.month == 4
339        assert rv.day == 30
340        assert rv.hour == 1
341        assert rv.minute == 2
342        assert rv.second == 3
343        assert rv.tzinfo._offset == datetime.timedelta(0, 9 * 60 * 60)
344
345        # with timezone name (case 2)
346        rv = field.deserialize_value("2016-04-30 01:02:03 KST+0900", pad=pad)
347        assert isinstance(rv, datetime.datetime)
348        assert rv.year == 2016
349        assert rv.month == 4
350        assert rv.day == 30
351        assert rv.hour == 1
352        assert rv.minute == 2
353        assert rv.second == 3
354        assert rv.tzinfo._offset == datetime.timedelta(0, 9 * 60 * 60)
355