Revision 93dd7360
Added by koszko almost 2 years ago
| common/patterns.js | ||
|---|---|---|
| 7 | 7 |
* Redistribution terms are gathered in the `copyright' file. |
| 8 | 8 |
*/ |
| 9 | 9 |
|
| 10 |
const MAX_URL_PATH_LEN = 12; |
|
| 11 |
const MAX_URL_PATH_CHARS = 255; |
|
| 12 |
const MAX_DOMAIN_LEN = 7; |
|
| 13 |
const MAX_DOMAIN_CHARS = 100; |
|
| 10 |
const MAX = {
|
|
| 11 |
URL_PATH_LEN: 12, |
|
| 12 |
URL_PATH_CHARS: 255, |
|
| 13 |
DOMAIN_LEN: 7, |
|
| 14 |
DOMAIN_CHARS: 100 |
|
| 15 |
}; |
|
| 14 | 16 |
|
| 15 | 17 |
const proto_regex = /^(\w+):\/\/(.*)$/; |
| 16 | 18 |
|
| 17 | 19 |
const user_re = "[^/?#@]+@" |
| 18 |
const domain_re = "[^/?#]+";
|
|
| 20 |
const domain_re = "[.a-zA-Z0-9-]+";
|
|
| 19 | 21 |
const path_re = "[^?#]*"; |
| 20 | 22 |
const query_re = "\\??[^#]*"; |
| 21 | 23 |
|
| ... | ... | |
| 25 | 27 |
|
| 26 | 28 |
const ftp_regex = new RegExp(`^(${user_re})?(${domain_re})(${path_re}).*`);
|
| 27 | 29 |
|
| 28 |
function deconstruct_url(url) |
|
| 30 |
function deconstruct_url(url, use_limits=true)
|
|
| 29 | 31 |
{
|
| 32 |
const max = MAX; |
|
| 33 |
if (!use_limits) {
|
|
| 34 |
for (key in MAX) |
|
| 35 |
max[key] = Infinity; |
|
| 36 |
} |
|
| 37 |
|
|
| 30 | 38 |
const proto_match = proto_regex.exec(url); |
| 31 | 39 |
if (proto_match === null) |
| 32 |
return undefined;
|
|
| 40 |
throw `bad url '${url}'`;
|
|
| 33 | 41 |
|
| 34 | 42 |
const deco = {proto: proto_match[1]};
|
| 35 | 43 |
|
| ... | ... | |
| 37 | 45 |
deco.path = file_regex.exec(proto_match[2])[1]; |
| 38 | 46 |
} else if (deco.proto === "ftp") {
|
| 39 | 47 |
[deco.domain, deco.path] = ftp_regex.exec(proto_match[2]).slice(2, 4); |
| 40 |
} else {
|
|
| 48 |
} else if (deco.proto === "http" || deco.proto === "https") {
|
|
| 41 | 49 |
const http_match = http_regex.exec(proto_match[2]); |
| 42 | 50 |
if (!http_match) |
| 43 | 51 |
return undefined; |
| 44 | 52 |
[deco.domain, deco.path, deco.query] = http_match.slice(1, 4); |
| 53 |
deco.domain = deco.domain.toLowerCase(); |
|
| 54 |
} else {
|
|
| 55 |
throw `unsupported protocol in url '${url}'`;
|
|
| 45 | 56 |
} |
| 46 | 57 |
|
| 47 |
const leading_dash = deco.path[0] === "/"; |
|
| 48 | 58 |
deco.trailing_dash = deco.path[deco.path.length - 1] === "/"; |
| 49 | 59 |
|
| 50 | 60 |
if (deco.domain) {
|
| 51 |
if (deco.domain.length > MAX_DOMAIN_CHARS) {
|
|
| 61 |
if (deco.domain.length > max.DOMAIN_CHARS) {
|
|
| 52 | 62 |
const idx = deco.domain.indexOf(".", deco.domain.length -
|
| 53 |
MAX_DOMAIN_CHARS);
|
|
| 63 |
max.DOMAIN_CHARS);
|
|
| 54 | 64 |
if (idx === -1) |
| 55 | 65 |
deco.domain = []; |
| 56 | 66 |
else |
| ... | ... | |
| 59 | 69 |
deco.domain_truncated = true; |
| 60 | 70 |
} |
| 61 | 71 |
|
| 62 |
if (deco.path.length > MAX_URL_PATH_CHARS) {
|
|
| 72 |
if (deco.path.length > max.URL_PATH_CHARS) {
|
|
| 63 | 73 |
deco.path = deco.path.substring(0, deco.path.lastIndexOf("/"));
|
| 64 | 74 |
deco.path_truncated = true; |
| 65 | 75 |
} |
| ... | ... | |
| 67 | 77 |
|
| 68 | 78 |
if (typeof deco.domain === "string") {
|
| 69 | 79 |
deco.domain = deco.domain.split(".");
|
| 70 |
if (deco.domain.splice(0, deco.domain.length - MAX_DOMAIN_LEN).length
|
|
| 80 |
if (deco.domain.splice(0, deco.domain.length - max.DOMAIN_LEN).length
|
|
| 71 | 81 |
> 0) |
| 72 | 82 |
deco.domain_truncated = true; |
| 73 | 83 |
} |
| 74 | 84 |
|
| 75 | 85 |
deco.path = deco.path.split("/").filter(s => s !== "");
|
| 76 |
if (deco.domain && deco.path.splice(MAX_URL_PATH_LEN).length > 0)
|
|
| 86 |
if (deco.domain && deco.path.splice(max.URL_PATH_LEN).length > 0)
|
|
| 77 | 87 |
deco.path_truncated = true; |
| 78 |
if (leading_dash || deco.path.length === 0) |
|
| 79 |
deco.path.unshift("");
|
|
| 80 | 88 |
|
| 81 | 89 |
return deco; |
| 82 | 90 |
} |
| ... | ... | |
| 98 | 106 |
|
| 99 | 107 |
function* each_path_pattern(deco) |
| 100 | 108 |
{
|
| 101 |
for (let slice = deco.path.length; slice > 0; slice--) {
|
|
| 102 |
const path_part = deco.path.slice(0, slice).join("/");
|
|
| 109 |
for (let slice = deco.path.length; slice >= 0; slice--) {
|
|
| 110 |
const path_part = ["", ...deco.path.slice(0, slice)].join("/");
|
|
| 103 | 111 |
const path_wildcards = []; |
| 104 | 112 |
if (slice === deco.path.length && !deco.path_truncated) {
|
| 105 | 113 |
if (deco.trailing_dash) |
| 106 | 114 |
yield path_part + "/"; |
| 107 |
yield path_part; |
|
| 115 |
if (slice > 0 || deco.proto !== "file") |
|
| 116 |
yield path_part; |
|
| 108 | 117 |
} |
| 109 | 118 |
if (slice === deco.path.length - 1 && !deco.path_truncated && |
| 110 | 119 |
deco.path[slice] !== "*") |
| ... | ... | |
| 137 | 146 |
/* |
| 138 | 147 |
* EXPORTS_START |
| 139 | 148 |
* EXPORT each_url_pattern |
| 149 |
* EXPORT deconstruct_url |
|
| 140 | 150 |
* EXPORTS_END |
| 141 | 151 |
*/ |
| compute_scripts.awk | ||
|---|---|---|
| 105 | 105 |
} |
| 106 | 106 |
} |
| 107 | 107 |
|
| 108 |
function wrap_file(filename) {
|
|
| 109 |
print "\"use strict\";\n\n({fun: (function() {\n"
|
|
| 108 |
function partially_wrap_file(filename) {
|
|
| 110 | 109 |
print_imports_code(filename) |
| 111 | 110 |
printf "\n\n" |
| 112 | 111 |
|
| ... | ... | |
| 114 | 113 |
|
| 115 | 114 |
printf "\n\n" |
| 116 | 115 |
print_exports_code(filename) |
| 116 |
} |
|
| 117 |
|
|
| 118 |
function wrap_file(filename) {
|
|
| 119 |
print "\"use strict\";\n\n({fun: (function() {\n"
|
|
| 120 |
|
|
| 121 |
partially_wrap_file(filename) |
|
| 122 |
|
|
| 117 | 123 |
print "\n})}).fun();" |
| 118 | 124 |
} |
| 119 | 125 |
|
| ... | ... | |
| 151 | 157 |
} |
| 152 | 158 |
|
| 153 | 159 |
function print_usage() {
|
| 154 |
printf "usage: %2 compute_scripts.awk script_dependencies|wrapped_code FILENAME[...]\n", |
|
| 160 |
printf "usage: %2 compute_scripts.awk script_dependencies|wrapped_code|partially_wrapped_code FILENAME[...]\n",
|
|
| 155 | 161 |
ARGV[0] > "/dev/stderr" |
| 156 | 162 |
exit 1 |
| 157 | 163 |
} |
| ... | ... | |
| 189 | 195 |
print("exports_init.js")
|
| 190 | 196 |
if (compute_dependencies(root_filename) > 0) |
| 191 | 197 |
exit 1 |
| 198 |
} else if (operation == "partially_wrapped_code") {
|
|
| 199 |
partially_wrap_file(root_filename) |
|
| 192 | 200 |
} else if (operation == "wrapped_code") {
|
| 193 | 201 |
wrap_file(root_filename) |
| 194 | 202 |
} else {
|
| copyright | ||
|---|---|---|
| 75 | 75 |
Comment: Wojtek Kosior promises not to sue even in case of violations |
| 76 | 76 |
of the license. |
| 77 | 77 |
|
| 78 |
Files: test/__init__.py test/test_unit.py test/default_profiles/icecat_empty/extensions.json |
|
| 78 |
Files: test/__init__.py test/unit/* |
|
| 79 |
test/default_profiles/icecat_empty/extensions.json |
|
| 79 | 80 |
Copyright: 2021 Wojtek Kosior <koszko@koszko.org> |
| 80 | 81 |
License: CC0 |
| 81 | 82 |
|
| 82 |
Files: test/profiles.py test/script_loader.py |
|
| 83 |
Files: test/profiles.py test/script_loader.py test/unit/conftest.py
|
|
| 83 | 84 |
Copyright: 2021 Wojtek Kosior <koszko@koszko.org> |
| 84 | 85 |
License: GPL-3+ |
| 85 | 86 |
Comment: Wojtek Kosior promises not to sue even in case of violations |
| test/__init__.py | ||
|---|---|---|
| 1 | 1 |
# SPDX-License-Identifier: CC0-1.0 |
| 2 |
# Copyright (C) 2021 Wojtek Kosior |
|
| test/profiles.py | ||
|---|---|---|
| 43 | 43 |
profile.set_preference(f'network.proxy.backup.{proto}', '')
|
| 44 | 44 |
profile.set_preference(f'network.proxy.backup.{proto}_port', 0)
|
| 45 | 45 |
|
| 46 |
def set_profile_console_logging(profile): |
|
| 47 |
profile.set_preference('devtools.console.stdout.content', True)
|
|
| 48 |
|
|
| 46 | 49 |
def firefox_safe_mode(firefox_binary=default_firefox_binary, |
| 47 | 50 |
proxy_host=default_proxy_host, |
| 48 | 51 |
proxy_port=default_proxy_port): |
| 49 | 52 |
profile = webdriver.FirefoxProfile() |
| 50 | 53 |
set_profile_proxy(profile, proxy_host, proxy_port) |
| 54 |
set_profile_console_logging(profile) |
|
| 51 | 55 |
|
| 52 | 56 |
options = Options() |
| 53 | 57 |
options.add_argument('--safe-mode')
|
| ... | ... | |
| 61 | 65 |
proxy_port=default_proxy_port): |
| 62 | 66 |
profile = webdriver.FirefoxProfile(profile_dir) |
| 63 | 67 |
set_profile_proxy(profile, proxy_host, proxy_port) |
| 68 |
set_profile_console_logging(profile) |
|
| 64 | 69 |
|
| 65 | 70 |
return webdriver.Firefox(firefox_profile=profile, |
| 66 | 71 |
firefox_binary=firefox_binary) |
| test/script_loader.py | ||
|---|---|---|
| 49 | 49 |
if script_name_regex.match(script.name): |
| 50 | 50 |
yield script |
| 51 | 51 |
|
| 52 |
def get_wrapped_script(script_path):
|
|
| 52 |
def wrapped_script(script_path, wrap_partially=True):
|
|
| 53 | 53 |
if script_path == 'exports_init.js': |
| 54 | 54 |
with open(script_root / 'MOZILLA_exports_init.js') as script: |
| 55 | 55 |
return script.read() |
| 56 | 56 |
|
| 57 |
awk = subprocess.run(['awk', '-f', str(awk_script), 'wrapped_code', |
|
| 58 |
str(script_path)], |
|
| 59 |
stdout=subprocess.PIPE, cwd=script_root, check=True) |
|
| 57 |
command = 'partially_wrapped_code' if wrap_partially else 'wrapped_code' |
|
| 58 |
awk_command = ['awk', '-f', str(awk_script), command, str(script_path)] |
|
| 59 |
awk = subprocess.run(awk_command, stdout=subprocess.PIPE, cwd=script_root, |
|
| 60 |
check=True) |
|
| 60 | 61 |
|
| 61 | 62 |
return awk.stdout.decode() |
| 62 | 63 |
|
| ... | ... | |
| 67 | 68 |
project directory. |
| 68 | 69 |
|
| 69 | 70 |
Return a string containing script from `path` together with all other |
| 70 |
scripts it depends on, wrapped in the same way Haketilo's build system wraps |
|
| 71 |
them, with imports properly satisfied. |
|
| 71 |
scripts it depends. Dependencies are wrapped in the same way Haketilo's |
|
| 72 |
build system wraps them, with imports properly satisfied. The main script |
|
| 73 |
being loaded is wrapped partially - it also has its imports satisfied, but |
|
| 74 |
its code is not placed inside an anonymous function, so the |
|
| 72 | 75 |
""" |
| 73 | 76 |
path = make_relative_path(path) |
| 74 | 77 |
|
| ... | ... | |
| 79 | 82 |
str(path), *[str(s) for s in available]], |
| 80 | 83 |
stdout=subprocess.PIPE, cwd=script_root, check=True) |
| 81 | 84 |
|
| 82 |
output = awk.stdout.decode() |
|
| 85 |
to_load = awk.stdout.decode().split() |
|
| 86 |
texts = [wrapped_script(path, wrap_partially=(i == len(to_load) - 1)) |
|
| 87 |
for i, path in enumerate(to_load)] |
|
| 83 | 88 |
|
| 84 |
return '\n'.join([get_wrapped_script(path) for path in output.split()]) |
|
| 89 |
return '\n'.join(texts) |
|
| test/test_unit.py | ||
|---|---|---|
| 1 |
# SPDX-License-Identifier: CC0-1.0 |
|
| 2 |
|
|
| 3 |
""" |
|
| 4 |
Haketilo unit tests |
|
| 5 |
""" |
|
| 6 |
|
|
| 7 |
# This file is part of Haketilo |
|
| 8 |
# |
|
| 9 |
# Copyright (C) 2021, jahoti |
|
| 10 |
# Copyright (C) 2021, Wojtek Kosior |
|
| 11 |
# |
|
| 12 |
# This program is free software: you can redistribute it and/or modify |
|
| 13 |
# it under the terms of the CC0 1.0 Universal License as published by |
|
| 14 |
# the Creative Commons Corporation. |
|
| 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 |
# CC0 1.0 Universal License for more details. |
|
| 20 |
|
|
| 21 |
import pytest |
|
| 22 |
from .profiles import firefox_safe_mode |
|
| 23 |
from .server import do_an_internet |
|
| 24 |
from .script_loader import load_script |
|
| 25 |
|
|
| 26 |
@pytest.fixture(scope="module") |
|
| 27 |
def proxy(): |
|
| 28 |
httpd = do_an_internet() |
|
| 29 |
yield httpd |
|
| 30 |
httpd.shutdown() |
|
| 31 |
|
|
| 32 |
@pytest.fixture(scope="module") |
|
| 33 |
def driver(proxy): |
|
| 34 |
with firefox_safe_mode() as driver: |
|
| 35 |
yield driver |
|
| 36 |
driver.quit() |
|
| 37 |
|
|
| 38 |
def test_proxy(driver): |
|
| 39 |
""" |
|
| 40 |
A trivial test case that verifies mocked web pages served by proxy can be |
|
| 41 |
accessed by the browser driven. |
|
| 42 |
""" |
|
| 43 |
for proto in ['http://', 'https://']: |
|
| 44 |
driver.get(proto + 'gotmyowndoma.in') |
|
| 45 |
element = driver.find_element_by_tag_name('title')
|
|
| 46 |
title = driver.execute_script('return arguments[0].innerText;', element)
|
|
| 47 |
assert "Schrodinger's Document" in title |
|
| 48 |
|
|
| 49 |
def test_script_loader(driver): |
|
| 50 |
""" |
|
| 51 |
A trivial test case that verifies Haketilo's .js files can be properly |
|
| 52 |
loaded into a test page together with their dependencies. |
|
| 53 |
""" |
|
| 54 |
driver.get('http://gotmyowndoma.in')
|
|
| 55 |
driver.execute_script(load_script('common/stored_types.js', ['common']))
|
|
| 56 |
get_var_prefix = 'return window.haketilo_exports.TYPE_PREFIX.VAR;' |
|
| 57 |
assert driver.execute_script(get_var_prefix) == '_' |
|
| test/unit/__init__.py | ||
|---|---|---|
| 1 |
# SPDX-License-Identifier: CC0-1.0 |
|
| 2 |
# Copyright (C) 2021 Wojtek Kosior |
|
| test/unit/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 this code in a |
|
| 26 |
# proprietary program, I am not going to enforce this in court. |
|
| 27 |
|
|
| 28 |
import pytest |
|
| 29 |
|
|
| 30 |
from ..profiles import firefox_safe_mode |
|
| 31 |
from ..server import do_an_internet |
|
| 32 |
from ..script_loader import load_script |
|
| 33 |
|
|
| 34 |
@pytest.fixture(scope="package") |
|
| 35 |
def proxy(): |
|
| 36 |
httpd = do_an_internet() |
|
| 37 |
yield httpd |
|
| 38 |
httpd.shutdown() |
|
| 39 |
|
|
| 40 |
@pytest.fixture(scope="package") |
|
| 41 |
def driver(proxy): |
|
| 42 |
with firefox_safe_mode() as driver: |
|
| 43 |
yield driver |
|
| 44 |
driver.quit() |
|
| 45 |
|
|
| 46 |
script_injecting_script = '''\ |
|
| 47 |
/* |
|
| 48 |
* Selenium by default executes scripts in some weird one-time context. We want |
|
| 49 |
* separately-loaded scripts to be able to access global variables defined |
|
| 50 |
* before, including those declared with `const` or `let`. To achieve that, we |
|
| 51 |
* run our scripts by injecting them into the page inside a <script> tag. We use |
|
| 52 |
* custom properties of the `window` object to communicate with injected code. |
|
| 53 |
*/ |
|
| 54 |
|
|
| 55 |
const script_elem = document.createElement('script');
|
|
| 56 |
script_elem.textContent = arguments[0]; |
|
| 57 |
|
|
| 58 |
delete window.haketilo_selenium_return_value; |
|
| 59 |
delete window.haketilo_selenium_exception; |
|
| 60 |
window.returnval = (val => window.haketilo_selenium_return_value = val); |
|
| 61 |
window.arguments = arguments[1]; |
|
| 62 |
|
|
| 63 |
document.body.append(script_elem); |
|
| 64 |
|
|
| 65 |
/* |
|
| 66 |
* To ease debugging, we want this script to forward signal all exceptions from |
|
| 67 |
* the injectee. |
|
| 68 |
*/ |
|
| 69 |
try {
|
|
| 70 |
if (window.haketilo_selenium_exception !== false) |
|
| 71 |
throw 'Error in injected script! Check your geckodriver.log!'; |
|
| 72 |
} finally {
|
|
| 73 |
script_elem.remove(); |
|
| 74 |
} |
|
| 75 |
|
|
| 76 |
return window.haketilo_selenium_return_value; |
|
| 77 |
''' |
|
| 78 |
|
|
| 79 |
def _execute_in_page_context(driver, script, *args): |
|
| 80 |
script = script + '\n;\nwindow.haketilo_selenium_exception = false;' |
|
| 81 |
try: |
|
| 82 |
return driver.execute_script(script_injecting_script, script, args) |
|
| 83 |
except Exception as e: |
|
| 84 |
import sys |
|
| 85 |
lines = enumerate(script.split('\n'), 1)
|
|
| 86 |
for err_info in [('Failing script\n',), *lines]:
|
|
| 87 |
print(*err_info, file=sys.stderr) |
|
| 88 |
|
|
| 89 |
raise e from None |
|
| 90 |
|
|
| 91 |
@pytest.fixture(scope="package") |
|
| 92 |
def execute_in_page(driver): |
|
| 93 |
def do_execute(script, *args, **kwargs): |
|
| 94 |
if 'page' in kwargs: |
|
| 95 |
driver.get(kwargs['page']) |
|
| 96 |
|
|
| 97 |
return _execute_in_page_context(driver, script, args) |
|
| 98 |
|
|
| 99 |
yield do_execute |
|
| 100 |
|
|
| 101 |
@pytest.fixture(scope="package") |
|
| 102 |
def load_into_page(driver): |
|
| 103 |
def do_load(path, import_dirs, *args, **kwargs): |
|
| 104 |
if 'page' in kwargs: |
|
| 105 |
driver.get(kwargs['page']) |
|
| 106 |
|
|
| 107 |
_execute_in_page_context(driver, load_script(path, import_dirs), args) |
|
| 108 |
|
|
| 109 |
yield do_load |
|
| test/unit/test_basic.py | ||
|---|---|---|
| 1 |
# SPDX-License-Identifier: CC0-1.0 |
|
| 2 |
|
|
| 3 |
""" |
|
| 4 |
Haketilo unit tests - base |
|
| 5 |
""" |
|
| 6 |
|
|
| 7 |
# This file is part of Haketilo |
|
| 8 |
# |
|
| 9 |
# Copyright (C) 2021, Wojtek Kosior |
|
| 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 |
import pytest |
|
| 21 |
|
|
| 22 |
def test_driver(driver): |
|
| 23 |
""" |
|
| 24 |
A trivial test case that verifies mocked web pages served by proxy can be |
|
| 25 |
accessed by the browser driven. |
|
| 26 |
""" |
|
| 27 |
for proto in ['http://', 'https://']: |
|
| 28 |
driver.get(proto + 'gotmyowndoma.in') |
|
| 29 |
element = driver.find_element_by_tag_name('title')
|
|
| 30 |
title = driver.execute_script('return arguments[0].innerText;', element)
|
|
| 31 |
assert "Schrodinger's Document" in title |
|
| 32 |
|
|
| 33 |
def test_script_loader(execute_in_page, load_into_page): |
|
| 34 |
""" |
|
| 35 |
A trivial test case that verifies Haketilo's .js files can be properly |
|
| 36 |
loaded into a test page together with their dependencies. |
|
| 37 |
""" |
|
| 38 |
load_into_page('common/stored_types.js', ['common'],
|
|
| 39 |
page='https://gotmyowndoma.in') |
|
| 40 |
|
|
| 41 |
assert execute_in_page('returnval(TYPE_PREFIX.VAR);') == '_'
|
|
| test/unit/test_patterns.py | ||
|---|---|---|
| 1 |
# SPDX-License-Identifier: CC0-1.0 |
|
| 2 |
|
|
| 3 |
""" |
|
| 4 |
Haketilo unit tests - URL patterns |
|
| 5 |
""" |
|
| 6 |
|
|
| 7 |
# This file is part of Haketilo |
|
| 8 |
# |
|
| 9 |
# Copyright (C) 2021, Wojtek Kosior |
|
| 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 |
import pytest |
|
| 21 |
|
|
| 22 |
from ..script_loader import load_script |
|
| 23 |
|
|
| 24 |
@pytest.fixture(scope="session") |
|
| 25 |
def patterns_code(): |
|
| 26 |
yield load_script('common/patterns.js', ['common'])
|
|
| 27 |
|
|
| 28 |
def test_regexes(execute_in_page, patterns_code): |
|
| 29 |
""" |
|
| 30 |
patterns.js contains regexes used for URL parsing. |
|
| 31 |
Verify they work properly. |
|
| 32 |
""" |
|
| 33 |
execute_in_page(patterns_code, page='https://gotmyowndoma.in') |
|
| 34 |
|
|
| 35 |
valid_url = 'https://example.com/a/b?ver=1.2.3#heading2' |
|
| 36 |
valid_url_rest = 'example.com/a/b?ver=1.2.3#heading2' |
|
| 37 |
|
|
| 38 |
# Test matching of URL protocol. |
|
| 39 |
match = execute_in_page('returnval(proto_regex.exec(arguments[0]));',
|
|
| 40 |
valid_url) |
|
| 41 |
assert match |
|
| 42 |
assert match[1] == 'https' |
|
| 43 |
assert match[2] == valid_url_rest |
|
| 44 |
|
|
| 45 |
match = execute_in_page('returnval(proto_regex.exec(arguments[0]));',
|
|
| 46 |
'://bad-url.missing/protocol') |
|
| 47 |
assert match is None |
|
| 48 |
|
|
| 49 |
# Test matching of http(s) URLs. |
|
| 50 |
match = execute_in_page('returnval(http_regex.exec(arguments[0]));',
|
|
| 51 |
valid_url_rest) |
|
| 52 |
assert match |
|
| 53 |
assert match[1] == 'example.com' |
|
| 54 |
assert match[2] == '/a/b' |
|
| 55 |
assert match[3] == '?ver=1.2.3' |
|
| 56 |
|
|
| 57 |
match = execute_in_page('returnval(http_regex.exec(arguments[0]));',
|
|
| 58 |
'another.example.com') |
|
| 59 |
assert match |
|
| 60 |
assert match[1] == 'another.example.com' |
|
| 61 |
assert match[2] == '' |
|
| 62 |
assert match[3] == '' |
|
| 63 |
|
|
| 64 |
match = execute_in_page('returnval(http_regex.exec(arguments[0]));',
|
|
| 65 |
'/bad/http/example') |
|
| 66 |
assert match == None |
|
| 67 |
|
|
| 68 |
# Test matching of file URLs. |
|
| 69 |
match = execute_in_page('returnval(file_regex.exec(arguments[0]));',
|
|
| 70 |
'/good/file/example') |
|
| 71 |
assert match |
|
| 72 |
assert match[1] == '/good/file/example' |
|
| 73 |
|
|
| 74 |
# Test matching of ftp URLs. |
|
| 75 |
match = execute_in_page('returnval(ftp_regex.exec(arguments[0]));',
|
|
| 76 |
'example.com/a/b#heading2') |
|
| 77 |
assert match |
|
| 78 |
assert match[1] is None |
|
| 79 |
assert match[2] == 'example.com' |
|
| 80 |
assert match[3] == '/a/b' |
|
| 81 |
|
|
| 82 |
match = execute_in_page('returnval(ftp_regex.exec(arguments[0]));',
|
|
| 83 |
'some_user@localhost') |
|
| 84 |
assert match |
|
| 85 |
assert match[1] == 'some_user@' |
|
| 86 |
assert match[2] == 'localhost' |
|
| 87 |
assert match[3] == '' |
|
| 88 |
|
|
| 89 |
match = execute_in_page('returnval(ftp_regex.exec(arguments[0]));',
|
|
| 90 |
'@bad.url/') |
|
| 91 |
assert match is None |
|
Also available in: Unified diff
improve unit testing approach
Unit tests were moved to their own subdirectory.
Fixtures common to many unit tests were moved to test/unit/conftest.py.
A facility to execute scripts in page's global scope was added.
A workaround was employed to present information about errors in injected scripts.
Sample unit tests for regexes in common/patterns.js were added.