robo_appian 0.0.27__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 +29 -0
- robo_appian/components/ButtonUtils.py +137 -0
- robo_appian/components/DateUtils.py +64 -0
- robo_appian/components/DropdownUtils.py +388 -0
- robo_appian/components/InputUtils.py +161 -0
- robo_appian/components/LabelUtils.py +67 -0
- robo_appian/components/LinkUtils.py +46 -0
- robo_appian/components/SearchDropdownUtils.py +91 -0
- robo_appian/components/SearchInputUtils.py +63 -0
- robo_appian/components/TabUtils.py +60 -0
- robo_appian/components/TableUtils.py +149 -0
- robo_appian/components/__init__.py +22 -0
- robo_appian/controllers/ComponentDriver.py +93 -0
- robo_appian/controllers/__init__.py +0 -0
- robo_appian/exceptions/MyCustomError.py +6 -0
- robo_appian/exceptions/__init__.py +0 -0
- robo_appian/utils/BrowserUtils.py +52 -0
- robo_appian/utils/ComponentUtils.py +292 -0
- robo_appian/utils/__init__.py +0 -0
- robo_appian-0.0.27.dist-info/METADATA +68 -0
- robo_appian-0.0.27.dist-info/RECORD +23 -0
- robo_appian-0.0.27.dist-info/WHEEL +4 -0
- robo_appian-0.0.27.dist-info/licenses/LICENSE +21 -0
|
@@ -0,0 +1,93 @@
|
|
|
1
|
+
from robo_appian.components.ButtonUtils import ButtonUtils
|
|
2
|
+
from selenium.webdriver.support.ui import WebDriverWait
|
|
3
|
+
from robo_appian.components.DateUtils import DateUtils
|
|
4
|
+
from robo_appian.components.DropdownUtils import DropdownUtils
|
|
5
|
+
from robo_appian.components.InputUtils import InputUtils
|
|
6
|
+
from robo_appian.components.LabelUtils import LabelUtils
|
|
7
|
+
from robo_appian.components.LinkUtils import LinkUtils
|
|
8
|
+
from robo_appian.components.TabUtils import TabUtils
|
|
9
|
+
from robo_appian.components.SearchInputUtils import SearchInputUtils
|
|
10
|
+
from robo_appian.components.SearchDropdownUtils import SearchDropdownUtils
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
class ComponentDriver:
|
|
14
|
+
"""
|
|
15
|
+
Utility class for interacting with various components in Appian UI.
|
|
16
|
+
Usage Example:
|
|
17
|
+
from robo_appian.utils.controllers.ComponentDriver import ComponentDriver
|
|
18
|
+
# Set a date value
|
|
19
|
+
ComponentDriver.execute(wait, "Date", "Set Value", "Start Date", "01/01/2024")
|
|
20
|
+
"""
|
|
21
|
+
|
|
22
|
+
@staticmethod
|
|
23
|
+
def execute(wait: WebDriverWait, type, action, label, value):
|
|
24
|
+
"""
|
|
25
|
+
Executes an action on a specified component type.
|
|
26
|
+
Parameters:
|
|
27
|
+
wait: Selenium WebDriverWait instance.
|
|
28
|
+
type: The type of component (e.g., "Date", "Input Text", "Search Input Text", etc.).
|
|
29
|
+
action: The action to perform on the component (e.g., "Set Value", "Click", "Select").
|
|
30
|
+
label: The visible text label of the component.
|
|
31
|
+
value: The value to set in the component (if applicable).
|
|
32
|
+
Example:
|
|
33
|
+
ComponentDriver.execute(wait, "Date", "Set Value", "Start Date", "01/01/2024")
|
|
34
|
+
"""
|
|
35
|
+
# This method executes an action on a specified component type based on the provided parameters.
|
|
36
|
+
|
|
37
|
+
match type:
|
|
38
|
+
case "Date":
|
|
39
|
+
match action:
|
|
40
|
+
case "Set Value":
|
|
41
|
+
DateUtils.setValueByLabelText(wait, label, value)
|
|
42
|
+
case _:
|
|
43
|
+
raise ValueError(f"Unsupported action for {type}: {action}")
|
|
44
|
+
case "Input Text":
|
|
45
|
+
match action:
|
|
46
|
+
case "Set Value":
|
|
47
|
+
InputUtils.setValueByLabelText(wait, label, value)
|
|
48
|
+
case _:
|
|
49
|
+
raise ValueError(f"Unsupported action for {type}: {action}")
|
|
50
|
+
case "Search Input Text":
|
|
51
|
+
match action:
|
|
52
|
+
case "Select":
|
|
53
|
+
SearchInputUtils.selectSearchDropdownByLabelText(wait, label, value)
|
|
54
|
+
case _:
|
|
55
|
+
raise ValueError(f"Unsupported action for {type}: {action}")
|
|
56
|
+
case "Label":
|
|
57
|
+
match action:
|
|
58
|
+
case "Find":
|
|
59
|
+
LabelUtils.isLabelExists(wait, label)
|
|
60
|
+
case _:
|
|
61
|
+
raise ValueError(f"Unsupported action for {type}: {action}")
|
|
62
|
+
case "Link":
|
|
63
|
+
match action:
|
|
64
|
+
case "Click":
|
|
65
|
+
LinkUtils.click(wait, label)
|
|
66
|
+
case _:
|
|
67
|
+
raise ValueError(f"Unsupported action for {type}: {action}")
|
|
68
|
+
case "Drop Down":
|
|
69
|
+
match action:
|
|
70
|
+
case "Select":
|
|
71
|
+
DropdownUtils.selectDropdownValueByLabelText(wait, label, value)
|
|
72
|
+
case _:
|
|
73
|
+
raise ValueError(f"Unsupported action for {type}: {action}")
|
|
74
|
+
case "Search Drop Down":
|
|
75
|
+
match action:
|
|
76
|
+
case "Select":
|
|
77
|
+
SearchDropdownUtils.selectSearchDropdownValueByLabelText(wait, label, value)
|
|
78
|
+
case _:
|
|
79
|
+
raise ValueError(f"Unsupported action for {type}: {action}")
|
|
80
|
+
case "Button":
|
|
81
|
+
match action:
|
|
82
|
+
case "Click":
|
|
83
|
+
ButtonUtils.clickByLabelText(wait, label)
|
|
84
|
+
case _:
|
|
85
|
+
raise ValueError(f"Unsupported action for {type}: {action}")
|
|
86
|
+
case "Tab":
|
|
87
|
+
match action:
|
|
88
|
+
case "Find":
|
|
89
|
+
TabUtils.selectTabByLabelText(wait, label)
|
|
90
|
+
case _:
|
|
91
|
+
raise ValueError(f"Unsupported action for {type}: {action}")
|
|
92
|
+
case _:
|
|
93
|
+
raise ValueError(f"Unsupported component type: {type}")
|
|
File without changes
|
|
File without changes
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
from selenium.webdriver.support.ui import WebDriverWait
|
|
2
|
+
|
|
3
|
+
|
|
4
|
+
class BrowserUtils:
|
|
5
|
+
@staticmethod
|
|
6
|
+
def switch_to_Tab(wait: WebDriverWait, tab_number):
|
|
7
|
+
"""
|
|
8
|
+
Switches to the specified browser tab.
|
|
9
|
+
|
|
10
|
+
:param wait: WebDriverWait instance
|
|
11
|
+
:param tab_number: The index of the tab to switch to
|
|
12
|
+
:return: None
|
|
13
|
+
Example usage:
|
|
14
|
+
BrowserUtils.switch_to_Tab(wait, 1)
|
|
15
|
+
"""
|
|
16
|
+
|
|
17
|
+
# Switch to the specified browser tab
|
|
18
|
+
handler = wait._driver.window_handles[tab_number]
|
|
19
|
+
wait._driver.switch_to.window(handler)
|
|
20
|
+
|
|
21
|
+
@staticmethod
|
|
22
|
+
def switch_to_next_tab(wait: WebDriverWait):
|
|
23
|
+
"""
|
|
24
|
+
Switches to the next browser tab.
|
|
25
|
+
|
|
26
|
+
:param wait: WebDriverWait instance
|
|
27
|
+
:return: None
|
|
28
|
+
Example usage:
|
|
29
|
+
BrowserUtils.switch_to_next_tab(wait)
|
|
30
|
+
"""
|
|
31
|
+
current_tab_index = wait._driver.window_handles.index(
|
|
32
|
+
wait._driver.current_window_handle
|
|
33
|
+
)
|
|
34
|
+
next_tab_index = (current_tab_index + 1) % len(wait._driver.window_handles)
|
|
35
|
+
BrowserUtils.switch_to_Tab(wait, next_tab_index)
|
|
36
|
+
|
|
37
|
+
@staticmethod
|
|
38
|
+
def close_current_tab_and_switch_back(wait: WebDriverWait):
|
|
39
|
+
"""
|
|
40
|
+
Closes the current browser tab and switches back to the original tab.
|
|
41
|
+
|
|
42
|
+
:param wait: WebDriverWait instance
|
|
43
|
+
:return: None
|
|
44
|
+
Example usage:
|
|
45
|
+
BrowserUtils.close_current_tab_and_switch_back(wait)
|
|
46
|
+
"""
|
|
47
|
+
current_tab_index = wait._driver.window_handles.index(
|
|
48
|
+
wait._driver.current_window_handle
|
|
49
|
+
)
|
|
50
|
+
wait._driver.close()
|
|
51
|
+
original_tab_index = (current_tab_index - 1) % len(wait._driver.window_handles)
|
|
52
|
+
BrowserUtils.switch_to_Tab(wait, original_tab_index)
|
|
@@ -0,0 +1,292 @@
|
|
|
1
|
+
|
|
2
|
+
import tomllib
|
|
3
|
+
from pathlib import Path
|
|
4
|
+
from selenium.common.exceptions import NoSuchElementException
|
|
5
|
+
from selenium.webdriver import ActionChains
|
|
6
|
+
from selenium.webdriver.common.by import By
|
|
7
|
+
from selenium.webdriver.common.keys import Keys
|
|
8
|
+
from selenium.webdriver.support import expected_conditions as EC
|
|
9
|
+
from selenium.webdriver.remote.webdriver import WebDriver
|
|
10
|
+
from selenium.webdriver.remote.webelement import WebElement
|
|
11
|
+
from selenium.webdriver.support.ui import WebDriverWait
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
class ComponentUtils:
|
|
15
|
+
|
|
16
|
+
@staticmethod
|
|
17
|
+
def get_version():
|
|
18
|
+
try:
|
|
19
|
+
toml_path = Path(__file__).parent.parent / "pyproject.toml"
|
|
20
|
+
with open(toml_path, "rb") as f:
|
|
21
|
+
data = tomllib.load(f)
|
|
22
|
+
return data.get("project", {}).get("version", "0.0.0")
|
|
23
|
+
except Exception:
|
|
24
|
+
return "0.0.0"
|
|
25
|
+
|
|
26
|
+
@staticmethod
|
|
27
|
+
def today():
|
|
28
|
+
"""
|
|
29
|
+
Returns today's date formatted as MM/DD/YYYY.
|
|
30
|
+
"""
|
|
31
|
+
|
|
32
|
+
from datetime import date
|
|
33
|
+
|
|
34
|
+
today = date.today()
|
|
35
|
+
yesterday_formatted = today.strftime("%m/%d/%Y")
|
|
36
|
+
return yesterday_formatted
|
|
37
|
+
|
|
38
|
+
@staticmethod
|
|
39
|
+
def yesterday():
|
|
40
|
+
"""
|
|
41
|
+
Returns yesterday's date formatted as MM/DD/YYYY.
|
|
42
|
+
"""
|
|
43
|
+
|
|
44
|
+
from datetime import date, timedelta
|
|
45
|
+
|
|
46
|
+
yesterday = date.today() - timedelta(days=1)
|
|
47
|
+
yesterday_formatted = yesterday.strftime("%m/%d/%Y")
|
|
48
|
+
return yesterday_formatted
|
|
49
|
+
|
|
50
|
+
@staticmethod
|
|
51
|
+
def findChildComponentByXpath(
|
|
52
|
+
wait: WebDriverWait, component: WebElement, xpath: str
|
|
53
|
+
):
|
|
54
|
+
"""Finds a child component using the given XPath within a parent component.
|
|
55
|
+
|
|
56
|
+
:param wait: WebDriverWait instance to wait for elements
|
|
57
|
+
:param component: Parent WebElement to search within
|
|
58
|
+
:param xpath: XPath string to locate the child component
|
|
59
|
+
:return: WebElement if found, raises NoSuchElementException otherwise
|
|
60
|
+
Example usage:
|
|
61
|
+
from selenium.webdriver.support.ui import WebDriverWait
|
|
62
|
+
from selenium import webdriver
|
|
63
|
+
|
|
64
|
+
driver = webdriver.Chrome()
|
|
65
|
+
wait = WebDriverWait(driver, 10)
|
|
66
|
+
parent_component = driver.find_element(By.ID, "parent")
|
|
67
|
+
xpath = ".//button[@class='child']"
|
|
68
|
+
child_component = ComponentUtils.findChildComponentByXpath(wait, parent_component, xpath)
|
|
69
|
+
"""
|
|
70
|
+
try:
|
|
71
|
+
component = wait.until(lambda comp: component.find_element(By.XPATH, xpath))
|
|
72
|
+
except Exception:
|
|
73
|
+
raise Exception(
|
|
74
|
+
f"Child component with XPath '{xpath}' not found within the given parent component."
|
|
75
|
+
)
|
|
76
|
+
return component
|
|
77
|
+
|
|
78
|
+
@staticmethod
|
|
79
|
+
def findComponentById(wait: WebDriverWait, id: str):
|
|
80
|
+
try:
|
|
81
|
+
component = wait.until(EC.presence_of_element_located((By.ID, id)))
|
|
82
|
+
except Exception:
|
|
83
|
+
raise Exception(f"Component with ID '{id}' not found.")
|
|
84
|
+
return component
|
|
85
|
+
|
|
86
|
+
@staticmethod
|
|
87
|
+
def waitForComponentToBeVisibleByXpath(wait: WebDriverWait, xpath: str):
|
|
88
|
+
try:
|
|
89
|
+
component = wait.until(EC.visibility_of_element_located((By.XPATH, xpath)))
|
|
90
|
+
except Exception:
|
|
91
|
+
raise Exception(f"Component with XPath '{xpath}' not visible.")
|
|
92
|
+
return component
|
|
93
|
+
|
|
94
|
+
# @staticmethod
|
|
95
|
+
# def waitForComponentToBeVisibleByXpath(wait: WebDriverWait, xpath: str):
|
|
96
|
+
# """
|
|
97
|
+
# Finds a component using the given XPath in the current WebDriver instance.
|
|
98
|
+
|
|
99
|
+
# :param wait: WebDriverWait instance to wait for elements
|
|
100
|
+
# :param xpath: XPath string to locate the component
|
|
101
|
+
# :return: WebElement if found, raises NoSuchElementException otherwise
|
|
102
|
+
|
|
103
|
+
# Example usage:
|
|
104
|
+
# component = ComponentUtils.waitForComponentToBeVisibleByXpath(wait, "//button[@id='submit']")
|
|
105
|
+
# component.click()
|
|
106
|
+
# """
|
|
107
|
+
# try:
|
|
108
|
+
# component = wait.until(EC.visibility_of_element_located((By.XPATH, xpath)))
|
|
109
|
+
# except Exception:
|
|
110
|
+
# raise Exception(f"Component with XPath '{xpath}' not visible.")
|
|
111
|
+
# return component
|
|
112
|
+
|
|
113
|
+
@staticmethod
|
|
114
|
+
def checkComponentExistsByXpath(wait: WebDriverWait, xpath: str):
|
|
115
|
+
"""Checks if a component with the given XPath exists in the current WebDriver instance."""
|
|
116
|
+
status = False
|
|
117
|
+
try:
|
|
118
|
+
ComponentUtils.waitForComponentToBeVisibleByXpath(wait, xpath)
|
|
119
|
+
status = True
|
|
120
|
+
except NoSuchElementException:
|
|
121
|
+
pass
|
|
122
|
+
|
|
123
|
+
return status
|
|
124
|
+
|
|
125
|
+
@staticmethod
|
|
126
|
+
def checkComponentExistsById(driver: WebDriver, id: str):
|
|
127
|
+
"""Checks if a component with the given ID exists in the current WebDriver instance.
|
|
128
|
+
|
|
129
|
+
:param driver: WebDriver instance to check for the component
|
|
130
|
+
:param id: ID of the component to check
|
|
131
|
+
:return: True if the component exists, False otherwise
|
|
132
|
+
Example usage:
|
|
133
|
+
exists = ComponentUtils.checkComponentExistsById(driver, "submit-button")
|
|
134
|
+
print(f"Component exists: {exists}")
|
|
135
|
+
"""
|
|
136
|
+
status = False
|
|
137
|
+
try:
|
|
138
|
+
driver.find_element(By.ID, id)
|
|
139
|
+
status = True
|
|
140
|
+
except NoSuchElementException:
|
|
141
|
+
pass
|
|
142
|
+
|
|
143
|
+
return status
|
|
144
|
+
|
|
145
|
+
@staticmethod
|
|
146
|
+
def findCount(wait: WebDriverWait, xpath: str):
|
|
147
|
+
"""Finds the count of components matching the given XPath.
|
|
148
|
+
|
|
149
|
+
:param wait: WebDriverWait instance to wait for elements
|
|
150
|
+
:param xpath: XPath string to locate components
|
|
151
|
+
:return: Count of components matching the XPath
|
|
152
|
+
Example usage:
|
|
153
|
+
count = ComponentUtils.findCount(wait, "//div[@class='item']")
|
|
154
|
+
print(f"Number of items found: {count}")
|
|
155
|
+
"""
|
|
156
|
+
|
|
157
|
+
length = 0
|
|
158
|
+
|
|
159
|
+
try:
|
|
160
|
+
component = wait.until(
|
|
161
|
+
EC.presence_of_all_elements_located((By.XPATH, xpath))
|
|
162
|
+
)
|
|
163
|
+
length = len(component)
|
|
164
|
+
except NoSuchElementException:
|
|
165
|
+
pass
|
|
166
|
+
|
|
167
|
+
return length
|
|
168
|
+
|
|
169
|
+
@staticmethod
|
|
170
|
+
def tab(wait: WebDriverWait):
|
|
171
|
+
"""Simulates a tab key press in the current WebDriver instance.
|
|
172
|
+
|
|
173
|
+
:param wait: WebDriverWait instance to wait for elements
|
|
174
|
+
:return: None
|
|
175
|
+
Example usage:
|
|
176
|
+
ComponentUtils.tab(wait)
|
|
177
|
+
"""
|
|
178
|
+
driver = wait._driver
|
|
179
|
+
actions = ActionChains(driver)
|
|
180
|
+
actions.send_keys(Keys.TAB).perform()
|
|
181
|
+
|
|
182
|
+
@staticmethod
|
|
183
|
+
def findComponentsByXPath(wait: WebDriverWait, xpath: str):
|
|
184
|
+
"""Finds all components matching the given XPath and returns a list of valid components
|
|
185
|
+
that are clickable and displayed.
|
|
186
|
+
|
|
187
|
+
:param wait: WebDriverWait instance to wait for elements
|
|
188
|
+
:param xpath: XPath string to locate components
|
|
189
|
+
:return: List of valid WebElement components
|
|
190
|
+
Example usage:
|
|
191
|
+
components = ComponentUtils.findComponentsByXPath(wait, "//button[@class='submit']")
|
|
192
|
+
for component in components:
|
|
193
|
+
component.click()
|
|
194
|
+
"""
|
|
195
|
+
# Wait for the presence of elements matching the XPath
|
|
196
|
+
wait.until(EC.presence_of_element_located((By.XPATH, xpath)))
|
|
197
|
+
|
|
198
|
+
# Find all matching elements
|
|
199
|
+
driver = wait._driver
|
|
200
|
+
components = driver.find_elements(By.XPATH, xpath)
|
|
201
|
+
|
|
202
|
+
# Filter for clickable and displayed components
|
|
203
|
+
valid_components = []
|
|
204
|
+
for component in components:
|
|
205
|
+
try:
|
|
206
|
+
if component.is_displayed() and component.is_enabled():
|
|
207
|
+
valid_components.append(component)
|
|
208
|
+
except Exception:
|
|
209
|
+
continue
|
|
210
|
+
|
|
211
|
+
if len(valid_components) > 0:
|
|
212
|
+
return valid_components
|
|
213
|
+
|
|
214
|
+
raise Exception(f"No valid components found for XPath: {xpath}")
|
|
215
|
+
|
|
216
|
+
@staticmethod
|
|
217
|
+
def findComponentByXPath(wait: WebDriverWait, xpath: str):
|
|
218
|
+
# component = wait.until(EC.presence_of_element_located((By.XPATH, xpath)))
|
|
219
|
+
component = wait._driver.find_element(By.XPATH, xpath)
|
|
220
|
+
return component
|
|
221
|
+
|
|
222
|
+
@staticmethod
|
|
223
|
+
def findComponentUsingXpathAndClick(wait: WebDriverWait, xpath: str):
|
|
224
|
+
"""Finds a component using the given XPath and clicks it.
|
|
225
|
+
|
|
226
|
+
:param wait: WebDriverWait instance to wait for elements
|
|
227
|
+
:param xpath: XPath string to locate the component
|
|
228
|
+
:return: None
|
|
229
|
+
Example usage:
|
|
230
|
+
from selenium.webdriver.support.ui import WebDriverWait
|
|
231
|
+
from selenium import webdriver
|
|
232
|
+
|
|
233
|
+
driver = webdriver.Chrome()
|
|
234
|
+
wait = WebDriverWait(driver, 10)
|
|
235
|
+
xpath = "//button[@id='submit']"
|
|
236
|
+
ComponentUtils.findComponentUsingXpathAndClick(wait, xpath)
|
|
237
|
+
"""
|
|
238
|
+
|
|
239
|
+
component = ComponentUtils.waitForComponentToBeVisibleByXpath(wait, xpath)
|
|
240
|
+
ComponentUtils.click(wait, component)
|
|
241
|
+
|
|
242
|
+
@staticmethod
|
|
243
|
+
def click(wait: WebDriverWait, component: WebElement):
|
|
244
|
+
"""
|
|
245
|
+
Clicks the given component after waiting for it to be clickable.
|
|
246
|
+
|
|
247
|
+
:param wait: WebDriverWait instance to wait for elements
|
|
248
|
+
:param component: WebElement representing the component to click
|
|
249
|
+
:return: None
|
|
250
|
+
Example usage:
|
|
251
|
+
ComponentUtils.click(wait, component)
|
|
252
|
+
"""
|
|
253
|
+
wait.until(EC.element_to_be_clickable(component))
|
|
254
|
+
component.click()
|
|
255
|
+
|
|
256
|
+
@staticmethod
|
|
257
|
+
def waitForComponentToBeInVisible(wait: WebDriverWait, component: WebElement):
|
|
258
|
+
try:
|
|
259
|
+
wait.until(EC.staleness_of(component))
|
|
260
|
+
except Exception:
|
|
261
|
+
raise Exception(
|
|
262
|
+
"Component did not become invisible (stale) within the timeout period."
|
|
263
|
+
)
|
|
264
|
+
|
|
265
|
+
@staticmethod
|
|
266
|
+
def isComponentPresentByXpath(wait: WebDriverWait, xpath: str):
|
|
267
|
+
status = False
|
|
268
|
+
try:
|
|
269
|
+
wait.until(EC.presence_of_element_located((By.XPATH, xpath)))
|
|
270
|
+
status = True
|
|
271
|
+
except NoSuchElementException:
|
|
272
|
+
pass
|
|
273
|
+
|
|
274
|
+
return status
|
|
275
|
+
|
|
276
|
+
@staticmethod
|
|
277
|
+
def waitForElementToBeVisibleById(wait: WebDriverWait, id: str):
|
|
278
|
+
return wait.until(EC.visibility_of_element_located((By.ID, id)))
|
|
279
|
+
|
|
280
|
+
@staticmethod
|
|
281
|
+
def waitForElementNotToBeVisibleById(wait: WebDriverWait, id: str):
|
|
282
|
+
return wait.until(EC.invisibility_of_element_located((By.ID, id)))
|
|
283
|
+
|
|
284
|
+
@staticmethod
|
|
285
|
+
def waitForElementToBeVisibleByText(wait: WebDriverWait, text: str):
|
|
286
|
+
xpath = f'//*[normalize-space(translate(., "\u00a0", " "))="{text}" and not(*[normalize-space(translate(., "\u00a0", " "))="{text}"]) and not(ancestor-or-self::*[contains(@class, "---hidden")])]'
|
|
287
|
+
return wait.until(EC.visibility_of_element_located((By.XPATH, xpath)))
|
|
288
|
+
|
|
289
|
+
@staticmethod
|
|
290
|
+
def waitForElementNotToBeVisibleByText(wait: WebDriverWait, text: str):
|
|
291
|
+
xpath = f'//*[normalize-space(translate(., "\u00a0", " "))="{text}" and not(*[normalize-space(translate(., "\u00a0", " "))="{text}"]) and not(ancestor-or-self::*[contains(@class, "---hidden")])]'
|
|
292
|
+
return wait.until(EC.invisibility_of_element_located((By.XPATH, xpath)))
|
|
File without changes
|
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: robo_appian
|
|
3
|
+
Version: 0.0.27
|
|
4
|
+
Summary: Automate your Appian code testing with Python. Boost quality, save time.
|
|
5
|
+
License-File: LICENSE
|
|
6
|
+
Author: Dinil Mithra
|
|
7
|
+
Author-email: dinilmithra@mailme@gmail.com
|
|
8
|
+
Requires-Python: >=3.12,<4.0
|
|
9
|
+
Classifier: Operating System :: OS Independent
|
|
10
|
+
Classifier: Programming Language :: Python :: 3
|
|
11
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
12
|
+
Classifier: Programming Language :: Python :: 3.13
|
|
13
|
+
Classifier: Programming Language :: Python :: 3.14
|
|
14
|
+
Requires-Dist: numpy
|
|
15
|
+
Requires-Dist: requests (>=2.25.1,<3.0.0)
|
|
16
|
+
Requires-Dist: selenium (>=4.34.0)
|
|
17
|
+
Project-URL: Homepage, https://github.com/dinilmithra/robo_appian
|
|
18
|
+
Project-URL: Repository, https://github.com/dinilmithra/robo_appian.git
|
|
19
|
+
Description-Content-Type: text/markdown
|
|
20
|
+
|
|
21
|
+
# Robo Appian
|
|
22
|
+
|
|
23
|
+
Robo Appian is a Python library for automated UI testing of Appian applications. It provides user-friendly utilities and best practices to help you write robust, maintainable, and business-focused test automation.
|
|
24
|
+
|
|
25
|
+
## Features
|
|
26
|
+
- Simple, readable API for Appian UI automation
|
|
27
|
+
- Utilities for buttons, inputs, dropdowns, tables, tabs, and more
|
|
28
|
+
- Data-driven and workflow testing support
|
|
29
|
+
- Error handling and debugging helpers
|
|
30
|
+
- Designed for both technical and business users
|
|
31
|
+
|
|
32
|
+
## Documentation
|
|
33
|
+
Full documentation, guides, and API reference are available at:
|
|
34
|
+
|
|
35
|
+
➡️ [Robo Appian Documentation](https://dinilmithra.github.io/robo_appian/)
|
|
36
|
+
|
|
37
|
+
## Quick Start
|
|
38
|
+
1. Install Robo Appian:
|
|
39
|
+
```bash
|
|
40
|
+
pip install robo_appian
|
|
41
|
+
```
|
|
42
|
+
2. See the [Getting Started Guide](docs/getting-started/installation.md) for setup and your first test.
|
|
43
|
+
|
|
44
|
+
## Example Usage
|
|
45
|
+
```python
|
|
46
|
+
from robo_appian.components import InputUtils, ButtonUtils
|
|
47
|
+
|
|
48
|
+
# Set value in a text field by label
|
|
49
|
+
InputUtils.setValueByLabelText(wait, "Username", "testuser")
|
|
50
|
+
|
|
51
|
+
# Click a button by label
|
|
52
|
+
ButtonUtils.clickByLabelText(wait, "Sign In")
|
|
53
|
+
```
|
|
54
|
+
|
|
55
|
+
## Project Structure
|
|
56
|
+
- `robo_appian/` - Library source code
|
|
57
|
+
- `docs/` - Documentation and guides
|
|
58
|
+
|
|
59
|
+
## Contributing
|
|
60
|
+
Contributions are welcome! Please see the [contributing guidelines](CONTRIBUTING.md) or open an issue to get started.
|
|
61
|
+
|
|
62
|
+
## License
|
|
63
|
+
MIT License. See [LICENSE](LICENSE) for details.
|
|
64
|
+
|
|
65
|
+
---
|
|
66
|
+
|
|
67
|
+
For questions or support, contact [Dinil Mithra](mailto:dinilmithra.mailme@gmail.com) or connect on [LinkedIn](https://www.linkedin.com/in/dinilmithra).
|
|
68
|
+
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
robo_appian/__init__.py,sha256=29XO60v2ci41N1dsWjsIXHdiLPbg7K34ckyHjceTVOc,1011
|
|
2
|
+
robo_appian/components/ButtonUtils.py,sha256=SmvpqLUjk728b3Xp2ydw56hZEB4HE_P0WGaS5iwUWXI,4931
|
|
3
|
+
robo_appian/components/DateUtils.py,sha256=N9XseKlgnCyOVowO9lQ_C7Nhv7xhAForoBa7D3MhhVo,2476
|
|
4
|
+
robo_appian/components/DropdownUtils.py,sha256=hi3gJuWjwKLEc5qQiIf70ffv6ouacawV9HLxTuQU78w,16184
|
|
5
|
+
robo_appian/components/InputUtils.py,sha256=EqAECJ-8VKRJKI6sJ8e5pYI9lTAuXHDQEYZKIW8ls5I,6071
|
|
6
|
+
robo_appian/components/LabelUtils.py,sha256=pC7odt10hgBG8Td8zu71IqJQ0h7YHN9O-sLbtnqE-tk,2430
|
|
7
|
+
robo_appian/components/LinkUtils.py,sha256=32_0mW-j624Be9xlFZAkZsLjJ2OaORfp-ePSRyFwTfs,1549
|
|
8
|
+
robo_appian/components/SearchDropdownUtils.py,sha256=sX9b__Rir7feH1SkCd53EeLJXO7OTLkeA1FMbZU_CNs,4504
|
|
9
|
+
robo_appian/components/SearchInputUtils.py,sha256=2mg1QkEZGVJ-rLTvB0pMNSKvNhiqOU9QrUH0CNr4la4,3260
|
|
10
|
+
robo_appian/components/TabUtils.py,sha256=k-HkIK8ca-KnHXSDTXDV4190jT5P84tZ3frWSCndn5I,2257
|
|
11
|
+
robo_appian/components/TableUtils.py,sha256=_NBytGtP0EzBkjzrffpQ-pm3PoRPUykUvEwTcXu0xPk,6322
|
|
12
|
+
robo_appian/components/__init__.py,sha256=BW-fDKwq9Ntq-F7qAt-kDUudowOJBHr4sLiPV9Rjo_M,718
|
|
13
|
+
robo_appian/controllers/ComponentDriver.py,sha256=4zKn6Jy71cFsGWqOh7EgQ6NYfRIVvi5l5Pn31tLLM9s,4350
|
|
14
|
+
robo_appian/controllers/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
15
|
+
robo_appian/exceptions/MyCustomError.py,sha256=DVAkytXNNQNjqyTyCjk6FFd6fr3AsBe57Y19erDSqVs,222
|
|
16
|
+
robo_appian/exceptions/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
17
|
+
robo_appian/utils/BrowserUtils.py,sha256=Lp0yeHjqstdyA5POqFv4TxGY1TKixApMT6G_nnWadXs,1747
|
|
18
|
+
robo_appian/utils/ComponentUtils.py,sha256=NBdqfpetJBmorLWEiOgc3fJWhun4d8aIkUcERf0_vIg,10873
|
|
19
|
+
robo_appian/utils/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
20
|
+
robo_appian-0.0.27.dist-info/METADATA,sha256=XocLA7aAC_1jjPF26UHnye_ZHoxmYGQxp_gFrjNRzz8,2334
|
|
21
|
+
robo_appian-0.0.27.dist-info/WHEEL,sha256=zp0Cn7JsFoX2ATtOhtaFYIiE2rmFAD4OcMhtUki8W3U,88
|
|
22
|
+
robo_appian-0.0.27.dist-info/licenses/LICENSE,sha256=g-xR4dRa9_4iFkMoJmED-wE-J5hoxbk3105Knhfpjm0,1068
|
|
23
|
+
robo_appian-0.0.27.dist-info/RECORD,,
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2025 dinilmithra
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|