diff --git a/newsfragments/1493.change.rst b/newsfragments/1493.change.rst new file mode 100644 index 00000000..b73d752f --- /dev/null +++ b/newsfragments/1493.change.rst @@ -0,0 +1,3 @@ +Make ``delete_license`` wait for the licenses table's search filter to +have been applied before clicking a row, rather than clicking a row +from the unfiltered table which is about to be replaced. diff --git a/src/vws_web_tools/__init__.py b/src/vws_web_tools/__init__.py index 5945b8ae..e9d9d3f7 100644 --- a/src/vws_web_tools/__init__.py +++ b/src/vws_web_tools/__init__.py @@ -378,23 +378,35 @@ def delete_license( search_input_element.send_keys(license_name) search_input_element.send_keys(Keys.ENTER) - license_name_xpath = _xpath_literal(value=license_name) - @beartype def _click_license_row( *, driver: WebDriver, ) -> bool: - """Find and click the row matching license_name.""" - element = driver.find_element( + """Find and click the row matching license_name. + + The search filter is applied asynchronously, so the table can + still hold unfiltered rows when this first runs. Wait for every + row shown to match the search text before clicking, rather than + clicking a row which is about to be replaced. + """ + rows = driver.find_elements( by=By.XPATH, value=( "//span[starts-with(@id, 'table_row_')" - " and contains(@id, '_app_name')" - f" and normalize-space(.)={license_name_xpath}]" + " and contains(@id, '_app_name')]" ), ) - element.click() + row_texts = [row.text.strip() for row in rows] + if not row_texts or not all( + license_name in row_text for row_text in row_texts + ): + return False + if license_name not in row_texts: # pragma: no cover + # Every row contains the search text by now, but a row whose + # text merely contains it is not the row we want. + return False + rows[row_texts.index(license_name)].click() return True thirty_second_wait.until(