AltTester-Driver 2.3.3__tar.gz → 2.4.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.
- {alttester_driver-2.3.3 → alttester_driver-2.4.1}/AltTester_Driver.egg-info/PKG-INFO +3 -1
- {alttester_driver-2.3.3 → alttester_driver-2.4.1}/AltTester_Driver.egg-info/SOURCES.txt +3 -0
- {alttester_driver-2.3.3 → alttester_driver-2.4.1}/PKG-INFO +3 -1
- {alttester_driver-2.3.3 → alttester_driver-2.4.1}/alttester/__version__.py +1 -1
- {alttester_driver-2.3.3 → alttester_driver-2.4.1}/alttester/_websocket.py +2 -2
- {alttester_driver-2.3.3 → alttester_driver-2.4.1}/alttester/altdriver.py +17 -4
- {alttester_driver-2.3.3 → alttester_driver-2.4.1}/setup.py +0 -1
- alttester_driver-2.4.1/tests/appium/conftest.py +113 -0
- alttester_driver-2.4.1/tests/appium/test_native_popup.py +106 -0
- {alttester_driver-2.3.3 → alttester_driver-2.4.1}/tests/integration/conftest.py +7 -8
- {alttester_driver-2.3.3 → alttester_driver-2.4.1}/tests/integration/test_driver.py +2 -2
- {alttester_driver-2.3.3 → alttester_driver-2.4.1}/tests/integration/test_scene01_part2.py +1 -0
- alttester_driver-2.4.1/tests/unit/__init__.py +3 -0
- {alttester_driver-2.3.3 → alttester_driver-2.4.1}/tests/unit/test_websocket.py +48 -0
- {alttester_driver-2.3.3 → alttester_driver-2.4.1}/AltTester_Driver.egg-info/dependency_links.txt +0 -0
- {alttester_driver-2.3.3 → alttester_driver-2.4.1}/AltTester_Driver.egg-info/not-zip-safe +0 -0
- {alttester_driver-2.3.3 → alttester_driver-2.4.1}/AltTester_Driver.egg-info/requires.txt +0 -0
- {alttester_driver-2.3.3 → alttester_driver-2.4.1}/AltTester_Driver.egg-info/top_level.txt +0 -0
- {alttester_driver-2.3.3 → alttester_driver-2.4.1}/LICENSE +0 -0
- {alttester_driver-2.3.3 → alttester_driver-2.4.1}/MANIFEST.in +0 -0
- {alttester_driver-2.3.3 → alttester_driver-2.4.1}/README.md +0 -0
- {alttester_driver-2.3.3 → alttester_driver-2.4.1}/alttester/__init__.py +0 -0
- {alttester_driver-2.3.3 → alttester_driver-2.4.1}/alttester/altobject.py +0 -0
- {alttester_driver-2.3.3 → alttester_driver-2.4.1}/alttester/by.py +0 -0
- {alttester_driver-2.3.3 → alttester_driver-2.4.1}/alttester/commands/AltTesterCommands/__init__.py +0 -0
- {alttester_driver-2.3.3 → alttester_driver-2.4.1}/alttester/commands/AltTesterCommands/add_notification_listener.py +0 -0
- {alttester_driver-2.3.3 → alttester_driver-2.4.1}/alttester/commands/AltTesterCommands/remove_notification_listener.py +0 -0
- {alttester_driver-2.3.3 → alttester_driver-2.4.1}/alttester/commands/AltTesterCommands/reset_input.py +0 -0
- {alttester_driver-2.3.3 → alttester_driver-2.4.1}/alttester/commands/AltTesterCommands/set_server_logging.py +0 -0
- {alttester_driver-2.3.3 → alttester_driver-2.4.1}/alttester/commands/FindObjects/__init__.py +0 -0
- {alttester_driver-2.3.3 → alttester_driver-2.4.1}/alttester/commands/FindObjects/find_object.py +0 -0
- {alttester_driver-2.3.3 → alttester_driver-2.4.1}/alttester/commands/FindObjects/find_object_at_coordinates.py +0 -0
- {alttester_driver-2.3.3 → alttester_driver-2.4.1}/alttester/commands/FindObjects/find_object_which_contains.py +0 -0
- {alttester_driver-2.3.3 → alttester_driver-2.4.1}/alttester/commands/FindObjects/find_objects.py +0 -0
- {alttester_driver-2.3.3 → alttester_driver-2.4.1}/alttester/commands/FindObjects/find_objects_which_contains.py +0 -0
- {alttester_driver-2.3.3 → alttester_driver-2.4.1}/alttester/commands/FindObjects/wait_for_component_property.py +0 -0
- {alttester_driver-2.3.3 → alttester_driver-2.4.1}/alttester/commands/FindObjects/wait_for_object.py +0 -0
- {alttester_driver-2.3.3 → alttester_driver-2.4.1}/alttester/commands/FindObjects/wait_for_object_to_not_be_present.py +0 -0
- {alttester_driver-2.3.3 → alttester_driver-2.4.1}/alttester/commands/FindObjects/wait_for_object_which_contains.py +0 -0
- {alttester_driver-2.3.3 → alttester_driver-2.4.1}/alttester/commands/FindObjects/wait_for_visual_element_property.py +0 -0
- {alttester_driver-2.3.3 → alttester_driver-2.4.1}/alttester/commands/InputActions/__init__.py +0 -0
- {alttester_driver-2.3.3 → alttester_driver-2.4.1}/alttester/commands/InputActions/begin_touch.py +0 -0
- {alttester_driver-2.3.3 → alttester_driver-2.4.1}/alttester/commands/InputActions/click_coordinates.py +0 -0
- {alttester_driver-2.3.3 → alttester_driver-2.4.1}/alttester/commands/InputActions/end_touch.py +0 -0
- {alttester_driver-2.3.3 → alttester_driver-2.4.1}/alttester/commands/InputActions/keys_down.py +0 -0
- {alttester_driver-2.3.3 → alttester_driver-2.4.1}/alttester/commands/InputActions/keys_up.py +0 -0
- {alttester_driver-2.3.3 → alttester_driver-2.4.1}/alttester/commands/InputActions/move_mouse.py +0 -0
- {alttester_driver-2.3.3 → alttester_driver-2.4.1}/alttester/commands/InputActions/move_touch.py +0 -0
- {alttester_driver-2.3.3 → alttester_driver-2.4.1}/alttester/commands/InputActions/multi_point_swipe.py +0 -0
- {alttester_driver-2.3.3 → alttester_driver-2.4.1}/alttester/commands/InputActions/press_keys.py +0 -0
- {alttester_driver-2.3.3 → alttester_driver-2.4.1}/alttester/commands/InputActions/scroll_mouse.py +0 -0
- {alttester_driver-2.3.3 → alttester_driver-2.4.1}/alttester/commands/InputActions/swipe.py +0 -0
- {alttester_driver-2.3.3 → alttester_driver-2.4.1}/alttester/commands/InputActions/tap_coordinates.py +0 -0
- {alttester_driver-2.3.3 → alttester_driver-2.4.1}/alttester/commands/InputActions/tilt.py +0 -0
- {alttester_driver-2.3.3 → alttester_driver-2.4.1}/alttester/commands/Notifications/__init__.py +0 -0
- {alttester_driver-2.3.3 → alttester_driver-2.4.1}/alttester/commands/Notifications/base_notification_callbacks.py +0 -0
- {alttester_driver-2.3.3 → alttester_driver-2.4.1}/alttester/commands/Notifications/load_scene_mode.py +0 -0
- {alttester_driver-2.3.3 → alttester_driver-2.4.1}/alttester/commands/Notifications/load_scene_notification_result.py +0 -0
- {alttester_driver-2.3.3 → alttester_driver-2.4.1}/alttester/commands/Notifications/log_notification_result.py +0 -0
- {alttester_driver-2.3.3 → alttester_driver-2.4.1}/alttester/commands/Notifications/notification_type.py +0 -0
- {alttester_driver-2.3.3 → alttester_driver-2.4.1}/alttester/commands/ObjectCommands/__init__.py +0 -0
- {alttester_driver-2.3.3 → alttester_driver-2.4.1}/alttester/commands/ObjectCommands/call_method.py +0 -0
- {alttester_driver-2.3.3 → alttester_driver-2.4.1}/alttester/commands/ObjectCommands/click_element.py +0 -0
- {alttester_driver-2.3.3 → alttester_driver-2.4.1}/alttester/commands/ObjectCommands/fetch_text.py +0 -0
- {alttester_driver-2.3.3 → alttester_driver-2.4.1}/alttester/commands/ObjectCommands/find_object_from_object.py +0 -0
- {alttester_driver-2.3.3 → alttester_driver-2.4.1}/alttester/commands/ObjectCommands/get_all_components.py +0 -0
- {alttester_driver-2.3.3 → alttester_driver-2.4.1}/alttester/commands/ObjectCommands/get_component_property.py +0 -0
- {alttester_driver-2.3.3 → alttester_driver-2.4.1}/alttester/commands/ObjectCommands/pointer_down.py +0 -0
- {alttester_driver-2.3.3 → alttester_driver-2.4.1}/alttester/commands/ObjectCommands/pointer_enter.py +0 -0
- {alttester_driver-2.3.3 → alttester_driver-2.4.1}/alttester/commands/ObjectCommands/pointer_exit.py +0 -0
- {alttester_driver-2.3.3 → alttester_driver-2.4.1}/alttester/commands/ObjectCommands/pointer_up.py +0 -0
- {alttester_driver-2.3.3 → alttester_driver-2.4.1}/alttester/commands/ObjectCommands/set_component_property.py +0 -0
- {alttester_driver-2.3.3 → alttester_driver-2.4.1}/alttester/commands/ObjectCommands/tap_element.py +0 -0
- {alttester_driver-2.3.3 → alttester_driver-2.4.1}/alttester/commands/ObjectCommands/update_text.py +0 -0
- {alttester_driver-2.3.3 → alttester_driver-2.4.1}/alttester/commands/UnityCommands/__init__.py +0 -0
- {alttester_driver-2.3.3 → alttester_driver-2.4.1}/alttester/commands/UnityCommands/delete_player_pref.py +0 -0
- {alttester_driver-2.3.3 → alttester_driver-2.4.1}/alttester/commands/UnityCommands/delete_player_pref_key.py +0 -0
- {alttester_driver-2.3.3 → alttester_driver-2.4.1}/alttester/commands/UnityCommands/get_all_loaded_scenes.py +0 -0
- {alttester_driver-2.3.3 → alttester_driver-2.4.1}/alttester/commands/UnityCommands/get_current_scene.py +0 -0
- {alttester_driver-2.3.3 → alttester_driver-2.4.1}/alttester/commands/UnityCommands/get_player_pref_key.py +0 -0
- {alttester_driver-2.3.3 → alttester_driver-2.4.1}/alttester/commands/UnityCommands/get_time_scale.py +0 -0
- {alttester_driver-2.3.3 → alttester_driver-2.4.1}/alttester/commands/UnityCommands/get_visual_element_property.py +0 -0
- {alttester_driver-2.3.3 → alttester_driver-2.4.1}/alttester/commands/UnityCommands/load_scene.py +0 -0
- {alttester_driver-2.3.3 → alttester_driver-2.4.1}/alttester/commands/UnityCommands/set_player_pref_key.py +0 -0
- {alttester_driver-2.3.3 → alttester_driver-2.4.1}/alttester/commands/UnityCommands/set_time_scale.py +0 -0
- {alttester_driver-2.3.3 → alttester_driver-2.4.1}/alttester/commands/UnityCommands/unload_scene.py +0 -0
- {alttester_driver-2.3.3 → alttester_driver-2.4.1}/alttester/commands/UnityCommands/wait_for_current_scene_to_be.py +0 -0
- {alttester_driver-2.3.3 → alttester_driver-2.4.1}/alttester/commands/__init__.py +0 -0
- {alttester_driver-2.3.3 → alttester_driver-2.4.1}/alttester/commands/base_command.py +0 -0
- {alttester_driver-2.3.3 → alttester_driver-2.4.1}/alttester/commands/get_png_screenshot.py +0 -0
- {alttester_driver-2.3.3 → alttester_driver-2.4.1}/alttester/commands/get_server_version.py +0 -0
- {alttester_driver-2.3.3 → alttester_driver-2.4.1}/alttester/commands/get_static_property.py +0 -0
- {alttester_driver-2.3.3 → alttester_driver-2.4.1}/alttester/commands/set_static_property.py +0 -0
- {alttester_driver-2.3.3 → alttester_driver-2.4.1}/alttester/exceptions.py +0 -0
- {alttester_driver-2.3.3 → alttester_driver-2.4.1}/alttester/keycode.py +0 -0
- {alttester_driver-2.3.3 → alttester_driver-2.4.1}/alttester/logging.py +0 -0
- {alttester_driver-2.3.3 → alttester_driver-2.4.1}/alttester/playerpref.py +0 -0
- {alttester_driver-2.3.3 → alttester_driver-2.4.1}/alttester/reverse_port_forwarding.py +0 -0
- {alttester_driver-2.3.3 → alttester_driver-2.4.1}/requirements-dev.txt +0 -0
- {alttester_driver-2.3.3 → alttester_driver-2.4.1}/requirements.txt +0 -0
- {alttester_driver-2.3.3 → alttester_driver-2.4.1}/setup.cfg +0 -0
- {alttester_driver-2.3.3/tests/integration → alttester_driver-2.4.1/tests/appium}/__init__.py +0 -0
- {alttester_driver-2.3.3/tests/unit → alttester_driver-2.4.1/tests/integration}/__init__.py +0 -0
- {alttester_driver-2.3.3 → alttester_driver-2.4.1}/tests/integration/test_notifications.py +0 -0
- {alttester_driver-2.3.3 → alttester_driver-2.4.1}/tests/integration/test_reverse_port_forwarding.py +0 -0
- {alttester_driver-2.3.3 → alttester_driver-2.4.1}/tests/integration/test_scene01_part1.py +0 -0
- {alttester_driver-2.3.3 → alttester_driver-2.4.1}/tests/integration/test_scene02.py +0 -0
- {alttester_driver-2.3.3 → alttester_driver-2.4.1}/tests/integration/test_scene03.py +0 -0
- {alttester_driver-2.3.3 → alttester_driver-2.4.1}/tests/integration/test_scene05.py +0 -0
- {alttester_driver-2.3.3 → alttester_driver-2.4.1}/tests/integration/test_scene07.py +0 -0
- {alttester_driver-2.3.3 → alttester_driver-2.4.1}/tests/integration/test_scene09.py +0 -0
- {alttester_driver-2.3.3 → alttester_driver-2.4.1}/tests/integration/test_scene10.py +0 -0
- {alttester_driver-2.3.3 → alttester_driver-2.4.1}/tests/integration/utils.py +0 -0
- {alttester_driver-2.3.3 → alttester_driver-2.4.1}/tests/unit/test_altobject.py +0 -0
- {alttester_driver-2.3.3 → alttester_driver-2.4.1}/tests/unit/test_commands.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: AltTester-Driver
|
|
3
|
-
Version: 2.
|
|
3
|
+
Version: 2.4.1
|
|
4
4
|
Summary: Python bindings for the AltTester® framework. AltTester® is an UI driven test automation tool that helps you find objects in your game and interacts with them.
|
|
5
5
|
Home-page: https://alttester.com/docs/sdk/latest/
|
|
6
6
|
Author: Altom Consulting
|
|
@@ -35,6 +35,7 @@ Classifier: Topic :: Software Development :: Testing :: Acceptance
|
|
|
35
35
|
Classifier: Topic :: Software Development :: Testing :: Unit
|
|
36
36
|
Requires-Python: >=3.4.0
|
|
37
37
|
Description-Content-Type: text/markdown
|
|
38
|
+
License-File: LICENSE
|
|
38
39
|
Requires-Dist: loguru
|
|
39
40
|
Requires-Dist: deprecated
|
|
40
41
|
Requires-Dist: websocket-client
|
|
@@ -47,6 +48,7 @@ Dynamic: description-content-type
|
|
|
47
48
|
Dynamic: home-page
|
|
48
49
|
Dynamic: keywords
|
|
49
50
|
Dynamic: license
|
|
51
|
+
Dynamic: license-file
|
|
50
52
|
Dynamic: project-url
|
|
51
53
|
Dynamic: requires-dist
|
|
52
54
|
Dynamic: requires-python
|
|
@@ -90,6 +90,9 @@ alttester/commands/UnityCommands/set_player_pref_key.py
|
|
|
90
90
|
alttester/commands/UnityCommands/set_time_scale.py
|
|
91
91
|
alttester/commands/UnityCommands/unload_scene.py
|
|
92
92
|
alttester/commands/UnityCommands/wait_for_current_scene_to_be.py
|
|
93
|
+
tests/appium/__init__.py
|
|
94
|
+
tests/appium/conftest.py
|
|
95
|
+
tests/appium/test_native_popup.py
|
|
93
96
|
tests/integration/__init__.py
|
|
94
97
|
tests/integration/conftest.py
|
|
95
98
|
tests/integration/test_driver.py
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: AltTester-Driver
|
|
3
|
-
Version: 2.
|
|
3
|
+
Version: 2.4.1
|
|
4
4
|
Summary: Python bindings for the AltTester® framework. AltTester® is an UI driven test automation tool that helps you find objects in your game and interacts with them.
|
|
5
5
|
Home-page: https://alttester.com/docs/sdk/latest/
|
|
6
6
|
Author: Altom Consulting
|
|
@@ -35,6 +35,7 @@ Classifier: Topic :: Software Development :: Testing :: Acceptance
|
|
|
35
35
|
Classifier: Topic :: Software Development :: Testing :: Unit
|
|
36
36
|
Requires-Python: >=3.4.0
|
|
37
37
|
Description-Content-Type: text/markdown
|
|
38
|
+
License-File: LICENSE
|
|
38
39
|
Requires-Dist: loguru
|
|
39
40
|
Requires-Dist: deprecated
|
|
40
41
|
Requires-Dist: websocket-client
|
|
@@ -47,6 +48,7 @@ Dynamic: description-content-type
|
|
|
47
48
|
Dynamic: home-page
|
|
48
49
|
Dynamic: keywords
|
|
49
50
|
Dynamic: license
|
|
51
|
+
Dynamic: license-file
|
|
50
52
|
Dynamic: project-url
|
|
51
53
|
Dynamic: requires-dist
|
|
52
54
|
Dynamic: requires-python
|
|
@@ -84,8 +84,8 @@ class NotificationHandler:
|
|
|
84
84
|
def add_notification_listener(self, notification_type, callback, overwrite=False):
|
|
85
85
|
if overwrite:
|
|
86
86
|
self._notification_callbacks[notification_type] = [callback]
|
|
87
|
-
|
|
88
|
-
|
|
87
|
+
else:
|
|
88
|
+
self._notification_callbacks[notification_type].append(callback)
|
|
89
89
|
|
|
90
90
|
def remove_notification_listener(self, notification_type):
|
|
91
91
|
self._notification_callbacks[notification_type].clear()
|
|
@@ -33,6 +33,7 @@ class AltDriver:
|
|
|
33
33
|
enable_logging (:obj:`bool`, optional): If set to ``True`` will turn on logging, by default logging is disabled.
|
|
34
34
|
timeout (:obj:`int`, :obj:`float`, optional): The timeout duration for establishing a connection, in seconds.
|
|
35
35
|
If set to ``None``, the connection attempt will wait indefinitely. The default value is ``60`` seconds.
|
|
36
|
+
driver_type (:obj:`str`, optional): The driver type reported to the AltTester® Server. Defaults to ``python``.
|
|
36
37
|
|
|
37
38
|
"""
|
|
38
39
|
|
|
@@ -47,7 +48,8 @@ class AltDriver:
|
|
|
47
48
|
platform_version="unknown",
|
|
48
49
|
device_instance_id="unknown",
|
|
49
50
|
app_id="unknown",
|
|
50
|
-
secure_mode=False
|
|
51
|
+
secure_mode=False,
|
|
52
|
+
driver_type="python"
|
|
51
53
|
):
|
|
52
54
|
self.host = host
|
|
53
55
|
self.port = port
|
|
@@ -59,6 +61,7 @@ class AltDriver:
|
|
|
59
61
|
self.device_instance_id = device_instance_id
|
|
60
62
|
self.app_id = app_id
|
|
61
63
|
self.secure_mode = secure_mode
|
|
64
|
+
self.driver_type = driver_type
|
|
62
65
|
|
|
63
66
|
self._config_logging(self.enable_logging)
|
|
64
67
|
|
|
@@ -83,13 +86,23 @@ class AltDriver:
|
|
|
83
86
|
"platformVersion": self.platform_version,
|
|
84
87
|
"deviceInstanceId": self.device_instance_id,
|
|
85
88
|
"appId": self.app_id,
|
|
86
|
-
"driverType": "
|
|
89
|
+
"driverType": self.driver_type + "_" + VERSION
|
|
87
90
|
},
|
|
88
91
|
command_handler=self._command_handler,
|
|
89
92
|
notification_handler=self._notification_handler
|
|
90
93
|
)
|
|
91
|
-
|
|
92
|
-
|
|
94
|
+
try:
|
|
95
|
+
self._connection.connect()
|
|
96
|
+
self._check_server_version()
|
|
97
|
+
except BaseException:
|
|
98
|
+
self._close_after_failed_connect()
|
|
99
|
+
raise
|
|
100
|
+
|
|
101
|
+
def _close_after_failed_connect(self):
|
|
102
|
+
try:
|
|
103
|
+
self._connection.close()
|
|
104
|
+
except Exception as ex:
|
|
105
|
+
logger.debug("Failed to close the connection after a failed connect: {}", ex)
|
|
93
106
|
|
|
94
107
|
def __repr__(self):
|
|
95
108
|
return "{}({!r}, {!r}, {!r}, {!r}, {!r})".format(
|
|
@@ -0,0 +1,113 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Copyright(C) 2026 Altom Consulting
|
|
3
|
+
"""
|
|
4
|
+
|
|
5
|
+
import datetime
|
|
6
|
+
import os
|
|
7
|
+
import time
|
|
8
|
+
import pytest
|
|
9
|
+
from appium import webdriver
|
|
10
|
+
from appium.options.android import UiAutomator2Options
|
|
11
|
+
from appium.options.ios import XCUITestOptions
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
saucelabs_devices = [
|
|
15
|
+
{"name": "Samsung Galaxy S23.*", "os": "android", "os_version": "13"},
|
|
16
|
+
{"name": "Samsung Galaxy S22.*", "os": "android", "os_version": "12"},
|
|
17
|
+
{"name": "iPhone 16.*", "os": "ios", "os_version": "18"},
|
|
18
|
+
{"name": "iPhone 15.*", "os": "ios", "os_version": "17"},
|
|
19
|
+
{"name": "iPhone 14.*", "os": "ios", "os_version": "16"},
|
|
20
|
+
{"name": "OnePlus 9.*", "os": "android", "os_version": "11"},
|
|
21
|
+
]
|
|
22
|
+
|
|
23
|
+
saucelabs_android_devices = [d for d in saucelabs_devices if d["os"] == "android"]
|
|
24
|
+
saucelabs_ios_devices = [d for d in saucelabs_devices if d["os"] == "ios"]
|
|
25
|
+
|
|
26
|
+
|
|
27
|
+
def get_saucelabs_username():
|
|
28
|
+
return os.environ.get("SAUCE_USERNAME", "")
|
|
29
|
+
|
|
30
|
+
|
|
31
|
+
def get_saucelabs_access_key():
|
|
32
|
+
return os.environ.get("SAUCE_ACCESS_KEY", "")
|
|
33
|
+
|
|
34
|
+
|
|
35
|
+
def get_saucelabs_region():
|
|
36
|
+
return os.environ.get("SAUCE_REGION", "us-west-1")
|
|
37
|
+
|
|
38
|
+
|
|
39
|
+
def get_saucelabs_tunnel_name():
|
|
40
|
+
return os.environ.get("SAUCE_TUNNEL_NAME", "alttester-tunnel")
|
|
41
|
+
|
|
42
|
+
|
|
43
|
+
def get_saucelabs_app_url(device):
|
|
44
|
+
if device["os"] == "android":
|
|
45
|
+
return os.environ.get("SAUCE_APP_URL_ANDROID", "")
|
|
46
|
+
return os.environ.get("SAUCE_APP_URL_IOS", "")
|
|
47
|
+
|
|
48
|
+
|
|
49
|
+
def get_saucelabs_capabilities(device):
|
|
50
|
+
return {
|
|
51
|
+
"platformName": "Android" if device["os"] == "android" else "iOS",
|
|
52
|
+
"appium:deviceName": device["name"],
|
|
53
|
+
"appium:platformVersion": device["os_version"],
|
|
54
|
+
"appium:app": get_saucelabs_app_url(device),
|
|
55
|
+
"appium:automationName": "UiAutomator2" if device["os"] == "android" else "XCUITest",
|
|
56
|
+
"appium:newCommandTimeout": 2000,
|
|
57
|
+
"sauce:options": {
|
|
58
|
+
"username": get_saucelabs_username(),
|
|
59
|
+
"accessKey": get_saucelabs_access_key(),
|
|
60
|
+
"build": "AltTester SDK - Native Popup",
|
|
61
|
+
"name": "native-popup-{date:%Y-%m-%d_%H:%M:%S}".format(date=datetime.datetime.now()),
|
|
62
|
+
"tunnelName": get_saucelabs_tunnel_name(),
|
|
63
|
+
"tunnelOwner": get_saucelabs_username(),
|
|
64
|
+
"appiumVersion": "latest",
|
|
65
|
+
"idleTimeout": 300,
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
|
|
70
|
+
def create_appium_driver(device):
|
|
71
|
+
caps = get_saucelabs_capabilities(device)
|
|
72
|
+
region = get_saucelabs_region()
|
|
73
|
+
hub_url = "https://ondemand.{}.saucelabs.com:443/wd/hub".format(region)
|
|
74
|
+
if device["os"] == "android":
|
|
75
|
+
options = UiAutomator2Options().load_capabilities(caps)
|
|
76
|
+
else:
|
|
77
|
+
options = XCUITestOptions().load_capabilities(caps)
|
|
78
|
+
driver = webdriver.Remote(hub_url, options=options)
|
|
79
|
+
time.sleep(10)
|
|
80
|
+
return driver
|
|
81
|
+
|
|
82
|
+
|
|
83
|
+
@pytest.fixture(autouse=True, scope="session")
|
|
84
|
+
def worker_id(request):
|
|
85
|
+
request.config.getoption("--dist", default="no", skip=True)
|
|
86
|
+
if hasattr(request.config, "workerinput"):
|
|
87
|
+
return request.config.workerinput["workerid"]
|
|
88
|
+
return "master"
|
|
89
|
+
|
|
90
|
+
|
|
91
|
+
@pytest.fixture(autouse=True, scope="session")
|
|
92
|
+
def current_device(worker_id):
|
|
93
|
+
if os.environ.get("RUN_IN_SAUCELABS_ANDROID_ONLY", "") == "true":
|
|
94
|
+
selected_devices = saucelabs_android_devices
|
|
95
|
+
elif os.environ.get("RUN_IN_SAUCELABS_IOS_ONLY", "") == "true":
|
|
96
|
+
selected_devices = saucelabs_ios_devices
|
|
97
|
+
else:
|
|
98
|
+
selected_devices = saucelabs_devices
|
|
99
|
+
|
|
100
|
+
if worker_id == "master":
|
|
101
|
+
return selected_devices[0]
|
|
102
|
+
index = int(worker_id.split("gw")[1])
|
|
103
|
+
return selected_devices[index % len(selected_devices)]
|
|
104
|
+
|
|
105
|
+
|
|
106
|
+
@pytest.fixture(scope="class")
|
|
107
|
+
def appium_driver(current_device):
|
|
108
|
+
driver = create_appium_driver(current_device)
|
|
109
|
+
yield driver
|
|
110
|
+
try:
|
|
111
|
+
driver.quit()
|
|
112
|
+
except Exception:
|
|
113
|
+
pass
|
|
@@ -0,0 +1,106 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Copyright(C) 2026 Altom Consulting
|
|
3
|
+
|
|
4
|
+
Tests that verify the AltTester native connection settings popup appears
|
|
5
|
+
and can be interacted with via Appium on iOS and Android.
|
|
6
|
+
"""
|
|
7
|
+
|
|
8
|
+
import pytest
|
|
9
|
+
from appium.webdriver.common.appiumby import AppiumBy
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
POPUP_ELEMENTS = [
|
|
13
|
+
"AltTesterHostInputField",
|
|
14
|
+
"AltTesterPortInputField",
|
|
15
|
+
"AltTesterAppNameInputField",
|
|
16
|
+
"AltTesterDontShowAgainCheckbox",
|
|
17
|
+
"AltTesterOkButton",
|
|
18
|
+
]
|
|
19
|
+
|
|
20
|
+
HOST_PLACEHOLDER = "Host"
|
|
21
|
+
PORT_PLACEHOLDER = "Port"
|
|
22
|
+
APP_NAME_PLACEHOLDER = "App Name"
|
|
23
|
+
IMPLICIT_WAIT_TIMEOUT = 60
|
|
24
|
+
|
|
25
|
+
|
|
26
|
+
class TestNativePopup:
|
|
27
|
+
|
|
28
|
+
@pytest.fixture(autouse=True)
|
|
29
|
+
def setup(self, appium_driver, current_device):
|
|
30
|
+
self.driver = appium_driver
|
|
31
|
+
self.device = current_device
|
|
32
|
+
self.driver.implicitly_wait(IMPLICIT_WAIT_TIMEOUT)
|
|
33
|
+
|
|
34
|
+
def _find(self, accessibility_id):
|
|
35
|
+
return self.driver.find_element(
|
|
36
|
+
by=AppiumBy.ACCESSIBILITY_ID, value=accessibility_id
|
|
37
|
+
)
|
|
38
|
+
|
|
39
|
+
def test_popup_appears_on_launch(self):
|
|
40
|
+
"""Verify the connection settings popup is displayed when the app launches."""
|
|
41
|
+
for element_id in POPUP_ELEMENTS:
|
|
42
|
+
element = self._find(element_id)
|
|
43
|
+
assert element.is_displayed(), \
|
|
44
|
+
"Expected '{}' to be visible but it was not".format(element_id)
|
|
45
|
+
|
|
46
|
+
def test_popup_host_field_starts_empty(self):
|
|
47
|
+
"""Verify the host input field starts empty, showing its placeholder text."""
|
|
48
|
+
host_field = self._find("AltTesterHostInputField")
|
|
49
|
+
assert host_field.text == HOST_PLACEHOLDER, \
|
|
50
|
+
"Expected placeholder '{}', got '{}'".format(HOST_PLACEHOLDER, host_field.text)
|
|
51
|
+
|
|
52
|
+
def test_popup_port_field_starts_empty(self):
|
|
53
|
+
"""Verify the port input field starts empty, showing its placeholder text."""
|
|
54
|
+
port_field = self._find("AltTesterPortInputField")
|
|
55
|
+
assert port_field.text == PORT_PLACEHOLDER, \
|
|
56
|
+
"Expected placeholder '{}', got '{}'".format(PORT_PLACEHOLDER, port_field.text)
|
|
57
|
+
|
|
58
|
+
def test_popup_app_name_field_starts_empty(self):
|
|
59
|
+
"""Verify the app name input field starts empty, showing its placeholder text."""
|
|
60
|
+
app_name_field = self._find("AltTesterAppNameInputField")
|
|
61
|
+
assert app_name_field.text == APP_NAME_PLACEHOLDER, \
|
|
62
|
+
"Expected placeholder '{}', got '{}'".format(APP_NAME_PLACEHOLDER, app_name_field.text)
|
|
63
|
+
|
|
64
|
+
def test_popup_fields_are_editable(self):
|
|
65
|
+
"""Verify host, port, and app name fields accept user input."""
|
|
66
|
+
host_field = self._find("AltTesterHostInputField")
|
|
67
|
+
host_field.clear()
|
|
68
|
+
host_field.send_keys("192.168.1.1")
|
|
69
|
+
assert host_field.text == "192.168.1.1"
|
|
70
|
+
|
|
71
|
+
port_field = self._find("AltTesterPortInputField")
|
|
72
|
+
port_field.clear()
|
|
73
|
+
port_field.send_keys("13001")
|
|
74
|
+
assert port_field.text == "13001"
|
|
75
|
+
|
|
76
|
+
app_name_field = self._find("AltTesterAppNameInputField")
|
|
77
|
+
app_name_field.clear()
|
|
78
|
+
app_name_field.send_keys("MyApp")
|
|
79
|
+
assert app_name_field.text == "MyApp"
|
|
80
|
+
|
|
81
|
+
def _is_checked(self):
|
|
82
|
+
# Android exposes "selected" and "checked" as distinct accessibility
|
|
83
|
+
# properties; a CheckBox only ever updates "checked" on toggle, so
|
|
84
|
+
# is_selected() (which reads "selected") never reflects its state.
|
|
85
|
+
return self._find("AltTesterDontShowAgainCheckbox").get_attribute("checked") == "true"
|
|
86
|
+
|
|
87
|
+
def test_dont_show_again_checkbox_is_toggleable(self):
|
|
88
|
+
"""Verify the 'Don't show this again' checkbox can be toggled."""
|
|
89
|
+
initial_state = self._is_checked()
|
|
90
|
+
self._find("AltTesterDontShowAgainCheckbox").click()
|
|
91
|
+
assert self._is_checked() != initial_state, \
|
|
92
|
+
"Checkbox state did not change after click"
|
|
93
|
+
# restore
|
|
94
|
+
self._find("AltTesterDontShowAgainCheckbox").click()
|
|
95
|
+
assert self._is_checked() == initial_state
|
|
96
|
+
|
|
97
|
+
def test_popup_dismisses_on_ok(self):
|
|
98
|
+
"""Verify the popup disappears after tapping OK."""
|
|
99
|
+
ok_button = self._find("AltTesterOkButton")
|
|
100
|
+
ok_button.click()
|
|
101
|
+
|
|
102
|
+
self.driver.implicitly_wait(5)
|
|
103
|
+
elements = self.driver.find_elements(
|
|
104
|
+
by=AppiumBy.ACCESSIBILITY_ID, value="AltTesterOkButton"
|
|
105
|
+
)
|
|
106
|
+
assert len(elements) == 0, "Popup was still visible after tapping OK"
|
|
@@ -76,6 +76,10 @@ def get_app_name():
|
|
|
76
76
|
return os.environ.get("ALTSERVER_APP_NAME", "__default__")
|
|
77
77
|
|
|
78
78
|
|
|
79
|
+
def get_platform_version():
|
|
80
|
+
return os.environ.get("ALTSERVER_PLATFORM_VERSION", "unknown")
|
|
81
|
+
|
|
82
|
+
|
|
79
83
|
def get_browserstack_username():
|
|
80
84
|
return os.environ.get("BROWSERSTACK_USERNAME", "")
|
|
81
85
|
|
|
@@ -143,7 +147,9 @@ def alt_driver(request, appium_driver, worker_id, current_device):
|
|
|
143
147
|
port=get_port(),
|
|
144
148
|
app_name=get_app_name(),
|
|
145
149
|
platform=platform,
|
|
146
|
-
platform_version=
|
|
150
|
+
platform_version=get_platform_version()
|
|
151
|
+
if current_device["os_version"] == "unknown"
|
|
152
|
+
else current_device["os_version"].split(".")[0],
|
|
147
153
|
timeout=180
|
|
148
154
|
)
|
|
149
155
|
if alt_driver is None:
|
|
@@ -189,13 +195,6 @@ def log_to_report(message):
|
|
|
189
195
|
|
|
190
196
|
@pytest.fixture(autouse=True, scope="session")
|
|
191
197
|
def current_device(request, worker_id):
|
|
192
|
-
global devices
|
|
193
|
-
global local_run_device
|
|
194
|
-
global android_devices
|
|
195
|
-
global ios_devices
|
|
196
|
-
global saucelabs_devices
|
|
197
|
-
global saucelabs_android_devices
|
|
198
|
-
global saucelabs_ios_devices
|
|
199
198
|
current_device = None
|
|
200
199
|
selected_devices = []
|
|
201
200
|
if os.environ.get("RUN_IN_SAUCELABS", "") == "true":
|
|
@@ -91,7 +91,7 @@ class TestDriver:
|
|
|
91
91
|
@pytest.mark.WebGLUnsupported
|
|
92
92
|
def test_set_server_logging(self):
|
|
93
93
|
rule = self.alt_driver.call_static_method(
|
|
94
|
-
"AltTester.AltTesterUnitySDK.Logging.
|
|
94
|
+
"AltTester.AltTesterUnitySDK.Logging.AltTesterLogManager",
|
|
95
95
|
"Instance.Configuration.FindRuleByName",
|
|
96
96
|
"Assembly-CSharp",
|
|
97
97
|
parameters=["AltServerFileRule"],
|
|
@@ -102,7 +102,7 @@ class TestDriver:
|
|
|
102
102
|
|
|
103
103
|
self.alt_driver.set_server_logging(AltLogger.File, AltLogLevel.Off)
|
|
104
104
|
rule = self.alt_driver.call_static_method(
|
|
105
|
-
"AltTester.AltTesterUnitySDK.Logging.
|
|
105
|
+
"AltTester.AltTesterUnitySDK.Logging.AltTesterLogManager",
|
|
106
106
|
"Instance.Configuration.FindRuleByName",
|
|
107
107
|
"Assembly-CSharp",
|
|
108
108
|
parameters=["AltServerFileRule"],
|
|
@@ -401,6 +401,7 @@ class TestScene01Part2:
|
|
|
401
401
|
assert type(plane.worldY) is float
|
|
402
402
|
assert type(plane.worldZ) is float
|
|
403
403
|
|
|
404
|
+
@pytest.mark.skip(reason="Flaky: tilt's server-side duration isn't reliably honored in CI")
|
|
404
405
|
def test_set_command_response_timeout(self):
|
|
405
406
|
self.alt_driver.set_command_response_timeout(1)
|
|
406
407
|
with pytest.raises(exceptions.CommandResponseTimeoutException) as execinfo:
|
|
@@ -9,6 +9,7 @@ import pytest
|
|
|
9
9
|
|
|
10
10
|
from alttester._websocket import Store, WebsocketConnection, CommandHandler, NotificationHandler
|
|
11
11
|
from alttester.exceptions import ConnectionError
|
|
12
|
+
from alttester.commands.Notifications.notification_type import NotificationType
|
|
12
13
|
|
|
13
14
|
|
|
14
15
|
class TestStore:
|
|
@@ -182,3 +183,50 @@ class TestWebsocketConnection:
|
|
|
182
183
|
|
|
183
184
|
assert self.connection._command_handler.get_current_command() != (
|
|
184
185
|
command["messageId"], command["commandName"])
|
|
186
|
+
|
|
187
|
+
|
|
188
|
+
class TestNotificationHandler:
|
|
189
|
+
|
|
190
|
+
@pytest.fixture(autouse=True)
|
|
191
|
+
def setup(self):
|
|
192
|
+
self.notification_handler = NotificationHandler()
|
|
193
|
+
self.paused_message = {
|
|
194
|
+
"commandName": "applicationPausedNotification",
|
|
195
|
+
"data": json.dumps(True)
|
|
196
|
+
}
|
|
197
|
+
|
|
198
|
+
def test_add_notification_listener_with_overwrite(self):
|
|
199
|
+
first_callback = mock.Mock()
|
|
200
|
+
second_callback = mock.Mock()
|
|
201
|
+
|
|
202
|
+
self.notification_handler.add_notification_listener(
|
|
203
|
+
NotificationType.APPLICATION_PAUSED, first_callback, overwrite=True)
|
|
204
|
+
self.notification_handler.add_notification_listener(
|
|
205
|
+
NotificationType.APPLICATION_PAUSED, second_callback, overwrite=True)
|
|
206
|
+
self.notification_handler.handle_notification(self.paused_message)
|
|
207
|
+
|
|
208
|
+
first_callback.assert_not_called()
|
|
209
|
+
second_callback.assert_called_once_with(True)
|
|
210
|
+
|
|
211
|
+
def test_add_notification_listener_without_overwrite(self):
|
|
212
|
+
first_callback = mock.Mock()
|
|
213
|
+
second_callback = mock.Mock()
|
|
214
|
+
|
|
215
|
+
self.notification_handler.add_notification_listener(
|
|
216
|
+
NotificationType.APPLICATION_PAUSED, first_callback, overwrite=False)
|
|
217
|
+
self.notification_handler.add_notification_listener(
|
|
218
|
+
NotificationType.APPLICATION_PAUSED, second_callback, overwrite=False)
|
|
219
|
+
self.notification_handler.handle_notification(self.paused_message)
|
|
220
|
+
|
|
221
|
+
first_callback.assert_called_once_with(True)
|
|
222
|
+
second_callback.assert_called_once_with(True)
|
|
223
|
+
|
|
224
|
+
def test_remove_notification_listener(self):
|
|
225
|
+
callback = mock.Mock()
|
|
226
|
+
|
|
227
|
+
self.notification_handler.add_notification_listener(
|
|
228
|
+
NotificationType.APPLICATION_PAUSED, callback, overwrite=True)
|
|
229
|
+
self.notification_handler.remove_notification_listener(NotificationType.APPLICATION_PAUSED)
|
|
230
|
+
self.notification_handler.handle_notification(self.paused_message)
|
|
231
|
+
|
|
232
|
+
callback.assert_not_called()
|
{alttester_driver-2.3.3 → alttester_driver-2.4.1}/AltTester_Driver.egg-info/dependency_links.txt
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{alttester_driver-2.3.3 → alttester_driver-2.4.1}/alttester/commands/AltTesterCommands/__init__.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{alttester_driver-2.3.3 → alttester_driver-2.4.1}/alttester/commands/FindObjects/__init__.py
RENAMED
|
File without changes
|
{alttester_driver-2.3.3 → alttester_driver-2.4.1}/alttester/commands/FindObjects/find_object.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
{alttester_driver-2.3.3 → alttester_driver-2.4.1}/alttester/commands/FindObjects/find_objects.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
{alttester_driver-2.3.3 → alttester_driver-2.4.1}/alttester/commands/FindObjects/wait_for_object.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{alttester_driver-2.3.3 → alttester_driver-2.4.1}/alttester/commands/InputActions/__init__.py
RENAMED
|
File without changes
|
{alttester_driver-2.3.3 → alttester_driver-2.4.1}/alttester/commands/InputActions/begin_touch.py
RENAMED
|
File without changes
|
|
File without changes
|
{alttester_driver-2.3.3 → alttester_driver-2.4.1}/alttester/commands/InputActions/end_touch.py
RENAMED
|
File without changes
|
{alttester_driver-2.3.3 → alttester_driver-2.4.1}/alttester/commands/InputActions/keys_down.py
RENAMED
|
File without changes
|
{alttester_driver-2.3.3 → alttester_driver-2.4.1}/alttester/commands/InputActions/keys_up.py
RENAMED
|
File without changes
|
{alttester_driver-2.3.3 → alttester_driver-2.4.1}/alttester/commands/InputActions/move_mouse.py
RENAMED
|
File without changes
|
{alttester_driver-2.3.3 → alttester_driver-2.4.1}/alttester/commands/InputActions/move_touch.py
RENAMED
|
File without changes
|
|
File without changes
|
{alttester_driver-2.3.3 → alttester_driver-2.4.1}/alttester/commands/InputActions/press_keys.py
RENAMED
|
File without changes
|
{alttester_driver-2.3.3 → alttester_driver-2.4.1}/alttester/commands/InputActions/scroll_mouse.py
RENAMED
|
File without changes
|
|
File without changes
|
{alttester_driver-2.3.3 → alttester_driver-2.4.1}/alttester/commands/InputActions/tap_coordinates.py
RENAMED
|
File without changes
|
|
File without changes
|
{alttester_driver-2.3.3 → alttester_driver-2.4.1}/alttester/commands/Notifications/__init__.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{alttester_driver-2.3.3 → alttester_driver-2.4.1}/alttester/commands/ObjectCommands/__init__.py
RENAMED
|
File without changes
|
{alttester_driver-2.3.3 → alttester_driver-2.4.1}/alttester/commands/ObjectCommands/call_method.py
RENAMED
|
File without changes
|
{alttester_driver-2.3.3 → alttester_driver-2.4.1}/alttester/commands/ObjectCommands/click_element.py
RENAMED
|
File without changes
|
{alttester_driver-2.3.3 → alttester_driver-2.4.1}/alttester/commands/ObjectCommands/fetch_text.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{alttester_driver-2.3.3 → alttester_driver-2.4.1}/alttester/commands/ObjectCommands/pointer_down.py
RENAMED
|
File without changes
|
{alttester_driver-2.3.3 → alttester_driver-2.4.1}/alttester/commands/ObjectCommands/pointer_enter.py
RENAMED
|
File without changes
|
{alttester_driver-2.3.3 → alttester_driver-2.4.1}/alttester/commands/ObjectCommands/pointer_exit.py
RENAMED
|
File without changes
|
{alttester_driver-2.3.3 → alttester_driver-2.4.1}/alttester/commands/ObjectCommands/pointer_up.py
RENAMED
|
File without changes
|
|
File without changes
|
{alttester_driver-2.3.3 → alttester_driver-2.4.1}/alttester/commands/ObjectCommands/tap_element.py
RENAMED
|
File without changes
|
{alttester_driver-2.3.3 → alttester_driver-2.4.1}/alttester/commands/ObjectCommands/update_text.py
RENAMED
|
File without changes
|
{alttester_driver-2.3.3 → alttester_driver-2.4.1}/alttester/commands/UnityCommands/__init__.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{alttester_driver-2.3.3 → alttester_driver-2.4.1}/alttester/commands/UnityCommands/get_time_scale.py
RENAMED
|
File without changes
|
|
File without changes
|
{alttester_driver-2.3.3 → alttester_driver-2.4.1}/alttester/commands/UnityCommands/load_scene.py
RENAMED
|
File without changes
|
|
File without changes
|
{alttester_driver-2.3.3 → alttester_driver-2.4.1}/alttester/commands/UnityCommands/set_time_scale.py
RENAMED
|
File without changes
|
{alttester_driver-2.3.3 → alttester_driver-2.4.1}/alttester/commands/UnityCommands/unload_scene.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{alttester_driver-2.3.3/tests/integration → alttester_driver-2.4.1/tests/appium}/__init__.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
{alttester_driver-2.3.3 → alttester_driver-2.4.1}/tests/integration/test_reverse_port_forwarding.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|