Project

General

Profile

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

haketilo / test / unit / test_item_list.py @ 1c65dd5c

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
        'file': {
110
            'sha256': sample_files_by_sha256
111
        }
112
    }
113

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

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

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

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

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

    
155
        indexes_added.update(to_include)
156
        WebDriverWait(driver, 10).until(lis_ready)
157

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

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

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

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

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

    
191
    sample_data = {
192
        'resources': {},
193
        'mappings': {},
194
        'file': {
195
            'sha256': sample_files_by_sha256
196
        }
197
    }
198
    sample_data[item_type + 's'] = sample_data_dict(items)
199

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

    
214
    assert not preview_container.is_displayed()
215

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

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

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

    
238
    # Check that queuing multiple removal confirmation dialogs is impossible.
239
    execute_in_page('list_ctx.remove_but.click();')
240

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

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

    
256
    remove_current_item()
257

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

    
265
    execute_in_page('list_ctx.ul.children[1].click();')
266

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

    
274
    # Destroy item list.
275
    assert True == execute_in_page(
276
        '''
277
        const main_div = list_ctx.main_div;
278
        destroy_list(list_ctx);
279
        returnval(main_div.parentElement === null);
280
        ''')
(11-11/25)