robo-appian 0.0.1__tar.gz
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-0.0.1/LICENSE +21 -0
- robo_appian-0.0.1/PKG-INFO +26 -0
- robo_appian-0.0.1/README.md +2 -0
- robo_appian-0.0.1/pyproject.toml +16 -0
- robo_appian-0.0.1/robo_appian/__init__.py +11 -0
- robo_appian-0.0.1/robo_appian/utils/__init__.py +0 -0
- robo_appian-0.0.1/robo_appian/utils/components/ButtonUtils.py +21 -0
- robo_appian-0.0.1/robo_appian/utils/components/ComponentUtils.py +178 -0
- robo_appian-0.0.1/robo_appian/utils/components/DateUtils.py +39 -0
- robo_appian-0.0.1/robo_appian/utils/components/DropdownUtils.py +50 -0
- robo_appian-0.0.1/robo_appian/utils/components/InputUtils.py +49 -0
- robo_appian-0.0.1/robo_appian/utils/components/LabelUtils.py +11 -0
- robo_appian-0.0.1/robo_appian/utils/components/LinkUtils.py +13 -0
- robo_appian-0.0.1/robo_appian/utils/components/TabUtils.py +18 -0
- robo_appian-0.0.1/robo_appian/utils/components/TableUtils.py +66 -0
- robo_appian-0.0.1/robo_appian/utils/components/__init__.py +0 -0
- robo_appian-0.0.1/robo_appian/utils/controllers/ComponentDriver.py +72 -0
- robo_appian-0.0.1/robo_appian/utils/controllers/__init__.py +0 -0
- robo_appian-0.0.1/robo_appian/utils/exceptions/MyCustomError.py +6 -0
- robo_appian-0.0.1/robo_appian/utils/exceptions/__init__.py +0 -0
- robo_appian-0.0.1/robo_appian.egg-info/PKG-INFO +26 -0
- robo_appian-0.0.1/robo_appian.egg-info/SOURCES.txt +25 -0
- robo_appian-0.0.1/robo_appian.egg-info/dependency_links.txt +1 -0
- robo_appian-0.0.1/robo_appian.egg-info/requires.txt +1 -0
- robo_appian-0.0.1/robo_appian.egg-info/top_level.txt +1 -0
- robo_appian-0.0.1/setup.cfg +4 -0
- robo_appian-0.0.1/setup.py +22 -0
|
@@ -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.
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: robo_appian
|
|
3
|
+
Version: 0.0.1
|
|
4
|
+
Summary: Automate your Appin code testing with Python. Boost quality, save time.
|
|
5
|
+
Home-page: https://github.com/dinilmithra/robo_appian.git
|
|
6
|
+
Author: Dinil Mithra
|
|
7
|
+
Author-email: dinilmithra@mailme@gmail.com
|
|
8
|
+
Classifier: Programming Language :: Python :: 3
|
|
9
|
+
Classifier: Operating System :: OS Independent
|
|
10
|
+
Requires-Python: >=3.12
|
|
11
|
+
Description-Content-Type: text/markdown
|
|
12
|
+
License-File: LICENSE
|
|
13
|
+
Requires-Dist: selenium>=4.34.0
|
|
14
|
+
Dynamic: author
|
|
15
|
+
Dynamic: author-email
|
|
16
|
+
Dynamic: classifier
|
|
17
|
+
Dynamic: description
|
|
18
|
+
Dynamic: description-content-type
|
|
19
|
+
Dynamic: home-page
|
|
20
|
+
Dynamic: license-file
|
|
21
|
+
Dynamic: requires-dist
|
|
22
|
+
Dynamic: requires-python
|
|
23
|
+
Dynamic: summary
|
|
24
|
+
|
|
25
|
+
# Robo Test suite for Appian framework
|
|
26
|
+
Robo Test suite for Appian framework
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
[tool.poetry]
|
|
2
|
+
name = "robo_appian"
|
|
3
|
+
version = "0.0.1"
|
|
4
|
+
description = "Automate your Appin code testing with Python. Boost quality, save time."
|
|
5
|
+
authors = ["Dinil Mithra <dinilmithra@mailme@gmail.com>"]
|
|
6
|
+
readme = "README.md"
|
|
7
|
+
|
|
8
|
+
[tool.poetry.dependencies]
|
|
9
|
+
python = ">=3.8,<4.0"
|
|
10
|
+
requests = "^2.25.1"
|
|
11
|
+
numpy = "*"
|
|
12
|
+
selenium = ">=4.34.0"
|
|
13
|
+
|
|
14
|
+
[build-system]
|
|
15
|
+
requires = ["setuptools>=61.0"]
|
|
16
|
+
build-backend = "setuptools.build_meta"
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
from robo_appian.utils.components.ButtonUtils import ButtonUtils
|
|
2
|
+
from robo_appian.utils.components.ComponentUtils import ComponentUtils
|
|
3
|
+
from robo_appian.utils.components.DateUtils import DateUtils
|
|
4
|
+
from robo_appian.utils.components.DropdownUtils import DropdownUtils
|
|
5
|
+
from robo_appian.utils.components.InputUtils import InputUtils
|
|
6
|
+
from robo_appian.utils.components.LabelUtils import LabelUtils
|
|
7
|
+
from robo_appian.utils.components.LinkUtils import LinkUtils
|
|
8
|
+
from robo_appian.utils.components.TableUtils import TableUtils
|
|
9
|
+
from robo_appian.utils.components.TabUtils import TabUtils
|
|
10
|
+
|
|
11
|
+
__version__ = "0.1.0"
|
|
File without changes
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
from selenium.webdriver.common.by import By
|
|
2
|
+
from selenium.webdriver.support import expected_conditions as EC
|
|
3
|
+
|
|
4
|
+
|
|
5
|
+
class ButtonUtils():
|
|
6
|
+
|
|
7
|
+
@staticmethod
|
|
8
|
+
def find(wait, label):
|
|
9
|
+
xpath = f".//button[./span[contains(translate(normalize-space(.), '\u00A0', ' '), '{label}')]]"
|
|
10
|
+
component = wait.until(EC.element_to_be_clickable((By.XPATH, xpath)))
|
|
11
|
+
return component
|
|
12
|
+
|
|
13
|
+
@staticmethod
|
|
14
|
+
def click(wait, label):
|
|
15
|
+
component = ButtonUtils.find(wait, label)
|
|
16
|
+
component.click()
|
|
17
|
+
|
|
18
|
+
@staticmethod
|
|
19
|
+
def clickInputButtonById(wait, id):
|
|
20
|
+
component = wait.until(EC.element_to_be_clickable((By.ID, id)))
|
|
21
|
+
component.click()
|
|
@@ -0,0 +1,178 @@
|
|
|
1
|
+
from selenium.common.exceptions import NoSuchElementException
|
|
2
|
+
from selenium.webdriver import ActionChains
|
|
3
|
+
from selenium.webdriver.common.by import By
|
|
4
|
+
from selenium.webdriver.common.keys import Keys
|
|
5
|
+
from selenium.webdriver.support import expected_conditions as EC
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
class ComponentUtils():
|
|
9
|
+
|
|
10
|
+
@staticmethod
|
|
11
|
+
def today():
|
|
12
|
+
from datetime import date
|
|
13
|
+
today = date.today()
|
|
14
|
+
yesterday_formatted = today.strftime("%m/%d/%Y")
|
|
15
|
+
return yesterday_formatted
|
|
16
|
+
|
|
17
|
+
@staticmethod
|
|
18
|
+
def yesterday():
|
|
19
|
+
from datetime import date, timedelta
|
|
20
|
+
yesterday = date.today() - timedelta(days=1)
|
|
21
|
+
yesterday_formatted = yesterday.strftime("%m/%d/%Y")
|
|
22
|
+
return yesterday_formatted
|
|
23
|
+
|
|
24
|
+
@staticmethod
|
|
25
|
+
def find_dropdown_id(wait, dropdown_label):
|
|
26
|
+
label_class_name = "FieldLayout---field_label"
|
|
27
|
+
xpath = f'.//div/span[normalize-space(text())="{dropdown_label}" and @class="{label_class_name}"]'
|
|
28
|
+
span_element = wait.until(EC.presence_of_element_located((By.XPATH, xpath)))
|
|
29
|
+
span_element_id = span_element.get_attribute('id')
|
|
30
|
+
return span_element_id
|
|
31
|
+
|
|
32
|
+
@staticmethod
|
|
33
|
+
def find_input_id(wait, label_text):
|
|
34
|
+
label_class_name = "FieldLayout---field_label"
|
|
35
|
+
xpath = f'.//div/div/label[@class="{label_class_name}" and contains(normalize-space(text()), "{label_text}")]'
|
|
36
|
+
label_element = wait.until(EC.presence_of_element_located((By.XPATH, xpath)))
|
|
37
|
+
input_element_id = label_element.get_attribute('for')
|
|
38
|
+
return input_element_id
|
|
39
|
+
|
|
40
|
+
@staticmethod
|
|
41
|
+
def set_input_value(wait, label_text, value):
|
|
42
|
+
input_element_id = ComponentUtils.find_input_id(wait, label_text)
|
|
43
|
+
input_element = ComponentUtils.set_input_value_using_id(wait, input_element_id, value)
|
|
44
|
+
return input_element
|
|
45
|
+
|
|
46
|
+
@staticmethod
|
|
47
|
+
def set_input_value_using_id(wait, input_element_id, value):
|
|
48
|
+
input_element = wait.until(EC.presence_of_element_located((By.ID, input_element_id)))
|
|
49
|
+
input_element = wait.until(EC.element_to_be_clickable((By.ID, input_element_id)))
|
|
50
|
+
input_element.clear()
|
|
51
|
+
input_element.send_keys(value)
|
|
52
|
+
input_element.send_keys(Keys.RETURN)
|
|
53
|
+
return input_element
|
|
54
|
+
|
|
55
|
+
@staticmethod
|
|
56
|
+
def select_search_dropdown_value(wait, label, value):
|
|
57
|
+
span_element_id = ComponentUtils.find_dropdown_id(wait, label)
|
|
58
|
+
xpath = f'.//div[@id="{span_element_id}_value"]'
|
|
59
|
+
combobox = wait.until(EC.presence_of_element_located((By.XPATH, xpath)))
|
|
60
|
+
disabled = combobox.get_attribute("aria-disabled")
|
|
61
|
+
if disabled == "true":
|
|
62
|
+
return
|
|
63
|
+
aria_controls = combobox.get_attribute("aria-controls")
|
|
64
|
+
combobox = wait.until(EC.element_to_be_clickable((By.XPATH, xpath)))
|
|
65
|
+
combobox.click()
|
|
66
|
+
|
|
67
|
+
input_element_id = span_element_id + "_searchInput"
|
|
68
|
+
ComponentUtils.set_input_value_using_id(wait, input_element_id, value)
|
|
69
|
+
|
|
70
|
+
xpath = f'.//ul[@id="{aria_controls}"]/li[./div[normalize-space(text())="{value}"]]'
|
|
71
|
+
component = wait.until(EC.element_to_be_clickable((By.XPATH, xpath)))
|
|
72
|
+
component.click()
|
|
73
|
+
|
|
74
|
+
@staticmethod
|
|
75
|
+
def selectDropdownValue(wait, combobox, value):
|
|
76
|
+
|
|
77
|
+
aria_controls = combobox.get_attribute("aria-controls")
|
|
78
|
+
combobox.click()
|
|
79
|
+
|
|
80
|
+
xpath = f'.//div/ul[@id="{aria_controls}"]/li[./div[normalize-space(text())="{value}"]]'
|
|
81
|
+
component = wait.until(EC.presence_of_element_located((By.XPATH, xpath)))
|
|
82
|
+
component = wait.until(EC.element_to_be_clickable((By.XPATH, xpath)))
|
|
83
|
+
component.click()
|
|
84
|
+
|
|
85
|
+
@staticmethod
|
|
86
|
+
def select_dropdown_value(wait, label, value):
|
|
87
|
+
span_element_id = ComponentUtils.find_dropdown_id(wait, label)
|
|
88
|
+
|
|
89
|
+
xpath = f'.//div[@id="{span_element_id}_value"]'
|
|
90
|
+
combobox = wait.until(EC.element_to_be_clickable((By.XPATH, xpath)))
|
|
91
|
+
disabled = combobox.get_attribute("aria-disabled")
|
|
92
|
+
if disabled == "true":
|
|
93
|
+
return
|
|
94
|
+
|
|
95
|
+
combobox = wait.until(EC.element_to_be_clickable((By.XPATH, xpath)))
|
|
96
|
+
ComponentUtils.selectDropdownValue(wait, combobox, value)
|
|
97
|
+
|
|
98
|
+
@staticmethod
|
|
99
|
+
def findButton(wait, button_text):
|
|
100
|
+
xpath = f'.//button[.//span/span[contains(normalize-space(text()), "{button_text}")]]'
|
|
101
|
+
component = wait.until(EC.element_to_be_clickable((By.XPATH, xpath)))
|
|
102
|
+
return component
|
|
103
|
+
|
|
104
|
+
@staticmethod
|
|
105
|
+
def click_button(wait, button_text):
|
|
106
|
+
component = ComponentUtils.findButton(wait, button_text)
|
|
107
|
+
component.click()
|
|
108
|
+
|
|
109
|
+
@staticmethod
|
|
110
|
+
def select_tab(wait, tab_label_text):
|
|
111
|
+
xpath = f'.//div[@role="presentation"]/div/div/p[./span[normalize-space(text())="{tab_label_text}"]]'
|
|
112
|
+
component = wait.until(EC.element_to_be_clickable((By.XPATH, xpath)))
|
|
113
|
+
component.click()
|
|
114
|
+
|
|
115
|
+
@staticmethod
|
|
116
|
+
def findSuccessMessage(wait, message):
|
|
117
|
+
xpath = f'.//div/div/p/span/strong[normalize-space(text())="{message}"]'
|
|
118
|
+
component = wait.until(EC.presence_of_element_located((By.XPATH, xpath)))
|
|
119
|
+
return component
|
|
120
|
+
|
|
121
|
+
@staticmethod
|
|
122
|
+
def findComponentUsingXpathAndClick(wait, xpath):
|
|
123
|
+
component = ComponentUtils.findComponentUsingXpath(wait, xpath)
|
|
124
|
+
component.click()
|
|
125
|
+
|
|
126
|
+
@staticmethod
|
|
127
|
+
def findComponentUsingXpath(wait, xpath):
|
|
128
|
+
component = wait.until(EC.presence_of_element_located((By.XPATH, xpath)))
|
|
129
|
+
return component
|
|
130
|
+
|
|
131
|
+
@staticmethod
|
|
132
|
+
def checkComponentExistsByXpath(wait, xpath):
|
|
133
|
+
status = False
|
|
134
|
+
try:
|
|
135
|
+
component = ComponentUtils.findComponentUsingXpath(wait, xpath)
|
|
136
|
+
status = True
|
|
137
|
+
except NoSuchElementException:
|
|
138
|
+
pass
|
|
139
|
+
|
|
140
|
+
return status
|
|
141
|
+
|
|
142
|
+
@staticmethod
|
|
143
|
+
def checkComponentExistsById(driver, id):
|
|
144
|
+
status = False
|
|
145
|
+
try:
|
|
146
|
+
component = driver.find_element(By.ID, id)
|
|
147
|
+
status = True
|
|
148
|
+
except NoSuchElementException:
|
|
149
|
+
pass
|
|
150
|
+
|
|
151
|
+
return status
|
|
152
|
+
|
|
153
|
+
@staticmethod
|
|
154
|
+
def findCount(wait, xpath):
|
|
155
|
+
|
|
156
|
+
length = 0
|
|
157
|
+
|
|
158
|
+
try:
|
|
159
|
+
component = wait.until(EC.presence_of_all_elements_located((By.XPATH, xpath)))
|
|
160
|
+
length = len(component)
|
|
161
|
+
except NoSuchElementException:
|
|
162
|
+
pass
|
|
163
|
+
|
|
164
|
+
return length
|
|
165
|
+
|
|
166
|
+
@staticmethod
|
|
167
|
+
def findComponent(wait, label):
|
|
168
|
+
xpath = f".//div/label[text()='{label}']"
|
|
169
|
+
component = wait.until(EC.element_to_be_clickable((By.XPATH, xpath)))
|
|
170
|
+
component_id = component.get_attribute("for")
|
|
171
|
+
|
|
172
|
+
component = wait.until(EC.element_to_be_clickable((By.ID, component_id)))
|
|
173
|
+
return component
|
|
174
|
+
|
|
175
|
+
@staticmethod
|
|
176
|
+
def tab(driver):
|
|
177
|
+
actions = ActionChains(driver)
|
|
178
|
+
actions.send_keys(Keys.TAB).perform()
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
from selenium.webdriver.common.by import By
|
|
2
|
+
from selenium.webdriver.support import expected_conditions as EC
|
|
3
|
+
|
|
4
|
+
from robo_appian.utils.components.InputUtils import InputUtils
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
class DateUtils():
|
|
8
|
+
|
|
9
|
+
@staticmethod
|
|
10
|
+
def findComponent(wait, label):
|
|
11
|
+
xpath = f".//div/label[text()='{label}']"
|
|
12
|
+
component = wait.until(EC.element_to_be_clickable((By.XPATH, xpath)))
|
|
13
|
+
component_id = component.get_attribute("for")
|
|
14
|
+
|
|
15
|
+
component = wait.until(EC.element_to_be_clickable((By.ID, component_id)))
|
|
16
|
+
return component
|
|
17
|
+
|
|
18
|
+
@staticmethod
|
|
19
|
+
def setDateValue(wait, label, value):
|
|
20
|
+
component = DateUtils.findComponent(wait, label)
|
|
21
|
+
InputUtils.setValueUsingComponent(component, value)
|
|
22
|
+
return component
|
|
23
|
+
|
|
24
|
+
@staticmethod
|
|
25
|
+
def setDateValueAndSubmit(wait, label, value):
|
|
26
|
+
# component=DateUtils.setDateValue(wait, label, value)
|
|
27
|
+
# component.send_keys(Keys.RETURN)
|
|
28
|
+
|
|
29
|
+
component = DateUtils.findComponent(wait, label)
|
|
30
|
+
InputUtils.setValueAndSubmitUsingComponent(component, value)
|
|
31
|
+
|
|
32
|
+
return component
|
|
33
|
+
|
|
34
|
+
@staticmethod
|
|
35
|
+
def click(wait, label):
|
|
36
|
+
component = DateUtils.findComponent(wait, label)
|
|
37
|
+
component.click()
|
|
38
|
+
|
|
39
|
+
return component
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
from selenium.webdriver.common.by import By
|
|
2
|
+
from selenium.webdriver.support import expected_conditions as EC
|
|
3
|
+
|
|
4
|
+
from robo_appian.utils.components.InputUtils import InputUtils
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
class DropdownUtils():
|
|
8
|
+
|
|
9
|
+
@staticmethod
|
|
10
|
+
def findDropdownEnabled(wait, dropdown_label):
|
|
11
|
+
xpath = f'.//div[./div/span[normalize-space(text())="{dropdown_label}"]]/div/div/div/div[@role="combobox" and @tabindex="0"]'
|
|
12
|
+
component = wait.until(EC.element_to_be_clickable((By.XPATH, xpath)))
|
|
13
|
+
return component
|
|
14
|
+
|
|
15
|
+
@staticmethod
|
|
16
|
+
def selectValueUsingComponent(wait, combobox, value):
|
|
17
|
+
component = combobox.find_element(By.XPATH, "./div/div")
|
|
18
|
+
aria_controls = component.get_attribute("aria-controls")
|
|
19
|
+
component.click()
|
|
20
|
+
|
|
21
|
+
xpath = f'.//div/ul[@id="{aria_controls}"]/li[./div[normalize-space(text())="{value}"]]'
|
|
22
|
+
# component = wait.until(EC.presence_of_element_located((By.XPATH, xpath)))
|
|
23
|
+
component = wait.until(EC.element_to_be_clickable((By.XPATH, xpath)))
|
|
24
|
+
component.click()
|
|
25
|
+
|
|
26
|
+
@staticmethod
|
|
27
|
+
def selectDropdownValue(wait, label, value):
|
|
28
|
+
combobox = DropdownUtils.findDropdownEnabled(wait, label)
|
|
29
|
+
aria_controls = combobox.get_attribute("aria-controls")
|
|
30
|
+
combobox.click()
|
|
31
|
+
|
|
32
|
+
xpath = f'.//div/ul[@id="{aria_controls}"]/li[./div[normalize-space(text())="{value}"]]'
|
|
33
|
+
component = wait.until(EC.presence_of_element_located((By.XPATH, xpath)))
|
|
34
|
+
component = wait.until(EC.element_to_be_clickable((By.XPATH, xpath)))
|
|
35
|
+
component.click()
|
|
36
|
+
|
|
37
|
+
@staticmethod
|
|
38
|
+
def selectSearchDropdownValue(wait, dropdown_label, value):
|
|
39
|
+
component = DropdownUtils.findDropdownEnabled(wait, dropdown_label)
|
|
40
|
+
component_id = component.get_attribute("aria-labelledby")
|
|
41
|
+
aria_controls = component.get_attribute("aria-controls")
|
|
42
|
+
component.click()
|
|
43
|
+
|
|
44
|
+
input_component_id = component_id + "_searchInput"
|
|
45
|
+
input_component = wait.until(EC.element_to_be_clickable((By.ID, input_component_id)))
|
|
46
|
+
InputUtils.setValueUsingComponent(input_component, value)
|
|
47
|
+
|
|
48
|
+
xpath = f'.//ul[@id="{aria_controls}"]/li[./div[normalize-space(text())="{value}"]][1]'
|
|
49
|
+
component = wait.until(EC.element_to_be_clickable((By.XPATH, xpath)))
|
|
50
|
+
component.click()
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
from selenium.webdriver.common.by import By
|
|
2
|
+
from selenium.webdriver.common.keys import Keys
|
|
3
|
+
from selenium.webdriver.support import expected_conditions as EC
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
class InputUtils():
|
|
7
|
+
|
|
8
|
+
@staticmethod
|
|
9
|
+
def findComponent(wait, label):
|
|
10
|
+
xpath = f".//div/label[text()='{label}']"
|
|
11
|
+
component = wait.until(EC.element_to_be_clickable((By.XPATH, xpath)))
|
|
12
|
+
component_id = component.get_attribute("for")
|
|
13
|
+
component = wait.until(EC.element_to_be_clickable((By.ID, component_id)))
|
|
14
|
+
return component
|
|
15
|
+
|
|
16
|
+
@staticmethod
|
|
17
|
+
def setValueUsingComponent(component, value):
|
|
18
|
+
component.clear()
|
|
19
|
+
component.send_keys(value)
|
|
20
|
+
return component
|
|
21
|
+
|
|
22
|
+
@staticmethod
|
|
23
|
+
def setValueAndSubmitUsingComponent(component, value):
|
|
24
|
+
component = InputUtils.setValueUsingComponent(component, value)
|
|
25
|
+
component.send_keys(Keys.ENTER)
|
|
26
|
+
return component
|
|
27
|
+
|
|
28
|
+
@staticmethod
|
|
29
|
+
def setInputValue(wait, label, value):
|
|
30
|
+
component = InputUtils.findComponent(wait, label)
|
|
31
|
+
InputUtils.setValueUsingComponent(component, value)
|
|
32
|
+
return component
|
|
33
|
+
|
|
34
|
+
@staticmethod
|
|
35
|
+
def setValueAndSubmit(wait, label, value):
|
|
36
|
+
component = InputUtils.findComponent(wait, label)
|
|
37
|
+
component = InputUtils.setValueAndSubmitUsingComponent(component, value)
|
|
38
|
+
return component
|
|
39
|
+
|
|
40
|
+
@staticmethod
|
|
41
|
+
def setSearchInputValue(wait, label, value):
|
|
42
|
+
xpath = f".//div[./div/span[text()='{label}']]/div/div/div/input[@role='combobox']"
|
|
43
|
+
search_input_component = wait.until(EC.element_to_be_clickable((By.XPATH, xpath)))
|
|
44
|
+
aria_controls = search_input_component.get_attribute("aria-controls")
|
|
45
|
+
InputUtils.setValueUsingComponent(search_input_component, value)
|
|
46
|
+
|
|
47
|
+
xpath = f".//ul[@id='{aria_controls}' and @role='listbox' ]/li[@role='option']/div/div/div/div/div/div/p[text()='{value}'][1]"
|
|
48
|
+
drop_down_item = wait.until(EC.element_to_be_clickable((By.XPATH, xpath)))
|
|
49
|
+
drop_down_item.click()
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
from selenium.webdriver.common.by import By
|
|
2
|
+
from selenium.webdriver.support import expected_conditions as EC
|
|
3
|
+
|
|
4
|
+
|
|
5
|
+
class LabelUtils():
|
|
6
|
+
|
|
7
|
+
@staticmethod
|
|
8
|
+
def find(wait, label):
|
|
9
|
+
xpath = f".//*[text()='{label}']"
|
|
10
|
+
component = wait.until(EC.presence_of_element_located((By.XPATH, xpath)))
|
|
11
|
+
return component
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
from selenium.webdriver.common.by import By
|
|
2
|
+
from selenium.webdriver.support import expected_conditions as EC
|
|
3
|
+
|
|
4
|
+
|
|
5
|
+
class LinkUtils():
|
|
6
|
+
|
|
7
|
+
@staticmethod
|
|
8
|
+
def click(wait, label):
|
|
9
|
+
xpath = f'.//p/span[text()="{label}"]'
|
|
10
|
+
# component = wait.until(EC.presence_of_element_located((By.XPATH, xpath)) )
|
|
11
|
+
component = wait.until(EC.element_to_be_clickable((By.XPATH, xpath)))
|
|
12
|
+
component.click()
|
|
13
|
+
return component
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
from selenium.webdriver.common.by import By
|
|
2
|
+
from selenium.webdriver.support import expected_conditions as EC
|
|
3
|
+
|
|
4
|
+
|
|
5
|
+
class TabUtils():
|
|
6
|
+
|
|
7
|
+
@staticmethod
|
|
8
|
+
def find_selected_tab(wait, label):
|
|
9
|
+
xpath = f".//div[./div[./div/div/div/div/div/p/strong[normalize-space(text())='{label}']]/span[text()='Selected Tab.']]/div[@role='link']"
|
|
10
|
+
component = wait.until(EC.element_to_be_clickable((By.XPATH, xpath)))
|
|
11
|
+
return component
|
|
12
|
+
|
|
13
|
+
@staticmethod
|
|
14
|
+
def select_tab(wait, label):
|
|
15
|
+
xpath = f".//div[@role='link']/div/div/div/div/div[./p/span[text()='{label}']]"
|
|
16
|
+
# xpath=f".//div[./div[./div/div/div/div/div/p/strong[normalize-space(text())='{label}']]/span[text()='Selected Tab.']]/div[@role='link']"
|
|
17
|
+
component = wait.until(EC.element_to_be_clickable((By.XPATH, xpath)))
|
|
18
|
+
component.click()
|
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
from selenium.webdriver.common.by import By
|
|
2
|
+
from selenium.webdriver.support import expected_conditions as EC
|
|
3
|
+
|
|
4
|
+
|
|
5
|
+
class TableUtils():
|
|
6
|
+
|
|
7
|
+
@staticmethod
|
|
8
|
+
def findTableUsingColumnName(wait, columnName):
|
|
9
|
+
# xpath = f".//table[./thead/tr/th[@abbr='{columnName}']]"
|
|
10
|
+
xpath = f'.//table[./thead/tr/th[@abbr="{columnName}"]]'
|
|
11
|
+
component = wait.until(EC.element_to_be_clickable((By.XPATH, xpath)))
|
|
12
|
+
return component
|
|
13
|
+
|
|
14
|
+
@staticmethod
|
|
15
|
+
def clickOnLinkUsingHoverText(wait, columnName, rowNumber, hoverText):
|
|
16
|
+
xpath = f".//table[./thead/tr/th[@abbr='{columnName}']]/tbody/tr[@data-dnd-name='row {rowNumber + 1}']/td[not (@data-empty-grid-message)]/div/p/a[./span[text()='{hoverText}']]"
|
|
17
|
+
# xpath=f".//table[./thead/tr/th/div[text()='{columnName}']][1]/tbody/tr[@data-dnd-name='row {rowNumber}']/td/div/p/a[./span[text()='{hoverText}']]"
|
|
18
|
+
component = wait.until(EC.element_to_be_clickable((By.XPATH, xpath)))
|
|
19
|
+
component.click()
|
|
20
|
+
|
|
21
|
+
@staticmethod
|
|
22
|
+
def clickOnButtonUsingHoverText(wait, columnName, rowNumber, hoverText):
|
|
23
|
+
xpath = f".//table[./thead/tr/th[@abbr='{columnName}']]/tbody/tr[@data-dnd-name='row {rowNumber}']/td[not (@data-empty-grid-message)]"
|
|
24
|
+
component = wait.until(EC.element_to_be_clickable((By.XPATH, xpath)))
|
|
25
|
+
component.click()
|
|
26
|
+
|
|
27
|
+
xpath = f".//table[./thead/tr/th[@abbr='{columnName}']]/tbody/tr[@data-dnd-name='row {rowNumber}']/td[not (@data-empty-grid-message)]/div/div/button[./span[text()='{hoverText}']]"
|
|
28
|
+
component = wait.until(EC.element_to_be_clickable((By.XPATH, xpath)))
|
|
29
|
+
component.click()
|
|
30
|
+
|
|
31
|
+
@staticmethod
|
|
32
|
+
def rowCount(tableObject):
|
|
33
|
+
xpath = "./tbody/tr[./td[not (@data-empty-grid-message)]]"
|
|
34
|
+
rows = tableObject.find_elements(By.XPATH, xpath)
|
|
35
|
+
return len(rows)
|
|
36
|
+
|
|
37
|
+
@staticmethod
|
|
38
|
+
def findColumNumberUsingColumnName(tableObject, columnName):
|
|
39
|
+
|
|
40
|
+
xpath = f'./thead/tr/th[@scope="col" and @abbr="{columnName}"]'
|
|
41
|
+
component = tableObject.find_element(By.XPATH, xpath)
|
|
42
|
+
class_string = component.get_attribute("class")
|
|
43
|
+
partial_string = "headCell_"
|
|
44
|
+
words = class_string.split()
|
|
45
|
+
selected_word = None
|
|
46
|
+
|
|
47
|
+
for word in words:
|
|
48
|
+
if partial_string in word:
|
|
49
|
+
selected_word = word
|
|
50
|
+
|
|
51
|
+
data = selected_word.split("_")
|
|
52
|
+
return int(data[1])
|
|
53
|
+
|
|
54
|
+
@staticmethod
|
|
55
|
+
def find_component_from_tabele_cell(wait, rowNumber, columnName):
|
|
56
|
+
|
|
57
|
+
tableObject = TableUtils.findTableUsingColumnName(wait, columnName)
|
|
58
|
+
columnNumber = TableUtils.findColumNumberUsingColumnName(tableObject, columnName)
|
|
59
|
+
# xpath=f'./tbody/tr[@data-dnd-name="row {rowNumber+1}"]/td[not (@data-empty-grid-message)][{columnNumber}]'
|
|
60
|
+
# component = tableObject.find_elements(By.XPATH, xpath)
|
|
61
|
+
rowNumber = rowNumber + 1
|
|
62
|
+
columnNumber = columnNumber + 1
|
|
63
|
+
xpath = f'.//table[./thead/tr/th[@abbr="{columnName}"]]/tbody/tr[@data-dnd-name="row {rowNumber}"]/td[not (@data-empty-grid-message)][{columnNumber}]/*'
|
|
64
|
+
component = wait.until(EC.element_to_be_clickable((By.XPATH, xpath)))
|
|
65
|
+
# childComponent=component.find_element(By.xpath("./*"))
|
|
66
|
+
return component
|
|
File without changes
|
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
from robo_appian.utils.components.ComponentUtils import ComponentUtils
|
|
2
|
+
from robo_appian.utils.components.DateUtils import DateUtils
|
|
3
|
+
from robo_appian.utils.components.DropdownUtils import DropdownUtils
|
|
4
|
+
from robo_appian.utils.components.InputUtils import InputUtils
|
|
5
|
+
from robo_appian.utils.components.LabelUtils import LabelUtils
|
|
6
|
+
from robo_appian.utils.components.LinkUtils import LinkUtils
|
|
7
|
+
from robo_appian.utils.components.TabUtils import TabUtils
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
class ComponentDriver():
|
|
11
|
+
|
|
12
|
+
@staticmethod
|
|
13
|
+
def execute(wait, type, action, label, value):
|
|
14
|
+
|
|
15
|
+
match type:
|
|
16
|
+
|
|
17
|
+
case "Date":
|
|
18
|
+
match action:
|
|
19
|
+
case "Set Value":
|
|
20
|
+
DateUtils.setDateValue(wait, label, value)
|
|
21
|
+
case _:
|
|
22
|
+
raise ValueError(f"Unsupported action for {type}: {action}")
|
|
23
|
+
case "Input Text":
|
|
24
|
+
match action:
|
|
25
|
+
case "Set Value":
|
|
26
|
+
InputUtils.setInputValue(wait, label, value)
|
|
27
|
+
case _:
|
|
28
|
+
raise ValueError(f"Unsupported action for {type}: {action}")
|
|
29
|
+
case "Search Input Text":
|
|
30
|
+
match action:
|
|
31
|
+
case "Select":
|
|
32
|
+
InputUtils.setSearchInputValue(wait, label, value)
|
|
33
|
+
case _:
|
|
34
|
+
raise ValueError(f"Unsupported action for {type}: {action}")
|
|
35
|
+
case "Label":
|
|
36
|
+
match action:
|
|
37
|
+
case "Find":
|
|
38
|
+
LabelUtils.find(wait, label)
|
|
39
|
+
case _:
|
|
40
|
+
raise ValueError(f"Unsupported action for {type}: {action}")
|
|
41
|
+
case "Link":
|
|
42
|
+
match action:
|
|
43
|
+
case "Click":
|
|
44
|
+
LinkUtils.click(wait, label)
|
|
45
|
+
case _:
|
|
46
|
+
raise ValueError(f"Unsupported action for {type}: {action}")
|
|
47
|
+
case "Drop Down":
|
|
48
|
+
match action:
|
|
49
|
+
case "Select":
|
|
50
|
+
DropdownUtils.selectDropdownValue(wait, label, value)
|
|
51
|
+
case _:
|
|
52
|
+
raise ValueError(f"Unsupported action for {type}: {action}")
|
|
53
|
+
case "Search Drop Down":
|
|
54
|
+
match action:
|
|
55
|
+
case "Select":
|
|
56
|
+
DropdownUtils.selectSearchDropdownValue(wait, label, value)
|
|
57
|
+
case _:
|
|
58
|
+
raise ValueError(f"Unsupported action for {type}: {action}")
|
|
59
|
+
case "Button":
|
|
60
|
+
match action:
|
|
61
|
+
case "Click":
|
|
62
|
+
ComponentUtils.click_button(wait, label)
|
|
63
|
+
case _:
|
|
64
|
+
raise ValueError(f"Unsupported action for {type}: {action}")
|
|
65
|
+
case "Tab":
|
|
66
|
+
match action:
|
|
67
|
+
case "Find":
|
|
68
|
+
TabUtils.find_selected_tab(wait, label)
|
|
69
|
+
case _:
|
|
70
|
+
raise ValueError(f"Unsupported action for {type}: {action}")
|
|
71
|
+
case _:
|
|
72
|
+
raise ValueError(f"Unsupported component type: {type}")
|
|
File without changes
|
|
File without changes
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: robo_appian
|
|
3
|
+
Version: 0.0.1
|
|
4
|
+
Summary: Automate your Appin code testing with Python. Boost quality, save time.
|
|
5
|
+
Home-page: https://github.com/dinilmithra/robo_appian.git
|
|
6
|
+
Author: Dinil Mithra
|
|
7
|
+
Author-email: dinilmithra@mailme@gmail.com
|
|
8
|
+
Classifier: Programming Language :: Python :: 3
|
|
9
|
+
Classifier: Operating System :: OS Independent
|
|
10
|
+
Requires-Python: >=3.12
|
|
11
|
+
Description-Content-Type: text/markdown
|
|
12
|
+
License-File: LICENSE
|
|
13
|
+
Requires-Dist: selenium>=4.34.0
|
|
14
|
+
Dynamic: author
|
|
15
|
+
Dynamic: author-email
|
|
16
|
+
Dynamic: classifier
|
|
17
|
+
Dynamic: description
|
|
18
|
+
Dynamic: description-content-type
|
|
19
|
+
Dynamic: home-page
|
|
20
|
+
Dynamic: license-file
|
|
21
|
+
Dynamic: requires-dist
|
|
22
|
+
Dynamic: requires-python
|
|
23
|
+
Dynamic: summary
|
|
24
|
+
|
|
25
|
+
# Robo Test suite for Appian framework
|
|
26
|
+
Robo Test suite for Appian framework
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
LICENSE
|
|
2
|
+
README.md
|
|
3
|
+
pyproject.toml
|
|
4
|
+
setup.py
|
|
5
|
+
robo_appian/__init__.py
|
|
6
|
+
robo_appian.egg-info/PKG-INFO
|
|
7
|
+
robo_appian.egg-info/SOURCES.txt
|
|
8
|
+
robo_appian.egg-info/dependency_links.txt
|
|
9
|
+
robo_appian.egg-info/requires.txt
|
|
10
|
+
robo_appian.egg-info/top_level.txt
|
|
11
|
+
robo_appian/utils/__init__.py
|
|
12
|
+
robo_appian/utils/components/ButtonUtils.py
|
|
13
|
+
robo_appian/utils/components/ComponentUtils.py
|
|
14
|
+
robo_appian/utils/components/DateUtils.py
|
|
15
|
+
robo_appian/utils/components/DropdownUtils.py
|
|
16
|
+
robo_appian/utils/components/InputUtils.py
|
|
17
|
+
robo_appian/utils/components/LabelUtils.py
|
|
18
|
+
robo_appian/utils/components/LinkUtils.py
|
|
19
|
+
robo_appian/utils/components/TabUtils.py
|
|
20
|
+
robo_appian/utils/components/TableUtils.py
|
|
21
|
+
robo_appian/utils/components/__init__.py
|
|
22
|
+
robo_appian/utils/controllers/ComponentDriver.py
|
|
23
|
+
robo_appian/utils/controllers/__init__.py
|
|
24
|
+
robo_appian/utils/exceptions/MyCustomError.py
|
|
25
|
+
robo_appian/utils/exceptions/__init__.py
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
selenium>=4.34.0
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
robo_appian
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
from setuptools import setup, find_packages
|
|
2
|
+
|
|
3
|
+
setup(
|
|
4
|
+
name='robo_appian',
|
|
5
|
+
version='0.0.1',
|
|
6
|
+
packages=find_packages(),
|
|
7
|
+
install_requires=[
|
|
8
|
+
'selenium>=4.34.0'
|
|
9
|
+
],
|
|
10
|
+
author='Dinil Mithra',
|
|
11
|
+
author_email='dinilmithra@mailme@gmail.com',
|
|
12
|
+
description='Automate your Appin code testing with Python. Boost quality, save time.',
|
|
13
|
+
long_description=open('README.md').read(),
|
|
14
|
+
long_description_content_type='text/markdown',
|
|
15
|
+
url='https://github.com/dinilmithra/robo_appian.git',
|
|
16
|
+
classifiers=[
|
|
17
|
+
'Programming Language :: Python :: 3',
|
|
18
|
+
# 'License :: MIT License',
|
|
19
|
+
'Operating System :: OS Independent',
|
|
20
|
+
],
|
|
21
|
+
python_requires='>=3.12',
|
|
22
|
+
)
|