Source code for galaxy.selenium.navigates_galaxy

"""A mixin that extends a HasDriver class with Galaxy-specific utilities.

Implementer must provide a self.build_url method to target Galaxy.
"""

import collections
import contextlib
import random
import string
import time
from abc import abstractmethod
from functools import (
    partial,
    wraps,
)
from typing import (
    Any,
    cast,
    Dict,
    Optional,
    Union,
)

import requests
import yaml
from selenium.webdriver.common.keys import Keys
from selenium.webdriver.remote.webdriver import WebDriver

from galaxy.util import DEFAULT_SOCKET_TIMEOUT
from . import sizzle
from .components import (
    Component,
    HasText,
)
from .data import load_root_component
from .has_driver import (
    exception_indicates_click_intercepted,
    exception_indicates_not_clickable,
    exception_indicates_stale_element,
    HasDriver,
    SeleniumTimeoutException,
)
from .smart_components import SmartComponent

# Test case data
DEFAULT_PASSWORD = "123456"

RETRY_DURING_TRANSITIONS_SLEEP_DEFAULT = 0.1
RETRY_DURING_TRANSITIONS_ATTEMPTS_DEFAULT = 10

GALAXY_MAIN_FRAME_ID = "galaxy_main"

WaitType = collections.namedtuple("WaitType", ["name", "default_length"])


# Default wait times should make sense for a development server under low
# load. Wait times for production servers can be scaled up with a multiplier.
[docs]class WAIT_TYPES: # Rendering a form and registering callbacks, etc... UX_RENDER = WaitType("ux_render", 1) # Fade in, fade out, etc... UX_TRANSITION = WaitType("ux_transition", 5) # Toastr popup and dismissal, etc... UX_POPUP = WaitType("ux_popup", 15) # Creating a new history and loading it into the panel. DATABASE_OPERATION = WaitType("database_operation", 10) # Wait time for jobs to complete in default environment. JOB_COMPLETION = WaitType("job_completion", 30) # Wait time for a GIE to spawn. GIE_SPAWN = WaitType("gie_spawn", 30) # Wait time for toolshed search SHED_SEARCH = WaitType("shed_search", 30) # Wait time for repository installation REPO_INSTALL = WaitType("repo_install", 60) # Beta history Polling Duration HISTORY_POLL = WaitType("history_poll", 3)
# Choose a moderate wait type for operations that don't specify a type. DEFAULT_WAIT_TYPE = WAIT_TYPES.DATABASE_OPERATION
[docs]class NullTourCallback:
[docs] def handle_step(self, step, step_index): pass
[docs]def exception_seems_to_indicate_transition(e): """True if exception seems to indicate the page state is transitioning. Galaxy features many different transition effects that change the page state over time. These transitions make it slightly more difficult to test Galaxy because atomic input actions take an indeterminate amount of time to be reflected on the screen. This method takes a Selenium assertion and tries to infer if such a transition could be the root cause of the exception. The methods that follow use it to allow retrying actions during transitions. Currently the two kinds of exceptions that we say may indicate a transition are StaleElement exceptions (a DOM element grabbed at one step is no longer available) and "not clickable" exceptions (so perhaps a popup modal is blocking a click). """ return ( exception_indicates_stale_element(e) or exception_indicates_not_clickable(e) or exception_indicates_click_intercepted(e) )
[docs]def retry_call_during_transitions( f, attempts=RETRY_DURING_TRANSITIONS_ATTEMPTS_DEFAULT, sleep=RETRY_DURING_TRANSITIONS_SLEEP_DEFAULT, exception_check=exception_seems_to_indicate_transition, ): previous_attempts = 0 while True: try: return f() except Exception as e: if previous_attempts > attempts: raise if not exception_check(e): raise time.sleep(sleep) previous_attempts += 1
[docs]def retry_during_transitions( f, attempts=RETRY_DURING_TRANSITIONS_ATTEMPTS_DEFAULT, sleep=RETRY_DURING_TRANSITIONS_SLEEP_DEFAULT, exception_check=exception_seems_to_indicate_transition, ): @wraps(f) def _retry(*args, **kwds): return retry_call_during_transitions( partial(f, *args, **kwds), attempts=attempts, sleep=sleep, exception_check=exception_check ) return _retry
[docs]def edit_details(f, scope=".history-index"): """Open the editor, run the edits, hit the save button""" @wraps(f) def func_wrapper(self, *args, **kwds): # open editor if self.is_beta_history(): self.open_history_editor(scope=scope) # run edits result = f(self, *args, **kwds) # save edits if self.is_beta_history(): self.history_click_editor_save() return result return func_wrapper
[docs]class NotLoggedInException(SeleniumTimeoutException):
[docs] def __init__(self, timeout_exception, user_info, dom_message): template = "Waiting for UI to reflect user logged in but it did not occur. API indicates no user is currently logged in. %s API response was [%s]. %s" msg = template % (dom_message, user_info, timeout_exception.msg) super().__init__(msg=msg, screen=timeout_exception.screen, stacktrace=timeout_exception.stacktrace)