Warning

This document is for an in-development version of Galaxy. You can alternatively view this page in the latest release if it exists or view the top of the latest release's documentation.

Source code for galaxy.selenium.components

import re
import string
from abc import (
    ABCMeta,
    abstractproperty,
)
from typing import (
    Dict,
    List,
    Optional,
    Tuple,
    Union,
)

from galaxy.util.bunch import Bunch


[docs]class By: """ Set of supported locator strategies. """ ID = "id" XPATH = "xpath" LINK_TEXT = "link text" PARTIAL_LINK_TEXT = "partial link text" NAME = "name" TAG_NAME = "tag name" CLASS_NAME = "class name" CSS_SELECTOR = "css selector"
LocatorT = Tuple[By, str]
[docs]class Target(metaclass=ABCMeta): @abstractproperty def description(self) -> str: """Return a plain-text description of the browser target for logging/messages.""" @abstractproperty def element_locator(self) -> LocatorT: """Return a (by, selector) Selenium elment locator tuple for this selector."""
[docs]class SelectorTemplate(Target):
[docs] def __init__( self, selector: Union[str, List[str]], selector_type: str, children=None, kwds=None, with_classes=None, with_data=None, ): if selector_type == "data-description": selector_type = "css" selector = f'[data-description="{selector}"]' self._selector = selector self.selector_type = selector_type self._children = children or {} self.__kwds = kwds or {} self.with_classes = with_classes or [] self._with_data = with_data or {}
[docs] @staticmethod def from_dict(raw_value, children=None): children = children or {} if isinstance(raw_value, dict): return SelectorTemplate(raw_value["selector"], raw_value.get("type", "css"), children=children) else: return SelectorTemplate(raw_value, "css", children=children)
[docs] def with_class(self, class_): assert self.selector_type == "css" return SelectorTemplate( self._selector, self.selector_type, kwds=self.__kwds, with_classes=self.with_classes + [class_], with_data=self._with_data.copy(), children=self._children, )
[docs] def with_data(self, key, value): assert self.selector_type == "css" with_data = self._with_data.copy() with_data[key] = value return SelectorTemplate( self._selector, self.selector_type, kwds=self.__kwds, with_classes=self.with_classes, with_data=with_data, children=self._children, )
[docs] def descendant(self, has_selector): assert self.selector_type == "css" if hasattr(has_selector, "selector"): selector = has_selector.selector else: selector = has_selector return SelectorTemplate( f"{self.selector} {selector}", self.selector_type, kwds=self.__kwds, children=self._children )
def __call__(self, **kwds): new_kwds = self.__kwds.copy() new_kwds.update(**kwds) return SelectorTemplate( self._selector, self.selector_type, kwds=new_kwds, with_classes=self.with_classes, children=self._children ) @property def description(self): if self.selector_type == "css": template = "CSS selector [%s]" elif self.selector_type == "xpath": template = "XPATH selector [%s]" elif self.selector_type == "id": template = "DOM element with id [%s]" return template % self.selector @property def selector(self): raw_selector = self._selector if self.__kwds is not None: if isinstance(raw_selector, list): selector = None last_error = None for raw_selector_str in raw_selector: try: selector = string.Template(raw_selector_str).substitute(self.__kwds) break except KeyError as e: last_error = e if selector is None: assert last_error raise last_error else: selector = string.Template(raw_selector).substitute(self.__kwds) assert selector selector = selector + "".join(f".{c}" for c in self.with_classes) if self._with_data: for key, value in self._with_data.items(): selector = selector + f'[data-{key}="{value}"]' return selector @property def element_locator(self): if self.selector_type == "css": by = By.CSS_SELECTOR elif self.selector_type == "xpath": by = By.XPATH elif self.selector_type == "id": by = By.ID else: raise Exception(f"Unknown selector type {self.selector_type}") return (by, self.selector) @property def as_css_class(self): assert self.selector_type == "css" selector = self._selector assert isinstance(selector, str) assert re.compile(r"\.\w+").match(selector) return selector[1:]
[docs] def resolve_element_locator(self, path: Optional[str] = None) -> LocatorT: if path: return self[path].element_locator else: return self.element_locator
def __getattr__(self, name): if name in self._children: return self._children[name](**{"_": self.selector}) else: raise AttributeError(f"Could not find child [{name}] in {self._children}") __getitem__ = __getattr__
[docs]class Label(Target):
[docs] def __init__(self, text): self.text = text
@property def description(self): return f"Link text [{self.text}]" @property def element_locator(self): return (By.LINK_TEXT, self.text)
[docs]class Text(Target):
[docs] def __init__(self, text): self.text = text
@property def description(self): return f"Text containing [{self.text}]" @property def element_locator(self): return (By.PARTIAL_LINK_TEXT, self.text)
HasText = Union[Label, Text] CALL_ARGUMENTS_RE = re.compile(r"(?P<SUBCOMPONENT>[^.(]*)(\((?P<ARGS>[^)]+)\))?(?:\.(?P<REST>.*))?")
[docs]class Component:
[docs] def __init__(self, name, sub_components, selectors, labels, text): self._name = name self._sub_components = sub_components self._selectors = selectors self._labels = labels self._text = text self.selectors = Bunch(**self._selectors) self.labels = Bunch(**self._labels) self.text = Bunch(**self._text)
@property def selector(self): if "_" in self._selectors: return self._selectors["_"] else: raise Exception(f"No _ selector for [{self}]")
[docs] def resolve_element_locator(self, path: Optional[str] = None) -> LocatorT: if not path: return self._selectors["_"].resolve_element_locator() def arguments() -> Tuple[str, Optional[Dict[str, str]], Optional[str]]: assert path match = CALL_ARGUMENTS_RE.match(path) if match: component_name = match.group("SUBCOMPONENT") expression = match.group("ARGS") rest = match.group("REST") if expression: parts = expression.split(",") parameters = {} for part in parts: key, val = part.split("=", 1) parameters[key.strip()] = val.strip() return component_name, parameters, rest return component_name, None, rest elif "." in path: component_name, rest = path.split(".", 1) else: component_name = path rest = None return component_name, None, rest component_name, parameters, rest = arguments() if not rest: if parameters: return getattr(self, component_name)(**parameters).resolve_element_locator() else: return getattr(self, component_name).resolve_element_locator() else: if parameters: return getattr(self, component_name)(**parameters).resolve_element_locator(rest) else: return getattr(self, component_name).resolve_element_locator(rest)
[docs] @staticmethod def from_dict(name, raw_value): selectors = {} labels = {} text = {} sub_components = {} for key, value in raw_value.items(): if key == "selectors": base_selector = None if "_" in value: base_selector = value["_"] del value["_"] for selector_key, selector_value in value.items(): selectors[selector_key] = SelectorTemplate.from_dict(selector_value) if base_selector: selectors["_"] = SelectorTemplate.from_dict(base_selector, children=selectors) elif key == "labels": for label_key, label_value in value.items(): labels[label_key] = Label(label_value) elif key == "text": for text_key, text_value in value.items(): text[text_key] = Text(text_value) else: component = Component.from_dict(key, value) sub_components[key] = component return Component(name, sub_components, selectors, labels, text)
def __getattr__(self, attr): if attr in self._sub_components: return self._sub_components[attr] elif attr in self._selectors: return self._selectors[attr] elif attr in self._labels: return self._labels[attr] elif attr in self._text: return self._text[attr] else: raise AttributeError(f"Failed to find referenced sub-component/selector/label/text [{attr}]") def __call__(self, **kwds): return self._selectors["_"](**kwds) __getitem__ = __getattr__ def __str__(self): return f"Component[{self._name}]"