summaryrefslogtreecommitdiff
path: root/tests/unit/utils/test_javascript.py
blob: 9366c46fb84e90d134fc2eb4cc348110feecd798 (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
# vim: ft=python fileencoding=utf-8 sts=4 sw=4 et:

# Copyright 2016-2020 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 <http://www.gnu.org/licenses/>.

"""Tests for qutebrowser.utils.javascript."""

import dataclasses

import pytest
import hypothesis
import hypothesis.strategies

from qutebrowser.utils import javascript, usertypes


@dataclasses.dataclass
class Case:

    original: str
    replacement: str
    webkit_only: bool = False

    def __str__(self):
        return self.original


class TestStringEscape:

    TESTS = [
        Case('foo\\bar', r'foo\\bar'),
        Case('foo\nbar', r'foo\nbar'),
        Case('foo\rbar', r'foo\rbar'),
        Case("foo'bar", r"foo\'bar"),
        Case('foo"bar', r'foo\"bar'),
        Case('one\\two\rthree\nfour\'five"six', r'one\\two\rthree\nfour\'five\"six'),
        Case('\x00', r'\x00', webkit_only=True),
        Case('hellö', 'hellö'),
        Case('☃', '☃'),
        Case('\x80Ā', '\x80Ā'),
        Case('𐀀\x00𐀀\x00', r'𐀀\x00𐀀\x00', webkit_only=True),
        Case('𐀀\ufeff', r'𐀀\ufeff'),
        Case('\ufeff', r'\ufeff', webkit_only=True),
        # http://stackoverflow.com/questions/2965293/
        Case('\u2028', r'\u2028'),
        Case('\u2029', r'\u2029'),
    ]

    # Once there was this warning here:
    #   load glyph failed err=6 face=0x2680ba0, glyph=1912
    # http://qutebrowser.org:8010/builders/debian-jessie/builds/765/steps/unittests/
    # Should that be ignored?

    @pytest.mark.parametrize('case', TESTS, ids=str)
    def test_fake_escape(self, case):
        """Test javascript escaping with some expected outcomes."""
        assert javascript.string_escape(case.original) == case.replacement

    def _test_escape(self, text, web_tab, qtbot):
        """Test conversion by running JS in a tab."""
        escaped = javascript.string_escape(text)

        with qtbot.waitCallback() as cb:
            web_tab.run_js_async('"{}";'.format(escaped), cb)

        cb.assert_called_with(text)

    @pytest.mark.parametrize('case', TESTS, ids=str)
    def test_real_escape(self, web_tab, qtbot, case):
        """Test javascript escaping with a real QWebPage."""
        if web_tab.backend == usertypes.Backend.QtWebEngine and case.webkit_only:
            pytest.xfail("Not supported with QtWebEngine")
        self._test_escape(case.original, web_tab, qtbot)

    @pytest.mark.qt_log_ignore('^OpenType support missing for script')
    @hypothesis.given(hypothesis.strategies.text())
    def test_real_escape_hypothesis(self, web_tab, qtbot, text):
        """Test javascript escaping with a real QWebPage and hypothesis."""
        if web_tab.backend == usertypes.Backend.QtWebEngine:
            hypothesis.assume('\x00' not in text)
        self._test_escape(text, web_tab, qtbot)


@pytest.mark.parametrize('arg, expected', [
    ('foobar', '"foobar"'),
    ('foo\\bar', r'"foo\\bar"'),
    (42, '42'),
    (23.42, '23.42'),
    (False, 'false'),
    (None, 'undefined'),
    (object(), TypeError),
    (True, 'true'),
    ([23, True, 'x'], '[23, true, "x"]'),
])
def test_to_js(arg, expected):
    if expected is TypeError:
        with pytest.raises(TypeError):
            javascript.to_js(arg)
    else:
        assert javascript.to_js(arg) == expected


@pytest.mark.parametrize('base, expected_base', [
    ('window', 'window'),
    ('foo', 'window._qutebrowser.foo'),
])
def test_assemble(base, expected_base):
    expected = '"use strict";\n{}.func(23);'.format(expected_base)
    assert javascript.assemble(base, 'func', 23) == expected


def test_wrap_global():
    source = javascript.wrap_global('name',
                                    'console.log("foo");',
                                    'console.log("bar");')
    assert 'window._qutebrowser.initialized["name"]' in source
    assert 'console.log("foo");' in source
    assert 'console.log("bar");' in source