diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml
index 027fe4e1..de3f1942 100644
--- a/.github/workflows/build.yml
+++ b/.github/workflows/build.yml
@@ -73,6 +73,8 @@ jobs:
SAMPLE_APP=1 ./runtests.py
coverage run runtests.py --parallel
coverage combine
+ env:
+ SELENIUM_HEADLESS: 1
- name: Upload Coverage
run: coveralls --service=github
diff --git a/openwisp_notifications/static/openwisp-notifications/js/notifications.js b/openwisp_notifications/static/openwisp-notifications/js/notifications.js
index 6311f38a..16fa3312 100644
--- a/openwisp_notifications/static/openwisp-notifications/js/notifications.js
+++ b/openwisp_notifications/static/openwisp-notifications/js/notifications.js
@@ -195,8 +195,9 @@ function notificationWidget($) {
}
function notificationListItem(elem) {
- let klass,
- datetime = dateTimeStampToDateTimeLocaleString(new Date(elem.timestamp));
+ let klass;
+ const datetime = dateTimeStampToDateTimeLocaleString(new Date(elem.timestamp)),
+ target_url = new URL(elem.target_url);
if (!notificationReadStatus.has(elem.id)) {
if (elem.unread) {
@@ -207,8 +208,23 @@ function notificationWidget($) {
}
klass = notificationReadStatus.get(elem.id);
+ // Used to convert absolute URLs in notification messages to relative paths
+ function convertMessageWithRelativeURL(htmlString) {
+ const parser = new DOMParser(),
+ doc = parser.parseFromString(htmlString, 'text/html'),
+ links = doc.querySelectorAll('a');
+ links.forEach((link) => {
+ let url = link.getAttribute('href');
+ if (url) {
+ url = new URL(url);
+ link.setAttribute('href', url.pathname);
+ }
+ });
+ return doc.body.innerHTML;
+ }
+
return `
+ data-location="${target_url.pathname}" role="link" tabindex="0">
- ${elem.message}
+ ${convertMessageWithRelativeURL(elem.message)}
`;
}
diff --git a/openwisp_notifications/tests/test_widget.py b/openwisp_notifications/tests/test_widget.py
new file mode 100644
index 00000000..0fc011ca
--- /dev/null
+++ b/openwisp_notifications/tests/test_widget.py
@@ -0,0 +1,56 @@
+from django.contrib.staticfiles.testing import StaticLiveServerTestCase
+from selenium.webdriver.common.by import By
+from selenium.webdriver.support import expected_conditions as EC
+from selenium.webdriver.support.ui import WebDriverWait
+
+from openwisp_notifications.swapper import load_model
+from openwisp_notifications.utils import _get_object_link
+from openwisp_users.tests.utils import TestOrganizationMixin
+from openwisp_utils.test_selenium_mixins import SeleniumTestMixin
+
+Notification = load_model('Notification')
+
+
+class TestWidget(
+ SeleniumTestMixin,
+ TestOrganizationMixin,
+ StaticLiveServerTestCase,
+):
+ serve_static = True
+
+ def setUp(self):
+ self.admin = self._create_admin(
+ username=self.admin_username, password=self.admin_password
+ )
+
+ def test_notification_relative_link(self):
+ self.login()
+ operator = super()._create_operator()
+ data = dict(
+ email_subject='Test Email subject',
+ url='http://localhost:8000/admin/',
+ )
+ notification = Notification.objects.create(
+ actor=self.admin,
+ recipient=self.admin,
+ description='Test Notification Description',
+ verb='Test Notification',
+ action_object=operator,
+ target=operator,
+ data=data,
+ )
+ self.web_driver.implicitly_wait(10)
+ WebDriverWait(self.web_driver, 10).until(
+ EC.visibility_of_element_located((By.ID, 'openwisp_notifications'))
+ )
+ self.web_driver.find_element(By.ID, 'openwisp_notifications').click()
+ WebDriverWait(self.web_driver, 10).until(
+ EC.visibility_of_element_located((By.CLASS_NAME, 'ow-notification-elem'))
+ )
+ notification_elem = self.web_driver.find_element(
+ By.CLASS_NAME, 'ow-notification-elem'
+ )
+ data_location_value = notification_elem.get_attribute('data-location')
+ self.assertEqual(
+ data_location_value, _get_object_link(notification, 'target', False)
+ )
diff --git a/requirements-test.txt b/requirements-test.txt
index 1eacb225..57b38551 100644
--- a/requirements-test.txt
+++ b/requirements-test.txt
@@ -1,4 +1,4 @@
-openwisp-utils[qa] @ https://github.com/openwisp/openwisp-utils/tarball/master
+openwisp-utils[qa,selenium] @ https://github.com/openwisp/openwisp-utils/tarball/master
django-cors-headers~=4.0.0
django-redis~=5.2.0
channels_redis~=4.1.0