Warning
This document is for an old release 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):
@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):
@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}]"