Skip to content

Commit

Permalink
implemented Selector Prefix and small Bugfixes (#2332)
Browse files Browse the repository at this point in the history
implemented Selector Prefix and small Bugfixes
  • Loading branch information
Snooz82 authored Sep 25, 2022
1 parent c49c7f2 commit f3eadb1
Show file tree
Hide file tree
Showing 71 changed files with 467 additions and 202 deletions.
39 changes: 38 additions & 1 deletion Browser/base/librarycomponent.py
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@
from copy import copy, deepcopy
from datetime import timedelta
from time import sleep
from typing import TYPE_CHECKING, Any, Set, Union
from typing import TYPE_CHECKING, Any, Optional, Set, Union

from robot.utils import timestr_to_secs # type: ignore

Expand Down Expand Up @@ -65,6 +65,33 @@ def retry_assertions_for_stack(self) -> SettingsStack:
def retry_assertions_for_stack(self, stack: SettingsStack):
self.library.retry_assertions_for_stack = stack

@property
def selector_prefix(self) -> str:
return self.library.selector_prefix_stack.get()

@property
def selector_prefix_stack(self) -> SettingsStack:
return self.library.selector_prefix_stack

@selector_prefix_stack.setter
def selector_prefix_stack(self, stack: SettingsStack):
self.library.selector_prefix_stack = stack

def resolve_selector(self, selector: Optional[str]) -> str:
if not selector:
return ""
if selector.startswith("!prefix "):
logger.trace("Using selector prefix, but muting Prefix.")
return selector[8:]
if selector.startswith("element=") or not self.selector_prefix:
return selector
if selector.startswith(f"{self.selector_prefix} "):
return selector
logger.debug(
f"Using selector prefix. Selector: '{self.selector_prefix} {selector}'"
)
return f"{self.selector_prefix} {selector}"

@property
def unresolved_promises(self):
return self.library._unresolved_promises
Expand Down Expand Up @@ -177,6 +204,7 @@ def get_presenter_mode(self) -> HighLightElement:
return {"duration": duration, "width": width, "style": style, "color": color}

def presenter_mode(self, selector, strict):
selector = self.resolve_selector(selector)
if self.library.presenter_mode:
mode = self.get_presenter_mode
try:
Expand All @@ -194,3 +222,12 @@ def presenter_mode(self, selector, strict):
else:
sleep(mode["duration"].seconds)
return selector

def exec_scroll_function(self, function: str, selector: Optional[str] = None):
if selector:
element_selector = "(element) => element"
else:
element_selector = "document.scrollingElement"
return self.library.execute_javascript(
f"{element_selector}.{function}", selector
)
17 changes: 10 additions & 7 deletions Browser/browser.py
Original file line number Diff line number Diff line change
Expand Up @@ -742,6 +742,7 @@ def __init__(
playwright_process_port: Optional[int] = None,
retry_assertions_for: timedelta = timedelta(seconds=1),
run_on_failure: str = "Take Screenshot fail-screenshot-{index}",
selector_prefix: Optional[str] = None,
show_keyword_call_banner: Optional[bool] = None,
strict: bool = True,
timeout: timedelta = timedelta(seconds=10),
Expand Down Expand Up @@ -826,7 +827,7 @@ def __init__(
]
self.strict_mode_stack = SettingsStack(params["strict"], self)
self.show_keyword_call_banner = params["show_keyword_call_banner"]

self.selector_prefix_stack = SettingsStack(selector_prefix, self)
self._execution_stack: List[dict] = []
self._running_on_failure_keyword = False
self.pause_on_failure: Set[str] = set()
Expand Down Expand Up @@ -979,7 +980,7 @@ def state_file(self):
return self.browser_output / "state"

def _start_suite(self, name, attrs):
self._start_stack(attrs, Scope.Suite)
self._add_to_scope_stack(attrs, Scope.Suite)
if not self._suite_cleanup_done:
self._suite_cleanup_done = True
for path in [
Expand All @@ -998,7 +999,7 @@ def _start_suite(self, name, attrs):
logger.debug(f"Browser._start_suite connection problem: {e}")

def _start_test(self, name, attrs):
self._start_stack(attrs, Scope.Test)
self._add_to_scope_stack(attrs, Scope.Test)
self.is_test_case_running = True
if self._auto_closing_level == AutoClosingLevel.TEST:
try:
Expand Down Expand Up @@ -1049,7 +1050,7 @@ def _end_keyword(self, name, attrs):
self._set_logging(True)

def _end_test(self, name, attrs):
self._end_scope(attrs)
self._remove_from_scope_stack(attrs)
self.is_test_case_running = False
if len(self._unresolved_promises) > 0:
logger.warn(f"Waiting unresolved promises at the end of test '{name}'")
Expand All @@ -1070,7 +1071,7 @@ def _end_test(self, name, attrs):
logger.debug(f"Browser._end_test connection problem: {e}")

def _end_suite(self, name, attrs):
self._end_scope(attrs)
self._remove_from_scope_stack(attrs)
if self._auto_closing_level != AutoClosingLevel.MANUAL:
if len(self._execution_stack) == 0:
logger.debug("Browser._end_suite empty execution stack")
Expand All @@ -1083,15 +1084,17 @@ def _end_suite(self, name, attrs):
except ConnectionError as e:
logger.debug(f"Browser._end_suite connection problem: {e}")

def _start_stack(self, attrs: Dict[str, Any], scope: Scope):
def _add_to_scope_stack(self, attrs: Dict[str, Any], scope: Scope):
self.timeout_stack.start(attrs["id"], scope)
self.strict_mode_stack.start(attrs["id"], scope)
self.retry_assertions_for_stack.start(attrs["id"], scope)
self.selector_prefix_stack.start(attrs["id"], scope)

def _end_scope(self, attrs: Dict[str, Any]):
def _remove_from_scope_stack(self, attrs: Dict[str, Any]):
self.timeout_stack.end(attrs["id"])
self.strict_mode_stack.end(attrs["id"])
self.retry_assertions_for_stack.end(attrs["id"])
self.selector_prefix_stack.end(attrs["id"])

def _prune_execution_stack(self, catalog_before: dict) -> None:
catalog_after = self.get_browser_catalog()
Expand Down
32 changes: 29 additions & 3 deletions Browser/keywords/browser_control.py
Original file line number Diff line number Diff line change
Expand Up @@ -143,9 +143,9 @@ def take_screenshot(
if mask:
mask_selectors: Optional[List[str]]
if isinstance(mask, str):
mask_selectors = [mask]
mask_selectors = [self.resolve_selector(mask)]
elif isinstance(mask, Iterable):
mask_selectors = [str(s) for s in mask]
mask_selectors = [self.resolve_selector(str(s)) for s in mask]
else:
raise ValueError(
f"'mask' argument is neither string nor list of string. It is {type(mask)}"
Expand All @@ -161,7 +161,7 @@ def take_screenshot(
options["clip"] = crop
response = stub.TakeScreenshot(
Request().ScreenshotOptions(
selector=selector or "",
selector=self.resolve_selector(selector) or "",
mask=json.dumps(mask_selectors),
options=json.dumps(options),
strict=self.strict_mode,
Expand Down Expand Up @@ -265,6 +265,32 @@ def set_retry_assertions_for(
self.retry_assertions_for_stack.set(self.convert_timeout(timeout), scope)
return old_retry_assertions_for

@keyword(tags=("Setter", "Config"))
def set_selector_prefix(
self, prefix: Optional[str], scope: Scope = Scope.Suite
) -> str:
"""Sets the prefix for all selectors in the given scope.
| =Arguments= | =Description= |
| ``prefix`` | Prefix for all selectors. Prefix and selector will be separated by a single space. |
| ``scope`` | Scope defines the live time of that setting. Available values are ``Global``, ``Suite`` or ``Test``/``Task``. See `Scope` for more details. |
Returns the previous value of the prefix.
Example:
| ${old} = `Set Selector Prefix` iframe#embedded_page >>>
| `Click` button#login_btn # Clicks on button inside iframe with the selector ``iframe#embedded_page >>> button#login_btn``
| `Set Selector Prefix` ${old}
Example will click on button with id ``login_btn`` inside iframe with id ``embedded_page``.
The resulting selector will be ``iframe#embedded_page >>> button#login_btn``.
[https://forum.robotframework.org/t//4330|Comment >>]
"""
old_prefix = self.selector_prefix
self.selector_prefix_stack.set(prefix or "", scope)
return old_prefix

@keyword(tags=("Setter", "Config"))
def show_keyword_banner(
self, show: bool = True, style: str = ""
Expand Down
4 changes: 3 additions & 1 deletion Browser/keywords/evaluation.py
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,7 @@ def execute_javascript(self, function: str, selector: str = "") -> Any:
[https://forum.robotframework.org/t//4252|Comment >>]
"""
selector = self.resolve_selector(selector)
with self.playwright.grpc_channel() as stub:
response = stub.ExecuteJavascript(
Request().JavascriptCode(
Expand Down Expand Up @@ -98,6 +99,7 @@ def evaluate_javascript(
[https://forum.robotframework.org/t//4251|Comment >>]
"""
selector = self.resolve_selector(selector)
with self.playwright.grpc_channel() as stub:
response = stub.EvaluateJavascript(
Request().EvaluateAll(
Expand Down Expand Up @@ -143,7 +145,7 @@ def highlight_elements(
with self.playwright.grpc_channel() as stub:
response = stub.HighlightElements(
Request().ElementSelectorWithDuration(
selector=selector,
selector=self.resolve_selector(selector),
duration=int(self.convert_timeout(duration)),
width=width,
style=style,
Expand Down
29 changes: 20 additions & 9 deletions Browser/keywords/getters.py
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@
from ..assertion_engine import with_assertion_polling
from ..base import LibraryComponent
from ..generated.playwright_pb2 import Request
from ..utils import exec_scroll_function, keyword, logger
from ..utils import keyword, logger
from ..utils.data_types import (
AreaFields,
BoundingBoxFields,
Expand Down Expand Up @@ -438,7 +438,7 @@ def get_classes(
list(class_dict.values()),
assertion_operator,
expected,
f"Classes of {selector}",
f"Classes of {self.resolve_selector(selector)}",
message,
)

Expand Down Expand Up @@ -543,6 +543,7 @@ def get_selected_options(
[https://forum.robotframework.org/t//4280|Comment >>]
"""
selector = self.resolve_selector(selector)
with self.playwright.grpc_channel() as stub:
response = stub.GetSelectContent(
Request().ElementSelector(selector=selector, strict=self.strict_mode)
Expand Down Expand Up @@ -596,6 +597,7 @@ def get_checkbox_state(
[https://forum.robotframework.org/t//4261|Comment >>]
"""
selector = self.resolve_selector(selector)
with self.playwright.grpc_channel() as stub:
response = stub.GetBoolProperty(
Request().ElementProperty(
Expand Down Expand Up @@ -641,6 +643,7 @@ def get_element_count(
[https://forum.robotframework.org/t//4270|Comment >>]
"""
selector = self.resolve_selector(selector)
with self.playwright.grpc_channel() as stub:
response = stub.GetElementCount(
Request().ElementSelector(selector=selector, strict=False)
Expand Down Expand Up @@ -748,7 +751,8 @@ def get_table_cell_element(self, table: str, column: str, row: str) -> str:
node_name = str(self.library.execute_javascript("e => e.nodeName", table))
if node_name != "TABLE":
raise ValueError(
f"Selector {table} must select a <table> element but selects <{node_name.lower()}>."
f"Selector {self.resolve_selector(table)} must select a "
f"<table> element but selects <{node_name.lower()}>."
)
column_idx = (
column
Expand Down Expand Up @@ -793,6 +797,7 @@ def get_table_cell_index(
[https://forum.robotframework.org/t//4283|Comment >>]
"""
selector = self.resolve_selector(selector)
with self.playwright.grpc_channel() as stub:
response = stub.GetTableCellIndex(
Request().ElementSelector(selector=selector, strict=self.strict_mode)
Expand Down Expand Up @@ -840,6 +845,7 @@ def get_table_row_index(
[https://forum.robotframework.org/t//4284|Comment >>]
"""
selector = self.resolve_selector(selector)
with self.playwright.grpc_channel() as stub:
response = stub.GetTableRowIndex(
Request().ElementSelector(selector=selector, strict=self.strict_mode)
Expand Down Expand Up @@ -876,6 +882,7 @@ def get_element(self, selector: str) -> str:
[https://forum.robotframework.org/t/comments-for-get-element/4269|Comment >>]
"""
selector = self.resolve_selector(selector)
with self.playwright.grpc_channel() as stub:
response = stub.GetElement(
Request().ElementSelector(selector=selector, strict=self.strict_mode)
Expand All @@ -899,6 +906,7 @@ def get_elements(self, selector: str) -> List[str]:
[https://forum.robotframework.org/t//4273|Comment >>]
"""
selector = self.resolve_selector(selector)
try:
with self.playwright.grpc_channel(original_error=True) as stub:
response = stub.GetElements(
Expand Down Expand Up @@ -1015,6 +1023,7 @@ def get_boundingbox(
[https://forum.robotframework.org/t//4258|Comment >>]
"""
selector = self.presenter_mode(selector, self.strict_mode)
with self.playwright.grpc_channel() as stub:
response = stub.GetBoundingBox(
Request.ElementSelector(selector=selector, strict=self.strict_mode)
Expand Down Expand Up @@ -1074,8 +1083,8 @@ def get_scroll_size(
[https://forum.robotframework.org/t//4278|Comment >>]
"""
scroll_size = DotDict()
scroll_size["width"] = exec_scroll_function(self, "scrollWidth", selector)
scroll_size["height"] = exec_scroll_function(self, "scrollHeight", selector)
scroll_size["width"] = self.exec_scroll_function("scrollWidth", selector)
scroll_size["height"] = self.exec_scroll_function("scrollHeight", selector)
if self.keyword_formatters.get(self.get_scroll_size):
logger.warn("Formatter is not supported by Get Scroll Size keyword.")
if key == SizeFields.ALL:
Expand Down Expand Up @@ -1130,8 +1139,8 @@ def get_scroll_position(
[https://forum.robotframework.org/t//4277|Comment >>]
"""
scroll_position = DotDict()
scroll_position["top"] = exec_scroll_function(self, "scrollTop", selector)
scroll_position["left"] = exec_scroll_function(self, "scrollLeft", selector)
scroll_position["top"] = self.exec_scroll_function("scrollTop", selector)
scroll_position["left"] = self.exec_scroll_function("scrollLeft", selector)
client_size = self.get_client_size(selector)
scroll_position["bottom"] = scroll_position["top"] + client_size["height"]
scroll_position["right"] = scroll_position["left"] + client_size["width"]
Expand Down Expand Up @@ -1186,8 +1195,8 @@ def get_client_size(
[https://forum.robotframework.org/t//4263|Comment >>]
"""
client_size = DotDict()
client_size["width"] = exec_scroll_function(self, "clientWidth", selector)
client_size["height"] = exec_scroll_function(self, "clientHeight", selector)
client_size["width"] = self.exec_scroll_function("clientWidth", selector)
client_size["height"] = self.exec_scroll_function("clientHeight", selector)
if self.keyword_formatters.get(self.get_client_size):
logger.warn("Formatter is not supported by Get Clinet Size keyword.")
if key == SizeFields.ALL:
Expand Down Expand Up @@ -1272,6 +1281,7 @@ def get_element_state(

if self.keyword_formatters.get(self.get_element_state):
logger.warn("Formatter is not supported by Get Element State keyword.")
selector = self.resolve_selector(selector)
return bool_verify_assertion(
result,
assertion_operator,
Expand Down Expand Up @@ -1341,6 +1351,7 @@ def get_element_states(
[https://forum.robotframework.org/t/comments-for-get-element-states/4272|Comment >>]
"""
selector = self.resolve_selector(selector)

def convert_str(f):
return f.name if isinstance(f, ElementState) else f
Expand Down
Loading

0 comments on commit f3eadb1

Please sign in to comment.