aboutsummaryrefslogtreecommitdiff
path: root/test
diff options
context:
space:
mode:
authorWojtek Kosior <koszko@koszko.org>2022-01-10 23:38:56 +0100
committerWojtek Kosior <koszko@koszko.org>2022-01-10 23:38:56 +0100
commit19304cd1ae4e4ba4f6dcf4f1db14de1e4e70c250 (patch)
tree2e7e6f904ad16f9402827a7bc215a419de5c2656 /test
parent38650a8102fe0841617cd80f3a6e45b1f5f62fd5 (diff)
downloadbrowser-extension-19304cd1ae4e4ba4f6dcf4f1db14de1e4e70c250.tar.gz
browser-extension-19304cd1ae4e4ba4f6dcf4f1db14de1e4e70c250.zip
improve item list styling; add payload creation form; exend dialog mechanism
Diffstat (limited to 'test')
-rw-r--r--test/unit/test_dialog.py45
-rw-r--r--test/unit/test_indexeddb.py46
-rw-r--r--test/unit/test_item_list.py42
-rw-r--r--test/unit/test_payload_create.py121
-rw-r--r--test/unit/utils.py53
5 files changed, 226 insertions, 81 deletions
diff --git a/test/unit/test_dialog.py b/test/unit/test_dialog.py
index 384a889..63af79e 100644
--- a/test/unit/test_dialog.py
+++ b/test/unit/test_dialog.py
@@ -29,31 +29,35 @@ from ..script_loader import load_script
@pytest.mark.usefixtures('webextension')
def test_dialog_show_close(driver, execute_in_page):
"""
- A test case of basing dialog showing/closing.
+ A test case of basic dialog showing/closing.
"""
execute_in_page(load_script('html/dialog.js'))
- execute_in_page(
+ buts = execute_in_page(
'''
let cb_calls, call_prom;
const dialog_context = make(() => cb_calls.push("show"),
() => cb_calls.push("hide"));
document.body.append(dialog_context.main_div);
+ const buts = {};
+ for (const but of document.getElementsByTagName("button"))
+ buts[but.textContent] = but;
+ returnval(buts);
''')
- buts = driver.find_elements_by_tag_name('button')
- buts = dict([(but.text, but) for but in buts])
-
- for i, (dialog_function, button_text, expected_result) in enumerate([
- ('info', 'Ok', None),
- ('error', 'Ok', None),
- ('ask', 'Yes', True),
- ('ask', 'No', False)
+ for i, (dialog_function, but_text, hidden, expected_result) in enumerate([
+ ('info', 'Ok', ['Yes', 'No'], None),
+ ('error', 'Ok', ['Yes', 'No'], None),
+ ('error', None, ['Yes', 'No'], None),
+ ('loader', None, ['Yes', 'No', 'Ok'], None),
+ ('ask', 'Yes', ['Ok'], True),
+ ('ask', None, ['Ok'], None),
+ ('ask', 'No', ['Ok'], False)
]):
cb_calls, is_shown = execute_in_page(
f'''
cb_calls = [];
call_prom = {dialog_function}(dialog_context,
- `sample_text_${{arguments[0]}}`);
+ `sample_text_${{arguments[0]}}`);
returnval([cb_calls, dialog_context.shown]);
''',
i)
@@ -64,14 +68,23 @@ def test_dialog_show_close(driver, execute_in_page):
assert f'sample_text_{i}' in page_source
assert f'sample_text_{i - 1}' not in page_source
- assert any([not but.is_displayed() for but in buts.values()])
-
- assert buts[button_text].is_displayed()
- buts[button_text].click()
+ # Verify the right buttons are displayed.
+ for text, but in buts.items():
+ if text in hidden:
+ assert not but.is_displayed()
+ # Verify clicking a hidden button does nothing.
+ execute_in_page('buts[arguments[0]].click();', text)
+ assert execute_in_page('returnval(cb_calls);') == cb_calls
+ else:
+ assert but.is_displayed()
+
+ if but_text is None:
+ execute_in_page('close_dialog(dialog_context);')
+ else:
+ buts[but_text].click()
cb_calls, result, is_shown = execute_in_page(
'''{
- console.error(dialog_context.msg.textContent);
const values_cb = r => [cb_calls, r, dialog_context.shown];
returnval(call_prom.then(values_cb));
}''')
diff --git a/test/unit/test_indexeddb.py b/test/unit/test_indexeddb.py
index 9dfbe63..0c0e7a0 100644
--- a/test/unit/test_indexeddb.py
+++ b/test/unit/test_indexeddb.py
@@ -6,7 +6,7 @@ Haketilo unit tests - IndexedDB access
# This file is part of Haketilo
#
-# Copyright (C) 2021, Wojtek Kosior <koszko@koszko.org>
+# Copyright (C) 2021,2022 Wojtek Kosior <koszko@koszko.org>
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the CC0 1.0 Universal License as published by
@@ -25,7 +25,7 @@ from selenium.webdriver.support import expected_conditions as EC
from selenium.common.exceptions import WebDriverException
from ..script_loader import load_script
-from .utils import sample_files, sample_files_by_hash, sample_file_ref
+from .utils import *
indexeddb_js = lambda: load_script('common/indexeddb.js')
broker_js = lambda: load_script('background/broadcast_broker.js') + ';start();'
@@ -54,48 +54,6 @@ def make_sample_mapping():
'identifier': 'helloapple'
}
-def clear_indexeddb(execute_in_page):
- execute_in_page(
- '''{
- async function delete_db() {
- if (db) {
- db.close();
- db = null;
- }
- let resolve, reject;
- const result = new Promise((...cbs) => [resolve, reject] = cbs);
- const request = indexedDB.deleteDatabase("haketilo");
- [request.onsuccess, request.onerror] = [resolve, reject];
- await result;
- }
-
- returnval(delete_db());
- }'''
- )
-
-def get_db_contents(execute_in_page):
- # Facilitate retrieving all IndexedDB contents.
- return execute_in_page(
- '''{
- async function get_database_contents()
- {
- const db = await get_db();
-
- const transaction = db.transaction(db.objectStoreNames);
- const store_names_reqs = [...db.objectStoreNames]
- .map(sn => [sn, transaction.objectStore(sn).getAll()])
-
- const promises = store_names_reqs
- .map(([_, req]) => wait_request(req));
- await Promise.all(promises);
-
- const result = {};
- store_names_reqs.forEach(([sn, req]) => result[sn] = req.result);
- return result;
- }
- returnval(get_database_contents());
- }''')
-
def mock_broadcast(execute_in_page):
execute_in_page(
'''{
diff --git a/test/unit/test_item_list.py b/test/unit/test_item_list.py
index e2e1af8..62ec84e 100644
--- a/test/unit/test_item_list.py
+++ b/test/unit/test_item_list.py
@@ -26,6 +26,27 @@ from .utils import *
broker_js = lambda: load_script('background/broadcast_broker.js') + ';start();'
+def make_sample_resource(identifier, long_name):
+ return {
+ 'source_name': 'hello',
+ 'source_copyright': [
+ sample_file_ref('report.spdx'),
+ sample_file_ref('LICENSES/CC0-1.0.txt')
+ ],
+ 'type': 'resource',
+ 'identifier': identifier,
+ 'long_name': long_name,
+ 'uuid': 'a6754dcb-58d8-4b7a-a245-24fd7ad4cd68',
+ 'version': [2021, 11, 10],
+ 'revision': 1,
+ 'description': 'greets an apple',
+ 'dependencies': ['hello-message'],
+ 'scripts': [
+ sample_file_ref('hello.js'),
+ sample_file_ref('bye.js')
+ ]
+ }
+
def make_sample_mapping(identifier, long_name):
return {
'source_name': 'example-org-fixes-new',
@@ -49,27 +70,6 @@ def make_sample_mapping(identifier, long_name):
}
}
-def make_sample_resource(identifier, long_name):
- return {
- 'source_name': 'hello',
- 'source_copyright': [
- sample_file_ref('report.spdx'),
- sample_file_ref('LICENSES/CC0-1.0.txt')
- ],
- 'type': 'resource',
- 'identifier': identifier,
- 'long_name': long_name,
- 'uuid': 'a6754dcb-58d8-4b7a-a245-24fd7ad4cd68',
- 'version': [2021, 11, 10],
- 'revision': 1,
- 'description': 'greets an apple',
- 'dependencies': ['hello-message'],
- 'scripts': [
- sample_file_ref('hello.js'),
- sample_file_ref('bye.js')
- ]
- }
-
def make_item(item_type, *args):
return make_sample_resource(*args) if item_type == 'resource' \
else make_sample_mapping(*args)
diff --git a/test/unit/test_payload_create.py b/test/unit/test_payload_create.py
new file mode 100644
index 0000000..cd08d43
--- /dev/null
+++ b/test/unit/test_payload_create.py
@@ -0,0 +1,121 @@
+# SPDX-License-Identifier: CC0-1.0
+
+"""
+Haketilo unit tests - using a form to create simple site payload
+"""
+
+# This file is part of Haketilo
+#
+# Copyright (C) 2022 Wojtek Kosior <koszko@koszko.org>
+#
+# This program is free software: you can redistribute it and/or modify
+# it under the terms of the CC0 1.0 Universal License as published by
+# the Creative Commons Corporation.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# CC0 1.0 Universal License for more details.
+
+import pytest
+import re
+from selenium.webdriver.support.ui import WebDriverWait
+
+from ..extension_crafting import ExtraHTML
+from ..script_loader import load_script
+from .utils import clear_indexeddb, get_db_contents, sample_files
+
+broker_js = lambda: load_script('background/broadcast_broker.js') + ';start();'
+
+uuidv4_re = re.compile(
+ r'^[0-9A-F]{8}-[0-9A-F]{4}-4[0-9A-F]{3}-[89AB][0-9A-F]{3}-[0-9A-F]{12}$',
+ re.IGNORECASE
+)
+
+sample_patterns = '''
+http://example.com/***
+
+https://*.example.org/**'''
+
+sample_form_data = {
+ 'identifier': 'someid',
+ 'long_name': 'Some Name',
+ 'description': 'blah blah blah',
+ 'patterns': sample_patterns,
+ 'script': sample_files['hello.js']['contents']
+}
+
+def fill_form_with_sample_data(execute_in_page, sample_data_override={},
+ form_ctx='form_ctx'):
+ form_data = sample_form_data.copy()
+ form_data.update(sample_data_override)
+ execute_in_page(
+ f'''
+ for (const [key, value] of Object.entries(arguments[0]))
+ {form_ctx}[key].value = value;
+ ''',
+ form_data)
+
+@pytest.mark.ext_data({
+ 'background_script': broker_js,
+ 'extra_html': ExtraHTML('html/payload_create.html', {}),
+ 'navigate_to': 'html/payload_create.html'
+})
+@pytest.mark.usefixtures('webextension')
+def test_payload_create(driver, execute_in_page):
+ """
+ A test case of creating a simple payload using a form.
+ """
+ clear_indexeddb(execute_in_page)
+ execute_in_page(load_script('html/payload_create.js'))
+
+ create_but, main_div = execute_in_page(
+ '''
+ const form_ctx = payload_create_form();
+ document.body.append(form_ctx.main_div);
+ returnval([form_ctx.create_but, form_ctx.main_div]);
+ ''')
+
+ fill_form_with_sample_data(execute_in_page)
+
+ create_but.click()
+
+ def success_reported(driver):
+ return 'Successfully saved payload' in main_div.text
+
+ WebDriverWait(driver, 10).until(success_reported)
+
+ db_contents = get_db_contents(execute_in_page)
+
+ assert uuidv4_re.match(db_contents['resources'][0]['uuid'])
+ assert db_contents['resources'] == [{
+ 'source_name': 'local-someid',
+ 'source_copyright': [],
+ 'type': 'resource',
+ 'identifier': 'local-someid',
+ 'long_name': 'Some Name',
+ 'uuid': db_contents['resources'][0]['uuid'],
+ 'version': [1],
+ 'description': 'blah blah blah',
+ 'dependencies': [],
+ 'scripts': [{
+ 'file': 'payload.js',
+ 'hash_key': sample_files['hello.js']['hash_key']
+ }]
+ }]
+
+ assert uuidv4_re.match(db_contents['mappings'][0]['uuid'])
+ assert db_contents['mappings'] == [{
+ 'source_name': 'local-someid',
+ 'source_copyright': [],
+ 'type': 'mapping',
+ 'identifier': 'local-someid',
+ 'long_name': 'Some Name',
+ 'uuid': db_contents['mappings'][0]['uuid'],
+ 'version': [1],
+ 'description': 'blah blah blah',
+ 'payloads': {
+ 'http://example.com/***': {'identifier': 'local-someid'},
+ 'https://*.example.org/**': {'identifier': 'local-someid'}
+ }
+ }]
diff --git a/test/unit/utils.py b/test/unit/utils.py
index b6b389f..a61e215 100644
--- a/test/unit/utils.py
+++ b/test/unit/utils.py
@@ -66,3 +66,56 @@ def sample_data_dict(items):
"""
return dict([(it['identifier'], {item_version_string(it): it})
for it in items])
+
+def clear_indexeddb(execute_in_page):
+ """
+ Remove Haketilo data from IndexedDB. If variables from common/indexeddb.js
+ are in the global scope, this function will handle closing the opened
+ database instance (if any). Otherwise, the caller is responsible for making
+ sure the database being deleted is not opened anywhere.
+ """
+ execute_in_page(
+ '''{
+ async function delete_db() {
+ if (typeof db !== "undefined" && db) {
+ db.close();
+ db = null;
+ }
+ let resolve, reject;
+ const result = new Promise((...cbs) => [resolve, reject] = cbs);
+ const request = indexedDB.deleteDatabase("haketilo");
+ [request.onsuccess, request.onerror] = [resolve, reject];
+ await result;
+ }
+
+ returnval(delete_db());
+ }'''
+ )
+
+def get_db_contents(execute_in_page):
+ """
+ Retrieve all IndexedDB contents. It is expected that either variables from
+ common/indexeddb.js are in the global scope or common/indexeddb.js is
+ imported as haketilodb.
+ """
+ return execute_in_page(
+ '''{
+ async function get_database_contents()
+ {
+ const db_getter =
+ typeof haketilodb === "undefined" ? get_db : haketilodb.get;
+ const db = await db_getter();
+
+ const transaction = db.transaction(db.objectStoreNames);
+ const result = {};
+
+ for (const store_name of db.objectStoreNames) {
+ const req = transaction.objectStore(store_name).getAll();
+ await new Promise(cb => req.onsuccess = cb);
+ result[store_name] = req.result;
+ }
+
+ return result;
+ }
+ returnval(get_database_contents());
+ }''')