optics-framework 1.0.0__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.
- optics_framework/__init__.py +0 -0
- optics_framework/api/__init__.py +6 -0
- optics_framework/api/action_keyword.py +432 -0
- optics_framework/api/app_management.py +81 -0
- optics_framework/api/flow_control.py +402 -0
- optics_framework/api/verifier.py +261 -0
- optics_framework/common/__init__.py +0 -0
- optics_framework/common/base_factory.py +187 -0
- optics_framework/common/config_handler.py +214 -0
- optics_framework/common/driver_interface.py +225 -0
- optics_framework/common/elementsource_interface.py +54 -0
- optics_framework/common/execution.py +257 -0
- optics_framework/common/expose_api.py +235 -0
- optics_framework/common/factories.py +38 -0
- optics_framework/common/image_interface.py +60 -0
- optics_framework/common/logging_config.py +394 -0
- optics_framework/common/optics_builder.py +71 -0
- optics_framework/common/runner/__init__.py +0 -0
- optics_framework/common/runner/data_reader.py +281 -0
- optics_framework/common/runner/keyword_register.py +52 -0
- optics_framework/common/runner/printers.py +186 -0
- optics_framework/common/runner/test_runnner.py +452 -0
- optics_framework/common/session_manager.py +70 -0
- optics_framework/common/strategies.py +236 -0
- optics_framework/common/text_interface.py +59 -0
- optics_framework/common/utils.py +192 -0
- optics_framework/engines/__init__.py +0 -0
- optics_framework/engines/drivers/__init__.py +0 -0
- optics_framework/engines/drivers/appium.py +375 -0
- optics_framework/engines/drivers/appium_UI_helper.py +687 -0
- optics_framework/engines/drivers/appium_driver_manager.py +20 -0
- optics_framework/engines/drivers/ble.py +19 -0
- optics_framework/engines/drivers/selenium.py +157 -0
- optics_framework/engines/drivers/selenium_driver_manager.py +24 -0
- optics_framework/engines/elementsources/__init__.py +0 -0
- optics_framework/engines/elementsources/appium_find_element.py +142 -0
- optics_framework/engines/elementsources/appium_page_source.py +247 -0
- optics_framework/engines/elementsources/camera_screenshot.py +60 -0
- optics_framework/engines/elementsources/device_screenshot.py +93 -0
- optics_framework/engines/elementsources/selenium_find_element.py +150 -0
- optics_framework/engines/elementsources/selenium_screenshot.py +119 -0
- optics_framework/engines/vision_models/__init__.py +0 -0
- optics_framework/engines/vision_models/image_models/__init__.py +0 -0
- optics_framework/engines/vision_models/image_models/remote_oir.py +178 -0
- optics_framework/engines/vision_models/image_models/templatematch.py +232 -0
- optics_framework/engines/vision_models/ocr_models/__init__.py +0 -0
- optics_framework/engines/vision_models/ocr_models/easyocr.py +104 -0
- optics_framework/engines/vision_models/ocr_models/googlevision.py +124 -0
- optics_framework/engines/vision_models/ocr_models/pytesseract.py +102 -0
- optics_framework/engines/vision_models/ocr_models/remote_ocr.py +179 -0
- optics_framework/helper/__init__.py +0 -0
- optics_framework/helper/cli.py +284 -0
- optics_framework/helper/config_manager.py +206 -0
- optics_framework/helper/execute.py +370 -0
- optics_framework/helper/generate.py +207 -0
- optics_framework/helper/initialize.py +105 -0
- optics_framework/helper/list_keyword.py +60 -0
- optics_framework/helper/setup.py +139 -0
- optics_framework/helper/version.py +1 -0
- optics_framework/optics.py +163 -0
- optics_framework/samples/contact/config.yaml +37 -0
- optics_framework/samples/contact/elements.csv +17 -0
- optics_framework/samples/contact/test_cases.csv +6 -0
- optics_framework/samples/contact/test_modules.csv +15 -0
- optics_framework/samples/youtube/config.yaml +64 -0
- optics_framework/samples/youtube/elements.csv +12 -0
- optics_framework/samples/youtube/input_templates/home.png +0 -0
- optics_framework/samples/youtube/input_templates/sub.jpeg +0 -0
- optics_framework/samples/youtube/input_templates/youtube.jpeg +0 -0
- optics_framework/samples/youtube/test_cases.csv +3 -0
- optics_framework/samples/youtube/test_modules.csv +18 -0
- optics_framework-1.0.0.dist-info/LICENSE +202 -0
- optics_framework-1.0.0.dist-info/METADATA +482 -0
- optics_framework-1.0.0.dist-info/RECORD +76 -0
- optics_framework-1.0.0.dist-info/WHEEL +4 -0
- optics_framework-1.0.0.dist-info/entry_points.txt +3 -0
|
File without changes
|
|
@@ -0,0 +1,432 @@
|
|
|
1
|
+
from functools import wraps
|
|
2
|
+
from typing import Callable, Optional, Any
|
|
3
|
+
from optics_framework.common.logging_config import internal_logger
|
|
4
|
+
from optics_framework.common.optics_builder import OpticsBuilder
|
|
5
|
+
from optics_framework.common.strategies import StrategyManager
|
|
6
|
+
from optics_framework.common import utils
|
|
7
|
+
from .verifier import Verifier
|
|
8
|
+
import time
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
# Action Executor Decorator
|
|
12
|
+
def with_self_healing(func: Callable) -> Callable:
|
|
13
|
+
@wraps(func)
|
|
14
|
+
def wrapper(self, element, *args, **kwargs):
|
|
15
|
+
screenshot_np = self.strategy_manager.capture_screenshot()
|
|
16
|
+
utils.save_screenshot(screenshot_np, func.__name__)
|
|
17
|
+
|
|
18
|
+
results = self.strategy_manager.locate(element)
|
|
19
|
+
|
|
20
|
+
last_exception = None
|
|
21
|
+
for result in results:
|
|
22
|
+
try:
|
|
23
|
+
return func(self, element, located=result.value, *args, **kwargs)
|
|
24
|
+
except Exception as e:
|
|
25
|
+
internal_logger.error(
|
|
26
|
+
f"Action '{func.__name__}' failed with {result.strategy.__class__.__name__}: {e}")
|
|
27
|
+
last_exception = e
|
|
28
|
+
|
|
29
|
+
if last_exception:
|
|
30
|
+
raise ValueError(
|
|
31
|
+
f"All strategies failed for '{element}' in '{func.__name__}': {last_exception}")
|
|
32
|
+
return wrapper
|
|
33
|
+
|
|
34
|
+
|
|
35
|
+
class ActionKeyword:
|
|
36
|
+
"""
|
|
37
|
+
High-Level API for Action Keywords
|
|
38
|
+
|
|
39
|
+
This class provides functionality for managing action keywords related to applications,
|
|
40
|
+
including pressing elements, scrolling, swiping, and text input.
|
|
41
|
+
"""
|
|
42
|
+
SCREENSHOT_DISABLED_MSG = "Screenshot taking is disabled, not possible to locate element."
|
|
43
|
+
XPAHT_NOT_SUPPORTED_MSG = "XPath is not supported for vision based search."
|
|
44
|
+
|
|
45
|
+
def __init__(self, builder: OpticsBuilder):
|
|
46
|
+
self.driver = builder.get_driver()
|
|
47
|
+
self.element_source = builder.get_element_source()
|
|
48
|
+
self.image_detection = builder.get_image_detection()
|
|
49
|
+
self.text_detection = builder.get_text_detection()
|
|
50
|
+
self.verifier = Verifier(builder)
|
|
51
|
+
self.strategy_manager = StrategyManager(
|
|
52
|
+
self.element_source, self.text_detection, self.image_detection)
|
|
53
|
+
|
|
54
|
+
# Click actions
|
|
55
|
+
@with_self_healing
|
|
56
|
+
def press_element(
|
|
57
|
+
self, element: str, repeat: int = 1, offset_x: int = 0, offset_y: int = 0, event_name: Optional[str] = None, *, located: Any
|
|
58
|
+
) -> None:
|
|
59
|
+
"""
|
|
60
|
+
Press a specified element.
|
|
61
|
+
|
|
62
|
+
:param element: The element to be pressed (text, xpath or image).
|
|
63
|
+
:param repeat: Number of times to repeat the press.
|
|
64
|
+
:param offset_x: X offset of the press.
|
|
65
|
+
:param offset_y: Y offset of the press.
|
|
66
|
+
:param event_name: The event triggering the press.
|
|
67
|
+
"""
|
|
68
|
+
if isinstance(located, tuple):
|
|
69
|
+
x, y = located
|
|
70
|
+
internal_logger.debug(
|
|
71
|
+
f"Pressing at coordinates ({x + offset_x}, {y + offset_y})")
|
|
72
|
+
self.driver.press_coordinates(
|
|
73
|
+
x + offset_x, y + offset_y, event_name)
|
|
74
|
+
else:
|
|
75
|
+
internal_logger.debug(f"Pressing element '{element}'")
|
|
76
|
+
self.driver.press_element(located, repeat, event_name)
|
|
77
|
+
|
|
78
|
+
def press_by_percentage(self, percent_x: float, percent_y: float, repeat: int = 1, event_name: Optional[str] = None) -> None:
|
|
79
|
+
"""
|
|
80
|
+
Press an element by percentage coordinates.
|
|
81
|
+
|
|
82
|
+
:param percent_x: X percentage of the press.
|
|
83
|
+
:param percent_y: Y percentage of the press.
|
|
84
|
+
:param repeat: Number of times to repeat the press.
|
|
85
|
+
:param event_name: The event triggering the press.
|
|
86
|
+
"""
|
|
87
|
+
screenshot_np = self.strategy_manager.capture_screenshot()
|
|
88
|
+
utils.save_screenshot(screenshot_np, "press_by_percentage")
|
|
89
|
+
element_source_type = type(
|
|
90
|
+
self.element_source.current_instance).__name__
|
|
91
|
+
if 'appium' in element_source_type.lower():
|
|
92
|
+
self.driver.press_percentage_coordinates(
|
|
93
|
+
percent_x, percent_y, repeat, event_name)
|
|
94
|
+
else:
|
|
95
|
+
# TODO: read device's screen specs from config
|
|
96
|
+
# DUMMY IMPLEMENTATION
|
|
97
|
+
screen_width = 1920
|
|
98
|
+
screen_height = 1080
|
|
99
|
+
x_coor = int(screen_width * percent_x)
|
|
100
|
+
y_coor = int(screen_height * percent_y)
|
|
101
|
+
self.driver.press_coordinates(x_coor, y_coor, event_name)
|
|
102
|
+
|
|
103
|
+
def press_by_coordinates(self, coor_x: int, coor_y: int, repeat: int = 1, event_name: Optional[str] = None) -> None:
|
|
104
|
+
"""
|
|
105
|
+
Press an element by absolute coordinates.
|
|
106
|
+
|
|
107
|
+
:param coor_x: X coordinate of the press.
|
|
108
|
+
:param coor_y: Y coordinate of the press.
|
|
109
|
+
:param repeat: Number of times to repeat the press.
|
|
110
|
+
:param event_name: The event triggering the press.
|
|
111
|
+
"""
|
|
112
|
+
screenshot_np = self.strategy_manager.capture_screenshot()
|
|
113
|
+
utils.save_screenshot(screenshot_np, "press_by_coordinates")
|
|
114
|
+
self.driver.press_coordinates(coor_x, coor_y, event_name)
|
|
115
|
+
|
|
116
|
+
def press_element_with_index(self, element: str, index: int = 0, event_name: Optional[str] = None) -> None:
|
|
117
|
+
"""
|
|
118
|
+
Press a specified text at a given index.
|
|
119
|
+
|
|
120
|
+
:param element: The text or image to be pressed.
|
|
121
|
+
:param index: The index of the element.
|
|
122
|
+
:param event_name: The event triggering the press.
|
|
123
|
+
"""
|
|
124
|
+
index = int(index)
|
|
125
|
+
screenshot_np = self.strategy_manager.capture_screenshot()
|
|
126
|
+
utils.save_screenshot(screenshot_np, "press_element_with_index")
|
|
127
|
+
element_source_type = type(
|
|
128
|
+
self.element_source.current_instance).__name__
|
|
129
|
+
element_type = utils.determine_element_type(element)
|
|
130
|
+
if element_type == 'Text':
|
|
131
|
+
if element_source_type == 'AppiumFindElement':
|
|
132
|
+
internal_logger.exception(
|
|
133
|
+
'Appium Find Element does not support finding text by index.')
|
|
134
|
+
elif element_source_type == 'AppiumPageSource':
|
|
135
|
+
appium_element = self.element_source.locate(
|
|
136
|
+
element, index)
|
|
137
|
+
self.driver.press_element(
|
|
138
|
+
appium_element, repeat=1, event_name=event_name)
|
|
139
|
+
else:
|
|
140
|
+
if 'screenshot' not in element_source_type.lower():
|
|
141
|
+
internal_logger.error(self.SCREENSHOT_DISABLED_MSG)
|
|
142
|
+
screenshot_image = self.element_source.capture()
|
|
143
|
+
x_coor, y_coor = self.text_detection.locate(
|
|
144
|
+
screenshot_image, element, index)
|
|
145
|
+
self.driver.press_coordinates(
|
|
146
|
+
x_coor, y_coor, event_name=event_name)
|
|
147
|
+
elif element_type == 'Image':
|
|
148
|
+
if 'screenshot' not in element_source_type.lower():
|
|
149
|
+
internal_logger.error(self.SCREENSHOT_DISABLED_MSG)
|
|
150
|
+
screenshot_image = self.element_source.capture()
|
|
151
|
+
x_coor, y_coor = self.image_detection.locate(
|
|
152
|
+
screenshot_image, element, index)
|
|
153
|
+
self.driver.press_coordinates(
|
|
154
|
+
x_coor, y_coor, event_name=event_name)
|
|
155
|
+
elif element_type == 'XPath':
|
|
156
|
+
internal_logger.debug(
|
|
157
|
+
'XPath is not supported for index based location. Provide the attribute as text.')
|
|
158
|
+
|
|
159
|
+
@with_self_healing
|
|
160
|
+
def detect_and_press(self, element: str, timeout: int, event_name: Optional[str] = None, *, located: Any) -> None:
|
|
161
|
+
"""
|
|
162
|
+
Detect and press a specified element.
|
|
163
|
+
|
|
164
|
+
:param element: The element to be detected and pressed (Image template, OCR template, or XPath).
|
|
165
|
+
:param timeout: Timeout for the detection operation.
|
|
166
|
+
:param event_name: The event triggering the press.
|
|
167
|
+
"""
|
|
168
|
+
screenshot_np = self.strategy_manager.capture_screenshot()
|
|
169
|
+
utils.save_screenshot(screenshot_np, "detect_and_press")
|
|
170
|
+
result = self.verifier.assert_presence(
|
|
171
|
+
element, timeout=timeout, rule="any")
|
|
172
|
+
if result:
|
|
173
|
+
if isinstance(located, tuple):
|
|
174
|
+
x, y = located
|
|
175
|
+
internal_logger.debug(
|
|
176
|
+
f"Pressing detected element at coordinates ({x}, {y})")
|
|
177
|
+
self.driver.press_coordinates(
|
|
178
|
+
x, y, event_name=event_name)
|
|
179
|
+
else:
|
|
180
|
+
internal_logger.debug(f"Pressing detected element '{element}'")
|
|
181
|
+
self.driver.press_element(
|
|
182
|
+
located, repeat=1, event_name=event_name)
|
|
183
|
+
|
|
184
|
+
@DeprecationWarning
|
|
185
|
+
@with_self_healing
|
|
186
|
+
def press_checkbox(self, element: str, event_name: Optional[str] = None, *, located: Any) -> None:
|
|
187
|
+
"""
|
|
188
|
+
Press a specified checkbox element.
|
|
189
|
+
|
|
190
|
+
:param element: The checkbox element (Image template, OCR template, or XPath).
|
|
191
|
+
:param event_name: The event triggering the press.
|
|
192
|
+
"""
|
|
193
|
+
self.press_element(element, event_name=event_name, located=located)
|
|
194
|
+
|
|
195
|
+
@DeprecationWarning
|
|
196
|
+
@with_self_healing
|
|
197
|
+
def press_radio_button(self, element: str, event_name: Optional[str] = None, *, located: Any) -> None:
|
|
198
|
+
"""
|
|
199
|
+
Press a specified radio button.
|
|
200
|
+
|
|
201
|
+
:param element: The radio button element (Image template, OCR template, or XPath).
|
|
202
|
+
:param event_name: The event triggering the press.
|
|
203
|
+
"""
|
|
204
|
+
self.press_element(element, event_name=event_name, located=located)
|
|
205
|
+
|
|
206
|
+
def select_dropdown_option(self, element: str, option: str, event_name: Optional[str] = None) -> None:
|
|
207
|
+
"""
|
|
208
|
+
Select a specified dropdown option.
|
|
209
|
+
|
|
210
|
+
:param element: The dropdown element (Image template, OCR template, or XPath).
|
|
211
|
+
:param option: The option to be selected.
|
|
212
|
+
:param event_name: The event triggering the selection.
|
|
213
|
+
"""
|
|
214
|
+
pass
|
|
215
|
+
|
|
216
|
+
# Swipe and Scroll actions
|
|
217
|
+
def swipe(self, coor_x: int, coor_y: int, direction: str = 'right', swipe_length: int = 50, event_name: Optional[str] = None) -> None:
|
|
218
|
+
"""
|
|
219
|
+
Perform a swipe action in a specified direction.
|
|
220
|
+
|
|
221
|
+
:param coor_x: X coordinate of the swipe.
|
|
222
|
+
:param coor_y: Y coordinate of the swipe.
|
|
223
|
+
:param direction: The swipe direction (up, down, left, right).
|
|
224
|
+
:param swipe_length: The length of the swipe.
|
|
225
|
+
:param event_name: The event triggering the swipe.
|
|
226
|
+
"""
|
|
227
|
+
screenshot_np = self.strategy_manager.capture_screenshot()
|
|
228
|
+
utils.save_screenshot(screenshot_np, "swipe")
|
|
229
|
+
self.driver.swipe(coor_x, coor_y, direction, swipe_length, event_name)
|
|
230
|
+
|
|
231
|
+
@DeprecationWarning
|
|
232
|
+
def swipe_seekbar_to_right_android(self, element: str, event_name: Optional[str] = None) -> None:
|
|
233
|
+
"""
|
|
234
|
+
Swipe a seekbar to the right.
|
|
235
|
+
|
|
236
|
+
:param element: The seekbar element (Image template, OCR template, or XPath).
|
|
237
|
+
"""
|
|
238
|
+
screenshot_np = self.strategy_manager.capture_screenshot()
|
|
239
|
+
utils.save_screenshot(screenshot_np, "swipe_seekbar_to_right_android")
|
|
240
|
+
self.driver.swipe_element(element, 'right', 50, event_name)
|
|
241
|
+
|
|
242
|
+
def swipe_until_element_appears(self, element: str, direction: str, timeout: int, event_name: Optional[str] = None) -> None:
|
|
243
|
+
"""
|
|
244
|
+
Swipe in a specified direction until an element appears.
|
|
245
|
+
|
|
246
|
+
:param element: The target element (Image template, OCR template, or XPath).
|
|
247
|
+
:param direction: The swipe direction (up, down, left, right).
|
|
248
|
+
:param timeout: Timeout until element search is performed.
|
|
249
|
+
:param event_name: The event triggering the swipe.
|
|
250
|
+
"""
|
|
251
|
+
screenshot_np = self.strategy_manager.capture_screenshot()
|
|
252
|
+
utils.save_screenshot(screenshot_np, "swipe_until_element_appears")
|
|
253
|
+
start_time = time.time()
|
|
254
|
+
while time.time() - start_time < int(timeout):
|
|
255
|
+
result = self.verifier.assert_presence(
|
|
256
|
+
element, timeout=3, rule="any")
|
|
257
|
+
if result:
|
|
258
|
+
break
|
|
259
|
+
self.driver.swipe_percentage(10, 50, direction, 25, event_name)
|
|
260
|
+
time.sleep(3)
|
|
261
|
+
|
|
262
|
+
@with_self_healing
|
|
263
|
+
def swipe_from_element(self, element: str, direction: str, swipe_length: int, event_name: Optional[str] = None, *, located: Any) -> None:
|
|
264
|
+
"""
|
|
265
|
+
Perform a swipe action starting from a specified element.
|
|
266
|
+
|
|
267
|
+
:param element: The element to swipe from (Image template, OCR template, or XPath).
|
|
268
|
+
:param direction: The swipe direction (up, down, left, right).
|
|
269
|
+
:param swipe_length: The length of the swipe.
|
|
270
|
+
:param event_name: The event triggering the swipe.
|
|
271
|
+
"""
|
|
272
|
+
if isinstance(located, tuple):
|
|
273
|
+
x, y = located
|
|
274
|
+
internal_logger.debug(f"Swiping from coordinates ({x}, {y})")
|
|
275
|
+
self.driver.swipe(x, y, direction, swipe_length, event_name)
|
|
276
|
+
else:
|
|
277
|
+
internal_logger.debug(f"Swiping from element '{element}'")
|
|
278
|
+
self.driver.swipe_element(
|
|
279
|
+
located, direction, swipe_length, event_name)
|
|
280
|
+
|
|
281
|
+
def scroll(self, direction: str, event_name: Optional[str] = None) -> None:
|
|
282
|
+
"""
|
|
283
|
+
Perform a scroll action in a specified direction.
|
|
284
|
+
|
|
285
|
+
:param direction: The scroll direction (up, down, left, right).
|
|
286
|
+
:param event_name: The event triggering the scroll.
|
|
287
|
+
"""
|
|
288
|
+
screenshot_np = self.strategy_manager.capture_screenshot()
|
|
289
|
+
utils.save_screenshot(screenshot_np, "scroll")
|
|
290
|
+
self.driver.scroll(direction, 1000, event_name)
|
|
291
|
+
|
|
292
|
+
@with_self_healing
|
|
293
|
+
def scroll_until_element_appears(self, element: str, direction: str, timeout: int, event_name: Optional[str] = None, *, located: Any) -> None:
|
|
294
|
+
"""
|
|
295
|
+
Scroll in a specified direction until an element appears.
|
|
296
|
+
|
|
297
|
+
:param element: The target element (Image template, OCR template, or XPath).
|
|
298
|
+
:param direction: The scroll direction (up, down, left, right).
|
|
299
|
+
:param timeout: Timeout for the scroll operation.
|
|
300
|
+
:param event_name: The event triggering the scroll.
|
|
301
|
+
"""
|
|
302
|
+
screenshot_np = self.strategy_manager.capture_screenshot()
|
|
303
|
+
utils.save_screenshot(screenshot_np, "scroll_until_element_appears")
|
|
304
|
+
start_time = time.time()
|
|
305
|
+
while time.time() - start_time < int(timeout):
|
|
306
|
+
result = self.verifier.assert_presence(
|
|
307
|
+
element, timeout=3, rule="any")
|
|
308
|
+
if result:
|
|
309
|
+
break
|
|
310
|
+
self.driver.scroll(direction, 1000, event_name)
|
|
311
|
+
time.sleep(3)
|
|
312
|
+
|
|
313
|
+
@with_self_healing
|
|
314
|
+
def scroll_from_element(self, element: str, direction: str, scroll_length: int, event_name: Optional[str] = None, *, located: Any) -> None:
|
|
315
|
+
"""
|
|
316
|
+
Perform a scroll action starting from a specified element.
|
|
317
|
+
|
|
318
|
+
:param element: The element to scroll from (Image template, OCR template, or XPath).
|
|
319
|
+
:param direction: The scroll direction (up, down, left, right).
|
|
320
|
+
:param scroll_length: The length of the scroll.
|
|
321
|
+
:param event_name: The event triggering the scroll.
|
|
322
|
+
"""
|
|
323
|
+
screenshot_np = self.strategy_manager.capture_screenshot()
|
|
324
|
+
utils.save_screenshot(screenshot_np, "scroll_from_element")
|
|
325
|
+
self.swipe_from_element(
|
|
326
|
+
element, direction, scroll_length, event_name, located=located)
|
|
327
|
+
|
|
328
|
+
# Text input actions
|
|
329
|
+
@with_self_healing
|
|
330
|
+
def enter_text(self, element: str, text: str, event_name: Optional[str] = None, *, located: Any) -> None:
|
|
331
|
+
"""
|
|
332
|
+
Enter text into a specified element.
|
|
333
|
+
|
|
334
|
+
:param element: The target element (Image template, OCR template, or XPath).
|
|
335
|
+
:param text: The text to be entered.
|
|
336
|
+
:param event_name: The event triggering the input.
|
|
337
|
+
"""
|
|
338
|
+
if isinstance(located, tuple):
|
|
339
|
+
x, y = located
|
|
340
|
+
internal_logger.debug(f"Entering text '{text}' at coordinates ({x}, {y})")
|
|
341
|
+
self.driver.press_coordinates(x, y, event_name=event_name)
|
|
342
|
+
self.driver.enter_text(text, event_name)
|
|
343
|
+
else:
|
|
344
|
+
internal_logger.debug(f"Entering text '{text}' into element '{element}'")
|
|
345
|
+
self.driver.enter_text_element(located, text, event_name)
|
|
346
|
+
|
|
347
|
+
@DeprecationWarning
|
|
348
|
+
def enter_text_using_keyboard_android(self, text: str, event_name: Optional[str] = None) -> None:
|
|
349
|
+
"""
|
|
350
|
+
Enter text using the keyboard.
|
|
351
|
+
|
|
352
|
+
:param text: The text to be entered.
|
|
353
|
+
:param event_name: The event triggering the input.
|
|
354
|
+
"""
|
|
355
|
+
screenshot_np = self.strategy_manager.capture_screenshot()
|
|
356
|
+
utils.save_screenshot(screenshot_np, "enter_text_using_keyboard_android")
|
|
357
|
+
self.driver.enter_text_using_keyboard(text, event_name)
|
|
358
|
+
|
|
359
|
+
@with_self_healing
|
|
360
|
+
def enter_number(self, element: str, number: float, event_name: Optional[str] = None, *, located: Any) -> None:
|
|
361
|
+
"""
|
|
362
|
+
Enter a specified number into an element.
|
|
363
|
+
|
|
364
|
+
:param element: The target element (Image template, OCR template, or XPath).
|
|
365
|
+
:param number: The number to be entered.
|
|
366
|
+
:param event_name: The event triggering the input.
|
|
367
|
+
"""
|
|
368
|
+
screenshot_np = self.strategy_manager.capture_screenshot()
|
|
369
|
+
utils.save_screenshot(screenshot_np, "enter_number")
|
|
370
|
+
self.enter_text(element, str(number), event_name, located=located)
|
|
371
|
+
|
|
372
|
+
def press_keycode(self, keycode: int, event_name: str) -> None:
|
|
373
|
+
"""
|
|
374
|
+
Press a specified keycode.
|
|
375
|
+
|
|
376
|
+
:param keycode: The keycode to be pressed.
|
|
377
|
+
:param event_name: The event triggering the press.
|
|
378
|
+
"""
|
|
379
|
+
screenshot_np = self.strategy_manager.capture_screenshot()
|
|
380
|
+
utils.save_screenshot(screenshot_np, "press_keycode")
|
|
381
|
+
self.driver.press_keycode(keycode, event_name)
|
|
382
|
+
|
|
383
|
+
@with_self_healing
|
|
384
|
+
def clear_element_text(self, element: str, event_name: Optional[str] = None, *, located: Any) -> None:
|
|
385
|
+
"""
|
|
386
|
+
Clear text from a specified element.
|
|
387
|
+
|
|
388
|
+
:param element: The target element (Image template, OCR template, or XPath).
|
|
389
|
+
:param event_name: The event triggering the action.
|
|
390
|
+
"""
|
|
391
|
+
if isinstance(located, tuple):
|
|
392
|
+
x, y = located
|
|
393
|
+
internal_logger.debug(f"Clearing text at coordinates ({x}, {y})")
|
|
394
|
+
self.driver.press_coordinates(
|
|
395
|
+
x, y, event_name=event_name)
|
|
396
|
+
self.driver.clear_text(event_name)
|
|
397
|
+
else:
|
|
398
|
+
internal_logger.debug(f"Clearing text from element '{element}'")
|
|
399
|
+
self.driver.clear_text_element(located, event_name)
|
|
400
|
+
|
|
401
|
+
def get_text(self, element: str) -> Optional[str]:
|
|
402
|
+
"""
|
|
403
|
+
Get the text from a specified element.
|
|
404
|
+
|
|
405
|
+
:param element: The target element (Image template, OCR template, or XPath).
|
|
406
|
+
:return: The text from the element or None if not supported.
|
|
407
|
+
"""
|
|
408
|
+
screenshot_np = self.strategy_manager.capture_screenshot()
|
|
409
|
+
utils.save_screenshot(screenshot_np, "get_text")
|
|
410
|
+
element_source_type = type(
|
|
411
|
+
self.element_source.current_instance).__name__
|
|
412
|
+
element_type = utils.determine_element_type(element)
|
|
413
|
+
if element_type in ["Text", "XPath"]:
|
|
414
|
+
if 'appium' in element_source_type.lower():
|
|
415
|
+
element = self.element_source.locate(element)
|
|
416
|
+
return self.driver.get_text_element(element)
|
|
417
|
+
else:
|
|
418
|
+
internal_logger.error(
|
|
419
|
+
'Get Text is not supported for vision based search yet.')
|
|
420
|
+
return None
|
|
421
|
+
else:
|
|
422
|
+
internal_logger.error(
|
|
423
|
+
'Get Text is not supported for image based search yet.')
|
|
424
|
+
return None
|
|
425
|
+
|
|
426
|
+
def sleep(self, duration: int) -> None:
|
|
427
|
+
"""
|
|
428
|
+
Sleep for a specified duration.
|
|
429
|
+
|
|
430
|
+
:param duration: The duration of the sleep in seconds.
|
|
431
|
+
"""
|
|
432
|
+
time.sleep(int(duration))
|
|
@@ -0,0 +1,81 @@
|
|
|
1
|
+
from typing import Optional
|
|
2
|
+
from optics_framework.common.logging_config import internal_logger
|
|
3
|
+
from optics_framework.common.optics_builder import OpticsBuilder
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
class AppManagement:
|
|
7
|
+
"""
|
|
8
|
+
A high-level API for managing applications.
|
|
9
|
+
|
|
10
|
+
This class provides functionality for launching, terminating,
|
|
11
|
+
and modifying app settings.
|
|
12
|
+
|
|
13
|
+
Attributes:
|
|
14
|
+
driver (object): The driver instance for managing applications.
|
|
15
|
+
"""
|
|
16
|
+
|
|
17
|
+
def __init__(self, builder: OpticsBuilder):
|
|
18
|
+
self.driver = builder.get_driver()
|
|
19
|
+
if self.driver is None:
|
|
20
|
+
internal_logger.error("Driver could not be initialized due to not being provided.")
|
|
21
|
+
# Optionally raise an exception if this should halt execution
|
|
22
|
+
# raise ValueError(f"Driver '{builder.driver_config}' could not be initialized.")
|
|
23
|
+
|
|
24
|
+
def initialise_setup(self) -> None:
|
|
25
|
+
"""
|
|
26
|
+
Sets up the environment for the driver module.
|
|
27
|
+
|
|
28
|
+
This method should be called before performing any application
|
|
29
|
+
management operations.
|
|
30
|
+
"""
|
|
31
|
+
internal_logger.debug("Initialising setup for AppManagement.")
|
|
32
|
+
|
|
33
|
+
def launch_app(self, event_name: Optional[str] = None) -> None:
|
|
34
|
+
"""
|
|
35
|
+
Launches the specified application.
|
|
36
|
+
|
|
37
|
+
:param event_name: The event triggering the app launch, if any.
|
|
38
|
+
"""
|
|
39
|
+
self.driver.launch_app(event_name)
|
|
40
|
+
|
|
41
|
+
def start_appium_session(self, event_name: Optional[str] = None) -> None:
|
|
42
|
+
"""
|
|
43
|
+
Starts an Appium session.
|
|
44
|
+
|
|
45
|
+
:param event_name: The event triggering the session start, if any.
|
|
46
|
+
"""
|
|
47
|
+
self.driver.launch_app(event_name)
|
|
48
|
+
|
|
49
|
+
def start_other_app(self, package_name: str, event_name: Optional[str] = None) -> None:
|
|
50
|
+
"""
|
|
51
|
+
Starts another application.
|
|
52
|
+
|
|
53
|
+
:param package_name: The package name of the application.
|
|
54
|
+
:param event_name: The event triggering the app start, if any.
|
|
55
|
+
"""
|
|
56
|
+
pass
|
|
57
|
+
|
|
58
|
+
def close_and_terminate_app(self, package_name: Optional[str] = None, event_name: Optional[str] = None) -> None:
|
|
59
|
+
"""
|
|
60
|
+
Closes and terminates a specified application.
|
|
61
|
+
|
|
62
|
+
:param package_name: The package name of the application.
|
|
63
|
+
:param event_name: The event triggering the app termination, if any.
|
|
64
|
+
"""
|
|
65
|
+
self.driver.terminate()
|
|
66
|
+
|
|
67
|
+
def force_terminate_app(self, event_name: Optional[str] = None) -> None:
|
|
68
|
+
"""
|
|
69
|
+
Forcefully terminates the specified application.
|
|
70
|
+
|
|
71
|
+
:param event_name: The event triggering the forced termination, if any.
|
|
72
|
+
"""
|
|
73
|
+
pass
|
|
74
|
+
|
|
75
|
+
def get_app_version(self) -> Optional[str]:
|
|
76
|
+
"""
|
|
77
|
+
Gets the version of the application.
|
|
78
|
+
|
|
79
|
+
:return: The version of the application, or None if not available.
|
|
80
|
+
"""
|
|
81
|
+
return self.driver.get_app_version()
|