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
|
''')
|