diff --git a/Browser/browser.py b/Browser/browser.py old mode 100644 new mode 100755 index c89681cb8..1c13705d3 --- a/Browser/browser.py +++ b/Browser/browser.py @@ -38,50 +38,278 @@ class Browser(DynamicCore): [https://www.mozilla.org/en-US/firefox/new/|Firefox] and [https://webkit.org/|WebKit] with a single library. + == Table of contents == %TOC% + = Browser, Context and Page = + + Browser library works with three different layers that build on each other: + *Browser*, *Context* and *Page*. + + + == Browsers == + + A *browser* can be started with one of the three + different engines Chromium, Firefox or Webkit. + + === Supported Browsers === + | Browser | Browser with this engine | + | ``chromium`` | Google Chrome, Microsoft Edge (since 2020), Opera | + | ``firefox`` | Mozilla Firefox | + | ``webkit`` | Apple Safari, Mail, AppStore on MacOS and iOS | + + Since [https://github.com/microsoft/playwright|Playwright] comes with a pack of builtin + binaries for all browsers, no additional drivers e.g. geckodriver are needed. + + All these browsers that cover more than 85% of the world wide used browsers, + can be tested on Windows, Linux and MacOS. + Theres is not need for dedicated machines anymore. + + A browser process is started ``headless`` (without a GUI) by default. + Run `New Browser` with specified arguments if a browser with a GUI is requested + or if a proxy has to be configured. + A browser process can contain several contexts. + + + == Contexts == + + A *context* corresponds to several independent incognito browsers in Chrome + that do not share cookies, sessions or profile settings. + Compared to Selenium, these do *not* require their own browser process! + Therefore, to get a clean environment the tests shall just close the current + context and open a new context. + Due to this new independent browser sessions can be opened with + Robot Framework Browser about 10 times faster than with Selenium by + just opening a `New Context` within the opened browser. + + The context layer is useful e.g. for testing different users sessions on the + same webpage without opening a whole new browser context. + Contexts can also have detailed configurations, such as geo-location, language settings, + the viewport size or color scheme. + Contexts do also support http credentials to be set, so that basic authentication + can also be tested. To be able to download files within the test, + the ``acceptDownloads`` argument must be set to ``True`` in `New Context` keyword. + A context can contain different pages. + + + == Pages == + + A *page* does contain the content of the loaded web site. + Pages and browser tabs are the same. + + Typical usage could be: + | *** Test Cases *** + | Starting a browser with a page + | New Browser chromium headless=false + | New Context viewport={'width': 1920, 'height': 1080} + | New Page https://marketsquare.github.io/robotframework-browser/Browser.html + | Get Title == Browser + + There are shortcuts to open new pages together with new browsers but the offer less control. + + The `Open Browser` keyword opens a new browser, a new context and a new page. + This keyword is usefull for quick experiments or debugging sessions. + + When a `New Page` is called without an open browser, `New Browser` + and `New Context` are executed with default values first. + + If there is no browser opened in Suite Setup and `New Page` is executed in + Test Setup, the corresponding pages and context is closed automatically at the end of + the test. The browser process remains open and will be closed at the end of + execution. + + Each Browser, Context and Page has a unique ID with which they can be adressed. + A full catalog of what is open can be recieved by `Get Browser Catalog` as dictionary. + + + = Finding elements = All keywords in the library that need to interact with an element on a web page take an argument typically named ``selector`` that specifies how to find the element. - == Selector syntax == + Selector strategies that are supported by default are listed in the table + below. - Browser library supports the same selector strategies as the underlying - Playwright node module: xpath, css, id and text. The strategy can either - be explicitly specified with a prefix or the strategy can be implicit. + | = Strategy = | = Match based on = | = Example = | + | ``css`` | CSS selector. | ``css=.class > #login_btn`` | + | ``xpath`` | XPath expression. | ``xpath=//input[@id="login_btn"]`` | + | ``text`` | Browser text engine. | ``text=Login`` | + | ``id`` | Element ID Attribute. | ``id=login_btn`` | - === Implicit selector strategy === - The default selector strategy is `css`. If selector does not contain - one of the know selector strategies, `css`, `xpath`, `id` or `text` it is - assumed to contain css selector. Also `selectors` starting with `//` - considered as xpath selectors. + == Explicit Selector Strategy == + + The explicit selector strategy is specified with a prefix using syntax + ``strategy=value``. Spaces around the separator are ignored, so + ``css=foo``, ``css= foo`` and ``css = foo`` are all equivalent. + + + == Implicit Selector Strategy == + + *The default selector strategy is `css`.* + + If selector does not contain one of the know explicit selector strategies, it is + assumed to contain css selector. + + Selectors that are starting with ``//`` or ``..`` are considered as xpath selectors. + + Selectors that are in quotes are considered as text selectors. Examples: - | `Click` | span > button | # Use css selector strategy the element. | - | `Click` | //span/button | # Use xpath selector strategy the element. | + | # CSS selectors are default. + | `Click` span > button.some_class # This is equivalent + | `Click` css=span > button.some_class # to this. + | + | # // or .. leads to xpath selector strategy + | `Click` //span/button[@class="some_class"] + | `Click` xpath=//span/button[@class="some_class"] + | + | # "text" in quotes leads to exact text selector strategy + | `Click` "Login" + | `Click` text="Login" - === Explicit selector strategy === - The explicit selector strategy is specified with a prefix using syntax - ``strategy=value``. Spaces around the separator are ignored, so - ``css=foo``, ``css= foo`` and ``css = foo`` are all equivalent. + == CSS == + As written before, the default selector strategy is `css`. See + [https://developer.mozilla.org/en-US/docs/Web/CSS/CSS_Selectors | css selector] + for more information. - Selector strategies that are supported by default are listed in the table - below. + Any malformed selector not starting with ``//`` or ``..`` nor starting and ending + with a quote is assumed to be a css selector. + + Example: + | `Click` span > button.some_class + + + == XPath == + + XPath engine is equivalent to [https://developer.mozilla.org/en/docs/Web/API/Document/evaluate|Document.evaluate]. + Example: ``xpath=//html/body//span[text()="Hello World"]``. + + Malformed selector starting with ``//`` or ``..`` is assumed to be an xpath selector. + For example, ``//html/body`` is converted to ``xpath=//html/body``. More + examples are displayed in `Examples`. + + Note that xpath does not pierce [https://developer.mozilla.org/en-US/docs/Web/Web_Components/Using_shadow_DOM|shadow_roots]. + + + == Text == + + Text engine finds an element that contains a text node with the passed text. + For example, ``Click text=Login`` clicks on a login button, and + ``Wait For Elements State text="lazy loaded text"`` waits for the "lazy loaded text" + to appear in the page. + + Malformed selector starting and ending with a quote (either ``"`` or ``'``) is assumed + to be a text selector. For example, ``Click "Login"`` is converted to ``Click text="Login"``. + Be aware that these leads to exact matches only! + More examples are displayed in `Examples`. + + + === insensitive match === + + By default, the match is case-insensitive, ignores leading/trailing whitespace and + searches for a substring. This means ``text= Login`` matches + ````. + + === exact match === + + Text body can be escaped with single or double quotes for precise matching, + insisting on exact match, including specified whitespace and case. + This means ``text="Login "`` will only match ```` with exactly + one space after "Login". Quoted text follows the usual escaping rules, e.g. + use ``\\"`` to escape double quote in a double-quoted string: ``text="foo\\"bar"``. + + === RegEx === + + Text body can also be a JavaScript-like regex wrapped in / symbols. + This means ``text=/^hello .*!$/i`` or ``text=/^Hello .*!$/`` will match ``Hello Peter Parker!`` + with any name after ``Hello``, ending with ``!``. + The first one flagged with ``i`` for case-insensitive. + See [https://regex101.com/] for more information about RegEx. + + === Button and Submit Values === - | = Strategy = | = Match based on = | = Example = | - | css | CSS selector. | ``css=div#example`` | - | xpath | XPath expression. | ``xpath=//div[@id="example"]`` | - | text | Browser text engine. | ``text=Login`` | + Input elements of the type button and submit are rendered with their value as text, + and text engine finds them. For example, ``text=Login`` matches + ````. - == Finding elements inside frames == + + + == Cascaded selector syntax == + + Browser library supports the same selector strategies as the underlying + Playwright node module: xpath, css, id and text. The strategy can either + be explicitly specified with a prefix or the strategy can be implicit. + + A major advantage of Browser is, that multiple selector engines can be used + within one selector. It is possible to mix XPath, CSS and Text selectors while + selecting a single element. + + Selectors are strings that consists of one or more clauses separated by + ``>>`` token, e.g. ``clause1 >> clause2 >> clause3``. When multiple clauses + are present, next one is queried relative to the previous one's result. + Browser library supports concatination of different selectors seperated by ``>>``. + + For example: + | `Highlight Elements` "Hello" >> ../.. >> .select_button + | `Highlight Elements` text=Hello >> xpath=../.. >> css=.select_button + + Each clause contains a selector engine name and selector body, e.g. + ``engine=body``. Here ``engine`` is one of the supported engines (e.g. css or + a custom one). Selector ``body`` follows the format of the particular engine, + e.g. for css engine it should be a [https://developer.mozilla.org/en-US/docs/Web/CSS/CSS_Selectors | css selector]. + Body format is assumed to ignore leading and trailing white spaces, + so that extra whitespace can be added for readability. If selector + engine needs to include ``>>`` in the body, it should be escaped + inside a string to not be confused with clause separator, + e.g. ``text="some >> text"``. + + Selector engine name can be prefixed with ``*`` to capture element that + matches the particular clause instead of the last one. For example, + ``css=article >> text=Hello`` captures the element with the text ``Hello``, + and ``*css=article >> text=Hello`` (note the *) captures the article element + that contains some element with the text Hello. + + For convenience, selectors in the wrong format are heuristically converted + to the right format. See `Implicit Selector Strategy` + + == Examples == + | # queries 'div' css selector + | Get Element css=div + | + | # queries '//html/body/div' xpath selector + | Get Element //html/body/div + | + | # queries '"foo"' text selector + | Get Element text=foo + | + | # queries 'span' css selector inside the result of '//html/body/div' xpath selector + | Get Element xpath=//html/body/div >> css=span + | + | # converted to 'css=div' + | Get Element div + | + | # converted to 'xpath=//html/body/div' + | Get Element //html/body/div + | + | # converted to 'text="foo"' + | Get Element "foo" + | + | # queries the div element of every 2nd span element inside an element with the id foo + | Get Element \\#foo >> css=span:nth-child(2n+1) >> div + | Get Element id=foo >> css=span:nth-child(2n+1) >> div + + Be aware that using ``#`` as a starting character in Robot Framework would be interpreted as comment. + Due to that fact a ``#id`` must be escaped as ``\\#id``. + + == Frames == By default, selector chains do not cross frame boundaries. It means that a simple CSS selector is not able to select and element located inside an iframe @@ -90,12 +318,94 @@ class Browser(DynamicCore): inside a frame. Given this simple pseudo html snippet: - ````, - here's a keyword call that clicks the button inside the frame. + | - | Click | iframe[name="iframe"] >>> #btn | + Here's a keyword call that clicks the button inside the frame. + + | Click id=iframe >>> id=btn The selectors on the left and right side of ``>>>`` can be any valid selectors. + The selector clause directly before the frame opener ``>>>`` must select the frame element. + + == WebComponents and Shadow DOM == + + Playwright and so also Browser are able to do automatic piercing of Shadow DOMs + and therefore are the best automation technology when working with WebComponents. + + Also other technologies claim that they can handle + [https://developer.mozilla.org/en-US/docs/Web/Web_Components/Using_shadow_DOM|Shadow DOM and Web Components]. + However, non of them do pierce shadow roots automatically, + which may be inconvenient when working with Shadow DOM and Web Components. + + For that reason, css engine pierces shadow roots. More specifically, every + [https://developer.mozilla.org/en-US/docs/Web/CSS/Descendant_combinator|Descendant combinator] + pierces an arbitrary number of open shadow roots, including the implicit descendant combinator + at the start of the selector. + + That means, it is not nessesary to select each shadow host, open its shadow root and + select the next shadow host until you reach the element that should be controlled. + + === CSS:light === + + ``css:light`` engine is equivalent to [https://developer.mozilla.org/en/docs/Web/API/Document/querySelector | Document.querySelector] + and behaves according to the CSS spec. + However, it does not pierce shadow roots. + + ``css`` engine first searches for elements in the light dom in the iteration order, + and then recursively inside open shadow roots in the iteration order. It does not + search inside closed shadow roots or iframes. + + Examples: + + |
+ |
In the light dom
+ |
In the light dom, but goes into the shadow slot
+ | + |
+ | + | In the shadow dom + | + |
  • Deep in the shadow
  • + |
    + |
    + |
    + | + |
    + |
    + + Note that ```` is not an html element, but rather a shadow root + created with ``element.attachShadow({mode: 'open'})``. + + - Both ``"css=article div"`` and ``"css:light=article div"`` match the first ``
    In the light dom
    ``. + - Both ``"css=article > div"`` and ``"css:light=article > div"`` match two ``div`` elements that are direct children of the ``article``. + - ``"css=article .in-the-shadow"`` matches the ``
    ``, piercing the shadow root, while ``"css:light=article .in-the-shadow"`` does not match anything. + - ``"css:light=article div > span"`` does not match anything, because both light-dom ``div`` elements do not contain a ``span``. + - ``"css=article div > span"`` matches the ````, piercing the shadow root. + - ``"css=article > .in-the-shadow"`` does not match anything, because ``
    `` is not a direct child of ``article`` + - ``"css:light=article > .in-the-shadow"`` does not match anything. + - ``"css=article li#target"`` matches the ``
  • Deep in the shadow
  • ``, piercing two shadow roots. + + === text:light === + + ``text`` engine open pierces shadow roots similarly to ``css``, while ``text:light`` does not. + Text engine first searches for elements in the light dom in the iteration order, and then + recursively inside open shadow roots in the iteration order. It does not search inside + closed shadow roots or iframes. + + === id, data-testid, data-test-id, data-test and their :light counterparts === + + Attribute engines are selecting based on the corresponding attribute value. + For example: ``data-test-id=foo`` is equivalent to ``css=[data-test-id="foo"]``, + and ``id:light=foo`` is equivalent to ``css:light=[id="foo"]``. == Element reference syntax == @@ -103,8 +413,8 @@ class Browser(DynamicCore): reference can be used as a *first* part of a selector by using a special selector syntax `element=` like this: - | ${ref}= | Get Element | .some_class | - | | Click | element=${ref} >> .some_child | + | ${ref}= Get Element .some_class + | Click element=${ref} >> .some_child The `.some_child` selector in the example is relative to the element referenced by ${ref}. @@ -145,12 +455,12 @@ class Browser(DynamicCore): == Examples == - | Keyword | Selector | Key | Assertion Operator | Assertion Expected | - | Get Title | | | equal | Page Title | - | Get Style | //*[@id="div-element"] | width | > | 100 | - | Get Title | | | matches | \\\\w+\\\\s\\\\w+ | - | Get Title | | | validate | value == "Login Page" | - | Get Title | | | evaluate | value if value == "some value" else "something else" | + | *Keyword* | *Selector* | *Key* | *Assertion Operator* | *Assertion Expected* | + | `Get Title` | | | equal | Page Title | + | `Get Style` | //*[@id="div-element"] | width | > | 100 | + | `Get Title` | | | matches | \\\\w+\\\\s\\\\w+ | + | `Get Title` | | | validate | value == "Login Page" | + | `Get Title` | | | evaluate | value if value == "some value" else "something else" | = Automatic page and context closing = diff --git a/Browser/keywords/browser_control.py b/Browser/keywords/browser_control.py index dc8d1974c..f6120b68f 100644 --- a/Browser/keywords/browser_control.py +++ b/Browser/keywords/browser_control.py @@ -56,7 +56,7 @@ def _get_screenshot_path(self, filename: str) -> Path: def take_screenshot(self, filename: str = "", selector: str = ""): """Takes a screenshot of the current window and saves it to ``path``. Saves it as a png. - ``filename`` Filename into which to save. The file will be saved into the robot framework output directory. + ``filename`` Filename into which to save. The file will be saved into the robot framework output directory by default. String ``{index}`` in path will be replaced with a rolling number. Use this to not override filenames. ``selector`` Take a screenshot of the element matched by selector. diff --git a/Browser/keywords/cookie.py b/Browser/keywords/cookie.py index 4b7b7fe5f..2c173950d 100644 --- a/Browser/keywords/cookie.py +++ b/Browser/keywords/cookie.py @@ -100,13 +100,13 @@ def add_cookie( ``secure`` Sets the secure token. - ``samesite`` Sets the samesite mode. + ``samesite`` < ``Strict`` | ``Lax`` | ``None`` > Sets the samesite mode. Example: - | `Add Cookie` | foo | bar | http://address.com/path/to/site | | # Using url argument. | - | `Add Cookie` | foo | bar | domain=example.com | path=/foo/bar | # Using domain and url arguments. | - | `Add Cookie` | foo | bar | http://address.com/path/to/site | expiry=2027-09-28 16:21:35 | # Expiry as timestamp. | - | `Add Cookie` | foo | bar | http://address.com/path/to/site | expiry=1822137695 | # Expiry as epoch seconds. | + | `Add Cookie` foo bar http://address.com/path/to/site # Using url argument. + | `Add Cookie` foo bar domain=example.com path=/foo/bar # Using domain and url arguments. + | `Add Cookie` foo bar http://address.com/path/to/site expiry=2027-09-28 16:21:35 # Expiry as timestamp. + | `Add Cookie` foo bar http://address.com/path/to/site expiry=1822137695 # Expiry as epoch seconds. """ params = locals_to_params(locals()) if expires: @@ -152,6 +152,8 @@ def get_cookie( ) -> Union[DotDict, str]: """Returns information of cookie with ``name`` as a Robot Framework dot dictionary or a string. + ``cookie`` Name of the cookie to be retrieved. + If ``return_type`` is ``dictionary`` or ``dict`` then keyword returns a of Robot Framework [https://robotframework.org/robotframework/latest/RobotFrameworkUserGuide.html#accessing-list-and-dictionary-items|dot dictionary] The dictionary contains all possible key value pairs of the cookie. If ``return_type`` is ``string`` or ``str``, @@ -161,7 +163,7 @@ def get_cookie( If no cookie is found with ``name`` keyword fails. The cookie dictionary contains details about the cookie. Keys available in the dictionary are documented in the table below. - | Value | Explanation | + | *Value* | *Explanation* | | name | The name of a cookie, mandatory. | | value | Value of the cookie, mandatory. | | url | Define the scope of the cookie, what URLs the cookies should be sent to. | @@ -177,9 +179,9 @@ def get_cookie( for details about each attribute. Example: - | ${cookie} = | Get Cookie | Foobar | - | Should Be Equal | ${cookie.value} | Tidii | - | Should Be Equal | ${cookie.expiry.year} | ${2020} | + | ${cookie}= Get Cookie Foobar + | Should Be Equal ${cookie.value} Tidii + | Should Be Equal ${cookie.expiry.year} ${2020} """ _, cookies = self._get_cookies() for cookie_dict in cookies: diff --git a/Browser/keywords/device_descriptors.py b/Browser/keywords/device_descriptors.py index 709996279..19096c0f2 100644 --- a/Browser/keywords/device_descriptors.py +++ b/Browser/keywords/device_descriptors.py @@ -35,10 +35,10 @@ def get_device(self, name: str): before using ensure your active page is on that context. Usage: - | ${device}= | Get Device | iPhone X - | | New Context | &{device} - | | New Page | - | | Get Viewport Size | returns { "width": 375, "height": 812 } + | ${device}= Get Device iPhone X + | New Context &{device} + | New Page + | Get Viewport Size returns { "width": 375, "height": 812 } """ with self.playwright.grpc_channel() as stub: response = stub.GetDevice(Request().Device(name=name)) diff --git a/Browser/keywords/evaluation.py b/Browser/keywords/evaluation.py index efe75957c..77887a07f 100644 --- a/Browser/keywords/evaluation.py +++ b/Browser/keywords/evaluation.py @@ -47,7 +47,7 @@ def highlight_elements( ``width`` Sets the width of the higlight border. Defaults to 2px. - ``style`` Sets the style of the border. Defaults to dotted. + ``style`` < ``solid`` | ``dotted`` | ``double`` | ``dashed`` > Sets the style of the border. Defaults to dotted. ``color`` Sets the color of the border. Valid colors i.e. are: ``red``, ``blue``, ``yellow``, ``pink``, ``black`` diff --git a/Browser/keywords/getters.py b/Browser/keywords/getters.py index 3a374435a..5ff0f2eee 100644 --- a/Browser/keywords/getters.py +++ b/Browser/keywords/getters.py @@ -198,7 +198,7 @@ def get_selected_options( ``selector`` Selector from which the info is to be retrieved. **Required** ``option_attribute`` Which attribute shall be returned/verified. - Allowed values are ``<"value"|"label"|"text"|"index">``. Defaults to label. + Allowed values are ``< ``value`` | ``label`` | ``text`` | ``index`` >``. Defaults to label. ``assertion_operator`` See `Assertions` for further details. Defaults to None. @@ -209,13 +209,13 @@ def get_selected_options( Example: - | `Select Options By` | label | //select[2] | Email | Mobile | | | - | ${selected_list} | `Get Selected Options` | //select[2] | | | | # getter | - | `Get Selected Options` | //select[2] | label | `==` | Mobile | Mail | #assertion content | - | `Select Options By` | label | select#names | 2 | 4 | | | - | `Get Selected Options` | select#names | index | `==` | 2 | 4 | #assertion index | - | `Get Selected Options` | select#names | label | *= | Mikko | | #assertion contains | - | `Get Selected Options` | select#names | label | validate | len(value) == 3 | | #assertion length | + | `Select Options By` label //select[2] Email Mobile + | ${selected_list} `Get Selected Options` //select[2] # getter + | `Get Selected Options` //select[2] label `==` Mobile Mail #assertion content + | `Select Options By` label select#names 2 4 + | `Get Selected Options` select#names index `==` 2 4 #assertion index + | `Get Selected Options` select#names label *= Mikko #assertion contain + | `Get Selected Options` select#names label validate len(value) == 3 #assertion length """ with self.playwright.grpc_channel() as stub: @@ -296,8 +296,6 @@ def get_element_count( ``assertion_operator`` See `Assertions` for further details. Defaults to None. ``expected_value`` Expected value for the counting - - """ with self.playwright.grpc_channel() as stub: response = stub.GetElementCount( @@ -460,15 +458,13 @@ def get_boundingbox( See `Assertions` for further details for the assertion arguments. Defaults to None. Example use: - | ${bounding_box}= Get BoundingBox id=element # unfiltered - | Log ${bounding_box} # {'x': 559.09375, 'y': 75.5, 'width': 188.796875, 'height': 18} - | ${x}= Get BoundingBox id=element x # filtered - | Log X: ${x} # X: 559.09375 + | ${bounding_box}= Get BoundingBox id=element # unfiltered + | Log ${bounding_box} # {'x': 559.09375, 'y': 75.5, 'width': 188.796875, 'height': 18} + | ${x}= Get BoundingBox id=element x # filtered + | Log X: ${x} # X: 559.09375 | # Assertions: - | Get BoundingBox id=element width > 180 - | Get BoundingBox id=element ALL validate value['x'] > value['y']*2 - - + | Get BoundingBox id=element width > 180 + | Get BoundingBox id=element ALL validate value['x'] > value['y']*2 """ with self.playwright.grpc_channel() as stub: response = stub.GetBoundingBox(Request.ElementSelector(selector=selector)) diff --git a/Browser/keywords/interaction.py b/Browser/keywords/interaction.py index 4c99822c7..a036366f9 100644 --- a/Browser/keywords/interaction.py +++ b/Browser/keywords/interaction.py @@ -1,478 +1,478 @@ -import json -from pathlib import Path -from typing import Dict, Optional - -from robotlibcore import keyword # type: ignore - -from ..base import LibraryComponent -from ..generated.playwright_pb2 import Request -from ..utils import logger -from ..utils.data_types import ( - AlertAction, - KeyAction, - KeyboardInputAction, - KeyboardModifier, - MouseButton, - MouseButtonAction, - MouseOptionsDict, - SelectAttribute, -) -from ..utils.time_conversion import timestr_to_millisecs - - -class Interaction(LibraryComponent): - @keyword(tags=["Setter", "PageContent"]) - def type_text( - self, selector: str, text: str, delay: str = "0 ms", clear: bool = True - ): - """Types the given ``text`` into the text field found by ``selector``. - - Sends a ``keydown``, ``keypress/input``, and ``keyup`` event for each - character in the text. - - ``selector`` Selector of the text field. **Required** - - ``text`` Text for the text field. **Required** - - ``delay`` Delay between the single key strokes. It may be either a - number or a Robot Framework time string. Time strings are fully - explained in an appendix of Robot Framework User Guide. Defaults to ``0 ms``. - Example: ``50 ms`` - - ``clear`` Set to false, if the field shall not be cleared before typing. - Defaults to true. - - See `Fill Text` for direct filling of the full text at once. - """ - self._type_text(selector, text, delay, clear) - - @keyword(tags=["Setter", "PageContent"]) - def fill_text(self, selector: str, text: str): - """Clears and fills the given ``text`` into the text field found by ``selector``. - - This method waits for an element matching the ``selector`` to appear, - waits for actionability checks, focuses the element, fills it and - triggers an input event after filling. - - If the element matching selector is not an ,