From 2de4eea4edc1d8b5d12945b127f6cc596b4e85a5 Mon Sep 17 00:00:00 2001 From: Gabriel Mechali Date: Sat, 18 Jan 2025 09:20:24 -0500 Subject: [PATCH] [Fixit Fridays] Make webdriver helpers wait till the element exists, and use those methods in tests (#4862) Modified the base_utils find_elem and find_elems methods such that they first have to wait for the element if it's not found. This allows us to remove all that logic from within each test to check if the element exists before trying to find it. Also fixed how the XPath is sometimes used as the entire path multiple times in the same test. Moving forward, we should aim to always used find_elem, and find_elems to retrieve elements for webdriver tests. Will keep working towards moving over the old tests to using this approach. This also fixed the drought webdriver test which was previously skipped for flakiness. I expect that this will allow us to unskip many other tests as well. --- server/routes/dev_place/utils.py | 9 +- server/webdriver/base_utils.py | 87 ++++++-- server/webdriver/shared_tests/browser_test.py | 57 +++-- server/webdriver/tests/browser_test.py | 80 +++---- server/webdriver/tests/disaster_page_test.py | 28 +-- server/webdriver/tests/event_page_test.py | 203 ++++++++---------- server/webdriver/tests/homepage_test.py | 40 ++-- .../tests/place_explorer_i18n_test.py | 11 +- server/webdriver/tests/place_explorer_test.py | 95 +++++--- static/js/place/dev_place_main.tsx | 4 +- 10 files changed, 326 insertions(+), 288 deletions(-) diff --git a/server/routes/dev_place/utils.py b/server/routes/dev_place/utils.py index 239e78ccd7..beb4e6640f 100644 --- a/server/routes/dev_place/utils.py +++ b/server/routes/dev_place/utils.py @@ -48,11 +48,12 @@ ] # Place page categories -OVERVIEW_CATEGORY = "Overview" -ORDERED_CATEGORIES = [ - OVERVIEW_CATEGORY, "Economics", "Health", "Equity", "Crime", "Education", - "Demographics", "Housing", "Environment", "Energy" +ORDERED_TOPICS = [ + "Economics", "Health", "Equity", "Crime", "Education", "Demographics", + "Housing", "Environment", "Energy" ] +OVERVIEW_CATEGORY = "Overview" +ORDERED_CATEGORIES = [OVERVIEW_CATEGORY] + ORDERED_TOPICS CATEGORIES = set(ORDERED_CATEGORIES) diff --git a/server/webdriver/base_utils.py b/server/webdriver/base_utils.py index df8ed96137..24a2517dba 100644 --- a/server/webdriver/base_utils.py +++ b/server/webdriver/base_utils.py @@ -13,11 +13,16 @@ # limitations under the License. """Utilities used by the base webddriver test.""" +from typing import List + from selenium import webdriver from selenium.webdriver.chrome.options import Options +from selenium.webdriver.common.by import By from selenium.webdriver.support import expected_conditions as EC from selenium.webdriver.support.ui import WebDriverWait +from server.webdriver import shared + DEFAULT_HEIGHT = 1200 DEFAULT_WIDTH = 1200 @@ -37,29 +42,81 @@ def create_driver(preferences=None): return driver -def find_elem(parent, by: str, value: str): +def find_parents(parents, + by: str = By.CLASS_NAME, + parent_path: List[str] = None + ) -> list[webdriver.remote.webelement.WebElement]: """ - Finds an element within the parent element with the specified by string and value. - Returns None if not found. + Returns the final list of elements matching the 'by' in parent_path, waits for elements if needed. + Note that we only return the elements matching all the way to the last parent_path value. + For example: +
+
+
+
+
+
+
+
+ For find_parents(driver, By.ID, ['block', 'chart']) --> returns all 4 chart elements within the blocks. + """ + if not parent_path: + return parents + + elements_to_return = [] + for value in parent_path: + this_level_elements = [] + for par in parents: + this_level_elements.extend(find_elems(par, by, value)) + elements_to_return = this_level_elements + + return elements_to_return + + +def find_elems( + parent: webdriver.remote.webelement.WebElement, + by: str = By.CLASS_NAME, + value: str = "", + path_to_elem: List[str] = None +) -> list[webdriver.remote.webelement.WebElement]: """ - try: - return parent.find_element(by, value) - except: - return None + Finds elements within the parent elements with the specified by string and value. + If not found, it will wait up to the timeout set, and then return None. + """ + parents = find_parents([parent], by, + path_to_elem) if path_to_elem else [parent] + elements = [] + for par in parents: + wait_elem(par, by, value, shared.TIMEOUT) + found_elements = par.find_elements(by, value) + if found_elements: + elements.extend(found_elements) + else: + elems_or_none = wait_elem(par, by, value, shared.TIMEOUT) + if elems_or_none: + elements.append(elems_or_none) + return elements if elements else [] -def find_elems(parent, by: str, value: str): + +def find_elem( + parent: webdriver.remote.webelement.WebElement, + by: str = By.CLASS_NAME, + value: str = "", + path_to_elem: List[str] = None +) -> webdriver.remote.webelement.WebElement | None: """ - Finds elements within the parent element with the specified by string and value. - Returns None if not found. + Finds an element within the parent element with the specified by string and value. + If not found, it will wait up to the timeout set, and then return None. """ - try: - return parent.find_elements(by, value) - except: - return None + elems = find_elems(parent, by, value, path_to_elem) + return elems[0] if elems else None -def wait_elem(driver, by: str, value: str, timeout_seconds: float = 5): +def wait_elem(driver, + by: str = By.CLASS_NAME, + value: str = "", + timeout_seconds: float = 5): """ Waits for an element within the parent element with the specified by string and value. Uses a default timeout of 5 seconds. diff --git a/server/webdriver/shared_tests/browser_test.py b/server/webdriver/shared_tests/browser_test.py index 6035d4ba5b..5550a860a6 100644 --- a/server/webdriver/shared_tests/browser_test.py +++ b/server/webdriver/shared_tests/browser_test.py @@ -17,6 +17,8 @@ from selenium.webdriver.support.ui import WebDriverWait from server.webdriver import shared +from server.webdriver.base_utils import find_elem +from server.webdriver.base_utils import find_elems MTV_URL = '/browser/geoId/0649670' CA_POPULATION_URL = '/browser/geoId/06?statVar=Count_Person' @@ -44,23 +46,16 @@ def test_page_landing(self): self.assertIn(title_text, self.driver.title) # Wait for title to be present - title_locator = (By.TAG_NAME, 'h1') - WebDriverWait(self.driver, self.TIMEOUT_SEC).until( - EC.text_to_be_present_in_element(title_locator, 'Knowledge Graph')) - title_element = self.driver.find_element(*title_locator) self.assertEqual( - "Knowledge Graph", title_element.text, - f"Expected title 'Knowledge Graph', but found: {title_element.text}") + find_elem(self.driver, by=By.TAG_NAME, value='h1').text, + "Knowledge Graph") # Assert intro is correct - description_locator = ( - By.XPATH, "//h1[text()='Knowledge Graph']/following-sibling::p") - description_element = self.driver.find_element(*description_locator) - expected_description_start = 'The Data Commons Knowledge Graph is constructed' self.assertTrue( - description_element.text.startswith(expected_description_start), - f"Intro text does not start with expected text. Found: {description_element.text}" - ) + find_elem(self.driver, + by=By.XPATH, + value="//h1[text()='Knowledge Graph']/following-sibling::p"). + text.startswith("The Data Commons Knowledge Graph is constructed")) def test_page_serve_ca_population(self): """Test the browser page for California population can be loaded successfully.""" @@ -81,36 +76,32 @@ def test_page_serve_ca_population(self): self.assertEqual(title_text, self.driver.title) # Assert header is correct. - element_present = EC.presence_of_element_located((By.TAG_NAME, 'h1')) - WebDriverWait(self.driver, self.TIMEOUT_SEC).until(element_present) - statvar_title = self.driver.find_element(By.XPATH, '//*[@id="node"]/h1[1]') - self.assertEqual(statvar_title.text, 'Statistical Variable: Count_Person') - about_title = self.driver.find_element(By.XPATH, '//*[@id="node"]/h1[2]') - self.assertEqual(about_title.text, 'About: California') + node = find_elem(self.driver, by=By.XPATH, value='//*[@id="node"]') + self.assertEqual( + find_elem(node, by=By.XPATH, value='.//h1[1]').text, + 'Statistical Variable: Count_Person') + self.assertEqual( + find_elem(node, by=By.XPATH, value='.//h1[2]').text, + 'About: California') # Assert properties section shows dcid and typeOf values for the statistical variable # Count_Person. - element_present = EC.presence_of_element_located( - (By.XPATH, '//*[@id="node-content"]/div[1]/div/table')) - WebDriverWait(self.driver, self.TIMEOUT_SEC).until(element_present) - table = self.driver.find_element( - By.XPATH, '//*[@id="node-content"]/div[1]/div/table') - dcid_row = table.find_elements(By.XPATH, './/tbody/tr[2]/td') + table = find_elem(self.driver, + by=By.XPATH, + value='//*[@id="node-content"]/div[1]/div/table') + dcid_row = find_elems(table, by=By.XPATH, value='.//tbody/tr[2]/td') self.assertEqual(dcid_row[0].text, 'dcid') self.assertEqual(dcid_row[1].text, 'Count_Person') - type_of_row = table.find_elements(By.XPATH, './/tbody/tr[3]/td') + type_of_row = find_elems(table, by=By.XPATH, value='.//tbody/tr[3]/td') self.assertEqual(type_of_row[0].text, 'typeOf') self.assertEqual(type_of_row[1].text, 'StatisticalVariable') self.assertEqual(type_of_row[2].text, 'datacommons.org') # Assert observation charts loaded. - element_present = EC.presence_of_element_located( - (By.CLASS_NAME, 'observation-chart')) - WebDriverWait(self.driver, self.TIMEOUT_SEC).until(element_present) - observations_section = self.driver.find_element( - By.XPATH, '//*[@id="node-content"]/div[2]') - observations = observations_section.find_elements(By.CLASS_NAME, 'card') - self.assertTrue(len(observations) > 0) + observations_section = find_elem(self.driver, + by=By.XPATH, + value='//*[@id="node-content"]/div[2]') + self.assertGreater(len(find_elems(observations_section, value='card')), 0) def test_page_serve_austrobaileya(self): """Test the browser page for Austrobaileya scandens can be loaded successfully.""" diff --git a/server/webdriver/tests/browser_test.py b/server/webdriver/tests/browser_test.py index 7339522496..fdab4401b2 100644 --- a/server/webdriver/tests/browser_test.py +++ b/server/webdriver/tests/browser_test.py @@ -21,6 +21,7 @@ from selenium.webdriver.support.ui import WebDriverWait from server.webdriver.base_dc_webdriver import BaseDcWebdriverTest +from server.webdriver.base_utils import find_elem import server.webdriver.shared as shared from server.webdriver.shared_tests.browser_test import BrowserTestMixin @@ -107,22 +108,23 @@ def test_observation_table_redirect(self): self.driver.get(self.url_ + CA_POPULATION_URL) # Wait for observation charts to be loaded. - element_present = EC.presence_of_element_located( - (By.CLASS_NAME, 'observation-chart')) - WebDriverWait(self.driver, self.TIMEOUT_SEC).until(element_present) - observations_section = self.driver.find_element( - By.XPATH, '//*[@id="node-content"]/div[2]') + self.assertIsNotNone(find_elem(self.driver, value='observation-chart')) + observations_section = find_elem(self.driver, + by=By.XPATH, + value='//*[@id="node-content"]/div[2]') # Switch to table view for the first chart - observation_section_chart_1 = observations_section.find_elements( - By.CLASS_NAME, 'card')[0] - table_view_button = observation_section_chart_1.find_element( - By.TAG_NAME, 'button') + observation_section_chart_1 = find_elem(observations_section, value='card') + table_view_button = find_elem(observation_section_chart_1, + by=By.TAG_NAME, + value='button') table_view_button.click() # Click the first row in the table view to open the browser page for that observation - table = observation_section_chart_1.find_element(By.TAG_NAME, 'table') - first_row = table.find_element(By.XPATH, './/tbody/tr[2]/td') + table = find_elem(observation_section_chart_1, + by=By.TAG_NAME, + value='table') + first_row = find_elem(table, by=By.XPATH, value='.//tbody/tr[2]/td') first_row.click() # Wait for the new page to open in a new tab @@ -140,32 +142,33 @@ def test_observation_table_redirect(self): self.assertEqual(new_page_title, self.driver.title) # Assert header of the new page is correct. - element_present = EC.presence_of_element_located((By.TAG_NAME, 'h1')) - WebDriverWait(self.driver, self.TIMEOUT_SEC).until(element_present) - about_title = self.driver.find_element(By.XPATH, '//*[@id="node"]/h1') - self.assertEqual(about_title.text, 'About: dc/o/y54f4zvqrzf67') - dcid_subtitle = self.driver.find_element(By.XPATH, '//*[@id="node"]/h2[1]') - self.assertEqual(dcid_subtitle.text, 'dcid: dc/o/y54f4zvqrzf67') - typeOf_subtitle = self.driver.find_element(By.XPATH, - '//*[@id="node"]/h2[2]') - self.assertEqual(typeOf_subtitle.text, 'typeOf: StatVarObservation') + node = find_elem(self.driver, by=By.XPATH, value='//*[@id="node"]') + self.assertEqual( + find_elem(node, by=By.XPATH, value='.//h1').text, + 'About: dc/o/y54f4zvqrzf67') # about title. + self.assertEqual( + find_elem(node, by=By.XPATH, value='.//h2[1]').text, + 'dcid: dc/o/y54f4zvqrzf67') # dcid subtitle + self.assertEqual( + find_elem(node, by=By.XPATH, value='.//h2[2]').text, + 'typeOf: StatVarObservation') # typeOf_subtitle def test_observation_chart_redirect(self): """Test that the observation chart observation node links can redirect properly""" # Load California population browser page. self.driver.get(self.url_ + CA_POPULATION_URL) - element_present = EC.presence_of_element_located( - (By.XPATH, '//*[@id="node-content"]/div[1]/div/table')) - WebDriverWait(self.driver, self.TIMEOUT_SEC).until(element_present) + self.assertIsNotNone( + find_elem(self.driver, + by=By.XPATH, + value='//*[@id="node-content"]/div[1]/div/table')) # Click the point on the chart for the year 1850 - element_present = EC.presence_of_element_located((By.XPATH, ( - '//*[@id="node-content"]/div[2]/div/div[1]/div[2]/div/div[2]/div/*[name()="svg"]/' - + '*[name()="g"][4]/*[name()="g"]/*[name()="circle"][1]'))) - WebDriverWait(self.driver, self.TIMEOUT_SEC).until(element_present) - point = self.driver.find_element(By.XPATH, ( + point = find_elem( + self.driver, + by=By.XPATH, + value= '//*[@id="node-content"]/div[2]/div/div[1]/div[2]/div/div[2]/div/*[name()="svg"]/' - + '*[name()="g"][4]/*[name()="g"]/*[name()="circle"][1]')) + + '*[name()="g"][4]/*[name()="g"]/*[name()="circle"][1]') point.click() # Wait for the new page to open in a new tab @@ -183,12 +186,13 @@ def test_observation_chart_redirect(self): self.assertEqual(new_page_title, self.driver.title) # Assert header of the new page is correct. - element_present = EC.presence_of_element_located((By.TAG_NAME, 'h1')) - WebDriverWait(self.driver, self.TIMEOUT_SEC).until(element_present) - about_title = self.driver.find_element(By.XPATH, '//*[@id="node"]/h1') - self.assertEqual(about_title.text, 'About: dc/o/y54f4zvqrzf67') - dcid_subtitle = self.driver.find_element(By.XPATH, '//*[@id="node"]/h2[1]') - self.assertEqual(dcid_subtitle.text, 'dcid: dc/o/y54f4zvqrzf67') - typeOf_subtitle = self.driver.find_element(By.XPATH, - '//*[@id="node"]/h2[2]') - self.assertEqual(typeOf_subtitle.text, 'typeOf: StatVarObservation') + node = find_elem(self.driver, by=By.XPATH, value='//*[@id="node"]') + self.assertEqual( + find_elem(node, by=By.XPATH, value='.//h1').text, + 'About: dc/o/y54f4zvqrzf67') # about title. + self.assertEqual( + find_elem(node, by=By.XPATH, value='.//h2[1]').text, + 'dcid: dc/o/y54f4zvqrzf67') # dcid subtitle + self.assertEqual( + find_elem(node, by=By.XPATH, value='.//h2[2]').text, + 'typeOf: StatVarObservation') # typeOf_subtitle diff --git a/server/webdriver/tests/disaster_page_test.py b/server/webdriver/tests/disaster_page_test.py index 7216e7a9a0..c0309cf228 100644 --- a/server/webdriver/tests/disaster_page_test.py +++ b/server/webdriver/tests/disaster_page_test.py @@ -19,6 +19,9 @@ from selenium.webdriver.support.ui import WebDriverWait from server.webdriver.base import WebdriverBaseTest +from server.webdriver.base_utils import find_elem +from server.webdriver.base_utils import find_elems +from server.webdriver.base_utils import wait_elem class TestCharts(WebdriverBaseTest): @@ -45,26 +48,23 @@ def test_server_and_page(self): EC.title_contains('Disasters Dashboard')) # Wait until the group of charts has loaded. - element_present = EC.presence_of_element_located( - (By.ID, 'subject-page-main-pane')) - WebDriverWait(self.driver, self.TIMEOUT_SEC).until(element_present) + self.assertIsNotNone( + wait_elem(self.driver, by=By.ID, value='subject-page-main-pane')) # Store a list of all the charts. - event_maps = self.driver.find_elements(By.CLASS_NAME, - 'disaster-event-map-tile') - # Assert there are 5+ maps. + event_maps = find_elems(self.driver, value='disaster-event-map-tile') self.assertGreater(len(event_maps), 5) # Wait until the svg loads in the first article. - WebDriverWait(self.driver, self.TIMEOUT_SEC).until( - lambda d: event_maps[0].find_element(By.TAG_NAME, 'svg')) + self.assertIsNotNone(find_elem(event_maps[0], by=By.TAG_NAME, value='svg')) # Assert first article has svg with map geo region. - map_geo_region = event_maps[0].find_element(By.ID, 'map-geo-regions') - path = map_geo_region.find_elements(By.TAG_NAME, 'path') + map_geo_region = find_elem(event_maps[0], by=By.ID, value='map-geo-regions') + self.assertIsNotNone(map_geo_region) + + path = find_elems(map_geo_region, by=By.TAG_NAME, value='path') self.assertEqual(len(path), 1) - # Assert first article has svg with points. - map_points_layers = event_maps[0].find_elements(By.CLASS_NAME, - 'map-points-layer') - self.assertGreater(len(map_points_layers), 2) + # Assert first article has svg with at least 2 points. + self.assertGreater(len(find_elems(event_maps[0], value='map-points-layer')), + 2) diff --git a/server/webdriver/tests/event_page_test.py b/server/webdriver/tests/event_page_test.py index 87b29210e9..3ae812cb5f 100644 --- a/server/webdriver/tests/event_page_test.py +++ b/server/webdriver/tests/event_page_test.py @@ -21,6 +21,8 @@ from selenium.webdriver.support.ui import WebDriverWait from server.webdriver.base_dc_webdriver import BaseDcWebdriverTest +from server.webdriver.base_utils import find_elem +from server.webdriver.base_utils import find_elems BASE_PAGE_URL = '/event/' CYCLONE_NICOLE_DCID = 'cyclone/ibtracs_2022309N16290' @@ -47,57 +49,46 @@ def test_page_cyclone(self): EC.title_contains('Nicole - Event Page - ' + self.dc_title_string)) # Check header section - element_present = EC.presence_of_element_located((By.TAG_NAME, 'h1')) - WebDriverWait(self.driver, self.TIMEOUT_SEC).until(element_present) - title = self.driver.find_element(By.XPATH, - '//*[@id="main-pane"]/div[1]/div[1]/h1') - self.assertEqual(title.text, 'Nicole') - dcid_subtitle = self.driver.find_element( - By.XPATH, '//*[@id="main-pane"]/div[1]/div[1]/h3') + div1 = find_elem(self.driver, + by=By.XPATH, + value='//*[@id="main-pane"]/div[1]/div[1]') + self.assertEqual(find_elem(div1, by=By.TAG_NAME, value='h1').text, 'Nicole') self.assertEqual( - dcid_subtitle.text, + find_elem(div1, by=By.TAG_NAME, value='h3').text, 'Cyclone Event in Indian River County, Florida, United States, North America, Earth' ) - # Check google map section - element_present = EC.presence_of_element_located( - (By.CLASS_NAME, 'map-container')) - WebDriverWait(self.driver, self.TIMEOUT_SEC).until(element_present) - iframe_list = self.driver.find_elements(By.TAG_NAME, 'iframe') - # assert there is 1 iframe - self.assertEqual(len(iframe_list), 1) + # Check the Google Map section + map_container = find_elem(self.driver, value='map-container') + self.assertEqual( + len(find_elems(map_container, by=By.TAG_NAME, value='iframe')), 1) # Check property values table - element_present = EC.presence_of_element_located( - (By.XPATH, '//*[@id="main-pane"]/div[1]/section/div/table')) - WebDriverWait(self.driver, self.TIMEOUT_SEC).until(element_present) - table = self.driver.find_element( - By.XPATH, '//*[@id="main-pane"]/div[1]/section/div/table') - table_rows = table.find_elements( - By.XPATH, '//*[@id="main-pane"]/div[1]/section/div/table/tbody/tr') + table = find_elem(self.driver, + by=By.XPATH, + value='//*[@id="main-pane"]/div[1]/section/div/table') + table_rows = find_elems(table, by=By.XPATH, value='.//tbody/tr') + # assert there are 3+ rows in the property values table self.assertGreater(len(table_rows), 3) - date_row = table.find_elements( - By.XPATH, - '//*[@id="main-pane"]/div[1]/section/div/table/tbody/tr[2]/td') + date_rows = find_elems( + table, + by=By.XPATH, + value='//*[@id="main-pane"]/div[1]/section/div/table/tbody/tr[2]/td') # assert the date is correct - self.assertEqual(date_row[0].text, 'Date') - self.assertEqual(date_row[1].text, + self.assertEqual(date_rows[0].text, 'Date') + self.assertEqual(date_rows[1].text, '2022-11-05T06:00:00 — 2022-11-11T18:00:00') # Check additional charts section - element_present = EC.presence_of_element_located((By.TAG_NAME, 'svg')) - WebDriverWait(self.driver, self.TIMEOUT_SEC).until(element_present) - chart_section = self.driver.find_element(By.ID, 'subject-page-main-pane') - charts = chart_section.find_elements(By.CLASS_NAME, 'chart-container') - # assert there are 4+ charts - self.assertGreater(len(charts), 4) + chart_section = find_elem(self.driver, + by=By.ID, + value='subject-page-main-pane') + charts = find_elems(chart_section, value='chart-container') - # assert that the first chart has data - line_present = EC.presence_of_element_located((By.CLASS_NAME, 'line')) - WebDriverWait(self.driver, self.TIMEOUT_SEC).until(line_present) - chart_lines = charts[0].find_elements(By.CLASS_NAME, 'line') - self.assertEqual(len(chart_lines), 1) + # assert there are 4+ charts, and the first line has data + self.assertGreater(len(charts), 4) + self.assertEqual(len(find_elems(charts[0], value='line')), 1) def test_page_fire(self): """Test a fire event page can be loaded successfully""" @@ -115,60 +106,44 @@ def test_page_fire(self): EC.title_contains('0003 Fire 2015 (2836427)')) # Check header section - element_present = EC.presence_of_element_located((By.TAG_NAME, 'h1')) - WebDriverWait(self.driver, self.TIMEOUT_SEC).until(element_present) - title = self.driver.find_element(By.XPATH, - '//*[@id="main-pane"]/div[1]/div[1]/h1') - self.assertEqual(title.text, '0003 Fire 2015 (2836427)') - dcid_subtitle = self.driver.find_element( - By.XPATH, '//*[@id="main-pane"]/div[1]/div[1]/h3') + div1 = find_elem(self.driver, + by=By.XPATH, + value='//*[@id="main-pane"]/div[1]/div[1]') + self.assertEqual( + find_elem(div1, by=By.TAG_NAME, value='h1').text, + '0003 Fire 2015 (2836427)') self.assertEqual( - dcid_subtitle.text, + find_elem(div1, by=By.TAG_NAME, value='h3').text, 'Wildland Fire Event in Deschutes County, Oregon, United States, North America, Earth' ) # Check google map section - element_present = EC.presence_of_element_located( - (By.CLASS_NAME, 'map-container')) - WebDriverWait(self.driver, self.TIMEOUT_SEC).until(element_present) - element_present = EC.presence_of_element_located((By.TAG_NAME, 'iframe')) - WebDriverWait(self.driver, self.TIMEOUT_SEC).until(element_present) - iframe_list = self.driver.find_elements(By.TAG_NAME, 'iframe') - # assert there is 1 iframe - self.assertEqual(len(iframe_list), 1) + map_container = find_elem(self.driver, value='map-container') + self.assertEqual( + len(find_elems(map_container, by=By.TAG_NAME, value='iframe')), 1) # Check property values table - element_present = EC.presence_of_element_located( - (By.XPATH, '//*[@id="main-pane"]/div[1]/section/div/table')) - WebDriverWait(self.driver, self.TIMEOUT_SEC).until(element_present) - table = self.driver.find_element( - By.XPATH, '//*[@id="main-pane"]/div[1]/section/div/table') - table_rows = table.find_elements( - By.XPATH, '//*[@id="main-pane"]/div[1]/section/div/table/tbody/tr') - # assert there are 2+ rows in the property values table - self.assertGreater(len(table_rows), 2) - date_row = table.find_elements( - By.XPATH, - '//*[@id="main-pane"]/div[1]/section/div/table/tbody/tr[2]/td') - # assert the date is correct - self.assertEqual(date_row[0].text, 'Date') - self.assertEqual(date_row[1].text, '2015-01-05') + table = find_elem(self.driver, + by=By.XPATH, + value='//*[@id="main-pane"]/div[1]/section/div/table') + + # assert there are 2+ rows in the property values table and they have the right dates + self.assertGreater(len(find_elems(table, by=By.XPATH, value='.//tbody/tr')), + 2) + date_rows = find_elems(table, by=By.XPATH, value='.//tbody/tr[2]/td') + self.assertEqual(date_rows[0].text, 'Date') + self.assertEqual(date_rows[1].text, '2015-01-05') # Check additional charts section - element_present = EC.presence_of_element_located((By.TAG_NAME, 'svg')) - WebDriverWait(self.driver, self.TIMEOUT_SEC).until(element_present) - chart_section = self.driver.find_element(By.ID, 'subject-page-main-pane') - charts = chart_section.find_elements(By.CLASS_NAME, 'chart-container') - # assert there are 4+ charts + chart_section = find_elem(self.driver, + by=By.ID, + value='subject-page-main-pane') + charts = find_elems(chart_section, value='chart-container') + + # assert there are 4+ charts, and the first line has data self.assertGreater(len(charts), 4) - # assert that the first chart has data - line_present = EC.presence_of_element_located((By.CLASS_NAME, 'line')) - WebDriverWait(self.driver, self.TIMEOUT_SEC).until(line_present) - chart_lines = charts[0].find_elements(By.CLASS_NAME, 'line') - self.assertEqual(len(chart_lines), 1) - - # TODO (boxu): fix the flaky test and reenable it. - @unittest.skip + self.assertEqual(len(find_elems(charts[0], value='line')), 1) + def test_page_drought(self): """Test a drought event page can be loaded successfully""" @@ -186,51 +161,41 @@ def test_page_drought(self): self.dc_title_string)) # Check header section - element_present = EC.presence_of_element_located((By.TAG_NAME, 'h1')) - WebDriverWait(self.driver, self.TIMEOUT_SEC).until(element_present) - title = self.driver.find_element(By.XPATH, - '//*[@id="main-pane"]/div[1]/div[1]/h1') - self.assertEqual(title.text, 'stormEvent/nws5512667') - dcid_subtitle = self.driver.find_element( - By.XPATH, '//*[@id="main-pane"]/div[1]/div[1]/h3') + div1 = find_elem(self.driver, + by=By.XPATH, + value='//*[@id="main-pane"]/div[1]/div[1]') + self.assertEqual( + find_elem(div1, by=By.TAG_NAME, value='h1').text, + 'stormEvent/nws5512667') self.assertEqual( - dcid_subtitle.text, + find_elem(div1, by=By.TAG_NAME, value='h3').text, 'Drought Event in Hood County, Texas, United States, North America, Earth' ) # Check google map section - element_present = EC.presence_of_element_located( - (By.CLASS_NAME, 'map-container')) - WebDriverWait(self.driver, self.TIMEOUT_SEC).until(element_present) - iframe_list = self.driver.find_elements(By.TAG_NAME, 'iframe') - # assert there is 1 iframe - self.assertEqual(len(iframe_list), 1) + map_container = find_elem(self.driver, value='map-container') + self.assertEqual( + len(find_elems(map_container, by=By.TAG_NAME, value='iframe')), 1) # Check property values table - element_present = EC.presence_of_element_located( - (By.XPATH, '//*[@id="main-pane"]/div[1]/section/div/table')) - WebDriverWait(self.driver, self.TIMEOUT_SEC).until(element_present) - table = self.driver.find_element( - By.XPATH, '//*[@id="main-pane"]/div[1]/section/div/table') - table_rows = table.find_elements( - By.XPATH, '//*[@id="main-pane"]/div[1]/section/div/table/tbody/tr') - # assert there are 3+ rows in the property values table - self.assertGreater(len(table_rows), 2) - date_row = table.find_elements( - By.XPATH, - '//*[@id="main-pane"]/div[1]/section/div/table/tbody/tr[2]/td') - # assert the date is correct - self.assertEqual(date_row[0].text, 'Date') - self.assertEqual(date_row[1].text, + table = find_elem(self.driver, + by=By.XPATH, + value='//*[@id="main-pane"]/div[1]/section/div/table') + + # assert there are 2+ rows in the property values table and they have the right dates + self.assertGreater(len(find_elems(table, by=By.XPATH, value='.//tbody/tr')), + 2) + date_rows = find_elems(table, by=By.XPATH, value='.//tbody/tr[2]/td') + self.assertEqual(date_rows[0].text, 'Date') + self.assertEqual(date_rows[1].text, '2006-05-01T00:00:00-05:00 — 2006-05-08T23:59:00-05:00') # Check additional charts section - element_present = EC.presence_of_element_located((By.TAG_NAME, 'svg')) - WebDriverWait(self.driver, self.TIMEOUT_SEC).until(element_present) - chart_section = self.driver.find_element(By.ID, 'subject-page-main-pane') - charts = chart_section.find_elements(By.CLASS_NAME, 'chart-container') - # assert there are 5+ charts + chart_section = find_elem(self.driver, + by=By.ID, + value='subject-page-main-pane') + charts = find_elems(chart_section, value='chart-container') + + # assert there are 4+ charts, and the first line has data self.assertGreater(len(charts), 5) - # assert that the first chart has data - chart_lines = charts[0].find_elements(By.CLASS_NAME, 'line') - self.assertEqual(len(chart_lines), 1) + self.assertEqual(len(find_elems(charts[0], value='line')), 1) diff --git a/server/webdriver/tests/homepage_test.py b/server/webdriver/tests/homepage_test.py index fc5de509cf..d4073a866e 100644 --- a/server/webdriver/tests/homepage_test.py +++ b/server/webdriver/tests/homepage_test.py @@ -17,6 +17,8 @@ from selenium.webdriver.support.ui import WebDriverWait from server.webdriver.base_dc_webdriver import BaseDcWebdriverTest +from server.webdriver.base_utils import find_elem +from server.webdriver.base_utils import find_elems from server.webdriver.shared_tests.homepage_test import HomepageTestMixin @@ -28,13 +30,12 @@ def test_homepage_en_by_css(self): self.driver.get(self.url_ + '/') - title_present = EC.text_to_be_present_in_element( - (By.CSS_SELECTOR, '.navbar-brand'), self.dc_title_string) - WebDriverWait(self.driver, self.TIMEOUT_SEC).until(title_present) + # Assert page title is correct + WebDriverWait(self.driver, self.TIMEOUT_SEC).until( + EC.title_contains(self.dc_title_string)) - hero_msg = self.driver.find_elements(By.ID, 'hero')[0] self.assertTrue( - hero_msg.text.startswith( + find_elem(self.driver, by=By.ID, value='hero').text.startswith( "Data Commons brings together the world's public data, making it simple to explore" )) @@ -43,13 +44,12 @@ def test_homepage_it(self): self.driver.get(self.url_ + '/?hl=it') - title_present = EC.text_to_be_present_in_element( - (By.CSS_SELECTOR, '.navbar-brand'), self.dc_title_string) - WebDriverWait(self.driver, self.TIMEOUT_SEC).until(title_present) + # Assert page title is correct + WebDriverWait(self.driver, self.TIMEOUT_SEC).until( + EC.title_contains(self.dc_title_string)) - hero_msg = self.driver.find_elements(By.ID, 'hero')[0] self.assertTrue( - hero_msg.text.startswith( + find_elem(self.driver, by=By.ID, value='hero').text.startswith( "Data Commons brings together the world's public data, making it simple to explore" )) @@ -100,19 +100,15 @@ def test_homepage_autocomplete(self): self.driver.get(self.url_ + '/?ac_on=true') - title_present = EC.text_to_be_present_in_element( - (By.CSS_SELECTOR, '.navbar-brand'), self.dc_title_string) - WebDriverWait(self.driver, self.TIMEOUT_SEC).until(title_present) - - search_box_input = self.driver.find_element(By.ID, 'query-search-input') + # Assert page title is correct + WebDriverWait(self.driver, self.TIMEOUT_SEC).until( + EC.title_contains(self.dc_title_string)) # Type california into the search box. + search_box_input = find_elem(self.driver, + by=By.ID, + value='query-search-input') search_box_input.send_keys("California") - suggestions_present = EC.presence_of_element_located( - (By.CLASS_NAME, 'search-input-result-section')) - WebDriverWait(self.driver, 300).until(suggestions_present) - - autocomplete_results = self.driver.find_elements( - By.CLASS_NAME, 'search-input-result-section') - self.assertTrue(len(autocomplete_results) == 5) + self.assertEqual( + len(find_elems(self.driver, value='search-input-result-section')), 5) diff --git a/server/webdriver/tests/place_explorer_i18n_test.py b/server/webdriver/tests/place_explorer_i18n_test.py index e5aa9997d7..4800836e64 100644 --- a/server/webdriver/tests/place_explorer_i18n_test.py +++ b/server/webdriver/tests/place_explorer_i18n_test.py @@ -18,6 +18,7 @@ from server.webdriver import shared from server.webdriver.base_dc_webdriver import BaseDcWebdriverTest +from server.webdriver.base_utils import find_elem from server.webdriver.shared_tests.place_explorer_i18n_test import \ PlaceI18nExplorerTestMixin @@ -41,9 +42,7 @@ def test_explorer_redirect_place_explorer_populates_search_bar(self): shared.wait_for_loading(self.driver) # Ensure the query string is set in the NL Search Bar. - search_bar_present = EC.presence_of_element_located( - (By.ID, 'query-search-input')) - WebDriverWait(self.driver, self.TIMEOUT_SEC).until(search_bar_present) - search_bar = self.driver.find_element(By.ID, 'query-search-input') - self.assertEqual(search_bar.get_attribute('value'), - 'United States Of America') + self.assertEqual( + find_elem(self.driver, by=By.ID, + value='query-search-input').get_attribute('value'), + 'United States Of America') diff --git a/server/webdriver/tests/place_explorer_test.py b/server/webdriver/tests/place_explorer_test.py index 9c04d82db7..0b11640caa 100644 --- a/server/webdriver/tests/place_explorer_test.py +++ b/server/webdriver/tests/place_explorer_test.py @@ -17,9 +17,11 @@ from selenium.webdriver.support.ui import WebDriverWait from server.routes.dev_place.utils import ORDERED_CATEGORIES -from server.webdriver import base_utils +from server.routes.dev_place.utils import ORDERED_TOPICS from server.webdriver import shared from server.webdriver.base_dc_webdriver import BaseDcWebdriverTest +from server.webdriver.base_utils import find_elem +from server.webdriver.base_utils import find_elems from server.webdriver.shared_tests.place_explorer_test import \ PlaceExplorerTestMixin @@ -31,39 +33,64 @@ def test_dev_place_overview_california(self): """Ensure experimental dev place page content loads""" self.driver.get(self.url_ + '/place/geoId/06?force_dev_places=true') - expected_topics = ORDERED_CATEGORIES + # Assert the subheader contains the parent places. + self.assertIsNotNone(find_elem(self.driver, value='place-info')) + self.assertEqual( + find_elem(self.driver, value='subheader').text, + 'State in United States of America, North America') + + # Asert the related places box exists + self.assertEqual( + find_elem(self.driver, value='related-places-callout').text, + 'Places in California') + + # Assert the overview exists, has a summary and a map. + self.assertNotEqual(len(find_elem(self.driver, value='place-summary').text), + "") + self.assertIsNotNone(find_elem(self.driver, value='map-container')) + + # Assert the key demographics table has data + self.assertEqual( + len( + find_elems(self.driver, + value='key-demographics-row', + path_to_elem=['key-demographics-table'])), 5) + shared.assert_topics(self, self.driver, path_to_topics=['explore-topics-box'], classname='item-list-item', - expected_topics=expected_topics) + expected_topics=ORDERED_CATEGORIES) - # Assert the subheader contains the parent places. - place_subheader_callout_el = base_utils.wait_elem(self.driver, - By.CLASS_NAME, - 'subheader', - self.TIMEOUT_SEC) - self.assertEqual(place_subheader_callout_el.text, - 'State in United States of America, North America') - - # For the dev place page, the related places callout is under the - # .related-places-callout div. - related_places_callout_el = base_utils.wait_elem(self.driver, By.CLASS_NAME, - 'related-places-callout', - self.TIMEOUT_SEC) - self.assertEqual(related_places_callout_el.text, 'Places in California') - - # Assert the "Download" link is present in charts - download_link_el = base_utils.wait_elem(self.driver, By.CLASS_NAME, - 'download-outlink', - self.TIMEOUT_SEC) - self.assertTrue('Download' in download_link_el.text) - - # Assert the "Explore in ... Tool" link is present in charts - explore_in_link_el = base_utils.wait_elem(self.driver, By.CLASS_NAME, - 'explore-in-outlink', - self.TIMEOUT_SEC) - self.assertTrue('Explore in' in explore_in_link_el.text) + # And that the categories have data in the overview + block_titles = find_elems(self.driver, value='block-title') + self.assertEqual(set([block.text for block in block_titles]), + set(ORDERED_TOPICS)) + + # Assert that every category is expected, and has at least one chart + category_containers = find_elems(self.driver, + value='category', + path_to_elem=['charts-container']) + self.assertEqual(len(category_containers), len(ORDERED_TOPICS)) + for category_container in category_containers: + chart_containers = find_elems(category_container, value='chart-container') + self.assertGreater(len(chart_containers), 0) + + def test_dev_place_chart_settings(self): + """Ensure the charts in the new place page contain the expected settings""" + self.driver.get(self.url_ + '/place/geoId/06?force_dev_places=true') + + self.assertTrue( + find_elem(self.driver, + value='download-outlink', + path_to_elem=['charts-container']).text, 'Download') + + self.assertTrue( + find_elem(self.driver, + value='explore-in-outlink', + path_to_elem=['charts-container']).text, + 'Explore in Timeline tool', + ) def test_explorer_redirect_place_explorer_populates_search_bar(self): """Test the redirection from explore to place explore for single place queries populates the search bar from the URL query""" @@ -81,9 +108,7 @@ def test_explorer_redirect_place_explorer_populates_search_bar(self): shared.wait_for_loading(self.driver) # Ensure the query string is set in the NL Search Bar. - search_bar_present = EC.presence_of_element_located( - (By.ID, 'query-search-input')) - WebDriverWait(self.driver, self.TIMEOUT_SEC).until(search_bar_present) - search_bar = self.driver.find_element(By.ID, 'query-search-input') - self.assertEqual(search_bar.get_attribute('value'), - 'United States Of America') + self.assertEqual( + find_elem(self.driver, by=By.ID, + value='query-search-input').get_attribute('value'), + 'United States Of America') diff --git a/static/js/place/dev_place_main.tsx b/static/js/place/dev_place_main.tsx index c006ed220b..5f75476021 100644 --- a/static/js/place/dev_place_main.tsx +++ b/static/js/place/dev_place_main.tsx @@ -224,7 +224,7 @@ const PlaceOverviewTable = (props: { }); return ( - +
+
@@ -241,7 +241,7 @@ const PlaceOverviewTable = (props: { const formattedObservationValue = dataRow.variable.observation.value.toLocaleString(); return ( -
{dataRow.variable.properties.name} {formattedObservationValue} {unit} (