# 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 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'] } resource_schema_id, mapping_schema_id = [ f'https://hydrilla.koszko.org/schemas/api_{t}_description-1.0.1.schema.json' for t in ('resource', 'mapping') ] 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'] == [{ '$schema': resource_schema_id, 'source_name': localid, 'source_copyright': [], 'type': 'resource', 'identifier': localid, 'uuid': db_contents['resource'][0]['uuid'], 'version': [1], 'revision': 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'] == [{ '$schema': mapping_schema_id, '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 <a> 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