From f62b91c9bbed661ca91eb7a6a67103712bef7ed5 Mon Sep 17 00:00:00 2001 From: nuwang <2070605+nuwang@users.noreply.github.com> Date: Fri, 6 Sep 2024 22:05:39 +0530 Subject: [PATCH 1/8] Use latest selenium and firefox --- Dockerfile | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-) diff --git a/Dockerfile b/Dockerfile index f2d546b..c625a30 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,6 +1,17 @@ # https://github.com/joyzoursky/docker-python-chromedriver # https://github.com/TomRoush/python-selenium-firefox-docker -FROM tomroush/python-selenium-firefox-docker:latest +FROM ubuntu:22.04 AS selenium + +ARG DEBIAN_FRONTEND=noninteractive +RUN apt-get update && apt-get install -y \ + python3 python3-pip \ + firefox \ + xvfb \ + && rm -rf /var/lib/apt/lists/* + +RUN pip3 install --no-cache-dir selenium tenacity + +FROM selenium ENV SELENIUM_HEADLESS=true From dc4c6f9500378037baf4b8df9fac517d8000cceb Mon Sep 17 00:00:00 2001 From: nuwang <2070605+nuwang@users.noreply.github.com> Date: Fri, 6 Sep 2024 22:06:01 +0530 Subject: [PATCH 2/8] Misc changes to support 24.1 --- page_perf_timer.py | 105 ++++++++++++++++++++++++++++++++------------- 1 file changed, 76 insertions(+), 29 deletions(-) diff --git a/page_perf_timer.py b/page_perf_timer.py index c839ff1..f28e964 100644 --- a/page_perf_timer.py +++ b/page_perf_timer.py @@ -5,9 +5,11 @@ import time import uuid +import tenacity + # Generated by Selenium IDE from selenium import webdriver -from selenium.common.exceptions import NoSuchElementException +from selenium.common.exceptions import NoSuchElementException, StaleElementReferenceException, ElementClickInterceptedException from selenium.webdriver.common.by import By from selenium.webdriver.common.keys import Keys from selenium.webdriver.support import expected_conditions @@ -154,7 +156,7 @@ def login_to_galaxy_homepage(self): # Wait for tool panel to load self.wait.until( expected_conditions.presence_of_element_located( - (By.XPATH, "//div[@class='tool-panel-section']//a[@class='title-link' and contains(., 'Get Data')]") + (By.XPATH, "//div[@class='tool-panel-section']//a[contains(@class, 'title-link') and contains(., 'Get Data')]") ) ) @@ -190,33 +192,41 @@ def load_tool_form(self): expected_conditions.presence_of_element_located((By.ID, "execute")) ) - @clock_action("shared_histories_page_load") - def load_shared_histories(self): + @clock_action("published_histories_page_load") + def load_published_histories(self): # Request history page - self.driver.get(f"{self.server}/histories/list_shared") + self.driver.get(f"{self.server}/histories/list_published") # Wait for history page to load self.wait.until( expected_conditions.presence_of_element_located( - (By.XPATH, "//h2[contains(., 'Histories shared with you by others')]") + (By.XPATH, "//li[@id='histories-published-tab' and contains(., 'Public Histories')]") ) ) self.wait_for_history_panel_to_load() - @clock_action("import_shared_history") - def import_shared_history(self): + @clock_action("import_published_history") + def import_published_history(self): + # Search for the relevant history + search_history_input = self.driver.find_element( + By.XPATH, + f"//div[@id='histories-published-grid']//input[@placeholder='search histories']", + ) + search_history_input.click() + search_history_input.send_keys(f'{self.workflow_name.lower()}_input_data') + # Select relevant history import_history_btn = self.driver.find_element( By.XPATH, - f"//tbody[@id='grid-table-body']//button[contains(translate(., 'ABCDEFGHIJKLMNOPQRSTUVWXYZ', 'abcdefghijklmnopqrstuvwxyz'), '{self.workflow_name.lower()}_input_data')]", + f"//table[@class='grid-table']//button[contains(translate(., 'ABCDEFGHIJKLMNOPQRSTUVWXYZ', 'abcdefghijklmnopqrstuvwxyz'), '{self.workflow_name.lower()}_input_data')]", ) - import_history_btn.click() - self.wait_for_history_panel_to_load() + # Workaround for ElementClickInterceptedException + self.driver.execute_script("arguments[0].click();", import_history_btn) # View history details - view_history_menu_item = self.driver.find_element( + view_history_menu_item = import_history_btn.find_element( By.XPATH, - f"//div[@id='{import_history_btn.get_attribute('id')}-menu']//a[contains(., 'View')]", + f"./following-sibling::div//button[contains(@data-description, 'grid operation view')]", ) view_history_menu_item.click() self.wait.until( @@ -239,6 +249,20 @@ def import_shared_history(self): By.XPATH, f"//button[contains(., 'Copy History')]", ).click() + + # activate the history + self.wait.until( + expected_conditions.presence_of_element_located( + ( + By.XPATH, + f"//div[@class='alert alert-info' and contains(., 'History imported and is now your active history')]", + ) + ) + ) + + # Request history page + self.driver.get(f"{self.server}/histories/list") + # Wait for history panel to load with new history self.wait.until( expected_conditions.presence_of_element_located( @@ -252,20 +276,32 @@ def import_shared_history(self): @clock_action("workflow_list_page_load") def load_workflow_list(self): # Request workflows list page - self.driver.get(f"{self.server}/workflows/list") + self.driver.get(f"{self.server}/workflows/list_shared_with_me") # Wait for workflow page to load and import button to appear self.wait.until( - expected_conditions.presence_of_element_located((By.ID, "workflow-import")) + expected_conditions.presence_of_element_located( + (By.XPATH, "//li[@id='published' and contains(., 'Public workflows')]") + ) ) @clock_action("workflow_run_page_load") def load_workflow_run_form(self): - # Select relevant workflow - run_workflow_btn = self.driver.find_element( + # Search for the relevant history + search_workflow_input = self.driver.find_element( By.XPATH, - f"//table[@id='workflow-table']//tr[contains(., '{self.workflow_name}')]//button[@title='Run workflow']", + f"//div[@id='workflow-list-filter']//input", ) - run_workflow_btn.click() + search_workflow_input.click() + search_workflow_input.send_keys(f'{self.workflow_name.lower()}') + + # wait for list to be filtered + self.wait.until(lambda d: len(d.find_elements(By.CSS_SELECTOR, '#workflow-cards .workflow-card')) == 1) + + # Select relevant workflow + run_workflow_btn = self.driver.find_element(By.ID, "workflow-run-button") + # Workaround for ElementClickInterceptedException + self.driver.execute_script("arguments[0].click();", run_workflow_btn) + # Wait for workflow form to load and run button to appear self.wait.until( expected_conditions.presence_of_element_located((By.ID, "run-workflow")) @@ -277,13 +313,13 @@ def run_workflow(self): # Select relevant choice input_1_select = self.driver.find_element( By.XPATH, - "//a[@class='select2-choice' and contains(., 'Subsample of reads from')]", + "//div[@class='ui-form-composite']//input/following-sibling::span[contains(., 'Subsample of reads from Human exome R2')][1]", ) input_1_select.click() # Select relevant choice input_1_select = self.driver.find_element( By.XPATH, - "//div[@id='select2-drop']//ul/li/div[contains(., 'Subsample of reads from human exome R1')]", + "//div[@class='ui-form-composite']//ul[@role='listbox']//li[@role='option']//span[contains(., 'Subsample of reads from human exome R1')]", ) input_1_select.click() workflow_wait = 14400 @@ -321,24 +357,35 @@ def run_workflow(self): # Run the workflow self.driver.find_element(By.ID, "run-workflow").click() - # Wait for view reports button to appear (workflow to finish) + + # wait for the running message to appear + self.wait.until( + expected_conditions.presence_of_element_located( + ( + By.XPATH, + f"//div[@id='center']//div[@role='tabpanel']//div[@role='alert']//span[@data-description='loading message' and contains(., 'Waiting to complete invocation')]", + ) + ) + ) + + # Wait for running message to disappear with SeleniumCustomWait(self.driver, workflow_wait): self.wait.until( - expected_conditions.presence_of_element_located( - ( - By.XPATH, - "//div[@id='center']//a[contains(@class, 'invocation-report-link') and contains(., 'View Report')]", - ) + expected_conditions.invisibility_of_element_located( + ( + By.XPATH, + f"//div[@id='center']//div[@role='tabpanel']//div[@role='alert']//span[@data-description='loading message' and contains(., 'Waiting to complete invocation')]", ) ) + ) def run_test_sequence(self): self.load_galaxy_login() self.login_to_galaxy_homepage() self.search_for_tool() self.load_tool_form() - self.load_shared_histories() - self.import_shared_history() + self.load_published_histories() + self.import_published_history() self.load_workflow_list() self.load_workflow_run_form() self.run_workflow() From 057746f020e2967a22e1e63d0076f77f9990780e Mon Sep 17 00:00:00 2001 From: nuwang <2070605+nuwang@users.noreply.github.com> Date: Fri, 6 Sep 2024 22:12:36 +0530 Subject: [PATCH 3/8] Run black against code --- page_perf_timer.py | 39 ++++++++++++------- .../registration_email_perf_timer.py | 10 +++-- 2 files changed, 32 insertions(+), 17 deletions(-) diff --git a/page_perf_timer.py b/page_perf_timer.py index f28e964..6a5c5e9 100644 --- a/page_perf_timer.py +++ b/page_perf_timer.py @@ -5,11 +5,9 @@ import time import uuid -import tenacity - # Generated by Selenium IDE from selenium import webdriver -from selenium.common.exceptions import NoSuchElementException, StaleElementReferenceException, ElementClickInterceptedException +from selenium.common.exceptions import NoSuchElementException from selenium.webdriver.common.by import By from selenium.webdriver.common.keys import Keys from selenium.webdriver.support import expected_conditions @@ -40,6 +38,7 @@ class EndStepReached(Exception): """ Raised when a specific action step has been reached """ + pass @@ -156,7 +155,10 @@ def login_to_galaxy_homepage(self): # Wait for tool panel to load self.wait.until( expected_conditions.presence_of_element_located( - (By.XPATH, "//div[@class='tool-panel-section']//a[contains(@class, 'title-link') and contains(., 'Get Data')]") + ( + By.XPATH, + "//div[@class='tool-panel-section']//a[contains(@class, 'title-link') and contains(., 'Get Data')]", + ) ) ) @@ -200,7 +202,10 @@ def load_published_histories(self): # Wait for history page to load self.wait.until( expected_conditions.presence_of_element_located( - (By.XPATH, "//li[@id='histories-published-tab' and contains(., 'Public Histories')]") + ( + By.XPATH, + "//li[@id='histories-published-tab' and contains(., 'Public Histories')]", + ) ) ) self.wait_for_history_panel_to_load() @@ -213,7 +218,7 @@ def import_published_history(self): f"//div[@id='histories-published-grid']//input[@placeholder='search histories']", ) search_history_input.click() - search_history_input.send_keys(f'{self.workflow_name.lower()}_input_data') + search_history_input.send_keys(f"{self.workflow_name.lower()}_input_data") # Select relevant history import_history_btn = self.driver.find_element( @@ -231,7 +236,10 @@ def import_published_history(self): view_history_menu_item.click() self.wait.until( expected_conditions.presence_of_element_located( - (By.XPATH, f"//h3[contains(., '{self.workflow_name.lower()}_input_data')]") + ( + By.XPATH, + f"//h3[contains(., '{self.workflow_name.lower()}_input_data')]", + ) ) ) # Invoke copy history dialogue @@ -292,10 +300,15 @@ def load_workflow_run_form(self): f"//div[@id='workflow-list-filter']//input", ) search_workflow_input.click() - search_workflow_input.send_keys(f'{self.workflow_name.lower()}') + search_workflow_input.send_keys(f"{self.workflow_name.lower()}") # wait for list to be filtered - self.wait.until(lambda d: len(d.find_elements(By.CSS_SELECTOR, '#workflow-cards .workflow-card')) == 1) + self.wait.until( + lambda d: len( + d.find_elements(By.CSS_SELECTOR, "#workflow-cards .workflow-card") + ) + == 1 + ) # Select relevant workflow run_workflow_btn = self.driver.find_element(By.ID, "workflow-run-button") @@ -372,12 +385,12 @@ def run_workflow(self): with SeleniumCustomWait(self.driver, workflow_wait): self.wait.until( expected_conditions.invisibility_of_element_located( - ( - By.XPATH, - f"//div[@id='center']//div[@role='tabpanel']//div[@role='alert']//span[@data-description='loading message' and contains(., 'Waiting to complete invocation')]", + ( + By.XPATH, + f"//div[@id='center']//div[@role='tabpanel']//div[@role='alert']//span[@data-description='loading message' and contains(., 'Waiting to complete invocation')]", + ) ) ) - ) def run_test_sequence(self): self.load_galaxy_login() diff --git a/registration_email/registration_email_perf_timer.py b/registration_email/registration_email_perf_timer.py index 46cc7e2..954ecf9 100644 --- a/registration_email/registration_email_perf_timer.py +++ b/registration_email/registration_email_perf_timer.py @@ -146,14 +146,16 @@ def register_new_account(self): def delete_test_account(self): gi = galaxy.GalaxyInstance(url=self.server, key=self.api_key) user = gi.users.get_users(f_name=self.username)[0] - if user['email'] == self.email: - gi.users.delete_user(user['id']) - gi.users.delete_user(user['id'], purge=True) + if user["email"] == self.email: + gi.users.delete_user(user["id"]) + gi.users.delete_user(user["id"], purge=True) @tenacity.retry( retry=tenacity.retry_if_result(lambda result: not result), wait=tenacity.wait_fixed(int(os.environ.get("IMAP_POLL_SECONDS", 10))), - stop=tenacity.stop_after_attempt(int(os.environ.get("IMAP_MAX_POLL_ATTEMPTS", 12))), + stop=tenacity.stop_after_attempt( + int(os.environ.get("IMAP_MAX_POLL_ATTEMPTS", 12)) + ), retry_error_callback=(lambda _: False), ) def verify_email_received(self): From 298ff52e9bb5cdb48514966b63cbfd043ef50db3 Mon Sep 17 00:00:00 2001 From: nuwang <2070605+nuwang@users.noreply.github.com> Date: Sat, 14 Sep 2024 14:49:11 +0530 Subject: [PATCH 4/8] More test fixes --- page_perf_timer.py | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/page_perf_timer.py b/page_perf_timer.py index 6a5c5e9..714a96c 100644 --- a/page_perf_timer.py +++ b/page_perf_timer.py @@ -238,7 +238,7 @@ def import_published_history(self): expected_conditions.presence_of_element_located( ( By.XPATH, - f"//h3[contains(., '{self.workflow_name.lower()}_input_data')]", + f"//h3[contains(translate(., 'ABCDEFGHIJKLMNOPQRSTUVWXYZ', 'abcdefghijklmnopqrstuvwxyz'), '{self.workflow_name.lower()}_input_data')]", ) ) ) @@ -342,28 +342,28 @@ def run_workflow(self): # Select relevant choice input_1_select = self.driver.find_element( By.XPATH, - "//div[@data-label='Forward Reads']//a[@class='select2-choice']", + "//div[@class='ui-form-composite']//input/following-sibling::span[contains(., 'ERR019289_2.fastq.gz')][1]", ) input_1_select.click() # Select relevant choice input_1_select = self.driver.find_element( By.XPATH, - "//div[@id='select2-drop']//ul/li/div[contains(., 'ERR019289_1.fastq.gz')]", + "//div[@class='ui-form-composite']//ul[@role='listbox']//li[@role='option']//span[contains(., 'ERR019289_1.fastq.gz')]", ) input_1_select.click() workflow_wait = 14400 elif self.workflow_name == "Selenium_test_4": - input_select = self.driver.find_element( + input_1_select = self.driver.find_element( By.XPATH, - "//div[@data-label='ARTIC primers to amplicon assignments']//a[@class='select2-choice']", + "//div[@class='ui-form-composite']//div[@data-label='ARTIC primers to amplicon assignments']//input/following-sibling::span[contains(., 'NC_045512.2.fna.fasta')][1]", ) - input_select.click() + input_1_select.click() # Select relevant choice - input_select = self.driver.find_element( + input_1_select = self.driver.find_element( By.XPATH, - "//div[@id='select2-drop']//ul/li/div[contains(., 'ARTIC_SARS_CoV-2_amplicon_info_v3.tsv')]", + "//div[@class='ui-form-composite']//div[@data-label='ARTIC primers to amplicon assignments']//ul[@role='listbox']//li[@role='option']//span[contains(., 'ARTIC_SARS_CoV-2_amplicon_info_v3.tsv')]", ) - input_select.click() + input_1_select.click() workflow_wait = 14400 else: raise Exception(f"Workflow name not in known list: {self.workflow_name}") From e8803dea1515d06d777a1bbd4456cfcc4d1460c2 Mon Sep 17 00:00:00 2001 From: nuwang <2070605+nuwang@users.noreply.github.com> Date: Sat, 14 Sep 2024 19:21:01 +0530 Subject: [PATCH 5/8] Some fixes for firefox --- Dockerfile | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/Dockerfile b/Dockerfile index c625a30..404d17c 100644 --- a/Dockerfile +++ b/Dockerfile @@ -5,15 +5,18 @@ FROM ubuntu:22.04 AS selenium ARG DEBIAN_FRONTEND=noninteractive RUN apt-get update && apt-get install -y \ python3 python3-pip \ + # firefox needs the following installed on 22.04 + libgtk-3-common libasound2 libdbus-glib-1-2 \ firefox \ xvfb \ && rm -rf /var/lib/apt/lists/* -RUN pip3 install --no-cache-dir selenium tenacity +RUN pip3 install --no-cache-dir selenium FROM selenium ENV SELENIUM_HEADLESS=true +ARG DEBIAN_FRONTEND=noninteractive ADD page_perf_timer.py /opt/page_timer/ From ce0bc65cdaf1737b964f3ef3d4b925168bb6fd33 Mon Sep 17 00:00:00 2001 From: nuwang <2070605+nuwang@users.noreply.github.com> Date: Sun, 15 Sep 2024 13:01:09 +0530 Subject: [PATCH 6/8] Add upload/download test --- page_perf_timer.py | 43 +++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 43 insertions(+) diff --git a/page_perf_timer.py b/page_perf_timer.py index 714a96c..d2d4ea0 100644 --- a/page_perf_timer.py +++ b/page_perf_timer.py @@ -1,10 +1,13 @@ import argparse import functools +import hashlib import os import sys import time import uuid +import requests + # Generated by Selenium IDE from selenium import webdriver from selenium.common.exceptions import NoSuchElementException @@ -162,6 +165,44 @@ def login_to_galaxy_homepage(self): ) ) + @clock_action("dummy_file_upload") + def upload_dummy_file(self): + upload_activity = self.driver.find_element(By.ID, "activity-upload") + upload_activity.click() + # paste/fetch data + paste_button = self.driver.find_element(By.ID, "btn-new") + paste_button.click() + # paste/fetch data + upload_row = self.driver.find_element(By.XPATH, "//div[@id='upload-row-0']//textarea") + upload_row.send_keys("https://zenodo.org/records/13711466/files/(Galaxy%20History)%20Selenium_test_1_Input_data.rocrate.zip?download=1") + # start + start_button = self.driver.find_element(By.ID, "btn-start") + start_button.click() + # close + close_button = self.driver.find_element(By.ID, "btn-close") + close_button.click() + # wait for history item to appear + self.wait.until( + expected_conditions.presence_of_element_located( + ( + By.XPATH, + "//div[@data-index='0']//div[@data-state='running' and contains(., 'Selenium_test_1_Input_data')]", + ) + ) + ) + + @clock_action("dummy_file_download") + def download_dummy_file(self): + open_download_link = self.driver.find_element(By.XPATH, "//div[@data-index='0']//div[@data-state='running' and contains(., 'Selenium_test_1_Input_data')]") + open_download_link.click() + with SeleniumCustomWait(self.driver, 1200): + download_link = self.driver.find_element(By.XPATH, "//div[@data-index='0']//div[@data-state='ok' and contains(., 'Selenium_test_1_Input_data')]//a[@title='Download']") + r = requests.get(download_link.get_attribute("href"), stream=True) + sig = hashlib.md5() + for line in r.iter_lines(): + sig.update(line) + assert sig.hexdigest() == "a62aaaefa04bdc7acd3b29a127bba3e6" + @clock_action("tool_search_load") def search_for_tool(self): # Select tool search box @@ -402,6 +443,8 @@ def run_test_sequence(self): self.load_workflow_list() self.load_workflow_run_form() self.run_workflow() + self.upload_dummy_file() + self.download_dummy_file() def measure_timings(self): self.timings = {} From 9c0b679a3efb8903cf50c8a3ca9bbdcb81812cdc Mon Sep 17 00:00:00 2001 From: nuwang <2070605+nuwang@users.noreply.github.com> Date: Tue, 24 Sep 2024 19:44:40 +0530 Subject: [PATCH 7/8] Fix urls --- page_perf_timer.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/page_perf_timer.py b/page_perf_timer.py index d2d4ea0..4d48bb1 100644 --- a/page_perf_timer.py +++ b/page_perf_timer.py @@ -131,7 +131,7 @@ def wait_for_history_panel_to_load(self): @clock_action("login_page_load") def load_galaxy_login(self): # Open Galaxy window - self.driver.get(f"{self.server}/login") + self.driver.get(f"{self.server}login") # Wait for username entry to appear self.wait.until(self.is_able_to_login) @@ -238,7 +238,7 @@ def load_tool_form(self): @clock_action("published_histories_page_load") def load_published_histories(self): # Request history page - self.driver.get(f"{self.server}/histories/list_published") + self.driver.get(f"{self.server}histories/list_published") # Wait for history page to load self.wait.until( @@ -310,7 +310,7 @@ def import_published_history(self): ) # Request history page - self.driver.get(f"{self.server}/histories/list") + self.driver.get(f"{self.server}histories/list") # Wait for history panel to load with new history self.wait.until( @@ -325,7 +325,7 @@ def import_published_history(self): @clock_action("workflow_list_page_load") def load_workflow_list(self): # Request workflows list page - self.driver.get(f"{self.server}/workflows/list_shared_with_me") + self.driver.get(f"{self.server}workflows/list_shared_with_me") # Wait for workflow page to load and import button to appear self.wait.until( expected_conditions.presence_of_element_located( From 669998aaae7622734c6dc36d506fdc84378c64cd Mon Sep 17 00:00:00 2001 From: nuwang <2070605+nuwang@users.noreply.github.com> Date: Wed, 25 Sep 2024 10:13:01 +0530 Subject: [PATCH 8/8] More tweaks to waiting --- Dockerfile | 2 +- page_perf_timer.py | 23 +++++------------------ 2 files changed, 6 insertions(+), 19 deletions(-) diff --git a/Dockerfile b/Dockerfile index 404d17c..996adfc 100644 --- a/Dockerfile +++ b/Dockerfile @@ -11,7 +11,7 @@ RUN apt-get update && apt-get install -y \ xvfb \ && rm -rf /var/lib/apt/lists/* -RUN pip3 install --no-cache-dir selenium +RUN pip3 install --no-cache-dir selenium requests FROM selenium diff --git a/page_perf_timer.py b/page_perf_timer.py index 4d48bb1..262e0c3 100644 --- a/page_perf_timer.py +++ b/page_perf_timer.py @@ -325,7 +325,7 @@ def import_published_history(self): @clock_action("workflow_list_page_load") def load_workflow_list(self): # Request workflows list page - self.driver.get(f"{self.server}workflows/list_shared_with_me") + self.driver.get(f"{self.server}workflows/list_published") # Wait for workflow page to load and import button to appear self.wait.until( expected_conditions.presence_of_element_located( @@ -413,25 +413,12 @@ def run_workflow(self): self.driver.find_element(By.ID, "run-workflow").click() # wait for the running message to appear - self.wait.until( - expected_conditions.presence_of_element_located( - ( - By.XPATH, - f"//div[@id='center']//div[@role='tabpanel']//div[@role='alert']//span[@data-description='loading message' and contains(., 'Waiting to complete invocation')]", - ) - ) - ) + loading_xpath = "//div[@id='center']//div[@role='tabpanel']//div[@role='alert']//span[@data-description='loading message' and contains(., 'Waiting to complete invocation')]" + self.wait.until(expected_conditions.presence_of_element_located((By.XPATH, loading_xpath))) # Wait for running message to disappear - with SeleniumCustomWait(self.driver, workflow_wait): - self.wait.until( - expected_conditions.invisibility_of_element_located( - ( - By.XPATH, - f"//div[@id='center']//div[@role='tabpanel']//div[@role='alert']//span[@data-description='loading message' and contains(., 'Waiting to complete invocation')]", - ) - ) - ) + custom_wait = WebDriverWait(self.driver, workflow_wait) + custom_wait.until(expected_conditions.invisibility_of_element_located((By.XPATH, loading_xpath))) def run_test_sequence(self): self.load_galaxy_login()