Skip to content

Commit 990bf08

Browse files
committed
[tests] Fixed selenium tests
1 parent 1989ad6 commit 990bf08

File tree

5 files changed

+44
-37
lines changed

5 files changed

+44
-37
lines changed

openwisp_controller/config/tests/test_selenium.py

Lines changed: 25 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,7 @@
11
from django.contrib.staticfiles.testing import StaticLiveServerTestCase
22
from django.test import tag
33
from django.urls.base import reverse
4-
from selenium.common.exceptions import (
5-
StaleElementReferenceException,
6-
TimeoutException,
7-
UnexpectedAlertPresentException,
8-
)
4+
from selenium.common.exceptions import TimeoutException
95
from selenium.webdriver.common.action_chains import ActionChains
106
from selenium.webdriver.common.alert import Alert
117
from selenium.webdriver.common.by import By
@@ -117,9 +113,10 @@ def test_device_preview_keyboard_shortcuts(self):
117113
def test_unsaved_changes(self):
118114
self.login()
119115
device = self._create_config(organization=self._get_org()).device
120-
self.open(reverse('admin:config_device_change', args=[device.id]))
116+
path = reverse('admin:config_device_change', args=[device.id])
117+
self.open(path)
121118
with self.subTest('Alert should not be displayed without any change'):
122-
self.web_driver.refresh()
119+
self.open(path)
123120
try:
124121
WebDriverWait(self.web_driver, 1).until(EC.alert_is_present())
125122
except TimeoutException:
@@ -143,8 +140,7 @@ def test_unsaved_changes(self):
143140
print(entry)
144141
self.fail('Timed out wating for unsaved changes alert')
145142
else:
146-
alert = Alert(self.web_driver)
147-
alert.accept()
143+
self.web_driver.switch_to.alert.accept()
148144

149145
def test_multiple_organization_templates(self):
150146
shared_required_template = self._create_template(
@@ -176,7 +172,6 @@ def test_multiple_organization_templates(self):
176172
reverse('admin:config_device_change', args=[org1_device.id])
177173
+ '#config-group'
178174
)
179-
wait = WebDriverWait(self.web_driver, 2)
180175
# org2 templates should not be visible
181176
self.wait_for_invisibility(
182177
By.XPATH, f'//*[@value="{org2_required_template.id}"]'
@@ -253,16 +248,27 @@ def test_force_delete_device_with_deactivating_config(self):
253248

254249
self.login()
255250
self.open(reverse('admin:config_device_change', args=[device.id]))
256-
self.find_elements(by=By.CSS_SELECTOR, value='input.deletelink[type="submit"]')[
257-
-1
258-
].click()
251+
# The webpage has two "submit-row" sections, each containing a "Deactivate"
252+
# button. The first (top) "Deactivate" button is hidden, causing
253+
# `wait_for_visibility` to fail. To avoid this issue, we use
254+
# `wait_for='presence'` instead, ensuring we locat the elements regardless
255+
# of visibility. We then select the last (visible) button and click it.
256+
self.find_elements(
257+
by=By.CSS_SELECTOR,
258+
value='input.deletelink[type="submit"]',
259+
wait_for='presence',
260+
)[-1].click()
259261
device.refresh_from_db()
260262
config.refresh_from_db()
261263
self.assertEqual(device.is_deactivated(), True)
262264
self.assertEqual(config.is_deactivating(), True)
263265

264266
self.open(reverse('admin:config_device_change', args=[device.id]))
265-
self.find_elements(by=By.CSS_SELECTOR, value='a.deletelink')[-1].click()
267+
# Use `presence` instead of `visibility` for `wait_for`,
268+
# as the same issue described above applies here.
269+
self.find_elements(
270+
by=By.CSS_SELECTOR, value='a.deletelink', wait_for='presence'
271+
)[-1].click()
266272
self.wait_for_visibility(
267273
By.CSS_SELECTOR, '#deactivating-warning .messagelist .warning p'
268274
)
@@ -306,6 +312,7 @@ def test_force_delete_multiple_devices_with_deactivating_config(self):
306312
self.assertEqual(Device.objects.count(), 0)
307313

308314

315+
@tag('selenium_tests')
309316
class TestVpnAdmin(
310317
SeleniumTestMixin,
311318
CreateConfigTemplateMixin,
@@ -317,10 +324,8 @@ def test_vpn_edit(self):
317324
device, vpn, template = self._create_wireguard_vpn_template()
318325
self.open(reverse('admin:config_vpn_change', args=[vpn.id]))
319326
with self.subTest('Ca and Cert should not be visible'):
320-
el = self.find_element(by=By.CLASS_NAME, value='field-ca')
321-
self.assertFalse(el.is_displayed())
322-
el = self.find_element(by=By.CLASS_NAME, value='field-cert')
323-
self.assertFalse(el.is_displayed())
327+
self.wait_for_invisibility(by=By.CLASS_NAME, value='field-ca')
328+
self.wait_for_invisibility(by=By.CLASS_NAME, value='field-cert')
324329

325330
with self.subTest('PrivateKey is shown in configuration preview'):
326331
self.find_element(by=By.CSS_SELECTOR, value='.previewlink').click()
@@ -335,7 +340,5 @@ def test_vpn_edit(self):
335340
with self.subTest('Changing VPN backend should hide webhook and authtoken'):
336341
backend = Select(self.find_element(by=By.ID, value='id_backend'))
337342
backend.select_by_visible_text('OpenVPN')
338-
el = self.find_element(by=By.CLASS_NAME, value='field-webhook_endpoint')
339-
self.assertFalse(el.is_displayed())
340-
el = self.find_element(by=By.CLASS_NAME, value='field-auth_token')
341-
self.assertFalse(el.is_displayed())
343+
self.wait_for_invisibility(by=By.CLASS_NAME, value='field-webhook_endpoint')
344+
self.wait_for_invisibility(by=By.CLASS_NAME, value='field-auth_token')

openwisp_controller/connection/static/connection/js/commands.js

Lines changed: 15 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,6 @@ django.jQuery(function ($) {
2727
timeoutInterval: 7000,
2828
},
2929
);
30-
commandWebSocket.open();
3130
let selector = $("#id_command_set-0-type"),
3231
showFields = function () {
3332
var fields = $(
@@ -40,18 +39,24 @@ django.jQuery(function ($) {
4039
$("#command_set-2-group fieldset .dynamic-command_set-2:first");
4140
fields.show();
4241
}
42+
},
43+
init = function () {
44+
showFields();
45+
initCommandDropdown($);
46+
initCommandOverlay($);
47+
initCommandWebSockets($, commandWebSocket);
4348
};
4449
selector.change(function () {
4550
showFields();
4651
});
47-
48-
$("#id_command_set-0-input").one("jsonschema-schemaloaded", function () {
49-
showFields();
50-
51-
initCommandDropdown($);
52-
initCommandOverlay($);
53-
initCommandWebSockets($, commandWebSocket);
54-
});
52+
if (django._schemas[$("#id_command_set-0-input").data("schema-url")]) {
53+
// It is possible that the schema is loaded before the event handler
54+
// is attached to the element. In that case, we need to manually call
55+
// the init function.
56+
init();
57+
} else {
58+
$("#id_command_set-0-input").one("jsonschema-schemaloaded", init);
59+
}
5560
});
5661

5762
function initCommandDropdown($) {
@@ -542,6 +547,7 @@ function initCommandOverlay($) {
542547
}
543548

544549
function initCommandWebSockets($, commandWebSocket) {
550+
commandWebSocket.open();
545551
commandWebSocket.addEventListener("message", function (e) {
546552
let data = JSON.parse(e.data);
547553
// Done for keeping future use of these websocket

openwisp_controller/connection/tests/test_selenium.py

Lines changed: 0 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,10 @@
11
from django.contrib.staticfiles.testing import StaticLiveServerTestCase
22
from django.test import tag
33
from django.urls import reverse
4-
import ipdb
54
from selenium.webdriver.common.by import By
65
from swapper import load_model
76

87
from ...tests.utils import DeviceAdminSeleniumTextMixin
9-
108
from .utils import CreateConnectionsMixin
119

1210
Command = load_model('connection', 'Command')
@@ -43,10 +41,8 @@ def test_command_widget_on_device(self):
4341
self.wait_for_invisibility(By.CSS_SELECTOR, 'ul.object-tools a#send-command')
4442
self._create_device_connection(device=device, credentials=creds)
4543
self.assertEqual(device.deviceconnection_set.count(), 1)
46-
import time; time.sleep(10)
4744
self.open(path)
4845
# Send reboot command to the device
49-
# import ipdb; ipdb.set_trace()
5046
self.find_element(
5147
by=By.CSS_SELECTOR, value='ul.object-tools a#send-command', timeout=5
5248
).click()

openwisp_controller/tests/test_selenium.py

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -90,7 +90,10 @@ def test_restoring_deleted_device(self, *args):
9090
# By checking that there are no WARNING level errors logged in the
9191
# browser console, we ensure that this issue is not happening.
9292
for error in self.get_browser_logs():
93-
self.assertNotEqual(error['level'], 'WARNING')
93+
if error['level'] == 'WARNING' and error['message'] not in [
94+
'wrong event specified: touchleave'
95+
]:
96+
self.fail(f'Browser console error: {error["message"]}')
9497
self.find_element(
9598
by=By.XPATH, value='//*[@id="device_form"]/div/div[1]/input[1]'
9699
).click()

openwisp_controller/tests/utils.py

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -75,7 +75,6 @@ def tearDown(self):
7575
pass
7676
else:
7777
self.web_driver.switch_to_alert().accept()
78-
self.web_driver.switch_to_alert.accept()
7978
self.web_driver.refresh()
8079
self.wait_for_visibility(By.XPATH, '//*[@id="site-name"]')
8180

0 commit comments

Comments
 (0)