Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

implemented Selector Prefix and small Bugfixes #2332

Merged
merged 2 commits into from
Sep 25, 2022
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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
30 changes: 27 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,30 @@ 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