From fd9f2fc4783cc606734e61116185c032a63d54a0 Mon Sep 17 00:00:00 2001 From: Wojtek Kosior Date: Wed, 16 Feb 2022 22:01:38 +0100 Subject: fix out-of-source builds --- test/haketilo_test/unit/test_payload_create.py | 248 +++++++++++++++++++++++++ 1 file changed, 248 insertions(+) create mode 100644 test/haketilo_test/unit/test_payload_create.py (limited to 'test/haketilo_test/unit/test_payload_create.py') diff --git a/test/haketilo_test/unit/test_payload_create.py b/test/haketilo_test/unit/test_payload_create.py new file mode 100644 index 0000000..9689c37 --- /dev/null +++ b/test/haketilo_test/unit/test_payload_create.py @@ -0,0 +1,248 @@ +# 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 +# +# 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 hashlib import sha256 + +from selenium.webdriver.support.ui import WebDriverWait + +from ..extension_crafting import ExtraHTML +from ..script_loader import load_script +from .utils import * + +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) + return form_data + +cleared_form_inputs = { + 'identifier': '', + 'long_name': '', + 'description': '', + 'patterns': 'https://example.com/***', + 'script': 'console.log("Hello, World!");' +} +def assert_form_contents(execute_in_page, inputs=cleared_form_inputs): + inputs_keys = [*inputs.keys()] + values = execute_in_page( + 'returnval(arguments[0].map(i => form_ctx[i].value));', + inputs_keys + ) + for key, value in zip(inputs_keys, values): + assert inputs[key] == value + +@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_normal_usage(driver, execute_in_page): + """ + A test case of normal usage of simple payload creation form. + """ + execute_in_page(load_script('html/payload_create.js')) + + create_but, form_container, dialog_container = execute_in_page( + ''' + const form_ctx = payload_create_form(); + document.body.append(form_ctx.main_div); + returnval([form_ctx.create_but, form_ctx.form_container, + form_ctx.dialog_container]); + ''') + + assert patterns_doc_url == \ + driver.find_element_by_link_text('URL patterns').get_attribute('href') + + assert form_container.is_displayed() + assert not dialog_container.is_displayed() + + assert_form_contents(execute_in_page) + + form_data = fill_form_with_sample_data(execute_in_page) + + create_but.click() + + assert not form_container.is_displayed() + assert dialog_container.is_displayed() + + def success_reported(driver): + return 'Successfully saved payload' in dialog_container.text + + WebDriverWait(driver, 10).until(success_reported) + execute_in_page('form_ctx.dialog_ctx.ok_but.click();') + + assert form_container.is_displayed() + assert not dialog_container.is_displayed() + + def assert_db_contents(): + db_contents = get_db_contents(execute_in_page) + + assert uuidv4_re.match(db_contents['resource'][0]['uuid']) + + localid = f'local-{form_data["identifier"]}' + long_name = form_data['long_name'] or form_data['identifier'] + payloads = dict([(pat, {'identifier': localid}) + for pat in form_data['patterns'].split('\n') if pat]) + + assert db_contents['resource'] == [{ + 'source_name': localid, + 'source_copyright': [], + 'type': 'resource', + 'identifier': localid, + 'uuid': db_contents['resource'][0]['uuid'], + 'version': [1], + 'description': form_data['description'], + 'dependencies': [], + 'long_name': long_name, + 'scripts': [{ + 'file': 'payload.js', + 'sha256': sha256(form_data['script'].encode()).digest().hex() + }] + }] + + assert uuidv4_re.match(db_contents['mapping'][0]['uuid']) + assert db_contents['mapping'] == [{ + 'source_name': localid, + 'source_copyright': [], + 'type': 'mapping', + 'identifier': localid, + 'uuid': db_contents['mapping'][0]['uuid'], + 'version': [1], + 'description': form_data['description'], + 'long_name': long_name, + 'payloads': payloads + }] + + assert_db_contents() + + form_data = fill_form_with_sample_data(execute_in_page, { + 'long_name': '', + 'description': 'bam bam bam', + 'patterns': 'https://new.example.com/***', + 'script': sample_files['bye.js']['contents'] + }) + + create_but.click() + + for type in ('Resource', 'Mapping'): + def override_asked(driver): + return f"{type} 'local-someid' already exists. Override?" \ + in dialog_container.text + WebDriverWait(driver, 10).until(override_asked) + execute_in_page('form_ctx.dialog_ctx.yes_but.click();') + + assert_db_contents() + +@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_errors(driver, execute_in_page): + """ + A test case of various error the simple payload form might show. + """ + execute_in_page(load_script('html/payload_create.js')) + + create_but, dialog_container = execute_in_page( + ''' + const form_ctx = payload_create_form(); + document.body.append(form_ctx.main_div); + returnval([form_ctx.create_but, form_ctx.dialog_container]); + ''') + + for data_override, expected_msg in [ + ({'identifier': ''}, "The 'identifier' field is required!"), + ({'identifier': ':('}, 'Identifier may only contain '), + ({'script': ''}, "The 'script' field is required!"), + ({'patterns': ''}, "The 'URL patterns' field is required!"), + ({'patterns': ':d'}, "':d' is not a valid URL pattern. See here for more details."), + ({'patterns': '\n'.join(['http://example.com'] * 2)}, + "Pattern 'http://example.com' specified multiple times!") + ]: + # Attempt creating the payload + form_data = fill_form_with_sample_data(execute_in_page, data_override) + create_but.click() + # Verify the error message + assert expected_msg in dialog_container.text + + # Verify patterns documentation link. + if expected_msg == {'patterns': ':d'}: + doc_link_elem = driver.find_element_by_link_text('here') + assert doc_link.get_attribute('href') == patterns_doc_url + + # Verify the form was NOT cleared upon failed saving. + execute_in_page('form_ctx.dialog_ctx.ok_but.click();') + assert_form_contents(execute_in_page, form_data) + + # Add a sample item and attempt overriding it. + fill_form_with_sample_data(execute_in_page) + create_but.click() + WebDriverWait(driver, 10).until(lambda _: 'Succes' in dialog_container.text) + execute_in_page('form_ctx.dialog_ctx.ok_but.click();') + + # Verify that denying override leads to saving failure. + form_data = fill_form_with_sample_data(execute_in_page) + create_but.click() + WebDriverWait(driver, 10).until(lambda _: 'Overri' in dialog_container.text) + execute_in_page('form_ctx.dialog_ctx.no_but.click();') + assert 'Failed to save payload :(' in dialog_container.text + execute_in_page('form_ctx.dialog_ctx.ok_but.click();') + assert_form_contents(execute_in_page, form_data) + + # Verify that IndexedDB errors get caught and reported as saving failures. + execute_in_page('haketilodb.get = async () => {throw "someerror";}') + form_data = fill_form_with_sample_data(execute_in_page, {'identifier': 'o'}) + create_but.click() + WebDriverWait(driver, 10).until(lambda _: 'Failed' in dialog_container.text) + execute_in_page('form_ctx.dialog_ctx.ok_but.click();') + assert_form_contents(execute_in_page, form_data) + + # Verify that the loading message gets shown during IndexedDB operations. + execute_in_page('haketilodb.get = () => new Promise(cb => null);') + create_but.click() + assert 'Saving payload...' in dialog_container.text -- cgit v1.2.3