Repository: jupyter/nbgrader (language: python)

Issue:
Fixes selenium tests
EDIT : Fix https://github.com/jupyter/nbgrader/issues/1612

All the methods like `find_element_by_*` and `find_elements_by_*` have been removed from selenium 4.3.0.

This PR replaces them by `find_element(By.*, ...)` and `find_elements(By.*, ...)`.


Discussion / hints:

Fix patch:
diff --git a/pyproject.toml b/pyproject.toml
index 7d0eae1bc..d1c372c7c 100644
--- a/pyproject.toml
+++ b/pyproject.toml
@@ -77,7 +77,7 @@ tests = [
     "pytest-xdist==2.5.0",
     "pytest==7.1.2",
     "requests-mock",
-    "selenium>2.4,<4.3",
+    "selenium>=4.3",
     "wheel",
 ]
 


Test patch:
diff --git a/nbgrader/tests/nbextensions/formgrade_utils.py b/nbgrader/tests/nbextensions/formgrade_utils.py
index 22cd6d2a5..9cb6e40a3 100644
--- a/nbgrader/tests/nbextensions/formgrade_utils.py
+++ b/nbgrader/tests/nbextensions/formgrade_utils.py
@@ -31,11 +31,11 @@ def _check_url(browser, port, url):
 
 def _check_breadcrumbs(browser, *breadcrumbs):
     # check that breadcrumbs are correct
-    elements = browser.find_elements_by_css_selector(".breadcrumb li")
+    elements = browser.find_elements(By.CSS_SELECTOR, ".breadcrumb li")
     assert tuple([e.text for e in elements]) == breadcrumbs
 
     # check that the active breadcrumb is correct
-    element = browser.find_element_by_css_selector(".breadcrumb li.active")
+    element = browser.find_element(By.CSS_SELECTOR, ".breadcrumb li.active")
     assert element.text == breadcrumbs[-1]
 
 
@@ -43,11 +43,11 @@ def _click_link(browser, link_text, partial=False):
     if partial:
         WebDriverWait(browser, 10).until(
             EC.presence_of_element_located((By.PARTIAL_LINK_TEXT, link_text)))
-        element = browser.find_element_by_partial_link_text(link_text)
+        element = browser.find_element(By.PARTIAL_LINK_TEXT, link_text)
     else:
         WebDriverWait(browser, 10).until(
             EC.presence_of_element_located((By.LINK_TEXT, link_text)))
-        element = browser.find_element_by_link_text(link_text)
+        element = browser.find_element(By.LINK_TEXT, link_text)
     element.click()
 
 
@@ -165,7 +165,7 @@ def _wait_for_formgrader(browser, port, url, retries=5):
 
 
 def _click_element(browser, name):
-    browser.find_element_by_css_selector(name).click()
+    browser.find_element(By.CSS_SELECTOR, name).click()
 
 
 def _focus_body(browser, num_tries=5):
@@ -185,57 +185,57 @@ def _focus_body(browser, num_tries=5):
 def _send_keys_to_body(browser, *keys):
     _wait_for_tag(browser, "body")
     _focus_body(browser)
-    body = browser.find_element_by_tag_name("body")
+    body = browser.find_element(By.TAG_NAME, "body")
     body.send_keys(*keys)
 
 
 def _get_next_arrow(browser):
-    return browser.find_element_by_css_selector(".next a")
+    return browser.find_element(By.CSS_SELECTOR, ".next a")
 
 
 def _get_comment_box(browser, index):
     def comment_is_present(browser):
-        comments = browser.find_elements_by_css_selector(".comment")
+        comments = browser.find_elements(By.CSS_SELECTOR, ".comment")
         if len(comments) <= index:
             return False
         return True
 
     WebDriverWait(browser, 10).until(comment_is_present)
-    return browser.find_elements_by_css_selector(".comment")[index]
+    return browser.find_elements(By.CSS_SELECTOR, ".comment")[index]
 
 
 def _get_score_box(browser, index):
     def score_is_present(browser):
-        scores = browser.find_elements_by_css_selector(".score")
+        scores = browser.find_elements(By.CSS_SELECTOR, ".score")
         if len(scores) <= index:
             return False
         return True
 
     WebDriverWait(browser, 10).until(score_is_present)
-    return browser.find_elements_by_css_selector(".score")[index]
+    return browser.find_elements(By.CSS_SELECTOR, ".score")[index]
 
 
 def _get_extra_credit_box(browser, index):
     def extra_credit_is_present(browser):
-        extra_credits = browser.find_elements_by_css_selector(".extra-credit")
+        extra_credits = browser.find_elements(By.CSS_SELECTOR, ".extra-credit")
         if len(extra_credits) <= index:
             return False
         return True
 
     WebDriverWait(browser, 10).until(extra_credit_is_present)
-    return browser.find_elements_by_css_selector(".extra-credit")[index]
+    return browser.find_elements(By.CSS_SELECTOR, ".extra-credit")[index]
 
 
 def _save_comment(browser, index):
     _send_keys_to_body(browser, Keys.ESCAPE)
-    glyph = browser.find_elements_by_css_selector(".comment-saved")[index]
+    glyph = browser.find_elements(By.CSS_SELECTOR, ".comment-saved")[index]
     WebDriverWait(browser, 10).until(lambda browser: glyph.is_displayed())
     WebDriverWait(browser, 10).until(lambda browser: not glyph.is_displayed())
 
 
 def _save_score(browser, index):
     _send_keys_to_body(browser, Keys.ESCAPE)
-    glyph = browser.find_elements_by_css_selector(".score-saved")[index]
+    glyph = browser.find_elements(By.CSS_SELECTOR, ".score-saved")[index]
     WebDriverWait(browser, 10).until(lambda browser: glyph.is_displayed())
     WebDriverWait(browser, 10).until(lambda browser: not glyph.is_displayed())
 
@@ -247,7 +247,7 @@ def _get_needs_manual_grade(browser, name):
 
 def _flag(browser):
     _send_keys_to_body(browser, Keys.SHIFT, Keys.CONTROL, "f")
-    message = browser.find_element_by_id("statusmessage")
+    message = browser.find_element(By.ID, "statusmessage")
     WebDriverWait(browser, 10).until(lambda browser: message.is_displayed())
     WebDriverWait(browser, 10).until(lambda browser: not message.is_displayed())
     return browser.execute_script("return $('#statusmessage').text();")
@@ -278,7 +278,7 @@ def _load_formgrade(browser, port, gradebook):
 
 def _child_exists(elem, selector):
     try:
-        elem.find_element_by_css_selector(selector)
+        elem.find_element(By.CSS_SELECTOR, selector)
     except NoSuchElementException:
         return False
     else:
diff --git a/nbgrader/tests/nbextensions/test_assignment_list.py b/nbgrader/tests/nbextensions/test_assignment_list.py
index d4caf596c..faba06972 100644
--- a/nbgrader/tests/nbextensions/test_assignment_list.py
+++ b/nbgrader/tests/nbextensions/test_assignment_list.py
@@ -98,7 +98,7 @@ def page_loaded(browser):
     _wait(browser).until(EC.presence_of_element_located((By.CSS_SELECTOR, "#assignments")))
 
     # switch to the assignments list
-    element = browser.find_element_by_link_text("Assignments")
+    element = browser.find_element(By.LINK_TEXT, "Assignments")
     element.click()
 
     # make sure released, downloaded, and submitted assignments are visible
@@ -108,18 +108,18 @@ def page_loaded(browser):
 
 
 def _expand(browser, list_id, assignment):
-    browser.find_element_by_id("fetched_assignments_list").find_element_by_link_text(assignment).click()
-    rows = browser.find_elements_by_css_selector("{} .list_item".format(list_id))
+    browser.find_element(By.ID, "fetched_assignments_list").find_element(By.LINK_TEXT, assignment).click()
+    rows = browser.find_elements(By.CSS_SELECTOR, "{} .list_item".format(list_id))
     for i in range(1, len(rows)):
-        _wait(browser).until(lambda browser: browser.find_elements_by_css_selector("{} .list_item".format(list_id))[i].is_displayed())
+        _wait(browser).until(lambda browser: browser.find_elements(By.CSS_SELECTOR, "{} .list_item".format(list_id))[i].is_displayed())
     return rows
 
 
 def _unexpand(browser, list_id, assignment):
-    browser.find_element_by_link_text(assignment).click()
-    rows = browser.find_elements_by_css_selector("{} .list_item".format(list_id))
+    browser.find_element(By.LINK_TEXT, assignment).click()
+    rows = browser.find_elements(By.CSS_SELECTOR, "{} .list_item".format(list_id))
     for i in range(1, len(rows)):
-        _wait(browser).until(lambda browser: not browser.find_elements_by_css_selector("{} .list_item".format(list_id))[i].is_displayed())
+        _wait(browser).until(lambda browser: not browser.find_elements(By.CSS_SELECTOR, "{} .list_item".format(list_id))[i].is_displayed())
 
 
 def _wait_for_modal(browser):
@@ -127,12 +127,12 @@ def _wait_for_modal(browser):
 
 
 def _dismiss_modal(browser):
-    button = browser.find_element_by_css_selector(".modal-footer .btn-primary")
+    button = browser.find_element(By.CSS_SELECTOR, ".modal-footer .btn-primary")
     button.click()
 
     def modal_gone(browser):
         try:
-            browser.find_element_by_css_selector(".modal-dialog")
+            browser.find_element(By.CSS_SELECTOR, ".modal-dialog")
         except NoSuchElementException:
             return True
         return False
@@ -141,14 +141,14 @@ def modal_gone(browser):
 
 def _sort_rows(x):
     try:
-        item_name = x.find_element_by_class_name("item_name").text
+        item_name = x.find_element(By.CLASS_NAME, "item_name").text
     except NoSuchElementException:
         item_name = ""
     return item_name
 
 
 def _wait_until_loaded(browser):
-    _wait(browser).until(lambda browser: browser.find_element_by_id("course_list_default").text != "Loading, please wait...")
+    _wait(browser).until(lambda browser: browser.find_element(By.ID, "course_list_default").text != "Loading, please wait...")
     _wait(browser).until(EC.element_to_be_clickable((By.ID, "course_list_dropdown")))
 
 
@@ -157,11 +157,11 @@ def _change_course(browser, course):
     _wait_until_loaded(browser)
 
     # click the dropdown to show the menu
-    dropdown = browser.find_element_by_css_selector("#course_list_dropdown")
+    dropdown = browser.find_element(By.CSS_SELECTOR, "#course_list_dropdown")
     dropdown.click()
 
     # parse the list of courses and click the one that's been requested
-    courses = browser.find_elements_by_css_selector("#course_list > li")
+    courses = browser.find_elements(By.CSS_SELECTOR, "#course_list > li")
     text = [x.text for x in courses]
     index = text.index(course)
     courses[index].click()
@@ -170,7 +170,7 @@ def _change_course(browser, course):
     _wait_until_loaded(browser)
 
     # verify the dropdown shows the correct course
-    default = browser.find_element_by_css_selector("#course_list_default")
+    default = browser.find_element(By.CSS_SELECTOR, "#course_list_default")
     assert default.text == course
 
 
@@ -178,8 +178,8 @@ def _wait_for_list(browser, name, num_rows):
     _wait(browser).until(EC.invisibility_of_element_located((By.CSS_SELECTOR, "#{}_assignments_list_loading".format(name))))
     _wait(browser).until(EC.invisibility_of_element_located((By.CSS_SELECTOR, "#{}_assignments_list_placeholder".format(name))))
     _wait(browser).until(EC.invisibility_of_element_located((By.CSS_SELECTOR, "#{}_assignments_list_error".format(name))))
-    _wait(browser).until(lambda browser: len(browser.find_elements_by_css_selector("#{}_assignments_list > .list_item".format(name))) == num_rows)
-    rows = browser.find_elements_by_css_selector("#{}_assignments_list > .list_item".format(name))
+    _wait(browser).until(lambda browser: len(browser.find_elements(By.CSS_SELECTOR, "#{}_assignments_list > .list_item".format(name))) == num_rows)
+    rows = browser.find_elements(By.CSS_SELECTOR, "#{}_assignments_list > .list_item".format(name))
     assert len(rows) == num_rows
     return rows
 
@@ -200,13 +200,13 @@ def test_show_assignments_list(browser, port, class_files, tempdir):
     run_nbgrader(["release_assignment", "Problem Set 1", "--course", "abc101", "--force"])
 
     # click the refresh button
-    browser.find_element_by_css_selector("#refresh_assignments_list").click()
+    browser.find_element(By.CSS_SELECTOR, "#refresh_assignments_list").click()
     _wait_until_loaded(browser)
 
     # wait for the released assignments to update
     rows = _wait_for_list(browser, "released", 1)
-    assert rows[0].find_element_by_class_name("item_name").text == "Problem Set 1"
-    assert rows[0].find_element_by_class_name("item_course").text == "abc101"
+    assert rows[0].find_element(By.CLASS_NAME, "item_name").text == "Problem Set 1"
+    assert rows[0].find_element(By.CLASS_NAME, "item_course").text == "abc101"
 
 
 @pytest.mark.nbextensions
@@ -220,15 +220,15 @@ def test_multiple_released_assignments(browser, port, class_files, tempdir):
     run_nbgrader(["release_assignment", "ps.01", "--course", "xyz 200", "--force"])
 
     # click the refresh button
-    browser.find_element_by_css_selector("#refresh_assignments_list").click()
+    browser.find_element(By.CSS_SELECTOR, "#refresh_assignments_list").click()
     _wait_until_loaded(browser)
 
     # choose the course "xyz 200"
     _change_course(browser, "xyz 200")
 
     rows = _wait_for_list(browser, "released", 1)
-    assert rows[0].find_element_by_class_name("item_name").text == "ps.01"
-    assert rows[0].find_element_by_class_name("item_course").text == "xyz 200"
+    assert rows[0].find_element(By.CLASS_NAME, "item_name").text == "ps.01"
+    assert rows[0].find_element(By.CLASS_NAME, "item_course").text == "xyz 200"
 
 
 @pytest.mark.nbextensions
@@ -242,19 +242,19 @@ def test_fetch_assignment(browser, port, class_files, tempdir):
 
     # click the "fetch" button
     rows = _wait_for_list(browser, "released", 1)
-    rows[0].find_element_by_css_selector(".item_status button").click()
+    rows[0].find_element(By.CSS_SELECTOR, ".item_status button").click()
 
     # wait for the downloaded assignments list to update
     rows = _wait_for_list(browser, "fetched", 1)
-    assert rows[0].find_element_by_class_name("item_name").text == "ps.01"
-    assert rows[0].find_element_by_class_name("item_course").text == "xyz 200"
+    assert rows[0].find_element(By.CLASS_NAME, "item_name").text == "ps.01"
+    assert rows[0].find_element(By.CLASS_NAME, "item_course").text == "xyz 200"
     assert os.path.exists(os.path.join(tempdir, "ps.01"))
 
     # expand the assignment to show the notebooks
     rows = _expand(browser, "#nbgrader-xyz_200-ps01", "ps.01")
     rows.sort(key=_sort_rows)
     assert len(rows) == 2
-    assert rows[1].find_element_by_class_name("item_name").text == "problem 1"
+    assert rows[1].find_element(By.CLASS_NAME, "item_name").text == "problem 1"
 
     # unexpand the assignment
     _unexpand(browser, "#nbgrader-xyz_200-ps01", "ps.01")
@@ -271,30 +271,30 @@ def test_submit_assignment(browser, port, class_files, tempdir):
 
     # submit it
     rows = _wait_for_list(browser, "fetched", 1)
-    rows[0].find_element_by_css_selector(".item_status button").click()
+    rows[0].find_element(By.CSS_SELECTOR, ".item_status button").click()
 
     # wait for the submitted assignments list to update
     rows = _wait_for_list(browser, "submitted", 1)
-    assert rows[0].find_element_by_class_name("item_name").text == "ps.01"
-    assert rows[0].find_element_by_class_name("item_course").text == "xyz 200"
-    rows = browser.find_elements_by_css_selector("#nbgrader-xyz_200-ps01-submissions > .list_item")
+    assert rows[0].find_element(By.CLASS_NAME, "item_name").text == "ps.01"
+    assert rows[0].find_element(By.CLASS_NAME, "item_course").text == "xyz 200"
+    rows = browser.find_elements(By.CSS_SELECTOR, "#nbgrader-xyz_200-ps01-submissions > .list_item")
     rows = rows[1:]  # first row is empty
     assert len(rows) == 1
 
     # submit it again
     time.sleep(1)
-    rows = browser.find_elements_by_css_selector("#fetched_assignments_list > .list_item")
-    rows[0].find_element_by_css_selector(".item_status button").click()
+    rows = browser.find_elements(By.CSS_SELECTOR, "#fetched_assignments_list > .list_item")
+    rows[0].find_element(By.CSS_SELECTOR, ".item_status button").click()
 
     # wait for the submitted assignments list to update
     rows = _wait_for_list(browser, "submitted", 1)
-    assert rows[0].find_element_by_class_name("item_name").text == "ps.01"
-    assert rows[0].find_element_by_class_name("item_course").text == "xyz 200"
-    rows = browser.find_elements_by_css_selector("#nbgrader-xyz_200-ps01-submissions > .list_item")
+    assert rows[0].find_element(By.CLASS_NAME, "item_name").text == "ps.01"
+    assert rows[0].find_element(By.CLASS_NAME, "item_course").text == "xyz 200"
+    rows = browser.find_elements(By.CSS_SELECTOR, "#nbgrader-xyz_200-ps01-submissions > .list_item")
     rows = rows[1:]  # first row is empty
     assert len(rows) == 2
-    timestamp1 = rows[0].find_element_by_css_selector(".item_name").text
-    timestamp2 = rows[1].find_element_by_css_selector(".item_name").text
+    timestamp1 = rows[0].find_element(By.CSS_SELECTOR, ".item_name").text
+    timestamp2 = rows[1].find_element(By.CSS_SELECTOR, ".item_name").text
     assert timestamp1 != timestamp2
 
 @pytest.mark.nbextensions
@@ -315,19 +315,19 @@ def test_submit_assignment_missing_notebooks(browser, port, class_files, tempdir
         )
 
     # submit it again
-    rows = browser.find_elements_by_css_selector("#fetched_assignments_list > .list_item")
-    rows[0].find_element_by_css_selector(".item_status button").click()
+    rows = browser.find_elements(By.CSS_SELECTOR, "#fetched_assignments_list > .list_item")
+    rows[0].find_element(By.CSS_SELECTOR, ".item_status button").click()
 
     # wait for the submitted assignments list to update
     rows = _wait_for_list(browser, "submitted", 1)
-    assert rows[0].find_element_by_class_name("item_name").text == "ps.01"
-    assert rows[0].find_element_by_class_name("item_course").text == "xyz 200"
-    rows = browser.find_elements_by_css_selector("#nbgrader-xyz_200-ps01-submissions > .list_item")
+    assert rows[0].find_element(By.CLASS_NAME, "item_name").text == "ps.01"
+    assert rows[0].find_element(By.CLASS_NAME, "item_course").text == "xyz 200"
+    rows = browser.find_elements(By.CSS_SELECTOR, "#nbgrader-xyz_200-ps01-submissions > .list_item")
     rows = rows[1:]  # first row is empty
     assert len(rows) == 3
-    timestamp1 = rows[0].find_element_by_css_selector(".item_name").text
-    timestamp2 = rows[1].find_element_by_css_selector(".item_name").text
-    timestamp3 = rows[2].find_element_by_css_selector(".item_name").text
+    timestamp1 = rows[0].find_element(By.CSS_SELECTOR, ".item_name").text
+    timestamp2 = rows[1].find_element(By.CSS_SELECTOR, ".item_name").text
+    timestamp3 = rows[2].find_element(By.CSS_SELECTOR, ".item_name").text
     assert timestamp1 != timestamp2
     assert timestamp2 != timestamp3
 
@@ -336,23 +336,23 @@ def test_submit_assignment_missing_notebooks(browser, port, class_files, tempdir
         config.write('c.ExchangeSubmit.strict = True')
 
     # submit it again
-    rows = browser.find_elements_by_css_selector("#fetched_assignments_list > .list_item")
-    rows[0].find_element_by_css_selector(".item_status button").click()
+    rows = browser.find_elements(By.CSS_SELECTOR, "#fetched_assignments_list > .list_item")
+    rows[0].find_element(By.CSS_SELECTOR, ".item_status button").click()
 
     # wait for the modal dialog to appear
     _wait_for_modal(browser)
 
     # check that the submission failed
-    browser.find_element_by_css_selector(".modal-dialog")
+    browser.find_element(By.CSS_SELECTOR, ".modal-dialog")
 
     # close the modal dialog
     _dismiss_modal(browser)
 
     # check submitted assignments list remains unchanged
     rows = _wait_for_list(browser, "submitted", 1)
-    assert rows[0].find_element_by_class_name("item_name").text == "ps.01"
-    assert rows[0].find_element_by_class_name("item_course").text == "xyz 200"
-    rows = browser.find_elements_by_css_selector("#nbgrader-xyz_200-ps01-submissions > .list_item")
+    assert rows[0].find_element(By.CLASS_NAME, "item_name").text == "ps.01"
+    assert rows[0].find_element(By.CLASS_NAME, "item_course").text == "xyz 200"
+    rows = browser.find_elements(By.CSS_SELECTOR, "#nbgrader-xyz_200-ps01-submissions > .list_item")
     rows = rows[1:]  # first row is empty
     assert len(rows) == 3
 
@@ -373,21 +373,21 @@ def test_fetch_second_assignment(browser, port, class_files, tempdir):
 
     # click the "fetch" button
     rows = _wait_for_list(browser, "released", 1)
-    rows[0].find_element_by_css_selector(".item_status button").click()
+    rows[0].find_element(By.CSS_SELECTOR, ".item_status button").click()
 
     # wait for the downloaded assignments list to update
     rows = _wait_for_list(browser, "fetched", 1)
     rows.sort(key=_sort_rows)
-    assert rows[0].find_element_by_class_name("item_name").text == "Problem Set 1"
-    assert rows[0].find_element_by_class_name("item_course").text == "abc101"
+    assert rows[0].find_element(By.CLASS_NAME, "item_name").text == "Problem Set 1"
+    assert rows[0].find_element(By.CLASS_NAME, "item_course").text == "abc101"
     assert os.path.exists(os.path.join(tempdir, "Problem Set 1"))
 
     # expand the assignment to show the notebooks
     rows = _expand(browser, "#nbgrader-abc101-Problem_Set_1", "Problem Set 1")
     rows.sort(key=_sort_rows)
     assert len(rows) == 3
-    assert rows[1].find_element_by_class_name("item_name").text == "Problem 1"
-    assert rows[2].find_element_by_class_name("item_name").text == "Problem 2"
+    assert rows[1].find_element(By.CLASS_NAME, "item_name").text == "Problem 1"
+    assert rows[2].find_element(By.CLASS_NAME, "item_name").text == "Problem 2"
 
     # unexpand the assignment
     _unexpand(browser, "abc101-Problem_Set_1", "Problem Set 1")
@@ -401,13 +401,13 @@ def test_submit_other_assignment(browser, port, class_files, tempdir):
 
     # submit it
     rows = _wait_for_list(browser, "fetched", 1)
-    rows[0].find_element_by_css_selector(".item_status button").click()
+    rows[0].find_element(By.CSS_SELECTOR, ".item_status button").click()
 
     # wait for the submitted assignments list to update
     rows = _wait_for_list(browser, "submitted", 1)
-    assert rows[0].find_element_by_class_name("item_name").text == "Problem Set 1"
-    assert rows[0].find_element_by_class_name("item_course").text == "abc101"
-    rows = browser.find_elements_by_css_selector("#nbgrader-abc101-Problem_Set_1-submissions > .list_item")
+    assert rows[0].find_element(By.CLASS_NAME, "item_name").text == "Problem Set 1"
+    assert rows[0].find_element(By.CLASS_NAME, "item_course").text == "abc101"
+    rows = browser.find_elements(By.CSS_SELECTOR, "#nbgrader-abc101-Problem_Set_1-submissions > .list_item")
     rows = rows[1:]  # first row is empty
     assert len(rows) == 1
 
@@ -426,16 +426,16 @@ def test_validate_ok(browser, port, class_files, tempdir):
     rows = _expand(browser, "#nbgrader-xyz_200-ps01", "ps.01")
     rows.sort(key=_sort_rows)
     assert len(rows) == 2
-    assert rows[1].find_element_by_class_name("item_name").text == "problem 1"
+    assert rows[1].find_element(By.CLASS_NAME, "item_name").text == "problem 1"
 
     # click the "validate" button
-    rows[1].find_element_by_css_selector(".item_status button").click()
+    rows[1].find_element(By.CSS_SELECTOR, ".item_status button").click()
 
     # wait for the modal dialog to appear
     _wait_for_modal(browser)
 
     # check that it succeeded
-    browser.find_element_by_css_selector(".modal-dialog .validation-success")
+    browser.find_element(By.CSS_SELECTOR, ".modal-dialog .validation-success")
 
     # close the modal dialog
     _dismiss_modal(browser)
@@ -452,17 +452,17 @@ def test_validate_failure(browser, port, class_files, tempdir):
     rows = _expand(browser, "#nbgrader-abc101-Problem_Set_1", "Problem Set 1")
     rows.sort(key=_sort_rows)
     assert len(rows) == 3
-    assert rows[1].find_element_by_class_name("item_name").text == "Problem 1"
-    assert rows[2].find_element_by_class_name("item_name").text == "Problem 2"
+    assert rows[1].find_element(By.CLASS_NAME, "item_name").text == "Problem 1"
+    assert rows[2].find_element(By.CLASS_NAME, "item_name").text == "Problem 2"
 
     # click the "validate" button
-    rows[2].find_element_by_css_selector(".item_status button").click()
+    rows[2].find_element(By.CSS_SELECTOR, ".item_status button").click()
 
     # wait for the modal dialog to appear
     _wait_for_modal(browser)
 
     # check that it succeeded
-    browser.find_element_by_css_selector(".modal-dialog .validation-failed")
+    browser.find_element(By.CSS_SELECTOR, ".modal-dialog .validation-failed")
 
     # close the modal dialog
     _dismiss_modal(browser)
@@ -484,7 +484,7 @@ def test_missing_exchange(exchange, browser, port, class_files, tempdir):
     _wait(browser).until(EC.visibility_of_element_located((By.CSS_SELECTOR, "#submitted_assignments_list_error")))
 
     # verify that the dropdown list shows an error too
-    default = browser.find_element_by_css_selector("#course_list_default")
+    default = browser.find_element(By.CSS_SELECTOR, "#course_list_default")
     assert default.text == "Error fetching courses!"
 
     # recreate the exchange and make sure refreshing works as expected
@@ -495,10 +495,10 @@ def test_missing_exchange(exchange, browser, port, class_files, tempdir):
     run_nbgrader(["release_assignment", "Problem Set 1", "--course", "abc101", "--force"])
 
     # click the refresh button
-    browser.find_element_by_css_selector("#refresh_assignments_list").click()
+    browser.find_element(By.CSS_SELECTOR, "#refresh_assignments_list").click()
     _wait_until_loaded(browser)
 
     # wait for the released assignments to update
     rows = _wait_for_list(browser, "released", 1)
-    assert rows[0].find_element_by_class_name("item_name").text == "Problem Set 1"
-    assert rows[0].find_element_by_class_name("item_course").text == "abc101"
+    assert rows[0].find_element(By.CLASS_NAME, "item_name").text == "Problem Set 1"
+    assert rows[0].find_element(By.CLASS_NAME, "item_course").text == "abc101"
diff --git a/nbgrader/tests/nbextensions/test_course_list.py b/nbgrader/tests/nbextensions/test_course_list.py
index 12271cb09..6027f2296 100644
--- a/nbgrader/tests/nbextensions/test_course_list.py
+++ b/nbgrader/tests/nbextensions/test_course_list.py
@@ -63,7 +63,7 @@ def page_loaded(browser):
     _wait(browser).until(EC.presence_of_element_located((By.CSS_SELECTOR, "#courses")))
 
     # switch to the courses list
-    element = browser.find_element_by_link_text("Courses")
+    element = browser.find_element(By.LINK_TEXT, "Courses")
     element.click()
 
     # make sure courses are visible
@@ -74,8 +74,8 @@ def _wait_for_list(browser, num_rows):
     _wait(browser).until(EC.invisibility_of_element_located((By.CSS_SELECTOR, "#formgrader_list_loading")))
     _wait(browser).until(EC.invisibility_of_element_located((By.CSS_SELECTOR, "#formgrader_list_placeholder")))
     _wait(browser).until(EC.invisibility_of_element_located((By.CSS_SELECTOR, "#formgrader_list_error")))
-    _wait(browser).until(lambda browser: len(browser.find_elements_by_css_selector("#formgrader_list > .list_item")) == num_rows)
-    rows = browser.find_elements_by_css_selector("#formgrader_list > .list_item")
+    _wait(browser).until(lambda browser: len(browser.find_elements(By.CSS_SELECTOR, "#formgrader_list > .list_item")) == num_rows)
+    rows = browser.find_elements(By.CSS_SELECTOR, "#formgrader_list > .list_item")
     assert len(rows) == num_rows
     return rows
 
@@ -112,7 +112,7 @@ def test_local_formgrader(browser, port, tempdir, jupyter_config_dir, jupyter_da
         assert rows[0].text == "course101\nlocal"
 
         # make sure the url of the course is correct
-        link = browser.find_elements_by_css_selector("#formgrader_list > .list_item a")[0]
+        link = browser.find_elements(By.CSS_SELECTOR, "#formgrader_list > .list_item a")[0]
         url = link.get_attribute("href")
         assert url == "http://localhost:{}/formgrader".format(port)
 
@@ -138,7 +138,7 @@ def update_config(env):
         assert rows[0].text == "course101\nlocal"
 
         # make sure the url of the course is correct
-        link = browser.find_elements_by_css_selector("#formgrader_list > .list_item a")[0]
+        link = browser.find_elements(By.CSS_SELECTOR, "#formgrader_list > .list_item a")[0]
         url = link.get_attribute("href")
         assert url == "http://localhost:{}/formgrader".format(port)
 
diff --git a/nbgrader/tests/nbextensions/test_create_assignment.py b/nbgrader/tests/nbextensions/test_create_assignment.py
index 6f29cf0b7..c97eb39ea 100644
--- a/nbgrader/tests/nbextensions/test_create_assignment.py
+++ b/nbgrader/tests/nbextensions/test_create_assignment.py
@@ -92,9 +92,9 @@ def page_loaded(browser):
 
     # delete all cells
     if name == "blank":
-        cells = browser.find_elements_by_css_selector(".cell")
+        cells = browser.find_elements(By.CSS_SELECTOR, ".cell")
         for _ in range(len(cells)):
-            element = browser.find_elements_by_css_selector(".cell")[0]
+            element = browser.find_elements(By.CSS_SELECTOR, ".cell")[0]
             element.click()
             element.send_keys(Keys.ESCAPE)
             element.send_keys("d")
@@ -128,26 +128,26 @@ def celltoolbar_exists(browser):
 
 def _select(browser, text, index=0):
     def is_clickable(browser):
-        elems = browser.find_elements_by_css_selector('.celltoolbar select')
+        elems = browser.find_elements(By.CSS_SELECTOR, '.celltoolbar select')
         if len(elems) <= index:
             return False
         elem = elems[index]
         return elem.is_displayed and elem.is_enabled
 
     def is_option(browser):
-        elem = browser.find_elements_by_css_selector('.celltoolbar select')[index]
+        elem = browser.find_elements(By.CSS_SELECTOR, '.celltoolbar select')[index]
         select = Select(elem)
         options = [x.get_attribute('value') for x in select.options]
         return text in options
 
     _wait(browser).until(is_clickable)
     _wait(browser).until(is_option)
-    elem = browser.find_elements_by_css_selector('.celltoolbar select')[index]
+    elem = browser.find_elements(By.CSS_SELECTOR, '.celltoolbar select')[index]
     select = Select(elem)
     select.select_by_value(text)
 
     def selected(browser):
-        elem = browser.find_elements_by_css_selector('.celltoolbar select')[index]
+        elem = browser.find_elements(By.CSS_SELECTOR, '.celltoolbar select')[index]
         return elem.get_attribute('value') == text
 
     _wait(browser).until(selected)
@@ -162,7 +162,7 @@ def _select_manual(browser, index=0):
 
 
 def _select_task(browser, index=0):
-    select = Select(browser.find_elements_by_css_selector('.celltoolbar select')[index])
+    select = Select(browser.find_elements(By.CSS_SELECTOR, '.celltoolbar select')[index])
     select.select_by_value('task')
 
 
@@ -191,7 +191,7 @@ def _set_points(browser, points=2, index=0):
         $($(".nbgrader-points-input")[{}]).val("{}").change().blur();
         """.format(index, points)
     )
-    browser.find_elements_by_css_selector(".nbgrader-cell")[index].click()
+    browser.find_elements(By.CSS_SELECTOR, ".nbgrader-cell")[index].click()
 
 
 def _set_id(browser, cell_id="foo", index=0):
@@ -201,7 +201,7 @@ def _set_id(browser, cell_id="foo", index=0):
         $($(".nbgrader-id-input")[{}]).val("{}").change().blur();
         """.format(index, cell_id)
     )
-    browser.find_elements_by_css_selector(".nbgrader-cell")[index].click()
+    browser.find_elements(By.CSS_SELECTOR, ".nbgrader-cell")[index].click()
 
 
 def _get_metadata(browser):
@@ -214,7 +214,7 @@ def _get_metadata(browser):
 
 
 def _get_total_points(browser):
-    element = browser.find_element_by_id("nbgrader-total-points")
+    element = browser.find_element(By.ID, "nbgrader-total-points")
     return float(element.get_attribute("value"))
 
 
@@ -254,12 +254,12 @@ def _wait_for_modal(browser):
 
 
 def _dismiss_modal(browser):
-    button = browser.find_element_by_css_selector(".modal-footer .btn-primary")
+    button = browser.find_element(By.CSS_SELECTOR, ".modal-footer .btn-primary")
     button.click()
 
     def modal_gone(browser):
         try:
-            browser.find_element_by_css_selector(".modal-dialog")
+            browser.find_element(By.CSS_SELECTOR, ".modal-dialog")
         except NoSuchElementException:
             return True
         return False
@@ -508,52 +508,52 @@ def test_grade_cell_css(browser, port):
 
     # make it manually graded
     _select_manual(browser)
-    elements = browser.find_elements_by_css_selector(".nbgrader-cell")
+    elements = browser.find_elements(By.CSS_SELECTOR, ".nbgrader-cell")
     assert len(elements) == 1
 
     # make it nothing
     _select_none(browser)
-    elements = browser.find_elements_by_css_selector(".nbgrader-cell")
+    elements = browser.find_elements(By.CSS_SELECTOR, ".nbgrader-cell")
     assert len(elements) == 0
 
     # make it a solution
     _select_solution(browser)
-    elements = browser.find_elements_by_css_selector(".nbgrader-cell")
+    elements = browser.find_elements(By.CSS_SELECTOR, ".nbgrader-cell")
     assert len(elements) == 1
 
     # make it nothing
     _select_none(browser)
-    elements = browser.find_elements_by_css_selector(".nbgrader-cell")
+    elements = browser.find_elements(By.CSS_SELECTOR, ".nbgrader-cell")
     assert len(elements) == 0
 
     # make it autograder tests
     _select_tests(browser)
-    elements = browser.find_elements_by_css_selector(".nbgrader-cell")
+    elements = browser.find_elements(By.CSS_SELECTOR, ".nbgrader-cell")
     assert len(elements) == 1
 
     # make it nothing
     _select_none(browser)
-    elements = browser.find_elements_by_css_selector(".nbgrader-cell")
+    elements = browser.find_elements(By.CSS_SELECTOR, ".nbgrader-cell")
     assert len(elements) == 0
 
     # make it autograder tests
     _select_tests(browser)
-    elements = browser.find_elements_by_css_selector(".nbgrader-cell")
+    elements = browser.find_elements(By.CSS_SELECTOR, ".nbgrader-cell")
     assert len(elements) == 1
 
     # deactivate the toolbar
     _activate_toolbar(browser, "None")
-    elements = browser.find_elements_by_css_selector(".nbgrader-cell")
+    elements = browser.find_elements(By.CSS_SELECTOR, ".nbgrader-cell")
     assert len(elements) == 0
 
     # activate the toolbar
     _activate_toolbar(browser)
-    elements = browser.find_elements_by_css_selector(".nbgrader-cell")
+    elements = browser.find_elements(By.CSS_SELECTOR, ".nbgrader-cell")
     assert len(elements) == 1
 
     # deactivate the toolbar
     _activate_toolbar(browser, "Edit%20Metadata")
-    elements = browser.find_elements_by_css_selector(".nbgrader-cell")
+    elements = browser.find_elements(By.CSS_SELECTOR, ".nbgrader-cell")
     assert len(elements) == 0
 
 
@@ -572,7 +572,7 @@ def waitfor(browser):
     _select_manual(browser)
 
     # click the id field
-    element = browser.find_element_by_css_selector(".nbgrader-points-input")
+    element = browser.find_element(By.CSS_SELECTOR, ".nbgrader-points-input")
     # There is a bug where sometimes the click doesn't register, so we also press enter
     # here which for some reason seems to help. It's not clear here what's happening
     # to cause this bug but see https://github.com/mozilla/geckodriver/issues/322 for
@@ -589,7 +589,7 @@ def waitfor(browser):
     _select_tests(browser)
 
     # click the id field
-    element = browser.find_element_by_css_selector(".nbgrader-points-input")
+    element = browser.find_element(By.CSS_SELECTOR, ".nbgrader-points-input")
     element.click()
     element.send_keys(Keys.RETURN)
     _wait(browser).until(active_element_is("nbgrader-points-input"))
@@ -628,14 +628,14 @@ def test_total_points(browser, port):
     assert _get_total_points(browser) == 2
 
     # create a new cell
-    element = browser.find_element_by_tag_name("body")
+    element = browser.find_element(By.TAG_NAME, "body")
     element.send_keys(Keys.ESCAPE)
     element.send_keys("b")
 
     # make sure the toolbar appeared
     def find_toolbar(browser):
         try:
-            browser.find_elements_by_css_selector(".celltoolbar select")[1]
+            browser.find_elements(By.CSS_SELECTOR, ".celltoolbar select")[1]
         except IndexError:
             return False
         return True
@@ -648,7 +648,7 @@ def find_toolbar(browser):
     assert _get_total_points(browser) == 3
 
     # delete the new cell
-    element = browser.find_elements_by_css_selector(".cell")[0]
+    element = browser.find_elements(By.CSS_SELECTOR, ".cell")[0]
     element.click()
     element.send_keys(Keys.ESCAPE)
     element.send_keys("d")
@@ -656,7 +656,7 @@ def find_toolbar(browser):
     assert _get_total_points(browser) == 1
 
     # delete the first cell
-    element = browser.find_elements_by_css_selector(".cell")[0]
+    element = browser.find_elements(By.CSS_SELECTOR, ".cell")[0]
     element.send_keys("d")
     element.send_keys("d")
     assert _get_total_points(browser) == 0
@@ -691,14 +691,14 @@ def test_task_total_points(browser, port):
     assert _get_total_points(browser) == 2
 
     # create a new cell
-    element = browser.find_element_by_tag_name("body")
+    element = browser.find_element(By.TAG_NAME, "body")
     element.send_keys(Keys.ESCAPE)
     element.send_keys("b")
 
     # make sure the toolbar appeared
     def find_toolbar(browser):
         try:
-            browser.find_elements_by_css_selector(".celltoolbar select")[1]
+            browser.find_elements(By.CSS_SELECTOR, ".celltoolbar select")[1]
         except IndexError:
             return False
         return True
@@ -711,7 +711,7 @@ def find_toolbar(browser):
     assert _get_total_points(browser) == 3
 
     # delete the new cell
-    element = browser.find_elements_by_css_selector(".cell")[0]
+    element = browser.find_elements(By.CSS_SELECTOR, ".cell")[0]
     element.click()
     element.send_keys(Keys.ESCAPE)
     element.send_keys("d")
@@ -719,7 +719,7 @@ def find_toolbar(browser):
     assert _get_total_points(browser) == 1
 
     # delete the first cell
-    element = browser.find_elements_by_css_selector(".cell")[0]
+    element = browser.find_elements(By.CSS_SELECTOR, ".cell")[0]
     element.send_keys("d")
     element.send_keys("d")
     assert _get_total_points(browser) == 0
@@ -745,14 +745,14 @@ def test_cell_ids(browser, port):
     _set_id(browser)
 
     # create a new cell
-    element = browser.find_element_by_tag_name("body")
+    element = browser.find_element(By.TAG_NAME, "body")
     element.send_keys(Keys.ESCAPE)
     element.send_keys("b")
 
     # make sure the toolbar appeared
     def find_toolbar(browser):
         try:
-            browser.find_elements_by_css_selector(".celltoolbar select")[1]
+            browser.find_elements(By.CSS_SELECTOR, ".celltoolbar select")[1]
         except IndexError:
             return False
         return True
@@ -786,14 +786,14 @@ def test_task_cell_ids(browser, port):
     _set_id(browser)
 
     # create a new cell
-    element = browser.find_element_by_tag_name("body")
+    element = browser.find_element(By.TAG_NAME, "body")
     element.send_keys(Keys.ESCAPE)
     element.send_keys("b")
 
     # make sure the toolbar appeared
     def find_toolbar(browser):
         try:
-            browser.find_elements_by_css_selector(".celltoolbar select")[1]
+            browser.find_elements(By.CSS_SELECTOR, ".celltoolbar select")[1]
         except IndexError:
             return False
         return True
@@ -886,14 +886,14 @@ def test_invalid_nbgrader_cell_type(browser, port):
     _save_and_validate(browser)
 
     # change the cell to a markdown cell
-    element = browser.find_element_by_tag_name("body")
+    element = browser.find_element(By.TAG_NAME, "body")
     element.send_keys(Keys.ESCAPE)
     element.send_keys("m")
 
     # make sure the toolbar appeared
     def find_toolbar(browser):
         try:
-            browser.find_elements_by_css_selector(".celltoolbar select")[0]
+            browser.find_elements(By.CSS_SELECTOR, ".celltoolbar select")[0]
         except IndexError:
             return False
         return True
diff --git a/nbgrader/tests/nbextensions/test_formgrader.py b/nbgrader/tests/nbextensions/test_formgrader.py
index ab6783e05..04230f6c0 100644
--- a/nbgrader/tests/nbextensions/test_formgrader.py
+++ b/nbgrader/tests/nbextensions/test_formgrader.py
@@ -144,7 +144,7 @@ def test_load_manage_assignments(browser, port, gradebook, fake_home_dir):
     utils._switch_to_window(browser, 0)
 
     # click on the preview link
-    browser.find_element_by_css_selector("td.preview .glyphicon").click()
+    browser.find_element(By.CSS_SELECTOR, "td.preview .glyphicon").click()
     utils._switch_to_window(browser, 1)
     utils._wait_for_tree_page(
         browser, port,
@@ -153,7 +153,7 @@ def test_load_manage_assignments(browser, port, gradebook, fake_home_dir):
     utils._switch_to_window(browser, 0)
 
     # click on the number of submissions
-    browser.find_element_by_css_selector("td.num-submissions a").click()
+    browser.find_element(By.CSS_SELECTOR, "td.num-submissions a").click()
     utils._wait_for_gradebook_page(browser, port, "manage_submissions/Problem Set 1")
 
 
@@ -191,7 +191,7 @@ def test_load_gradebook1(browser, port, gradebook):
     utils._wait_for_gradebook_page(browser, port, "gradebook/Problem Set 1")
 
     # test that the task column is present
-    elements = browser.find_elements_by_xpath('//th[text()="Avg. Task Score"]')
+    elements = browser.find_elements(By.XPATH, '//th[text()="Avg. Task Score"]')
     assert len(elements) == 1
 
 
@@ -209,7 +209,7 @@ def test_load_gradebook2(browser, port, gradebook):
     for problem in gradebook.find_assignment("Problem Set 1").notebooks:
         utils._click_link(browser, problem.name)
         utils._wait_for_gradebook_page(browser, port, "gradebook/Problem Set 1/{}".format(problem.name))
-        elements = browser.find_elements_by_xpath('//th[text()="Task Score"]')
+        elements = browser.find_elements(By.XPATH, '//th[text()="Task Score"]')
         assert len(elements) == 1
         browser.back()
 
@@ -238,7 +238,7 @@ def test_load_gradebook3(browser, port, gradebook):
             utils._wait_for_formgrader(browser, port, "submissions/{}/?index=0".format(submission.id))
             # only the first problem has a task cell
             if problem.name == "Problem 1":
-                elements = browser.find_elements_by_xpath('//span[text()="Student\'s task"]')
+                elements = browser.find_elements(By.XPATH, '//span[text()="Student\'s task"]')
                 assert len(elements) == 1
             browser.back()
 
@@ -251,10 +251,10 @@ def test_gradebook3_show_hide_names(browser, port, gradebook):
     submissions.sort(key=lambda x: x.id)
     submission = submissions[0]
 
-    top_elem = browser.find_elements_by_css_selector("tbody tr")[0]
-    col1, col2 = top_elem.find_elements_by_css_selector("td")[:2]
-    hidden = col1.find_element_by_css_selector(".glyphicon.name-hidden")
-    shown = col1.find_element_by_css_selector(".glyphicon.name-shown")
+    top_elem = browser.find_elements(By.CSS_SELECTOR, "tbody tr")[0]
+    col1, col2 = top_elem.find_elements(By.CSS_SELECTOR, "td")[:2]
+    hidden = col1.find_element(By.CSS_SELECTOR, ".glyphicon.name-hidden")
+    shown = col1.find_element(By.CSS_SELECTOR, ".glyphicon.name-shown")
 
     # check that the name is hidden
     assert col2.text == "Submission #1"
@@ -288,7 +288,7 @@ def test_load_student1(browser, port, gradebook):
     for student in gradebook.students:
         utils._click_link(browser, "{}, {}".format(student.last_name, student.first_name))
         utils._wait_for_gradebook_page(browser, port, "manage_students/{}".format(student.id))
-        elements = browser.find_elements_by_xpath('//th[text()="Task Score"]')
+        elements = browser.find_elements(By.XPATH, '//th[text()="Task Score"]')
         assert len(elements) == 1
         browser.back()
 
@@ -305,7 +305,7 @@ def test_load_student2(browser, port, gradebook):
 
         utils._click_link(browser, "Problem Set 1")
         utils._wait_for_gradebook_page(browser, port, "manage_students/{}/Problem Set 1".format(student.id))
-    elements = browser.find_elements_by_xpath('//th[text()="Task Score"]')
+    elements = browser.find_elements(By.XPATH, '//th[text()="Task Score"]')
     assert len(elements) == 1
 
 
@@ -413,7 +413,7 @@ def test_formgrade_images(browser, port, gradebook):
         utils._get(browser, utils._formgrade_url(port, "submissions/{}".format(submission.id)))
         utils._wait_for_formgrader(browser, port, "submissions/{}/?index=0".format(submission.id))
 
-        images = browser.find_elements_by_tag_name("img")
+        images = browser.find_elements(By.TAG_NAME, "img")
         for image in images:
             # check that the image is loaded, and that it has a width
             assert browser.execute_script("return arguments[0].complete", image)
@@ -862,9 +862,9 @@ def test_formgrade_show_hide_names(browser, port, gradebook):
     submissions.sort(key=lambda x: x.id)
     submission = submissions[0]
 
-    name = browser.find_elements_by_css_selector(".breadcrumb li")[-1]
-    hidden = browser.find_element_by_css_selector(".glyphicon.name-hidden")
-    shown = browser.find_element_by_css_selector(".glyphicon.name-shown")
+    name = browser.find_elements(By.CSS_SELECTOR, ".breadcrumb li")[-1]
+    hidden = browser.find_element(By.CSS_SELECTOR, ".glyphicon.name-hidden")
+    shown = browser.find_element(By.CSS_SELECTOR, ".glyphicon.name-shown")
 
     # check that the name is hidden
     assert name.text == "Submission #1"
@@ -874,7 +874,7 @@ def test_formgrade_show_hide_names(browser, port, gradebook):
     # click the show icon
     hidden.click()
     # move the mouse to the first breadcrumb so it's not hovering over the tooltip
-    ActionChains(browser).move_to_element(browser.find_elements_by_css_selector(".breadcrumb li")[0]).perform()
+    ActionChains(browser).move_to_element(browser.find_elements(By.CSS_SELECTOR, ".breadcrumb li")[0]).perform()
     WebDriverWait(browser, 10).until_not(EC.presence_of_element_located((By.CSS_SELECTOR, ".tooltip")))
 
     # check that the name is shown
@@ -885,7 +885,7 @@ def test_formgrade_show_hide_names(browser, port, gradebook):
     # click the hide icon
     shown.click()
     # move the mouse to the first breadcrumb so it's not hovering over the tooltip
-    ActionChains(browser).move_to_element(browser.find_elements_by_css_selector(".breadcrumb li")[0]).perform()
+    ActionChains(browser).move_to_element(browser.find_elements(By.CSS_SELECTOR, ".breadcrumb li")[0]).perform()
     WebDriverWait(browser, 10).until_not(EC.presence_of_element_located((By.CSS_SELECTOR, ".tooltip")))
 
     # check that the name is hidden
@@ -897,13 +897,13 @@ def test_formgrade_show_hide_names(browser, port, gradebook):
 @pytest.mark.nbextensions
 def test_before_add_new_assignment(browser, port, gradebook):
     utils._load_gradebook_page(browser, port, "")
-    assert len(browser.find_elements_by_css_selector("tbody tr")) == 1
+    assert len(browser.find_elements(By.CSS_SELECTOR, "tbody tr")) == 1
 
 
 @pytest.mark.nbextensions
 def test_add_new_assignment(browser, port, gradebook):
     utils._load_gradebook_page(browser, port, "")
-    n = len(browser.find_elements_by_css_selector("tbody tr"))
+    n = len(browser.find_elements(By.CSS_SELECTOR, "tbody tr"))
 
     # click the "add new assignment" button
     utils._click_link(browser, "Add new assignment...")
@@ -911,14 +911,14 @@ def test_add_new_assignment(browser, port, gradebook):
     WebDriverWait(browser, 10).until(EC.visibility_of_element_located((By.CSS_SELECTOR, "#add-assignment-modal .save")))
 
     # set the name and dudedate
-    elem = browser.find_element_by_css_selector("#add-assignment-modal .name")
+    elem = browser.find_element(By.CSS_SELECTOR, "#add-assignment-modal .name")
     elem.click()
     elem.send_keys("ps2+a")
-    elem = browser.find_element_by_css_selector("#add-assignment-modal .duedate")
+    elem = browser.find_element(By.CSS_SELECTOR, "#add-assignment-modal .duedate")
     elem.click()
     browser.execute_script("arguments[0].value = '2017-07-05T17:00';", elem)
 
-    elem = browser.find_element_by_css_selector("#add-assignment-modal .timezone")
+    elem = browser.find_element(By.CSS_SELECTOR, "#add-assignment-modal .timezone")
     elem.click()
     elem.send_keys("UTC")
 
@@ -927,7 +927,7 @@ def test_add_new_assignment(browser, port, gradebook):
     WebDriverWait(browser, 10).until(EC.visibility_of_element_located((By.CSS_SELECTOR, "#create-error")))
 
     # set a valid name
-    elem = browser.find_element_by_css_selector("#add-assignment-modal .name")
+    elem = browser.find_element(By.CSS_SELECTOR, "#add-assignment-modal .name")
     elem.clear()
     elem.click()
     # check with a name containing whitespace, as this should be stripped
@@ -940,33 +940,33 @@ def test_add_new_assignment(browser, port, gradebook):
     WebDriverWait(browser, 10).until(modal_not_present)
 
     # wait until both rows are present
-    rows_present = lambda browser: len(browser.find_elements_by_css_selector("tbody tr")) == (n + 1)
+    rows_present = lambda browser: len(browser.find_elements(By.CSS_SELECTOR, "tbody tr")) == (n + 1)
     WebDriverWait(browser, 10).until(rows_present)
 
     # check that the new row is correct
-    row = browser.find_elements_by_css_selector("tbody tr")[1]
-    assert row.find_element_by_css_selector(".name").text == "ps2"
-    assert row.find_element_by_css_selector(".duedate").text == "2017-07-05 17:00:00 {}".format(tz)
-    assert row.find_element_by_css_selector(".status").text == "draft"
+    row = browser.find_elements(By.CSS_SELECTOR, "tbody tr")[1]
+    assert row.find_element(By.CSS_SELECTOR, ".name").text == "ps2"
+    assert row.find_element(By.CSS_SELECTOR, ".duedate").text == "2017-07-05 17:00:00 {}".format(tz)
+    assert row.find_element(By.CSS_SELECTOR, ".status").text == "draft"
     assert utils._child_exists(row, ".edit a")
     assert utils._child_exists(row, ".assign a")
     assert not utils._child_exists(row, ".preview a")
     assert not utils._child_exists(row, ".release a")
     assert not utils._child_exists(row, ".collect a")
-    assert row.find_element_by_css_selector(".num-submissions").text == "0"
+    assert row.find_element(By.CSS_SELECTOR, ".num-submissions").text == "0"
 
     # reload the page and make sure everything is still correct
     utils._load_gradebook_page(browser, port, "")
-    row = browser.find_elements_by_css_selector("tbody tr")[1]
-    assert row.find_element_by_css_selector(".name").text == "ps2"
-    assert row.find_element_by_css_selector(".duedate").text == "2017-07-05 17:00:00 {}".format(tz)
-    assert row.find_element_by_css_selector(".status").text == "draft"
+    row = browser.find_elements(By.CSS_SELECTOR, "tbody tr")[1]
+    assert row.find_element(By.CSS_SELECTOR, ".name").text == "ps2"
+    assert row.find_element(By.CSS_SELECTOR, ".duedate").text == "2017-07-05 17:00:00 {}".format(tz)
+    assert row.find_element(By.CSS_SELECTOR, ".status").text == "draft"
     assert utils._child_exists(row, ".edit a")
     assert utils._child_exists(row, ".assign a")
     assert not utils._child_exists(row, ".preview a")
     assert not utils._child_exists(row, ".release a")
     assert not utils._child_exists(row, ".collect a")
-    assert row.find_element_by_css_selector(".num-submissions").text == "0"
+    assert row.find_element(By.CSS_SELECTOR, ".num-submissions").text == "0"
 
 
 @pytest.mark.nbextensions
@@ -974,13 +974,13 @@ def test_edit_assignment(browser, port, gradebook):
     utils._load_gradebook_page(browser, port, "")
 
     # click on the edit button
-    row = browser.find_elements_by_css_selector("tbody tr")[1]
-    row.find_element_by_css_selector(".edit a").click()
+    row = browser.find_elements(By.CSS_SELECTOR, "tbody tr")[1]
+    row.find_element(By.CSS_SELECTOR, ".edit a").click()
     utils._wait_for_element(browser, "edit-assignment-modal")
     WebDriverWait(browser, 10).until(EC.visibility_of_element_located((By.CSS_SELECTOR, "#edit-assignment-modal .save")))
 
     # modify the duedate
-    elem = browser.find_element_by_css_selector("#edit-assignment-modal .modal-duedate")
+    elem = browser.find_element(By.CSS_SELECTOR, "#edit-assignment-modal .modal-duedate")
     elem.clear()
     elem.click()
     browser.execute_script("arguments[0].value = '2017-07-05T18:00';", elem)
@@ -991,29 +991,29 @@ def test_edit_assignment(browser, port, gradebook):
     WebDriverWait(browser, 10).until(modal_not_present)
 
     # check that the modified row is correct
-    row = browser.find_elements_by_css_selector("tbody tr")[1]
-    assert row.find_element_by_css_selector(".name").text == "ps2"
-    assert row.find_element_by_css_selector(".duedate").text == "2017-07-05 18:00:00 {}".format(tz)
-    assert row.find_element_by_css_selector(".status").text == "draft"
+    row = browser.find_elements(By.CSS_SELECTOR, "tbody tr")[1]
+    assert row.find_element(By.CSS_SELECTOR, ".name").text == "ps2"
+    assert row.find_element(By.CSS_SELECTOR, ".duedate").text == "2017-07-05 18:00:00 {}".format(tz)
+    assert row.find_element(By.CSS_SELECTOR, ".status").text == "draft"
     assert utils._child_exists(row, ".edit a")
     assert utils._child_exists(row, ".assign a")
     assert not utils._child_exists(row, ".preview a")
     assert not utils._child_exists(row, ".release a")
     assert not utils._child_exists(row, ".collect a")
-    assert row.find_element_by_css_selector(".num-submissions").text == "0"
+    assert row.find_element(By.CSS_SELECTOR, ".num-submissions").text == "0"
 
     # reload the page and make sure everything is still correct
     utils._load_gradebook_page(browser, port, "")
-    row = browser.find_elements_by_css_selector("tbody tr")[1]
-    assert row.find_element_by_css_selector(".name").text == "ps2"
-    assert row.find_element_by_css_selector(".duedate").text == "2017-07-05 18:00:00 {}".format(tz)
-    assert row.find_element_by_css_selector(".status").text == "draft"
+    row = browser.find_elements(By.CSS_SELECTOR, "tbody tr")[1]
+    assert row.find_element(By.CSS_SELECTOR, ".name").text == "ps2"
+    assert row.find_element(By.CSS_SELECTOR, ".duedate").text == "2017-07-05 18:00:00 {}".format(tz)
+    assert row.find_element(By.CSS_SELECTOR, ".status").text == "draft"
     assert utils._child_exists(row, ".edit a")
     assert utils._child_exists(row, ".assign a")
     assert not utils._child_exists(row, ".preview a")
     assert not utils._child_exists(row, ".release a")
     assert not utils._child_exists(row, ".collect a")
-    assert row.find_element_by_css_selector(".num-submissions").text == "0"
+    assert row.find_element(By.CSS_SELECTOR, ".num-submissions").text == "0"
 
 
 @pytest.mark.nbextensions
@@ -1022,8 +1022,8 @@ def test_generate_assignment_fail(browser, port, gradebook):
 
     # click on the generate button -- should produce an error because there
     # are no notebooks for ps2 yet
-    row = browser.find_elements_by_css_selector("tbody tr")[1]
-    row.find_element_by_css_selector(".assign a").click()
+    row = browser.find_elements(By.CSS_SELECTOR, "tbody tr")[1]
+    row.find_element(By.CSS_SELECTOR, ".assign a").click()
     utils._wait_for_element(browser, "error-modal")
     WebDriverWait(browser, 10).until(EC.visibility_of_element_located((By.CSS_SELECTOR, "#error-modal .close")))
     utils._click_element(browser, "#error-modal .close")
@@ -1040,8 +1040,8 @@ def test_generate_assignment_success(browser, port, gradebook):
     shutil.copy(source_path, join("source", "ps2", "Problem 1.ipynb"))
 
     # click on the generate button -- should now succeed
-    row = browser.find_elements_by_css_selector("tbody tr")[1]
-    row.find_element_by_css_selector(".assign a").click()
+    row = browser.find_elements(By.CSS_SELECTOR, "tbody tr")[1]
+    row.find_element(By.CSS_SELECTOR, ".assign a").click()
     utils._wait_for_element(browser, "success-modal")
     WebDriverWait(browser, 10).until(EC.visibility_of_element_located((By.CSS_SELECTOR, "#success-modal .close")))
     utils._click_element(browser, "#success-modal .close")
@@ -1049,10 +1049,10 @@ def test_generate_assignment_success(browser, port, gradebook):
     WebDriverWait(browser, 10).until(modal_not_present)
 
     # check that the modified row is correct
-    row = browser.find_elements_by_css_selector("tbody tr")[1]
-    assert row.find_element_by_css_selector(".name").text == "ps2"
-    assert row.find_element_by_css_selector(".duedate").text == "2017-07-05 18:00:00 {}".format(tz)
-    assert row.find_element_by_css_selector(".status").text == "draft"
+    row = browser.find_elements(By.CSS_SELECTOR, "tbody tr")[1]
+    assert row.find_element(By.CSS_SELECTOR, ".name").text == "ps2"
+    assert row.find_element(By.CSS_SELECTOR, ".duedate").text == "2017-07-05 18:00:00 {}".format(tz)
+    assert row.find_element(By.CSS_SELECTOR, ".status").text == "draft"
     assert utils._child_exists(row, ".edit a")
     assert utils._child_exists(row, ".assign a")
     assert utils._child_exists(row, ".preview a")
@@ -1061,14 +1061,14 @@ def test_generate_assignment_success(browser, port, gradebook):
     else:
         assert utils._child_exists(row, ".release a")
     assert not utils._child_exists(row, ".collect a")
-    assert row.find_element_by_css_selector(".num-submissions").text == "0"
+    assert row.find_element(By.CSS_SELECTOR, ".num-submissions").text == "0"
 
     # reload the page and make sure everything is still correct
     utils._load_gradebook_page(browser, port, "")
-    row = browser.find_elements_by_css_selector("tbody tr")[1]
-    assert row.find_element_by_css_selector(".name").text == "ps2"
-    assert row.find_element_by_css_selector(".duedate").text == "2017-07-05 18:00:00 {}".format(tz)
-    assert row.find_element_by_css_selector(".status").text == "draft"
+    row = browser.find_elements(By.CSS_SELECTOR, "tbody tr")[1]
+    assert row.find_element(By.CSS_SELECTOR, ".name").text == "ps2"
+    assert row.find_element(By.CSS_SELECTOR, ".duedate").text == "2017-07-05 18:00:00 {}".format(tz)
+    assert row.find_element(By.CSS_SELECTOR, ".status").text == "draft"
     assert utils._child_exists(row, ".edit a")
     assert utils._child_exists(row, ".assign a")
     assert utils._child_exists(row, ".preview a")
@@ -1077,7 +1077,7 @@ def test_generate_assignment_success(browser, port, gradebook):
     else:
         assert utils._child_exists(row, ".release a")
     assert not utils._child_exists(row, ".collect a")
-    assert row.find_element_by_css_selector(".num-submissions").text == "0"
+    assert row.find_element(By.CSS_SELECTOR, ".num-submissions").text == "0"
 
 
 @notwindows
@@ -1086,8 +1086,8 @@ def test_release_assignment(browser, port, gradebook):
     utils._load_gradebook_page(browser, port, "")
 
     # click on the release button
-    row = browser.find_elements_by_css_selector("tbody tr")[1]
-    row.find_element_by_css_selector(".release a").click()
+    row = browser.find_elements(By.CSS_SELECTOR, "tbody tr")[1]
+    row.find_element(By.CSS_SELECTOR, ".release a").click()
     utils._wait_for_element(browser, "success-modal")
     WebDriverWait(browser, 10).until(EC.visibility_of_element_located((By.CSS_SELECTOR, "#success-modal .close")))
     utils._click_element(browser, "#success-modal .close")
@@ -1095,29 +1095,29 @@ def test_release_assignment(browser, port, gradebook):
     WebDriverWait(browser, 10).until(modal_not_present)
 
     # check that the modified row is correct
-    row = browser.find_elements_by_css_selector("tbody tr")[1]
-    assert row.find_element_by_css_selector(".name").text == "ps2"
-    assert row.find_element_by_css_selector(".duedate").text == "2017-07-05 18:00:00 {}".format(tz)
-    assert row.find_element_by_css_selector(".status").text == "released"
+    row = browser.find_elements(By.CSS_SELECTOR, "tbody tr")[1]
+    assert row.find_element(By.CSS_SELECTOR, ".name").text == "ps2"
+    assert row.find_element(By.CSS_SELECTOR, ".duedate").text == "2017-07-05 18:00:00 {}".format(tz)
+    assert row.find_element(By.CSS_SELECTOR, ".status").text == "released"
     assert utils._child_exists(row, ".edit a")
     assert utils._child_exists(row, ".assign a")
     assert utils._child_exists(row, ".preview a")
     assert utils._child_exists(row, ".release a")
     assert utils._child_exists(row, ".collect a")
-    assert row.find_element_by_css_selector(".num-submissions").text == "0"
+    assert row.find_element(By.CSS_SELECTOR, ".num-submissions").text == "0"
 
     # reload the page and make sure everything is still correct
     utils._load_gradebook_page(browser, port, "")
-    row = browser.find_elements_by_css_selector("tbody tr")[1]
-    assert row.find_element_by_css_selector(".name").text == "ps2"
-    assert row.find_element_by_css_selector(".duedate").text == "2017-07-05 18:00:00 {}".format(tz)
-    assert row.find_element_by_css_selector(".status").text == "released"
+    row = browser.find_elements(By.CSS_SELECTOR, "tbody tr")[1]
+    assert row.find_element(By.CSS_SELECTOR, ".name").text == "ps2"
+    assert row.find_element(By.CSS_SELECTOR, ".duedate").text == "2017-07-05 18:00:00 {}".format(tz)
+    assert row.find_element(By.CSS_SELECTOR, ".status").text == "released"
     assert utils._child_exists(row, ".edit a")
     assert utils._child_exists(row, ".assign a")
     assert utils._child_exists(row, ".preview a")
     assert utils._child_exists(row, ".release a")
     assert utils._child_exists(row, ".collect a")
-    assert row.find_element_by_css_selector(".num-submissions").text == "0"
+    assert row.find_element(By.CSS_SELECTOR, ".num-submissions").text == "0"
 
 
 @notwindows
@@ -1130,8 +1130,8 @@ def test_collect_assignment(browser, port, gradebook):
     utils._load_gradebook_page(browser, port, "")
 
     # click on the collect button
-    row = browser.find_elements_by_css_selector("tbody tr")[1]
-    row.find_element_by_css_selector(".collect a").click()
+    row = browser.find_elements(By.CSS_SELECTOR, "tbody tr")[1]
+    row.find_element(By.CSS_SELECTOR, ".collect a").click()
     utils._wait_for_element(browser, "success-modal")
     WebDriverWait(browser, 10).until(EC.visibility_of_element_located((By.CSS_SELECTOR, "#success-modal .close")))
     utils._click_element(browser, "#success-modal .close")
@@ -1139,29 +1139,29 @@ def test_collect_assignment(browser, port, gradebook):
     WebDriverWait(browser, 10).until(modal_not_present)
 
     # check that the modified row is correct
-    row = browser.find_elements_by_css_selector("tbody tr")[1]
-    assert row.find_element_by_css_selector(".name").text == "ps2"
-    assert row.find_element_by_css_selector(".duedate").text == "2017-07-05 18:00:00 {}".format(tz)
-    assert row.find_element_by_css_selector(".status").text == "released"
+    row = browser.find_elements(By.CSS_SELECTOR, "tbody tr")[1]
+    assert row.find_element(By.CSS_SELECTOR, ".name").text == "ps2"
+    assert row.find_element(By.CSS_SELECTOR, ".duedate").text == "2017-07-05 18:00:00 {}".format(tz)
+    assert row.find_element(By.CSS_SELECTOR, ".status").text == "released"
     assert utils._child_exists(row, ".edit a")
     assert utils._child_exists(row, ".assign a")
     assert utils._child_exists(row, ".preview a")
     assert utils._child_exists(row, ".release a")
     assert utils._child_exists(row, ".collect a")
-    assert row.find_element_by_css_selector(".num-submissions").text == "1"
+    assert row.find_element(By.CSS_SELECTOR, ".num-submissions").text == "1"
 
     # reload the page and make sure everything is still correct
     utils._load_gradebook_page(browser, port, "")
-    row = browser.find_elements_by_css_selector("tbody tr")[1]
-    assert row.find_element_by_css_selector(".name").text == "ps2"
-    assert row.find_element_by_css_selector(".duedate").text == "2017-07-05 18:00:00 {}".format(tz)
-    assert row.find_element_by_css_selector(".status").text == "released"
+    row = browser.find_elements(By.CSS_SELECTOR, "tbody tr")[1]
+    assert row.find_element(By.CSS_SELECTOR, ".name").text == "ps2"
+    assert row.find_element(By.CSS_SELECTOR, ".duedate").text == "2017-07-05 18:00:00 {}".format(tz)
+    assert row.find_element(By.CSS_SELECTOR, ".status").text == "released"
     assert utils._child_exists(row, ".edit a")
     assert utils._child_exists(row, ".assign a")
     assert utils._child_exists(row, ".preview a")
     assert utils._child_exists(row, ".release a")
     assert utils._child_exists(row, ".collect a")
-    assert row.find_element_by_css_selector(".num-submissions").text == "1"
+    assert row.find_element(By.CSS_SELECTOR, ".num-submissions").text == "1"
 
 
 @notwindows
@@ -1170,8 +1170,8 @@ def test_unrelease_assignment(browser, port, gradebook):
     utils._load_gradebook_page(browser, port, "")
 
     # click on the unrelease button
-    row = browser.find_elements_by_css_selector("tbody tr")[1]
-    row.find_element_by_css_selector(".release a").click()
+    row = browser.find_elements(By.CSS_SELECTOR, "tbody tr")[1]
+    row.find_element(By.CSS_SELECTOR, ".release a").click()
     utils._wait_for_element(browser, "success-modal")
     WebDriverWait(browser, 10).until(EC.visibility_of_element_located((By.CSS_SELECTOR, "#success-modal .close")))
     utils._click_element(browser, "#success-modal .close")
@@ -1179,29 +1179,29 @@ def test_unrelease_assignment(browser, port, gradebook):
     WebDriverWait(browser, 10).until(modal_not_present)
 
     # check that the modified row is correct
-    row = browser.find_elements_by_css_selector("tbody tr")[1]
-    assert row.find_element_by_css_selector(".name").text == "ps2"
-    assert row.find_element_by_css_selector(".duedate").text == "2017-07-05 18:00:00 {}".format(tz)
-    assert row.find_element_by_css_selector(".status").text == "draft"
+    row = browser.find_elements(By.CSS_SELECTOR, "tbody tr")[1]
+    assert row.find_element(By.CSS_SELECTOR, ".name").text == "ps2"
+    assert row.find_element(By.CSS_SELECTOR, ".duedate").text == "2017-07-05 18:00:00 {}".format(tz)
+    assert row.find_element(By.CSS_SELECTOR, ".status").text == "draft"
     assert utils._child_exists(row, ".edit a")
     assert utils._child_exists(row, ".assign a")
     assert utils._child_exists(row, ".preview a")
     assert utils._child_exists(row, ".release a")
     assert not utils._child_exists(row, ".collect a")
-    assert row.find_element_by_css_selector(".num-submissions").text == "1"
+    assert row.find_element(By.CSS_SELECTOR, ".num-submissions").text == "1"
 
     # reload the page and make sure everything is still correct
     utils._load_gradebook_page(browser, port, "")
-    row = browser.find_elements_by_css_selector("tbody tr")[1]
-    assert row.find_element_by_css_selector(".name").text == "ps2"
-    assert row.find_element_by_css_selector(".duedate").text == "2017-07-05 18:00:00 {}".format(tz)
-    assert row.find_element_by_css_selector(".status").text == "draft"
+    row = browser.find_elements(By.CSS_SELECTOR, "tbody tr")[1]
+    assert row.find_element(By.CSS_SELECTOR, ".name").text == "ps2"
+    assert row.find_element(By.CSS_SELECTOR, ".duedate").text == "2017-07-05 18:00:00 {}".format(tz)
+    assert row.find_element(By.CSS_SELECTOR, ".status").text == "draft"
     assert utils._child_exists(row, ".edit a")
     assert utils._child_exists(row, ".assign a")
     assert utils._child_exists(row, ".preview a")
     assert utils._child_exists(row, ".release a")
     assert not utils._child_exists(row, ".collect a")
-    assert row.find_element_by_css_selector(".num-submissions").text == "1"
+    assert row.find_element(By.CSS_SELECTOR, ".num-submissions").text == "1"
 
 
 @pytest.mark.nbextensions
@@ -1219,10 +1219,10 @@ def test_manually_collect_assignment(browser, port, gradebook):
     utils._load_gradebook_page(browser, port, "")
 
     # check that the row is correct
-    row = browser.find_elements_by_css_selector("tbody tr")[1]
-    assert row.find_element_by_css_selector(".name").text == "ps2"
-    assert row.find_element_by_css_selector(".duedate").text == "2017-07-05 18:00:00 {}".format(tz)
-    assert row.find_element_by_css_selector(".status").text == "draft"
+    row = browser.find_elements(By.CSS_SELECTOR, "tbody tr")[1]
+    assert row.find_element(By.CSS_SELECTOR, ".name").text == "ps2"
+    assert row.find_element(By.CSS_SELECTOR, ".duedate").text == "2017-07-05 18:00:00 {}".format(tz)
+    assert row.find_element(By.CSS_SELECTOR, ".status").text == "draft"
     assert utils._child_exists(row, ".edit a")
     assert utils._child_exists(row, ".assign a")
     assert utils._child_exists(row, ".preview a")
@@ -1231,7 +1231,7 @@ def test_manually_collect_assignment(browser, port, gradebook):
     else:
         assert utils._child_exists(row, ".release a")
     assert not utils._child_exists(row, ".collect a")
-    assert row.find_element_by_css_selector(".num-submissions").text == "1"
+    assert row.find_element(By.CSS_SELECTOR, ".num-submissions").text == "1"
 
 
 @pytest.mark.nbextensions
@@ -1239,12 +1239,12 @@ def test_before_autograde_assignment(browser, port, gradebook):
     utils._load_gradebook_page(browser, port, "manage_submissions/ps2")
 
     # check the contents of the row before grading
-    row = browser.find_elements_by_css_selector("tbody tr")[0]
-    assert row.find_element_by_css_selector(".student-name").text == "B, Ben"
-    assert row.find_element_by_css_selector(".student-id").text == "Bitdiddle"
-    assert row.find_element_by_css_selector(".timestamp").text == "2017-07-05 18:05:21 {}".format(tz)
-    assert row.find_element_by_css_selector(".status").text == "needs autograding"
-    assert row.find_element_by_css_selector(".score").text == ""
+    row = browser.find_elements(By.CSS_SELECTOR, "tbody tr")[0]
+    assert row.find_element(By.CSS_SELECTOR, ".student-name").text == "B, Ben"
+    assert row.find_element(By.CSS_SELECTOR, ".student-id").text == "Bitdiddle"
+    assert row.find_element(By.CSS_SELECTOR, ".timestamp").text == "2017-07-05 18:05:21 {}".format(tz)
+    assert row.find_element(By.CSS_SELECTOR, ".status").text == "needs autograding"
+    assert row.find_element(By.CSS_SELECTOR, ".score").text == ""
     assert utils._child_exists(row, ".autograde a")
 
 
@@ -1253,8 +1253,8 @@ def test_autograde_assignment1(browser, port, gradebook):
     utils._load_gradebook_page(browser, port, "manage_submissions/ps2")
 
     # click on the autograde button
-    row = browser.find_elements_by_css_selector("tbody tr")[0]
-    row.find_element_by_css_selector(".autograde a").click()
+    row = browser.find_elements(By.CSS_SELECTOR, "tbody tr")[0]
+    row.find_element(By.CSS_SELECTOR, ".autograde a").click()
     utils._wait_for_element(browser, "success-modal")
     WebDriverWait(browser, 10).until(EC.visibility_of_element_located((By.CSS_SELECTOR, "#success-modal .close")))
     utils._click_element(browser, "#success-modal .close")
@@ -1262,24 +1262,24 @@ def test_autograde_assignment1(browser, port, gradebook):
     WebDriverWait(browser, 10).until(modal_not_present)
 
     # check that the modified row is correct
-    row = browser.find_elements_by_css_selector("tbody tr")[0]
-    assert row.find_element_by_css_selector(".student-name").text == "B, Ben"
-    assert row.find_element_by_css_selector(".student-id").text == "Bitdiddle"
-    assert row.find_element_by_css_selector(".timestamp").text == "2017-07-05 18:05:21 {}".format(tz)
+    row = browser.find_elements(By.CSS_SELECTOR, "tbody tr")[0]
+    assert row.find_element(By.CSS_SELECTOR, ".student-name").text == "B, Ben"
+    assert row.find_element(By.CSS_SELECTOR, ".student-id").text == "Bitdiddle"
+    assert row.find_element(By.CSS_SELECTOR, ".timestamp").text == "2017-07-05 18:05:21 {}".format(tz)
     # the task cell needs to be manually graded
-    assert row.find_element_by_css_selector(".status").text == "needs manual grading"
-    assert row.find_element_by_css_selector(".score").text == "0 / 10"
+    assert row.find_element(By.CSS_SELECTOR, ".status").text == "needs manual grading"
+    assert row.find_element(By.CSS_SELECTOR, ".score").text == "0 / 10"
     assert utils._child_exists(row, ".autograde a")
 
     # refresh and check again
     utils._load_gradebook_page(browser, port, "manage_submissions/ps2")
-    row = browser.find_elements_by_css_selector("tbody tr")[0]
-    assert row.find_element_by_css_selector(".student-name").text == "B, Ben"
-    assert row.find_element_by_css_selector(".student-id").text == "Bitdiddle"
-    assert row.find_element_by_css_selector(".timestamp").text == "2017-07-05 18:05:21 {}".format(tz)
+    row = browser.find_elements(By.CSS_SELECTOR, "tbody tr")[0]
+    assert row.find_element(By.CSS_SELECTOR, ".student-name").text == "B, Ben"
+    assert row.find_element(By.CSS_SELECTOR, ".student-id").text == "Bitdiddle"
+    assert row.find_element(By.CSS_SELECTOR, ".timestamp").text == "2017-07-05 18:05:21 {}".format(tz)
     # the task cell needs to be manually graded
-    assert row.find_element_by_css_selector(".status").text == "needs manual grading"
-    assert row.find_element_by_css_selector(".score").text == "0 / 10"
+    assert row.find_element(By.CSS_SELECTOR, ".status").text == "needs manual grading"
+    assert row.find_element(By.CSS_SELECTOR, ".score").text == "0 / 10"
     assert utils._child_exists(row, ".autograde a")
 
 
@@ -1292,8 +1292,8 @@ def test_autograde_assignment2(browser, port, gradebook):
     shutil.copy(source_path, join("submitted", "Bitdiddle", "ps2", "Problem 1.ipynb"))
 
     # click on the autograde button
-    row = browser.find_elements_by_css_selector("tbody tr")[0]
-    row.find_element_by_css_selector(".autograde a").click()
+    row = browser.find_elements(By.CSS_SELECTOR, "tbody tr")[0]
+    row.find_element(By.CSS_SELECTOR, ".autograde a").click()
     utils._wait_for_element(browser, "success-modal")
     WebDriverWait(browser, 10).until(EC.visibility_of_element_located((By.CSS_SELECTOR, "#success-modal .close")))
     utils._click_element(browser, "#success-modal .close")
@@ -1301,35 +1301,35 @@ def test_autograde_assignment2(browser, port, gradebook):
     WebDriverWait(browser, 10).until(modal_not_present)
 
     # check that the modified row is correct
-    row = browser.find_elements_by_css_selector("tbody tr")[0]
-    assert row.find_element_by_css_selector(".student-name").text == "B, Ben"
-    assert row.find_element_by_css_selector(".student-id").text == "Bitdiddle"
-    assert row.find_element_by_css_selector(".timestamp").text == "2017-07-05 18:05:21 {}".format(tz)
-    assert row.find_element_by_css_selector(".status").text == "needs manual grading"
-    assert row.find_element_by_css_selector(".score").text == "3 / 10"
+    row = browser.find_elements(By.CSS_SELECTOR, "tbody tr")[0]
+    assert row.find_element(By.CSS_SELECTOR, ".student-name").text == "B, Ben"
+    assert row.find_element(By.CSS_SELECTOR, ".student-id").text == "Bitdiddle"
+    assert row.find_element(By.CSS_SELECTOR, ".timestamp").text == "2017-07-05 18:05:21 {}".format(tz)
+    assert row.find_element(By.CSS_SELECTOR, ".status").text == "needs manual grading"
+    assert row.find_element(By.CSS_SELECTOR, ".score").text == "3 / 10"
     assert utils._child_exists(row, ".autograde a")
 
     # refresh and check again
     utils._load_gradebook_page(browser, port, "manage_submissions/ps2")
-    row = browser.find_elements_by_css_selector("tbody tr")[0]
-    assert row.find_element_by_css_selector(".student-name").text == "B, Ben"
-    assert row.find_element_by_css_selector(".student-id").text == "Bitdiddle"
-    assert row.find_element_by_css_selector(".timestamp").text == "2017-07-05 18:05:21 {}".format(tz)
-    assert row.find_element_by_css_selector(".status").text == "needs manual grading"
-    assert row.find_element_by_css_selector(".score").text == "3 / 10"
+    row = browser.find_elements(By.CSS_SELECTOR, "tbody tr")[0]
+    assert row.find_element(By.CSS_SELECTOR, ".student-name").text == "B, Ben"
+    assert row.find_element(By.CSS_SELECTOR, ".student-id").text == "Bitdiddle"
+    assert row.find_element(By.CSS_SELECTOR, ".timestamp").text == "2017-07-05 18:05:21 {}".format(tz)
+    assert row.find_element(By.CSS_SELECTOR, ".status").text == "needs manual grading"
+    assert row.find_element(By.CSS_SELECTOR, ".score").text == "3 / 10"
     assert utils._child_exists(row, ".autograde a")
 
 
 @pytest.mark.nbextensions
 def test_before_add_new_student(browser, port, gradebook):
     utils._load_gradebook_page(browser, port, "manage_students")
-    assert len(browser.find_elements_by_css_selector("tbody tr")) == 3
+    assert len(browser.find_elements(By.CSS_SELECTOR, "tbody tr")) == 3
 
 
 @pytest.mark.nbextensions
 def test_add_new_student(browser, port, gradebook):
     utils._load_gradebook_page(browser, port, "manage_students")
-    n = len(browser.find_elements_by_css_selector("tbody tr"))
+    n = len(browser.find_elements(By.CSS_SELECTOR, "tbody tr"))
 
     # click the "add new assignment" button
     utils._click_link(browser, "Add new student...")
@@ -1337,18 +1337,18 @@ def test_add_new_student(browser, port, gradebook):
     WebDriverWait(browser, 10).until(EC.visibility_of_element_located((By.CSS_SELECTOR, "#add-student-modal .save")))
 
     # set the name and dudedate
-    elem = browser.find_element_by_css_selector("#add-student-modal .id")
+    elem = browser.find_element(By.CSS_SELECTOR, "#add-student-modal .id")
     elem.click()
     # check with a name containing whitespace, as this should be stripped
     # away and handled by the interface
     elem.send_keys("ator  ")
-    elem = browser.find_element_by_css_selector("#add-student-modal .first-name")
+    elem = browser.find_element(By.CSS_SELECTOR, "#add-student-modal .first-name")
     elem.click()
     elem.send_keys("Eva Lou")
-    elem = browser.find_element_by_css_selector("#add-student-modal .last-name")
+    elem = browser.find_element(By.CSS_SELECTOR, "#add-student-modal .last-name")
     elem.click()
     elem.send_keys("Ator")
-    elem = browser.find_element_by_css_selector("#add-student-modal .email")
+    elem = browser.find_element(By.CSS_SELECTOR, "#add-student-modal .email")
     elem.click()
     elem.send_keys("ela@email.com")
 
@@ -1358,24 +1358,24 @@ def test_add_new_student(browser, port, gradebook):
     WebDriverWait(browser, 10).until(modal_not_present)
 
     # wait until both rows are present
-    rows_present = lambda browser: len(browser.find_elements_by_css_selector("tbody tr")) == (n + 1)
+    rows_present = lambda browser: len(browser.find_elements(By.CSS_SELECTOR, "tbody tr")) == (n + 1)
     WebDriverWait(browser, 10).until(rows_present)
 
     # check that the new row is correct
-    row = browser.find_elements_by_css_selector("tbody tr")[0]
-    assert row.find_element_by_css_selector(".name").text == "Ator, Eva Lou"
-    assert row.find_element_by_css_selector(".id").text == "ator"
-    assert row.find_element_by_css_selector(".email").text == "ela@email.com"
-    assert row.find_element_by_css_selector(".score").text == "0 / 23"
+    row = browser.find_elements(By.CSS_SELECTOR, "tbody tr")[0]
+    assert row.find_element(By.CSS_SELECTOR, ".name").text == "Ator, Eva Lou"
+    assert row.find_element(By.CSS_SELECTOR, ".id").text == "ator"
+    assert row.find_element(By.CSS_SELECTOR, ".email").text == "ela@email.com"
+    assert row.find_element(By.CSS_SELECTOR, ".score").text == "0 / 23"
     assert utils._child_exists(row, ".edit a")
 
     # reload the page and make sure everything is still correct
     utils._load_gradebook_page(browser, port, "manage_students")
-    row = browser.find_elements_by_css_selector("tbody tr")[0]
-    assert row.find_element_by_css_selector(".name").text == "Ator, Eva Lou"
-    assert row.find_element_by_css_selector(".id").text == "ator"
-    assert row.find_element_by_css_selector(".email").text == "ela@email.com"
-    assert row.find_element_by_css_selector(".score").text == "0 / 23"
+    row = browser.find_elements(By.CSS_SELECTOR, "tbody tr")[0]
+    assert row.find_element(By.CSS_SELECTOR, ".name").text == "Ator, Eva Lou"
+    assert row.find_element(By.CSS_SELECTOR, ".id").text == "ator"
+    assert row.find_element(By.CSS_SELECTOR, ".email").text == "ela@email.com"
+    assert row.find_element(By.CSS_SELECTOR, ".score").text == "0 / 23"
     assert utils._child_exists(row, ".edit a")
 
 
@@ -1384,13 +1384,13 @@ def test_edit_student(browser, port, gradebook):
     utils._load_gradebook_page(browser, port, "manage_students")
 
     # click on the edit button
-    row = browser.find_elements_by_css_selector("tbody tr")[0]
-    row.find_element_by_css_selector(".edit a").click()
+    row = browser.find_elements(By.CSS_SELECTOR, "tbody tr")[0]
+    row.find_element(By.CSS_SELECTOR, ".edit a").click()
     utils._wait_for_element(browser, "edit-student-modal")
     WebDriverWait(browser, 10).until(EC.visibility_of_element_located((By.CSS_SELECTOR, "#edit-student-modal .modal-email")))
 
     # modify the duedate
-    elem = browser.find_element_by_css_selector("#edit-student-modal .modal-email")
+    elem = browser.find_element(By.CSS_SELECTOR, "#edit-student-modal .modal-email")
     elem.clear()
     elem.click()
     elem.send_keys("ela@email.net")
@@ -1401,18 +1401,18 @@ def test_edit_student(browser, port, gradebook):
     WebDriverWait(browser, 10).until(modal_not_present)
 
     # check that the modified row is correct
-    row = browser.find_elements_by_css_selector("tbody tr")[0]
-    assert row.find_element_by_css_selector(".name").text == "Ator, Eva Lou"
-    assert row.find_element_by_css_selector(".id").text == "ator"
-    assert row.find_element_by_css_selector(".email").text == "ela@email.net"
-    assert row.find_element_by_css_selector(".score").text == "0 / 23"
+    row = browser.find_elements(By.CSS_SELECTOR, "tbody tr")[0]
+    assert row.find_element(By.CSS_SELECTOR, ".name").text == "Ator, Eva Lou"
+    assert row.find_element(By.CSS_SELECTOR, ".id").text == "ator"
+    assert row.find_element(By.CSS_SELECTOR, ".email").text == "ela@email.net"
+    assert row.find_element(By.CSS_SELECTOR, ".score").text == "0 / 23"
     assert utils._child_exists(row, ".edit a")
 
     # reload the page and make sure everything is still correct
     utils._load_gradebook_page(browser, port, "manage_students")
-    row = browser.find_elements_by_css_selector("tbody tr")[0]
-    assert row.find_element_by_css_selector(".name").text == "Ator, Eva Lou"
-    assert row.find_element_by_css_selector(".id").text == "ator"
-    assert row.find_element_by_css_selector(".email").text == "ela@email.net"
-    assert row.find_element_by_css_selector(".score").text == "0 / 23"
+    row = browser.find_elements(By.CSS_SELECTOR, "tbody tr")[0]
+    assert row.find_element(By.CSS_SELECTOR, ".name").text == "Ator, Eva Lou"
+    assert row.find_element(By.CSS_SELECTOR, ".id").text == "ator"
+    assert row.find_element(By.CSS_SELECTOR, ".email").text == "ela@email.net"
+    assert row.find_element(By.CSS_SELECTOR, ".score").text == "0 / 23"
     assert utils._child_exists(row, ".edit a")
diff --git a/nbgrader/tests/nbextensions/test_validate_assignment.py b/nbgrader/tests/nbextensions/test_validate_assignment.py
index 3b65e99c7..1421ed3be 100644
--- a/nbgrader/tests/nbextensions/test_validate_assignment.py
+++ b/nbgrader/tests/nbextensions/test_validate_assignment.py
@@ -77,7 +77,7 @@ def page_loaded(browser):
 def _wait_for_validate_button(browser):
     def validate_exists(browser):
         try:
-            browser.find_element_by_css_selector("button.validate")
+            browser.find_element(By.CSS_SELECTOR, "button.validate")
         except NoSuchElementException:
             return False
         return True
@@ -89,12 +89,12 @@ def _wait_for_modal(browser):
 
 
 def _dismiss_modal(browser):
-    button = browser.find_element_by_css_selector(".modal-footer .btn-primary")
+    button = browser.find_element(By.CSS_SELECTOR, ".modal-footer .btn-primary")
     button.click()
 
     def modal_gone(browser):
         try:
-            browser.find_element_by_css_selector(".modal-dialog")
+            browser.find_element(By.CSS_SELECTOR, ".modal-dialog")
         except NoSuchElementException:
             return True
         return False
@@ -107,13 +107,13 @@ def test_validate_ok(browser, port):
     _wait_for_validate_button(browser)
 
     # click the "validate" button
-    browser.find_element_by_css_selector("button.validate").click()
+    browser.find_element(By.CSS_SELECTOR, "button.validate").click()
 
     # wait for the modal dialog to appear
     _wait_for_modal(browser)
 
     # check that it succeeded
-    browser.find_element_by_css_selector(".modal-dialog .validation-success")
+    browser.find_element(By.CSS_SELECTOR, ".modal-dialog .validation-success")
 
     # close the modal dialog
     _dismiss_modal(browser)
@@ -125,13 +125,13 @@ def test_validate_failure(browser, port):
     _wait_for_validate_button(browser)
 
     # click the "validate" button
-    browser.find_element_by_css_selector("button.validate").click()
+    browser.find_element(By.CSS_SELECTOR, "button.validate").click()
 
     # wait for the modal dialog to appear
     _wait_for_modal(browser)
 
     # check that it failed
-    browser.find_element_by_css_selector(".modal-dialog .validation-failed")
+    browser.find_element(By.CSS_SELECTOR, ".modal-dialog .validation-failed")
 
     # close the modal dialog
     _dismiss_modal(browser)
@@ -143,13 +143,13 @@ def test_validate_grade_cell_changed(browser, port):
     _wait_for_validate_button(browser)
 
     # click the "validate" button
-    browser.find_element_by_css_selector("button.validate").click()
+    browser.find_element(By.CSS_SELECTOR, "button.validate").click()
 
     # wait for the modal dialog to appear
     _wait_for_modal(browser)
 
     # check that it failed
-    browser.find_element_by_css_selector(".modal-dialog .validation-changed")
+    browser.find_element(By.CSS_SELECTOR, ".modal-dialog .validation-changed")
 
     # close the modal dialog
     _dismiss_modal(browser)
@@ -161,13 +161,13 @@ def test_validate_locked_cell_changed(browser, port):
     _wait_for_validate_button(browser)
 
     # click the "validate" button
-    browser.find_element_by_css_selector("button.validate").click()
+    browser.find_element(By.CSS_SELECTOR, "button.validate").click()
 
     # wait for the modal dialog to appear
     _wait_for_modal(browser)
 
     # check that it failed
-    browser.find_element_by_css_selector(".modal-dialog .validation-changed")
+    browser.find_element(By.CSS_SELECTOR, ".modal-dialog .validation-changed")
 
     # close the modal dialog
     _dismiss_modal(browser)
@@ -179,13 +179,13 @@ def test_validate_open_relative_file(browser, port):
     _wait_for_validate_button(browser)
 
     # click the "validate" button
-    browser.find_element_by_css_selector("button.validate").click()
+    browser.find_element(By.CSS_SELECTOR, "button.validate").click()
 
     # wait for the modal dialog to appear
     _wait_for_modal(browser)
 
     # check that it succeeded
-    browser.find_element_by_css_selector(".modal-dialog .validation-success")
+    browser.find_element(By.CSS_SELECTOR, ".modal-dialog .validation-success")
 
     # close the modal dialog
     _dismiss_modal(browser)
@@ -197,13 +197,13 @@ def test_validate_grade_cell_type_changed(browser, port):
     _wait_for_validate_button(browser)
 
     # click the "validate" button
-    browser.find_element_by_css_selector("button.validate").click()
+    browser.find_element(By.CSS_SELECTOR, "button.validate").click()
 
     # wait for the modal dialog to appear
     _wait_for_modal(browser)
 
     # check that it failed
-    browser.find_element_by_css_selector(".modal-dialog .validation-type-changed")
+    browser.find_element(By.CSS_SELECTOR, ".modal-dialog .validation-type-changed")
 
     # close the modal dialog
     _dismiss_modal(browser)
@@ -215,13 +215,13 @@ def test_validate_answer_cell_type_changed(browser, port):
     _wait_for_validate_button(browser)
 
     # click the "validate" button
-    browser.find_element_by_css_selector("button.validate").click()
+    browser.find_element(By.CSS_SELECTOR, "button.validate").click()
 
     # wait for the modal dialog to appear
     _wait_for_modal(browser)
 
     # check that it failed
-    browser.find_element_by_css_selector(".modal-dialog .validation-type-changed")
+    browser.find_element(By.CSS_SELECTOR, ".modal-dialog .validation-type-changed")
 
     # close the modal dialog
     _dismiss_modal(browser)