From 8fa974fcdde90b6719a1058e77541389ff1809b5 Mon Sep 17 00:00:00 2001 From: Carlton Gibson Date: Wed, 20 Oct 2021 14:36:13 +0200 Subject: [PATCH] Refs #33211 -- Added assertCountSeleniumElements() test helper. --- django/contrib/admin/tests.py | 9 ++ tests/admin_inlines/tests.py | 144 ++++++++------------ tests/admin_views/test_autocomplete_view.py | 37 ++--- tests/admin_widgets/tests.py | 8 +- 4 files changed, 88 insertions(+), 110 deletions(-) diff --git a/django/contrib/admin/tests.py b/django/contrib/admin/tests.py index 482027b1ae7..1e51542fa2b 100644 --- a/django/contrib/admin/tests.py +++ b/django/contrib/admin/tests.py @@ -154,6 +154,15 @@ class AdminSeleniumTestCase(SeleniumTestCase, StaticLiveServerTestCase): select = Select(self.selenium.find_element_by_css_selector(selector)) select.deselect_by_value(value) + def assertCountSeleniumElements(self, selector, count, root_element=None): + """ + Assert number of matches for a CSS selector. + + `root_element` allow restriction to a pre-selected node. + """ + root_element = root_element or self.selenium + self.assertEqual(len(root_element.find_elements_by_css_selector(selector)), count) + def _assertOptionsValues(self, options_selector, values): if values: options = self.selenium.find_elements_by_css_selector(options_selector) diff --git a/tests/admin_inlines/tests.py b/tests/admin_inlines/tests.py index b7249f300fa..831135bdc4a 100644 --- a/tests/admin_inlines/tests.py +++ b/tests/admin_inlines/tests.py @@ -1279,37 +1279,33 @@ class SeleniumTests(AdminSeleniumTestCase): self.selenium.get(self.live_server_url + reverse('admin:admin_inlines_holder4_add')) inline_id = '#inner4stacked_set-group' + rows_selector = '%s .dynamic-inner4stacked_set' % inline_id - def rows_length(): - return len(self.selenium.find_elements_by_css_selector('%s .dynamic-inner4stacked_set' % inline_id)) - self.assertEqual(rows_length(), 3) - + self.assertCountSeleniumElements(rows_selector, 3) add_button = self.selenium.find_element_by_link_text( 'Add another Inner4 stacked') add_button.click() - - self.assertEqual(rows_length(), 4) + self.assertCountSeleniumElements(rows_selector, 4) def test_delete_stackeds(self): self.admin_login(username='super', password='secret') self.selenium.get(self.live_server_url + reverse('admin:admin_inlines_holder4_add')) inline_id = '#inner4stacked_set-group' + rows_selector = '%s .dynamic-inner4stacked_set' % inline_id - def rows_length(): - return len(self.selenium.find_elements_by_css_selector('%s .dynamic-inner4stacked_set' % inline_id)) - self.assertEqual(rows_length(), 3) + self.assertCountSeleniumElements(rows_selector, 3) add_button = self.selenium.find_element_by_link_text( 'Add another Inner4 stacked') add_button.click() add_button.click() - self.assertEqual(rows_length(), 5, msg="sanity check") + self.assertCountSeleniumElements(rows_selector, 5) for delete_link in self.selenium.find_elements_by_css_selector('%s .inline-deletelink' % inline_id): delete_link.click() with self.disable_implicit_wait(): - self.assertEqual(rows_length(), 0) + self.assertCountSeleniumElements(rows_selector, 0) def test_delete_invalid_stacked_inlines(self): from selenium.common.exceptions import NoSuchElementException @@ -1317,16 +1313,15 @@ class SeleniumTests(AdminSeleniumTestCase): self.selenium.get(self.live_server_url + reverse('admin:admin_inlines_holder4_add')) inline_id = '#inner4stacked_set-group' + rows_selector = '%s .dynamic-inner4stacked_set' % inline_id - def rows_length(): - return len(self.selenium.find_elements_by_css_selector('%s .dynamic-inner4stacked_set' % inline_id)) - self.assertEqual(rows_length(), 3) + self.assertCountSeleniumElements(rows_selector, 3) add_button = self.selenium.find_element_by_link_text( 'Add another Inner4 stacked') add_button.click() add_button.click() - self.assertEqual(len(self.selenium.find_elements_by_css_selector('#id_inner4stacked_set-4-dummy')), 1) + self.assertCountSeleniumElements('#id_inner4stacked_set-4-dummy', 1) # Enter some data and click 'Save'. self.selenium.find_element_by_name('dummy').send_keys('1') @@ -1338,14 +1333,15 @@ class SeleniumTests(AdminSeleniumTestCase): with self.wait_page_loaded(): self.selenium.find_element_by_xpath('//input[@value="Save"]').click() - self.assertEqual(rows_length(), 5, msg="sanity check") + # Sanity check. + self.assertCountSeleniumElements(rows_selector, 5) errorlist = self.selenium.find_element_by_css_selector( '%s .dynamic-inner4stacked_set .errorlist li' % inline_id ) self.assertEqual('Please correct the duplicate values below.', errorlist.text) delete_link = self.selenium.find_element_by_css_selector('#inner4stacked_set-4 .inline-deletelink') delete_link.click() - self.assertEqual(rows_length(), 4) + self.assertCountSeleniumElements(rows_selector, 4) with self.disable_implicit_wait(), self.assertRaises(NoSuchElementException): self.selenium.find_element_by_css_selector('%s .dynamic-inner4stacked_set .errorlist li' % inline_id) @@ -1361,16 +1357,15 @@ class SeleniumTests(AdminSeleniumTestCase): self.selenium.get(self.live_server_url + reverse('admin:admin_inlines_holder4_add')) inline_id = '#inner4tabular_set-group' + rows_selector = '%s .dynamic-inner4tabular_set' % inline_id - def rows_length(): - return len(self.selenium.find_elements_by_css_selector('%s .dynamic-inner4tabular_set' % inline_id)) - self.assertEqual(rows_length(), 3) + self.assertCountSeleniumElements(rows_selector, 3) add_button = self.selenium.find_element_by_link_text( 'Add another Inner4 tabular') add_button.click() add_button.click() - self.assertEqual(len(self.selenium.find_elements_by_css_selector('#id_inner4tabular_set-4-dummy')), 1) + self.assertCountSeleniumElements('#id_inner4tabular_set-4-dummy', 1) # Enter some data and click 'Save'. self.selenium.find_element_by_name('dummy').send_keys('1') @@ -1381,8 +1376,8 @@ class SeleniumTests(AdminSeleniumTestCase): self.selenium.find_element_by_name('inner4tabular_set-4-dummy').send_keys('222') with self.wait_page_loaded(): self.selenium.find_element_by_xpath('//input[@value="Save"]').click() - - self.assertEqual(rows_length(), 5, msg="sanity check") + # Sanity Check. + self.assertCountSeleniumElements(rows_selector, 5) # Non-field errorlist is in its own just before # tr#inner4tabular_set-3: @@ -1392,7 +1387,8 @@ class SeleniumTests(AdminSeleniumTestCase): self.assertEqual('Please correct the duplicate values below.', errorlist.text) delete_link = self.selenium.find_element_by_css_selector('#inner4tabular_set-4 .inline-deletelink') delete_link.click() - self.assertEqual(rows_length(), 4) + + self.assertCountSeleniumElements(rows_selector, 4) with self.disable_implicit_wait(), self.assertRaises(NoSuchElementException): self.selenium.find_element_by_css_selector('%s .dynamic-inner4tabular_set .errorlist li' % inline_id) @@ -1410,38 +1406,35 @@ class SeleniumTests(AdminSeleniumTestCase): self.selenium.get(self.live_server_url + reverse('admin:admin_inlines_profilecollection_add')) # There's only one inline to start with and it has the correct ID. - self.assertEqual(len(self.selenium.find_elements_by_css_selector( - '.dynamic-profile_set')), 1) - self.assertEqual(self.selenium.find_elements_by_css_selector( - '.dynamic-profile_set')[0].get_attribute('id'), - 'profile_set-0') - self.assertEqual(len(self.selenium.find_elements_by_css_selector( - '.dynamic-profile_set#profile_set-0 input[name=profile_set-0-first_name]')), 1) - self.assertEqual(len(self.selenium.find_elements_by_css_selector( - '.dynamic-profile_set#profile_set-0 input[name=profile_set-0-last_name]')), 1) + self.assertCountSeleniumElements('.dynamic-profile_set', 1) + self.assertEqual( + self.selenium.find_elements_by_css_selector('.dynamic-profile_set')[0].get_attribute('id'), + 'profile_set-0', + ) + self.assertCountSeleniumElements('.dynamic-profile_set#profile_set-0 input[name=profile_set-0-first_name]', 1) + self.assertCountSeleniumElements('.dynamic-profile_set#profile_set-0 input[name=profile_set-0-last_name]', 1) # Add an inline self.selenium.find_element_by_link_text('Add another Profile').click() # The inline has been added, it has the right id, and it contains the # correct fields. - self.assertEqual(len(self.selenium.find_elements_by_css_selector('.dynamic-profile_set')), 2) - self.assertEqual(self.selenium.find_elements_by_css_selector( - '.dynamic-profile_set')[1].get_attribute('id'), 'profile_set-1') - self.assertEqual(len(self.selenium.find_elements_by_css_selector( - '.dynamic-profile_set#profile_set-1 input[name=profile_set-1-first_name]')), 1) - self.assertEqual(len(self.selenium.find_elements_by_css_selector( - '.dynamic-profile_set#profile_set-1 input[name=profile_set-1-last_name]')), 1) - + self.assertCountSeleniumElements('.dynamic-profile_set', 2) + self.assertEqual( + self.selenium.find_elements_by_css_selector('.dynamic-profile_set')[1].get_attribute('id'), + 'profile_set-1', + ) + self.assertCountSeleniumElements('.dynamic-profile_set#profile_set-1 input[name=profile_set-1-first_name]', 1) + self.assertCountSeleniumElements('.dynamic-profile_set#profile_set-1 input[name=profile_set-1-last_name]', 1) # Let's add another one to be sure self.selenium.find_element_by_link_text('Add another Profile').click() - self.assertEqual(len(self.selenium.find_elements_by_css_selector('.dynamic-profile_set')), 3) - self.assertEqual(self.selenium.find_elements_by_css_selector( - '.dynamic-profile_set')[2].get_attribute('id'), 'profile_set-2') - self.assertEqual(len(self.selenium.find_elements_by_css_selector( - '.dynamic-profile_set#profile_set-2 input[name=profile_set-2-first_name]')), 1) - self.assertEqual(len(self.selenium.find_elements_by_css_selector( - '.dynamic-profile_set#profile_set-2 input[name=profile_set-2-last_name]')), 1) + self.assertCountSeleniumElements('.dynamic-profile_set', 3) + self.assertEqual( + self.selenium.find_elements_by_css_selector('.dynamic-profile_set')[2].get_attribute('id'), + 'profile_set-2', + ) + self.assertCountSeleniumElements('.dynamic-profile_set#profile_set-2 input[name=profile_set-2-first_name]', 1) + self.assertCountSeleniumElements('.dynamic-profile_set#profile_set-2 input[name=profile_set-2-last_name]', 1) # Enter some data and click 'Save' self.selenium.find_element_by_name('profile_set-0-first_name').send_keys('0 first name 1') @@ -1486,33 +1479,22 @@ class SeleniumTests(AdminSeleniumTestCase): self.selenium.find_element_by_link_text('Add another Profile').click() self.selenium.find_element_by_link_text('Add another Profile').click() self.selenium.find_element_by_link_text('Add another Profile').click() - self.assertEqual(len(self.selenium.find_elements_by_css_selector( - '#profile_set-group table tr.dynamic-profile_set')), 5) - self.assertEqual(len(self.selenium.find_elements_by_css_selector( - 'form#profilecollection_form tr.dynamic-profile_set#profile_set-0')), 1) - self.assertEqual(len(self.selenium.find_elements_by_css_selector( - 'form#profilecollection_form tr.dynamic-profile_set#profile_set-1')), 1) - self.assertEqual(len(self.selenium.find_elements_by_css_selector( - 'form#profilecollection_form tr.dynamic-profile_set#profile_set-2')), 1) - self.assertEqual(len(self.selenium.find_elements_by_css_selector( - 'form#profilecollection_form tr.dynamic-profile_set#profile_set-3')), 1) - self.assertEqual(len(self.selenium.find_elements_by_css_selector( - 'form#profilecollection_form tr.dynamic-profile_set#profile_set-4')), 1) - + self.assertCountSeleniumElements('#profile_set-group table tr.dynamic-profile_set', 5) + self.assertCountSeleniumElements('form#profilecollection_form tr.dynamic-profile_set#profile_set-0', 1) + self.assertCountSeleniumElements('form#profilecollection_form tr.dynamic-profile_set#profile_set-1', 1) + self.assertCountSeleniumElements('form#profilecollection_form tr.dynamic-profile_set#profile_set-2', 1) + self.assertCountSeleniumElements('form#profilecollection_form tr.dynamic-profile_set#profile_set-3', 1) + self.assertCountSeleniumElements('form#profilecollection_form tr.dynamic-profile_set#profile_set-4', 1) # Click on a few delete buttons self.selenium.find_element_by_css_selector( 'form#profilecollection_form tr.dynamic-profile_set#profile_set-1 td.delete a').click() self.selenium.find_element_by_css_selector( 'form#profilecollection_form tr.dynamic-profile_set#profile_set-2 td.delete a').click() # The rows are gone and the IDs have been re-sequenced - self.assertEqual(len(self.selenium.find_elements_by_css_selector( - '#profile_set-group table tr.dynamic-profile_set')), 3) - self.assertEqual(len(self.selenium.find_elements_by_css_selector( - 'form#profilecollection_form tr.dynamic-profile_set#profile_set-0')), 1) - self.assertEqual(len(self.selenium.find_elements_by_css_selector( - 'form#profilecollection_form tr.dynamic-profile_set#profile_set-1')), 1) - self.assertEqual(len(self.selenium.find_elements_by_css_selector( - 'form#profilecollection_form tr.dynamic-profile_set#profile_set-2')), 1) + self.assertCountSeleniumElements('#profile_set-group table tr.dynamic-profile_set', 3) + self.assertCountSeleniumElements('form#profilecollection_form tr.dynamic-profile_set#profile_set-0', 1) + self.assertCountSeleniumElements('form#profilecollection_form tr.dynamic-profile_set#profile_set-1', 1) + self.assertCountSeleniumElements('form#profilecollection_form tr.dynamic-profile_set#profile_set-2', 1) def test_collapsed_inlines(self): # Collapsed inlines have SHOW/HIDE links. @@ -1622,12 +1604,8 @@ class SeleniumTests(AdminSeleniumTestCase): tabular_inline_formset_selector = 'div#inner5tabular_set-group fieldset.module.collapse' # Inlines without errors, both inlines collapsed self.selenium.find_element_by_xpath('//input[@value="Save"]').click() - self.assertEqual( - len(self.selenium.find_elements_by_css_selector(stacked_inline_formset_selector + '.collapsed')), 1 - ) - self.assertEqual( - len(self.selenium.find_elements_by_css_selector(tabular_inline_formset_selector + '.collapsed')), 1 - ) + self.assertCountSeleniumElements(stacked_inline_formset_selector + '.collapsed', 1) + self.assertCountSeleniumElements(tabular_inline_formset_selector + '.collapsed', 1) show_links = self.selenium.find_elements_by_link_text('SHOW') self.assertEqual(len(show_links), 2) @@ -1647,18 +1625,10 @@ class SeleniumTests(AdminSeleniumTestCase): with self.wait_page_loaded(): self.selenium.find_element_by_xpath('//input[@value="Save"]').click() with self.disable_implicit_wait(): - self.assertEqual( - len(self.selenium.find_elements_by_css_selector(stacked_inline_formset_selector + '.collapsed')), 0 - ) - self.assertEqual( - len(self.selenium.find_elements_by_css_selector(tabular_inline_formset_selector + '.collapsed')), 0 - ) - self.assertEqual( - len(self.selenium.find_elements_by_css_selector(stacked_inline_formset_selector)), 1 - ) - self.assertEqual( - len(self.selenium.find_elements_by_css_selector(tabular_inline_formset_selector)), 1 - ) + self.assertCountSeleniumElements(stacked_inline_formset_selector + '.collapsed', 0) + self.assertCountSeleniumElements(tabular_inline_formset_selector + '.collapsed', 0) + self.assertCountSeleniumElements(stacked_inline_formset_selector, 1) + self.assertCountSeleniumElements(tabular_inline_formset_selector, 1) def test_inlines_verbose_name(self): """ diff --git a/tests/admin_views/test_autocomplete_view.py b/tests/admin_views/test_autocomplete_view.py index 0685d0ac752..302e4541096 100644 --- a/tests/admin_views/test_autocomplete_view.py +++ b/tests/admin_views/test_autocomplete_view.py @@ -359,27 +359,31 @@ class SeleniumTests(AdminSeleniumTestCase): elem.click() # Reopen the dropdown now that some objects exist. result_container = self.selenium.find_element_by_css_selector('.select2-results') self.assertTrue(result_container.is_displayed()) - results = result_container.find_elements_by_css_selector('.select2-results__option') # PAGINATOR_SIZE results and "Loading more results". - self.assertEqual(len(results), PAGINATOR_SIZE + 1) + self.assertCountSeleniumElements('.select2-results__option', PAGINATOR_SIZE + 1, root_element=result_container) search = self.selenium.find_element_by_css_selector('.select2-search__field') # Load next page of results by scrolling to the bottom of the list. with self.select2_ajax_wait(): - for _ in range(len(results)): + for _ in range(PAGINATOR_SIZE + 1): search.send_keys(Keys.ARROW_DOWN) - results = result_container.find_elements_by_css_selector('.select2-results__option') # All objects are now loaded. - self.assertEqual(len(results), PAGINATOR_SIZE + 11) + self.assertCountSeleniumElements( + '.select2-results__option', + PAGINATOR_SIZE + 11, + root_element=result_container, + ) # Limit the results with the search field. with self.select2_ajax_wait(): search.send_keys('Who') # Ajax request is delayed. self.assertTrue(result_container.is_displayed()) - results = result_container.find_elements_by_css_selector('.select2-results__option') - self.assertEqual(len(results), PAGINATOR_SIZE + 12) + self.assertCountSeleniumElements( + '.select2-results__option', + PAGINATOR_SIZE + 12, + root_element=result_container, + ) self.assertTrue(result_container.is_displayed()) - results = result_container.find_elements_by_css_selector('.select2-results__option') - self.assertEqual(len(results), 1) + self.assertCountSeleniumElements('.select2-results__option', 1, root_element=result_container) # Select the result. search.send_keys(Keys.RETURN) select = Select(self.selenium.find_element_by_id('id_question')) @@ -401,25 +405,22 @@ class SeleniumTests(AdminSeleniumTestCase): elem.click() # Reopen the dropdown now that some objects exist. result_container = self.selenium.find_element_by_css_selector('.select2-results') self.assertTrue(result_container.is_displayed()) - results = result_container.find_elements_by_css_selector('.select2-results__option') - self.assertEqual(len(results), PAGINATOR_SIZE + 1) + self.assertCountSeleniumElements('.select2-results__option', PAGINATOR_SIZE + 1, root_element=result_container) search = self.selenium.find_element_by_css_selector('.select2-search__field') # Load next page of results by scrolling to the bottom of the list. with self.select2_ajax_wait(): - for _ in range(len(results)): + for _ in range(PAGINATOR_SIZE + 1): search.send_keys(Keys.ARROW_DOWN) - results = result_container.find_elements_by_css_selector('.select2-results__option') - self.assertEqual(len(results), 31) + self.assertCountSeleniumElements('.select2-results__option', 31, root_element=result_container) # Limit the results with the search field. with self.select2_ajax_wait(): search.send_keys('Who') # Ajax request is delayed. self.assertTrue(result_container.is_displayed()) - results = result_container.find_elements_by_css_selector('.select2-results__option') - self.assertEqual(len(results), 32) + self.assertCountSeleniumElements('.select2-results__option', 32, root_element=result_container) self.assertTrue(result_container.is_displayed()) - results = result_container.find_elements_by_css_selector('.select2-results__option') - self.assertEqual(len(results), 1) + + self.assertCountSeleniumElements('.select2-results__option', 1, root_element=result_container) # Select the result. search.send_keys(Keys.RETURN) # Reopen the dropdown and add the first result to the selection. diff --git a/tests/admin_widgets/tests.py b/tests/admin_widgets/tests.py index 3a57227caa2..b79f6e0903c 100644 --- a/tests/admin_widgets/tests.py +++ b/tests/admin_widgets/tests.py @@ -992,7 +992,7 @@ class DateTimePickerShortcutsSeleniumTests(AdminWidgetSeleniumTestCase): # Warning: This would effectively fail if the TIME_ZONE defined in the # settings has the same UTC offset as "Asia/Singapore" because the # mismatch warning would be rightfully missing from the page. - self.selenium.find_elements_by_css_selector('.field-birthdate .timezonewarning') + self.assertCountSeleniumElements('.field-birthdate .timezonewarning', 1) # Submit the form. with self.wait_page_loaded(): @@ -1322,8 +1322,7 @@ class HorizontalVerticalFilterSeleniumTests(AdminWidgetSeleniumTestCase): change_url = reverse('admin:admin_widgets_school_change', args=(self.school.id,)) self.selenium.get(self.live_server_url + change_url) - options_len = len(self.selenium.find_elements_by_css_selector('#id_students_to > option')) - self.assertEqual(options_len, 2) + self.assertCountSeleniumElements('#id_students_to > option', 2) # self.selenium.refresh() or send_keys(Keys.F5) does hard reload and # doesn't replicate what happens when a user clicks the browser's @@ -1331,8 +1330,7 @@ class HorizontalVerticalFilterSeleniumTests(AdminWidgetSeleniumTestCase): with self.wait_page_loaded(): self.selenium.execute_script("location.reload()") - options_len = len(self.selenium.find_elements_by_css_selector('#id_students_to > option')) - self.assertEqual(options_len, 2) + self.assertCountSeleniumElements('#id_students_to > option', 2) class AdminRawIdWidgetSeleniumTests(AdminWidgetSeleniumTestCase):