19
19
import os .path
20
20
import re
21
21
import textwrap
22
+ from typing import Any , Protocol
22
23
23
- from testlib import Error , MachineCase , wait
24
+ from machine import testvm
25
+ from testlib import Browser , Error , MachineCase , wait
24
26
25
27
26
- def from_udisks_ascii (codepoints ) :
28
+ def from_udisks_ascii (codepoints : list [ int ]) -> str :
27
29
return '' .join (map (chr , codepoints [:- 1 ]))
28
30
29
31
30
- class StorageHelpers :
32
+ class StorageMachineProtocol (Protocol ):
33
+ """Classes that StorageHelpers is mixed into need to implement this"""
34
+
35
+ machine : testvm .Machine
36
+ browser : Browser
37
+
38
+ def allow_browser_errors (self , * patterns : str ) -> None : ...
39
+
40
+
41
+ class StorageHelpers (StorageMachineProtocol ):
31
42
"""Mix-in class for using in tests that derive from something else than MachineCase or StorageCase"""
43
+ machine : testvm .Machine
44
+ browser : Browser
45
+
46
+ def allow_browser_errors (self , * patterns : str ) -> None : ...
32
47
33
- def inode (self , f ) :
48
+ def inode (self , f : str ) -> str :
34
49
return self .machine .execute ("stat -L '%s' -c %%i" % f )
35
50
36
51
def retry (self , setup , check , teardown ):
@@ -45,7 +60,7 @@ def step():
45
60
46
61
self .browser .wait (step )
47
62
48
- def add_ram_disk (self , size = 50 , delay = None ):
63
+ def add_ram_disk (self , size : int = 50 , delay : int | None = None ):
49
64
"""Add per-test RAM disk
50
65
51
66
The disk gets removed automatically when the test ends. This is safe for @nondestructive tests.
@@ -119,7 +134,7 @@ def add_targetd_loopback_disk(self, index, size=50):
119
134
raise Error ("Device not found" )
120
135
return dev
121
136
122
- def force_remove_disk (self , device ) :
137
+ def force_remove_disk (self , device : str ) -> None :
123
138
"""Act like the given device gets physically removed.
124
139
125
140
This circumvents all the normal EBUSY failures, and thus can be used for testing
@@ -140,22 +155,22 @@ def addCleanupMount(self, mount_point):
140
155
141
156
# Dialogs
142
157
143
- def dialog_wait_open (self ):
158
+ def dialog_wait_open (self ) -> None :
144
159
self .browser .wait_visible ('#dialog' )
145
160
146
- def dialog_wait_alert (self , text1 , text2 = None ):
161
+ def dialog_wait_alert (self , text1 : str , text2 : str | None = None ) -> None :
147
162
def has_alert_title ():
148
163
t = self .browser .text ('#dialog .pf-v5-c-alert__title' )
149
164
return text1 in t or (text2 is not None and text2 in t )
150
165
self .browser .wait (has_alert_title )
151
166
152
- def dialog_wait_title (self , text ) :
167
+ def dialog_wait_title (self , text : str ) -> None :
153
168
self .browser .wait_in_text ('#dialog .pf-v5-c-modal-box__title' , text )
154
169
155
- def dialog_field (self , field ) :
170
+ def dialog_field (self , field : str ) -> str :
156
171
return f'#dialog [data-field="{ field } "]'
157
172
158
- def dialog_val (self , field ) :
173
+ def dialog_val (self , field : str ) -> Any :
159
174
sel = self .dialog_field (field )
160
175
ftype = self .browser .attr (sel , "data-field-type" )
161
176
if ftype == "text-input-checked" :
@@ -198,7 +213,7 @@ def dialog_set_val(self, field, val):
198
213
else :
199
214
self .browser .set_val (sel , val )
200
215
201
- def dialog_combobox_choices (self , field ) :
216
+ def dialog_combobox_choices (self , field : str ) -> None :
202
217
return self .browser .call_js_func ("""(function (sel) {
203
218
var lis = ph_find(sel).querySelectorAll('li');
204
219
var result = [];
@@ -207,10 +222,10 @@ def dialog_combobox_choices(self, field):
207
222
return result;
208
223
})""" , self .dialog_field (field ))
209
224
210
- def dialog_is_present (self , field , label ) :
225
+ def dialog_is_present (self , field : str , label : str ) -> bool :
211
226
return self .browser .is_present (f'{ self .dialog_field (field )} :contains("{ label } ") input' )
212
227
213
- def dialog_wait_val (self , field , val , unit = None ):
228
+ def dialog_wait_val (self , field : str , val : str , unit : str | None = None ) -> None :
214
229
if unit is None :
215
230
unit = "1000000"
216
231
@@ -226,29 +241,29 @@ def dialog_wait_val(self, field, val, unit=None):
226
241
else :
227
242
self .browser .wait_val (sel , val )
228
243
229
- def dialog_wait_error (self , field , val ) :
244
+ def dialog_wait_error (self , field : str , val : str ) -> None :
230
245
# XXX - allow for more than one error
231
246
self .browser .wait_in_text ('#dialog .pf-v5-c-form__helper-text .pf-m-error' , val )
232
247
233
- def dialog_wait_not_present (self , field ) :
248
+ def dialog_wait_not_present (self , field : str ) -> None :
234
249
self .browser .wait_not_present (self .dialog_field (field ))
235
250
236
- def dialog_wait_apply_enabled (self ):
251
+ def dialog_wait_apply_enabled (self ) -> None :
237
252
self .browser .wait_attr ('#dialog button.apply:nth-of-type(1)' , "disabled" , None )
238
253
239
- def dialog_wait_apply_disabled (self ):
254
+ def dialog_wait_apply_disabled (self ) -> None :
240
255
self .browser .wait_visible ('#dialog button.apply:nth-of-type(1)[disabled]' )
241
256
242
- def dialog_apply (self ):
257
+ def dialog_apply (self ) -> None :
243
258
self .browser .click ('#dialog button.apply:nth-of-type(1)' )
244
259
245
- def dialog_apply_secondary (self ):
260
+ def dialog_apply_secondary (self ) -> None :
246
261
self .browser .click ('#dialog button.apply:nth-of-type(2)' )
247
262
248
- def dialog_cancel (self ):
263
+ def dialog_cancel (self ) -> None :
249
264
self .browser .click ('#dialog button.cancel' )
250
265
251
- def dialog_wait_close (self ):
266
+ def dialog_wait_close (self ) -> None :
252
267
# file system operations often take longer than 10s
253
268
with self .browser .wait_timeout (max (self .browser .timeout , 60 )):
254
269
self .browser .wait_not_present ('#dialog' )
@@ -386,7 +401,7 @@ def udisks_objects(self):
386
401
"org.freedesktop.DBus.ObjectManager",
387
402
"GetManagedObjects", "", [])))""" )]))
388
403
389
- def configuration_field (self , dev , tab , field ) :
404
+ def configuration_field (self , dev : str , tab : str , field : str ) -> str :
390
405
managerObjects = self .udisks_objects ()
391
406
for path in managerObjects :
392
407
if "org.freedesktop.UDisks2.Block" in managerObjects [path ]:
@@ -405,7 +420,7 @@ def assert_in_configuration(self, dev, tab, field, text):
405
420
def assert_not_in_configuration (self , dev , tab , field , text ):
406
421
self .assertNotIn (text , self .configuration_field (dev , tab , field ))
407
422
408
- def child_configuration_field (self , dev , tab , field ) :
423
+ def child_configuration_field (self , dev : str , tab : str , field : str ) -> str :
409
424
udisks_objects = self .udisks_objects ()
410
425
for path in udisks_objects :
411
426
if "org.freedesktop.UDisks2.Encrypted" in udisks_objects [path ]:
@@ -579,16 +594,16 @@ def encrypt_root(self, passphrase):
579
594
580
595
# Cards and tables
581
596
582
- def card (self , title ) :
597
+ def card (self , title : str ) -> str :
583
598
return f"[data-test-card-title='{ title } ']"
584
599
585
- def card_parent_link (self ):
600
+ def card_parent_link (self ) -> str :
586
601
return ".pf-v5-c-breadcrumb__item:nth-last-child(2) > a"
587
602
588
- def card_header (self , title ) :
603
+ def card_header (self , title : str ) -> str :
589
604
return self .card (title ) + " .pf-v5-c-card__header"
590
605
591
- def card_row (self , title , index = None , name = None , location = None ):
606
+ def card_row (self , title : str , index : int | None = None , name : str | None = None , location : str | None = None ) -> str :
592
607
if index is not None :
593
608
return self .card (title ) + f" tbody tr:nth-child({ index } )"
594
609
elif name is not None :
@@ -597,58 +612,58 @@ def card_row(self, title, index=None, name=None, location=None):
597
612
else :
598
613
return self .card (title ) + f" tbody [data-test-row-location='{ location } ']"
599
614
600
- def click_card_row (self , title , index = None , name = None , location = None ):
615
+ def click_card_row (self , title : str , index : int | None = None , name : str | None = None , location : str | None = None ) -> None :
601
616
# We need to click on a <td> element since that's where the handlers are...
602
617
self .browser .click (self .card_row (title , index , name , location ) + " td:nth-child(1)" )
603
618
604
619
def card_row_col (self , title , row_index = None , col_index = None , row_name = None , row_location = None ):
605
620
return self .card_row (title , row_index , row_name , row_location ) + f" td:nth-child({ col_index } )"
606
621
607
- def card_desc (self , card_title , desc_title ) :
622
+ def card_desc (self , card_title : str , desc_title : str ) -> str :
608
623
return self .card (card_title ) + f" [data-test-desc-title='{ desc_title } '] [data-test-value=true]"
609
624
610
- def card_desc_action (self , card_title , desc_title ) :
625
+ def card_desc_action (self , card_title : str , desc_title : str ) -> str :
611
626
return self .card (card_title ) + f" [data-test-desc-title='{ desc_title } '] [data-test-action=true] button"
612
627
613
- def card_button (self , card_title , button_title ) :
628
+ def card_button (self , card_title : str , button_title : str ) -> str :
614
629
return self .card (card_title ) + f" button:contains('{ button_title } ')"
615
630
616
- def dropdown_toggle (self , parent ) :
631
+ def dropdown_toggle (self , parent : str ) -> str :
617
632
return parent + " .pf-v5-c-menu-toggle"
618
633
619
- def dropdown_action (self , parent , title ) :
634
+ def dropdown_action (self , parent : str , title : str ) -> str :
620
635
return parent + f" .pf-v5-c-menu button:contains('{ title } ')"
621
636
622
- def dropdown_description (self , parent , title ) :
637
+ def dropdown_description (self , parent : str , title : str ) -> str :
623
638
return parent + f" .pf-v5-c-menu button:contains('{ title } ') .pf-v5-c-menu__item-description"
624
639
625
- def click_dropdown (self , parent , title ) :
640
+ def click_dropdown (self , parent : str , title : str ) -> None :
626
641
self .browser .click (self .dropdown_toggle (parent ))
627
642
self .browser .click (self .dropdown_action (parent , title ))
628
643
629
- def click_card_dropdown (self , card_title , button_title ) :
644
+ def click_card_dropdown (self , card_title : str , button_title : str ) -> None :
630
645
self .click_dropdown (self .card_header (card_title ), button_title )
631
646
632
- def click_devices_dropdown (self , title ) :
647
+ def click_devices_dropdown (self , title : str ) -> None :
633
648
self .click_card_dropdown ("Storage" , title )
634
649
635
- def check_dropdown_action_disabled (self , parent , title , expected_text ) :
650
+ def check_dropdown_action_disabled (self , parent : str , title : str , expected_text : str ) -> None :
636
651
self .browser .click (self .dropdown_toggle (parent ))
637
652
self .browser .wait_visible (self .dropdown_action (parent , title ) + "[disabled]" )
638
653
self .browser .wait_text (self .dropdown_description (parent , title ), expected_text )
639
654
self .browser .click (self .dropdown_toggle (parent ))
640
655
641
- def wait_mounted (self , card_title ) :
656
+ def wait_mounted (self , card_title : str ) -> None :
642
657
with self .browser .wait_timeout (30 ):
643
658
self .browser .wait_not_in_text (self .card_desc (card_title , "Mount point" ),
644
659
"The filesystem is not mounted." )
645
660
646
- def wait_not_mounted (self , card_title ) :
661
+ def wait_not_mounted (self , card_title : str ) -> None :
647
662
with self .browser .wait_timeout (30 ):
648
663
self .browser .wait_in_text (self .card_desc (card_title , "Mount point" ),
649
664
"The filesystem is not mounted." )
650
665
651
- def wait_card_button_disabled (self , card_title , button_title ) :
666
+ def wait_card_button_disabled (self , card_title : str , button_title : str ) -> None :
652
667
with self .browser .wait_timeout (30 ):
653
668
self .browser .wait_visible (self .card_button (card_title , button_title ) + ":disabled" )
654
669
0 commit comments