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.
Files changed (76) hide show
  1. optics_framework/__init__.py +0 -0
  2. optics_framework/api/__init__.py +6 -0
  3. optics_framework/api/action_keyword.py +432 -0
  4. optics_framework/api/app_management.py +81 -0
  5. optics_framework/api/flow_control.py +402 -0
  6. optics_framework/api/verifier.py +261 -0
  7. optics_framework/common/__init__.py +0 -0
  8. optics_framework/common/base_factory.py +187 -0
  9. optics_framework/common/config_handler.py +214 -0
  10. optics_framework/common/driver_interface.py +225 -0
  11. optics_framework/common/elementsource_interface.py +54 -0
  12. optics_framework/common/execution.py +257 -0
  13. optics_framework/common/expose_api.py +235 -0
  14. optics_framework/common/factories.py +38 -0
  15. optics_framework/common/image_interface.py +60 -0
  16. optics_framework/common/logging_config.py +394 -0
  17. optics_framework/common/optics_builder.py +71 -0
  18. optics_framework/common/runner/__init__.py +0 -0
  19. optics_framework/common/runner/data_reader.py +281 -0
  20. optics_framework/common/runner/keyword_register.py +52 -0
  21. optics_framework/common/runner/printers.py +186 -0
  22. optics_framework/common/runner/test_runnner.py +452 -0
  23. optics_framework/common/session_manager.py +70 -0
  24. optics_framework/common/strategies.py +236 -0
  25. optics_framework/common/text_interface.py +59 -0
  26. optics_framework/common/utils.py +192 -0
  27. optics_framework/engines/__init__.py +0 -0
  28. optics_framework/engines/drivers/__init__.py +0 -0
  29. optics_framework/engines/drivers/appium.py +375 -0
  30. optics_framework/engines/drivers/appium_UI_helper.py +687 -0
  31. optics_framework/engines/drivers/appium_driver_manager.py +20 -0
  32. optics_framework/engines/drivers/ble.py +19 -0
  33. optics_framework/engines/drivers/selenium.py +157 -0
  34. optics_framework/engines/drivers/selenium_driver_manager.py +24 -0
  35. optics_framework/engines/elementsources/__init__.py +0 -0
  36. optics_framework/engines/elementsources/appium_find_element.py +142 -0
  37. optics_framework/engines/elementsources/appium_page_source.py +247 -0
  38. optics_framework/engines/elementsources/camera_screenshot.py +60 -0
  39. optics_framework/engines/elementsources/device_screenshot.py +93 -0
  40. optics_framework/engines/elementsources/selenium_find_element.py +150 -0
  41. optics_framework/engines/elementsources/selenium_screenshot.py +119 -0
  42. optics_framework/engines/vision_models/__init__.py +0 -0
  43. optics_framework/engines/vision_models/image_models/__init__.py +0 -0
  44. optics_framework/engines/vision_models/image_models/remote_oir.py +178 -0
  45. optics_framework/engines/vision_models/image_models/templatematch.py +232 -0
  46. optics_framework/engines/vision_models/ocr_models/__init__.py +0 -0
  47. optics_framework/engines/vision_models/ocr_models/easyocr.py +104 -0
  48. optics_framework/engines/vision_models/ocr_models/googlevision.py +124 -0
  49. optics_framework/engines/vision_models/ocr_models/pytesseract.py +102 -0
  50. optics_framework/engines/vision_models/ocr_models/remote_ocr.py +179 -0
  51. optics_framework/helper/__init__.py +0 -0
  52. optics_framework/helper/cli.py +284 -0
  53. optics_framework/helper/config_manager.py +206 -0
  54. optics_framework/helper/execute.py +370 -0
  55. optics_framework/helper/generate.py +207 -0
  56. optics_framework/helper/initialize.py +105 -0
  57. optics_framework/helper/list_keyword.py +60 -0
  58. optics_framework/helper/setup.py +139 -0
  59. optics_framework/helper/version.py +1 -0
  60. optics_framework/optics.py +163 -0
  61. optics_framework/samples/contact/config.yaml +37 -0
  62. optics_framework/samples/contact/elements.csv +17 -0
  63. optics_framework/samples/contact/test_cases.csv +6 -0
  64. optics_framework/samples/contact/test_modules.csv +15 -0
  65. optics_framework/samples/youtube/config.yaml +64 -0
  66. optics_framework/samples/youtube/elements.csv +12 -0
  67. optics_framework/samples/youtube/input_templates/home.png +0 -0
  68. optics_framework/samples/youtube/input_templates/sub.jpeg +0 -0
  69. optics_framework/samples/youtube/input_templates/youtube.jpeg +0 -0
  70. optics_framework/samples/youtube/test_cases.csv +3 -0
  71. optics_framework/samples/youtube/test_modules.csv +18 -0
  72. optics_framework-1.0.0.dist-info/LICENSE +202 -0
  73. optics_framework-1.0.0.dist-info/METADATA +482 -0
  74. optics_framework-1.0.0.dist-info/RECORD +76 -0
  75. optics_framework-1.0.0.dist-info/WHEEL +4 -0
  76. optics_framework-1.0.0.dist-info/entry_points.txt +3 -0
File without changes
@@ -0,0 +1,6 @@
1
+ from .action_keyword import ActionKeyword
2
+ from .app_management import AppManagement
3
+ from .flow_control import FlowControl
4
+ from .verifier import Verifier
5
+
6
+ __all__ = ["ActionKeyword", "AppManagement", "FlowControl", "Verifier"]
@@ -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()