Skip to content

Commit 34d2ca8

Browse files
Add option to find element right now only instead of retrying all the time (#240)
* feat(element): add option to find element right now instead of always waiting for retry * feat: add keyword 'Find One Element' It runs immediately and never retries. It is useful with 'Wait Until Keyword Succeeds`. * Revert "feat(element): add option to find element right now instead of always waiting for retry" This reverts commit b4a0822. * docs: improve CHANGELOG how-to * style: fix robocop trailing whitespace * fix: remove library String to avoid name clash Library String was added in the current PR. Using keyword 'Evaluate' instead. [ WARN ] Keyword 'Format String' found both from a custom library 'StringFormat' and a standard library 'String' * style: run keen.bat tidy-fix
1 parent 2f4768e commit 34d2ca8

File tree

6 files changed

+135
-2
lines changed

6 files changed

+135
-2
lines changed

CHANGELOG.md

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,21 @@ This document follows the conventions laid out in [Keep a CHANGELOG][].
77

88
## [Unreleased][]
99

10+
## [Release][4.0.4] [4.0.4][4.0.3-4.0.4] - 2025-11-25
11+
12+
### Added
13+
14+
- New keyword
15+
- Find One Element
16+
17+
Keyword 'Find One Element' runs immediately and does not retry after the timeout.
18+
19+
### Changed
20+
21+
- Add a note that keyword 'Find All Elements' does not retry after the timeout
22+
- Add a section in README.md to run a single test in folder `atests`
23+
- Add a section in README.md when someone wants to release a new version
24+
1025
## [Release][4.0.3] [4.0.3][4.0.2-4.0.3] - 2025-11-17
1126

1227
### Added
@@ -749,6 +764,10 @@ General bugfixing from keywords and improvements from artifact building and test
749764
750765
[unreleased]: https://github.com/GDATASoftwareAG/robotframework-flaui/compare/4.0.2...main
751766
767+
[4.0.4]: https://github.com/GDATASoftwareAG/robotframework-flaui/releases/tag/4.0.4
768+
769+
[4.0.3-4.0.4]: https://github.com/GDATASoftwareAG/robotframework-flaui/compare/4.0.3...4.0.4
770+
752771
[4.0.3]: https://github.com/GDATASoftwareAG/robotframework-flaui/releases/tag/4.0.3
753772
754773
[4.0.2-4.0.3]: https://github.com/GDATASoftwareAG/robotframework-flaui/compare/4.0.2...4.0.3

README.md

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -142,6 +142,16 @@ Two test projects were used for the UI automation:
142142
* FlaUI WPF Test App 'Standard application that contains all common UI elements'
143143
* Notifier Test App 'Application which closes automatically after a time'
144144

145+
### Testing locally TLDR
146+
147+
```powershell
148+
python -m venv .venv
149+
.venv/Scripts/pip install -r requirements-dev.txt
150+
151+
cd ./atests
152+
../.venv/Scripts/robot -v UIA:UIA3 -d ../result -P ../src -t "Element Should Be Offscreen" Element.robot
153+
```
154+
145155
### Building and testing locally
146156

147157
Use the provided 'Commander Keen' file:
@@ -169,6 +179,17 @@ Following arguments are supported:
169179
* pylint - Static code analysis for python code
170180
* robocop - Static code analysis for robotframework code
171181
* tidy - Formatter for robotframework code
182+
183+
### Releasing a new version - Frequent tasks
184+
185+
- add or update keywords in folders
186+
- `src/FlaUILibrary/flaui/module`
187+
- `src/FlaUILibrary/keywords`
188+
- add or update tests in folder `atests`
189+
- update version in `src/FlaUILibrary/version.py`
190+
- update `CHANGELOG.md`
191+
- add a release near the top
192+
- add the links at the bottom in section `### Added`
172193

173194
## Acknowledgements
174195

atests/Element.robot

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -176,6 +176,38 @@ Find All Elements If Xpath Is Wrong
176176
${elements} Find All Elements /NOT_A_XPATH
177177
Length Should Be ${elements} 0
178178
179+
Find One Element
180+
${element} Find One Element ${XPATH_ENABLE_ELEMENT}
181+
182+
${window_xpath} Evaluate '${element.Xpath}'.split("/Tab")[0]
183+
184+
Should Be Equal ${element.AutomationId} ${window_xpath}/Tab/TabItem[1]/Button[@AutomationId="EnableButton"]
185+
Should Be Equal ${element.ClassName} ${window_xpath}/Tab/TabItem[1]/Button[@ClassName="Button"]
186+
Should Be Equal ${element.Name} ${window_xpath}/Tab/TabItem[1]/Button[@Name="Enable Button"]
187+
Should Be Equal ${element.Xpath} ${window_xpath}/Tab/TabItem[1]/Button[6]
188+
189+
Find One Element Not Supported Exception Should Return Empty String
190+
${element} Find One Element ${XPATH_ELEMENT}
191+
192+
${window_xpath} Evaluate '${element.Xpath}'.split("/Tab")[0]
193+
194+
Should Be Empty ${element.AutomationId}
195+
Should Be Equal ${element.ClassName} ${window_xpath}/Tab/TabItem[1]/Text[@ClassName="Text"]
196+
Should Be Equal ${element.Name} ${window_xpath}/Tab/TabItem[1]/Text[@Name="Test Label"]
197+
Should Be Equal ${element.Xpath} ${window_xpath}/Tab/TabItem[1]/Text[2]
198+
199+
Find One Element Xpath Usage Can Be Used To Any Keyword
200+
${element} Find One Element ${XPATH_ENABLE_ELEMENT}
201+
Element Should Exist ${element}
202+
203+
Find One Element If Xpath Is Wrong
204+
TRY
205+
Find One Element /NOT_A_XPATH
206+
Fail 'Find One Element' was supposed to fail because the xpath does NOT exist
207+
EXCEPT FlaUiError: Element from XPath '/NOT_A_XPATH' could not be found
208+
Log 'Find One Element' failed as expected
209+
END
210+
179211
Is Element Offscreen
180212
${IS_OFFSCREEN} Is Element Offscreen ${XPATH_ELEMENT}
181213
Should Be Equal ${IS_OFFSCREEN} ${False}

src/FlaUILibrary/flaui/module/element.py

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,7 @@ class Action(Enum):
3737
GET_ELEMENT_BY_XPATH = "GET_ELEMENT_BY_XPATH"
3838
GET_ELEMENT_RECTANGLE_BOUNDING = "GET_ELEMENT_RECTANGLE_BOUNDING"
3939
FOCUS_ELEMENT = "FOCUS_ELEMENT"
40+
FIND_ONE_ELEMENT = "FIND_ONE_ELEMENT"
4041
FIND_ALL_ELEMENTS = "FIND_ALL_ELEMENTS"
4142
IS_ELEMENT_ENABLED = "IS_ELEMENT_ENABLED"
4243
IS_ELEMENT_OFFSCREEN = "IS_ELEMENT_OFFSCREEN"
@@ -126,6 +127,8 @@ def execute_action(self, action: Action, values: Container):
126127
lambda: self._wait_until_element_is_offscreen(values["xpath"], values["retries"]),
127128
self.Action.WAIT_UNTIL_ELEMENT_IS_ENABLED:
128129
lambda: self._wait_until_element_is_enabled(values["xpath"], values["retries"]),
130+
self.Action.FIND_ONE_ELEMENT:
131+
lambda: self._find_one_element(values["xpath"]),
129132
self.Action.FIND_ALL_ELEMENTS:
130133
lambda: self._find_all_elements(values["xpath"]),
131134
self.Action.WAIT_UNTIL_ELEMENT_EXIST:
@@ -228,6 +231,28 @@ def _get_element_by_xpath(self, xpath: str):
228231
except CSharpException:
229232
return None
230233

234+
def _find_one_element(self, xpath: str):
235+
"""
236+
Try to find one element from xpath by desktop.
237+
238+
Args:
239+
xpath (string): XPath identifier from element.
240+
241+
Raises:
242+
FlaUiError: If node could not be found by xpath.
243+
"""
244+
element = self._get_element_by_xpath(xpath)
245+
246+
if element:
247+
return AutomationElement(
248+
self._try_get_automation_id_property(element),
249+
self._try_get_name_property(element),
250+
self._try_get_classname_property(element),
251+
FlaUIDebug.GetXPathToElement(element)
252+
)
253+
254+
raise FlaUiError(FlaUiError.XPathNotFound.format(xpath))
255+
231256
def _find_all_elements(self, xpath: str):
232257
"""
233258
Try to find all elements from xpath by desktop.

src/FlaUILibrary/keywords/element.py

Lines changed: 37 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -455,10 +455,46 @@ def wait_until_element_is_enabled(self, identifier, retries=10, msg=None):
455455
Element.create_value_container(xpath=identifier, retries=retries),
456456
msg)
457457

458+
@keyword
459+
def find_one_element(self, identifier, msg=None):
460+
"""
461+
Find one element from given xpath.
462+
Runs immediately and without retrying later.
463+
Returns an AutomationElement which contains properties to Xpath.
464+
If AutomationId, ClassName or Name is set. Xpath can be used by these values and will be returned.
465+
466+
| Example usage AutomationElement |
467+
| Xpath --> /Window[1]/Tab/TabItem[1] |
468+
| AutomationId --> /Window[1]/Tab/TabItem[@AutomationId="SimpleControl"] |
469+
| Name --> /Window[1]/Tab/TabItem[@Name="Simple Controls"] |
470+
| ClassName --> /Window[1]/Tab/TabItem[@ClassName="TabItem"] |
471+
472+
If any property is not set empty string value will be returned.
473+
474+
XPath syntax is explained in `XPath locator`.
475+
476+
Arguments:
477+
| Argument | Type | Description |
478+
| identifier | string | XPath identifier from element |
479+
| msg | string | Custom error message |
480+
481+
Example:
482+
| ${element} Find One Element <XPATH> |
483+
| Click ${element} |
484+
| Wait Until Keyword Succeeds 10s 1ms Find One Element /xpath/text/update/successful |
485+
| Click /xpath/button/continue |
486+
"""
487+
module = self._container.create_or_get_module()
488+
return module.action(Element.Action.FIND_ONE_ELEMENT,
489+
Element.create_value_container(xpath=identifier),
490+
msg)
491+
458492
@keyword
459493
def find_all_elements(self, identifier, msg=None):
460494
"""
461-
Find all elements from given xpath, Returns an AutomationElement list which contains properties to Xpath.
495+
Find all elements from given xpath.
496+
Runs immediately and without retrying later.
497+
Returns an AutomationElement list which contains properties to Xpath.
462498
If AutomationId, ClassName or Name is set. Xpath can be used by these values and will be returned.
463499
464500
| Example usage AutomationElement |

src/FlaUILibrary/version.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1 +1 @@
1-
VERSION = "4.0.3"
1+
VERSION = "4.0.4"

0 commit comments

Comments
 (0)