Skip to content

chore: relative url for notification links #266

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 11 commits into from
Apr 22, 2024
2 changes: 2 additions & 0 deletions .github/workflows/build.yml
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -197,6 +197,7 @@ function notificationWidget($) {
function notificationListItem(elem) {
let klass,
datetime = dateTimeStampToDateTimeLocaleString(new Date(elem.timestamp));
const target_url = new URL(elem.target_url);

if (!notificationReadStatus.has(elem.id)) {
if (elem.unread) {
Expand All @@ -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();
const doc = parser.parseFromString(htmlString, 'text/html');
const 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 `<div class="ow-notification-elem ${klass}" id=ow-${elem.id}
data-location="${elem.target_url}" role="link" tabindex="0">
data-location="${target_url.pathname}" role="link" tabindex="0">
<div class="ow-notification-inner">
<div class="ow-notification-meta">
<div class="ow-notification-level-wrapper">
Expand All @@ -217,7 +233,7 @@ function notificationWidget($) {
</div>
<div class="ow-notification-date">${datetime}</div>
</div>
${elem.message}
${convertMessageWithRelativeURL(elem.message)}
</div>
</div>`;
}
Expand Down
52 changes: 51 additions & 1 deletion openwisp_notifications/tests/test_notifications.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
from django.apps.registry import apps
from django.contrib.auth import get_user_model
from django.contrib.contenttypes.models import ContentType
from django.contrib.staticfiles.testing import StaticLiveServerTestCase
from django.core import mail
from django.core.cache import cache
from django.core.exceptions import ImproperlyConfigured
Expand All @@ -14,6 +15,9 @@
from django.urls import reverse
from django.utils import timezone
from django.utils.timesince import timesince
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 import settings as app_settings
from openwisp_notifications import tasks
Expand All @@ -33,8 +37,9 @@
_unregister_notification_choice,
get_notification_configuration,
)
from openwisp_notifications.utils import _get_absolute_url
from openwisp_notifications.utils import _get_absolute_url, _get_object_link
from openwisp_users.tests.utils import TestOrganizationMixin
from openwisp_utils.test_selenium_mixins import SeleniumTestMixin
from openwisp_utils.tests import capture_any_output

User = get_user_model()
Expand Down Expand Up @@ -927,3 +932,48 @@ def test_notification_cache_update(self):
self.assertEqual(notification.target.username, 'new operator name')
# Done for populating cache
self.assertEqual(operator_cache.username, 'new operator name')


class SeleniumTestNotifications(
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://127.0.0.1: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)
)
1 change: 1 addition & 0 deletions requirements-test.txt
Original file line number Diff line number Diff line change
Expand Up @@ -5,3 +5,4 @@ channels_redis~=4.1.0
pytest-asyncio~=0.21.0
pytest-django~=4.5.0
freezegun~=1.2.2
selenium~=4.18.1
3 changes: 3 additions & 0 deletions tests/openwisp2/settings.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,9 @@
'default': {
'ENGINE': 'django.db.backends.sqlite3',
'NAME': os.path.join(BASE_DIR, 'openwisp_notifications.db'),
"TEST": {
"NAME": os.path.join(BASE_DIR, "openwisp_notifications_test.db"),
},
}
}

Expand Down