robo_appian 0.0.27__py3-none-any.whl → 0.0.34__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- robo_appian/__init__.py +4 -2
- robo_appian/components/ButtonUtils.py +127 -54
- robo_appian/components/DateUtils.py +54 -20
- robo_appian/components/DropdownUtils.py +61 -61
- robo_appian/components/InputUtils.py +51 -19
- robo_appian/components/LabelUtils.py +82 -26
- robo_appian/components/LinkUtils.py +58 -23
- robo_appian/components/SearchDropdownUtils.py +101 -47
- robo_appian/components/SearchInputUtils.py +89 -43
- robo_appian/components/TabUtils.py +71 -24
- robo_appian/components/TableUtils.py +68 -44
- robo_appian/controllers/ComponentDriver.py +65 -16
- robo_appian/exceptions/MyCustomError.py +13 -1
- robo_appian/utils/BrowserUtils.py +55 -16
- robo_appian/utils/ComponentUtils.py +102 -56
- robo_appian/utils/RoboUtils.py +64 -0
- {robo_appian-0.0.27.dist-info → robo_appian-0.0.34.dist-info}/METADATA +4 -2
- robo_appian-0.0.34.dist-info/RECORD +20 -0
- robo_appian-0.0.34.dist-info/licenses/LICENSE +201 -0
- robo_appian/components/__init__.py +0 -22
- robo_appian/controllers/__init__.py +0 -0
- robo_appian/exceptions/__init__.py +0 -0
- robo_appian/utils/__init__.py +0 -0
- robo_appian-0.0.27.dist-info/RECORD +0 -23
- robo_appian-0.0.27.dist-info/licenses/LICENSE +0 -21
- {robo_appian-0.0.27.dist-info → robo_appian-0.0.34.dist-info}/WHEEL +0 -0
robo_appian/__init__.py
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
|
-
from robo_appian.
|
|
1
|
+
from robo_appian.utils.RoboUtils import RoboUtils
|
|
2
2
|
from robo_appian.utils.ComponentUtils import ComponentUtils
|
|
3
|
+
from robo_appian.components.ButtonUtils import ButtonUtils
|
|
3
4
|
from robo_appian.components.DateUtils import DateUtils
|
|
4
5
|
from robo_appian.components.DropdownUtils import DropdownUtils
|
|
5
6
|
from robo_appian.components.InputUtils import InputUtils
|
|
@@ -15,6 +16,7 @@ __version__ = ComponentUtils.get_version()
|
|
|
15
16
|
|
|
16
17
|
__all__ = [
|
|
17
18
|
"ButtonUtils",
|
|
19
|
+
"RoboUtils",
|
|
18
20
|
"ComponentUtils",
|
|
19
21
|
"DateUtils",
|
|
20
22
|
"DropdownUtils",
|
|
@@ -25,5 +27,5 @@ __all__ = [
|
|
|
25
27
|
"TableUtils",
|
|
26
28
|
"TabUtils",
|
|
27
29
|
"BrowserUtils",
|
|
28
|
-
"SearchInputUtils"
|
|
30
|
+
"SearchInputUtils",
|
|
29
31
|
]
|
|
@@ -1,23 +1,35 @@
|
|
|
1
1
|
from selenium.webdriver.common.by import By
|
|
2
2
|
from selenium.webdriver.support import expected_conditions as EC
|
|
3
3
|
from selenium.webdriver.support.ui import WebDriverWait
|
|
4
|
-
from selenium.webdriver.remote.webelement import WebElement
|
|
5
4
|
from robo_appian.utils.ComponentUtils import ComponentUtils
|
|
6
5
|
|
|
7
6
|
|
|
8
7
|
class ButtonUtils:
|
|
9
8
|
"""
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
9
|
+
Click buttons, action links, and control components using visible text labels.
|
|
10
|
+
|
|
11
|
+
Find and interact with buttons by their user-visible text. All methods automatically
|
|
12
|
+
handle whitespace variations and work reliably with overlaid or animated elements.
|
|
13
|
+
|
|
14
|
+
All methods follow the wait-first pattern: pass WebDriverWait as the first argument.
|
|
15
|
+
|
|
16
|
+
Examples:
|
|
17
|
+
>>> from robo_appian import ButtonUtils
|
|
18
|
+
|
|
19
|
+
# Click by exact label match
|
|
14
20
|
ButtonUtils.clickByLabelText(wait, "Submit")
|
|
15
|
-
|
|
21
|
+
ButtonUtils.clickByLabelText(wait, "Save Changes")
|
|
16
22
|
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
23
|
+
# Click by partial label match (useful for buttons with dynamic text)
|
|
24
|
+
ButtonUtils.clickByPartialLabelText(wait, "Save")
|
|
25
|
+
|
|
26
|
+
# Click by element ID (when labels are unavailable)
|
|
27
|
+
ButtonUtils.clickById(wait, "save_button_123")
|
|
28
|
+
|
|
29
|
+
# Check if button exists before clicking
|
|
30
|
+
if ButtonUtils.isButtonExistsByLabelText(wait, "Delete"):
|
|
31
|
+
ButtonUtils.clickByLabelText(wait, "Delete")
|
|
32
|
+
"""
|
|
21
33
|
|
|
22
34
|
@staticmethod
|
|
23
35
|
def _findByPartialLabelText(wait: WebDriverWait, label: str):
|
|
@@ -27,7 +39,6 @@ class ButtonUtils:
|
|
|
27
39
|
Parameters:
|
|
28
40
|
wait: Selenium WebDriverWait instance.
|
|
29
41
|
label: The label of the button to find.
|
|
30
|
-
label: The label of the button to find.
|
|
31
42
|
|
|
32
43
|
Returns:
|
|
33
44
|
WebElement representing the button.
|
|
@@ -36,26 +47,17 @@ class ButtonUtils:
|
|
|
36
47
|
component = ButtonUtils._findByPartialLabelText(wait, "Submit")
|
|
37
48
|
"""
|
|
38
49
|
xpath = f"//button[./span[contains(translate(normalize-space(.), '\u00a0', ' '), '{label}')]]"
|
|
39
|
-
|
|
40
|
-
component = ComponentUtils.waitForComponentToBeVisibleByXpath(wait, xpath)
|
|
41
|
-
except Exception as e:
|
|
42
|
-
raise Exception(
|
|
43
|
-
f"Button with label '{label}' not found or not clickable."
|
|
44
|
-
) from e
|
|
45
|
-
return component
|
|
50
|
+
return ComponentUtils.waitForComponentToBeVisibleByXpath(wait, xpath)
|
|
46
51
|
|
|
47
52
|
@staticmethod
|
|
48
53
|
def __findByLabelText(wait: WebDriverWait, label: str):
|
|
49
54
|
xpath = f".//button[./span[normalize-space(.)='{label}']]"
|
|
50
|
-
|
|
51
|
-
component = ComponentUtils.waitForComponentToBeVisibleByXpath(wait, xpath)
|
|
52
|
-
except Exception as e:
|
|
53
|
-
raise Exception(f"Button with label '{label}' not found.") from e
|
|
54
|
-
return component
|
|
55
|
+
return ComponentUtils.waitForComponentToBeVisibleByXpath(wait, xpath)
|
|
55
56
|
|
|
56
57
|
@staticmethod
|
|
57
58
|
def clickByPartialLabelText(wait: WebDriverWait, label: str):
|
|
58
|
-
"""
|
|
59
|
+
"""
|
|
60
|
+
Finds a button by its partial label and clicks it.
|
|
59
61
|
|
|
60
62
|
Parameters:
|
|
61
63
|
wait: Selenium WebDriverWait instance.
|
|
@@ -64,45 +66,73 @@ class ButtonUtils:
|
|
|
64
66
|
ButtonUtils.clickByPartialLabelText(wait, "Button Label")
|
|
65
67
|
"""
|
|
66
68
|
component = ButtonUtils._findByPartialLabelText(wait, label)
|
|
67
|
-
|
|
69
|
+
|
|
70
|
+
ComponentUtils.click(wait, component)
|
|
68
71
|
|
|
69
72
|
@staticmethod
|
|
70
73
|
def clickByLabelText(wait: WebDriverWait, label: str):
|
|
71
|
-
"""
|
|
74
|
+
"""
|
|
75
|
+
Click a button by its exact label text.
|
|
72
76
|
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
77
|
+
Finds and clicks a button element containing the exact label text, handling whitespace
|
|
78
|
+
and NBSP characters reliably through normalized matching.
|
|
79
|
+
|
|
80
|
+
Args:
|
|
81
|
+
wait: WebDriverWait instance.
|
|
82
|
+
label: Exact button text (e.g., "Submit", "Save", "Cancel").
|
|
83
|
+
|
|
84
|
+
Raises:
|
|
85
|
+
TimeoutException: If button not found or not clickable within timeout.
|
|
86
|
+
|
|
87
|
+
Examples:
|
|
88
|
+
>>> ButtonUtils.clickByLabelText(wait, "Submit")
|
|
89
|
+
>>> ButtonUtils.clickByLabelText(wait, "Save Changes")
|
|
90
|
+
>>> ButtonUtils.clickByLabelText(wait, "Cancel")
|
|
78
91
|
"""
|
|
79
92
|
component = ButtonUtils.__findByLabelText(wait, label)
|
|
80
|
-
|
|
93
|
+
|
|
94
|
+
ComponentUtils.click(wait, component)
|
|
81
95
|
|
|
82
96
|
@staticmethod
|
|
83
97
|
def clickById(wait: WebDriverWait, id: str):
|
|
84
|
-
"""
|
|
85
|
-
Finds and clicks an input button by its HTML id attribute.
|
|
98
|
+
"""Click a button by its HTML id attribute.
|
|
86
99
|
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
id: The HTML id of the input button.
|
|
100
|
+
Finds and clicks a button using its HTML id. Use when label-based locators
|
|
101
|
+
are unavailable or unreliable.
|
|
90
102
|
|
|
91
|
-
|
|
92
|
-
|
|
103
|
+
Args:
|
|
104
|
+
wait: WebDriverWait instance.
|
|
105
|
+
id: The HTML id of the button element.
|
|
93
106
|
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
component = wait.until(EC.element_to_be_clickable((By.ID, id)))
|
|
97
|
-
except Exception as e:
|
|
98
|
-
raise Exception(
|
|
99
|
-
f"Input button with id '{id}' not found or not clickable."
|
|
100
|
-
) from e
|
|
107
|
+
Raises:
|
|
108
|
+
TimeoutException: If button not found or not clickable within timeout.
|
|
101
109
|
|
|
102
|
-
|
|
110
|
+
Examples:
|
|
111
|
+
>>> ButtonUtils.clickById(wait, "save_button")
|
|
112
|
+
>>> ButtonUtils.clickById(wait, "submit_btn_123")
|
|
113
|
+
"""
|
|
114
|
+
component = wait.until(EC.element_to_be_clickable((By.ID, id)))
|
|
115
|
+
ComponentUtils.click(wait, component)
|
|
103
116
|
|
|
104
117
|
@staticmethod
|
|
105
118
|
def isButtonExistsByLabelText(wait: WebDriverWait, label: str):
|
|
119
|
+
"""
|
|
120
|
+
Check if a button exists by exact label match.
|
|
121
|
+
|
|
122
|
+
Searches for a button with the exact label text and returns True if found.
|
|
123
|
+
Does not raise exceptions; returns boolean result.
|
|
124
|
+
|
|
125
|
+
Args:
|
|
126
|
+
wait: WebDriverWait instance.
|
|
127
|
+
label: Exact button label text to match.
|
|
128
|
+
|
|
129
|
+
Returns:
|
|
130
|
+
bool: True if button found, False otherwise.
|
|
131
|
+
|
|
132
|
+
Examples:
|
|
133
|
+
>>> if ButtonUtils.isButtonExistsByLabelText(wait, "Delete"):
|
|
134
|
+
... ButtonUtils.clickByLabelText(wait, "Delete")
|
|
135
|
+
"""
|
|
106
136
|
xpath = f".//button[./span[normalize-space(.)='{label}']]"
|
|
107
137
|
try:
|
|
108
138
|
ComponentUtils.findComponentByXPath(wait, xpath)
|
|
@@ -112,6 +142,19 @@ class ButtonUtils:
|
|
|
112
142
|
|
|
113
143
|
@staticmethod
|
|
114
144
|
def isButtonExistsByPartialLabelText(wait: WebDriverWait, label: str):
|
|
145
|
+
"""
|
|
146
|
+
Check if a button exists by partial label match.
|
|
147
|
+
|
|
148
|
+
Searches for a button containing the partial label text. Returns True if found, False otherwise.
|
|
149
|
+
Does not raise exceptions.
|
|
150
|
+
|
|
151
|
+
Args:
|
|
152
|
+
wait: WebDriverWait instance.
|
|
153
|
+
label: Partial button label text to match.
|
|
154
|
+
|
|
155
|
+
Returns:
|
|
156
|
+
bool: True if button found, False otherwise.
|
|
157
|
+
"""
|
|
115
158
|
xpath = f".//button[./span[contains(translate(normalize-space(.), '\u00a0', ' '), '{label}')]]"
|
|
116
159
|
try:
|
|
117
160
|
ComponentUtils.findComponentByXPath(wait, xpath)
|
|
@@ -121,17 +164,47 @@ class ButtonUtils:
|
|
|
121
164
|
|
|
122
165
|
@staticmethod
|
|
123
166
|
def isButtonExistsByPartialLabelTextAfterLoad(wait: WebDriverWait, label: str):
|
|
167
|
+
"""
|
|
168
|
+
Check if a button exists by partial label match after page load.
|
|
169
|
+
|
|
170
|
+
Validates button presence after page reloads or dynamic content loading.
|
|
171
|
+
Returns True if found, False otherwise.
|
|
172
|
+
|
|
173
|
+
Args:
|
|
174
|
+
wait: WebDriverWait instance.
|
|
175
|
+
label: Partial button label text to match.
|
|
176
|
+
|
|
177
|
+
Returns:
|
|
178
|
+
bool: True if button found and visible, False otherwise.
|
|
179
|
+
"""
|
|
180
|
+
xpath = f".//button[./span[contains(translate(normalize-space(.), '\u00a0', ' '), '{label}')]]"
|
|
124
181
|
try:
|
|
125
|
-
|
|
182
|
+
ComponentUtils.waitForComponentToBeVisibleByXpath(wait, xpath)
|
|
183
|
+
return True
|
|
126
184
|
except Exception:
|
|
127
185
|
return False
|
|
128
|
-
return True
|
|
129
186
|
|
|
130
187
|
@staticmethod
|
|
131
188
|
def waitForButtonToBeVisibleByPartialLabelText(wait: WebDriverWait, label: str):
|
|
189
|
+
"""
|
|
190
|
+
Wait for a button to be visible by partial label match.
|
|
191
|
+
|
|
192
|
+
Blocks until a button containing the partial label text becomes visible
|
|
193
|
+
or timeout occurs.
|
|
194
|
+
|
|
195
|
+
Args:
|
|
196
|
+
wait: WebDriverWait instance.
|
|
197
|
+
label: Partial button label text to match.
|
|
198
|
+
|
|
199
|
+
Returns:
|
|
200
|
+
WebElement: The visible button element.
|
|
201
|
+
|
|
202
|
+
Raises:
|
|
203
|
+
TimeoutException: If button not visible within timeout.
|
|
204
|
+
|
|
205
|
+
Examples:
|
|
206
|
+
>>> ButtonUtils.waitForButtonToBeVisibleByPartialLabelText(wait, "Submit")
|
|
207
|
+
>>> ButtonUtils.clickByPartialLabelText(wait, "Submit")
|
|
208
|
+
"""
|
|
132
209
|
xpath = f".//button[./span[contains(translate(normalize-space(.), '\u00a0', ' '), '{label}')]]"
|
|
133
|
-
|
|
134
|
-
component = ComponentUtils.waitForComponentToBeVisibleByXpath(wait, xpath)
|
|
135
|
-
except Exception as e:
|
|
136
|
-
raise Exception(f"Button with partial label '{label}' not visible.") from e
|
|
137
|
-
return component
|
|
210
|
+
return ComponentUtils.waitForComponentToBeVisibleByXpath(wait, xpath)
|
|
@@ -3,15 +3,37 @@ from selenium.webdriver.support import expected_conditions as EC
|
|
|
3
3
|
from selenium.webdriver.support.ui import WebDriverWait
|
|
4
4
|
|
|
5
5
|
from robo_appian.components.InputUtils import InputUtils
|
|
6
|
+
from robo_appian.utils.ComponentUtils import ComponentUtils
|
|
6
7
|
|
|
7
8
|
|
|
8
9
|
class DateUtils:
|
|
9
10
|
"""
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
11
|
+
Fill date picker components by label or interact with date input fields.
|
|
12
|
+
|
|
13
|
+
Set dates in Appian date picker components by their associated label. Handles both
|
|
14
|
+
text-based date entry and date picker interaction. Automatically waits for clickability
|
|
15
|
+
and formats dates according to Appian's expected format.
|
|
16
|
+
|
|
17
|
+
All methods follow the wait-first pattern: pass WebDriverWait as the first argument.
|
|
18
|
+
|
|
19
|
+
Examples:
|
|
20
|
+
>>> from robo_appian import DateUtils, ComponentUtils
|
|
21
|
+
|
|
22
|
+
# Set a date value
|
|
23
|
+
DateUtils.setValueByLabelText(wait, "Start Date", "01/15/2024")
|
|
24
|
+
DateUtils.setValueByLabelText(wait, "End Date", "12/31/2024")
|
|
25
|
+
|
|
26
|
+
# Use helper functions for common dates
|
|
27
|
+
DateUtils.setValueByLabelText(wait, "Today", ComponentUtils.today())
|
|
28
|
+
DateUtils.setValueByLabelText(wait, "Yesterday", ComponentUtils.yesterday())
|
|
29
|
+
|
|
30
|
+
# Click to open date picker
|
|
31
|
+
DateUtils.clickByLabelText(wait, "Event Date")
|
|
32
|
+
|
|
33
|
+
Note:
|
|
34
|
+
- Date format is typically MM/DD/YYYY for Appian
|
|
35
|
+
- Waits for clickability before interacting with date fields
|
|
36
|
+
- ComponentUtils.today() returns today's date as MM/DD/YYYY
|
|
15
37
|
"""
|
|
16
38
|
|
|
17
39
|
@staticmethod
|
|
@@ -25,25 +47,36 @@ class DateUtils:
|
|
|
25
47
|
DateUtils.__findComponent(wait, "Start Date")
|
|
26
48
|
"""
|
|
27
49
|
|
|
28
|
-
xpath = f'.//div[./div/label[
|
|
29
|
-
|
|
30
|
-
component = wait.until(EC.element_to_be_clickable((By.XPATH, xpath)))
|
|
31
|
-
except Exception as e:
|
|
32
|
-
raise Exception(
|
|
33
|
-
f"Could not find clickable date component with label '{label}': {e}"
|
|
34
|
-
)
|
|
50
|
+
xpath = f'.//div[./div/label[normalize-space(translate(., "\u00a0", " "))="{label}"]]/div/div/div/input'
|
|
51
|
+
component = wait.until(EC.element_to_be_clickable((By.XPATH, xpath)))
|
|
35
52
|
return component
|
|
36
53
|
|
|
37
54
|
@staticmethod
|
|
38
55
|
def setValueByLabelText(wait: WebDriverWait, label: str, value: str):
|
|
39
56
|
"""
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
57
|
+
Set a date in a date picker component by label.
|
|
58
|
+
|
|
59
|
+
Finds the date input by its associated label, waits for clickability, clears any
|
|
60
|
+
existing date, and enters the new date value.
|
|
61
|
+
|
|
62
|
+
Args:
|
|
63
|
+
wait: WebDriverWait instance.
|
|
64
|
+
label: Exact label text of the date component (e.g., "Start Date").
|
|
65
|
+
value: Date string in MM/DD/YYYY format (e.g., "01/15/2024").
|
|
66
|
+
|
|
67
|
+
Returns:
|
|
68
|
+
WebElement: The date input component (for chaining if needed).
|
|
69
|
+
|
|
70
|
+
Raises:
|
|
71
|
+
ValueError: If label has no 'for' attribute.
|
|
72
|
+
TimeoutException: If date input not found or not clickable within timeout.
|
|
73
|
+
|
|
74
|
+
Examples:
|
|
75
|
+
>>> DateUtils.setValueByLabelText(wait, "Start Date", "01/15/2024")
|
|
76
|
+
>>> DateUtils.setValueByLabelText(wait, "End Date", "12/31/2024")
|
|
77
|
+
>>> # Using helper for today's date
|
|
78
|
+
>>> from robo_appian.utils.ComponentUtils import ComponentUtils
|
|
79
|
+
>>> DateUtils.setValueByLabelText(wait, "Date", ComponentUtils.today())
|
|
47
80
|
"""
|
|
48
81
|
component = DateUtils.__findComponent(wait, label)
|
|
49
82
|
InputUtils._setValueByComponent(wait, component, value)
|
|
@@ -60,5 +93,6 @@ class DateUtils:
|
|
|
60
93
|
DateUtils.clickByLabelText(wait, "Start Date")
|
|
61
94
|
"""
|
|
62
95
|
component = DateUtils.__findComponent(wait, label)
|
|
63
|
-
|
|
96
|
+
|
|
97
|
+
ComponentUtils.click(wait, component)
|
|
64
98
|
return component
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import time
|
|
2
|
+
from robo_appian.utils.ComponentUtils import ComponentUtils
|
|
2
3
|
from selenium.webdriver.common.by import By
|
|
3
4
|
from selenium.webdriver.support import expected_conditions as EC
|
|
4
5
|
from selenium.webdriver.support.ui import WebDriverWait
|
|
@@ -8,11 +9,10 @@ from selenium.common.exceptions import NoSuchElementException
|
|
|
8
9
|
|
|
9
10
|
class DropdownUtils:
|
|
10
11
|
"""
|
|
11
|
-
Utility class for interacting with dropdown components in
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
DropdownUtils.selectDropdownValueByLabelText(wait, "Status", "Approved")
|
|
12
|
+
Utility class for interacting with dropdown components in a web application.
|
|
13
|
+
Provides methods to select values, check statuses, and retrieve options from dropdowns.
|
|
14
|
+
Example:
|
|
15
|
+
DropdownUtils.selectDropdownValueByLabelText(wait, "Dropdown Label", "Option Value")
|
|
16
16
|
"""
|
|
17
17
|
|
|
18
18
|
@staticmethod
|
|
@@ -22,26 +22,20 @@ class DropdownUtils:
|
|
|
22
22
|
"""
|
|
23
23
|
Finds the combobox element by its label text.
|
|
24
24
|
:param wait: WebDriverWait instance to wait for elements.
|
|
25
|
-
:param label: The label of the
|
|
25
|
+
:param label: The label of the dropdown.
|
|
26
26
|
:param isPartialText: Whether to use partial text matching for the label.
|
|
27
27
|
:return: The combobox WebElement.
|
|
28
28
|
Example:
|
|
29
|
-
combobox = DropdownUtils.__findComboboxByLabelText(wait, "Dropdown Label", isPartialText=False)
|
|
30
|
-
combobox = DropdownUtils.__findComboboxByLabelText(wait, "Dropdown Label", isPartialText=True)
|
|
31
29
|
combobox = DropdownUtils.__findComboboxByLabelText(wait, "Dropdown Label")
|
|
30
|
+
combobox = DropdownUtils.__findComboboxByLabelText(wait, "Dropdown Label", isPartialText=True)
|
|
32
31
|
"""
|
|
33
32
|
|
|
34
33
|
if isPartialText:
|
|
35
|
-
xpath = f'//span[contains(normalize-space(.), "{label}")]/ancestor::div[@role="presentation"][1]//div[@
|
|
34
|
+
xpath = f'//span[contains(normalize-space(.), "{label}")]/ancestor::div[@role="presentation"][1]//div[@role="combobox" and not(@aria-disabled="true")]'
|
|
36
35
|
else:
|
|
37
|
-
xpath = f'//span[
|
|
36
|
+
xpath = f'//span[text()="{label}"]/ancestor::div[@role="presentation"][1]//div[@role="combobox" and not(@aria-disabled="true")]'
|
|
38
37
|
|
|
39
|
-
|
|
40
|
-
component = wait.until(EC.element_to_be_clickable((By.XPATH, xpath)))
|
|
41
|
-
except Exception as e:
|
|
42
|
-
raise Exception(f'Could not find combobox with label "{label}" : {str(e)}')
|
|
43
|
-
|
|
44
|
-
return component
|
|
38
|
+
return wait.until(EC.element_to_be_clickable((By.XPATH, xpath)))
|
|
45
39
|
|
|
46
40
|
@staticmethod
|
|
47
41
|
def __clickCombobox(wait: WebDriverWait, combobox: WebElement):
|
|
@@ -53,12 +47,11 @@ class DropdownUtils:
|
|
|
53
47
|
Example:
|
|
54
48
|
DropdownUtils.__clickCombobox(wait, combobox)
|
|
55
49
|
"""
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
raise Exception(f"Could not click combobox: {str(e)}")
|
|
50
|
+
component_id = combobox.get_attribute("id")
|
|
51
|
+
if not component_id:
|
|
52
|
+
raise ValueError("Combobox element does not have an 'id' attribute.")
|
|
53
|
+
element = wait.until(EC.element_to_be_clickable((By.ID, component_id)))
|
|
54
|
+
ComponentUtils.click(wait, element)
|
|
62
55
|
|
|
63
56
|
@staticmethod
|
|
64
57
|
def __findDropdownOptionId(combobox: WebElement):
|
|
@@ -103,9 +96,7 @@ class DropdownUtils:
|
|
|
103
96
|
except NoSuchElementException:
|
|
104
97
|
return False
|
|
105
98
|
except Exception as e:
|
|
106
|
-
raise
|
|
107
|
-
f'Could not find dropdown option "{value}" with dropdown option id "{dropdown_option_id}": {str(e)}'
|
|
108
|
-
)
|
|
99
|
+
raise
|
|
109
100
|
|
|
110
101
|
@staticmethod
|
|
111
102
|
def __selectDropdownValueByDropdownOptionId(
|
|
@@ -113,31 +104,15 @@ class DropdownUtils:
|
|
|
113
104
|
):
|
|
114
105
|
"""
|
|
115
106
|
Selects a value from a dropdown by its option id and value.
|
|
116
|
-
|
|
117
107
|
:param wait: WebDriverWait instance to wait for elements.
|
|
118
108
|
:param dropdown_option_id: The id of the dropdown options list.
|
|
119
109
|
:param value: The value to select from the dropdown.
|
|
120
110
|
Example:
|
|
121
|
-
DropdownUtils.
|
|
111
|
+
DropdownUtils.__selectDropdownValueByDropdownOptionId(wait, "dropdown_option_id", "Option Value")
|
|
122
112
|
"""
|
|
123
113
|
option_xpath = f'.//div/ul[@id="{dropdown_option_id}"]/li[./div[normalize-space(.)="{value}"]]'
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
component = wait.until(
|
|
127
|
-
EC.presence_of_element_located((By.XPATH, option_xpath))
|
|
128
|
-
)
|
|
129
|
-
component = wait.until(
|
|
130
|
-
EC.element_to_be_clickable((By.XPATH, option_xpath))
|
|
131
|
-
)
|
|
132
|
-
component.click()
|
|
133
|
-
except Exception as e:
|
|
134
|
-
raise Exception(
|
|
135
|
-
f'Could not locate or click dropdown option "{value}" with dropdown option id "{dropdown_option_id}": {str(e)}' # noqa: E501
|
|
136
|
-
)
|
|
137
|
-
except Exception as e:
|
|
138
|
-
raise Exception(
|
|
139
|
-
f'Could not find or click dropdown option "{value}" with xpath "{option_xpath}": {str(e)}'
|
|
140
|
-
)
|
|
114
|
+
component = wait.until(EC.element_to_be_clickable((By.XPATH, option_xpath)))
|
|
115
|
+
component.click()
|
|
141
116
|
|
|
142
117
|
@staticmethod
|
|
143
118
|
def __selectDropdownValueByPartialLabelText(
|
|
@@ -176,27 +151,26 @@ class DropdownUtils:
|
|
|
176
151
|
@staticmethod
|
|
177
152
|
def checkReadOnlyStatusByLabelText(wait: WebDriverWait, label: str):
|
|
178
153
|
"""
|
|
179
|
-
Checks if a dropdown is read-only by its label text.
|
|
180
|
-
|
|
154
|
+
Checks if a dropdown is read-only (disabled) by its label text.
|
|
181
155
|
:param wait: WebDriverWait instance to wait for elements.
|
|
182
156
|
:param label: The label of the dropdown.
|
|
183
|
-
:return: True if the dropdown is read-only, False
|
|
157
|
+
:return: True if the dropdown is read-only, False if editable.
|
|
184
158
|
Example:
|
|
185
|
-
|
|
186
|
-
if
|
|
159
|
+
is_readonly = DropdownUtils.checkReadOnlyStatusByLabelText(wait, "Dropdown Label")
|
|
160
|
+
if is_readonly:
|
|
187
161
|
print("The dropdown is read-only.")
|
|
188
162
|
else:
|
|
189
163
|
print("The dropdown is editable.")
|
|
190
164
|
"""
|
|
191
|
-
# xpath = f'.//div[./div/span[normalize-space(.)="{label}"]]/div/div/p[
|
|
165
|
+
# xpath = f'.//div[./div/span[normalize-space(.)="{label}"]]/div/div/p[normalize-space(translate(., "\u00a0", " "))]'
|
|
192
166
|
xpath = f'//span[normalize-space(.)="{label}"]/ancestor::div[@role="presentation"][1]//div[@aria-labelledby=//span[normalize-space(.)="{label}"]/@id and not(@role="combobox")]'
|
|
193
167
|
try:
|
|
194
168
|
wait._driver.find_element(By.XPATH, xpath)
|
|
195
169
|
return True
|
|
196
170
|
except NoSuchElementException:
|
|
197
171
|
return False
|
|
198
|
-
except Exception:
|
|
199
|
-
raise
|
|
172
|
+
except Exception as e:
|
|
173
|
+
raise
|
|
200
174
|
|
|
201
175
|
@staticmethod
|
|
202
176
|
def checkEditableStatusByLabelText(wait: WebDriverWait, label: str):
|
|
@@ -213,21 +187,34 @@ class DropdownUtils:
|
|
|
213
187
|
else:
|
|
214
188
|
print("The dropdown is disabled.")
|
|
215
189
|
"""
|
|
216
|
-
xpath = f'//span[
|
|
190
|
+
xpath = f'//span[normalize-space(translate(., "\u00a0", " "))="{label}"]/ancestor::div[@role="presentation"][1]//div[@aria-labelledby=//span[normalize-space(.)="{label}"]/@id and @role="combobox" and not(@aria-disabled="true")]'
|
|
217
191
|
try:
|
|
218
192
|
wait._driver.find_element(By.XPATH, xpath)
|
|
219
193
|
return True # If disabled element is found, dropdown is not editable
|
|
220
194
|
except NoSuchElementException:
|
|
221
195
|
return False # If disabled element is not found, dropdown is editable
|
|
222
|
-
except Exception:
|
|
223
|
-
raise
|
|
196
|
+
except Exception as e:
|
|
197
|
+
raise
|
|
224
198
|
|
|
225
199
|
@staticmethod
|
|
226
200
|
def waitForDropdownToBeEnabled(
|
|
227
201
|
wait: WebDriverWait, label: str, wait_interval: float = 0.5, timeout: int = 2
|
|
228
202
|
):
|
|
203
|
+
"""
|
|
204
|
+
Waits for a dropdown to become enabled (editable) by its label text.
|
|
205
|
+
:param wait: WebDriverWait instance to wait for elements.
|
|
206
|
+
:param label: The label of the dropdown.
|
|
207
|
+
:param wait_interval: The interval (in seconds) to wait between checks.
|
|
208
|
+
:param timeout: The maximum time (in seconds) to wait for the dropdown to become enabled.
|
|
209
|
+
:return: True if the dropdown becomes enabled within the timeout, False otherwise.
|
|
210
|
+
Example:
|
|
211
|
+
is_enabled = DropdownUtils.waitForDropdownToBeEnabled(wait, "Dropdown Label")
|
|
212
|
+
if is_enabled:
|
|
213
|
+
print("The dropdown is enabled.")
|
|
214
|
+
else:
|
|
215
|
+
print("The dropdown is still disabled.")
|
|
216
|
+
"""
|
|
229
217
|
elapsed_time = 0
|
|
230
|
-
status = False
|
|
231
218
|
|
|
232
219
|
while elapsed_time < timeout:
|
|
233
220
|
status = DropdownUtils.checkEditableStatusByLabelText(wait, label)
|
|
@@ -359,9 +346,7 @@ class DropdownUtils:
|
|
|
359
346
|
DropdownUtils.__clickCombobox(wait, combobox)
|
|
360
347
|
return option_texts
|
|
361
348
|
except Exception as e:
|
|
362
|
-
raise
|
|
363
|
-
f'Could not get dropdown option values for label "{dropdown_label}": {str(e)}'
|
|
364
|
-
)
|
|
349
|
+
raise
|
|
365
350
|
|
|
366
351
|
@staticmethod
|
|
367
352
|
def waitForDropdownValuesToBeChanged(
|
|
@@ -371,10 +356,25 @@ class DropdownUtils:
|
|
|
371
356
|
poll_frequency: float = 0.5,
|
|
372
357
|
timeout: int = 2,
|
|
373
358
|
):
|
|
359
|
+
"""
|
|
360
|
+
Waits for the values of a dropdown to change from the initial values.
|
|
361
|
+
|
|
362
|
+
:param wait: WebDriverWait instance to wait for elements.
|
|
363
|
+
:param dropdown_label: The label of the dropdown.
|
|
364
|
+
:param initial_values: The initial values of the dropdown.
|
|
365
|
+
:param poll_frequency: The interval (in seconds) to wait between checks.
|
|
366
|
+
:param timeout: The maximum time (in seconds) to wait for the dropdown values to change.
|
|
367
|
+
:return: True if the dropdown values change within the timeout, False otherwise.
|
|
368
|
+
Example:
|
|
369
|
+
initial_values = DropdownUtils.getDropdownOptionValues(wait, "Dropdown Label")
|
|
370
|
+
is_changed = DropdownUtils.waitForDropdownValuesToBeChanged(wait, "Dropdown Label", initial_values)
|
|
371
|
+
if is_changed:
|
|
372
|
+
print("The dropdown values have changed.")
|
|
373
|
+
else:
|
|
374
|
+
print("The dropdown values have not changed within the timeout.")
|
|
375
|
+
"""
|
|
374
376
|
|
|
375
377
|
elapsed_time = 0
|
|
376
|
-
poll_frequency = 0.5
|
|
377
|
-
timeout = 4 # seconds
|
|
378
378
|
while elapsed_time < timeout:
|
|
379
379
|
|
|
380
380
|
current_values: list[str] = DropdownUtils.getDropdownOptionValues(
|