Skip to content

Commit 9b51544

Browse files
committed
[tests] Made selenium tests resilient to failures
1 parent d84383c commit 9b51544

File tree

4 files changed

+124
-200
lines changed

4 files changed

+124
-200
lines changed

openwisp_controller/config/tests/test_selenium.py

Lines changed: 74 additions & 173 deletions
Original file line numberDiff line numberDiff line change
@@ -16,75 +16,42 @@
1616

1717
from openwisp_utils.test_selenium_mixins import SeleniumTestMixin
1818

19+
from ...tests.utils import DeviceAdminSeleniumTextMixin
1920
from .utils import CreateConfigTemplateMixin, TestWireguardVpnMixin
2021

2122
Device = load_model('config', 'Device')
2223

2324

24-
class SeleniumBaseMixin(CreateConfigTemplateMixin, SeleniumTestMixin):
25-
def setUp(self):
26-
self.admin = self._create_admin(
27-
username=self.admin_username, password=self.admin_password
28-
)
29-
30-
3125
@tag('selenium_tests')
3226
class TestDeviceAdmin(
33-
SeleniumBaseMixin,
27+
DeviceAdminSeleniumTextMixin,
28+
CreateConfigTemplateMixin,
3429
StaticLiveServerTestCase,
3530
):
36-
def tearDown(self):
37-
# Accept unsaved changes alert to allow other tests to run
38-
try:
39-
self.web_driver.refresh()
40-
except UnexpectedAlertPresentException:
41-
alert = Alert(self.web_driver)
42-
alert.accept()
43-
else:
44-
try:
45-
WebDriverWait(self.web_driver, 1).until(EC.alert_is_present())
46-
except TimeoutException:
47-
pass
48-
else:
49-
alert = Alert(self.web_driver)
50-
alert.accept()
51-
self.web_driver.refresh()
52-
WebDriverWait(self.web_driver, 2).until(
53-
EC.visibility_of_element_located((By.XPATH, '//*[@id="site-name"]'))
54-
)
55-
5631
def test_create_new_device(self):
5732
required_template = self._create_template(name='Required', required=True)
5833
default_template = self._create_template(name='Default', default=True)
5934
org = self._get_org()
6035
self.login()
6136
self.open(reverse('admin:config_device_add'))
62-
self.web_driver.find_element(by=By.NAME, value='name').send_keys(
63-
'11:22:33:44:55:66'
64-
)
65-
self.web_driver.find_element(
37+
self.find_element(by=By.NAME, value='name').send_keys('11:22:33:44:55:66')
38+
self.find_element(
6639
by=By.CSS_SELECTOR, value='#select2-id_organization-container'
6740
).click()
68-
WebDriverWait(self.web_driver, 2).until(
69-
EC.invisibility_of_element_located(
70-
(By.CSS_SELECTOR, '.select2-results__option.loading-results')
71-
)
41+
self.wait_for_invisibility(
42+
By.CSS_SELECTOR, '.select2-results__option.loading-results'
7243
)
73-
self.web_driver.find_element(
74-
by=By.CLASS_NAME, value='select2-search__field'
75-
).send_keys(org.name)
76-
WebDriverWait(self.web_driver, 2).until(
77-
EC.invisibility_of_element_located(
78-
(By.CSS_SELECTOR, '.select2-results__option.loading-results')
79-
)
44+
self.find_element(by=By.CLASS_NAME, value='select2-search__field').send_keys(
45+
org.name
8046
)
81-
self.web_driver.find_element(
82-
by=By.CLASS_NAME, value='select2-results__option'
83-
).click()
84-
self.web_driver.find_element(by=By.NAME, value='mac_address').send_keys(
47+
self.wait_for_invisibility(
48+
By.CSS_SELECTOR, '.select2-results__option.loading-results'
49+
)
50+
self.find_element(by=By.CLASS_NAME, value='select2-results__option').click()
51+
self.find_element(by=By.NAME, value='mac_address').send_keys(
8552
'11:22:33:44:55:66'
8653
)
87-
self.web_driver.find_element(
54+
self.find_element(
8855
by=By.XPATH, value='//*[@id="config-group"]/fieldset/div[2]/a'
8956
).click()
9057
try:
@@ -107,10 +74,10 @@ def test_create_new_device(self):
10774
)
10875
except TimeoutException:
10976
self.fail('Relevant templates logic was not executed')
110-
required_template_element = self.web_driver.find_element(
77+
required_template_element = self.find_element(
11178
by=By.XPATH, value=f'//*[@value="{required_template.id}"]'
11279
)
113-
default_template_element = self.web_driver.find_element(
80+
default_template_element = self.find_element(
11481
by=By.XPATH, value=f'//*[@value="{default_template.id}"]'
11582
)
11683
self.assertEqual(required_template_element.is_enabled(), False)
@@ -121,17 +88,10 @@ def test_create_new_device(self):
12188
self.web_driver.execute_script(
12289
'document.querySelector("#ow-user-tools").style.display="none"'
12390
)
124-
self.web_driver.find_element(by=By.NAME, value='_save').click()
125-
try:
126-
WebDriverWait(self.web_driver, 5).until(
127-
EC.presence_of_element_located(
128-
(By.CSS_SELECTOR, '.messagelist .success')
129-
)
130-
)
131-
except TimeoutException:
132-
self.fail('Device added success message timed out')
91+
self.find_element(by=By.NAME, value='_save').click()
92+
self.wait_for_presence(By.CSS_SELECTOR, '.messagelist .success')
13393
self.assertEqual(
134-
self.web_driver.find_elements(by=By.CLASS_NAME, value='success')[0].text,
94+
self.find_elements(by=By.CLASS_NAME, value='success')[0].text,
13595
'The Device “11:22:33:44:55:66” was added successfully.',
13696
)
13797

@@ -147,26 +107,12 @@ def test_device_preview_keyboard_shortcuts(self):
147107
with self.subTest('press ALT + P and expect overlay to be shown'):
148108
actions = ActionChains(self.web_driver)
149109
actions.key_down(Keys.ALT).send_keys('p').key_up(Keys.ALT).perform()
150-
try:
151-
WebDriverWait(self.web_driver, 1).until(
152-
EC.visibility_of_element_located(
153-
(By.CSS_SELECTOR, '.djnjc-overlay:not(.loading)')
154-
)
155-
)
156-
except TimeoutException:
157-
self.fail('The preview overlay is unexpectedly not visible')
110+
self.wait_for_visibility(By.CSS_SELECTOR, '.djnjc-overlay:not(.loading)')
158111

159112
with self.subTest('press ESC to close preview overlay'):
160113
actions = ActionChains(self.web_driver)
161114
actions.send_keys(Keys.ESCAPE).perform()
162-
try:
163-
WebDriverWait(self.web_driver, 1).until(
164-
EC.invisibility_of_element_located(
165-
(By.CSS_SELECTOR, '.djnjc-overlay:not(.loading)')
166-
)
167-
)
168-
except TimeoutException:
169-
self.fail('The preview overlay has not been closed as expected')
115+
self.wait_for_invisibility(By.CSS_SELECTOR, '.djnjc-overlay:not(.loading)')
170116

171117
def test_unsaved_changes(self):
172118
self.login()
@@ -183,19 +129,17 @@ def test_unsaved_changes(self):
183129

184130
with self.subTest('Alert should be displayed after making changes'):
185131
# simulate hand gestures
186-
self.web_driver.find_element(by=By.TAG_NAME, value='body').click()
187-
self.web_driver.find_element(by=By.NAME, value='name').click()
132+
self.find_element(by=By.TAG_NAME, value='body').click()
133+
self.find_element(by=By.NAME, value='name').click()
188134
# set name
189-
self.web_driver.find_element(by=By.NAME, value='name').send_keys(
190-
'new.device.name'
191-
)
135+
self.find_element(by=By.NAME, value='name').send_keys('new.device.name')
192136
# simulate hand gestures
193-
self.web_driver.find_element(by=By.TAG_NAME, value='body').click()
137+
self.find_element(by=By.TAG_NAME, value='body').click()
194138
self.web_driver.refresh()
195139
try:
196-
WebDriverWait(self.web_driver, 1).until(EC.alert_is_present())
140+
WebDriverWait(self.web_driver, 5).until(EC.alert_is_present())
197141
except TimeoutException:
198-
for entry in self.web_driver.get_log('browser'):
142+
for entry in self.get_browser_logs():
199143
print(entry)
200144
self.fail('Timed out wating for unsaved changes alert')
201145
else:
@@ -234,35 +178,18 @@ def test_multiple_organization_templates(self):
234178
)
235179
wait = WebDriverWait(self.web_driver, 2)
236180
# org2 templates should not be visible
237-
try:
238-
wait.until(
239-
EC.invisibility_of_element_located(
240-
(By.XPATH, f'//*[@value="{org2_required_template.id}"]')
241-
)
242-
)
243-
wait.until(
244-
EC.invisibility_of_element_located(
245-
(By.XPATH, f'//*[@value="{org2_default_template.id}"]')
246-
)
247-
)
248-
except (TimeoutException, StaleElementReferenceException):
249-
self.fail('Template belonging to other organization found')
250-
251-
# org1 and shared templates should be visible
252-
wait.until(
253-
EC.visibility_of_any_elements_located(
254-
(By.XPATH, f'//*[@value="{org1_required_template.id}"]')
255-
)
181+
self.wait_for_invisibility(
182+
By.XPATH, f'//*[@value="{org2_required_template.id}"]'
256183
)
257-
wait.until(
258-
EC.visibility_of_any_elements_located(
259-
(By.XPATH, f'//*[@value="{org1_default_template.id}"]')
260-
)
184+
self.wait_for_invisibility(
185+
By.XPATH, f'//*[@value="{org2_default_template.id}"]'
261186
)
262-
wait.until(
263-
EC.visibility_of_any_elements_located(
264-
(By.XPATH, f'//*[@value="{shared_required_template.id}"]')
265-
)
187+
188+
# org1 and shared templates should be visible
189+
self.wait_for_visibility(By.XPATH, f'//*[@value="{org1_required_template.id}"]')
190+
self.wait_for_visibility(By.XPATH, f'//*[@value="{org1_default_template.id}"]')
191+
self.wait_for_visibility(
192+
By.XPATH, f'//*[@value="{shared_required_template.id}"]'
266193
)
267194

268195
def test_change_config_backend(self):
@@ -273,20 +200,13 @@ def test_change_config_backend(self):
273200
self.open(
274201
reverse('admin:config_device_change', args=[device.id]) + '#config-group'
275202
)
276-
self.web_driver.find_element(by=By.XPATH, value=f'//*[@value="{template.id}"]')
203+
self.find_element(by=By.XPATH, value=f'//*[@value="{template.id}"]')
277204
# Change config backed to
278205
config_backend_select = Select(
279-
self.web_driver.find_element(by=By.NAME, value='config-0-backend')
206+
self.find_element(by=By.NAME, value='config-0-backend')
280207
)
281208
config_backend_select.select_by_visible_text('OpenWISP Firmware 1.x')
282-
try:
283-
WebDriverWait(self.web_driver, 1).until(
284-
EC.invisibility_of_element_located(
285-
(By.XPATH, f'//*[@value="{template.id}"]')
286-
)
287-
)
288-
except TimeoutException:
289-
self.fail('Template for other config backend found')
209+
self.wait_for_invisibility(By.XPATH, f'//*[@value="{template.id}"]')
290210

291211
def test_template_context_variables(self):
292212
self._create_template(
@@ -312,7 +232,7 @@ def test_template_context_variables(self):
312232
)
313233
except TimeoutException:
314234
self.fail('Timed out wating for configuration variabled to get loaded')
315-
self.web_driver.find_element(
235+
self.find_element(
316236
by=By.XPATH, value='//*[@id="main-content"]/div[2]/a[3]'
317237
).click()
318238
try:
@@ -333,28 +253,22 @@ def test_force_delete_device_with_deactivating_config(self):
333253

334254
self.login()
335255
self.open(reverse('admin:config_device_change', args=[device.id]))
336-
self.web_driver.find_elements(
337-
by=By.CSS_SELECTOR, value='input.deletelink[type="submit"]'
338-
)[-1].click()
256+
self.find_elements(by=By.CSS_SELECTOR, value='input.deletelink[type="submit"]')[
257+
-1
258+
].click()
339259
device.refresh_from_db()
340260
config.refresh_from_db()
341261
self.assertEqual(device.is_deactivated(), True)
342262
self.assertEqual(config.is_deactivating(), True)
343263

344264
self.open(reverse('admin:config_device_change', args=[device.id]))
345-
self.web_driver.find_elements(by=By.CSS_SELECTOR, value='a.deletelink')[
346-
-1
347-
].click()
348-
WebDriverWait(self.web_driver, 5).until(
349-
EC.visibility_of_element_located(
350-
(By.CSS_SELECTOR, '#deactivating-warning .messagelist .warning p')
351-
)
265+
self.find_elements(by=By.CSS_SELECTOR, value='a.deletelink')[-1].click()
266+
self.wait_for_visibility(
267+
By.CSS_SELECTOR, '#deactivating-warning .messagelist .warning p'
352268
)
353-
self.web_driver.find_element(by=By.CSS_SELECTOR, value='#warning-ack').click()
354-
delete_confirm = WebDriverWait(self.web_driver, 2).until(
355-
EC.visibility_of_element_located(
356-
(By.CSS_SELECTOR, 'form[method="post"] input[type="submit"]')
357-
)
269+
self.find_element(by=By.CSS_SELECTOR, value='#warning-ack').click()
270+
delete_confirm = self.find_element(
271+
By.CSS_SELECTOR, 'form[method="post"] input[type="submit"]'
358272
)
359273
delete_confirm.click()
360274
self.assertEqual(Device.objects.count(), 0)
@@ -375,66 +289,53 @@ def test_force_delete_multiple_devices_with_deactivating_config(self):
375289

376290
self.login()
377291
self.open(reverse('admin:config_device_changelist'))
378-
self.web_driver.find_element(by=By.CSS_SELECTOR, value='#action-toggle').click()
379-
select = Select(self.web_driver.find_element(by=By.NAME, value='action'))
292+
self.find_element(by=By.CSS_SELECTOR, value='#action-toggle').click()
293+
select = Select(self.find_element(by=By.NAME, value='action'))
380294
select.select_by_value('delete_selected')
381-
self.web_driver.find_element(
295+
self.find_element(
382296
by=By.CSS_SELECTOR, value='button[type="submit"][name="index"][value="0"]'
383297
).click()
384-
WebDriverWait(self.web_driver, 5).until(
385-
EC.visibility_of_element_located(
386-
(By.CSS_SELECTOR, '#deactivating-warning .messagelist .warning p')
387-
)
298+
self.wait_for_visibility(
299+
By.CSS_SELECTOR, '#deactivating-warning .messagelist .warning p'
388300
)
389-
self.web_driver.find_element(by=By.CSS_SELECTOR, value='#warning-ack').click()
390-
delete_confirm = WebDriverWait(self.web_driver, 2).until(
391-
EC.visibility_of_element_located(
392-
(By.CSS_SELECTOR, 'form[method="post"] input[type="submit"]')
393-
)
301+
self.find_element(by=By.CSS_SELECTOR, value='#warning-ack').click()
302+
delete_confirm = self.find_element(
303+
By.CSS_SELECTOR, 'form[method="post"] input[type="submit"]'
394304
)
395305
delete_confirm.click()
396306
self.assertEqual(Device.objects.count(), 0)
397307

398308

399-
class TestVpnAdmin(SeleniumBaseMixin, TestWireguardVpnMixin, StaticLiveServerTestCase):
309+
class TestVpnAdmin(
310+
SeleniumTestMixin,
311+
CreateConfigTemplateMixin,
312+
TestWireguardVpnMixin,
313+
StaticLiveServerTestCase,
314+
):
400315
def test_vpn_edit(self):
401316
self.login()
402317
device, vpn, template = self._create_wireguard_vpn_template()
403318
self.open(reverse('admin:config_vpn_change', args=[vpn.id]))
404319
with self.subTest('Ca and Cert should not be visible'):
405-
el = self.web_driver.find_element(by=By.CLASS_NAME, value='field-ca')
320+
el = self.find_element(by=By.CLASS_NAME, value='field-ca')
406321
self.assertFalse(el.is_displayed())
407-
el = self.web_driver.find_element(by=By.CLASS_NAME, value='field-cert')
322+
el = self.find_element(by=By.CLASS_NAME, value='field-cert')
408323
self.assertFalse(el.is_displayed())
409324

410325
with self.subTest('PrivateKey is shown in configuration preview'):
411-
self.web_driver.find_element(
412-
by=By.CSS_SELECTOR, value='.previewlink'
413-
).click()
414-
WebDriverWait(self.web_driver, 2).until(
415-
EC.visibility_of_element_located(
416-
(By.CSS_SELECTOR, '.djnjc-preformatted')
417-
)
418-
)
326+
self.find_element(by=By.CSS_SELECTOR, value='.previewlink').click()
327+
self.wait_for_visibility(By.CSS_SELECTOR, '.djnjc-preformatted')
419328
self.assertIn(
420329
f'PrivateKey = {vpn.private_key}',
421-
self.web_driver.find_element(
422-
by=By.CSS_SELECTOR, value='.djnjc-preformatted'
423-
).text,
330+
self.find_element(by=By.CSS_SELECTOR, value='.djnjc-preformatted').text,
424331
)
425332
# Close the configuration preview
426-
self.web_driver.find_element(
427-
by=By.CSS_SELECTOR, value='.djnjc-overlay a.close'
428-
).click()
333+
self.find_element(by=By.CSS_SELECTOR, value='.djnjc-overlay a.close').click()
429334

430335
with self.subTest('Changing VPN backend should hide webhook and authtoken'):
431-
backend = Select(self.web_driver.find_element(by=By.ID, value='id_backend'))
336+
backend = Select(self.find_element(by=By.ID, value='id_backend'))
432337
backend.select_by_visible_text('OpenVPN')
433-
el = self.web_driver.find_element(
434-
by=By.CLASS_NAME, value='field-webhook_endpoint'
435-
)
338+
el = self.find_element(by=By.CLASS_NAME, value='field-webhook_endpoint')
436339
self.assertFalse(el.is_displayed())
437-
el = self.web_driver.find_element(
438-
by=By.CLASS_NAME, value='field-auth_token'
439-
)
340+
el = self.find_element(by=By.CLASS_NAME, value='field-auth_token')
440341
self.assertFalse(el.is_displayed())

0 commit comments

Comments
 (0)