1import logging
2
3import pytest
4import werkzeug.serving
5from jinja2 import TemplateNotFound
6
7import flask
8
9
10def test_context_processing(app, client):
11    @app.context_processor
12    def context_processor():
13        return {"injected_value": 42}
14
15    @app.route("/")
16    def index():
17        return flask.render_template("context_template.html", value=23)
18
19    rv = client.get("/")
20    assert rv.data == b"<p>23|42"
21
22
23def test_original_win(app, client):
24    @app.route("/")
25    def index():
26        return flask.render_template_string("{{ config }}", config=42)
27
28    rv = client.get("/")
29    assert rv.data == b"42"
30
31
32def test_request_less_rendering(app, app_ctx):
33    app.config["WORLD_NAME"] = "Special World"
34
35    @app.context_processor
36    def context_processor():
37        return dict(foo=42)
38
39    rv = flask.render_template_string("Hello {{ config.WORLD_NAME }} {{ foo }}")
40    assert rv == "Hello Special World 42"
41
42
43def test_standard_context(app, client):
44    @app.route("/")
45    def index():
46        flask.g.foo = 23
47        flask.session["test"] = "aha"
48        return flask.render_template_string(
49            """
50            {{ request.args.foo }}
51            {{ g.foo }}
52            {{ config.DEBUG }}
53            {{ session.test }}
54        """
55        )
56
57    rv = client.get("/?foo=42")
58    assert rv.data.split() == [b"42", b"23", b"False", b"aha"]
59
60
61def test_escaping(app, client):
62    text = "<p>Hello World!"
63
64    @app.route("/")
65    def index():
66        return flask.render_template(
67            "escaping_template.html", text=text, html=flask.Markup(text)
68        )
69
70    lines = client.get("/").data.splitlines()
71    assert lines == [
72        b"&lt;p&gt;Hello World!",
73        b"<p>Hello World!",
74        b"<p>Hello World!",
75        b"<p>Hello World!",
76        b"&lt;p&gt;Hello World!",
77        b"<p>Hello World!",
78    ]
79
80
81def test_no_escaping(app, client):
82    text = "<p>Hello World!"
83
84    @app.route("/")
85    def index():
86        return flask.render_template(
87            "non_escaping_template.txt", text=text, html=flask.Markup(text)
88        )
89
90    lines = client.get("/").data.splitlines()
91    assert lines == [
92        b"<p>Hello World!",
93        b"<p>Hello World!",
94        b"<p>Hello World!",
95        b"<p>Hello World!",
96        b"&lt;p&gt;Hello World!",
97        b"<p>Hello World!",
98        b"<p>Hello World!",
99        b"<p>Hello World!",
100    ]
101
102
103def test_escaping_without_template_filename(app, client, req_ctx):
104    assert flask.render_template_string("{{ foo }}", foo="<test>") == "&lt;test&gt;"
105    assert flask.render_template("mail.txt", foo="<test>") == "<test> Mail"
106
107
108def test_macros(app, req_ctx):
109    macro = flask.get_template_attribute("_macro.html", "hello")
110    assert macro("World") == "Hello World!"
111
112
113def test_template_filter(app):
114    @app.template_filter()
115    def my_reverse(s):
116        return s[::-1]
117
118    assert "my_reverse" in app.jinja_env.filters.keys()
119    assert app.jinja_env.filters["my_reverse"] == my_reverse
120    assert app.jinja_env.filters["my_reverse"]("abcd") == "dcba"
121
122
123def test_add_template_filter(app):
124    def my_reverse(s):
125        return s[::-1]
126
127    app.add_template_filter(my_reverse)
128    assert "my_reverse" in app.jinja_env.filters.keys()
129    assert app.jinja_env.filters["my_reverse"] == my_reverse
130    assert app.jinja_env.filters["my_reverse"]("abcd") == "dcba"
131
132
133def test_template_filter_with_name(app):
134    @app.template_filter("strrev")
135    def my_reverse(s):
136        return s[::-1]
137
138    assert "strrev" in app.jinja_env.filters.keys()
139    assert app.jinja_env.filters["strrev"] == my_reverse
140    assert app.jinja_env.filters["strrev"]("abcd") == "dcba"
141
142
143def test_add_template_filter_with_name(app):
144    def my_reverse(s):
145        return s[::-1]
146
147    app.add_template_filter(my_reverse, "strrev")
148    assert "strrev" in app.jinja_env.filters.keys()
149    assert app.jinja_env.filters["strrev"] == my_reverse
150    assert app.jinja_env.filters["strrev"]("abcd") == "dcba"
151
152
153def test_template_filter_with_template(app, client):
154    @app.template_filter()
155    def super_reverse(s):
156        return s[::-1]
157
158    @app.route("/")
159    def index():
160        return flask.render_template("template_filter.html", value="abcd")
161
162    rv = client.get("/")
163    assert rv.data == b"dcba"
164
165
166def test_add_template_filter_with_template(app, client):
167    def super_reverse(s):
168        return s[::-1]
169
170    app.add_template_filter(super_reverse)
171
172    @app.route("/")
173    def index():
174        return flask.render_template("template_filter.html", value="abcd")
175
176    rv = client.get("/")
177    assert rv.data == b"dcba"
178
179
180def test_template_filter_with_name_and_template(app, client):
181    @app.template_filter("super_reverse")
182    def my_reverse(s):
183        return s[::-1]
184
185    @app.route("/")
186    def index():
187        return flask.render_template("template_filter.html", value="abcd")
188
189    rv = client.get("/")
190    assert rv.data == b"dcba"
191
192
193def test_add_template_filter_with_name_and_template(app, client):
194    def my_reverse(s):
195        return s[::-1]
196
197    app.add_template_filter(my_reverse, "super_reverse")
198
199    @app.route("/")
200    def index():
201        return flask.render_template("template_filter.html", value="abcd")
202
203    rv = client.get("/")
204    assert rv.data == b"dcba"
205
206
207def test_template_test(app):
208    @app.template_test()
209    def boolean(value):
210        return isinstance(value, bool)
211
212    assert "boolean" in app.jinja_env.tests.keys()
213    assert app.jinja_env.tests["boolean"] == boolean
214    assert app.jinja_env.tests["boolean"](False)
215
216
217def test_add_template_test(app):
218    def boolean(value):
219        return isinstance(value, bool)
220
221    app.add_template_test(boolean)
222    assert "boolean" in app.jinja_env.tests.keys()
223    assert app.jinja_env.tests["boolean"] == boolean
224    assert app.jinja_env.tests["boolean"](False)
225
226
227def test_template_test_with_name(app):
228    @app.template_test("boolean")
229    def is_boolean(value):
230        return isinstance(value, bool)
231
232    assert "boolean" in app.jinja_env.tests.keys()
233    assert app.jinja_env.tests["boolean"] == is_boolean
234    assert app.jinja_env.tests["boolean"](False)
235
236
237def test_add_template_test_with_name(app):
238    def is_boolean(value):
239        return isinstance(value, bool)
240
241    app.add_template_test(is_boolean, "boolean")
242    assert "boolean" in app.jinja_env.tests.keys()
243    assert app.jinja_env.tests["boolean"] == is_boolean
244    assert app.jinja_env.tests["boolean"](False)
245
246
247def test_template_test_with_template(app, client):
248    @app.template_test()
249    def boolean(value):
250        return isinstance(value, bool)
251
252    @app.route("/")
253    def index():
254        return flask.render_template("template_test.html", value=False)
255
256    rv = client.get("/")
257    assert b"Success!" in rv.data
258
259
260def test_add_template_test_with_template(app, client):
261    def boolean(value):
262        return isinstance(value, bool)
263
264    app.add_template_test(boolean)
265
266    @app.route("/")
267    def index():
268        return flask.render_template("template_test.html", value=False)
269
270    rv = client.get("/")
271    assert b"Success!" in rv.data
272
273
274def test_template_test_with_name_and_template(app, client):
275    @app.template_test("boolean")
276    def is_boolean(value):
277        return isinstance(value, bool)
278
279    @app.route("/")
280    def index():
281        return flask.render_template("template_test.html", value=False)
282
283    rv = client.get("/")
284    assert b"Success!" in rv.data
285
286
287def test_add_template_test_with_name_and_template(app, client):
288    def is_boolean(value):
289        return isinstance(value, bool)
290
291    app.add_template_test(is_boolean, "boolean")
292
293    @app.route("/")
294    def index():
295        return flask.render_template("template_test.html", value=False)
296
297    rv = client.get("/")
298    assert b"Success!" in rv.data
299
300
301def test_add_template_global(app, app_ctx):
302    @app.template_global()
303    def get_stuff():
304        return 42
305
306    assert "get_stuff" in app.jinja_env.globals.keys()
307    assert app.jinja_env.globals["get_stuff"] == get_stuff
308    assert app.jinja_env.globals["get_stuff"](), 42
309
310    rv = flask.render_template_string("{{ get_stuff() }}")
311    assert rv == "42"
312
313
314def test_custom_template_loader(client):
315    class MyFlask(flask.Flask):
316        def create_global_jinja_loader(self):
317            from jinja2 import DictLoader
318
319            return DictLoader({"index.html": "Hello Custom World!"})
320
321    app = MyFlask(__name__)
322
323    @app.route("/")
324    def index():
325        return flask.render_template("index.html")
326
327    c = app.test_client()
328    rv = c.get("/")
329    assert rv.data == b"Hello Custom World!"
330
331
332def test_iterable_loader(app, client):
333    @app.context_processor
334    def context_processor():
335        return {"whiskey": "Jameson"}
336
337    @app.route("/")
338    def index():
339        return flask.render_template(
340            [
341                "no_template.xml",  # should skip this one
342                "simple_template.html",  # should render this
343                "context_template.html",
344            ],
345            value=23,
346        )
347
348    rv = client.get("/")
349    assert rv.data == b"<h1>Jameson</h1>"
350
351
352def test_templates_auto_reload(app):
353    # debug is False, config option is None
354    assert app.debug is False
355    assert app.config["TEMPLATES_AUTO_RELOAD"] is None
356    assert app.jinja_env.auto_reload is False
357    # debug is False, config option is False
358    app = flask.Flask(__name__)
359    app.config["TEMPLATES_AUTO_RELOAD"] = False
360    assert app.debug is False
361    assert app.jinja_env.auto_reload is False
362    # debug is False, config option is True
363    app = flask.Flask(__name__)
364    app.config["TEMPLATES_AUTO_RELOAD"] = True
365    assert app.debug is False
366    assert app.jinja_env.auto_reload is True
367    # debug is True, config option is None
368    app = flask.Flask(__name__)
369    app.config["DEBUG"] = True
370    assert app.config["TEMPLATES_AUTO_RELOAD"] is None
371    assert app.jinja_env.auto_reload is True
372    # debug is True, config option is False
373    app = flask.Flask(__name__)
374    app.config["DEBUG"] = True
375    app.config["TEMPLATES_AUTO_RELOAD"] = False
376    assert app.jinja_env.auto_reload is False
377    # debug is True, config option is True
378    app = flask.Flask(__name__)
379    app.config["DEBUG"] = True
380    app.config["TEMPLATES_AUTO_RELOAD"] = True
381    assert app.jinja_env.auto_reload is True
382
383
384def test_templates_auto_reload_debug_run(app, monkeypatch):
385    def run_simple_mock(*args, **kwargs):
386        pass
387
388    monkeypatch.setattr(werkzeug.serving, "run_simple", run_simple_mock)
389
390    app.run()
391    assert not app.templates_auto_reload
392    assert not app.jinja_env.auto_reload
393
394    app.run(debug=True)
395    assert app.templates_auto_reload
396    assert app.jinja_env.auto_reload
397
398
399def test_template_loader_debugging(test_apps, monkeypatch):
400    from blueprintapp import app
401
402    called = []
403
404    class _TestHandler(logging.Handler):
405        def handle(self, record):
406            called.append(True)
407            text = str(record.msg)
408            assert "1: trying loader of application 'blueprintapp'" in text
409            assert (
410                "2: trying loader of blueprint 'admin' (blueprintapp.apps.admin)"
411            ) in text
412            assert (
413                "trying loader of blueprint 'frontend' (blueprintapp.apps.frontend)"
414            ) in text
415            assert "Error: the template could not be found" in text
416            assert (
417                "looked up from an endpoint that belongs to the blueprint 'frontend'"
418            ) in text
419            assert "See https://flask.palletsprojects.com/blueprints/#templates" in text
420
421    with app.test_client() as c:
422        monkeypatch.setitem(app.config, "EXPLAIN_TEMPLATE_LOADING", True)
423        monkeypatch.setattr(
424            logging.getLogger("blueprintapp"), "handlers", [_TestHandler()]
425        )
426
427        with pytest.raises(TemplateNotFound) as excinfo:
428            c.get("/missing")
429
430        assert "missing_template.html" in str(excinfo.value)
431
432    assert len(called) == 1
433
434
435def test_custom_jinja_env():
436    class CustomEnvironment(flask.templating.Environment):
437        pass
438
439    class CustomFlask(flask.Flask):
440        jinja_environment = CustomEnvironment
441
442    app = CustomFlask(__name__)
443    assert isinstance(app.jinja_env, CustomEnvironment)
444