Project

General

Profile

Download (5.44 KB) Statistics
| Branch: | Tag: | Revision:

haketilo / test / unit / conftest.py @ 42fe4405

1
# SPDX-License-Identifier: GPL-3.0-or-later
2

    
3
"""
4
Common fixtures for Haketilo unit tests
5
"""
6

    
7
# This file is part of Haketilo.
8
#
9
# Copyright (C) 2021 Wojtek Kosior <koszko@koszko.org>
10
#
11
# This program is free software: you can redistribute it and/or modify
12
# it under the terms of the GNU General Public License as published by
13
# the Free Software Foundation, either version 3 of the License, or
14
# (at your option) any later version.
15
#
16
# This program is distributed in the hope that it will be useful,
17
# but WITHOUT ANY WARRANTY; without even the implied warranty of
18
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
19
# GNU General Public License for more details.
20
#
21
# You should have received a copy of the GNU General Public License
22
# along with this program.  If not, see <https://www.gnu.org/licenses/>.
23
#
24
# I, Wojtek Kosior, thereby promise not to sue for violation of this file's
25
# license. Although I request that you do not make use of this code in a
26
# proprietary program, I am not going to enforce this in court.
27

    
28
import pytest
29
from pathlib import Path
30
from selenium.webdriver.common.by import By
31
from selenium.webdriver.support.ui import WebDriverWait
32
from selenium.webdriver.support import expected_conditions as EC
33

    
34
from ..profiles           import firefox_safe_mode
35
from ..server             import do_an_internet
36
from ..extension_crafting import make_extension
37
from ..world_wide_library import start_serving_script, dump_scripts
38

    
39
@pytest.fixture(scope="package")
40
def proxy():
41
    httpd = do_an_internet()
42
    yield httpd
43
    httpd.shutdown()
44

    
45
@pytest.fixture(scope="package")
46
def _driver(proxy):
47
    with firefox_safe_mode() as driver:
48
        yield driver
49
        driver.quit()
50

    
51
def close_all_but_one_window(driver):
52
    while len(driver.window_handles) > 1:
53
        driver.switch_to.window(driver.window_handles[-1])
54
        driver.close()
55
    driver.switch_to.window(driver.window_handles[0])
56

    
57
@pytest.fixture()
58
def driver(_driver, request):
59
    nav_target = request.node.get_closest_marker('get_page')
60
    close_all_but_one_window(_driver)
61
    _driver.get(nav_target.args[0] if nav_target else 'about:blank')
62
    _driver.implicitly_wait(0)
63
    yield _driver
64

    
65
@pytest.fixture()
66
def webextension(driver, request):
67
    ext_data = request.node.get_closest_marker('ext_data')
68
    if ext_data is None:
69
        raise Exception('"webextension" fixture requires "ext_data" marker to be set')
70
    ext_data = ext_data.args[0].copy()
71

    
72
    navigate_to = ext_data.get('navigate_to')
73
    if navigate_to is not None:
74
        del ext_data['navigate_to']
75

    
76
    driver.get('https://gotmyowndoma.in/')
77
    ext_path = make_extension(Path(driver.firefox_profile.path), **ext_data)
78
    addon_id = driver.install_addon(str(ext_path), temporary=True)
79
    WebDriverWait(driver, 10).until(
80
        EC.url_matches('^moz-extension://.*')
81
    )
82

    
83
    if navigate_to is not None:
84
        testpage_url = driver.execute_script('return window.location.href;')
85
        driver.get(testpage_url.replace('testpage.html', navigate_to))
86

    
87
    yield
88

    
89
    close_all_but_one_window(driver)
90
    driver.get('https://gotmyowndoma.in/')
91
    driver.uninstall_addon(addon_id)
92
    ext_path.unlink()
93

    
94
script_injector_script = '''\
95
/*
96
 * Selenium by default executes scripts in some weird one-time context. We want
97
 * separately-loaded scripts to be able to access global variables defined
98
 * before, including those declared with `const` or `let`. To achieve that, we
99
 * run our scripts by injecting them into the page with a <script> tag that runs
100
 * javascript served by our proxy. We use custom properties of the `window`
101
 * object to communicate with injected code.
102
 */
103
const inject = async () => {
104
    delete window.haketilo_selenium_return_value;
105
    delete window.haketilo_selenium_exception;
106
    window.returnval = val => window.haketilo_selenium_return_value = val;
107

    
108
    const injectee = document.createElement('script');
109
    injectee.src = arguments[0];
110
    injectee.type = "application/javascript";
111
    injectee.async = true;
112
    const prom = new Promise(cb => injectee.onload = cb);
113

    
114
    window.arguments = arguments[1];
115
    document.body.append(injectee);
116

    
117
    await prom;
118

    
119
    /*
120
     * To ease debugging, we want this script to signal all exceptions from the
121
     * injectee.
122
     */
123
    if (window.haketilo_selenium_exception !== false)
124
        throw ['haketilo_selenium_error',
125
               'Error in injected script! Check your geckodriver.log and ./injected_scripts/!'];
126

    
127
    return window.haketilo_selenium_return_value;
128
}
129
return inject();
130
'''
131

    
132
def _execute_in_page_context(driver, script, args):
133
    script = script + '\n;\nwindow.haketilo_selenium_exception = false;'
134
    script_url = start_serving_script(script)
135

    
136
    try:
137
        result = driver.execute_script(script_injector_script, script_url, args)
138
        if type(result) is list and len(result) == 2 and \
139
           result[0] == 'haketilo_selenium_error':
140
            raise Exception(result[1])
141
        return result
142
    except Exception as e:
143
        dump_scripts()
144
        raise e from None
145

    
146
# Some fixtures here just define functions that operate on driver. We should
147
# consider making them into webdriver wrapper class methods.
148

    
149
@pytest.fixture()
150
def execute_in_page(driver):
151
    def do_execute(script, *args):
152
        return _execute_in_page_context(driver, script, args)
153

    
154
    yield do_execute
155

    
156
@pytest.fixture()
157
def wait_elem_text(driver):
158
    def do_wait(id, text):
159
        WebDriverWait(driver, 10).until(
160
            EC.text_to_be_present_in_element((By.ID, id), text)
161
        )
162

    
163
    yield do_wait
(2-2/24)