import logging import pytest import werkzeug.serving from jinja2 import TemplateNotFound from markupsafe import Markup import flask def test_context_processing(app, client): @app.context_processor def context_processor(): return {"injected_value": 42} @app.route("/") def index(): return flask.render_template("context_template.html", value=23) rv = client.get("/") assert rv.data == b"
23|42" def test_original_win(app, client): @app.route("/") def index(): return flask.render_template_string("{{ config }}", config=42) rv = client.get("/") assert rv.data == b"42" def test_simple_stream(app, client): @app.route("/") def index(): return flask.stream_template_string("{{ config }}", config=42) rv = client.get("/") assert rv.data == b"42" def test_request_less_rendering(app, app_ctx): app.config["WORLD_NAME"] = "Special World" @app.context_processor def context_processor(): return dict(foo=42) rv = flask.render_template_string("Hello {{ config.WORLD_NAME }} {{ foo }}") assert rv == "Hello Special World 42" def test_standard_context(app, client): @app.route("/") def index(): flask.g.foo = 23 flask.session["test"] = "aha" return flask.render_template_string( """ {{ request.args.foo }} {{ g.foo }} {{ config.DEBUG }} {{ session.test }} """ ) rv = client.get("/?foo=42") assert rv.data.split() == [b"42", b"23", b"False", b"aha"] def test_escaping(app, client): text = "
Hello World!" @app.route("/") def index(): return flask.render_template( "escaping_template.html", text=text, html=Markup(text) ) lines = client.get("/").data.splitlines() assert lines == [ b"<p>Hello World!", b"
Hello World!", b"
Hello World!", b"
Hello World!", b"<p>Hello World!", b"
Hello World!", ] def test_no_escaping(app, client): text = "
Hello World!" @app.route("/") def index(): return flask.render_template( "non_escaping_template.txt", text=text, html=Markup(text) ) lines = client.get("/").data.splitlines() assert lines == [ b"
Hello World!", b"
Hello World!", b"
Hello World!", b"
Hello World!", b"<p>Hello World!", b"
Hello World!", b"
Hello World!", b"
Hello World!",
]
def test_escaping_without_template_filename(app, client, req_ctx):
assert flask.render_template_string("{{ foo }}", foo="Jameson
"
def test_templates_auto_reload(app):
# debug is False, config option is None
assert app.debug is False
assert app.config["TEMPLATES_AUTO_RELOAD"] is None
assert app.jinja_env.auto_reload is False
# debug is False, config option is False
app = flask.Flask(__name__)
app.config["TEMPLATES_AUTO_RELOAD"] = False
assert app.debug is False
assert app.jinja_env.auto_reload is False
# debug is False, config option is True
app = flask.Flask(__name__)
app.config["TEMPLATES_AUTO_RELOAD"] = True
assert app.debug is False
assert app.jinja_env.auto_reload is True
# debug is True, config option is None
app = flask.Flask(__name__)
app.config["DEBUG"] = True
assert app.config["TEMPLATES_AUTO_RELOAD"] is None
assert app.jinja_env.auto_reload is True
# debug is True, config option is False
app = flask.Flask(__name__)
app.config["DEBUG"] = True
app.config["TEMPLATES_AUTO_RELOAD"] = False
assert app.jinja_env.auto_reload is False
# debug is True, config option is True
app = flask.Flask(__name__)
app.config["DEBUG"] = True
app.config["TEMPLATES_AUTO_RELOAD"] = True
assert app.jinja_env.auto_reload is True
def test_templates_auto_reload_debug_run(app, monkeypatch):
def run_simple_mock(*args, **kwargs):
pass
monkeypatch.setattr(werkzeug.serving, "run_simple", run_simple_mock)
app.run()
assert not app.jinja_env.auto_reload
app.run(debug=True)
assert app.jinja_env.auto_reload
def test_template_loader_debugging(test_apps, monkeypatch):
from blueprintapp import app
called = []
class _TestHandler(logging.Handler):
def handle(self, record):
called.append(True)
text = str(record.msg)
assert "1: trying loader of application 'blueprintapp'" in text
assert (
"2: trying loader of blueprint 'admin' (blueprintapp.apps.admin)"
) in text
assert (
"trying loader of blueprint 'frontend' (blueprintapp.apps.frontend)"
) in text
assert "Error: the template could not be found" in text
assert (
"looked up from an endpoint that belongs to the blueprint 'frontend'"
) in text
assert "See https://flask.palletsprojects.com/blueprints/#templates" in text
with app.test_client() as c:
monkeypatch.setitem(app.config, "EXPLAIN_TEMPLATE_LOADING", True)
monkeypatch.setattr(
logging.getLogger("blueprintapp"), "handlers", [_TestHandler()]
)
with pytest.raises(TemplateNotFound) as excinfo:
c.get("/missing")
assert "missing_template.html" in str(excinfo.value)
assert len(called) == 1
def test_custom_jinja_env():
class CustomEnvironment(flask.templating.Environment):
pass
class CustomFlask(flask.Flask):
jinja_environment = CustomEnvironment
app = CustomFlask(__name__)
assert isinstance(app.jinja_env, CustomEnvironment)
def test_template_string_caching(app, app_ctx):
"""Test that render_template_string caches compiled templates."""
source = "Hello {{ name }}!"
cache = app.jinja_env._string_template_cache
# Cache should be empty initially
assert len(cache) == 0
# First render should add to cache
result1 = flask.render_template_string(source, name="World")
assert result1 == "Hello World!"
assert len(cache) == 1
assert source in cache
# Second render should use cached template
cached_template = cache[source]
result2 = flask.render_template_string(source, name="Flask")
assert result2 == "Hello Flask!"
assert len(cache) == 1
assert cache[source] is cached_template # Same object
def test_stream_template_string_caching(app, app_ctx):
"""Test that stream_template_string uses the same cache."""
source = "Hello {{ name }}!"
cache = app.jinja_env._string_template_cache
# Render with stream_template_string
result = "".join(flask.stream_template_string(source, name="World"))
assert result == "Hello World!"
assert source in cache
# Render same source with render_template_string should hit cache
cached_template = cache[source]
result2 = flask.render_template_string(source, name="Flask")
assert result2 == "Hello Flask!"
assert cache[source] is cached_template
def test_template_string_cache_per_app(app_ctx):
"""Test that each app has its own template string cache."""
app1 = flask.Flask(__name__)
app2 = flask.Flask(__name__)
source = "Hello {{ name }}!"
with app1.app_context():
flask.render_template_string(source, name="App1")
with app2.app_context():
flask.render_template_string(source, name="App2")
# Each app should have its own cache with the template
assert source in app1.jinja_env._string_template_cache
assert source in app2.jinja_env._string_template_cache
# But they should be different template objects (different environments)
assert (
app1.jinja_env._string_template_cache[source]
is not app2.jinja_env._string_template_cache[source]
)
def test_template_string_cache_lru_eviction(app, app_ctx):
"""Test that the cache evicts least recently used templates."""
# Set a small cache size for testing
app.jinja_env.string_template_cache_size = 3
cache = app.jinja_env._string_template_cache
# Fill the cache
flask.render_template_string("{{ a }}", a=1)
flask.render_template_string("{{ b }}", b=2)
flask.render_template_string("{{ c }}", c=3)
assert len(cache) == 3
assert list(cache.keys()) == ["{{ a }}", "{{ b }}", "{{ c }}"]
# Access the first one to make it recently used
flask.render_template_string("{{ a }}", a=1)
assert list(cache.keys()) == ["{{ b }}", "{{ c }}", "{{ a }}"]
# Add a new template, should evict "{{ b }}" (least recently used)
flask.render_template_string("{{ d }}", d=4)
assert len(cache) == 3
assert "{{ b }}" not in cache
assert list(cache.keys()) == ["{{ c }}", "{{ a }}", "{{ d }}"]
def test_template_string_cache_max_len(app, app_ctx):
"""Test that templates exceeding max length are not cached."""
# Set a small max length for testing
app.jinja_env.string_template_cache_max_len = 20
cache = app.jinja_env._string_template_cache
# Short template should be cached
short_source = "{{ x }}"
flask.render_template_string(short_source, x=1)
assert short_source in cache
# Long template should not be cached
long_source = "{{ x }}" + " " * 20 # exceeds max_len of 20
result = flask.render_template_string(long_source, x=2)
assert result == "2" + " " * 20
assert long_source not in cache