Hugoifier

hugoifier / tests / test_decapify.py
Blame History Raw 192 lines
1
"""Tests for utils.decapify."""
2
import os
3
import tempfile
4
import unittest
5
6
from hugoifier.utils.decapify import (
7
_build_collections,
8
_infer_fields_for_file,
9
_infer_fields_for_folder,
10
_parse_frontmatter,
11
_sanitize_color,
12
_widget_for_value,
13
decapify,
14
)
15
16
17
class TestSanitizeColor(unittest.TestCase):
18
def test_valid_hex6(self):
19
self.assertEqual(_sanitize_color("#2e3748"), "#2e3748")
20
21
def test_valid_hex3(self):
22
self.assertEqual(_sanitize_color("#fff"), "#fff")
23
24
def test_uppercase(self):
25
self.assertEqual(_sanitize_color("#AABBCC"), "#AABBCC")
26
27
def test_invalid_falls_back(self):
28
self.assertEqual(_sanitize_color("red"), "#2e3748")
29
self.assertEqual(_sanitize_color("javascript:alert(1)"), "#2e3748")
30
self.assertEqual(_sanitize_color("#gggggg"), "#2e3748")
31
32
33
class TestWidgetForValue(unittest.TestCase):
34
def test_bool(self):
35
self.assertEqual(_widget_for_value(True), "boolean")
36
self.assertEqual(_widget_for_value(False), "boolean")
37
38
def test_number(self):
39
self.assertEqual(_widget_for_value(42), "number")
40
self.assertEqual(_widget_for_value(3.14), "number")
41
42
def test_list(self):
43
self.assertEqual(_widget_for_value([]), "list")
44
self.assertEqual(_widget_for_value(["a", "b"]), "list")
45
46
def test_string_default(self):
47
self.assertEqual(_widget_for_value("hello"), "string")
48
self.assertEqual(_widget_for_value(None), "string")
49
50
51
class TestParseFrontmatter(unittest.TestCase):
52
def test_parses_yaml_frontmatter(self):
53
with tempfile.NamedTemporaryFile(mode="w", suffix=".md", delete=False) as f:
54
f.write("---\ntitle: Hello\ndate: 2024-01-01\ndraft: false\n---\n\nBody text.\n")
55
path = f.name
56
try:
57
result = _parse_frontmatter(path)
58
self.assertEqual(result["title"], "Hello")
59
self.assertEqual(result["draft"], False)
60
finally:
61
os.unlink(path)
62
63
def test_empty_on_missing_frontmatter(self):
64
with tempfile.NamedTemporaryFile(mode="w", suffix=".md", delete=False) as f:
65
f.write("No frontmatter here.\n")
66
path = f.name
67
try:
68
self.assertEqual(_parse_frontmatter(path), {})
69
finally:
70
os.unlink(path)
71
72
def test_empty_on_missing_file(self):
73
self.assertEqual(_parse_frontmatter("/nonexistent/path.md"), {})
74
75
76
class TestInferFields(unittest.TestCase):
77
def _make_md(self, dirpath, name, frontmatter):
78
path = os.path.join(dirpath, name)
79
with open(path, "w") as f:
80
f.write("---\n")
81
for k, v in frontmatter.items():
82
f.write(f"{k}: {v!r}\n")
83
f.write("---\n\nBody.\n")
84
return path
85
86
def test_folder_fields_include_known_keys(self):
87
with tempfile.TemporaryDirectory() as tmp:
88
self._make_md(tmp, "post1.md", {"title": "Hello", "date": "2024-01-01", "tags": ["a"]})
89
fields = _infer_fields_for_folder(tmp, ["post1.md"])
90
names = [f["name"] for f in fields]
91
self.assertIn("title", names)
92
self.assertIn("date", names)
93
self.assertIn("tags", names)
94
self.assertIn("body", names)
95
96
def test_folder_fields_always_end_with_body(self):
97
with tempfile.TemporaryDirectory() as tmp:
98
self._make_md(tmp, "post.md", {"title": "Test"})
99
fields = _infer_fields_for_folder(tmp, ["post.md"])
100
self.assertEqual(fields[-1]["name"], "body")
101
self.assertEqual(fields[-1]["widget"], "markdown")
102
103
def test_file_fields_include_all_frontmatter_keys(self):
104
with tempfile.NamedTemporaryFile(mode="w", suffix=".md", delete=False) as f:
105
f.write("---\ntitle: About\nsubtitle: Us\n---\n")
106
path = f.name
107
try:
108
fields = _infer_fields_for_file(path)
109
names = [fi["name"] for fi in fields]
110
self.assertIn("title", names)
111
self.assertIn("subtitle", names)
112
self.assertIn("body", names)
113
finally:
114
os.unlink(path)
115
116
117
class TestBuildCollections(unittest.TestCase):
118
def test_folder_collection_from_md_files(self):
119
with tempfile.TemporaryDirectory() as tmp:
120
blog = os.path.join(tmp, "blog")
121
os.makedirs(blog)
122
for i in range(3):
123
with open(os.path.join(blog, f"post{i}.md"), "w") as f:
124
f.write("---\ntitle: Post\n---\n")
125
collections = _build_collections(tmp)
126
self.assertEqual(len(collections), 1)
127
self.assertEqual(collections[0]["name"], "blog")
128
self.assertIn("folder", collections[0])
129
130
def test_file_collection_from_index_only(self):
131
with tempfile.TemporaryDirectory() as tmp:
132
about = os.path.join(tmp, "about")
133
os.makedirs(about)
134
with open(os.path.join(about, "_index.md"), "w") as f:
135
f.write("---\ntitle: About\n---\n")
136
collections = _build_collections(tmp)
137
self.assertEqual(len(collections), 1)
138
self.assertIn("files", collections[0])
139
140
def test_recursive_md_discovery(self):
141
with tempfile.TemporaryDirectory() as tmp:
142
deep = os.path.join(tmp, "blog", "2024")
143
os.makedirs(deep)
144
with open(os.path.join(deep, "post.md"), "w") as f:
145
f.write("---\ntitle: Deep Post\n---\n")
146
collections = _build_collections(tmp)
147
self.assertEqual(len(collections), 1)
148
self.assertEqual(collections[0]["name"], "blog")
149
150
def test_default_collection_when_empty(self):
151
with tempfile.TemporaryDirectory() as tmp:
152
collections = _build_collections(tmp)
153
self.assertEqual(len(collections), 1)
154
self.assertEqual(collections[0]["name"], "pages")
155
156
def test_nonexistent_dir_returns_default(self):
157
collections = _build_collections("/nonexistent/content")
158
self.assertEqual(len(collections), 1)
159
self.assertEqual(collections[0]["name"], "pages")
160
161
162
class TestDecapify(unittest.TestCase):
163
def test_creates_admin_files(self):
164
with tempfile.TemporaryDirectory() as tmp:
165
content = os.path.join(tmp, "content")
166
os.makedirs(content)
167
with open(os.path.join(content, "_index.md"), "w") as f:
168
f.write("---\ntitle: Home\n---\n")
169
result = decapify(tmp)
170
self.assertIn("complete", result.lower())
171
self.assertTrue(os.path.exists(os.path.join(tmp, "static", "admin", "index.html")))
172
self.assertTrue(os.path.exists(os.path.join(tmp, "static", "admin", "config.yml")))
173
174
def test_admin_index_escapes_name(self):
175
with tempfile.TemporaryDirectory() as tmp:
176
decapify(tmp, cms_name='<script>alert("xss")</script>')
177
with open(os.path.join(tmp, "static", "admin", "index.html")) as f:
178
html = f.read()
179
self.assertNotIn("<script>alert", html)
180
self.assertIn("&lt;script&gt;", html)
181
182
def test_invalid_color_falls_back(self):
183
with tempfile.TemporaryDirectory() as tmp:
184
decapify(tmp, cms_color="not-a-color")
185
with open(os.path.join(tmp, "static", "admin", "index.html")) as f:
186
html = f.read()
187
self.assertIn("#2e3748", html)
188
189
190
if __name__ == "__main__":
191
unittest.main()
192

Keyboard Shortcuts

Open search /
Next entry (timeline) j
Previous entry (timeline) k
Open focused entry Enter
Show this help ?
Toggle theme Top nav button