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_item_list.py | 280 ++++++++++++++++++++++++++++++ 1 file changed, 280 insertions(+) create mode 100644 test/haketilo_test/unit/test_item_list.py (limited to 'test/haketilo_test/unit/test_item_list.py') diff --git a/test/haketilo_test/unit/test_item_list.py b/test/haketilo_test/unit/test_item_list.py new file mode 100644 index 0000000..35ed1d5 --- /dev/null +++ b/test/haketilo_test/unit/test_item_list.py @@ -0,0 +1,280 @@ +# SPDX-License-Identifier: CC0-1.0 + +""" +Haketilo unit tests - displaying list of resources/mappings +""" + +# 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 +from selenium.webdriver.support.ui import WebDriverWait + +from ..extension_crafting import ExtraHTML +from ..script_loader import load_script +from .utils import * + +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': [{'identifier': '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', + 'source_copyright': [ + sample_file_ref('report.spdx'), + sample_file_ref('LICENSES/CC0-1.0.txt') + ], + 'type': 'mapping', + 'identifier': identifier, + 'long_name': long_name, + 'uuid': '54d23bba-472e-42f5-9194-eaa24c0e3ee7', + 'version': [2022, 5, 10], + 'description': 'suckless something something', + 'payloads': { + 'https://example.org/a/*': { + 'identifier': 'some-KISS-resource' + }, + 'https://example.org/t/*': { + 'identifier': 'another-KISS-resource' + } + } + } + +def make_item(item_type, *args): + return make_sample_resource(*args) if item_type == 'resource' \ + else make_sample_mapping(*args) + +@pytest.mark.ext_data({ + 'background_script': broker_js, + 'extra_html': ExtraHTML('html/item_list.html', {}), + 'navigate_to': 'html/item_list.html' +}) +@pytest.mark.usefixtures('webextension') +@pytest.mark.parametrize('item_type', ['resource', 'mapping']) +def test_item_list_ordering(driver, execute_in_page, item_type): + """ + A test case of items list proper ordering. + """ + execute_in_page(load_script('html/item_list.js')) + + # Choose sample long names so as to test automatic sorting of items. + long_names = ['sample', 'sample it', 'Sample it', 'SAMPLE IT', + 'test', 'test it', 'Test it', 'TEST IT'] + # Let's operate on a reverse-sorted copy + long_names_reversed = [*long_names] + long_names_reversed.reverse() + + items = [make_item(item_type, f'it_{hex(2 * i + copy)[-1]}', name) + for i, name in enumerate(long_names_reversed) + for copy in (1, 0)] + # When adding/updating items this item will be updated at the end and this + # last update will be used to verify that a set of opertions completed. + extra_item = make_item(item_type, 'extraitem', 'extra item') + + # After this reversal items are sorted in the exact order they are expected + # to appear in the HTML list. + items.reverse() + + sample_data = { + 'resource': {}, + 'mapping': {}, + 'file': { + 'sha256': sample_files_by_sha256 + } + } + + indexes_added = set() + for iteration, to_include in enumerate([ + set([i for i in range(len(items)) if is_prime(i)]), + set([i for i in range(len(items)) + if not is_prime(i) and i & 1]), + set([i for i in range(len(items)) if i % 3 == 0]), + set([i for i in range(len(items)) + if i % 3 and not i & 1 and not is_prime(i)]), + set(range(len(items))) + ]): + # On the last iteration, re-add ALL items but with changed names. + if len(to_include) == len(items): + for it in items: + it['long_name'] = f'somewhat renamed {it["long_name"]}' + + items_to_inclue = [items[i] for i in sorted(to_include)] + sample_data[item_type] = sample_data_dict(items_to_inclue) + execute_in_page('returnval(haketilodb.save_items(arguments[0]));', + sample_data) + + extra_item['long_name'] = f'{iteration} {extra_item["long_name"]}' + sample_data[item_type] = sample_data_dict([extra_item]) + execute_in_page('returnval(haketilodb.save_items(arguments[0]));', + sample_data) + + if iteration == 0: + execute_in_page( + f''' + let list_ctx; + async function create_list() {{ + list_ctx = await {item_type}_list(); + document.body.append(list_ctx.main_div); + }} + returnval(create_list()); + ''') + + def lis_ready(driver): + return extra_item['long_name'] == execute_in_page( + 'returnval(list_ctx.ul.firstElementChild.textContent);' + ) + + indexes_added.update(to_include) + WebDriverWait(driver, 10).until(lis_ready) + + li_texts = execute_in_page( + ''' + var lis = [...list_ctx.ul.children].slice(1); + returnval(lis.map(li => li.textContent)); + ''') + assert li_texts == [items[i]['long_name'] for i in indexes_added] + + preview_texts = execute_in_page( + '''{ + const get_texts = + li => [li.click(), list_ctx.preview_container.textContent][1]; + returnval(lis.map(get_texts)); + }''') + + for i, text in zip(sorted(indexes_added), preview_texts): + assert items[i]['identifier'] in text + assert items[i]['long_name'] in text + +@pytest.mark.ext_data({ + 'background_script': broker_js, + 'extra_html': ExtraHTML('html/item_list.html', {}), + 'navigate_to': 'html/item_list.html' +}) +@pytest.mark.usefixtures('webextension') +@pytest.mark.parametrize('item_type', ['resource', 'mapping']) +def test_item_list_displaying(driver, execute_in_page, item_type): + """ + A test case of items list interaction with preview and dialog. + """ + execute_in_page(load_script('html/item_list.js')) + + items = [make_item(item_type, f'item{i}', f'Item {i}') for i in range(3)] + + sample_data = { + 'resource': {}, + 'mapping': {}, + 'file': { + 'sha256': sample_files_by_sha256 + } + } + sample_data[item_type] = sample_data_dict(items) + + preview_container, dialog_container, ul = execute_in_page( + f''' + let list_ctx, sample_data = arguments[0]; + async function create_list() {{ + await haketilodb.save_items(sample_data); + list_ctx = await {item_type}_list(); + document.body.append(list_ctx.main_div); + return [list_ctx.preview_container, list_ctx.dialog_container, + list_ctx.ul]; + }} + returnval(create_list()); + ''', + sample_data) + + assert not preview_container.is_displayed() + + # Check that preview is displayed correctly. + for i in range(3): + execute_in_page('list_ctx.ul.children[arguments[0]].click();', i) + assert preview_container.is_displayed() + text = preview_container.text + assert f'item{i}' in text + assert f'Item {i}' in text + + # Check that item removal confirmation dialog is displayed correctly. + execute_in_page('list_ctx.remove_but.click();') + WebDriverWait(driver, 10).until(lambda _: dialog_container.is_displayed()) + assert 'list_disabled' in ul.get_attribute('class') + assert not preview_container.is_displayed() + msg = execute_in_page('returnval(list_ctx.dialog_ctx.msg.textContent);') + assert msg == "Are you sure you want to delete 'item2'?" + + # Check that previewing other item is impossible while dialog is open. + execute_in_page('list_ctx.ul.children[0].click();') + assert dialog_container.is_displayed() + assert 'list_disabled' in ul.get_attribute('class') + assert not preview_container.is_displayed() + + # Check that queuing multiple removal confirmation dialogs is impossible. + execute_in_page('list_ctx.remove_but.click();') + + # Check that answering "No" causes the item not to be removed and unhides + # item preview. + execute_in_page('list_ctx.dialog_ctx.no_but.click();') + WebDriverWait(driver, 10).until(lambda _: preview_container.is_displayed()) + assert not dialog_container.is_displayed() + assert 'list_disabled' not in ul.get_attribute('class') + assert execute_in_page('returnval(list_ctx.ul.children.length);') == 3 + + # Check that item removal works properly. + def remove_current_item(): + execute_in_page('list_ctx.remove_but.click();') + WebDriverWait(driver, 10)\ + .until(lambda _: dialog_container.is_displayed()) + execute_in_page('list_ctx.dialog_ctx.yes_but.click();') + + remove_current_item() + + def item_deleted(driver): + return execute_in_page('returnval(list_ctx.ul.children.length);') == 2 + WebDriverWait(driver, 10).until(item_deleted) + assert not dialog_container.is_displayed() + assert not preview_container.is_displayed() + assert 'list_disabled' not in ul.get_attribute('class') + + execute_in_page('list_ctx.ul.children[1].click();') + + # Check that item removal failure causes the right error dialog to appear. + execute_in_page('haketilodb.finalize_transaction = () => {throw "sth";};') + remove_current_item() + WebDriverWait(driver, 10).until(lambda _: dialog_container.is_displayed()) + msg = execute_in_page('returnval(list_ctx.dialog_ctx.msg.textContent);') + assert msg == "Couldn't remove 'item1' :(" + + # Destroy item list. + assert True == execute_in_page( + ''' + const main_div = list_ctx.main_div; + destroy_list(list_ctx); + returnval(main_div.parentElement === null); + ''') -- cgit v1.2.3