fix: Selenium tests via scroll_and_click (#8150)

* fix: selenium tests scroll_and_click

* fix: reduce default timeout to 5 seconds

* fix: also use scroll_and_click on test_upcoming_materials_modal

* fix: remove conditional check on restoring scroll CSS

* fix: restore conditional check on restoring scroll CSS

* chore: code comments and adding jstest.py to coverage ignore
This commit is contained in:
Matthew Holloway 2024-11-05 15:03:21 +00:00 committed by GitHub
parent d530e9aaba
commit de494790b6
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
3 changed files with 52 additions and 10 deletions

View file

@ -249,7 +249,9 @@ class EditMeetingScheduleTests(IetfSeleniumTestCase):
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')
@ -299,9 +301,9 @@ class EditMeetingScheduleTests(IetfSeleniumTestCase):
'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)
@ -1373,13 +1375,8 @@ class InterimTests(IetfSeleniumTestCase):
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',

View file

@ -598,6 +598,7 @@ TEST_CODE_COVERAGE_EXCLUDE_FILES = [
"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

View file

@ -12,6 +12,8 @@ try:
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
@ -87,6 +89,48 @@ class IetfSeleniumTestCase(IetfLiveServerTestCase):
# 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