Coverage for app / backend / src / tests / test_templating.py: 93%

96 statements  

« prev     ^ index     » next       coverage.py v7.13.5, created at 2026-05-05 09:44 +0000

1# Tests jinja template rendering 

2 

3from datetime import UTC, date 

4from typing import Any 

5 

6from markupsafe import Markup 

7 

8from couchers.i18n import LocalizationContext 

9from couchers.i18n.i18next import I18Next 

10from couchers.templating import Jinja2Template 

11 

12 

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

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

15 

16 

17def test_multiline() -> None: 

18 template = Jinja2Template(source="{{ text|multiline }}", html=False) 

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

20 assert rendered == "a\nb" 

21 

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

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

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

25 

26 

27def test_quotelines() -> None: 

28 template = Jinja2Template(source="{{ text|quotelines }}", html=False) 

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

30 assert rendered == "> a\n> b" 

31 

32 

33def test_html_escaping() -> None: 

34 template = Jinja2Template(source="Hello {{ name }}!", html=True) 

35 rendered = _render_en_utc(template, {"name": "<script />"}) 

36 assert rendered == "Hello &lt;script /&gt;!" 

37 

38 

39def test_safe_html() -> None: 

40 template = Jinja2Template(source="Hello {{ name|html }}!", html=True) 

41 rendered = _render_en_utc(template, {"name": "<script />"}) 

42 assert rendered == "Hello <script />!" 

43 

44 

45def test_date_formatting() -> None: 

46 the_date = date(1970, 1, 1) 

47 template = Jinja2Template(source="Date: {{ date }}", html=False) 

48 rendered = _render_en_utc(template, {"date": the_date}) 

49 assert rendered == "Date: Jan 1, 1970" 

50 

51 

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

53 i18next = I18Next() 

54 language = i18next.add_translation("en") 

55 language.add_string("greeting", value) 

56 return i18next 

57 

58 

59def _i18next_from_dict(value: dict[str, dict[str, str]]) -> I18Next: 

60 i18next = I18Next() 

61 for lang_code, strings in value.items(): 

62 language = i18next.add_translation(lang_code) 

63 language.load_json_dict(strings) 

64 return i18next 

65 

66 

67def test_translate_no_substitutions() -> None: 

68 template = Jinja2Template(source='{{ "greeting"|translate }}', html=False) 

69 i18next = _greeting_i18next("Hello!") 

70 rendered = template.render({}, LocalizationContext.en_utc(), i18next) 

71 assert rendered == "Hello!" 

72 

73 

74def test_translate_multiple_languages() -> None: 

75 template = Jinja2Template(source='{{ "greeting"|translate }}', html=False) 

76 i18next = I18Next() 

77 i18next.add_translation("en").add_string("greeting", "Hello!") 

78 i18next.add_translation("fr").add_string("greeting", "Bonjour!") 

79 fr_loc_context = LocalizationContext(locale="fr", timezone=UTC) 

80 rendered = template.render({}, fr_loc_context, i18next) 

81 assert rendered == "Bonjour!" 

82 

83 

84def test_translate_with_substitutions() -> None: 

85 template = Jinja2Template(source='{{ "greeting"|translate(name=user_name) }}', html=False) 

86 i18next = _greeting_i18next("Hello, {{name}}!") 

87 rendered = template.render({"user_name": "Jack"}, LocalizationContext.en_utc(), i18next) 

88 assert rendered == "Hello, Jack!" 

89 

90 

91def test_translate_substitution_escaping() -> None: 

92 template = Jinja2Template(source='{{ "greeting"|translate(name=name) }}', html=True) 

93 i18next = _greeting_i18next("Hello, {{name}}!") 

94 rendered = template.render({"name": "<script />"}, LocalizationContext.en_utc(), i18next) 

95 assert rendered == "Hello, &lt;script /&gt;!" 

96 

97 

98def test_translate_substitution_safe_html() -> None: 

99 template = Jinja2Template(source='{{ "greeting"|translate(name=name) }}', html=True) 

100 i18next = _greeting_i18next("Hello, {{name}}!") 

101 rendered = template.render({"name": Markup("<script />")}, LocalizationContext.en_utc(), i18next) 

102 assert rendered == "Hello, <script />!" 

103 

104 

105def test_translate_translation_tags() -> None: 

106 template = Jinja2Template(source='{{ "greeting"|translate }}', html=True) 

107 i18next = _greeting_i18next("<b>Hello!</b>") 

108 rendered = template.render({}, LocalizationContext.en_utc(), i18next) 

109 assert rendered == "<b>Hello!</b>" 

110 

111 

112def test_translate_newlines_br() -> None: 

113 template = Jinja2Template(source='{{ "greeting"|translate }}', html=True) 

114 i18next = _greeting_i18next("Hello!\nWelcome!") 

115 rendered = template.render({}, LocalizationContext.en_utc(), i18next) 

116 assert rendered == "Hello!<br>Welcome!" 

117 

118 

119def test_translate_plain_strip_tags() -> None: 

120 template = Jinja2Template(source='{{ "greeting"|translate }}', html=False) 

121 i18next = _greeting_i18next("<b>Hello!</b>") 

122 rendered = template.render({}, LocalizationContext.en_utc(), i18next) 

123 assert rendered == "Hello!" 

124 

125 

126def test_translate_plain_strip_links() -> None: 

127 template = Jinja2Template(source='{{ "greeting"|translate }}', html=False) 

128 i18next = _greeting_i18next('<a href="https://example.com">Hello!</a>') 

129 rendered = template.render({}, LocalizationContext.en_utc(), i18next) 

130 assert rendered == "<https://example.com>" 

131 

132 

133def test_translate_plain_strip_mailto() -> None: 

134 template = Jinja2Template(source='{{ "greeting"|translate }}', html=False) 

135 i18next = _greeting_i18next('<a href="mailto:me@example.com">Hello!</a>') 

136 rendered = template.render({}, LocalizationContext.en_utc(), i18next) 

137 assert rendered == "<me@example.com>"