Revision fd9f2fc4
Added by koszko over 1 year ago
| Makefile.in | ||
|---|---|---|
| 17 | 17 |
|
| 18 | 18 |
version = <<VERSION>> |
| 19 | 19 |
extension_files = background/ common/ content/ html/ licenses/ \ |
| 20 |
copyright default_settings.json manifest.json
|
|
| 20 |
icons/ copyright default_settings.json manifest.json
|
|
| 21 | 21 |
|
| 22 |
metafiles = build.sh configure Makefile.in process_html_file.sh README.txt \ |
|
| 22 |
|
|
| 23 |
metafiles = build.sh configure Makefile.in compute_scripts.awk README.txt \ |
|
| 23 | 24 |
re-generate_icons.sh shell_utils.sh upload_amo.sh write_makefile.sh |
| 24 | 25 |
|
| 25 | 26 |
# Configuration gets included here by write_makefile.sh |
| ... | ... | |
| 47 | 48 |
unpacked: $(default_target)-unpacked Makefile |
| 48 | 49 |
all-unpacked: mozilla-unpacked chromium-unpacked |
| 49 | 50 |
%-unpacked: $(extension_files) icons/haketilo16.png |
| 50 |
$(srcdir)/build.sh $* $(srcdir) $(UPDATE_URL)
|
|
| 51 |
"$(srcdir)/build.sh" $* $(srcdir) $(UPDATE_URL)
|
|
| 51 | 52 |
|
| 52 | 53 |
install install-strip: $(default_target)-unpacked |
| 53 | 54 |
cp -R $(default_target)-unpacked \ |
| 54 |
"$(DESTDIR)/{6fe13369-88e9-440f-b837-5012fb3bedec}"
|
|
| 55 |
"$(DESTDIR)/{6fe13369-88e9-440f-b837-5012fb3bedec}"
|
|
| 55 | 56 |
|
| 56 | 57 |
uninstall: |
| 57 | 58 |
rm -r "$(DESTDIR)/{6fe13369-88e9-440f-b837-5012fb3bedec}"
|
| ... | ... | |
| 59 | 60 |
%-build.zip: %-unpacked Makefile |
| 60 | 61 |
cd $< && zip -q -r ../$@ * |
| 61 | 62 |
|
| 62 |
test/: |
|
| 63 |
mkdir $@ |
|
| 64 |
|
|
| 65 |
test/certs/: | test/ |
|
| 63 |
certs/: |
|
| 66 | 64 |
mkdir $@ |
| 67 | 65 |
|
| 68 |
test/certs/%.key: | test/certs/
|
|
| 66 |
certs/%.key: | certs/
|
|
| 69 | 67 |
openssl genrsa -out $@ 2048 |
| 70 | 68 |
|
| 71 |
test/certs/rootCA.pem: test/certs/rootCA.key
|
|
| 69 |
certs/rootCA.pem: certs/rootCA.key
|
|
| 72 | 70 |
openssl req -x509 -new -nodes -key $< -days 1024 -out $@ \ |
| 73 | 71 |
-subj "/CN=Haketilo Test" |
| 74 | 72 |
|
| 75 |
test: test/certs/rootCA.pem test/certs/site.key $(default_target)-build.zip
|
|
| 76 |
MOZ_HEADLESS=whatever $(PYTHON) -m pytest
|
|
| 73 |
pytest.ini: pytest.ini.in
|
|
| 74 |
sed "s|<<SRCDIR>>|$(srcdir)|" <$< > $@
|
|
| 77 | 75 |
|
| 78 |
test-environment: test/certs/rootCA.pem test/certs/site.key |
|
| 79 |
python3 -m test |
|
| 76 |
test: certs/rootCA.pem certs/site.key $(default_target)-build.zip \ |
|
| 77 |
pytest.ini |
|
| 78 |
PYTHONPYCACHEPREFIX=$$(pwd)/test__pycache__ MOZ_HEADLESS=whatever \ |
|
| 79 |
"$(PYTHON)" -m pytest |
|
| 80 | 80 |
|
| 81 |
test-environment-with-haketilo: test/certs/rootCA.pem test/certs/site.key \ |
|
| 81 |
test-environment: certs/rootCA.pem certs/site.key |
|
| 82 |
"$(PYTHON)" -m test |
|
| 83 |
|
|
| 84 |
test-environment-with-haketilo: certs/rootCA.pem certs/site.key \ |
|
| 82 | 85 |
$(default_target)-build.zip |
| 83 |
python3 -m test --load-haketilo
|
|
| 86 |
"$(PYTHON)" -m test --load-haketilo
|
|
| 84 | 87 |
|
| 85 | 88 |
# helper targets |
| 86 | 89 |
clean mostlyclean: |
| 87 | 90 |
rm -rf mozilla-unpacked chromium-unpacked haketilo-[1-9]* |
| 88 | 91 |
rm -f mozilla-build.zip chromium-build.zip exports_init.js |
| 89 |
rm -rf test/certs |
|
| 90 |
rm -rf $$(find . -name geckodriver.log) |
|
| 91 |
rm -rf $$(find . -type d -name __pycache__) |
|
| 92 |
rm -rf $$(find . -type d -name injected_scripts) |
|
| 92 |
rm -rf pytest.ini certs injected_scripts geckodriver.log |
|
| 93 |
rm -rf certs/ test__pycache__/ .pytest_cache/ |
|
| 93 | 94 |
|
| 94 | 95 |
distclean: clean |
| 95 | 96 |
rm -f Makefile config.status record.conf |
| ... | ... | |
| 99 | 100 |
@echo 'deletes files that may need special tools to rebuild.' |
| 100 | 101 |
rm -f "$(srcdir)"/icons/*.png |
| 101 | 102 |
|
| 102 |
dist: $(extension_files) $(metafiles) icons/haketilo16.png |
|
| 103 |
test -d haketilo-$(version) || mkdir haketilo-$(version) |
|
| 104 |
for file in $(extension_files) $(metafiles) icons/; do \ |
|
| 105 |
cp -R "$(srcdir)"/$$file haketilo-$(version); \ |
|
| 106 |
done |
|
| 107 |
tar cf haketilo-$(version).tar haketilo-$(version) |
|
| 108 |
gzip haketilo-$(version).tar |
|
| 109 |
|
|
| 110 | 103 |
# Files for constructing the makefile |
| 111 | 104 |
Makefile: config.status Makefile.in record.conf |
| 112 | 105 |
./config.status |
| ... | ... | |
| 115 | 108 |
cp "$(srcdir)"/write_makefile.sh config.status |
| 116 | 109 |
|
| 117 | 110 |
# Unused GNU-specified targets |
| 111 |
dist: |
|
| 118 | 112 |
install-html: |
| 119 | 113 |
install-dvi: |
| 120 | 114 |
install-pdf: |
| build.sh | ||
|---|---|---|
| 15 | 15 |
|
| 16 | 16 |
set -e |
| 17 | 17 |
|
| 18 |
. ./shell_utils.sh |
|
| 19 |
|
|
| 20 | 18 |
print_usage() {
|
| 21 | 19 |
printf 'usage: %s mozilla|chromium [source directory] [update url]\n' \ |
| 22 | 20 |
"$0" >&2 |
| pytest.ini | ||
|---|---|---|
| 1 |
#!/usr/bin/env pytest |
|
| 2 |
|
|
| 3 |
# This file is part of Haketilo |
|
| 4 |
# |
|
| 5 |
# Copyright (C) 2021, Wojtek Kosior |
|
| 6 |
# |
|
| 7 |
# This program is free software: you can redistribute it and/or modify |
|
| 8 |
# it under the terms of the CC0 1.0 Universal License as published by |
|
| 9 |
# the Creative Commons Corporation. |
|
| 10 |
# |
|
| 11 |
# This program is distributed in the hope that it will be useful, |
|
| 12 |
# but WITHOUT ANY WARRANTY; without even the implied warranty of |
|
| 13 |
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
|
| 14 |
# CC0 1.0 Universal License for more details. |
|
| 15 |
|
|
| 16 |
[pytest] |
|
| 17 |
markers = |
|
| 18 |
ext_data: define a custom testing extension for `webextension` fixture. |
|
| 19 |
get_page: define a url the `driver` fixture should navigate the browser to. |
|
| 20 |
second_driver: tell `driver` fixture to spawn a separate browser instance fr this test. |
|
| pytest.ini.in | ||
|---|---|---|
| 1 |
#!/usr/bin/env pytest |
|
| 2 |
|
|
| 3 |
# This file is part of Haketilo |
|
| 4 |
# |
|
| 5 |
# Copyright (C) 2021, 2022 Wojtek Kosior <koszko@koszko.org> |
|
| 6 |
# |
|
| 7 |
# This program is free software: you can redistribute it and/or modify |
|
| 8 |
# it under the terms of the CC0 1.0 Universal License as published by |
|
| 9 |
# the Creative Commons Corporation. |
|
| 10 |
# |
|
| 11 |
# This program is distributed in the hope that it will be useful, |
|
| 12 |
# but WITHOUT ANY WARRANTY; without even the implied warranty of |
|
| 13 |
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
|
| 14 |
# CC0 1.0 Universal License for more details. |
|
| 15 |
|
|
| 16 |
[pytest] |
|
| 17 |
markers = |
|
| 18 |
ext_data: define a custom testing extension for `webextension` fixture. |
|
| 19 |
get_page: define a url the `driver` fixture should navigate the browser to. |
|
| 20 |
second_driver: tell `driver` fixture to spawn a separate browser instance for this test. |
|
| 21 |
testpaths = |
|
| 22 |
<<SRCDIR>>/test/haketilo_test |
|
| test/__init__.py | ||
|---|---|---|
| 1 |
# SPDX-License-Identifier: CC0-1.0 |
|
| 2 |
# Copyright (C) 2021 Wojtek Kosior |
|
| test/__main__.py | ||
|---|---|---|
| 1 |
# SPDX-License-Identifier: AGPL-3.0-or-later |
|
| 2 |
|
|
| 3 |
""" |
|
| 4 |
Run a Firefox-type browser with WebDriver attached and Python console open |
|
| 5 |
""" |
|
| 6 |
|
|
| 7 |
# This file is part of Haketilo. |
|
| 8 |
# |
|
| 9 |
# Copyright (C) 2021 jahoti <jahoti@tilde.team> |
|
| 10 |
# Copyright (C) 2021 Wojtek Kosior <koszko@koszko.org> |
|
| 11 |
# |
|
| 12 |
# This program is free software: you can redistribute it and/or modify |
|
| 13 |
# it under the terms of the GNU Affero General Public License as |
|
| 14 |
# published by the Free Software Foundation, either version 3 of the |
|
| 15 |
# License, or (at your option) any later version. |
|
| 16 |
# |
|
| 17 |
# This program is distributed in the hope that it will be useful, |
|
| 18 |
# but WITHOUT ANY WARRANTY; without even the implied warranty of |
|
| 19 |
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
|
| 20 |
# GNU Affero General Public License for more details. |
|
| 21 |
# |
|
| 22 |
# You should have received a copy of the GNU Affero General Public License |
|
| 23 |
# along with this program. If not, see <https://www.gnu.org/licenses/>. |
|
| 24 |
# |
|
| 25 |
# |
|
| 26 |
# I, Wojtek Kosior, thereby promise not to sue for violation of this |
|
| 27 |
# file's license. Although I request that you do not make use of this code |
|
| 28 |
# in a proprietary program, I am not going to enforce this in court. |
|
| 29 |
|
|
| 30 |
import sys |
|
| 31 |
import time |
|
| 32 |
import code |
|
| 33 |
from rlcompleter import Completer |
|
| 34 |
import readline |
|
| 35 |
|
|
| 36 |
from .server import do_an_internet |
|
| 37 |
from .misc_constants import * |
|
| 38 |
from .profiles import firefox_safe_mode |
|
| 39 |
from .extension_crafting import get_extension_base_url |
|
| 40 |
|
|
| 41 |
def fail(msg, error_code): |
|
| 42 |
print('Error:', msg)
|
|
| 43 |
print('Usage:', sys.argv[0], '[--load-haketilo]', '[certificates_directory] [proxy_port]')
|
|
| 44 |
sys.exit(error_code) |
|
| 45 |
|
|
| 46 |
load_haketilo = False |
|
| 47 |
argv_idx = 1 |
|
| 48 |
if len(sys.argv) > argv_idx and sys.argv[argv_idx] == '--load-haketilo': |
|
| 49 |
load_haketilo = True |
|
| 50 |
argv_idx += 1 |
|
| 51 |
|
|
| 52 |
certdir = Path(sys.argv[argv_idx]).resolve() if len(sys.argv) > argv_idx \ |
|
| 53 |
else default_cert_dir |
|
| 54 |
|
|
| 55 |
if not certdir.is_dir(): |
|
| 56 |
fail('selected certificate directory does not exist.', 2)
|
|
| 57 |
|
|
| 58 |
argv_idx += 1 |
|
| 59 |
|
|
| 60 |
port = sys.argv[argv_idx] if len(sys.argv) > argv_idx \ |
|
| 61 |
else str(default_proxy_port) |
|
| 62 |
|
|
| 63 |
if not port.isnumeric(): |
|
| 64 |
fail('port must be an integer.', 3)
|
|
| 65 |
|
|
| 66 |
httpd = do_an_internet(certdir, int(port)) |
|
| 67 |
driver = firefox_safe_mode(proxy_port=int(port)) |
|
| 68 |
|
|
| 69 |
if load_haketilo: |
|
| 70 |
driver.install_addon(str(here.parent / 'mozilla-build.zip'), temporary=True) |
|
| 71 |
driver.get(get_extension_base_url(driver) + 'html/settings.html') |
|
| 72 |
|
|
| 73 |
print("You can now control the browser through 'driver' object")
|
|
| 74 |
|
|
| 75 |
# Here we enable readline-enhanced editing: |
|
| 76 |
# https://stackoverflow.com/questions/35115208/is-there-any-way-to-combine-readline-rlcompleter-and-interactiveconsole-in-pytho#answer-35116399 |
|
| 77 |
readline.parse_and_bind('tab: complete');
|
|
| 78 |
console_locals = globals() |
|
| 79 |
readline.set_completer(Completer(console_locals).complete) |
|
| 80 |
code.InteractiveConsole(locals=globals()).interact() |
|
| 81 |
|
|
| 82 |
driver.quit() |
|
| 83 |
httpd.shutdown() |
|
| test/conftest.py | ||
|---|---|---|
| 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 tempfile import TemporaryDirectory |
|
| 31 |
from selenium.webdriver.common.by import By |
|
| 32 |
from selenium.webdriver.support.ui import WebDriverWait |
|
| 33 |
from selenium.webdriver.support import expected_conditions as EC |
|
| 34 |
|
|
| 35 |
from .profiles import firefox_safe_mode |
|
| 36 |
from .server import do_an_internet |
|
| 37 |
from .extension_crafting import make_extension |
|
| 38 |
from .world_wide_library import start_serving_script, dump_scripts |
|
| 39 |
from .misc_constants import here |
|
| 40 |
|
|
| 41 |
@pytest.fixture(scope="session") |
|
| 42 |
def proxy(): |
|
| 43 |
httpd = do_an_internet() |
|
| 44 |
yield httpd |
|
| 45 |
httpd.shutdown() |
|
| 46 |
|
|
| 47 |
@pytest.fixture(scope="session") |
|
| 48 |
def _driver(proxy): |
|
| 49 |
with firefox_safe_mode() as driver: |
|
| 50 |
yield driver |
|
| 51 |
driver.quit() |
|
| 52 |
|
|
| 53 |
def close_all_but_one_window(driver): |
|
| 54 |
while len(driver.window_handles) > 1: |
|
| 55 |
driver.switch_to.window(driver.window_handles[-1]) |
|
| 56 |
driver.close() |
|
| 57 |
driver.switch_to.window(driver.window_handles[0]) |
|
| 58 |
|
|
| 59 |
@pytest.fixture() |
|
| 60 |
def driver(_driver, request): |
|
| 61 |
nav_target = request.node.get_closest_marker('get_page')
|
|
| 62 |
nav_target = nav_target.args[0] if nav_target else 'about:blank' |
|
| 63 |
|
|
| 64 |
second_driver = request.node.get_closest_marker('second_driver')
|
|
| 65 |
|
|
| 66 |
if second_driver: |
|
| 67 |
with firefox_safe_mode() as _driver: |
|
| 68 |
_driver.get(nav_target) |
|
| 69 |
yield _driver |
|
| 70 |
_driver.quit() |
|
| 71 |
else: |
|
| 72 |
close_all_but_one_window(_driver) |
|
| 73 |
_driver.get(nav_target) |
|
| 74 |
_driver.implicitly_wait(0) |
|
| 75 |
yield _driver |
|
| 76 |
|
|
| 77 |
@pytest.fixture() |
|
| 78 |
def webextension(driver, request): |
|
| 79 |
ext_data = request.node.get_closest_marker('ext_data')
|
|
| 80 |
if ext_data is None: |
|
| 81 |
raise Exception('"webextension" fixture requires "ext_data" marker to be set')
|
|
| 82 |
ext_data = ext_data.args[0].copy() |
|
| 83 |
|
|
| 84 |
navigate_to = ext_data.get('navigate_to')
|
|
| 85 |
if navigate_to is not None: |
|
| 86 |
del ext_data['navigate_to'] |
|
| 87 |
|
|
| 88 |
driver.get('https://gotmyowndoma.in/')
|
|
| 89 |
ext_path = make_extension(Path(driver.firefox_profile.path), **ext_data) |
|
| 90 |
addon_id = driver.install_addon(str(ext_path), temporary=True) |
|
| 91 |
get_url = lambda d: d.execute_script('return window.ext_page_url;')
|
|
| 92 |
ext_page_url = WebDriverWait(driver, 10).until(get_url) |
|
| 93 |
driver.get(ext_page_url) |
|
| 94 |
|
|
| 95 |
if navigate_to is not None: |
|
| 96 |
driver.get(driver.current_url.replace('testpage.html', navigate_to))
|
|
| 97 |
|
|
| 98 |
yield |
|
| 99 |
|
|
| 100 |
# Unloading an extension might cause its windows to vanish. Make sure |
|
| 101 |
# there's at least one window navigated to some other page before |
|
| 102 |
# uninstalling the addon. Otherwise, we could be left with a windowless |
|
| 103 |
# browser :c |
|
| 104 |
driver.switch_to.window(driver.window_handles[-1]) |
|
| 105 |
driver.get('https://gotmyowndoma.in/')
|
|
| 106 |
driver.uninstall_addon(addon_id) |
|
| 107 |
ext_path.unlink() |
|
| 108 |
|
|
| 109 |
@pytest.fixture() |
|
| 110 |
def haketilo(driver): |
|
| 111 |
addon_id = driver.install_addon(str(here.parent / 'mozilla-build.zip'), |
|
| 112 |
temporary=True) |
|
| 113 |
|
|
| 114 |
yield |
|
| 115 |
|
|
| 116 |
driver.uninstall_addon(addon_id) |
|
| 117 |
|
|
| 118 |
script_injector_script = '''\ |
|
| 119 |
/* |
|
| 120 |
* Selenium by default executes scripts in some weird one-time context. We want |
|
| 121 |
* separately-loaded scripts to be able to access global variables defined |
|
| 122 |
* before, including those declared with `const` or `let`. To achieve that, we |
|
| 123 |
* run our scripts by injecting them into the page with a <script> tag that runs |
|
| 124 |
* javascript served by our proxy. We use custom properties of the `window` |
|
| 125 |
* object to communicate with injected code. |
|
| 126 |
*/ |
|
| 127 |
const inject = async () => {
|
|
| 128 |
delete window.haketilo_selenium_return_value; |
|
| 129 |
delete window.haketilo_selenium_exception; |
|
| 130 |
window.returnval = val => window.haketilo_selenium_return_value = val; |
|
| 131 |
|
|
| 132 |
const injectee = document.createElement('script');
|
|
| 133 |
injectee.src = arguments[0]; |
|
| 134 |
injectee.type = "application/javascript"; |
|
| 135 |
injectee.async = true; |
|
| 136 |
const prom = new Promise(cb => injectee.onload = cb); |
|
| 137 |
|
|
| 138 |
window.arguments = arguments[1]; |
|
| 139 |
document.body.append(injectee); |
|
| 140 |
|
|
| 141 |
await prom; |
|
| 142 |
|
|
| 143 |
/* |
|
| 144 |
* To ease debugging, we want this script to signal all exceptions from the |
|
| 145 |
* injectee. |
|
| 146 |
*/ |
|
| 147 |
if (window.haketilo_selenium_exception !== false) |
|
| 148 |
throw ['haketilo_selenium_error', |
|
| 149 |
'Error in injected script! Check your geckodriver.log and ./injected_scripts/!']; |
|
| 150 |
|
|
| 151 |
return window.haketilo_selenium_return_value; |
|
| 152 |
} |
|
| 153 |
return inject(); |
|
| 154 |
''' |
|
| 155 |
|
|
| 156 |
def _execute_in_page_context(driver, script, args): |
|
| 157 |
script = script + '\n;\nwindow.haketilo_selenium_exception = false;' |
|
| 158 |
script_url = start_serving_script(script) |
|
| 159 |
|
|
| 160 |
try: |
|
| 161 |
result = driver.execute_script(script_injector_script, script_url, args) |
|
| 162 |
if type(result) is list and len(result) == 2 and \ |
|
| 163 |
result[0] == 'haketilo_selenium_error': |
|
| 164 |
raise Exception(result[1]) |
|
| 165 |
return result |
|
| 166 |
except Exception as e: |
|
| 167 |
dump_scripts() |
|
| 168 |
raise e from None |
|
| 169 |
|
|
| 170 |
# Some fixtures here just define functions that operate on driver. We should |
|
| 171 |
# consider making them into webdriver wrapper class methods. |
|
| 172 |
|
|
| 173 |
@pytest.fixture() |
|
| 174 |
def execute_in_page(driver): |
|
| 175 |
def do_execute(script, *args): |
|
| 176 |
return _execute_in_page_context(driver, script, args) |
|
| 177 |
|
|
| 178 |
yield do_execute |
|
| 179 |
|
|
| 180 |
@pytest.fixture() |
|
| 181 |
def wait_elem_text(driver): |
|
| 182 |
def do_wait(id, text): |
|
| 183 |
WebDriverWait(driver, 10).until( |
|
| 184 |
EC.text_to_be_present_in_element((By.ID, id), text) |
|
| 185 |
) |
|
| 186 |
|
|
| 187 |
yield do_wait |
|
| test/data/pages/gotmyowndomain.html | ||
|---|---|---|
| 1 |
<!DOCTYPE html> |
|
| 2 |
<!-- |
|
| 3 |
SPDX-License-Identifier: AGPL-3.0-or-later |
|
| 4 |
|
|
| 5 |
Sample testing page |
|
| 6 |
|
|
| 7 |
This file is part of Haketilo. |
|
| 8 |
|
|
| 9 |
Copyright (C) 2021 jahoti <jahoti@tilde.team> |
|
| 10 |
|
|
| 11 |
This program is free software: you can redistribute it and/or modify |
|
| 12 |
it under the terms of the GNU Affero General Public License as |
|
| 13 |
published by the Free Software Foundation, either version 3 of the |
|
| 14 |
License, or (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 Affero General Public License for more details. |
|
| 20 |
|
|
| 21 |
You should have received a copy of the GNU Affero General Public License |
|
| 22 |
along with this program. If not, see <https://www.gnu.org/licenses/>. |
|
| 23 |
--> |
|
| 24 |
<html> |
|
| 25 |
<head> |
|
| 26 |
<meta name=charset value="latin1"> |
|
| 27 |
<title>Schrodinger's Document</title> |
|
| 28 |
</head> |
|
| 29 |
<body> |
|
| 30 |
A nice, simple page for testing. |
|
| 31 |
<script> |
|
| 32 |
document.write('<p><b>Or so you thought...</b></p>');
|
|
| 33 |
</script> |
|
| 34 |
</body> |
|
| 35 |
</html> |
|
| test/data/pages/gotmyowndomain_https.html | ||
|---|---|---|
| 1 |
<!DOCTYPE html> |
|
| 2 |
<!-- |
|
| 3 |
SPDX-License-Identifier: AGPL-3.0-or-later |
|
| 4 |
|
|
| 5 |
Sample testing page to serve over HTTPS |
|
| 6 |
|
|
| 7 |
This file is part of Haketilo. |
|
| 8 |
|
|
| 9 |
Copyright (C) 2021 jahoti <jahoti@tilde.team> |
|
| 10 |
|
|
| 11 |
This program is free software: you can redistribute it and/or modify |
|
| 12 |
it under the terms of the GNU Affero General Public License as |
|
| 13 |
published by the Free Software Foundation, either version 3 of the |
|
| 14 |
License, or (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 Affero General Public License for more details. |
|
| 20 |
|
|
| 21 |
You should have received a copy of the GNU Affero General Public License |
|
| 22 |
along with this program. If not, see <https://www.gnu.org/licenses/>. |
|
| 23 |
--> |
|
| 24 |
<html> |
|
| 25 |
<head> |
|
| 26 |
<meta name="charset" value="latin1"> |
|
| 27 |
<title>Schrodinger's Document</title> |
|
| 28 |
</head> |
|
| 29 |
<body> |
|
| 30 |
A nice, simple page for testing (using HTTPS). |
|
| 31 |
<script> |
|
| 32 |
document.write('<p><b>Or so you thought...</b></p>');
|
|
| 33 |
</script> |
|
| 34 |
</body> |
|
| 35 |
</html> |
|
| test/data/pages/scripts_to_block_1.html | ||
|---|---|---|
| 1 |
<!DOCTYPE html> |
|
| 2 |
<!-- |
|
| 3 |
SPDX-License-Identifier: CC0-1.0 |
|
| 4 |
|
|
| 5 |
A testing page with various scripts that need to get blocked. |
|
| 6 |
|
|
| 7 |
This file is part of Haketilo. |
|
| 8 |
|
|
| 9 |
Copyright (C) 2022 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 CC0 1.0 Universal License as published by |
|
| 13 |
the Creative Commons Corporation. |
|
| 14 |
|
|
| 15 |
This program is distributed in the hope that it will be useful, |
|
| 16 |
but WITHOUT ANY WARRANTY; without even the implied warranty of |
|
| 17 |
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
|
| 18 |
CC0 1.0 Universal License for more details. |
|
| 19 |
--> |
|
| 20 |
<html> |
|
| 21 |
<head> |
|
| 22 |
<meta name="charset" value="latin1"> |
|
| 23 |
<script> |
|
| 24 |
window.__run = [...(window.__run || []), 'inline']; |
|
| 25 |
</script> |
|
| 26 |
<!-- the one below shall not execute even when blocking is off... --> |
|
| 27 |
<script type="application/json"> |
|
| 28 |
window.__run = [...(window.__run || []), 'json']; |
|
| 29 |
</script> |
|
| 30 |
</head> |
|
| 31 |
<body> |
|
| 32 |
<button id="clickme1" |
|
| 33 |
onclick="window.__run = [...(window.__run || []), 'on'];"> |
|
| 34 |
Click Meee! |
|
| 35 |
</button> |
|
| 36 |
<a id="clickme2" |
|
| 37 |
href="javascript:window.__run = [...(window.__run || []), 'href'];void(0);"> |
|
| 38 |
Click Meee! |
|
| 39 |
</a> |
|
| 40 |
<iframe src="javascript:void(window.parent.__run = [...(window.parent.__run || []), 'src']);"> |
|
| 41 |
</iframe> |
|
| 42 |
<object data="javascript:window.__run = [...(window.__run || []), 'data'];"> |
|
| 43 |
</object> |
|
| 44 |
</body> |
|
| 45 |
</html> |
|
| test/default_profiles/icecat_empty/extensions.json | ||
|---|---|---|
| 1 |
{"schemaVersion":25,"addons":[{"id":"jid1-KtlZuoiikVfFew@jetpack","location":"app-global","userDisabled":true,"path":"/usr/lib/icecat/browser/extensions/jid1-KtlZuoiikVfFew@jetpack"},{"id":"uBlock0@raymondhill.net","location":"app-global","userDisabled":true,"path":"/usr/lib/icecat/browser/extensions/uBlock0@raymondhill.net.xpi"},{"id":"SubmitMe@0xbeef.coffee","location":"app-global","userDisabled":true,"path":"/usr/lib/icecat/browser/extensions/SubmitMe@0xbeef.coffee"},{"id":"FreeUSPS@0xbeef.coffee","location":"app-global","userDisabled":true,"path":"/usr/lib/icecat/browser/extensions/FreeUSPS@0xbeef.coffee"},{"id":"tortm-browser-button@jeremybenthum","location":"app-global","userDisabled":true,"path":"/usr/lib/icecat/browser/extensions/tortm-browser-button@jeremybenthum"},{"id":"tprb.addon@searxes.danwin1210.me","location":"app-global","userDisabled":true,"path":"/usr/lib/icecat/browser/extensions/tprb.addon@searxes.danwin1210.me"},{"id":"SimpleSumOfUs@0xbeef.coffee","location":"app-global","userDisabled":true,"path":"/usr/lib/icecat/browser/extensions/SimpleSumOfUs@0xbeef.coffee"}]}
|
|
| test/extension_crafting.py | ||
|---|---|---|
| 1 |
# SPDX-License-Identifier: GPL-3.0-or-later |
|
| 2 |
|
|
| 3 |
""" |
|
| 4 |
Making temporary WebExtensions for use in the test suite |
|
| 5 |
""" |
|
| 6 |
|
|
| 7 |
# This file is part of Haketilo. |
|
| 8 |
# |
|
| 9 |
# Copyright (C) 2021, 2022 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 json |
|
| 29 |
import zipfile |
|
| 30 |
import re |
|
| 31 |
import shutil |
|
| 32 |
import subprocess |
|
| 33 |
|
|
| 34 |
from pathlib import Path |
|
| 35 |
from uuid import uuid4 |
|
| 36 |
from tempfile import TemporaryDirectory |
|
| 37 |
|
|
| 38 |
from selenium.webdriver.support.ui import WebDriverWait |
|
| 39 |
from selenium.common.exceptions import NoSuchElementException |
|
| 40 |
|
|
| 41 |
from .misc_constants import * |
|
| 42 |
|
|
| 43 |
class ManifestTemplateValueToFill: |
|
| 44 |
pass |
|
| 45 |
|
|
| 46 |
def manifest_template(): |
|
| 47 |
return {
|
|
| 48 |
'manifest_version': 2, |
|
| 49 |
'name': 'Haketilo test extension', |
|
| 50 |
'version': '1.0', |
|
| 51 |
'applications': {
|
|
| 52 |
'gecko': {
|
|
| 53 |
'id': ManifestTemplateValueToFill(), |
|
| 54 |
'strict_min_version': '60.0' |
|
| 55 |
} |
|
| 56 |
}, |
|
| 57 |
'permissions': [ |
|
| 58 |
'contextMenus', |
|
| 59 |
'webRequest', |
|
| 60 |
'webRequestBlocking', |
|
| 61 |
'activeTab', |
|
| 62 |
'notifications', |
|
| 63 |
'sessions', |
|
| 64 |
'storage', |
|
| 65 |
'tabs', |
|
| 66 |
'<all_urls>', |
|
| 67 |
'unlimitedStorage' |
|
| 68 |
], |
|
| 69 |
'content_security_policy': "object-src 'none'; script-src 'self' https://serve.scrip.ts;", |
|
| 70 |
'web_accessible_resources': ['testpage.html'], |
|
| 71 |
'options_ui': {
|
|
| 72 |
'page': 'testpage.html', |
|
| 73 |
'open_in_tab': True |
|
| 74 |
}, |
|
| 75 |
'background': {
|
|
| 76 |
'persistent': True, |
|
| 77 |
'scripts': ['__open_test_page.js', 'background.js'] |
|
| 78 |
}, |
|
| 79 |
'content_scripts': [ |
|
| 80 |
{
|
|
| 81 |
'run_at': 'document_start', |
|
| 82 |
'matches': ['<all_urls>'], |
|
| 83 |
'match_about_blank': True, |
|
| 84 |
'all_frames': True, |
|
| 85 |
'js': ['content.js'] |
|
| 86 |
} |
|
| 87 |
] |
|
| 88 |
} |
|
| 89 |
|
|
| 90 |
class ExtraHTML: |
|
| 91 |
def __init__(self, html_path, append={}, wrap_into_htmldoc=True):
|
|
| 92 |
self.html_path = html_path |
|
| 93 |
self.append = append |
|
| 94 |
self.wrap_into_htmldoc = wrap_into_htmldoc |
|
| 95 |
|
|
| 96 |
def add_to_xpi(self, xpi, tmpdir=None): |
|
| 97 |
if tmpdir is None: |
|
| 98 |
with TemporaryDirectory() as tmpdir: |
|
| 99 |
return self.add_to_xpi(xpi, tmpdir) |
|
| 100 |
|
|
| 101 |
append_flags = [] |
|
| 102 |
for filename, code in self.append.items(): |
|
| 103 |
append_flags.extend(['-A', f'{filename}:{code}'])
|
|
| 104 |
|
|
| 105 |
awk = subprocess.run( |
|
| 106 |
['awk', '-f', awk_script_name, '--', *unit_test_defines, |
|
| 107 |
*append_flags, '-H', self.html_path, '--write-js-deps', |
|
| 108 |
'--output=files-to-copy', f'--output-dir={tmpdir}'],
|
|
| 109 |
stdout=subprocess.PIPE, cwd=script_root, check=True |
|
| 110 |
) |
|
| 111 |
|
|
| 112 |
for path in filter(None, awk.stdout.decode().split('\n')):
|
|
| 113 |
xpi.write(script_root / path, path) |
|
| 114 |
|
|
| 115 |
tmpdir = Path(tmpdir) |
|
| 116 |
for path in tmpdir.rglob('*'):
|
|
| 117 |
relpath = str(path.relative_to(tmpdir)) |
|
| 118 |
if not path.is_dir() and relpath != self.html_path: |
|
| 119 |
xpi.write(path, relpath) |
|
| 120 |
|
|
| 121 |
with open(tmpdir / self.html_path, 'rt') as html_file: |
|
| 122 |
html = html_file.read() |
|
| 123 |
if self.wrap_into_htmldoc: |
|
| 124 |
html = f'<!DOCTYPE html><html><body>{html}</body></html>'
|
|
| 125 |
xpi.writestr(self.html_path, html) |
|
| 126 |
|
|
| 127 |
default_background_script = '' |
|
| 128 |
default_content_script = '' |
|
| 129 |
default_test_page = ''' |
|
| 130 |
<!DOCTYPE html> |
|
| 131 |
<html> |
|
| 132 |
<head> |
|
| 133 |
<title>Extension's options page for testing</title> |
|
| 134 |
</head> |
|
| 135 |
<body> |
|
| 136 |
<h1>Extension's options page for testing</h1> |
|
| 137 |
</body> |
|
| 138 |
</html> |
|
| 139 |
''' |
|
| 140 |
|
|
| 141 |
open_test_page_script = '''(() => {
|
|
| 142 |
const page_url = browser.runtime.getURL("testpage.html");
|
|
| 143 |
const execute_details = {
|
|
| 144 |
code: `window.wrappedJSObject.ext_page_url=${JSON.stringify(page_url)};`
|
|
| 145 |
}; |
|
| 146 |
browser.tabs.query({currentWindow: true, active: true})
|
|
| 147 |
.then(t => browser.tabs.executeScript(t.id, execute_details)); |
|
| 148 |
})();''' |
|
| 149 |
|
|
| 150 |
def make_extension(destination_dir, |
|
| 151 |
background_script=default_background_script, |
|
| 152 |
content_script=default_content_script, |
|
| 153 |
test_page=default_test_page, |
|
| 154 |
extra_files={}, extra_html=[]):
|
|
| 155 |
if not hasattr(extra_html, '__iter__'): |
|
| 156 |
extra_html = [extra_html] |
|
| 157 |
manifest = manifest_template() |
|
| 158 |
extension_id = '{%s}' % uuid4()
|
|
| 159 |
manifest['applications']['gecko']['id'] = extension_id |
|
| 160 |
files = {
|
|
| 161 |
'manifest.json' : json.dumps(manifest), |
|
| 162 |
'__open_test_page.js': open_test_page_script, |
|
| 163 |
'background.js' : background_script, |
|
| 164 |
'content.js' : content_script, |
|
| 165 |
'testpage.html' : test_page, |
|
| 166 |
**extra_files |
|
| 167 |
} |
|
| 168 |
destination_path = destination_dir / f'{extension_id}.xpi'
|
|
| 169 |
with zipfile.ZipFile(destination_path, 'x') as xpi: |
|
| 170 |
for filename, contents in files.items(): |
|
| 171 |
if hasattr(contents, '__call__'): |
|
| 172 |
contents = contents() |
|
| 173 |
xpi.writestr(filename, contents) |
|
| 174 |
for html in extra_html: |
|
| 175 |
html.add_to_xpi(xpi) |
|
| 176 |
|
|
| 177 |
return destination_path |
|
| 178 |
|
|
| 179 |
extract_base_url_re = re.compile(r'^(.*)manifest.json$') |
|
| 180 |
|
|
| 181 |
def get_extension_base_url(driver): |
|
| 182 |
""" |
|
| 183 |
Extension's internall UUID is not directly exposed in Selenium. Instead, we |
|
| 184 |
can navigate to about:debugging and inspect the manifest URL present there |
|
| 185 |
to get the base url like: |
|
| 186 |
moz-extension://b225c78f-d108-4caa-8406-f38b37d8dee5/ |
|
| 187 |
which can then be used to navigate to extension-bundled pages. |
|
| 188 |
""" |
|
| 189 |
# For newer Firefoxes |
|
| 190 |
driver.get('about:debugging#/runtime/this-firefox')
|
|
| 191 |
|
|
| 192 |
def get_manifest_link_newer_ff(driver): |
|
| 193 |
try: |
|
| 194 |
return driver.find_element_by_class_name('qa-manifest-url')
|
|
| 195 |
except NoSuchElementException: |
|
| 196 |
pass |
|
| 197 |
|
|
| 198 |
try: |
|
| 199 |
details = driver.find_element_by_class_name('error-page-details')
|
|
| 200 |
except NoSuchElementException: |
|
| 201 |
return False |
|
| 202 |
|
|
| 203 |
if '#/runtime/this-firefox' in details.text: |
|
| 204 |
return "not_newer_ff" |
|
| 205 |
|
|
| 206 |
manifest_link = WebDriverWait(driver, 10).until(get_manifest_link_newer_ff) |
|
| 207 |
|
|
| 208 |
if manifest_link == "not_newer_ff": |
|
| 209 |
driver.get("about:debugging#addons")
|
|
| 210 |
driver.implicitly_wait(10) |
|
| 211 |
manifest_link = driver.find_element_by_class_name('manifest-url')
|
|
| 212 |
driver.implicitly_wait(0) |
|
| 213 |
|
|
| 214 |
manifest_url = manifest_link.get_attribute('href')
|
|
| 215 |
return extract_base_url_re.match(manifest_url).group(1) |
|
| test/haketilo_test/__init__.py | ||
|---|---|---|
| 1 |
# SPDX-License-Identifier: CC0-1.0 |
|
| 2 |
# Copyright (C) 2021 Wojtek Kosior |
|
| test/haketilo_test/__main__.py | ||
|---|---|---|
| 1 |
# SPDX-License-Identifier: AGPL-3.0-or-later |
|
| 2 |
|
|
| 3 |
""" |
|
| 4 |
Run a Firefox-type browser with WebDriver attached and Python console open |
|
| 5 |
""" |
|
| 6 |
|
|
| 7 |
# This file is part of Haketilo. |
|
| 8 |
# |
|
| 9 |
# Copyright (C) 2021 jahoti <jahoti@tilde.team> |
|
| 10 |
# Copyright (C) 2021 Wojtek Kosior <koszko@koszko.org> |
|
| 11 |
# |
|
| 12 |
# This program is free software: you can redistribute it and/or modify |
|
| 13 |
# it under the terms of the GNU Affero General Public License as |
|
| 14 |
# published by the Free Software Foundation, either version 3 of the |
|
| 15 |
# License, or (at your option) any later version. |
|
| 16 |
# |
|
| 17 |
# This program is distributed in the hope that it will be useful, |
|
| 18 |
# but WITHOUT ANY WARRANTY; without even the implied warranty of |
|
| 19 |
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
|
| 20 |
# GNU Affero General Public License for more details. |
|
| 21 |
# |
|
| 22 |
# You should have received a copy of the GNU Affero General Public License |
|
| 23 |
# along with this program. If not, see <https://www.gnu.org/licenses/>. |
|
| 24 |
# |
|
| 25 |
# |
|
| 26 |
# I, Wojtek Kosior, thereby promise not to sue for violation of this |
|
| 27 |
# file's license. Although I request that you do not make use of this code |
|
| 28 |
# in a proprietary program, I am not going to enforce this in court. |
|
| 29 |
|
|
| 30 |
import sys |
|
| 31 |
import time |
|
| 32 |
import code |
|
| 33 |
from rlcompleter import Completer |
|
| 34 |
import readline |
|
| 35 |
|
|
| 36 |
from .server import do_an_internet |
|
| 37 |
from .misc_constants import * |
|
| 38 |
from .profiles import firefox_safe_mode |
|
| 39 |
from .extension_crafting import get_extension_base_url |
|
| 40 |
|
|
| 41 |
def fail(msg, error_code): |
|
| 42 |
print('Error:', msg)
|
|
| 43 |
print('Usage:', sys.argv[0], '[--load-haketilo]', '[certificates_directory] [proxy_port]')
|
|
| 44 |
sys.exit(error_code) |
|
| 45 |
|
|
| 46 |
load_haketilo = False |
|
| 47 |
argv_idx = 1 |
|
| 48 |
if len(sys.argv) > argv_idx and sys.argv[argv_idx] == '--load-haketilo': |
|
| 49 |
load_haketilo = True |
|
| 50 |
argv_idx += 1 |
|
| 51 |
|
|
| 52 |
certdir = Path(sys.argv[argv_idx]).resolve() if len(sys.argv) > argv_idx \ |
|
| 53 |
else default_cert_dir |
|
| 54 |
|
|
| 55 |
if not certdir.is_dir(): |
|
| 56 |
fail('selected certificate directory does not exist.', 2)
|
|
| 57 |
|
|
| 58 |
argv_idx += 1 |
|
| 59 |
|
|
| 60 |
port = sys.argv[argv_idx] if len(sys.argv) > argv_idx \ |
|
| 61 |
else str(default_proxy_port) |
|
| 62 |
|
|
| 63 |
if not port.isnumeric(): |
|
| 64 |
fail('port must be an integer.', 3)
|
|
| 65 |
|
|
| 66 |
httpd = do_an_internet(certdir, int(port)) |
|
| 67 |
driver = firefox_safe_mode(proxy_port=int(port)) |
|
| 68 |
|
|
| 69 |
if load_haketilo: |
|
| 70 |
driver.install_addon(str(here.parent / 'mozilla-build.zip'), temporary=True) |
|
| 71 |
driver.get(get_extension_base_url(driver) + 'html/settings.html') |
|
| 72 |
|
|
| 73 |
print("You can now control the browser through 'driver' object")
|
|
| 74 |
|
|
| 75 |
# Here we enable readline-enhanced editing: |
|
| 76 |
# https://stackoverflow.com/questions/35115208/is-there-any-way-to-combine-readline-rlcompleter-and-interactiveconsole-in-pytho#answer-35116399 |
|
| 77 |
readline.parse_and_bind('tab: complete');
|
|
| 78 |
console_locals = globals() |
|
| 79 |
readline.set_completer(Completer(console_locals).complete) |
|
| 80 |
code.InteractiveConsole(locals=globals()).interact() |
|
| 81 |
|
|
| 82 |
driver.quit() |
|
| 83 |
httpd.shutdown() |
|
| test/haketilo_test/conftest.py | ||
|---|---|---|
| 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 tempfile import TemporaryDirectory |
|
| 31 |
from selenium.webdriver.common.by import By |
|
| 32 |
from selenium.webdriver.support.ui import WebDriverWait |
|
| 33 |
from selenium.webdriver.support import expected_conditions as EC |
|
| 34 |
|
|
| 35 |
from .profiles import firefox_safe_mode |
|
| 36 |
from .server import do_an_internet |
|
| 37 |
from .extension_crafting import make_extension |
|
| 38 |
from .world_wide_library import start_serving_script, dump_scripts |
|
| 39 |
from .misc_constants import proj_root |
|
| 40 |
|
|
| 41 |
@pytest.fixture(scope="session") |
|
| 42 |
def proxy(): |
|
| 43 |
httpd = do_an_internet() |
|
| 44 |
yield httpd |
|
| 45 |
httpd.shutdown() |
|
| 46 |
|
|
| 47 |
@pytest.fixture(scope="session") |
|
| 48 |
def _driver(proxy): |
|
| 49 |
with firefox_safe_mode() as driver: |
|
| 50 |
yield driver |
|
| 51 |
driver.quit() |
|
| 52 |
|
|
| 53 |
def close_all_but_one_window(driver): |
|
| 54 |
while len(driver.window_handles) > 1: |
|
| 55 |
driver.switch_to.window(driver.window_handles[-1]) |
|
| 56 |
driver.close() |
|
| 57 |
driver.switch_to.window(driver.window_handles[0]) |
|
| 58 |
|
|
| 59 |
@pytest.fixture() |
|
| 60 |
def driver(_driver, request): |
|
| 61 |
nav_target = request.node.get_closest_marker('get_page')
|
|
| 62 |
nav_target = nav_target.args[0] if nav_target else 'about:blank' |
|
| 63 |
|
|
| 64 |
second_driver = request.node.get_closest_marker('second_driver')
|
|
| 65 |
|
|
| 66 |
if second_driver: |
|
| 67 |
with firefox_safe_mode() as _driver: |
|
| 68 |
_driver.get(nav_target) |
|
| 69 |
yield _driver |
|
| 70 |
_driver.quit() |
|
| 71 |
else: |
|
| 72 |
close_all_but_one_window(_driver) |
|
| 73 |
_driver.get(nav_target) |
|
| 74 |
_driver.implicitly_wait(0) |
|
| 75 |
yield _driver |
|
| 76 |
|
|
| 77 |
@pytest.fixture() |
|
| 78 |
def webextension(driver, request): |
|
| 79 |
ext_data = request.node.get_closest_marker('ext_data')
|
|
| 80 |
if ext_data is None: |
|
| 81 |
raise Exception('"webextension" fixture requires "ext_data" marker to be set')
|
|
| 82 |
ext_data = ext_data.args[0].copy() |
|
| 83 |
|
|
| 84 |
navigate_to = ext_data.get('navigate_to')
|
|
| 85 |
if navigate_to is not None: |
|
| 86 |
del ext_data['navigate_to'] |
|
| 87 |
|
|
| 88 |
driver.get('https://gotmyowndoma.in/')
|
|
| 89 |
ext_path = make_extension(Path(driver.firefox_profile.path), **ext_data) |
|
| 90 |
addon_id = driver.install_addon(str(ext_path), temporary=True) |
|
| 91 |
get_url = lambda d: d.execute_script('return window.ext_page_url;')
|
|
| 92 |
ext_page_url = WebDriverWait(driver, 10).until(get_url) |
|
| 93 |
driver.get(ext_page_url) |
|
| 94 |
|
|
| 95 |
if navigate_to is not None: |
|
| 96 |
driver.get(driver.current_url.replace('testpage.html', navigate_to))
|
|
| 97 |
|
|
| 98 |
yield |
|
| 99 |
|
|
| 100 |
# Unloading an extension might cause its windows to vanish. Make sure |
|
| 101 |
# there's at least one window navigated to some other page before |
|
| 102 |
# uninstalling the addon. Otherwise, we could be left with a windowless |
|
| 103 |
# browser :c |
|
| 104 |
driver.switch_to.window(driver.window_handles[-1]) |
|
| 105 |
driver.get('https://gotmyowndoma.in/')
|
|
| 106 |
driver.uninstall_addon(addon_id) |
|
| 107 |
ext_path.unlink() |
|
| 108 |
|
|
| 109 |
@pytest.fixture() |
|
| 110 |
def haketilo(driver): |
|
| 111 |
addon_id = driver.install_addon(str(Path.cwd() / 'mozilla-build.zip'), |
|
| 112 |
temporary=True) |
|
| 113 |
|
|
| 114 |
yield |
|
| 115 |
|
|
| 116 |
driver.uninstall_addon(addon_id) |
|
| 117 |
|
|
| 118 |
script_injector_script = '''\ |
|
| 119 |
/* |
|
| 120 |
* Selenium by default executes scripts in some weird one-time context. We want |
|
| 121 |
* separately-loaded scripts to be able to access global variables defined |
|
| 122 |
* before, including those declared with `const` or `let`. To achieve that, we |
|
| 123 |
* run our scripts by injecting them into the page with a <script> tag that runs |
|
| 124 |
* javascript served by our proxy. We use custom properties of the `window` |
|
| 125 |
* object to communicate with injected code. |
|
| 126 |
*/ |
|
| 127 |
const inject = async () => {
|
|
| 128 |
delete window.haketilo_selenium_return_value; |
|
| 129 |
delete window.haketilo_selenium_exception; |
|
| 130 |
window.returnval = val => window.haketilo_selenium_return_value = val; |
|
| 131 |
|
|
| 132 |
const injectee = document.createElement('script');
|
|
| 133 |
injectee.src = arguments[0]; |
|
| 134 |
injectee.type = "application/javascript"; |
|
| 135 |
injectee.async = true; |
|
| 136 |
const prom = new Promise(cb => injectee.onload = cb); |
|
| 137 |
|
|
| 138 |
window.arguments = arguments[1]; |
|
| 139 |
document.body.append(injectee); |
|
| 140 |
|
|
| 141 |
await prom; |
|
| 142 |
|
|
| 143 |
/* |
|
| 144 |
* To ease debugging, we want this script to signal all exceptions from the |
|
| 145 |
* injectee. |
|
| 146 |
*/ |
|
| 147 |
if (window.haketilo_selenium_exception !== false) |
|
| 148 |
throw ['haketilo_selenium_error', |
|
| 149 |
'Error in injected script! Check your geckodriver.log and ./injected_scripts/!']; |
|
| 150 |
|
|
| 151 |
return window.haketilo_selenium_return_value; |
|
| 152 |
} |
|
| 153 |
return inject(); |
|
| 154 |
''' |
|
| 155 |
|
|
| 156 |
def _execute_in_page_context(driver, script, args): |
|
| 157 |
script = script + '\n;\nwindow.haketilo_selenium_exception = false;' |
|
| 158 |
script_url = start_serving_script(script) |
|
| 159 |
|
|
| 160 |
try: |
|
| 161 |
result = driver.execute_script(script_injector_script, script_url, args) |
|
| 162 |
if type(result) is list and len(result) == 2 and \ |
|
| 163 |
result[0] == 'haketilo_selenium_error': |
|
| 164 |
raise Exception(result[1]) |
|
| 165 |
return result |
|
| 166 |
except Exception as e: |
|
| 167 |
dump_scripts() |
|
| 168 |
raise e from None |
|
| 169 |
|
|
| 170 |
# Some fixtures here just define functions that operate on driver. We should |
|
| 171 |
# consider making them into webdriver wrapper class methods. |
|
| 172 |
|
|
| 173 |
@pytest.fixture() |
|
| 174 |
def execute_in_page(driver): |
|
| 175 |
def do_execute(script, *args): |
|
| 176 |
return _execute_in_page_context(driver, script, args) |
|
| 177 |
|
|
| 178 |
yield do_execute |
|
| 179 |
|
|
| 180 |
@pytest.fixture() |
|
| 181 |
def wait_elem_text(driver): |
|
| 182 |
def do_wait(id, text): |
|
| 183 |
WebDriverWait(driver, 10).until( |
|
| 184 |
EC.text_to_be_present_in_element((By.ID, id), text) |
|
| 185 |
) |
|
| 186 |
|
|
| 187 |
yield do_wait |
|
| test/haketilo_test/data/pages/gotmyowndomain.html | ||
|---|---|---|
| 1 |
<!DOCTYPE html> |
|
| 2 |
<!-- |
|
| 3 |
SPDX-License-Identifier: AGPL-3.0-or-later |
|
| 4 |
|
|
| 5 |
Sample testing page |
|
| 6 |
|
|
| 7 |
This file is part of Haketilo. |
|
| 8 |
|
|
| 9 |
Copyright (C) 2021 jahoti <jahoti@tilde.team> |
|
| 10 |
|
|
| 11 |
This program is free software: you can redistribute it and/or modify |
|
| 12 |
it under the terms of the GNU Affero General Public License as |
|
| 13 |
published by the Free Software Foundation, either version 3 of the |
|
| 14 |
License, or (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 Affero General Public License for more details. |
|
| 20 |
|
|
| 21 |
You should have received a copy of the GNU Affero General Public License |
|
| 22 |
along with this program. If not, see <https://www.gnu.org/licenses/>. |
|
| 23 |
--> |
|
| 24 |
<html> |
|
| 25 |
<head> |
|
| 26 |
<meta name=charset value="latin1"> |
|
| 27 |
<title>Schrodinger's Document</title> |
|
| 28 |
</head> |
|
| 29 |
<body> |
|
| 30 |
A nice, simple page for testing. |
|
| 31 |
<script> |
|
| 32 |
document.write('<p><b>Or so you thought...</b></p>');
|
|
| 33 |
</script> |
|
| 34 |
</body> |
|
| 35 |
</html> |
|
| test/haketilo_test/data/pages/gotmyowndomain_https.html | ||
|---|---|---|
| 1 |
<!DOCTYPE html> |
|
| 2 |
<!-- |
|
| 3 |
SPDX-License-Identifier: AGPL-3.0-or-later |
|
| 4 |
|
|
| 5 |
Sample testing page to serve over HTTPS |
|
| 6 |
|
|
| 7 |
This file is part of Haketilo. |
|
| 8 |
|
|
| 9 |
Copyright (C) 2021 jahoti <jahoti@tilde.team> |
|
| 10 |
|
|
| 11 |
This program is free software: you can redistribute it and/or modify |
|
| 12 |
it under the terms of the GNU Affero General Public License as |
|
| 13 |
published by the Free Software Foundation, either version 3 of the |
|
| 14 |
License, or (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 Affero General Public License for more details. |
|
| 20 |
|
|
| 21 |
You should have received a copy of the GNU Affero General Public License |
|
| 22 |
along with this program. If not, see <https://www.gnu.org/licenses/>. |
|
| 23 |
--> |
|
| 24 |
<html> |
|
| 25 |
<head> |
|
| 26 |
<meta name="charset" value="latin1"> |
|
| 27 |
<title>Schrodinger's Document</title> |
|
| 28 |
</head> |
|
| 29 |
<body> |
|
| 30 |
A nice, simple page for testing (using HTTPS). |
|
| 31 |
<script> |
|
| 32 |
document.write('<p><b>Or so you thought...</b></p>');
|
|
| 33 |
</script> |
|
| 34 |
</body> |
|
| 35 |
</html> |
|
| test/haketilo_test/data/pages/scripts_to_block_1.html | ||
|---|---|---|
| 1 |
<!DOCTYPE html> |
|
| 2 |
<!-- |
|
| 3 |
SPDX-License-Identifier: CC0-1.0 |
|
| 4 |
|
|
| 5 |
A testing page with various scripts that need to get blocked. |
|
| 6 |
|
|
| 7 |
This file is part of Haketilo. |
|
| 8 |
|
|
| 9 |
Copyright (C) 2022 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 CC0 1.0 Universal License as published by |
|
| 13 |
the Creative Commons Corporation. |
|
| 14 |
|
|
| 15 |
This program is distributed in the hope that it will be useful, |
|
| 16 |
but WITHOUT ANY WARRANTY; without even the implied warranty of |
|
| 17 |
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
|
| 18 |
CC0 1.0 Universal License for more details. |
|
| 19 |
--> |
|
| 20 |
<html> |
|
| 21 |
<head> |
|
| 22 |
<meta name="charset" value="latin1"> |
|
| 23 |
<script> |
|
| 24 |
window.__run = [...(window.__run || []), 'inline']; |
|
| 25 |
</script> |
|
| 26 |
<!-- the one below shall not execute even when blocking is off... --> |
|
| 27 |
<script type="application/json"> |
|
| 28 |
window.__run = [...(window.__run || []), 'json']; |
|
| 29 |
</script> |
|
| 30 |
</head> |
|
| 31 |
<body> |
|
| 32 |
<button id="clickme1" |
|
| 33 |
onclick="window.__run = [...(window.__run || []), 'on'];"> |
|
| 34 |
Click Meee! |
|
| 35 |
</button> |
|
| 36 |
<a id="clickme2" |
|
| 37 |
href="javascript:window.__run = [...(window.__run || []), 'href'];void(0);"> |
|
| 38 |
Click Meee! |
|
| 39 |
</a> |
|
| 40 |
<iframe src="javascript:void(window.parent.__run = [...(window.parent.__run || []), 'src']);"> |
|
| 41 |
</iframe> |
|
| 42 |
<object data="javascript:window.__run = [...(window.__run || []), 'data'];"> |
|
| 43 |
</object> |
|
| 44 |
</body> |
|
| 45 |
</html> |
|
| test/haketilo_test/default_profiles/icecat_empty/extensions.json | ||
|---|---|---|
| 1 |
{"schemaVersion":25,"addons":[{"id":"jid1-KtlZuoiikVfFew@jetpack","location":"app-global","userDisabled":true,"path":"/usr/lib/icecat/browser/extensions/jid1-KtlZuoiikVfFew@jetpack"},{"id":"uBlock0@raymondhill.net","location":"app-global","userDisabled":true,"path":"/usr/lib/icecat/browser/extensions/uBlock0@raymondhill.net.xpi"},{"id":"SubmitMe@0xbeef.coffee","location":"app-global","userDisabled":true,"path":"/usr/lib/icecat/browser/extensions/SubmitMe@0xbeef.coffee"},{"id":"FreeUSPS@0xbeef.coffee","location":"app-global","userDisabled":true,"path":"/usr/lib/icecat/browser/extensions/FreeUSPS@0xbeef.coffee"},{"id":"tortm-browser-button@jeremybenthum","location":"app-global","userDisabled":true,"path":"/usr/lib/icecat/browser/extensions/tortm-browser-button@jeremybenthum"},{"id":"tprb.addon@searxes.danwin1210.me","location":"app-global","userDisabled":true,"path":"/usr/lib/icecat/browser/extensions/tprb.addon@searxes.danwin1210.me"},{"id":"SimpleSumOfUs@0xbeef.coffee","location":"app-global","userDisabled":true,"path":"/usr/lib/icecat/browser/extensions/SimpleSumOfUs@0xbeef.coffee"}]}
|
|
| test/haketilo_test/extension_crafting.py | ||
|---|---|---|
| 1 |
# SPDX-License-Identifier: GPL-3.0-or-later |
|
| 2 |
|
|
| 3 |
""" |
|
| 4 |
Making temporary WebExtensions for use in the test suite |
|
| 5 |
""" |
|
| 6 |
|
|
| 7 |
# This file is part of Haketilo. |
|
| 8 |
# |
|
| 9 |
# Copyright (C) 2021, 2022 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 json |
|
| 29 |
import zipfile |
|
| 30 |
import re |
|
| 31 |
import shutil |
|
| 32 |
import subprocess |
|
| 33 |
|
|
| 34 |
from pathlib import Path |
|
| 35 |
from uuid import uuid4 |
|
| 36 |
from tempfile import TemporaryDirectory |
|
| 37 |
|
|
| 38 |
from selenium.webdriver.support.ui import WebDriverWait |
|
| 39 |
from selenium.common.exceptions import NoSuchElementException |
|
| 40 |
|
|
| 41 |
from .misc_constants import * |
|
| 42 |
|
|
| 43 |
class ManifestTemplateValueToFill: |
|
| 44 |
pass |
|
| 45 |
|
|
| 46 |
def manifest_template(): |
|
| 47 |
return {
|
|
| 48 |
'manifest_version': 2, |
|
| 49 |
'name': 'Haketilo test extension', |
|
| 50 |
'version': '1.0', |
|
| 51 |
'applications': {
|
|
| 52 |
'gecko': {
|
|
| 53 |
'id': ManifestTemplateValueToFill(), |
|
| 54 |
'strict_min_version': '60.0' |
|
| 55 |
} |
|
| 56 |
}, |
|
| 57 |
'permissions': [ |
|
| 58 |
'contextMenus', |
|
| 59 |
'webRequest', |
|
| 60 |
'webRequestBlocking', |
|
| 61 |
'activeTab', |
|
| 62 |
'notifications', |
|
| 63 |
'sessions', |
|
| 64 |
'storage', |
|
| 65 |
'tabs', |
|
| 66 |
'<all_urls>', |
|
| 67 |
'unlimitedStorage' |
|
| 68 |
], |
|
| 69 |
'content_security_policy': "object-src 'none'; script-src 'self' https://serve.scrip.ts;", |
|
| 70 |
'web_accessible_resources': ['testpage.html'], |
|
| 71 |
'options_ui': {
|
|
| 72 |
'page': 'testpage.html', |
|
| 73 |
'open_in_tab': True |
|
| 74 |
}, |
|
| 75 |
'background': {
|
|
| 76 |
'persistent': True, |
|
| 77 |
'scripts': ['__open_test_page.js', 'background.js'] |
|
| 78 |
}, |
|
| 79 |
'content_scripts': [ |
|
| 80 |
{
|
|
| 81 |
'run_at': 'document_start', |
|
| 82 |
'matches': ['<all_urls>'], |
|
| 83 |
'match_about_blank': True, |
|
| 84 |
'all_frames': True, |
|
| 85 |
'js': ['content.js'] |
|
| 86 |
} |
|
| 87 |
] |
|
| 88 |
} |
|
| 89 |
|
|
| 90 |
class ExtraHTML: |
|
| 91 |
def __init__(self, html_path, append={}, wrap_into_htmldoc=True):
|
|
| 92 |
self.html_path = html_path |
|
| 93 |
self.append = append |
|
| 94 |
self.wrap_into_htmldoc = wrap_into_htmldoc |
|
| 95 |
|
|
| 96 |
def add_to_xpi(self, xpi, tmpdir=None): |
|
| 97 |
if tmpdir is None: |
|
| 98 |
with TemporaryDirectory() as tmpdir: |
|
| 99 |
return self.add_to_xpi(xpi, tmpdir) |
|
| 100 |
|
|
| 101 |
append_flags = [] |
|
| 102 |
for filename, code in self.append.items(): |
|
| 103 |
append_flags.extend(['-A', f'{filename}:{code}'])
|
|
| 104 |
|
|
| 105 |
awk = subprocess.run( |
|
| 106 |
['awk', '-f', awk_script_name, '--', *unit_test_defines, |
|
| 107 |
*append_flags, '-H', self.html_path, '--write-js-deps', |
|
| 108 |
'--output=files-to-copy', f'--output-dir={tmpdir}'],
|
|
| 109 |
stdout=subprocess.PIPE, cwd=proj_root, check=True |
|
| 110 |
) |
|
| 111 |
|
|
| 112 |
for path in filter(None, awk.stdout.decode().split('\n')):
|
|
| 113 |
xpi.write(proj_root / path, path) |
|
| 114 |
|
|
| 115 |
tmpdir = Path(tmpdir) |
|
| 116 |
for path in tmpdir.rglob('*'):
|
|
| 117 |
relpath = str(path.relative_to(tmpdir)) |
|
| 118 |
if not path.is_dir() and relpath != self.html_path: |
|
| 119 |
xpi.write(path, relpath) |
|
| 120 |
|
|
| 121 |
with open(tmpdir / self.html_path, 'rt') as html_file: |
|
| 122 |
html = html_file.read() |
|
| 123 |
if self.wrap_into_htmldoc: |
|
| 124 |
html = f'<!DOCTYPE html><html><body>{html}</body></html>'
|
|
| 125 |
xpi.writestr(self.html_path, html) |
|
| 126 |
|
|
| 127 |
default_background_script = '' |
|
| 128 |
default_content_script = '' |
|
| 129 |
default_test_page = ''' |
|
| 130 |
<!DOCTYPE html> |
|
| 131 |
<html> |
|
| 132 |
<head> |
|
| 133 |
<title>Extension's options page for testing</title> |
|
| 134 |
</head> |
|
| 135 |
<body> |
|
| 136 |
<h1>Extension's options page for testing</h1> |
|
| 137 |
</body> |
|
| 138 |
</html> |
|
| 139 |
''' |
|
| 140 |
|
|
| 141 |
open_test_page_script = '''(() => {
|
|
| 142 |
const page_url = browser.runtime.getURL("testpage.html");
|
|
| 143 |
const execute_details = {
|
|
| 144 |
code: `window.wrappedJSObject.ext_page_url=${JSON.stringify(page_url)};`
|
|
| 145 |
}; |
|
| 146 |
browser.tabs.query({currentWindow: true, active: true})
|
|
| 147 |
.then(t => browser.tabs.executeScript(t.id, execute_details)); |
|
| 148 |
})();''' |
|
| 149 |
|
|
| 150 |
def make_extension(destination_dir, |
|
| 151 |
background_script=default_background_script, |
|
| 152 |
content_script=default_content_script, |
|
| 153 |
test_page=default_test_page, |
|
| 154 |
extra_files={}, extra_html=[]):
|
|
| 155 |
if not hasattr(extra_html, '__iter__'): |
|
| 156 |
extra_html = [extra_html] |
|
| 157 |
manifest = manifest_template() |
|
| 158 |
extension_id = '{%s}' % uuid4()
|
|
| 159 |
manifest['applications']['gecko']['id'] = extension_id |
|
| 160 |
files = {
|
|
| 161 |
'manifest.json' : json.dumps(manifest), |
|
| 162 |
'__open_test_page.js': open_test_page_script, |
|
| 163 |
'background.js' : background_script, |
|
| 164 |
'content.js' : content_script, |
|
| 165 |
'testpage.html' : test_page, |
|
| 166 |
**extra_files |
|
| 167 |
} |
|
| 168 |
destination_path = destination_dir / f'{extension_id}.xpi'
|
|
| 169 |
with zipfile.ZipFile(destination_path, 'x') as xpi: |
|
| 170 |
for filename, contents in files.items(): |
|
| 171 |
if hasattr(contents, '__call__'): |
|
| 172 |
contents = contents() |
|
| 173 |
xpi.writestr(filename, contents) |
|
| 174 |
for html in extra_html: |
|
| 175 |
html.add_to_xpi(xpi) |
|
| 176 |
|
|
| 177 |
return destination_path |
|
| 178 |
|
|
| 179 |
extract_base_url_re = re.compile(r'^(.*)manifest.json$') |
|
| 180 |
|
|
| 181 |
def get_extension_base_url(driver): |
|
| 182 |
""" |
|
| 183 |
Extension's internall UUID is not directly exposed in Selenium. Instead, we |
|
| 184 |
can navigate to about:debugging and inspect the manifest URL present there |
|
| 185 |
to get the base url like: |
|
| 186 |
moz-extension://b225c78f-d108-4caa-8406-f38b37d8dee5/ |
|
| 187 |
which can then be used to navigate to extension-bundled pages. |
|
| 188 |
""" |
|
| 189 |
# For newer Firefoxes |
|
| 190 |
driver.get('about:debugging#/runtime/this-firefox')
|
|
| 191 |
|
|
| 192 |
def get_manifest_link_newer_ff(driver): |
|
| 193 |
try: |
|
| 194 |
return driver.find_element_by_class_name('qa-manifest-url')
|
|
| 195 |
except NoSuchElementException: |
|
| 196 |
pass |
|
| 197 |
|
|
| 198 |
try: |
|
| 199 |
details = driver.find_element_by_class_name('error-page-details')
|
|
| 200 |
except NoSuchElementException: |
|
| 201 |
return False |
|
| 202 |
|
|
| 203 |
if '#/runtime/this-firefox' in details.text: |
|
| 204 |
return "not_newer_ff" |
|
| 205 |
|
|
| 206 |
manifest_link = WebDriverWait(driver, 10).until(get_manifest_link_newer_ff) |
|
| 207 |
|
|
| 208 |
if manifest_link == "not_newer_ff": |
|
| 209 |
driver.get("about:debugging#addons")
|
|
| 210 |
driver.implicitly_wait(10) |
|
| 211 |
manifest_link = driver.find_element_by_class_name('manifest-url')
|
|
| 212 |
driver.implicitly_wait(0) |
|
| 213 |
|
|
| 214 |
manifest_url = manifest_link.get_attribute('href')
|
|
| 215 |
return extract_base_url_re.match(manifest_url).group(1) |
|
| test/haketilo_test/misc_constants.py | ||
|---|---|---|
| 1 |
# SPDX-License-Identifier: AGPL-3.0-or-later |
|
| 2 |
|
|
| 3 |
""" |
|
| 4 |
Miscellaneous data that were found useful |
|
| 5 |
""" |
|
| 6 |
|
|
| 7 |
# This file is part of Haketilo. |
|
| 8 |
# |
|
| 9 |
# Copyright (C) 2021 jahoti <jahoti@tilde.team> |
|
| 10 |
# Copyright (C) 2021 Wojtek Kosior <koszko@koszko.org> |
|
Also available in: Unified diff
fix out-of-source builds