summaryrefslogtreecommitdiff
path: root/tests/unit/utils/test_jinja.py
blob: 5555560bf59ec33dffb3f51278511be22cceb16c (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
# Copyright 2014-2021 Florian Bruhin (The Compiler) <mail@qutebrowser.org>
# vim: ft=python fileencoding=utf-8 sts=4 sw=4 et:

#
# This file is part of qutebrowser.
#
# qutebrowser is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# qutebrowser is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with qutebrowser.  If not, see <https://www.gnu.org/licenses/>.

"""Tests for qutebrowser.utils.jinja."""

import os
import os.path
import logging

import jinja2.exceptions
import pytest
from PyQt5.QtCore import QUrl

from qutebrowser.utils import jinja
from qutebrowser.config import configexc


@pytest.fixture(autouse=True)
def patch_read_file(monkeypatch):
    """pytest fixture to patch utils.read_file."""
    def _read_file(path):
        """A read_file which returns a simple template if the path is right."""
        if path == os.path.join('html', 'test.html'):
            return """Hello {{var}}"""
        elif path == os.path.join('html', 'test2.html'):
            return """{{ resource_url('utils/testfile') }}"""
        elif path == os.path.join('html', 'test3.html'):
            return """{{ data_url('testfile.txt') }}"""
        elif path == os.path.join('html', 'undef.html'):
            return """{{ does_not_exist() }}"""
        elif path == os.path.join('html', 'attributeerror.html'):
            return """{{ obj.foobar }}"""
        else:
            raise OSError("Invalid path {}!".format(path))

    def _read_file_binary(path):
        if path == 'testfile.txt':
            return b'foo'
        else:
            raise OSError("Invalid path {}!".format(path))

    monkeypatch.setattr(jinja.utils, 'read_file', _read_file)
    monkeypatch.setattr(jinja.utils, 'read_file_binary', _read_file_binary)


def test_simple_template():
    """Test with a simple template."""
    data = jinja.render('test.html', var='World')
    assert data == "Hello World"


def test_resource_url():
    """Test resource_url() which can be used from templates."""
    data = jinja.render('test2.html')
    print(data)
    url = QUrl(data)
    assert url.isValid()
    assert url.toDisplayString() == 'qute://resource/utils/testfile'


def test_data_url():
    """Test data_url() which can be used from templates."""
    data = jinja.render('test3.html')
    print(data)
    url = QUrl(data)
    assert url.isValid()
    assert data == 'data:text/plain;base64,Zm9v'  # 'foo'


def test_not_found(caplog):
    """Test with a template which does not exist."""
    with caplog.at_level(logging.ERROR):
        data = jinja.render('does_not_exist.html')
    assert "The does_not_exist.html template could not be found!" in data

    assert caplog.messages[0].startswith("The does_not_exist.html template"
                                         " could not be loaded from")


def test_utf8():
    """Test rendering with a UTF8 template.

    This was an attempt to get a failing test case for #127 but it seems
    the issue is elsewhere.

    https://github.com/qutebrowser/qutebrowser/issues/127
    """
    data = jinja.render('test.html', var='\u2603')
    assert data == "Hello \u2603"


def test_undefined_function(caplog):
    """Make sure undefined attributes crash since we preload resources.."""
    with pytest.raises(jinja2.exceptions.UndefinedError):
        jinja.render('undef.html')


def test_attribute_error():
    """Make sure accessing an unknown attribute fails."""
    with pytest.raises(AttributeError):
        jinja.render('attributeerror.html', obj=object())


@pytest.mark.parametrize('escape', [True, False])
def test_autoescape(escape):
    if not escape:
        with jinja.environment.no_autoescape():
            template = jinja.environment.from_string("{{ v }}")
        assert template.render(v='<foo') == '<foo'

    template = jinja.environment.from_string("{{ v }}")
    assert template.render(v='<foo') == '&lt;foo'


@pytest.mark.parametrize('template, expected', [
    ('{{ func1(conf.aliases) }} {{ func2(conf.backend) }}',
     ['aliases', 'backend']),
    ('{{ conf.aliases["a"].propname }}', ['aliases']),
    ('{{ conf.auto_save.interval + conf.hints.min_chars }}',
     ['auto_save.interval', 'hints.min_chars']),
    ('{{ notconf.a.b.c }}', []),
])
def test_template_config_variables(template, expected, config_stub):
    assert jinja.template_config_variables(template) == frozenset(expected)


@pytest.mark.parametrize('template', [
    '{{ func1(conf.aaa) }}',
    '{{ conf.bbb["a"].propname }}',
    '{{ conf.ccc + 1 }}',
])
def test_template_config_variables_no_option(template, config_stub):
    with pytest.raises(configexc.NoOptionError):
        jinja.template_config_variables(template)