Skip to content

Commit 1a2f653

Browse files
committed
feat: support more PW API for Get Text
Support allInnerTexts, allTextContents, innerText, inputValue, innerHTML and the old functionality Fixes: #4763
1 parent 4041422 commit 1a2f653

16 files changed

Lines changed: 422 additions & 84 deletions

File tree

Browser/gen_stub.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -69,7 +69,7 @@ def parse_kw_stubs():
6969
from Browser.utils .data_types import (
7070
MouseButton, KeyboardModifier, ScrollBehavior, ScrollBehavior, DialogAction, MouseButtonAction, NotSet, Dimensions,
7171
SizeFields, AreaFields, BoundingBoxFields, SelectionStrategy, ElementRole, AriaSnapshotReturnType,
72-
KeyboardInputAction, KeyAction
72+
KeyboardInputAction, KeyAction, TextType
7373
)
7474
from Browser.utils.types import Secret
7575
"""

Browser/keywords/evaluation.py

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,13 @@
2020

2121
from ..base import LibraryComponent
2222
from ..generated.playwright_pb2 import Request
23-
from ..utils import DownloadInfo, HighlightMode, keyword, logger
23+
from ..utils import (
24+
ROBOT_FRAMEWORK_BROWSER_NO_SET,
25+
DownloadInfo,
26+
HighlightMode,
27+
keyword,
28+
logger,
29+
)
2430

2531

2632
class Evaluation(LibraryComponent):
@@ -117,7 +123,7 @@ def highlight_elements(
117123
Request().ElementSelectorWithDuration(
118124
selector=self.resolve_selector(selector)
119125
if selector
120-
else "ROBOT_FRAMEWORK_BROWSER_NO_ELEMENT",
126+
else ROBOT_FRAMEWORK_BROWSER_NO_SET,
121127
duration=int(self.convert_timeout(duration)),
122128
width=width,
123129
style=style,

Browser/keywords/getters.py

Lines changed: 41 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,7 @@
3838
from ..generated.playwright_pb2 import Request
3939
from ..utils import keyword, logger
4040
from ..utils.data_types import (
41+
ROBOT_FRAMEWORK_BROWSER_NO_SET,
4142
AreaFields,
4243
AriaSnapshotReturnType,
4344
BoundingBox,
@@ -52,6 +53,7 @@
5253
SelectionStrategy,
5354
SelectOptions,
5455
SizeFields,
56+
TextType,
5557
ViewportDimensions,
5658
)
5759

@@ -228,47 +230,67 @@ def get_text(
228230
assertion_operator: AssertionOperator | None = None,
229231
assertion_expected: Any | None = None,
230232
message: str | None = None,
231-
) -> str:
233+
*,
234+
text_type: TextType | None = None,
235+
) -> str | list:
232236
"""Returns text attribute of the element found by ``selector``.
233237
234-
Keyword can also return `input` or `textarea` value property text.
238+
Keyword can also return `input` or `textarea` value property text.
235239
See the `Finding elements` section for details about the selectors.
236240
237-
| =Arguments= | =Description= |
238-
| ``assertion_operator`` | See `Assertions` for further details. Defaults to None. |
239-
| ``assertion_expected`` | Expected value for the state |
240-
| ``message`` | overrides the default error message for assertion. |
241+
| =Arguments= | =Description= |
242+
| ``assertion_operator`` | See `Assertions` for further details. Defaults to None. |
243+
| ``assertion_expected`` | Expected value for the state |
244+
| ``message`` | overrides the default error message for assertion. |
245+
| ``text_type`` | How text is text is returned. Possible values are ``allInnerTexts``, ``allTextContents``, ``innerText``, ``inputValue``, and ``innerHTML``. |
241246
242-
Keyword uses strict mode, see `Finding elements` for more details about strict mode.
247+
Keyword uses strict mode, see `Finding elements` for more details about strict mode.
248+
The ``text_type`` argument determines how text is returned. The ``allInnerTexts`` and
249+
``allTextContents`` will return a list of strings, while other types return a single
250+
string.
243251
244-
Optionally asserts that the text matches the specified assertion. See `Assertions`
245-
for further details for the assertion arguments. By default, assertion is not done.
252+
Optionally asserts that the text matches the specified assertion. See `Assertions`
253+
for further details for the assertion arguments. By default, assertion is not done.
246254
247-
Example:
248-
| ${text} = `Get Text` id=important # Returns element text without assertion.
249-
| ${text} = `Get Text` id=important == Important text # Returns element text with assertion.
250-
| ${text} = `Get Text` //input == root # Returns input element text with assertion.
255+
Example:
256+
| ${text} = `Get Text` id=important # Returns element text without assertion.
257+
| ${text} = `Get Text` id=important == Important text # Returns element text with assertion.
258+
| ${text} = `Get Text` //input == root # Returns input element text with assertion.
259+
| ${text} = `Get Text` id=important text_type=innerHTML # Returns element inner HTML.
260+
| ${text} = `Get Text` id=important text_type=allInnerTexts # Returns element inner text as list of strings.
251261
252-
[https://forum.robotframework.org/t//4285|Comment >>]
262+
[https://forum.robotframework.org/t//4285|Comment >>]
253263
"""
254264
selector = self.presenter_mode(selector, self.strict_mode)
255-
response = self._get_text(selector)
265+
response = self._get_text(selector, text_type)
256266
logger.debug(response.log)
257-
logger.info(f"Text: {response.body!r}")
267+
is_all_type = text_type in (TextType.allInnerTexts, TextType.allTextContents)
268+
if is_all_type:
269+
value = [str(item) for item in response.items]
270+
else:
271+
value = [str(response.items[0])]
272+
logger.info(f"Text: {value!r}")
258273
formatter = self.get_assertion_formatter("Get Text")
259274
return verify_assertion(
260-
response.body,
275+
value[0] if not is_all_type else value,
261276
assertion_operator,
262277
assertion_expected,
263278
"Text",
264279
message,
265280
formatter,
266281
)
267282

268-
def _get_text(self, selector: str): # To ease unit testing
283+
def _get_text(
284+
self, selector: str, text_type: TextType | None
285+
): # To ease unit testing
286+
text_type_value = (
287+
str(text_type.name) if text_type else ROBOT_FRAMEWORK_BROWSER_NO_SET
288+
)
269289
with self.playwright.grpc_channel() as stub:
270290
return stub.GetText(
271-
Request().ElementSelector(selector=selector, strict=self.strict_mode)
291+
Request().ElementSelectorWithTextType(
292+
selector=selector, strict=self.strict_mode, textType=text_type_value
293+
)
272294
)
273295

274296
@keyword(tags=("Getter", "Assertion", "PageContent"))

Browser/utils/__init__.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -63,8 +63,10 @@
6363
SelectionType,
6464
ServiceWorkersPermissions,
6565
SupportedBrowsers,
66+
TextType,
6667
ViewportDimensions,
6768
convert_typed_dict,
69+
ROBOT_FRAMEWORK_BROWSER_NO_SET,
6870
)
6971
from .js_utilities import get_abs_scroll_coordinates, get_rel_scroll_coordinates
7072
from .meta_python import find_by_id, locals_to_params

Browser/utils/data_types.py

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1483,6 +1483,27 @@ class TracingGroupMode(Enum):
14831483
Playwright = auto()
14841484

14851485

1486+
class TextType(Enum):
1487+
"""Defines which Playwright method is used to get the text of an element.
1488+
1489+
``allInnerTexts``: Returns an list of `node.innerText` values for all matching nodes.
1490+
1491+
``allTextContents``: Returns an list of `node.textContent` values for all matching nodes.
1492+
1493+
``innerText``: Returns the element node.innerText value, which represents the rendered text content of a node and its descendants.
1494+
1495+
``inputValue``: Returns the value for the matching <input> or <textarea> or <select> element.
1496+
1497+
``innerHTML``: Returns the element node.innerHTML value, which is the HTML markup contained within the element, omitting any shadow roots.
1498+
"""
1499+
1500+
allInnerTexts = auto()
1501+
allTextContents = auto()
1502+
innerText = auto()
1503+
inputValue = auto()
1504+
innerHTML = auto()
1505+
1506+
14861507
InstallableBrowser = Enum(
14871508
"InstallableBrowser",
14881509
{
@@ -1522,3 +1543,6 @@ class TracingGroupMode(Enum):
15221543
"only-shell": "only install headless shell when installing chromium",
15231544
"no-shell": "do not install chromium headless shell",
15241545
}
1546+
1547+
1548+
ROBOT_FRAMEWORK_BROWSER_NO_SET = "ROBOT_FRAMEWORK_BROWSER_NO_SET"

atest/test/02_Content_Keywords/basic_getters.robot

Lines changed: 6 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22
Resource imports.resource
33
Library ../../library/presenter_mode.py
44

5-
Suite Setup Setup
5+
Suite Setup Setup Basic Getters
66
Suite Teardown Set Retry Assertions For ${assert_timeout}
77
Test Setup Ensure Location ${LOGIN_URL}
88

@@ -11,33 +11,6 @@ ${UserNameLabel} = label[for="username_field"]
1111
${InputUsername} = id=username_field
1212

1313
*** Test Cases ***
14-
Get Text
15-
${h1} = Get Text h1
16-
Should Be Equal ${h1} Login Page
17-
18-
Get Text Disabled
19-
[Setup] Go To ${ELEMENT_STATE_URL}
20-
presenter_mode.Set Presenter Mode {"color": "red", "duration": "1s", "style": "solid"}
21-
${text} = Get Text //input[@name="readonly_with_equals_only"]
22-
[Teardown] presenter_mode.Set Presenter Mode False
23-
24-
Get Text And Assert ==
25-
Get Text ${UserNameLabel} == User Name:
26-
27-
Get Text And Assert !=
28-
Get Text ${UserNameLabel} !=
29-
30-
Get Text Assert Validate
31-
Get Text h1 validate value.startswith('Login')
32-
33-
Get Text With Nonmatching Selector
34-
[Tags] no-iframe
35-
Set Browser Timeout 50ms
36-
Run Keyword And Expect Error
37-
... *Error: locator.elementHandle: Timeout 50ms exceeded.*waiting for locator('notamatch')*
38-
... Get Text notamatch
39-
[Teardown] Set Browser Timeout ${PLAYWRIGHT_TIMEOUT}
40-
4114
Get Property And Assert
4215
Get Property h1 innerText == Login Page
4316
Get Property h1 innerText != ${None}
@@ -148,20 +121,20 @@ Get Element Count
148121
${count} = Get Element Count h1
149122
Should Be Equal ${count} ${1}
150123
${count} = Get Element Count label
151-
Should Be Equal ${count} ${13}
124+
Should Be Equal ${count} ${14}
152125
${count} = Get Element Count not-existing
153126
Should Be Equal ${count} ${0}
154127

155128
Get Element Count And Assert
156129
[Setup] Ensure Location ${LOGIN_URL}
157130
Get Element Count h1 == 1
158131
Get Element Count h1 == ${1}
159-
Get Element Count label validate value == 13
132+
Get Element Count label validate value == 14
160133
Get Element Count label > 1
161134
Get Element Count not-existing ==
162135
${promise} = Promise To Get Element Count label
163136
${count} = Wait For ${promise}
164-
Should Be Equal ${count} ${13}
137+
Should Be Equal ${count} ${14}
165138

166139
Get Style And Assert
167140
Get Style h1 ALL *= align-content
@@ -353,7 +326,7 @@ Get Element States Return Flags
353326
Should Be Equal ${input_state} ${pwd_state}
354327

355328
Get Console Log Test
356-
[Setup] Setup
329+
[Setup] Setup Basic Getters
357330
${first} = Get Console Log then len(value)
358331
Click With Options "Click with Options" left ALT SHIFT
359332
# Sometimes test app emist React Router Future Flag Warning: React Router ...
@@ -395,7 +368,7 @@ Get Console Log Test
395368
... $now - datetime.datetime.strptime($first_log['time'], '%Y-%m-%dT%H:%M:%S.%f%z') < datetime.timedelta(seconds=0.5)
396369

397370
*** Keywords ***
398-
Setup
371+
Setup Basic Getters
399372
Close Page ALL
400373
Ensure Open Page ${LOGIN_URL}
401374
${assert_timeout} = Set Retry Assertions For 2 sec
Lines changed: 98 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,98 @@
1+
*** Settings ***
2+
Resource imports.resource
3+
Library ../../library/presenter_mode.py
4+
5+
Suite Setup Setup Get Text
6+
Suite Teardown Set Retry Assertions For ${assert_timeout}
7+
Test Setup Ensure Location ${LOGIN_URL}
8+
9+
*** Variables ***
10+
${USERNAMELABEL} = label[for="username_field"]
11+
${INPUTUSERNAME} = id=username_field
12+
13+
*** Test Cases ***
14+
Get Text
15+
${h1} = Get Text h1
16+
Should Be Equal ${h1} Login Page
17+
18+
Get Text Disabled
19+
[Setup] Go To ${ELEMENT_STATE_URL}
20+
presenter_mode.Set Presenter Mode {"color": "red", "duration": "1s", "style": "solid"}
21+
${text} = Get Text //input[@name="readonly_with_equals_only"]
22+
[Teardown] presenter_mode.Set Presenter Mode False
23+
24+
Get Text And Assert ==
25+
Get Text ${USERNAMELABEL} == User Name:
26+
27+
Get Text And Assert !=
28+
Get Text ${USERNAMELABEL} !=
29+
30+
Get Text No Text Type And Input Field
31+
Type Text ${INPUTUSERNAME} MyUserName
32+
Get Text ${INPUTUSERNAME} == MyUserName
33+
34+
Get Text Text Type As InputValue And Input Field
35+
Type Text ${INPUTUSERNAME} MyUserName
36+
Get Text ${INPUTUSERNAME} == MyUserName text_type=inputValue
37+
38+
Get Text No Text Type And Select Element
39+
Select Options By id=pet-select value dog
40+
Get Text id=pet-select == dog
41+
42+
Get Text Text Type As InputValue And Select Element
43+
Select Options By id=pet-select value dog
44+
Get Text id=pet-select == dog text_type=inputValue
45+
46+
Get Text Assert Validate
47+
Get Text h1 validate value.startswith('Login')
48+
49+
Get Text With No Matching Selector
50+
[Tags] no-iframe
51+
Set Browser Timeout 50ms
52+
Run Keyword And Expect Error
53+
... *TimeoutError: locator.evaluate: Timeout 50ms exceeded.*
54+
... Get Text notamatch
55+
[Teardown] Set Browser Timeout ${PLAYWRIGHT_TIMEOUT}
56+
57+
Get Text With Text Type As InnerHTML
58+
${text} = Get Text h1 == Login Page text_type=innerHTML
59+
Should Be Equal ${text} Login Page
60+
${text} = Get Text [name="login_form"] text_type=innerHTML
61+
Should Contain ${text} <label for="username_field">User Name:</label>
62+
Should Contain ${text} <label for="password_field">Password:</label>
63+
64+
Get Text With Text Type As InnerText
65+
${text} = Get Text h1 == Login Page text_type=innerText
66+
Should Be Equal ${text} Login Page
67+
${text} = Get Text ${UserNameLabel} text_type=innerText
68+
Should Be Equal ${text} User Name:
69+
70+
Get Text With Text Type As All InnerTexts
71+
${texts} = Get Text //option text_type=allInnerTexts
72+
VAR @{expected} = --Please choose an option-- Dog Cat Hamster Parrot Spider Goldfish
73+
Lists Should Be Equal ${texts} ${expected}
74+
75+
Get Text With Text Type As All InnerTexts On Single Element With Multiple Text
76+
${texts} = Get Text [name="login_form"] text_type=allInnerTexts
77+
Should Contain ${texts[0]} User Name:
78+
Should Contain ${texts[0]} Password:
79+
Length Should Be ${texts} 1
80+
81+
Get Text With Text Type As All TextContents With Multiple Text
82+
${texts} = Get Text //option text_type=allTextContents
83+
VAR @{expected} = --Please choose an option-- Dog Cat Hamster Parrot Spider Goldfish
84+
Lists Should Be Equal ${texts} ${expected}
85+
86+
Text Area Access And No Text Type
87+
Get Text id=textarea51 == Some initial text
88+
Type Text id=textarea51 Area 51
89+
Get Text id=textarea51 == Area 51
90+
Type Text id=textarea51 Ufo detected
91+
Get Text id=textarea51 == Ufo detected text_type=inputValue
92+
93+
*** Keywords ***
94+
Setup Get Text
95+
Close Page ALL
96+
Ensure Open Page ${LOGIN_URL}
97+
${assert_timeout} = Set Retry Assertions For 2 sec
98+
VAR ${assert_timeout} = ${assert_timeout} scope=SUITE

atest/test/02_Content_Keywords/text_keywords.robot

Lines changed: 0 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -331,13 +331,6 @@ Get Text Custom Error
331331
... Get Text css=input#username_field == username Tidii
332332
[Teardown] Set Retry Assertions For 1s
333333

334-
Text Area Access
335-
Get Text id=textarea51 == Some initial text
336-
Type Text id=textarea51 Area 51
337-
Get Text id=textarea51 == Area 51
338-
Type Text id=textarea51 Ufo detected
339-
Get Text id=textarea51 == Ufo detected
340-
341334
Type Secret With CryptoLibrary
342335
Type Secret
343336
... input#username_field

node/dynamic-test-app/src/login.tsx

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -348,6 +348,18 @@ export default function Site() {
348348
</tbody>
349349
</table>
350350

351+
<label id="pet-select-label">Choose a pet:</label>
352+
<select name="pets" id="pet-select">
353+
<option value="">--Please choose an option--</option>
354+
<option value="dog">Dog</option>
355+
<option value="cat">Cat</option>
356+
<option value="hamster">Hamster</option>
357+
<option value="parrot">Parrot</option>
358+
<option value="spider">Spider</option>
359+
<option value="goldfish">Goldfish</option>
360+
</select>
361+
<br></br>
362+
351363
<input
352364
type="file"
353365
id="file_chooser"

0 commit comments

Comments
 (0)