diff options
Diffstat (limited to 'test/unit')
-rw-r--r-- | test/unit/conftest.py | 163 | ||||
-rw-r--r-- | test/unit/test_basic.py | 4 | ||||
-rw-r--r-- | test/unit/test_broadcast.py | 3 | ||||
-rw-r--r-- | test/unit/test_content.py | 8 | ||||
-rw-r--r-- | test/unit/test_indexeddb.py | 2 | ||||
-rw-r--r-- | test/unit/test_patterns_query_manager.py | 19 | ||||
-rw-r--r-- | test/unit/test_repo_query_cacher.py | 11 |
7 files changed, 31 insertions, 179 deletions
diff --git a/test/unit/conftest.py b/test/unit/conftest.py deleted file mode 100644 index a3064f1..0000000 --- a/test/unit/conftest.py +++ /dev/null @@ -1,163 +0,0 @@ -# SPDX-License-Identifier: GPL-3.0-or-later - -""" -Common fixtures for Haketilo unit tests -""" - -# This file is part of Haketilo. -# -# Copyright (C) 2021 Wojtek Kosior <koszko@koszko.org> -# -# This program 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. -# -# This program 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 this program. If not, see <https://www.gnu.org/licenses/>. -# -# I, Wojtek Kosior, thereby promise not to sue for violation of this file's -# license. Although I request that you do not make use of this code in a -# proprietary program, I am not going to enforce this in court. - -import pytest -from pathlib import Path -from selenium.webdriver.common.by import By -from selenium.webdriver.support.ui import WebDriverWait -from selenium.webdriver.support import expected_conditions as EC - -from ..profiles import firefox_safe_mode -from ..server import do_an_internet -from ..extension_crafting import make_extension -from ..world_wide_library import start_serving_script, dump_scripts - -@pytest.fixture(scope="package") -def proxy(): - httpd = do_an_internet() - yield httpd - httpd.shutdown() - -@pytest.fixture(scope="package") -def _driver(proxy): - with firefox_safe_mode() as driver: - yield driver - driver.quit() - -def close_all_but_one_window(driver): - while len(driver.window_handles) > 1: - driver.switch_to.window(driver.window_handles[-1]) - driver.close() - driver.switch_to.window(driver.window_handles[0]) - -@pytest.fixture() -def driver(_driver, request): - nav_target = request.node.get_closest_marker('get_page') - close_all_but_one_window(_driver) - _driver.get(nav_target.args[0] if nav_target else 'about:blank') - _driver.implicitly_wait(0) - yield _driver - -@pytest.fixture() -def webextension(driver, request): - ext_data = request.node.get_closest_marker('ext_data') - if ext_data is None: - raise Exception('"webextension" fixture requires "ext_data" marker to be set') - ext_data = ext_data.args[0].copy() - - navigate_to = ext_data.get('navigate_to') - if navigate_to is not None: - del ext_data['navigate_to'] - - driver.get('https://gotmyowndoma.in/') - ext_path = make_extension(Path(driver.firefox_profile.path), **ext_data) - addon_id = driver.install_addon(str(ext_path), temporary=True) - WebDriverWait(driver, 10).until( - EC.url_matches('^moz-extension://.*') - ) - - if navigate_to is not None: - testpage_url = driver.execute_script('return window.location.href;') - driver.get(testpage_url.replace('testpage.html', navigate_to)) - - yield - - close_all_but_one_window(driver) - driver.get('https://gotmyowndoma.in/') - driver.uninstall_addon(addon_id) - ext_path.unlink() - -script_injector_script = '''\ -/* - * Selenium by default executes scripts in some weird one-time context. We want - * separately-loaded scripts to be able to access global variables defined - * before, including those declared with `const` or `let`. To achieve that, we - * run our scripts by injecting them into the page with a <script> tag that runs - * javascript served by our proxy. We use custom properties of the `window` - * object to communicate with injected code. - */ -const inject = async () => { - delete window.haketilo_selenium_return_value; - delete window.haketilo_selenium_exception; - window.returnval = val => window.haketilo_selenium_return_value = val; - - const injectee = document.createElement('script'); - injectee.src = arguments[0]; - injectee.type = "application/javascript"; - injectee.async = true; - const prom = new Promise(cb => injectee.onload = cb); - - window.arguments = arguments[1]; - document.body.append(injectee); - - await prom; - - /* - * To ease debugging, we want this script to signal all exceptions from the - * injectee. - */ - if (window.haketilo_selenium_exception !== false) - throw ['haketilo_selenium_error', - 'Error in injected script! Check your geckodriver.log and ./injected_scripts/!']; - - return window.haketilo_selenium_return_value; -} -return inject(); -''' - -def _execute_in_page_context(driver, script, args): - script = script + '\n;\nwindow.haketilo_selenium_exception = false;' - script_url = start_serving_script(script) - - try: - result = driver.execute_script(script_injector_script, script_url, args) - if type(result) is list and len(result) == 2 and \ - result[0] == 'haketilo_selenium_error': - raise Exception(result[1]) - return result - except Exception as e: - dump_scripts() - raise e from None - -# Some fixtures here just define functions that operate on driver. We should -# consider making them into webdriver wrapper class methods. - -@pytest.fixture() -def execute_in_page(driver): - def do_execute(script, *args): - return _execute_in_page_context(driver, script, args) - - yield do_execute - -@pytest.fixture() -def wait_elem_text(driver): - def do_wait(id, text): - WebDriverWait(driver, 10).until( - EC.text_to_be_present_in_element((By.ID, id), text) - ) - - yield do_wait diff --git a/test/unit/test_basic.py b/test/unit/test_basic.py index 5f42f5d..6ec54cc 100644 --- a/test/unit/test_basic.py +++ b/test/unit/test_basic.py @@ -40,9 +40,9 @@ def test_script_loader(execute_in_page): A trivial test case that verifies Haketilo's .js files can be properly loaded into a test page together with their dependencies. """ - execute_in_page(load_script('common/stored_types.js')) + execute_in_page(load_script('common/indexeddb.js')) - assert execute_in_page('returnval(TYPE_PREFIX.VAR);') == '_' + assert 'mapping' in execute_in_page('returnval(stores.map(s => s[0]));') @pytest.mark.ext_data({}) @pytest.mark.usefixtures('webextension') diff --git a/test/unit/test_broadcast.py b/test/unit/test_broadcast.py index 7de6c80..7c2c051 100644 --- a/test/unit/test_broadcast.py +++ b/test/unit/test_broadcast.py @@ -18,6 +18,7 @@ Haketilo unit tests - message broadcasting # CC0 1.0 Universal License for more details. import pytest +from selenium.webdriver.support.ui import WebDriverWait from ..script_loader import load_script from .utils import broker_js @@ -55,8 +56,8 @@ def test_broadcast(driver, execute_in_page, wait_elem_text): window.open(window.location.href, "_blank"); window.open(window.location.href, "_blank"); ''') + WebDriverWait(driver, 10).until(lambda d: len(d.window_handles) == 3) windows = [*driver.window_handles] - assert len(windows) == 3 # Let's first test if a simple message can be successfully broadcasted driver.switch_to.window(windows[0]) diff --git a/test/unit/test_content.py b/test/unit/test_content.py index 35ab027..8220160 100644 --- a/test/unit/test_content.py +++ b/test/unit/test_content.py @@ -33,7 +33,7 @@ dynamic_script = \ '''; this.haketilo_secret = "abracadabra"; this.haketilo_pattern_tree = {}; - this.haketilo_defualt_allow = false; + this.haketilo_default_allow = false; if (this.haketilo_content_script_main) this.haketilo_content_script_main(); @@ -69,7 +69,7 @@ content_script = \ async function mock_payload_ok([type, res_id]) { if (type === "indexeddb_files") - return [1, 2].map(n => `window.haketilo_injected_${n} = ${n}${n};`); + return {files: [1, 2].map(n => `window.hak_injected_${n} = ${n};`)}; } if (/payload_error/.test(document.URL)) { @@ -162,7 +162,7 @@ def test_content_unprivileged_page(driver, execute_in_page, target1, target2): def vars_made_by_payload(driver): vars_values = driver.execute_script( - 'return [1, 2].map(n => window[`haketilo_injected_${n}`]);' + 'return [1, 2].map(n => window[`hak_injected_${n}`]);' ) if vars_values != [None, None]: return vars_values @@ -174,7 +174,7 @@ def test_content_unprivileged_page(driver, execute_in_page, target1, target2): } elif target2 == 'payload_ok': vars_values = WebDriverWait(driver, 10).until(vars_made_by_payload) - assert vars_values == [11, 22] + assert vars_values == [1, 2] @pytest.mark.ext_data({'content_script': content_script}) @pytest.mark.usefixtures('webextension') diff --git a/test/unit/test_indexeddb.py b/test/unit/test_indexeddb.py index b320cff..550b923 100644 --- a/test/unit/test_indexeddb.py +++ b/test/unit/test_indexeddb.py @@ -318,8 +318,8 @@ def test_haketilodb_track(driver, execute_in_page, wait_elem_text): # will be used to make changes to IndexedDB and window 0 to "track" those # changes. driver.execute_script('window.open(window.location.href, "_blank");') + WebDriverWait(driver, 10).until(lambda d: len(d.window_handles) == 2) windows = [*driver.window_handles] - assert len(windows) == 2 # Create elements that will have tracked data inserted under them. driver.switch_to.window(windows[0]) diff --git a/test/unit/test_patterns_query_manager.py b/test/unit/test_patterns_query_manager.py index c6ebb81..5daf3a0 100644 --- a/test/unit/test_patterns_query_manager.py +++ b/test/unit/test_patterns_query_manager.py @@ -150,14 +150,14 @@ def test_pqm_tree_building(driver, execute_in_page): all([m['identifier'] in last_script for m in sample_mappings])) execute_in_page( - ''' + '''{ const new_setting_val = {name: "default_allow", value: false}; settingchange({key: "default_allow", new_val: new_setting_val}); for (const mapping of arguments[0]) mappingchange({key: mapping.identifier, new_val: mapping}); for (const blocking of arguments[1]) blockingchange({key: blocking.pattern, new_val: blocking}); - ''', + }''', sample_mappings[2:], sample_blocking[2:]) WebDriverWait(driver, 10).until(condition_all_added) @@ -201,6 +201,19 @@ def test_pqm_tree_building(driver, execute_in_page): WebDriverWait(driver, 10).until(condition_all_removed) + def condition_default_allowed_again(driver): + content_script = execute_in_page('returnval(last_script);') + cs_values = get_content_script_values(driver, content_script) + return cs_values['haketilo_default_allow'] == True + + execute_in_page( + '''{ + const new_setting_val = {name: "default_allow", value: true}; + settingchange({key: "default_allow", new_val: new_setting_val}); + }''') + + WebDriverWait(driver, 10).until(condition_default_allowed_again) + content_js = ''' let already_run = false; this.haketilo_content_script_main = function() { @@ -229,8 +242,8 @@ def test_pqm_script_injection(driver, execute_in_page): # Let's open a normal page in a second window. Window 0 will be used to make # changed to IndexedDB and window 1 to test the working of content scripts. driver.execute_script('window.open("about:blank", "_blank");') + WebDriverWait(driver, 10).until(lambda d: len(d.window_handles) == 2) windows = [*driver.window_handles] - assert len(windows) == 2 def run_content_script(): driver.switch_to.window(windows[1]) diff --git a/test/unit/test_repo_query_cacher.py b/test/unit/test_repo_query_cacher.py index b1ce4c8..5fbc5cd 100644 --- a/test/unit/test_repo_query_cacher.py +++ b/test/unit/test_repo_query_cacher.py @@ -65,18 +65,19 @@ def run_content_script_in_new_window(driver, url): Open the provided url in a new tab, find its tab id and return it, with current window changed back to the initial one. """ - initial_handle = driver.current_window_handle - handles = driver.window_handles + handle0 = driver.current_window_handle + initial_handles = [*driver.window_handles] driver.execute_script('window.open(arguments[0], "_blank");', url) - WebDriverWait(driver, 10).until(lambda d: d.window_handles is not handles) - new_handle = [h for h in driver.window_handles if h not in handles][0] + window_added = lambda d: set(d.window_handles) != set(initial_handles) + WebDriverWait(driver, 10).until(window_added) + new_handle = [*set(driver.window_handles).difference(initial_handles)][0] driver.switch_to.window(new_handle) get_tab_id = lambda d: d.execute_script('return window.haketilo_tab;') tab_id = WebDriverWait(driver, 10).until(get_tab_id) - driver.switch_to.window(initial_handle) + driver.switch_to.window(handle0) return tab_id @pytest.mark.ext_data({ |