Revision ad69f9c8
Added by koszko over 1 year ago
Makefile.in | ||
---|---|---|
16 | 16 |
VPATH = <<VPATH>> |
17 | 17 |
|
18 | 18 |
version = <<VERSION>> |
19 |
PYTEST = <<PYTEST>> |
|
19 | 20 |
extension_files = background/ common/ content/ html/ licenses/ \ |
20 | 21 |
copyright default_settings.json manifest.json |
21 | 22 |
|
... | ... | |
70 | 71 |
-subj "/CN=Haketilo Test" |
71 | 72 |
|
72 | 73 |
test: test/certs/rootCA.pem test/certs/site.key $(default_target)-build.zip |
73 |
MOZ_HEADLESS=whatever pytest
|
|
74 |
MOZ_HEADLESS=whatever $(PYTEST)
|
|
74 | 75 |
|
75 | 76 |
test-environment: test/certs/rootCA.pem test/certs/site.key |
76 | 77 |
python3 -m test |
common/broadcast.js | ||
---|---|---|
46 | 46 |
function sender_connection() |
47 | 47 |
{ |
48 | 48 |
return { |
49 |
type: "sender", |
|
49 | 50 |
port: connect_to_background("broadcast_send") |
50 | 51 |
}; |
51 | 52 |
} |
... | ... | |
92 | 93 |
function listener_connection(cb) |
93 | 94 |
{ |
94 | 95 |
const conn = { |
96 |
type: "listener", |
|
95 | 97 |
port: connect_to_background("broadcast_listen") |
96 | 98 |
}; |
97 | 99 |
|
... | ... | |
115 | 117 |
|
116 | 118 |
function close(conn) |
117 | 119 |
{ |
120 |
if (conn.type === "sender") |
|
121 |
flush(conn); |
|
118 | 122 |
conn.port.disconnect(); |
119 | 123 |
} |
120 | 124 |
#EXPORT close |
common/entities.js | ||
---|---|---|
74 | 74 |
#EXPORT item_id_string |
75 | 75 |
|
76 | 76 |
/* vers should be an array of comparable values. Return the greatest one. */ |
77 |
const max = vals => Array.reduce(vals, (v1, v2) => v1 > v2 ? v1 : v2);
|
|
77 |
const max = vals => vals.reduce((v1, v2) => v1 > v2 ? v1 : v2);
|
|
78 | 78 |
|
79 | 79 |
/* |
80 | 80 |
* versioned_item should be a dict with keys being version strings and values |
... | ... | |
167 | 167 |
* |
168 | 168 |
* Returns a *new* array. Doesn't modify its argument. |
169 | 169 |
*/ |
170 |
const normalize_version = ver => Array.reduceRight(ver, version_reductor, []);
|
|
170 |
const normalize_version = ver => ver.reduceRight(version_reductor, []);
|
|
171 | 171 |
|
172 | 172 |
#ENDIF |
common/indexeddb.js | ||
---|---|---|
56 | 56 |
const db_version = [1, 0, 0]; |
57 | 57 |
|
58 | 58 |
const nr_reductor = ([i, s], num) => [i - 1, s + num * 1024 ** i]; |
59 |
const version_nr = ver => Array.reduce(ver.slice(0, 3), nr_reductor, [2, 0])[1];
|
|
59 |
const version_nr = ver => ver.slice(0, 3).reduce(nr_reductor, [2, 0])[1];
|
|
60 | 60 |
|
61 | 61 |
const stores = [ |
62 | 62 |
["files", {keyPath: "hash_key"}], |
common/patterns_query_tree.js | ||
---|---|---|
68 | 68 |
return false; |
69 | 69 |
} |
70 | 70 |
|
71 |
if (Array.reduce(tree_node.wildcard_matches, (a, b) => b && a !== null, 1))
|
|
71 |
if (tree_node.wildcard_matches.reduce((a, b) => b && a !== null, 1))
|
|
72 | 72 |
return false; |
73 | 73 |
|
74 | 74 |
return tree_node.literal_match === null; |
configure | ||
---|---|---|
18 | 18 |
BROWSERPATH='' |
19 | 19 |
SRCDIR='' |
20 | 20 |
TARGET='' |
21 |
BROWSER_BINARY='' |
|
22 |
CLEAN_PROFILE='' |
|
23 |
DRIVER='' |
|
24 |
PYTEST='' |
|
21 | 25 |
|
22 | 26 |
# Parse command line options |
23 | 27 |
while [ "x$1" != x ]; do |
24 | 28 |
case "$1" in |
25 |
--srcdir=*) SRCDIR="$(echo "$1" | cut -c 10-)";; |
|
26 |
--srcdir) SRCDIR="$2"; shift;; |
|
27 |
"DESTDIR"=*) DESTDIR="$(echo "$1" | cut -c 9-)";; |
|
28 |
"UPDATE_URL"=*) UPDATE_URL="$(echo "$1" | cut -c 12-)";; |
|
29 |
--host=*) TARGET="$(echo "$1" | cut -c 8-)";; |
|
30 |
--host) TARGET="$2"; shift;; |
|
29 |
--srcdir=*) SRCDIR="$(printf %s "$1" | cut -c 10-)";; |
|
30 |
--srcdir) SRCDIR="$2"; shift;; |
|
31 |
--browser-binary=*) BROWSER_BINARY="$(printf %s "$1" | cut -c 18-)";; |
|
32 |
--browser-binary) BROWSER_BINARY="$2"; shift;; |
|
33 |
--clean-profile=*) CLEAN_PROFILE="$(printf %s "$1" | cut -c 17-)";; |
|
34 |
--clean-profile) CLEAN_PROFILE="$2"; shift;; |
|
35 |
--driver=*) DRIVER="$(printf %s "$1" | cut -c 10-)";; |
|
36 |
--driver) DRIVER="$2"; shift;; |
|
37 |
--pytest=*) PYTEST="$(printf %s "$1" | cut -c 10-)";; |
|
38 |
--pytest) PYTEST="$2"; shift;; |
|
39 |
--srcdir) SRCDIR="$2"; shift;; |
|
40 |
"DESTDIR"=*) DESTDIR="$(printf %s "$1" | cut -c 9-)";; |
|
41 |
"UPDATE_URL"=*) UPDATE_URL="$(printf %s "$1" | cut -c 12-)";; |
|
42 |
--host=*) TARGET="$(printf %s "$1" | cut -c 8-)";; |
|
43 |
--host) TARGET="$2"; shift;; |
|
31 | 44 |
|
32 | 45 |
# browsers |
33 | 46 |
chromium | chrome | google-chrome | mozilla |\ |
... | ... | |
70 | 83 |
BROWSERPATH="$(realpath "$(which $TARGET)")" |
71 | 84 |
fi |
72 | 85 |
|
86 |
# Autodetect browser binary (needed for Selenium) |
|
87 |
if [ "x$BROWSER_BINARY" = x ]; then |
|
88 |
if [ "x$TARGET" = xabrowser ]; then |
|
89 |
# Trisquel's path to Abrowser |
|
90 |
BROWSER_BINARY=/usr/lib/abrowser/abrowser |
|
91 |
if [ "x$TARGET" = xabrowser ]; then |
|
92 |
# Debian's path to Librewolf |
|
93 |
BROWSER_BINARY=/usr/share/librewolf/librewolf |
|
94 |
elif [ "x$TARGET" = xicecat ]; then |
|
95 |
# Parabola's path to IceCat |
|
96 |
BROWSER_BINARY=/usr/lib/icecat/icecat |
|
97 |
fi |
|
98 |
fi |
|
99 |
|
|
73 | 100 |
# Check and standardize target |
74 | 101 |
case "$TARGET" in |
75 | 102 |
mozilla | firefox | abrowser | icecat | iceweasel-uxp |\ |
... | ... | |
79 | 106 |
*) echo Invalid target "'$TARGET'" >&2; exit 2;; |
80 | 107 |
esac |
81 | 108 |
|
109 |
# Autodetect Selenium driver |
|
110 |
if [ "x$DRIVER" = x ]; then |
|
111 |
if [ "x$TARGET" = mozilla ]; then |
|
112 |
DRIVER=geckodriver |
|
113 |
fi |
|
114 |
fi |
|
115 |
|
|
116 |
# Autodetect clean profile directory for use in selenium tests |
|
117 |
if [ "x$CLEAN_PROFILE" = x ]; then |
|
118 |
if [ "x$TARGET" = mozilla ]; then |
|
119 |
CLEAN_PROFILE="$SRCDIR"/test/default_profile/icecat_empty |
|
120 |
fi |
|
121 |
fi |
|
122 |
|
|
123 |
# Autodetect pytest |
|
124 |
for PYTEST_GUESS in pytest pytest-3 pytest3; do |
|
125 |
if [ "x$PYTEST" = x ]; then |
|
126 |
PYTEST="$(which $PYTEST_GUESS || true)" |
|
127 |
fi |
|
128 |
done |
|
129 |
|
|
82 | 130 |
# Autodetect DESTDIR (no check needed) |
83 | 131 |
if [ "x$DESTDIR" = x ]; then |
84 | 132 |
echo Guessing installation directory. |
... | ... | |
95 | 143 |
fi |
96 | 144 |
|
97 | 145 |
# Write record.conf (LEAVE SRCDIR FIRST) |
98 |
echo srcdir = "$SRCDIR" > record.conf |
|
99 |
echo default_target = "$TARGET" >> record.conf |
|
100 |
echo DESTDIR = "$DESTDIR" >> record.conf |
|
101 |
echo UPDATE_URL = "$UPDATE_URL" >> record.conf |
|
102 |
|
|
146 |
printf '%s\n' "srcdir = $SRCDIR" > record.conf |
|
147 |
printf '%s\n' "default_target = $TARGET" >> record.conf |
|
148 |
printf '%s\n' "DESTDIR = $DESTDIR" >> record.conf |
|
149 |
printf '%s\n' "UPDATE_URL = $UPDATE_URL" >> record.conf |
|
150 |
printf '%s\n' "DRIVER = $DRIVER" >> record.conf |
|
151 |
printf '%s\n' "BROWSER_BINARY = $BROWSER_BINARY" >> record.conf |
|
152 |
printf '%s\n' "CLEAN_PROFILE = $CLEAN_PROFILE" >> record.conf |
|
153 |
printf '%s\n' "PYTEST = $PYTEST" >> record.conf |
|
103 | 154 |
|
104 | 155 |
# Prepare and run write_makefile.sh (as config.status) |
105 | 156 |
if [ ! -e config.status ]; then |
html/file_preview.html | ||
---|---|---|
1 |
<!DOCTYPE html> |
|
2 |
<!-- |
|
3 |
SPDX-License-Identifier: GPL-3.0-or-later OR CC-BY-SA-4.0 |
|
4 |
|
|
5 |
Haketilo's preview of a file kept in IndexedDB |
|
6 |
|
|
7 |
This file is part of Haketilo. |
|
8 |
|
|
9 |
Copyright (C) 2022 Wojtek Kosior <koszko@koszko.org> |
|
10 |
|
|
11 |
File is dual-licensed. You can choose either GPLv3+, CC BY-SA or both. |
|
12 |
|
|
13 |
This program is free software: you can redistribute it and/or modify |
|
14 |
it under the terms of the GNU General Public License as published by |
|
15 |
the Free Software Foundation, either version 3 of the License, or |
|
16 |
(at your option) any later version. |
|
17 |
|
|
18 |
This program is distributed in the hope that it will be useful, |
|
19 |
but WITHOUT ANY WARRANTY; without even the implied warranty of |
|
20 |
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
|
21 |
GNU General Public License for more details. |
|
22 |
|
|
23 |
You should have received a copy of the GNU General Public License |
|
24 |
along with this program. If not, see <https://www.gnu.org/licenses/>. |
|
25 |
|
|
26 |
I, Wojtek Kosior, thereby promise not to sue for violation of this file's |
|
27 |
licenses. Although I request that you do not make use of this code in a |
|
28 |
proprietary program, I am not going to enforce this in court. |
|
29 |
--> |
|
30 |
<html> |
|
31 |
<head> |
|
32 |
<meta charset="utf-8"/> |
|
33 |
<title>File preview</title> |
|
34 |
#LOADCSS html/reset.css |
|
35 |
#LOADCSS html/base.css |
|
36 |
<style> |
|
37 |
.error_msg { |
|
38 |
color: #a33; |
|
39 |
/* also italics maybe?*/ |
|
40 |
} |
|
41 |
</style> |
|
42 |
</head> |
|
43 |
<body> |
|
44 |
<div id="info_msg">loading...</div> |
|
45 |
<div id="error_msg" class="hide"></div> |
|
46 |
#LOADJS html/file_preview.js |
|
47 |
</body> |
|
48 |
</html> |
html/file_preview.js | ||
---|---|---|
1 |
/** |
|
2 |
* This file is part of Haketilo. |
|
3 |
* |
|
4 |
* Function: Haketilo's preview of a file kept in IndexedDB. |
|
5 |
* |
|
6 |
* Copyright (C) 2022 Wojtek Kosior |
|
7 |
* |
|
8 |
* This program is free software: you can redistribute it and/or modify |
|
9 |
* it under the terms of the GNU General Public License as published by |
|
10 |
* the Free Software Foundation, either version 3 of the License, or |
|
11 |
* (at your option) any later version. |
|
12 |
* |
|
13 |
* This program is distributed in the hope that it will be useful, |
|
14 |
* but WITHOUT ANY WARRANTY; without even the implied warranty of |
|
15 |
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
|
16 |
* GNU General Public License for more details. |
|
17 |
* |
|
18 |
* As additional permission under GNU GPL version 3 section 7, you |
|
19 |
* may distribute forms of that code without the copy of the GNU |
|
20 |
* GPL normally required by section 4, provided you include this |
|
21 |
* license notice and, in case of non-source distribution, a URL |
|
22 |
* through which recipients can access the Corresponding Source. |
|
23 |
* If you modify file(s) with this exception, you may extend this |
|
24 |
* exception to your version of the file(s), but you are not |
|
25 |
* obligated to do so. If you do not wish to do so, delete this |
|
26 |
* exception statement from your version. |
|
27 |
* |
|
28 |
* As a special exception to the GPL, any HTML file which merely |
|
29 |
* makes function calls to this code, and for that purpose |
|
30 |
* includes it by reference shall be deemed a separate work for |
|
31 |
* copyright law purposes. If you modify this code, you may extend |
|
32 |
* this exception to your version of the code, but you are not |
|
33 |
* obligated to do so. If you do not wish to do so, delete this |
|
34 |
* exception statement from your version. |
|
35 |
* |
|
36 |
* You should have received a copy of the GNU General Public License |
|
37 |
* along with this program. If not, see <https://www.gnu.org/licenses/>. |
|
38 |
* |
|
39 |
* I, Wojtek Kosior, thereby promise not to sue for violation of this file's |
|
40 |
* license. Although I request that you do not make use of this code in a |
|
41 |
* proprietary program, I am not going to enforce this in court. |
|
42 |
*/ |
|
43 |
|
|
44 |
#IMPORT common/indexeddb.js AS haketilodb |
|
45 |
|
|
46 |
#FROM html/DOM_helpers.js IMPORT by_id |
|
47 |
|
|
48 |
const error_msg = by_id("error_msg"), info_msg = by_id("info_msg"); |
|
49 |
|
|
50 |
function error(msg) { |
|
51 |
info_msg.remove(); |
|
52 |
error_msg.classList.remove("hide"); |
|
53 |
error_msg.innerText = msg; |
|
54 |
} |
|
55 |
|
|
56 |
async function show_preview(hash_key) { |
|
57 |
const db = await haketilodb.get(); |
|
58 |
const file = await haketilodb.idb_get(db.transaction("files"), |
|
59 |
"files", hash_key); |
|
60 |
if (file === undefined) { |
|
61 |
error("Couldn't find file in Haketilo's internal database :("); |
|
62 |
} else { |
|
63 |
const blobby_opts = {type: "text/plain;charset=UTF-8"}; |
|
64 |
const blobby = new Blob([file.contents], blobby_opts); |
|
65 |
location.replace(URL.createObjectURL(blobby)); |
|
66 |
} |
|
67 |
} |
|
68 |
|
|
69 |
const match = /^[^#]*#(.*)$/.exec(document.URL); |
|
70 |
if (!match) { |
|
71 |
error("Bad URL :("); |
|
72 |
} else { |
|
73 |
const hash_key = match[1]; |
|
74 |
show_preview(hash_key); |
|
75 |
} |
html/item_list.js | ||
---|---|---|
52 | 52 |
if (list_ctx.dialog_ctx.shown && !ignore_dialog) |
53 | 53 |
return; |
54 | 54 |
|
55 |
list_ctx.preview_ctx = list_ctx.preview_cb( |
|
56 |
item.definition, |
|
57 |
list_ctx.preview_ctx, |
|
58 |
list_ctx.dialog_ctx |
|
59 |
); |
|
55 |
list_ctx.preview_ctx = |
|
56 |
list_ctx.preview_cb(item.definition, list_ctx.preview_ctx); |
|
60 | 57 |
list_ctx.preview_container.prepend(list_ctx.preview_ctx.main_div); |
61 | 58 |
|
62 | 59 |
if (list_ctx.previewed_item !== null) |
html/item_preview.js | ||
---|---|---|
44 | 44 |
#IMPORT common/indexeddb.js AS haketilodb |
45 | 45 |
#IMPORT html/dialog.js |
46 | 46 |
|
47 |
#FROM common/browser.js IMPORT browser |
|
47 | 48 |
#FROM html/DOM_helpers.js IMPORT clone_template |
48 | 49 |
|
49 |
function populate_list(ul, items) |
|
50 |
{ |
|
50 |
function populate_list(ul, items) { |
|
51 | 51 |
for (const item of items) { |
52 | 52 |
const li = document.createElement("li"); |
53 | 53 |
li.append(item); |
... | ... | |
55 | 55 |
} |
56 | 56 |
} |
57 | 57 |
|
58 |
/* Link click handler used in make_file_link(). */ |
|
59 |
async function file_link_clicked(preview_object, file_ref, event) |
|
60 |
{ |
|
61 |
event.preventDefault(); |
|
62 |
|
|
63 |
const db = await haketilodb.get(); |
|
64 |
const file = await haketilodb.idb_get(db.transaction("files"), |
|
65 |
"files", file_ref.hash_key); |
|
66 |
if (file === undefined) { |
|
67 |
dialog.error(preview_object.dialog_context, |
|
68 |
"File missing from Haketilo's internal database :("); |
|
69 |
} else { |
|
70 |
const encoded_file = encodeURIComponent(file.contents); |
|
71 |
open(`data:text/plain;charset=utf8,${encoded_file}`, '_blank'); |
|
72 |
} |
|
73 |
} |
|
58 |
const file_preview_link = browser.runtime.getURL("html/file_preview.html"); |
|
74 | 59 |
|
75 | 60 |
/* |
76 | 61 |
* The default function to use to create file preview link. Links it creates can |
77 | 62 |
* be used to view files from IndexedDB. |
78 | 63 |
*/ |
79 |
function make_file_link(preview_object, file_ref) |
|
80 |
{ |
|
64 |
function make_file_link(preview_object, file_ref) { |
|
81 | 65 |
const a = document.createElement("a"); |
82 |
a.href = "javascript:void(0)";
|
|
66 |
a.href = `${file_preview_link}#${file_ref.hash_key}`;
|
|
83 | 67 |
a.innerText = file_ref.file; |
84 |
a.addEventListener("click", |
|
85 |
e => file_link_clicked(preview_object, file_ref, e)); |
|
86 |
|
|
68 |
a.target = "_blank"; |
|
87 | 69 |
return a; |
88 | 70 |
} |
89 | 71 |
|
90 |
function resource_preview(resource, preview_object, dialog_context, |
|
91 |
make_link_cb=make_file_link) |
|
92 |
{ |
|
72 |
function resource_preview(resource, preview_object, link_cb=make_file_link) { |
|
93 | 73 |
if (preview_object === undefined) |
94 | 74 |
preview_object = clone_template("resource_preview"); |
95 | 75 |
|
... | ... | |
104 | 84 |
[...preview_object.dependencies.childNodes].forEach(n => n.remove()); |
105 | 85 |
populate_list(preview_object.dependencies, resource.dependencies); |
106 | 86 |
|
107 |
const link_maker = file_ref => make_link_cb(preview_object, file_ref);
|
|
87 |
const link_maker = file_ref => link_cb(preview_object, file_ref); |
|
108 | 88 |
|
109 | 89 |
[...preview_object.scripts.childNodes].forEach(n => n.remove()); |
110 | 90 |
populate_list(preview_object.scripts, resource.scripts.map(link_maker)); |
... | ... | |
113 | 93 |
populate_list(preview_object.copyright, |
114 | 94 |
resource.source_copyright.map(link_maker)); |
115 | 95 |
|
116 |
preview_object.dialog_context = dialog_context; |
|
117 |
|
|
118 | 96 |
return preview_object; |
119 | 97 |
} |
120 | 98 |
#EXPORT resource_preview |
121 | 99 |
|
122 |
function mapping_preview(mapping, preview_object, dialog_context, |
|
123 |
make_link_cb=make_file_link) |
|
124 |
{ |
|
100 |
function mapping_preview(mapping, preview_object, link_cb=make_file_link) { |
|
125 | 101 |
if (preview_object === undefined) |
126 | 102 |
preview_object = clone_template("mapping_preview"); |
127 | 103 |
|
... | ... | |
145 | 121 |
} |
146 | 122 |
} |
147 | 123 |
|
148 |
const link_maker = file_ref => make_link_cb(preview_object, file_ref);
|
|
124 |
const link_maker = file_ref => link_cb(preview_object, file_ref); |
|
149 | 125 |
|
150 | 126 |
[...preview_object.copyright.childNodes].forEach(n => n.remove()); |
151 | 127 |
populate_list(preview_object.copyright, |
152 | 128 |
mapping.source_copyright.map(link_maker)); |
153 | 129 |
|
154 |
preview_object.dialog_context = dialog_context; |
|
155 |
|
|
156 | 130 |
return preview_object; |
157 | 131 |
} |
158 | 132 |
#EXPORT mapping_preview |
manifest.json | ||
---|---|---|
68 | 68 |
"16": "icons/haketilo16.png" |
69 | 69 |
}, |
70 | 70 |
"default_title": "Haketilo", |
71 |
// Both popup.html and settings.html depend on file_preview.html. |
|
72 |
#LOADHTML html/file_preview.html |
|
71 | 73 |
#LOADHTML html/popup.html |
72 | 74 |
"default_popup": "html/popup.html" |
73 | 75 |
}, |
test/data/pages/scripts_to_block_1.html | ||
---|---|---|
37 | 37 |
href="javascript:window.__run = [...(window.__run || []), 'href'];void(0);"> |
38 | 38 |
Click Meee! |
39 | 39 |
</a> |
40 |
<iframe src="javascript:window.parent.__run = [...(window.parent.__run || []), 'src'];">
|
|
40 |
<iframe src="javascript:void(window.parent.__run = [...(window.parent.__run || []), 'src']);">
|
|
41 | 41 |
</iframe> |
42 | 42 |
<object data="javascript:window.__run = [...(window.__run || []), 'data'];"> |
43 | 43 |
</object> |
test/misc_constants.py | ||
---|---|---|
27 | 27 |
# file's license. Although I request that you do not make use of this code |
28 | 28 |
# in a proprietary program, I am not going to enforce this in court. |
29 | 29 |
|
30 |
import re |
|
30 | 31 |
from pathlib import Path |
31 | 32 |
|
32 | 33 |
here = Path(__file__).resolve().parent |
... | ... | |
36 | 37 |
unit_test_defines = ['-D', 'MOZILLA', '-D', 'MV2', '-D', 'TEST', |
37 | 38 |
'-D', 'UNIT_TEST', '-D', 'DEBUG'] |
38 | 39 |
|
39 |
default_firefox_binary = '/usr/lib/icecat/icecat' |
|
40 |
# The browser might be loading some globally-installed add-ons by default. They |
|
41 |
# could interfere with the tests, so we'll disable all of them. |
|
42 |
default_clean_profile_dir = here / 'default_profile' / 'icecat_empty' |
|
40 |
conf_line_regex = re.compile(r'^([^=]+)=(.*)$') |
|
41 |
conf_settings = {} |
|
42 |
with open(here.parent / 'record.conf', 'rt') as conf: |
|
43 |
for line in conf.readlines(): |
|
44 |
match = conf_line_regex.match(line) |
|
45 |
if match: |
|
46 |
conf_settings[match.group(1).strip()] = match.group(2).strip() |
|
43 | 47 |
|
44 | 48 |
default_proxy_host = '127.0.0.1' |
45 | 49 |
default_proxy_port = 1337 |
test/profiles.py | ||
---|---|---|
80 | 80 |
profile.set_preference('extensions.webextensions.uuids', |
81 | 81 |
json.dumps({extension_id: uuid})) |
82 | 82 |
|
83 |
def firefox_safe_mode(firefox_binary=default_firefox_binary,
|
|
83 |
def firefox_safe_mode(firefox_binary=conf_settings['BROWSER_BINARY'],
|
|
84 | 84 |
proxy_host=default_proxy_host, |
85 | 85 |
proxy_port=default_proxy_port): |
86 | 86 |
""" |
... | ... | |
97 | 97 |
return HaketiloFirefox(options=options, firefox_profile=profile, |
98 | 98 |
firefox_binary=firefox_binary) |
99 | 99 |
|
100 |
def firefox_with_profile(firefox_binary=default_firefox_binary,
|
|
101 |
profile_dir=default_clean_profile_dir,
|
|
100 |
def firefox_with_profile(firefox_binary=conf_settings['BROWSER_BINARY'],
|
|
101 |
profile_dir=conf_settings['CLEAN_PROFILE'],
|
|
102 | 102 |
proxy_host=default_proxy_host, |
103 | 103 |
proxy_port=default_proxy_port): |
104 | 104 |
""" |
test/unit/test_indexeddb.py | ||
---|---|---|
323 | 323 |
|
324 | 324 |
# Create elements that will have tracked data inserted under them. |
325 | 325 |
driver.switch_to.window(windows[0]) |
326 |
execute_in_page(''' |
|
327 |
for (const store_name of trackable) { |
|
328 |
const h2 = document.createElement("h2"); |
|
329 |
h2.innerText = store_name; |
|
330 |
document.body.append(h2); |
|
331 |
|
|
332 |
const ul = document.createElement("ul"); |
|
333 |
ul.id = store_name; |
|
334 |
document.body.append(ul); |
|
335 |
} |
|
336 |
''') |
|
326 |
execute_in_page( |
|
327 |
''' |
|
328 |
for (const store_name of trackable) { |
|
329 |
const h2 = document.createElement("h2"); |
|
330 |
h2.innerText = store_name; |
|
331 |
document.body.append(h2); |
|
332 |
|
|
333 |
const ul = document.createElement("ul"); |
|
334 |
ul.id = store_name; |
|
335 |
document.body.append(ul); |
|
336 |
} |
|
337 |
''') |
|
337 | 338 |
|
338 | 339 |
# Mock initial_data. |
339 | 340 |
sample_resource = make_sample_resource() |
test/unit/test_item_list.py | ||
---|---|---|
217 | 217 |
assert f'item{i}' in text |
218 | 218 |
assert f'Item {i}' in text |
219 | 219 |
|
220 |
# Check that file preview link works. |
|
221 |
window0 = driver.window_handles[0] |
|
222 |
driver.find_element_by_link_text('report.spdx').click() |
|
223 |
WebDriverWait(driver, 10).until(lambda _: len(driver.window_handles) == 2) |
|
224 |
window1 = next(filter(lambda w: w != window0, driver.window_handles)) |
|
225 |
driver.switch_to.window(window1) |
|
226 |
assert 'dummy report' in driver.page_source |
|
227 |
|
|
228 |
driver.close() |
|
229 |
driver.switch_to.window(window0) |
|
230 |
|
|
231 | 220 |
# Check that item removal confirmation dialog is displayed correctly. |
232 | 221 |
execute_in_page('list_ctx.remove_but.click();') |
233 | 222 |
WebDriverWait(driver, 10).until(lambda _: dialog_container.is_displayed()) |
... | ... | |
271 | 260 |
|
272 | 261 |
execute_in_page('list_ctx.ul.children[1].click();') |
273 | 262 |
|
274 |
# Check that missing file causes the right error dialog to appear. |
|
275 |
execute_in_page( |
|
276 |
'''{ |
|
277 |
async function steal_file(hash_key) |
|
278 |
{ |
|
279 |
const db = await haketilodb.get(); |
|
280 |
const transaction = db.transaction("files", "readwrite"); |
|
281 |
transaction.objectStore("files").delete(hash_key); |
|
282 |
} |
|
283 |
returnval(steal_file(arguments[0])); |
|
284 |
}''', |
|
285 |
sample_files['LICENSES/CC0-1.0.txt']['hash_key']) |
|
286 |
driver.find_element_by_link_text('LICENSES/CC0-1.0.txt').click() |
|
287 |
WebDriverWait(driver, 10).until(lambda _: dialog_container.is_displayed()) |
|
288 |
assert 'list_disabled' in ul.get_attribute('class') |
|
289 |
assert not preview_container.is_displayed() |
|
290 |
|
|
291 |
msg = execute_in_page('returnval(list_ctx.dialog_ctx.msg.textContent);') |
|
292 |
assert msg == "File missing from Haketilo's internal database :(" |
|
293 |
|
|
294 |
execute_in_page('returnval(list_ctx.dialog_ctx.ok_but.click());') |
|
295 |
WebDriverWait(driver, 10).until(lambda _: preview_container.is_displayed()) |
|
296 |
|
|
297 | 263 |
# Check that item removal failure causes the right error dialog to appear. |
298 | 264 |
execute_in_page('haketilodb.finalize_transaction = () => {throw "sth";};') |
299 | 265 |
remove_current_item() |
test/unit/test_item_preview.py | ||
---|---|---|
19 | 19 |
|
20 | 20 |
import pytest |
21 | 21 |
from selenium.webdriver.support.ui import WebDriverWait |
22 |
from selenium.common.exceptions import NoSuchWindowException |
|
22 | 23 |
|
23 | 24 |
from ..extension_crafting import ExtraHTML |
24 | 25 |
from ..script_loader import load_script |
... | ... | |
138 | 139 |
|
139 | 140 |
@pytest.mark.ext_data({ |
140 | 141 |
'background_script': broker_js, |
141 |
'extra_html': ExtraHTML('html/item_preview.html', {}), |
|
142 |
'extra_html': [ |
|
143 |
ExtraHTML('html/item_preview.html', {}), |
|
144 |
ExtraHTML('html/file_preview.html', {}, wrap_into_htmldoc=False) |
|
145 |
], |
|
142 | 146 |
'navigate_to': 'html/item_preview.html' |
143 | 147 |
}) |
144 | 148 |
@pytest.mark.usefixtures('webextension') |
... | ... | |
148 | 152 |
referenced file to be previewed. |
149 | 153 |
""" |
150 | 154 |
execute_in_page(load_script('html/item_preview.js')) |
151 |
# Mock dialog |
|
152 |
execute_in_page('dialog.error = (...args) => window.error_args = args;') |
|
153 | 155 |
|
154 | 156 |
sample_data = make_complete_sample_data() |
155 | 157 |
sample_data['mappings'] = {} |
... | ... | |
162 | 164 |
|
163 | 165 |
execute_in_page( |
164 | 166 |
''' |
165 |
let resource_preview_object = |
|
166 |
resource_preview(arguments[0], undefined, "dummy dialog ctx"); |
|
167 |
let resource_preview_object = resource_preview(arguments[0], undefined); |
|
167 | 168 |
document.body.append(resource_preview_object.main_div); |
168 | 169 |
''', |
169 | 170 |
sample_resource) |
170 | 171 |
|
171 | 172 |
window0 = driver.window_handles[0] |
172 | 173 |
driver.find_element_by_link_text('hello.js').click() |
173 |
WebDriverWait(driver, 10).until(lambda d: len(d.window_handles) > 1) |
|
174 |
window1 = [wh for wh in driver.window_handles if wh != window0][0] |
|
175 |
driver.switch_to.window(window1) |
|
176 |
assert sample_files['hello.js']['contents'] in driver.page_source |
|
177 | 174 |
|
175 |
def blob_url_navigated(driver): |
|
176 |
if len(driver.window_handles) < 2: |
|
177 |
return |
|
178 |
window1 = [wh for wh in driver.window_handles if wh != window0][0] |
|
179 |
driver.switch_to.window(window1) |
|
180 |
try: |
|
181 |
return driver.current_url.startswith('blob') |
|
182 |
except NoSuchWindowException: |
|
183 |
pass |
|
184 |
|
|
185 |
WebDriverWait(driver, 10).until(blob_url_navigated) |
|
186 |
|
|
187 |
assert sample_files['hello.js']['contents'].strip() \ |
|
188 |
in driver.find_element_by_tag_name("pre").text |
|
189 |
|
|
190 |
driver.close() |
|
178 | 191 |
driver.switch_to.window(window0) |
192 |
|
|
179 | 193 |
driver.find_element_by_link_text('bye.js').click() |
180 |
assert driver.execute_script('return window.error_args;') == [ |
|
181 |
'dummy dialog ctx', |
|
182 |
"File missing from Haketilo's internal database :(" |
|
183 |
] |
|
194 |
|
|
195 |
def get_error_span(driver): |
|
196 |
if len(driver.window_handles) < 2: |
|
197 |
return |
|
198 |
window1 = [wh for wh in driver.window_handles if wh != window0][0] |
|
199 |
driver.switch_to.window(window1) |
|
200 |
try: |
|
201 |
return driver.find_element_by_id('error_msg') |
|
202 |
except NoSuchWindowException: |
|
203 |
pass |
|
204 |
|
|
205 |
error_span = WebDriverWait(driver, 10).until(get_error_span) |
|
206 |
assert error_span.is_displayed() |
|
207 |
assert "Couldn't find file in Haketilo's internal database :(" \ |
|
208 |
in error_span.text |
test/unit/test_patterns_query_manager.py | ||
---|---|---|
20 | 20 |
import pytest |
21 | 21 |
import json |
22 | 22 |
from selenium.webdriver.support.ui import WebDriverWait |
23 |
from selenium.common.exceptions import TimeoutException |
|
23 | 24 |
|
24 | 25 |
from ..script_loader import load_script |
25 | 26 |
|
... | ... | |
240 | 241 |
@pytest.mark.usefixtures('webextension') |
241 | 242 |
def test_pqm_script_injection(driver, execute_in_page): |
242 | 243 |
# Let's open a normal page in a second window. Window 0 will be used to make |
243 |
# changed to IndexedDB and window 1 to test the working of content scripts.
|
|
244 |
# changes to IndexedDB and window 1 to test the working of content scripts.
|
|
244 | 245 |
driver.execute_script('window.open("about:blank", "_blank");') |
245 | 246 |
WebDriverWait(driver, 10).until(lambda d: len(d.window_handles) == 2) |
246 | 247 |
windows = [*driver.window_handles] |
247 | 248 |
|
248 |
def run_content_script(): |
|
249 |
driver.switch_to.window(windows[1]) |
|
250 |
driver.get('https://gotmyowndoma.in/index.html') |
|
251 |
windows[1] = driver.window_handles[1] |
|
249 |
def get_tree_json(driver): |
|
252 | 250 |
return driver.execute_script( |
253 | 251 |
''' |
254 | 252 |
return (document.getElementById("tree-json") || {}).innerText; |
255 | 253 |
''') |
256 | 254 |
|
257 |
for attempt in range(10): |
|
255 |
def run_content_script(): |
|
256 |
driver.switch_to.window(windows[1]) |
|
257 |
driver.get('https://gotmyowndoma.in/index.html') |
|
258 |
windows[1] = driver.current_window_handle |
|
259 |
try: |
|
260 |
return WebDriverWait(driver, 10).until(get_tree_json) |
|
261 |
except TimeoutException: |
|
262 |
pass |
|
263 |
|
|
264 |
for attempt in range(2): |
|
258 | 265 |
json_txt = run_content_script() |
259 |
if json.loads(json_txt) == {}: |
|
266 |
if json_txt and json.loads(json_txt) == {}:
|
|
260 | 267 |
break; |
261 |
assert attempt != 9
|
|
268 |
assert attempt != 2
|
|
262 | 269 |
|
263 | 270 |
driver.switch_to.window(windows[0]) |
264 | 271 |
execute_in_page(load_script('common/indexeddb.js')) |
... | ... | |
271 | 278 |
} |
272 | 279 |
execute_in_page('returnval(save_items(arguments[0]));', sample_data) |
273 | 280 |
|
274 |
for attempt in range(10):
|
|
275 |
tree_json = run_content_script() |
|
281 |
for attempt in range(2):
|
|
282 |
tree_json = run_content_script() or '{}'
|
|
276 | 283 |
json.loads(tree_json) |
277 | 284 |
if all([m['identifier'] in tree_json for m in sample_mappings]): |
278 | 285 |
break |
279 |
assert attempt != 9
|
|
286 |
assert attempt != 2
|
|
280 | 287 |
|
281 | 288 |
driver.switch_to.window(windows[0]) |
282 | 289 |
execute_in_page( |
test/unit/test_popup.py | ||
---|---|---|
20 | 20 |
import pytest |
21 | 21 |
import json |
22 | 22 |
from selenium.webdriver.support.ui import WebDriverWait |
23 |
from selenium.common.exceptions import ElementNotInteractableException |
|
23 | 24 |
|
24 | 25 |
from ..extension_crafting import ExtraHTML |
25 | 26 |
from ..script_loader import load_script |
... | ... | |
146 | 147 |
""" |
147 | 148 |
reload_with_target(driver, f'mock_page_info-{page_info_key}') |
148 | 149 |
|
149 |
by_id = driver.execute_script(''' |
|
150 |
const nodes = [...document.querySelectorAll("[id]")]; |
|
151 |
return nodes.reduce((ob, node) => Object.assign(ob, {[node.id]: node}), {}); |
|
152 |
'''); |
|
150 |
def get_nodes_by_id(driver): |
|
151 |
by_id = driver.execute_script( |
|
152 |
''' |
|
153 |
const nodes = [...document.querySelectorAll("[id]")]; |
|
154 |
const reductor = (ob, node) => Object.assign(ob, {[node.id]: node}); |
|
155 |
return nodes.reduce(reductor, {}); |
|
156 |
'''); |
|
157 |
return by_id if by_id and 'repo_query_container' in by_id else None |
|
158 |
|
|
159 |
by_id = WebDriverWait(driver, 10).until(get_nodes_by_id) |
|
153 | 160 |
|
154 | 161 |
if page_info_key == '': |
155 | 162 |
error_msg = 'Page info not avaialable. Try reloading the page.' |
... | ... | |
213 | 220 |
""" |
214 | 221 |
reload_with_target(driver, f'mock_page_info-blocked_rule') |
215 | 222 |
|
223 |
driver.implicitly_wait(10) |
|
216 | 224 |
search_but = driver.find_element_by_id("search_resources_but") |
217 |
WebDriverWait(driver, 10).until(lambda d: search_but.is_displayed()) |
|
218 |
search_but.click() |
|
225 |
driver.implicitly_wait(0) |
|
226 |
|
|
227 |
# For unknown reasons waiting for search_but.is_displayed() to return True |
|
228 |
# does not guarantee the button will be interactable afterwards under newer |
|
229 |
# browsers. Hence, this workaround. |
|
230 |
def click_search_but(driver): |
|
231 |
try: |
|
232 |
search_but.click() |
|
233 |
return True |
|
234 |
except ElementNotInteractableException: |
|
235 |
pass |
|
236 |
|
|
237 |
WebDriverWait(driver, 10).until(click_search_but) |
|
238 |
|
|
219 | 239 |
containers = dict([(name, driver.find_element_by_id(f'{name}_container')) |
220 | 240 |
for name in ('page_info', 'repo_query')]) |
221 | 241 |
assert not containers['page_info'].is_displayed() |
Also available in: Unified diff
add support for testing with other browsers (especially Abrowser and Librewolf)
There are still some spurious failures when running under those newer browsers. Those will be systematically investigated and fixed.