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

1# Tests jinja template rendering 

2 

3from datetime import date 

4from typing import Any 

5from zoneinfo import ZoneInfo 

6 

7from markupsafe import Markup 

8 

9from couchers.i18n import LocalizationContext 

10from couchers.i18n.i18next import I18Next 

11from couchers.templating import Jinja2Template 

12 

13 

14def _render_en_utc(template: Jinja2Template, args: dict[str, Any]) -> str: 

15 return template.render(args, LocalizationContext.en_utc(), i18next=I18Next()) 

16 

17 

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" 

22 

23 template = Jinja2Template(source="{{ text|multiline }}", html=True) 

24 rendered = _render_en_utc(template, {"text": "a\nb"}) 

25 assert rendered == "a<br>b" 

26 

27 

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" 

32 

33 

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 &lt;script /&gt;!" 

38 

39 

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 />!" 

44 

45 

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" 

51 

52 

53def _greeting_i18next(value: str) -> I18Next: 

54 i18next = I18Next() 

55 language = i18next.add_translation("en") 

56 language.add_string("greeting", value) 

57 return i18next 

58 

59 

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 

66 

67 

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!" 

73 

74 

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!" 

83 

84 

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!" 

90 

91 

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, &lt;script /&gt;!" 

97 

98 

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 />!" 

104 

105 

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>" 

111 

112 

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!" 

118 

119 

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!" 

125 

126 

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>" 

132 

133 

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>"