Revision 42fe4405
Added by koszko over 1 year ago
html/popup.html | ||
---|---|---|
1 |
<!DOCTYPE html> |
|
2 |
<!-- |
|
3 |
SPDX-License-Identifier: GPL-3.0-or-later OR CC-BY-SA-4.0 |
|
4 |
|
|
5 |
Show details of how Haketilo handled given page and allow querying |
|
6 |
repositories for custom scripts. |
|
7 |
|
|
8 |
This file is part of Haketilo. |
|
9 |
|
|
10 |
Copyright (C) 2022 Wojtek Kosior <koszko@koszko.org> |
|
11 |
|
|
12 |
File is dual-licensed. You can choose either GPLv3+, CC BY-SA or both. |
|
13 |
|
|
14 |
This program is free software: you can redistribute it and/or modify |
|
15 |
it under the terms of the GNU General Public License as published by |
|
16 |
the Free Software Foundation, either version 3 of the License, or |
|
17 |
(at your option) any later version. |
|
18 |
|
|
19 |
This program is distributed in the hope that it will be useful, |
|
20 |
but WITHOUT ANY WARRANTY; without even the implied warranty of |
|
21 |
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
|
22 |
GNU General Public License for more details. |
|
23 |
|
|
24 |
You should have received a copy of the GNU General Public License |
|
25 |
along with this program. If not, see <https://www.gnu.org/licenses/>. |
|
26 |
|
|
27 |
I, Wojtek Kosior, thereby promise not to sue for violation of this file's |
|
28 |
licenses. Although I request that you do not make use of this code in a |
|
29 |
proprietary program, I am not going to enforce this in court. |
|
30 |
--> |
|
31 |
<html> |
|
32 |
<head> |
|
33 |
<meta charset="utf-8"/> |
|
34 |
<title>Haketilo popup</title> |
|
35 |
#LOADCSS html/reset.css |
|
36 |
#LOADCSS html/base.css |
|
37 |
#LOADCSS html/grid.css |
|
38 |
<style> |
|
39 |
#IF TEST |
|
40 |
html { |
|
41 |
background-color: #444; |
|
42 |
} |
|
43 |
#ENDIF |
|
44 |
|
|
45 |
html, body { |
|
46 |
width: 400px; |
|
47 |
overflow-x: hidden; |
|
48 |
overflow-y: auto; |
|
49 |
} |
|
50 |
|
|
51 |
#page_info_container { |
|
52 |
padding: 0.4em; |
|
53 |
} |
|
54 |
|
|
55 |
#info_form, #unprivileged_page_info { |
|
56 |
display: grid; |
|
57 |
grid-template-columns: auto; |
|
58 |
text-align: center; |
|
59 |
} |
|
60 |
|
|
61 |
#info_form * { |
|
62 |
white-space: nowrap; |
|
63 |
text-overflow: ellipsis; |
|
64 |
overflow-x: hidden; |
|
65 |
} |
|
66 |
|
|
67 |
#info_form label { |
|
68 |
padding-bottom: 0.2em; |
|
69 |
} |
|
70 |
#info_form label+span, .top_but_container { |
|
71 |
padding-bottom: 0.5em; |
|
72 |
} |
|
73 |
</style> |
|
74 |
</head> |
|
75 |
<body> |
|
76 |
<!-- It contains just templates, we can include it at the top --> |
|
77 |
#INCLUDE html/repo_query.html |
|
78 |
<div id="page_info_container"> |
|
79 |
<div id="loading_info"> |
|
80 |
Loading page info... |
|
81 |
</div> |
|
82 |
<div id="info_form" class="hide"> |
|
83 |
<label>Page URL:</label> |
|
84 |
<span id="page_url"></span> |
|
85 |
<label id="privileged_page_info" class="hide">Privileged page</label> |
|
86 |
<div id="unprivileged_page_info" class="hide"> |
|
87 |
<label>Scripts blocked:</label> |
|
88 |
<span id="scripts_blocked"></span> |
|
89 |
<label>Injected payload:</label> |
|
90 |
<span id="injected_payload"></span> |
|
91 |
<label>Mapping used:</label> |
|
92 |
<span id="mapping_used"></span> |
|
93 |
</div> |
|
94 |
</div> |
|
95 |
<div class="text_center top_but_container"> |
|
96 |
<button id="search_resources_but" class="hide"> |
|
97 |
Search for custom resources |
|
98 |
</button> |
|
99 |
</div> |
|
100 |
<div class="text_center"> |
|
101 |
<button id="settings_but"> |
|
102 |
Open settings |
|
103 |
</button> |
|
104 |
</div> |
|
105 |
</div> |
|
106 |
<div id="repo_query_container" class="hide"> |
|
107 |
<!-- Repo query view will be dynamically inserted here. --> |
|
108 |
</div> |
|
109 |
#LOADJS html/popup.js |
|
110 |
</body> |
|
111 |
</html> |
html/popup.js | ||
---|---|---|
1 |
/** |
|
2 |
* This file is part of Haketilo. |
|
3 |
* |
|
4 |
* Function: Show details of how Haketilo handled given page, drive popup. |
|
5 |
* |
|
6 |
* Copyright (C) 2021,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 |
#FROM common/browser.js IMPORT browser |
|
45 |
#FROM common/misc.js IMPORT is_privileged_url |
|
46 |
#FROM html/DOM_helpers.js IMPORT by_id |
|
47 |
#FROM html/repo_query.js IMPORT RepoQueryView |
|
48 |
|
|
49 |
const tab_query = {currentWindow: true, active: true}; |
|
50 |
|
|
51 |
async function get_current_tab() { |
|
52 |
#IF CHROMIUM |
|
53 |
const callback = (cb) => browser.tabs.query(tab_query, tab => cb(tab)); |
|
54 |
const promise = new Promise(callback); |
|
55 |
#ELIF MOZILLA |
|
56 |
const promise = browser.tabs.query(tab_query); |
|
57 |
#ENDIF |
|
58 |
|
|
59 |
try { |
|
60 |
return (await promise)[0]; |
|
61 |
} catch(e) { |
|
62 |
console.log(e); |
|
63 |
} |
|
64 |
} |
|
65 |
|
|
66 |
async function get_page_info(tab_id) { |
|
67 |
return browser.tabs.sendMessage(tab_id, ["page_info"]); |
|
68 |
} |
|
69 |
|
|
70 |
function show_page_info(page_info) { |
|
71 |
by_id("loading_info").remove(); |
|
72 |
by_id("info_form").classList.remove("hide"); |
|
73 |
by_id("page_url").innerText = page_info.url; |
|
74 |
|
|
75 |
if (page_info.privileged) { |
|
76 |
by_id("privileged_page_info").classList.remove("hide"); |
|
77 |
} else { |
|
78 |
by_id("unprivileged_page_info").classList.remove("hide"); |
|
79 |
|
|
80 |
by_id("scripts_blocked").innerText = page_info.allow ? "no" : "yes"; |
|
81 |
|
|
82 |
by_id("injected_payload").innerText = page_info.payload ? |
|
83 |
page_info.payload.identifier : "None"; |
|
84 |
|
|
85 |
const scripts_fate = page_info.allow ? "allowed" : "blocked"; |
|
86 |
|
|
87 |
if (page_info.mapping === "~allow") |
|
88 |
var mapping = `None (scripts ${scripts_fate} by a rule)`; |
|
89 |
else if (page_info.mapping) |
|
90 |
var mapping = page_info.mapping; |
|
91 |
else |
|
92 |
var mapping = `None (scripts ${scripts_fate} by default policy)`; |
|
93 |
by_id("mapping_used").innerText = mapping; |
|
94 |
} |
|
95 |
} |
|
96 |
|
|
97 |
function repo_query_showing(show) { |
|
98 |
for (const [id, i] of [["repo_query", 0], ["page_info", 1]]) |
|
99 |
by_id(`${id}_container`).classList[["add", "remove"][show ^ i]]("hide"); |
|
100 |
} |
|
101 |
|
|
102 |
function prepare_repo_query_view(tab_id, page_info) { |
|
103 |
const repo_query_view = new RepoQueryView(tab_id, |
|
104 |
() => repo_query_showing(true), |
|
105 |
() => repo_query_showing(false)); |
|
106 |
by_id("repo_query_container").prepend(repo_query_view.main_div); |
|
107 |
|
|
108 |
let search_cb = () => repo_query_view.show(page_info.url); |
|
109 |
search_cb = repo_query_view.when_hidden(search_cb); |
|
110 |
by_id("search_resources_but").addEventListener("click", search_cb); |
|
111 |
by_id("search_resources_but").classList.remove("hide"); |
|
112 |
} |
|
113 |
|
|
114 |
async function main() { |
|
115 |
const settings_opener = (e) => browser.runtime.openOptionsPage(); |
|
116 |
by_id("settings_but").addEventListener("click", settings_opener); |
|
117 |
|
|
118 |
try { |
|
119 |
var tab = await get_current_tab(); |
|
120 |
var tab_id = tab.id; |
|
121 |
|
|
122 |
if (is_privileged_url(tab.url)) |
|
123 |
var page_info = {privileged: true, url: tab.url}; |
|
124 |
else |
|
125 |
var page_info = await get_page_info(tab_id); |
|
126 |
} catch(e) { |
|
127 |
console.error(e); |
|
128 |
} |
|
129 |
|
|
130 |
if (page_info) { |
|
131 |
show_page_info(page_info); |
|
132 |
if (!page_info.privileged) |
|
133 |
prepare_repo_query_view(tab_id, page_info); |
|
134 |
} else { |
|
135 |
by_id("loading_info").innerText = |
|
136 |
"Page info not avaialable. Try reloading the page."; |
|
137 |
} |
|
138 |
} |
|
139 |
|
|
140 |
main(); |
html/repo_query.html | ||
---|---|---|
38 | 38 |
|
39 | 39 |
#LOADCSS html/reset.css |
40 | 40 |
#LOADCSS html/base.css |
41 |
#LOADCSS html/grid.css |
|
42 | 41 |
<style> |
43 | 42 |
.repo_query_top_text { |
44 | 43 |
text-align: center; |
45 |
margin: 0.4em; |
|
44 |
padding: 0.4em; |
|
45 |
text-overflow: ellipsis; |
|
46 |
overflow: hidden; |
|
46 | 47 |
} |
47 | 48 |
.repo_queried_url { |
48 | 49 |
text-decoration: underline; |
50 |
white-space: nowrap; |
|
49 | 51 |
} |
50 | 52 |
|
51 | 53 |
.repo_query_repo_li { |
... | ... | |
77 | 79 |
flex: 1 1 auto; |
78 | 80 |
min-width: 0; |
79 | 81 |
} |
82 |
|
|
80 | 83 |
.repo_query_entry_info > * { |
81 | 84 |
white-space: nowrap; |
82 | 85 |
overflow: hidden; |
83 | 86 |
text-overflow: ellipsis; |
87 |
padding-bottom: 0.1em; |
|
84 | 88 |
} |
85 | 89 |
.repo_query_entry button { |
86 | 90 |
white-space: nowrap; |
... | ... | |
97 | 101 |
} |
98 | 102 |
</style> |
99 | 103 |
<template> |
100 |
<div id="repo_query" data-template="main_div" |
|
101 |
class="grid_1 repo_query_main_div"> |
|
104 |
<div id="repo_query" data-template="main_div" class="repo_query_main_div"> |
|
102 | 105 |
<div data-template="repos_list_container"> |
103 | 106 |
<div class="repo_query_top_text"> |
104 |
Browsing custom resources for |
|
105 |
<span data-template="url_span" class="repo_queried_url"></span>.
|
|
107 |
Browsing custom resources for:
|
|
108 |
<span data-template="url_span" class="repo_queried_url"></span> |
|
106 | 109 |
</div> |
107 | 110 |
<ul data-template="repos_list"></ul> |
108 | 111 |
<div class="repo_query_bottom_buttons"> |
html/repo_query.js | ||
---|---|---|
118 | 118 |
return; |
119 | 119 |
} |
120 | 120 |
|
121 |
this.info_span.remove(); |
|
122 |
this.results_list.classList.remove("hide"); |
|
123 |
|
|
124 | 121 |
this.result_entries = results.map(ref => new ResultEntry(this, ref)); |
125 | 122 |
|
126 |
const to_append = this.result_entries.length > 0 ?
|
|
127 |
this.result_entries.map(re => re.main_li) :
|
|
128 |
["No results :("];
|
|
123 |
if (this.result_entries.length > 0) {
|
|
124 |
this.results_list.classList.remove("hide");
|
|
125 |
this.info_span.remove();
|
|
129 | 126 |
|
130 |
this.results_list.append(...to_append); |
|
127 |
const to_append = this.result_entries.map(re => re.main_li); |
|
128 |
this.results_list.append(...to_append); |
|
129 |
} else { |
|
130 |
this.info_span.innerText = "No results :("; |
|
131 |
} |
|
131 | 132 |
} |
132 | 133 |
|
133 | 134 |
let show_results = () => { |
test/extension_crafting.py | ||
---|---|---|
63 | 63 |
], |
64 | 64 |
'content_security_policy': "object-src 'none'; script-src 'self' https://serve.scrip.ts;", |
65 | 65 |
'web_accessible_resources': ['testpage.html'], |
66 |
'options_ui': { |
|
67 |
'page': 'testpage.html', |
|
68 |
'open_in_tab': True |
|
69 |
}, |
|
66 | 70 |
'background': { |
67 | 71 |
'persistent': True, |
68 | 72 |
'scripts': ['__open_test_page.js', 'background.js'] |
test/unit/conftest.py | ||
---|---|---|
59 | 59 |
nav_target = request.node.get_closest_marker('get_page') |
60 | 60 |
close_all_but_one_window(_driver) |
61 | 61 |
_driver.get(nav_target.args[0] if nav_target else 'about:blank') |
62 |
_driver.implicitly_wait(0) |
|
62 | 63 |
yield _driver |
63 | 64 |
|
64 | 65 |
@pytest.fixture() |
test/unit/test_popup.py | ||
---|---|---|
1 |
# SPDX-License-Identifier: CC0-1.0 |
|
2 |
|
|
3 |
""" |
|
4 |
Haketilo unit tests - repository querying |
|
5 |
""" |
|
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 |
import pytest |
|
21 |
import json |
|
22 |
from selenium.webdriver.support.ui import WebDriverWait |
|
23 |
|
|
24 |
from ..extension_crafting import ExtraHTML |
|
25 |
from ..script_loader import load_script |
|
26 |
from .utils import * |
|
27 |
|
|
28 |
def reload_with_target(driver, target): |
|
29 |
current_url = driver.execute_script('return location.href') |
|
30 |
driver.execute_script( |
|
31 |
''' |
|
32 |
window.location.href = arguments[0]; |
|
33 |
window.location.reload(); |
|
34 |
''', |
|
35 |
f'{current_url}#{target}') |
|
36 |
|
|
37 |
unprivileged_page_info = { |
|
38 |
'url': 'https://example_a.com/something', |
|
39 |
'allow': False |
|
40 |
} |
|
41 |
|
|
42 |
mocked_page_infos = { |
|
43 |
'privileged': { |
|
44 |
'url': 'moz-extension://<some-id>/file.html', |
|
45 |
'privileged': True |
|
46 |
}, |
|
47 |
'blocked_default': unprivileged_page_info, |
|
48 |
'allowed_default': { |
|
49 |
**unprivileged_page_info, |
|
50 |
'allow': True |
|
51 |
}, |
|
52 |
'blocked_rule': { |
|
53 |
**unprivileged_page_info, |
|
54 |
'mapping': '~allow' |
|
55 |
}, |
|
56 |
'allowed_rule': { |
|
57 |
**unprivileged_page_info, |
|
58 |
'allow': True, |
|
59 |
'mapping': '~allow' |
|
60 |
}, |
|
61 |
'mapping': { |
|
62 |
**unprivileged_page_info, |
|
63 |
'mapping': 'm1', |
|
64 |
'payload': {'identifier': 'res1'} |
|
65 |
} |
|
66 |
} |
|
67 |
|
|
68 |
tab_mock_js = ''' |
|
69 |
; |
|
70 |
const mocked_page_info = (%s)[/#mock_page_info-(.*)$/.exec(document.URL)[1]]; |
|
71 |
browser.tabs.sendMessage = async function(tab_id, msg) { |
|
72 |
const this_tab_id = (await browser.tabs.getCurrent()).id; |
|
73 |
if (tab_id !== this_tab_id) |
|
74 |
throw `not current tab id (${tab_id} instead of ${this_tab_id})`; |
|
75 |
|
|
76 |
if (msg[0] === "page_info") { |
|
77 |
return mocked_page_info; |
|
78 |
} else if (msg[0] === "repo_query") { |
|
79 |
const response = await fetch(msg[1]); |
|
80 |
if (!response) |
|
81 |
return {error: "Something happened :o"}; |
|
82 |
|
|
83 |
const result = {ok: response.ok, status: response.status}; |
|
84 |
try { |
|
85 |
result.json = await response.json(); |
|
86 |
} catch(e) { |
|
87 |
result.error_json = "" + e; |
|
88 |
} |
|
89 |
return result; |
|
90 |
} else { |
|
91 |
throw `bad sendMessage message type: '${msg[0]}'`; |
|
92 |
} |
|
93 |
} |
|
94 |
|
|
95 |
const old_tabs_query = browser.tabs.query; |
|
96 |
browser.tabs.query = async function(query) { |
|
97 |
const tabs = await old_tabs_query(query); |
|
98 |
tabs.forEach(t => t.url = mocked_page_info.url); |
|
99 |
return tabs; |
|
100 |
} |
|
101 |
''' % json.dumps(mocked_page_infos) |
|
102 |
|
|
103 |
popup_ext_data = { |
|
104 |
'background_script': broker_js, |
|
105 |
'extra_html': ExtraHTML( |
|
106 |
'html/popup.html', |
|
107 |
{ |
|
108 |
'common/browser.js': tab_mock_js, |
|
109 |
'common/indexeddb.js': '; set_repo("https://hydril.la/");' |
|
110 |
}, |
|
111 |
wrap_into_htmldoc=False |
|
112 |
), |
|
113 |
'navigate_to': 'html/popup.html' |
|
114 |
} |
|
115 |
|
|
116 |
@pytest.mark.ext_data(popup_ext_data) |
|
117 |
@pytest.mark.usefixtures('webextension') |
|
118 |
@pytest.mark.parametrize('page_info_key', ['', *mocked_page_infos.keys()]) |
|
119 |
def test_popup_display(driver, execute_in_page, page_info_key): |
|
120 |
""" |
|
121 |
Test popup viewing while on a page. Test parametrized with different |
|
122 |
possible values of page_info object passed in message from the content |
|
123 |
script. |
|
124 |
""" |
|
125 |
reload_with_target(driver, f'mock_page_info-{page_info_key}') |
|
126 |
|
|
127 |
by_id = driver.execute_script(''' |
|
128 |
const nodes = [...document.querySelectorAll("[id]")]; |
|
129 |
return nodes.reduce((ob, node) => Object.assign(ob, {[node.id]: node}), {}); |
|
130 |
'''); |
|
131 |
|
|
132 |
if page_info_key == '': |
|
133 |
error_msg = 'Page info not avaialable. Try reloading the page.' |
|
134 |
error_msg_shown = lambda d: by_id['loading_info'].text == error_msg |
|
135 |
WebDriverWait(driver, 10).until(error_msg_shown) |
|
136 |
return |
|
137 |
|
|
138 |
WebDriverWait(driver, 10).until(lambda d: by_id['info_form'].is_displayed()) |
|
139 |
assert (page_info_key == 'privileged') == \ |
|
140 |
by_id['privileged_page_info'].is_displayed() |
|
141 |
assert (page_info_key == 'privileged') ^ \ |
|
142 |
by_id['unprivileged_page_info'].is_displayed() |
|
143 |
assert by_id['page_url'].text == mocked_page_infos[page_info_key]['url'] |
|
144 |
assert not by_id['repo_query_container'].is_displayed() |
|
145 |
|
|
146 |
if 'blocked' in page_info_key or page_info_key == 'mapping': |
|
147 |
assert by_id['scripts_blocked'].text.lower() == 'yes' |
|
148 |
elif 'allowed' in page_info_key: |
|
149 |
assert by_id['scripts_blocked'].text.lower() == 'no' |
|
150 |
|
|
151 |
if page_info_key == 'mapping': |
|
152 |
assert by_id['injected_payload'].text == 'res1' |
|
153 |
elif page_info_key != 'privileged': |
|
154 |
assert by_id['injected_payload'].text == 'None' |
|
155 |
|
|
156 |
mapping_text = by_id['mapping_used'].text |
|
157 |
if page_info_key == 'mapping': |
|
158 |
assert mapping_text == 'm1' |
|
159 |
|
|
160 |
if 'allowed' in page_info_key: |
|
161 |
'None (scripts allowed by' in mapping_text |
|
162 |
elif 'blocked' in page_info_key: |
|
163 |
'None (scripts blocked by' in mapping_text |
|
164 |
|
|
165 |
if 'rule' in page_info_key: |
|
166 |
'by a rule)' in mapping_text |
|
167 |
elif 'default' in page_info_key: |
|
168 |
'by default_policy)' in mapping_text |
|
169 |
|
|
170 |
@pytest.mark.ext_data(popup_ext_data) |
|
171 |
@pytest.mark.usefixtures('webextension') |
|
172 |
def test_popup_repo_query(driver, execute_in_page): |
|
173 |
""" |
|
174 |
Test opening and closing the repo query view in popup. |
|
175 |
""" |
|
176 |
reload_with_target(driver, f'mock_page_info-blocked_rule') |
|
177 |
|
|
178 |
search_but = driver.find_element_by_id("search_resources_but") |
|
179 |
WebDriverWait(driver, 10).until(lambda d: search_but.is_displayed()) |
|
180 |
search_but.click() |
|
181 |
containers = dict([(name, driver.find_element_by_id(f'{name}_container')) |
|
182 |
for name in ('page_info', 'repo_query')]) |
|
183 |
assert not containers['page_info'].is_displayed() |
|
184 |
assert containers['repo_query'].is_displayed() |
|
185 |
shown = lambda d: 'https://hydril.la/' in containers['repo_query'].text |
|
186 |
WebDriverWait(driver, 10).until(shown) |
|
187 |
|
|
188 |
# Click the "Show results" button. |
|
189 |
selector = '.repo_query_buttons > button:first-child' |
|
190 |
driver.find_element_by_css_selector(selector).click() |
|
191 |
shown = lambda d: 'MAPPING_A' in containers['repo_query'].text |
|
192 |
WebDriverWait(driver, 10).until(shown) |
|
193 |
|
|
194 |
# Click the "Cancel" button |
|
195 |
selector = '.repo_query_bottom_buttons > button' |
|
196 |
driver.find_element_by_css_selector(selector).click() |
|
197 |
assert containers['page_info'].is_displayed() |
|
198 |
assert not containers['repo_query'].is_displayed() |
|
199 |
|
|
200 |
@pytest.mark.ext_data(popup_ext_data) |
|
201 |
@pytest.mark.usefixtures('webextension') |
|
202 |
def test_popup_settings_opening(driver, execute_in_page): |
|
203 |
""" |
|
204 |
Test opening the settings page from popup through button click. |
|
205 |
""" |
|
206 |
driver.find_element_by_id("settings_but").click() |
|
207 |
|
|
208 |
first_handle = driver.current_window_handle |
|
209 |
WebDriverWait(driver, 10).until(lambda d: len(d.window_handles) == 2) |
|
210 |
new_handle = [h for h in driver.window_handles if h != first_handle][0] |
|
211 |
|
|
212 |
driver.switch_to.window(new_handle) |
|
213 |
driver.implicitly_wait(10) |
|
214 |
assert "Extension's options page for testing" in \ |
|
215 |
driver.find_element_by_tag_name("h1").text |
test/unit/test_repo_query.py | ||
---|---|---|
1 | 1 |
# SPDX-License-Identifier: CC0-1.0 |
2 | 2 |
|
3 | 3 |
""" |
4 |
Haketilo unit tests - .............
|
|
4 |
Haketilo unit tests - repository querying
|
|
5 | 5 |
""" |
6 | 6 |
|
7 | 7 |
# This file is part of Haketilo |
... | ... | |
18 | 18 |
# CC0 1.0 Universal License for more details. |
19 | 19 |
|
20 | 20 |
import pytest |
21 |
import json |
|
22 | 21 |
from selenium.webdriver.support.ui import WebDriverWait |
23 | 22 |
|
24 |
from ..extension_crafting import ExtraHTML |
|
25 |
from ..script_loader import load_script |
|
26 |
|
|
27 | 23 |
from ..extension_crafting import ExtraHTML |
28 | 24 |
from ..script_loader import load_script |
29 | 25 |
from .utils import * |
... | ... | |
172 | 168 |
show_and_wait_for_repo_entry() |
173 | 169 |
|
174 | 170 |
elem = execute_in_page('returnval(view.url_span.parentNode);') |
175 |
assert has_msg(f'Browsing custom resources for {queried_url}.', elem)(0)
|
|
171 |
assert has_msg(f'Browsing custom resources for: {queried_url}', elem)(0)
|
|
176 | 172 |
elif message == 'no_repos': |
177 | 173 |
setup_view(execute_in_page, []) |
178 | 174 |
show_and_wait_for_repo_entry() |
... | ... | |
272 | 268 |
''') |
273 | 269 |
show_and_wait_for_repo_entry() |
274 | 270 |
|
275 |
elem = execute_in_page('returnval(view.repo_entries[0].results_list);')
|
|
271 |
elem = execute_in_page('returnval(view.repo_entries[0].info_span);')
|
|
276 | 272 |
WebDriverWait(driver, 10).until(has_msg('No results :(', elem)) |
277 | 273 |
else: |
278 | 274 |
raise Exception('made a typo in test function params?') |
Also available in: Unified diff
add new extension's popup page