Skip to content

Commit

Permalink
test: add a JS unit test to verify filterSubVerticals
Browse files Browse the repository at this point in the history
  • Loading branch information
Ali-D-Akbar committed Feb 6, 2025
1 parent 2e5327a commit 2f0f3dc
Show file tree
Hide file tree
Showing 2 changed files with 151 additions and 2 deletions.
3 changes: 2 additions & 1 deletion conftest.py
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,8 @@
# List of test classes that are backed by TransactionTestCase
TTC = ['course_discovery/apps/course_metadata/management/commands/tests/test_refresh_course_metadata.py::'
'RefreshCourseMetadataCommandTests',
'course_discovery/apps/course_metadata/tests/test_admin.py::ProgramAdminFunctionalTests']
'course_discovery/apps/course_metadata/tests/test_admin.py::ProgramAdminFunctionalTests',
'course_discovery/apps/tagging/tests/test_views.py::CourseTaggingDetailViewJSTests']


class LoadScopeSchedulingDjangoOrdered(LoadScopeScheduling):
Expand Down
150 changes: 149 additions & 1 deletion course_discovery/apps/tagging/tests/test_views.py
Original file line number Diff line number Diff line change
@@ -1,8 +1,14 @@
from ddt import data, ddt
from django.conf import settings
from django.contrib.auth.models import Group
from django.test import TestCase
from django.contrib.contenttypes.models import ContentType
from django.test import LiveServerTestCase, TestCase
from django.urls import reverse
from selenium import webdriver
from selenium.webdriver.common.by import By
from selenium.webdriver.firefox.options import Options
from selenium.webdriver.support import expected_conditions as EC
from selenium.webdriver.support.ui import Select, WebDriverWait

from course_discovery.apps.core.tests.factories import UserFactory
from course_discovery.apps.course_metadata.tests.factories import CourseFactory
Expand Down Expand Up @@ -68,6 +74,148 @@ def test_post_invalid_sub_vertical(self):
self.assertContains(response, 'Sub-vertical does not belong to the selected vertical.')


class CourseTaggingDetailViewJSTests(LiveServerTestCase):
"""
Functional tests using Selenium to verify the JS script filterSubVerticals behavior in the CourseTaggingDetailView.
"""

@classmethod
def setUpClass(cls):
super().setUpClass()
firefox_options = Options()
firefox_options.headless = True
cls.driver = webdriver.Firefox(options=firefox_options)
cls.driver.implicitly_wait(10)

@classmethod
def tearDownClass(cls):
cls.driver.quit()
super().tearDownClass()

def _wait_for_page_load(self, timeout=10):
"""Wait until the page's document.readyState is 'complete'."""
WebDriverWait(self.driver, timeout).until(
lambda d: d.execute_script("return document.readyState") == "complete"
)

def setUp(self):
"""
Create a superuser, a course, verticals and sub-verticals used for testing.
"""
super().setUp()
ContentType.objects.clear_cache()

self.user = UserFactory(username='superuser', is_superuser=True, is_staff=True)
self.user.set_password('password')
self.user.save()

self.course = CourseFactory(title='Advanced Python')

self.vertical = VerticalFactory(name='vertical')
self.sub_vertical = SubVerticalFactory(name='sub_vertical', vertical=self.vertical)

self.other_vertical = VerticalFactory(name='other_vertical')
self.other_sub_vertical = SubVerticalFactory(name='other_sub_vertical', vertical=self.other_vertical)

self.multi_vertical = VerticalFactory(name='multi_vertical')
self.multi_sub_vertical1 = SubVerticalFactory(name='multi_sub_vertical1', vertical=self.multi_vertical)
self.multi_sub_vertical2 = SubVerticalFactory(name='multi_sub_vertical2', vertical=self.multi_vertical)

_ = CourseVerticalFactory(course=self.course, vertical=self.vertical, sub_vertical=self.sub_vertical)

self.url = self.live_server_url + reverse('tagging:course_tagging_detail', kwargs={'uuid': self.course.uuid})

self._login()

self.driver.get(self.url)
self._wait_for_page_load()

def _login(self):
"""Log into Django via test client, then add the session cookie to Selenium."""
self.client.force_login(self.user)
session_cookie = self.client.cookies[settings.SESSION_COOKIE_NAME]
self.driver.get(self.live_server_url)
cookie_dict = {
'name': settings.SESSION_COOKIE_NAME,
'value': session_cookie.value,
'path': '/',
}
self.driver.add_cookie(cookie_dict)
self.driver.get(self.url)
self._wait_for_page_load()

def get_visible_options(self, select_id):
"""
Returns a list of visible <option> elements for the given select element.
Visible options are defined as those with a 'data-vertical' attribute and no inline 'display: none' style.
"""
select_element = self.driver.find_element(By.ID, select_id)
options = select_element.find_elements(By.TAG_NAME, 'option')
visible_options = [
option for option in options
if option.get_attribute('data-vertical') is not None and
'display: none' not in (option.get_attribute('style') or '')
]
return visible_options

def filter_sub_verticals(self, vertical_slug, expected_slugs):
"""
Selects the vertical with the given slug, triggers the JavaScript change event, and waits
until the expected sub-vertical options (by their slug values) are visible in the sub_vertical select.
Returns the list of visible option elements.
"""
vertical_element = WebDriverWait(self.driver, 20).until(
EC.presence_of_element_located((By.ID, 'vertical'))
)
vertical_select = Select(vertical_element)
vertical_select.select_by_value(vertical_slug)
self.driver.execute_script(
"document.getElementById('vertical').dispatchEvent(new Event('change'));"
)
WebDriverWait(self.driver, 10).until(
lambda d: set(
option.get_attribute('value')
for option in self.get_visible_options('sub_vertical')
) == set(expected_slugs)
)
return self.get_visible_options('sub_vertical')

def test_initial_load(self):
"""
Test that on initial load (before any user interaction), the course displays the pre-assigned vertical
and sub-vertical.
"""
vertical_select = Select(self.driver.find_element(By.ID, 'vertical'))
selected_vertical = vertical_select.first_selected_option.get_attribute('value')
self.assertEqual(selected_vertical, self.vertical.slug)

sub_vertical_select = Select(self.driver.find_element(By.ID, 'sub_vertical'))
selected_sub_vertical = sub_vertical_select.first_selected_option.get_attribute('value')
self.assertEqual(selected_sub_vertical, self.sub_vertical.slug)

def test_filter_sub_verticals_javascript(self):
"""
Verify that selecting a vertical with one sub-vertical shows the expected single sub-vertical
and a vertical with two sub-verticals shows both sub-verticals.
"""
visible_options = self.filter_sub_verticals(self.vertical.slug, [self.sub_vertical.slug])
self.assertEqual(len(visible_options), 1)
self.assertEqual(visible_options[0].get_attribute('value'), self.sub_vertical.slug)

visible_options = self.filter_sub_verticals(self.other_vertical.slug, [self.other_sub_vertical.slug])
self.assertEqual(len(visible_options), 1)
self.assertEqual(visible_options[0].get_attribute('value'), self.other_sub_vertical.slug)

visible_options = self.filter_sub_verticals(
self.multi_vertical.slug,
[self.multi_sub_vertical1.slug, self.multi_sub_vertical2.slug]
)
option_values = sorted([option.get_attribute('value') for option in visible_options])
expected_values = sorted([self.multi_sub_vertical1.slug, self.multi_sub_vertical2.slug])
self.assertEqual(len(visible_options), 2)
self.assertEqual(option_values, expected_values)


@ddt
class CourseListViewTests(BaseViewsTestCase):
"""Tests for the CourseListView."""
Expand Down

0 comments on commit 2f0f3dc

Please sign in to comment.