From 70e6fde69f0c11011f284b390da0ee9225bf8eb5 Mon Sep 17 00:00:00 2001
From: Michael Plunkett <5885605+michplunkett@users.noreply.github.com>
Date: Fri, 22 Nov 2024 21:28:53 -0600
Subject: [PATCH] Address Selenium warnings (#1135)

---
 OpenOversight/tests/conftest.py        |  8 +++-
 OpenOversight/tests/test_functional.py | 54 +++++++++++++-------------
 dev-requirements.txt                   |  2 +-
 dockerfiles/web/Dockerfile-test        |  4 +-
 requirements.txt                       |  3 +-
 5 files changed, 38 insertions(+), 33 deletions(-)

diff --git a/OpenOversight/tests/conftest.py b/OpenOversight/tests/conftest.py
index c494b1edc..4c51a8cd1 100644
--- a/OpenOversight/tests/conftest.py
+++ b/OpenOversight/tests/conftest.py
@@ -16,6 +16,8 @@
 from flask import current_app
 from PIL import Image as Pimage
 from selenium import webdriver
+from selenium.webdriver.firefox.options import Options
+from selenium.webdriver.firefox.service import Service
 from sqlalchemy.orm import scoped_session, sessionmaker
 from xvfbwrapper import Xvfb
 
@@ -906,7 +908,11 @@ def browser(app, server_port):
     # start headless webdriver
     visual_display = Xvfb()
     visual_display.start()
-    driver = webdriver.Firefox(service_log_path="/tmp/geckodriver.log")
+    options = Options()
+    options.add_argument("--headless")
+    driver = webdriver.Firefox(
+        options=options, service=Service(log_path="/tmp/geckodriver.log")
+    )
     # wait for browser to start up
     sleep(3)
     yield driver
diff --git a/OpenOversight/tests/test_functional.py b/OpenOversight/tests/test_functional.py
index 77c61d40f..e7098b40b 100644
--- a/OpenOversight/tests/test_functional.py
+++ b/OpenOversight/tests/test_functional.py
@@ -21,7 +21,7 @@
 
 @contextmanager
 def wait_for_page_load(browser, timeout=10):
-    old_page = browser.find_element_by_tag_name(FILE_TYPE_HTML)
+    old_page = browser.find_element(By.TAG_NAME, FILE_TYPE_HTML)
     yield
     WebDriverWait(browser, timeout).until(expected_conditions.staleness_of(old_page))
 
@@ -29,14 +29,14 @@ def wait_for_page_load(browser, timeout=10):
 def login_admin(browser, server_port):
     browser.get(f"http://localhost:{server_port}/auth/login")
     with wait_for_page_load(browser):
-        elem = browser.find_element_by_id("email")
+        elem = browser.find_element(By.ID, "email")
         elem.clear()
         elem.send_keys(ADMIN_USER_EMAIL)
-        elem = browser.find_element_by_id("password")
+        elem = browser.find_element(By.ID, "password")
         elem.clear()
         elem.send_keys("testtest")
         with wait_for_page_load(browser):
-            browser.find_element_by_id("submit").click()
+            browser.find_element(By.ID, "submit").click()
             wait_for_element(browser, By.TAG_NAME, "body")
 
 
@@ -91,7 +91,7 @@ def test_user_can_load_homepage_and_get_to_form(mockdata, browser, server_port):
     scroll_to_element(browser, element)
     element.click()
 
-    page_text = browser.find_element_by_tag_name("body").text
+    page_text = browser.find_element(By.TAG_NAME, "body").text
     assert "Find an Officer" in page_text
 
 
@@ -103,10 +103,10 @@ def test_user_can_get_to_complaint(browser, server_port):
     )
 
     wait_for_element(browser, By.TAG_NAME, "h1")
-    # Complainant arrives at page with the badge number, name, and link
+    # Complaint arrives at page with the badge number, name, and link
     # to complaint form
 
-    title_text = browser.find_element_by_tag_name("h1").text
+    title_text = browser.find_element(By.TAG_NAME, "h1").text
     assert "File a Complaint" in title_text
 
 
@@ -118,7 +118,7 @@ def test_officer_browse_pagination(mockdata, browser, server_port):
         f"http://localhost:{server_port}/departments/{AC_DEPT}?page=1&gender=Not+Sure"
     )
     wait_for_element(browser, By.TAG_NAME, "body")
-    page_text = browser.find_element_by_tag_name("body").text
+    page_text = browser.find_element(By.TAG_NAME, "body").text
     expected = f"Showing 1-{current_app.config[KEY_OFFICERS_PER_PAGE]} of {total}"
     assert expected in page_text
 
@@ -135,7 +135,7 @@ def test_officer_browse_pagination(mockdata, browser, server_port):
         f"http://localhost:{server_port}/departments/{AC_DEPT}?page={last_page_index}&gender=Not+Sure"
     )
     wait_for_element(browser, By.TAG_NAME, "body")
-    page_text = browser.find_element_by_tag_name("body").text
+    page_text = browser.find_element(By.TAG_NAME, "body").text
     start_of_page = (
         current_app.config[KEY_OFFICERS_PER_PAGE]
         * (total // current_app.config[KEY_OFFICERS_PER_PAGE])
@@ -162,8 +162,8 @@ def test_find_officer_can_see_uii_question_for_depts_with_uiis(
         Department.unique_internal_identifier_label.is_not(None)
     ).first()
 
-    dept_selector = Select(browser.find_element_by_id("dept"))
-    uii_element = browser.find_element_by_id("uii-question")
+    dept_selector = Select(browser.find_element(By.ID, "dept"))
+    uii_element = browser.find_element(By.ID, "uii-question")
 
     dept_selector.select_by_value(str(dept_with_uii.id))
     assert uii_element.is_displayed()
@@ -179,11 +179,11 @@ def test_find_officer_cannot_see_uii_question_for_depts_without_uiis(
         unique_internal_identifier_label=None
     ).first()
 
-    dept_selector = browser.find_element_by_id("dept")
+    dept_selector = browser.find_element(By.ID, "dept")
     scroll_to_element(browser, dept_selector)
     Select(dept_selector).select_by_value(str(dept_without_uii.id))
 
-    uii_element = browser.find_element("id", "uii-question")
+    uii_element = browser.find_element(By.ID, "uii-question")
     assert not uii_element.is_displayed()
 
 
@@ -198,7 +198,7 @@ def test_incident_detail_display_read_more_button_for_descriptions_over_cutoff(
     ).one_or_none()
     incident_id = str(incident_long_description.id)
 
-    result = browser.find_element_by_id("description-overflow-row_" + incident_id)
+    result = browser.find_element(By.ID, "description-overflow-row_" + incident_id)
     scroll_to_element(browser, result)
     assert result.is_displayed()
 
@@ -216,7 +216,7 @@ def test_incident_detail_truncate_description_for_descriptions_over_cutoff(
 
     # Check that the text is truncated and contains more than just the ellipsis
     truncated_text = browser.find_element(
-        "id", "incident-description_" + incident_id
+        By.ID, "incident-description_" + incident_id
     ).text
     assert "…" in truncated_text
     # Include buffer for jinja rendered spaces
@@ -230,7 +230,7 @@ def test_incident_detail_do_not_display_read_more_button_for_descriptions_under_
     browser.get(f"http://localhost:{server_port}/officers/1")
 
     # Select incident for officer that has description under cutoff chars
-    result = browser.find_element_by_id("description-overflow-row_1")
+    result = browser.find_element(By.ID, "description-overflow-row_1")
     scroll_to_element(browser, result)
     assert not result.is_displayed()
 
@@ -245,12 +245,12 @@ def test_click_to_read_more_displays_full_description(mockdata, browser, server_
     original_description = incident_long_description.description.strip()
     incident_id = str(incident_long_description.id)
 
-    button = browser.find_element_by_id("description-overflow-button_" + incident_id)
+    button = browser.find_element(By.ID, "description-overflow-button_" + incident_id)
     scroll_to_element(browser, button)
     button.click()
 
-    description_text = browser.find_element_by_id(
-        "incident-description_" + incident_id
+    description_text = browser.find_element(
+        By.ID, "incident-description_" + incident_id
     ).text.strip()
     assert len(description_text) == len(original_description)
     assert description_text == original_description
@@ -265,11 +265,11 @@ def test_click_to_read_more_hides_the_read_more_button(mockdata, browser, server
     ).one_or_none()
     incident_id = str(incident_long_description.id)
 
-    button = browser.find_element_by_id("description-overflow-button_" + incident_id)
+    button = browser.find_element(By.ID, "description-overflow-button_" + incident_id)
     scroll_to_element(browser, button)
     button.click()
 
-    buttonRow = browser.find_element_by_id("description-overflow-row_" + incident_id)
+    buttonRow = browser.find_element(By.ID, "description-overflow-row_" + incident_id)
     assert not buttonRow.is_displayed()
 
 
@@ -286,13 +286,13 @@ def test_officer_form_has_units_alpha_sorted(browser, server_port, session):
 
     # Check for the Unit sort on the 'add officer' form
     browser.get(f"http://localhost:{server_port}/officers/new")
-    unit_select = Select(browser.find_element_by_id("unit"))
+    unit_select = Select(browser.find_element(By.ID, "unit"))
     select_units_sorted = [x.text for x in unit_select.options]
     assert db_units_sorted == select_units_sorted
 
     # Check for the Unit sort on the 'add assignment' form
     browser.get(f"http://localhost:{server_port}/officers/1")
-    unit_select = Select(browser.find_element_by_id("unit"))
+    unit_select = Select(browser.find_element(By.ID, "unit"))
     select_units_sorted = [x.text for x in unit_select.options]
     assert db_units_sorted == select_units_sorted
 
@@ -312,13 +312,13 @@ def test_edit_officer_form_coerces_none_race_or_gender_to_not_sure(
     browser.get(f"http://localhost:{server_port}/officers/1/edit")
 
     wait_for_element(browser, By.ID, "gender")
-    select = Select(browser.find_element_by_id("gender"))
+    select = Select(browser.find_element(By.ID, "gender"))
     selected_option = select.first_selected_option
     selected_text = selected_option.text
     assert selected_text == "Not Sure"
 
     wait_for_element(browser, By.ID, "race")
-    select = Select(browser.find_element_by_id("race"))
+    select = Select(browser.find_element(By.ID, "race"))
     selected_option = select.first_selected_option
     selected_text = selected_option.text
     assert selected_text == "Not Sure"
@@ -344,7 +344,7 @@ def test_image_classification_and_tagging(browser, server_port):
     browser.get(f"http://localhost:{server_port}/officers/new")
     wait_for_page_load(browser)
 
-    dept_select = Select(browser.find_element("id", "department"))
+    dept_select = Select(browser.find_element(By.ID, "department"))
     dept_select.select_by_visible_text("Auburn Police Department")
     dept_id = dept_select.first_selected_option.get_attribute("value")
 
@@ -364,7 +364,7 @@ def test_image_classification_and_tagging(browser, server_port):
     browser.get(f"http://localhost:{server_port}/submit")
     wait_for_page_load(browser)
 
-    select = browser.find_element("id", "department")
+    select = browser.find_element(By.ID, "department")
     scroll_to_element(browser, select)
     Select(select).select_by_value(dept_id)
     submit_image_to_dropzone(browser, img_path)
diff --git a/dev-requirements.txt b/dev-requirements.txt
index 88a72f6cb..01306a5d3 100644
--- a/dev-requirements.txt
+++ b/dev-requirements.txt
@@ -8,7 +8,7 @@ pytest==7.4.0
 pytest-cov==4.1.0
 pytest-pep8==1.0.6
 pytest-xdist==3.3.1
-selenium==3.14.0 # Updating this breaks the build for python 3.9
+selenium==4.26.0
 sphinx==7.0.1
 sphinx-autobuild==2021.3.14
 xvfbwrapper==0.2.9
diff --git a/dockerfiles/web/Dockerfile-test b/dockerfiles/web/Dockerfile-test
index c5db22405..b5214d7af 100644
--- a/dockerfiles/web/Dockerfile-test
+++ b/dockerfiles/web/Dockerfile-test
@@ -14,8 +14,8 @@ RUN apt-get update && apt-get install -y xvfb firefox-esr libpq-dev python3-dev
     apt-get install -y libsqlite3-0 && apt-get clean
 
 # install geckodriver
-ENV GECKODRIVER_VERSION="v0.26.0"
-ENV GECKODRIVER_SHA=d59ca434d8e41ec1e30dd7707b0c95171dd6d16056fb6db9c978449ad8b93cc0
+ENV GECKODRIVER_VERSION="v0.35.0"
+ENV GECKODRIVER_SHA=ac26e9ba8f3b8ce0fbf7339b9c9020192f6dcfcbf04a2bcd2af80dfe6bb24260
 ENV GECKODRIVER_BASE_URL="https://github.com/mozilla/geckodriver/releases/download"
 RUN curl ${CURL_FLAGS} \
       ${GECKODRIVER_BASE_URL}/${GECKODRIVER_VERSION}/geckodriver-${GECKODRIVER_VERSION}-linux64.tar.gz
diff --git a/requirements.txt b/requirements.txt
index 0d16256e7..b6ca34aae 100644
--- a/requirements.txt
+++ b/requirements.txt
@@ -56,13 +56,12 @@ recommonmark==0.7.1
 requests~=2.31.0
 rich~=13.4.2
 s3transfer~=0.6.1
-selenium~=4.10.0
 six~=1.16.0
 sphinx==7.1.2
 SQLAlchemy==1.4.52
 tornado~=6.4
 trio==0.22.1
-urllib3~=1.26.16 # This version is required for other packages to run
+urllib3==1.26.20
 us==3.1.1
 visitor~=0.1.3
 webencodings~=0.5.1