Project

General

Profile

« Previous | Next » 

Revision ad69f9c8

Added by koszko over 1 year ago

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.

View differences:

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