Project

General

Profile

Download (10.1 KB) Statistics
| Branch: | Tag: | Revision:

haketilo / test / unit / test_item_list.py @ ad69f9c8

1
# SPDX-License-Identifier: CC0-1.0
2

    
3
"""
4
Haketilo unit tests - displaying list of resources/mappings
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
from selenium.webdriver.support.ui import WebDriverWait
22

    
23
from ..extension_crafting import ExtraHTML
24
from ..script_loader import load_script
25
from .utils import *
26

    
27
def make_sample_resource(identifier, long_name):
28
    return {
29
        'source_name': 'hello',
30
        'source_copyright': [
31
            sample_file_ref('report.spdx'),
32
            sample_file_ref('LICENSES/CC0-1.0.txt')
33
        ],
34
        'type': 'resource',
35
        'identifier': identifier,
36
        'long_name': long_name,
37
        'uuid': 'a6754dcb-58d8-4b7a-a245-24fd7ad4cd68',
38
        'version': [2021, 11, 10],
39
        'revision': 1,
40
        'description': 'greets an apple',
41
        'dependencies': ['hello-message'],
42
        'scripts': [
43
            sample_file_ref('hello.js'),
44
            sample_file_ref('bye.js')
45
        ]
46
    }
47

    
48
def make_sample_mapping(identifier, long_name):
49
    return {
50
        'source_name': 'example-org-fixes-new',
51
        'source_copyright': [
52
            sample_file_ref('report.spdx'),
53
            sample_file_ref('LICENSES/CC0-1.0.txt')
54
        ],
55
        'type': 'mapping',
56
        'identifier': identifier,
57
        'long_name': long_name,
58
        'uuid': '54d23bba-472e-42f5-9194-eaa24c0e3ee7',
59
        'version': [2022, 5, 10],
60
        'description': 'suckless something something',
61
        'payloads': {
62
            'https://example.org/a/*': {
63
                'identifier': 'some-KISS-resource'
64
            },
65
            'https://example.org/t/*': {
66
                'identifier':  'another-KISS-resource'
67
            }
68
        }
69
    }
70

    
71
def make_item(item_type, *args):
72
    return make_sample_resource(*args) if item_type == 'resource' \
73
        else make_sample_mapping(*args)
74

    
75
@pytest.mark.ext_data({
76
    'background_script': broker_js,
77
    'extra_html': ExtraHTML('html/item_list.html', {}),
78
    'navigate_to': 'html/item_list.html'
79
})
80
@pytest.mark.usefixtures('webextension')
81
@pytest.mark.parametrize('item_type', ['resource', 'mapping'])
82
def test_item_list_ordering(driver, execute_in_page, item_type):
83
    """
84
    A test case of items list proper ordering.
85
    """
86
    execute_in_page(load_script('html/item_list.js'))
87

    
88
    # Choose sample long names so as to test automatic sorting of items.
89
    long_names = ['sample', 'sample it', 'Sample it', 'SAMPLE IT',
90
                  'test', 'test it', 'Test it', 'TEST IT']
91
    # Let's operate on a reverse-sorted copy
92
    long_names_reversed = [*long_names]
93
    long_names_reversed.reverse()
94

    
95
    items = [make_item(item_type, f'it_{hex(2 * i + copy)[-1]}', name)
96
             for i, name in enumerate(long_names_reversed)
97
             for copy in (1, 0)]
98
    # When adding/updating items this item will be updated at the end and this
99
    # last update will be used to verify that a set of opertions completed.
100
    extra_item = make_item(item_type, 'extraitem', 'extra item')
101

    
102
    # After this reversal items are sorted in the exact order they are expected
103
    # to appear in the HTML list.
104
    items.reverse()
105

    
106
    sample_data = {
107
        'resources': {},
108
        'mappings': {},
109
        'files': sample_files_by_hash
110
    }
111

    
112
    indexes_added = set()
113
    for iteration, to_include in enumerate([
114
            set([i for i in range(len(items)) if is_prime(i)]),
115
            set([i for i in range(len(items))
116
                 if not is_prime(i) and i & 1]),
117
            set([i for i in range(len(items)) if i % 3 == 0]),
118
            set([i for i in range(len(items))
119
                 if i % 3 and not i & 1 and not is_prime(i)]),
120
            set(range(len(items)))
121
    ]):
122
        # On the last iteration, re-add ALL items but with changed names.
123
        if len(to_include) == len(items):
124
            for it in items:
125
                it['long_name'] = f'somewhat renamed {it["long_name"]}'
126

    
127
        items_to_inclue = [items[i] for i in sorted(to_include)]
128
        sample_data[item_type + 's'] = sample_data_dict(items_to_inclue)
129
        execute_in_page('returnval(haketilodb.save_items(arguments[0]));',
130
                        sample_data)
131

    
132
        extra_item['long_name'] = f'{iteration} {extra_item["long_name"]}'
133
        sample_data[item_type + 's'] = sample_data_dict([extra_item])
134
        execute_in_page('returnval(haketilodb.save_items(arguments[0]));',
135
                        sample_data)
136

    
137
        if iteration == 0:
138
            execute_in_page(
139
                f'''
140
                let list_ctx;
141
                async function create_list() {{
142
                    list_ctx = await {item_type}_list();
143
                    document.body.append(list_ctx.main_div);
144
                }}
145
                returnval(create_list());
146
                ''')
147

    
148
        def lis_ready(driver):
149
            return extra_item['long_name'] == execute_in_page(
150
                'returnval(list_ctx.ul.firstElementChild.textContent);'
151
            )
152

    
153
        indexes_added.update(to_include)
154
        WebDriverWait(driver, 10).until(lis_ready)
155

    
156
        li_texts = execute_in_page(
157
            '''
158
            var lis = [...list_ctx.ul.children].slice(1);
159
            returnval(lis.map(li => li.textContent));
160
            ''')
161
        assert li_texts == [items[i]['long_name'] for i in indexes_added]
162

    
163
        preview_texts = execute_in_page(
164
            '''{
165
            const get_texts =
166
                li => [li.click(), list_ctx.preview_container.textContent][1];
167
            returnval(lis.map(get_texts));
168
            }''')
169

    
170
        for i, text in zip(sorted(indexes_added), preview_texts):
171
            assert items[i]['identifier'] in text
172
            assert items[i]['long_name']  in text
173

    
174
@pytest.mark.ext_data({
175
    'background_script': broker_js,
176
    'extra_html': ExtraHTML('html/item_list.html', {}),
177
    'navigate_to': 'html/item_list.html'
178
})
179
@pytest.mark.usefixtures('webextension')
180
@pytest.mark.parametrize('item_type', ['resource', 'mapping'])
181
def test_item_list_displaying(driver, execute_in_page, item_type):
182
    """
183
    A test case of items list interaction with preview and dialog.
184
    """
185
    execute_in_page(load_script('html/item_list.js'))
186

    
187
    items = [make_item(item_type, f'item{i}', f'Item {i}') for i in range(3)]
188

    
189
    sample_data = {
190
        'resources': {},
191
        'mappings': {},
192
        'files': sample_files_by_hash
193
    }
194
    sample_data[item_type + 's'] = sample_data_dict(items)
195

    
196
    preview_container, dialog_container, ul = execute_in_page(
197
        f'''
198
        let list_ctx, sample_data = arguments[0];
199
        async function create_list() {{
200
            await haketilodb.save_items(sample_data);
201
            list_ctx = await {item_type}_list();
202
            document.body.append(list_ctx.main_div);
203
            return [list_ctx.preview_container, list_ctx.dialog_container,
204
                    list_ctx.ul];
205
        }}
206
        returnval(create_list());
207
        ''',
208
        sample_data)
209

    
210
    assert not preview_container.is_displayed()
211

    
212
    # Check that preview is displayed correctly.
213
    for i in range(3):
214
        execute_in_page('list_ctx.ul.children[arguments[0]].click();', i)
215
        assert preview_container.is_displayed()
216
        text = preview_container.text
217
        assert f'item{i}' in text
218
        assert f'Item {i}' in text
219

    
220
    # Check that item removal confirmation dialog is displayed correctly.
221
    execute_in_page('list_ctx.remove_but.click();')
222
    WebDriverWait(driver, 10).until(lambda _: dialog_container.is_displayed())
223
    assert 'list_disabled' in ul.get_attribute('class')
224
    assert not preview_container.is_displayed()
225
    msg = execute_in_page('returnval(list_ctx.dialog_ctx.msg.textContent);')
226
    assert msg == "Are you sure you want to delete 'item2'?"
227

    
228
    # Check that previewing other item is impossible while dialog is open.
229
    execute_in_page('list_ctx.ul.children[0].click();')
230
    assert dialog_container.is_displayed()
231
    assert 'list_disabled' in ul.get_attribute('class')
232
    assert not preview_container.is_displayed()
233

    
234
    # Check that queuing multiple removal confirmation dialogs is impossible.
235
    execute_in_page('list_ctx.remove_but.click();')
236

    
237
    # Check that answering "No" causes the item not to be removed and unhides
238
    # item preview.
239
    execute_in_page('list_ctx.dialog_ctx.no_but.click();')
240
    WebDriverWait(driver, 10).until(lambda _: preview_container.is_displayed())
241
    assert not dialog_container.is_displayed()
242
    assert 'list_disabled' not in ul.get_attribute('class')
243
    assert execute_in_page('returnval(list_ctx.ul.children.length);') == 3
244

    
245
    # Check that item removal works properly.
246
    def remove_current_item():
247
        execute_in_page('list_ctx.remove_but.click();')
248
        WebDriverWait(driver, 10)\
249
            .until(lambda _: dialog_container.is_displayed())
250
        execute_in_page('list_ctx.dialog_ctx.yes_but.click();')
251

    
252
    remove_current_item()
253

    
254
    def item_deleted(driver):
255
        return execute_in_page('returnval(list_ctx.ul.children.length);') == 2
256
    WebDriverWait(driver, 10).until(item_deleted)
257
    assert not dialog_container.is_displayed()
258
    assert not preview_container.is_displayed()
259
    assert 'list_disabled' not in ul.get_attribute('class')
260

    
261
    execute_in_page('list_ctx.ul.children[1].click();')
262

    
263
    # Check that item removal failure causes the right error dialog to appear.
264
    execute_in_page('haketilodb.finalize_transaction = () => {throw "sth";};')
265
    remove_current_item()
266
    WebDriverWait(driver, 10).until(lambda _: dialog_container.is_displayed())
267
    msg = execute_in_page('returnval(list_ctx.dialog_ctx.msg.textContent);')
268
    assert msg == "Couldn't remove 'item1' :("
269

    
270
    # Destroy item list.
271
    assert True == execute_in_page(
272
        '''
273
        const main_div = list_ctx.main_div;
274
        destroy_list(list_ctx);
275
        returnval(main_div.parentElement === null);
276
        ''')
(11-11/25)