Coverage for app / backend / src / tests / test_templating.py: 93%
97 statements
« prev ^ index » next coverage.py v7.13.5, created at 2026-03-19 14:14 +0000
« prev ^ index » next coverage.py v7.13.5, created at 2026-03-19 14:14 +0000
1# Tests jinja template rendering
3from datetime import date
4from typing import Any
5from zoneinfo import ZoneInfo
7from markupsafe import Markup
9from couchers.i18n import LocalizationContext
10from couchers.i18n.i18next import I18Next
11from couchers.templating import Jinja2Template
14def _render_en_utc(template: Jinja2Template, args: dict[str, Any]) -> str:
15 return template.render(args, LocalizationContext.en_utc(), i18next=I18Next())
18def test_multiline() -> None:
19 template = Jinja2Template(source="{{ text|multiline }}", html=False)
20 rendered = _render_en_utc(template, {"text": "a\nb"})
21 assert rendered == "a\nb"
23 template = Jinja2Template(source="{{ text|multiline }}", html=True)
24 rendered = _render_en_utc(template, {"text": "a\nb"})
25 assert rendered == "a<br>b"
28def test_quotelines() -> None:
29 template = Jinja2Template(source="{{ text|quotelines }}", html=False)
30 rendered = _render_en_utc(template, {"text": "a\nb"})
31 assert rendered == "> a\n> b"
34def test_html_escaping() -> None:
35 template = Jinja2Template(source="Hello {{ name }}!", html=True)
36 rendered = _render_en_utc(template, {"name": "<script />"})
37 assert rendered == "Hello <script />!"
40def test_safe_html() -> None:
41 template = Jinja2Template(source="Hello {{ name|html }}!", html=True)
42 rendered = _render_en_utc(template, {"name": "<script />"})
43 assert rendered == "Hello <script />!"
46def test_date_formatting() -> None:
47 the_date = date(1970, 1, 1)
48 template = Jinja2Template(source="Date: {{ date }}", html=False)
49 rendered = _render_en_utc(template, {"date": the_date})
50 assert rendered == "Date: Jan 1, 1970"
53def _greeting_i18next(value: str) -> I18Next:
54 i18next = I18Next()
55 language = i18next.add_translation("en")
56 language.add_string("greeting", value)
57 return i18next
60def _i18next_from_dict(value: dict[str, dict[str, str]]) -> I18Next:
61 i18next = I18Next()
62 for lang_code, strings in value.items():
63 language = i18next.add_translation(lang_code)
64 language.load_json_dict(strings)
65 return i18next
68def test_translate_no_substitutions() -> None:
69 template = Jinja2Template(source='{{ "greeting"|translate }}', html=False)
70 i18next = _greeting_i18next("Hello!")
71 rendered = template.render({}, LocalizationContext.en_utc(), i18next)
72 assert rendered == "Hello!"
75def test_translate_multiple_languages() -> None:
76 template = Jinja2Template(source='{{ "greeting"|translate }}', html=False)
77 i18next = I18Next()
78 i18next.add_translation("en").add_string("greeting", "Hello!")
79 i18next.add_translation("fr").add_string("greeting", "Bonjour!")
80 fr_loc_context = LocalizationContext(locale="fr", timezone=ZoneInfo("Etc/UTC"))
81 rendered = template.render({}, fr_loc_context, i18next)
82 assert rendered == "Bonjour!"
85def test_translate_with_substitutions() -> None:
86 template = Jinja2Template(source='{{ "greeting"|translate(name=user_name) }}', html=False)
87 i18next = _greeting_i18next("Hello, {{name}}!")
88 rendered = template.render({"user_name": "Jack"}, LocalizationContext.en_utc(), i18next)
89 assert rendered == "Hello, Jack!"
92def test_translate_substitution_escaping() -> None:
93 template = Jinja2Template(source='{{ "greeting"|translate(name=name) }}', html=True)
94 i18next = _greeting_i18next("Hello, {{name}}!")
95 rendered = template.render({"name": "<script />"}, LocalizationContext.en_utc(), i18next)
96 assert rendered == "Hello, <script />!"
99def test_translate_substitution_safe_html() -> None:
100 template = Jinja2Template(source='{{ "greeting"|translate(name=name) }}', html=True)
101 i18next = _greeting_i18next("Hello, {{name}}!")
102 rendered = template.render({"name": Markup("<script />")}, LocalizationContext.en_utc(), i18next)
103 assert rendered == "Hello, <script />!"
106def test_translate_translation_tags() -> None:
107 template = Jinja2Template(source='{{ "greeting"|translate }}', html=True)
108 i18next = _greeting_i18next("<b>Hello!</b>")
109 rendered = template.render({}, LocalizationContext.en_utc(), i18next)
110 assert rendered == "<b>Hello!</b>"
113def test_translate_newlines_br() -> None:
114 template = Jinja2Template(source='{{ "greeting"|translate }}', html=True)
115 i18next = _greeting_i18next("Hello!\nWelcome!")
116 rendered = template.render({}, LocalizationContext.en_utc(), i18next)
117 assert rendered == "Hello!<br>Welcome!"
120def test_translate_plain_strip_tags() -> None:
121 template = Jinja2Template(source='{{ "greeting"|translate }}', html=False)
122 i18next = _greeting_i18next("<b>Hello!</b>")
123 rendered = template.render({}, LocalizationContext.en_utc(), i18next)
124 assert rendered == "Hello!"
127def test_translate_plain_strip_links() -> None:
128 template = Jinja2Template(source='{{ "greeting"|translate }}', html=False)
129 i18next = _greeting_i18next('<a href="https://example.com">Hello!</a>')
130 rendered = template.render({}, LocalizationContext.en_utc(), i18next)
131 assert rendered == "<https://example.com>"
134def test_translate_plain_strip_mailto() -> None:
135 template = Jinja2Template(source='{{ "greeting"|translate }}', html=False)
136 i18next = _greeting_i18next('<a href="mailto:me@example.com">Hello!</a>')
137 rendered = template.render({}, LocalizationContext.en_utc(), i18next)
138 assert rendered == "<me@example.com>"