Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
17 changes: 7 additions & 10 deletions ietf/meeting/tests_js.py
Original file line number Diff line number Diff line change
Expand Up @@ -249,7 +249,9 @@ def test_edit_meeting_schedule(self):
self.assertTrue(s1_element.is_displayed()) # should still be displayed
self.assertIn('hidden-parent', s1_element.get_attribute('class'),
'Session should be hidden when parent disabled')
s1_element.click() # try to select

self.scroll_and_click((By.CSS_SELECTOR, '#session{}'.format(s1.pk)))

self.assertNotIn('selected', s1_element.get_attribute('class'),
'Session should not be selectable when parent disabled')

Expand Down Expand Up @@ -299,9 +301,9 @@ def test_edit_meeting_schedule(self):
'Session s1 should have moved to second meeting day')

# swap timeslot column - put session in a differently-timed timeslot
self.driver.find_element(By.CSS_SELECTOR,
self.scroll_and_click((By.CSS_SELECTOR,
'.day .swap-timeslot-col[data-timeslot-pk="{}"]'.format(slot1b.pk)
).click() # open modal on the second timeslot for room1
)) # open modal on the second timeslot for room1
self.assertTrue(self.driver.find_element(By.CSS_SELECTOR, "#swap-timeslot-col-modal").is_displayed())
self.driver.find_element(By.CSS_SELECTOR,
'#swap-timeslot-col-modal input[name="target_timeslot"][value="{}"]'.format(slot4.pk)
Expand Down Expand Up @@ -1373,13 +1375,8 @@ def test_upcoming_materials_modal(self):
self.assertFalse(modal_div.is_displayed())

# Click the 'materials' button
open_modal_button = self.wait.until(
expected_conditions.element_to_be_clickable(
(By.CSS_SELECTOR, '[data-bs-target="#modal-%s"]' % slug)
),
'Modal open button not found or not clickable',
)
open_modal_button.click()
open_modal_button_locator = (By.CSS_SELECTOR, '[data-bs-target="#modal-%s"]' % slug)
self.scroll_and_click(open_modal_button_locator)
self.wait.until(
expected_conditions.visibility_of(modal_div),
'Modal did not become visible after clicking open button',
Expand Down
1 change: 1 addition & 0 deletions ietf/settings.py
Original file line number Diff line number Diff line change
Expand Up @@ -598,6 +598,7 @@ def skip_unreadable_post(record):
"ietf/review/import_from_review_tool.py",
"ietf/utils/patch.py",
"ietf/utils/test_data.py",
"ietf/utils/jstest.py",
]

# These are code line regex patterns
Expand Down
44 changes: 44 additions & 0 deletions ietf/utils/jstest.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,8 @@
from selenium import webdriver
from selenium.webdriver.firefox.service import Service
from selenium.webdriver.firefox.options import Options
from selenium.webdriver.support.ui import WebDriverWait
from selenium.webdriver.support import expected_conditions
from selenium.webdriver.common.by import By
except ImportError as e:
skip_selenium = True
Expand Down Expand Up @@ -87,6 +89,48 @@ def scroll_to_element(self, element):
# actions = ActionChains(self.driver)
# actions.move_to_element(element).perform()

def scroll_and_click(self, element_locator, timeout_seconds=5):
"""
Selenium has restrictions around clicking elements outside the viewport, so
this wrapper encapsulates the boilerplate of forcing scrolling and clicking.

:param element_locator: A two item tuple of a Selenium locator eg `(By.CSS_SELECTOR, '#something')`
"""

# so that we can restore the state of the webpage after clicking
original_html_scroll_behaviour_to_restore = self.driver.execute_script('return document.documentElement.style.scrollBehavior')
original_html_overflow_to_restore = self.driver.execute_script('return document.documentElement.style.overflow')

original_body_scroll_behaviour_to_restore = self.driver.execute_script('return document.body.style.scrollBehavior')
original_body_overflow_to_restore = self.driver.execute_script('return document.body.style.overflow')

self.driver.execute_script('document.documentElement.style.scrollBehavior = "auto"')
self.driver.execute_script('document.documentElement.style.overflow = "auto"')

self.driver.execute_script('document.body.style.scrollBehavior = "auto"')
self.driver.execute_script('document.body.style.overflow = "auto"')

element = self.driver.find_element(element_locator[0], element_locator[1])
self.scroll_to_element(element)

# Note that Selenium itself seems to have multiple definitions of 'clickable'.
# You might expect that the following wait for the 'element_to_be_clickable'
# would confirm that the following .click() would succeed but it doesn't.
# That's why the preceeding code attempts to force scrolling to bring the
# element into the viewport to allow clicking.
WebDriverWait(self.driver, timeout_seconds).until(expected_conditions.element_to_be_clickable(element_locator))

element.click()

if original_html_scroll_behaviour_to_restore:
self.driver.execute_script(f'document.documentElement.style.scrollBehavior = "{original_html_scroll_behaviour_to_restore}"')
if original_html_overflow_to_restore:
self.driver.execute_script(f'document.documentElement.style.overflow = "{original_html_overflow_to_restore}"')

if original_body_scroll_behaviour_to_restore:
self.driver.execute_script(f'document.body.style.scrollBehavior = "{original_body_scroll_behaviour_to_restore}"')
if original_body_overflow_to_restore:
self.driver.execute_script(f'document.body.style.overflow = "{original_body_overflow_to_restore}"')

class presence_of_element_child_by_css_selector:
"""Wait for presence of a child of a WebElement matching a CSS selector
Expand Down