summaryrefslogtreecommitdiff
path: root/tests/unit/javascript/conftest.py
blob: 85d5ebe0a71fba219e463b91e8e040c08c2b0c3b (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
# vim: ft=python fileencoding=utf-8 sts=4 sw=4 et:

# Copyright 2015-2021 Florian Bruhin (The Compiler) <mail@qutebrowser.org>
#
# 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/>.

"""pytest conftest file for javascript tests."""

import os
import os.path

import pytest
import jinja2

from PyQt5.QtCore import QUrl

import qutebrowser
from qutebrowser.utils import usertypes


class JSTester:

    """Common subclass providing basic functionality for all JS testers.

    Attributes:
        tab: The tab object which is used.
        qtbot: The QtBot fixture from pytest-qt.
        _jinja_env: The jinja2 environment used to get templates.
    """

    def __init__(self, tab, qtbot, config_stub):
        self.tab = tab
        self.qtbot = qtbot
        loader = jinja2.FileSystemLoader(os.path.dirname(__file__))
        self._jinja_env = jinja2.Environment(loader=loader, autoescape=True)
        # Make sure error logging via JS fails tests
        config_stub.val.content.javascript.log = {
            'info': 'info',
            'error': 'error',
            'unknown': 'error',
            'warning': 'error'
        }

    def load(self, path, base_url=QUrl(), **kwargs):
        """Load and display the given jinja test data.

        Args:
            path: The path to the test file, relative to the javascript/
                  folder.
            base_url: The url to pass to set_html.
            **kwargs: Passed to jinja's template.render().
        """
        template = self._jinja_env.get_template(path)

        try:
            with self.qtbot.wait_signal(self.tab.load_finished,
                                       timeout=2000) as blocker:
                self.tab.set_html(template.render(**kwargs), base_url=base_url)
        except self.qtbot.TimeoutError:
            # Sometimes this fails for some odd reason on macOS, let's just try
            # again.
            print("Trying to load page again...")
            with self.qtbot.wait_signal(self.tab.load_finished,
                                       timeout=2000) as blocker:
                self.tab.set_html(template.render(**kwargs), base_url=base_url)

        assert blocker.args == [True]

    def load_file(self, path: str, force: bool = False):
        """Load a file from disk.

        Args:
            path: The string path from disk to load (relative to this file)
            force: Whether to force loading even if the file is invalid.
        """
        self.load_url(QUrl.fromLocalFile(
            os.path.join(os.path.dirname(__file__), path)), force)

    def load_url(self, url: QUrl, force: bool = False):
        """Load a given QUrl.

        Args:
            url: The QUrl to load.
            force: Whether to force loading even if the file is invalid.
        """
        with self.qtbot.wait_signal(self.tab.load_finished,
                                   timeout=2000) as blocker:
            self.tab.load_url(url)
        if not force:
            assert blocker.args == [True]

    def run_file(self, path: str, expected=None) -> None:
        """Run a javascript file.

        Args:
            path: The path to the JS file, relative to the qutebrowser package.
            expected: The value expected return from the javascript execution
        """
        base_path = os.path.dirname(os.path.abspath(qutebrowser.__file__))
        with open(os.path.join(base_path, path), 'r', encoding='utf-8') as f:
            source = f.read()
        self.run(source, expected)

    def run(self, source: str, expected=usertypes.UNSET, world=None) -> None:
        """Run the given javascript source.

        Args:
            source: The source to run as a string.
            expected: The value expected return from the javascript execution
            world: The scope the javascript will run in
        """
        with self.qtbot.wait_callback() as callback:
            self.tab.run_js_async(source, callback, world=world)

        if expected is not usertypes.UNSET:
            callback.assert_called_with(expected)


@pytest.fixture
def js_tester_webkit(webkit_tab, qtbot, config_stub):
    """Fixture to test javascript snippets in webkit."""
    return JSTester(webkit_tab, qtbot, config_stub)


@pytest.fixture
def js_tester_webengine(webengine_tab, qtbot, config_stub):
    """Fixture to test javascript snippets in webengine."""
    return JSTester(webengine_tab, qtbot, config_stub)


@pytest.fixture
def js_tester(web_tab, qtbot, config_stub):
    """Fixture to test javascript snippets with both backends."""
    return JSTester(web_tab, qtbot, config_stub)