Skip to content

mobile_utils

MobileUtils

Provides utility functions for mobile automation, including BrowserStack integration and device management.

Source code in libs\cafex_ui\src\cafex_ui\mobile_client\mobile_utils.py
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
class MobileUtils:
    """Provides utility functions for mobile automation, including BrowserStack
    integration and device management."""

    def __init__(self):
        self.bs_local = None
        self.logger = CoreLogger(name=__name__).get_logger()

    def browserstack_upload_appcenter_app(
        self,
        appcenter_token: str,
        appcenter_url: str,
        browserstack_url: str,
        browserstack_user_name: str,
        browserstack_access_key: str,
        custom_app_id: str,
        proxy: str = None,
        verify_ssl: bool = True,
    ) -> bool:
        """Uploads the latest app from App Center to BrowserStack.

        Args:
            appcenter_token: App Center API token.
            appcenter_url: App Center URL for the latest release.
            browserstack_url: BrowserStack app upload URL.
            browserstack_user_name: BrowserStack username.
            browserstack_access_key: BrowserStack access key.
            custom_app_id: Custom ID for the app on BrowserStack.
            proxy: Proxy server URL (optional).
            verify_ssl: Whether to verify SSL certificates (default: True).

        Returns:
            True if the upload is successful, False otherwise.

        Raises:
            ValueError: If any of the required parameters are missing.
            Exception: If an error occurs during the upload process.
        """
        try:
            required_params = [
                appcenter_token,
                appcenter_url,
                browserstack_url,
                browserstack_user_name,
                browserstack_access_key,
                custom_app_id,
            ]
            if any(param is None for param in required_params):
                raise ValueError(
                    "Missing required parameters for BrowserStack app upload. "
                    "Please provide appcenter_token, appcenter_url, browserstack_url, "
                    "browserstack_user_name, browserstack_access_key, and custom_app_id."
                )

            headers = {
                "Content-Type": "application/json",
                "X-Api-Token": appcenter_token,
            }
            appcenter_response = requests.get(
                url=appcenter_url, headers=headers, proxies=proxy, verify=verify_ssl, timeout=30
            )

            if appcenter_response.status_code == 200:
                appcenter_json = appcenter_response.json()
                appcenter_app_url = appcenter_json["download_url"]
                bs_response = UISecurity().mobile_upload_browserstack_app(
                    browserstack_user_name=browserstack_user_name,
                    browserstack_access_key=browserstack_access_key,
                    appcenter_app_url=appcenter_app_url,
                    custom_id=custom_app_id,
                    browserstack_url=browserstack_url,
                )
                if bs_response.status_code == 200:
                    bs_json = bs_response.json()
                    self.logger.info("App uploaded to BrowserStack. Details: %s", bs_json)
                    return True

                self.logger.error(
                    "Failed to upload app to BrowserStack. Response: %s", bs_response.text
                )
                return False

            self.logger.error(
                "Failed to fetch app from App Center. Response: %s", appcenter_response.text
            )
            return False
        except ValueError as e:
            self.logger.exception(str(e))
            raise e
        except Exception as e:
            self.logger.exception("Error uploading app to BrowserStack: %s", e)
            raise e

    def get_browser_stack_device_list(
        self,
        mobile_os: str,
        browserstack_user_name: str,
        browserstack_access_key: str,
        get_browserstack_devices_url: str,
    ) -> List[Dict[str, Any]]:
        """Retrieves a list of available devices from BrowserStack.

        Args:
            mobile_os: The target mobile OS ('ios' or 'android').
            browserstack_user_name: BrowserStack username.
            browserstack_access_key: BrowserStack access key.
            get_browserstack_devices_url: BrowserStack devices API URL.

        Returns:
            A list of dictionaries, each representing a device.

        Raises:
            Exception: If an error occurs while fetching devices from BrowserStack.
        """
        try:
            bs_response = UISecurity().get_browser_stack_devices_list(
                browserstack_user_name=browserstack_user_name,
                browserstack_access_key=browserstack_access_key,
                browserstack_url=get_browserstack_devices_url,
            )
            if bs_response.status_code == 200:
                devices = bs_response.json()
                filtered_devices = [
                    {k: v for k, v in device.items() if k not in ("os", "realMobile")}
                    for device in devices
                    if device["os"] == mobile_os.lower()
                ]
                return filtered_devices

            self.logger.error(
                "Failed to fetch devices from BrowserStack. Response: %s", bs_response.text
            )
            return []
        except Exception as e:
            self.logger.exception("Error fetching devices from BrowserStack: %s", e)
            raise e

    def get_device_list_from_json(
        self, mobile_os: str, ios_device_json_path: str, android_device_json_path: str
    ) -> List[Dict[str, Any]]:
        """Reads a JSON file containing a list of devices.

        Args:
            mobile_os: The target mobile OS ('ios' or 'android').
            ios_device_json_path: Path to the JSON file for iOS devices.
            android_device_json_path: Path to the JSON file for Android devices.

        Returns:
            A list of dictionaries, each representing a device.

        Raises:
            FileNotFoundError: If the specified JSON file is not found.
            ValueError: If the file is not in JSON format.
            Exception: For any other errors during file reading.
        """
        try:
            file_path = (
                ios_device_json_path if mobile_os.lower() == "ios" else android_device_json_path
            )
            return self.get_device_list_from_file(file_path)
        except Exception as e:
            self.logger.exception("Error getting device list from JSON: %s", e)
            raise e

    def get_device_list_from_file(self, json_file_path: str) -> List[Dict[str, Any]]:
        """Reads a JSON file and returns a list of devices.

        Args:
            json_file_path (str): The path to the JSON file.

        Returns:
            A list of dictionaries, each representing a device.

        Raises:
            FileNotFoundError: If the specified file does not exist.
            ValueError: If the given file is not in JSON format.
            Exception: For any other errors during file reading.
        """
        try:
            if not os.path.exists(json_file_path):
                raise FileNotFoundError(f"The device list file '{json_file_path}' was not found")

            if not json_file_path.endswith(".json"):
                raise ValueError("Given file is not in JSON format.")

            with open(json_file_path, "r", encoding="utf-8") as json_file:
                json_data = json.load(json_file)
            return json_data

        except (FileNotFoundError, ValueError) as e:
            self.logger.exception("Error reading device list from file: %s", e)
            raise e

        except Exception as e:
            self.logger.exception("Error reading device list from file: %s", e)
            raise e

    def get_available_devices(
        self,
        mobile_os: str,
        browserstack_user_name: str,
        browserstack_access_key: str,
        get_browserstack_devices_url: str,
        ios_device_json_path: str = None,
        android_device_json_path: str = None,
    ) -> dict:
        """Retrieves a random available device from BrowserStack, filtering by
        devices specified in local JSON files.

        Args:
            mobile_os: The target mobile OS ('ios' or 'android').
            browserstack_user_name: BrowserStack username.
            browserstack_access_key: BrowserStack access key.
            get_browserstack_devices_url: BrowserStack devices API URL.
            ios_device_json_path: Path to the JSON file for iOS devices (required if mobile_os is 'ios').
            android_device_json_path: Path to the JSON file for Android devices (required if mobile_os is 'android').

        Returns:
            A dictionary representing a randomly selected available device.

        Raises:
            ValueError: If any of the required parameters are missing or if no common devices are found.
            Exception: For any other errors while fetching devices.
        """
        try:
            required_params = [
                mobile_os,
                browserstack_user_name,
                browserstack_access_key,
                get_browserstack_devices_url,
            ]
            if any(param is None for param in required_params):
                raise ValueError(
                    "Missing required parameters: mobile_os, browserstack_user_name, browserstack_access_key, "
                    "and get_browserstack_devices_url are mandatory."
                )

            if mobile_os.lower() == "android" and android_device_json_path is None:
                raise ValueError(
                    "android_device_json_path is required when mobile_os is 'android'."
                )
            if mobile_os.lower() == "ios" and ios_device_json_path is None:
                raise ValueError("ios_device_json_path is required when mobile_os is 'ios'.")

            browser_stack_devices = self.get_browser_stack_device_list(
                mobile_os=mobile_os,
                browserstack_user_name=browserstack_user_name,
                browserstack_access_key=browserstack_access_key,
                get_browserstack_devices_url=get_browserstack_devices_url,
            )
            client_devices = self.get_device_list_from_json(
                mobile_os=mobile_os,
                ios_device_json_path=ios_device_json_path,
                android_device_json_path=android_device_json_path,
            )
            common_devices = [
                device for device in browser_stack_devices if device in client_devices
            ]

            if not common_devices:
                raise ValueError(
                    "No common devices found between client usage and BrowserStack lists."
                )

            return random.choice(common_devices)
        except Exception as e:
            self.logger.exception("Error while fetching devices: %s", e)
            raise e

browserstack_upload_appcenter_app(appcenter_token, appcenter_url, browserstack_url, browserstack_user_name, browserstack_access_key, custom_app_id, proxy=None, verify_ssl=True)

Uploads the latest app from App Center to BrowserStack.

Parameters:

Name Type Description Default
appcenter_token str

App Center API token.

required
appcenter_url str

App Center URL for the latest release.

required
browserstack_url str

BrowserStack app upload URL.

required
browserstack_user_name str

BrowserStack username.

required
browserstack_access_key str

BrowserStack access key.

required
custom_app_id str

Custom ID for the app on BrowserStack.

required
proxy str

Proxy server URL (optional).

None
verify_ssl bool

Whether to verify SSL certificates (default: True).

True

Returns:

Type Description
bool

True if the upload is successful, False otherwise.

Raises:

Type Description
ValueError

If any of the required parameters are missing.

Exception

If an error occurs during the upload process.

Source code in libs\cafex_ui\src\cafex_ui\mobile_client\mobile_utils.py
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
def browserstack_upload_appcenter_app(
    self,
    appcenter_token: str,
    appcenter_url: str,
    browserstack_url: str,
    browserstack_user_name: str,
    browserstack_access_key: str,
    custom_app_id: str,
    proxy: str = None,
    verify_ssl: bool = True,
) -> bool:
    """Uploads the latest app from App Center to BrowserStack.

    Args:
        appcenter_token: App Center API token.
        appcenter_url: App Center URL for the latest release.
        browserstack_url: BrowserStack app upload URL.
        browserstack_user_name: BrowserStack username.
        browserstack_access_key: BrowserStack access key.
        custom_app_id: Custom ID for the app on BrowserStack.
        proxy: Proxy server URL (optional).
        verify_ssl: Whether to verify SSL certificates (default: True).

    Returns:
        True if the upload is successful, False otherwise.

    Raises:
        ValueError: If any of the required parameters are missing.
        Exception: If an error occurs during the upload process.
    """
    try:
        required_params = [
            appcenter_token,
            appcenter_url,
            browserstack_url,
            browserstack_user_name,
            browserstack_access_key,
            custom_app_id,
        ]
        if any(param is None for param in required_params):
            raise ValueError(
                "Missing required parameters for BrowserStack app upload. "
                "Please provide appcenter_token, appcenter_url, browserstack_url, "
                "browserstack_user_name, browserstack_access_key, and custom_app_id."
            )

        headers = {
            "Content-Type": "application/json",
            "X-Api-Token": appcenter_token,
        }
        appcenter_response = requests.get(
            url=appcenter_url, headers=headers, proxies=proxy, verify=verify_ssl, timeout=30
        )

        if appcenter_response.status_code == 200:
            appcenter_json = appcenter_response.json()
            appcenter_app_url = appcenter_json["download_url"]
            bs_response = UISecurity().mobile_upload_browserstack_app(
                browserstack_user_name=browserstack_user_name,
                browserstack_access_key=browserstack_access_key,
                appcenter_app_url=appcenter_app_url,
                custom_id=custom_app_id,
                browserstack_url=browserstack_url,
            )
            if bs_response.status_code == 200:
                bs_json = bs_response.json()
                self.logger.info("App uploaded to BrowserStack. Details: %s", bs_json)
                return True

            self.logger.error(
                "Failed to upload app to BrowserStack. Response: %s", bs_response.text
            )
            return False

        self.logger.error(
            "Failed to fetch app from App Center. Response: %s", appcenter_response.text
        )
        return False
    except ValueError as e:
        self.logger.exception(str(e))
        raise e
    except Exception as e:
        self.logger.exception("Error uploading app to BrowserStack: %s", e)
        raise e

get_available_devices(mobile_os, browserstack_user_name, browserstack_access_key, get_browserstack_devices_url, ios_device_json_path=None, android_device_json_path=None)

Retrieves a random available device from BrowserStack, filtering by devices specified in local JSON files.

Parameters:

Name Type Description Default
mobile_os str

The target mobile OS ('ios' or 'android').

required
browserstack_user_name str

BrowserStack username.

required
browserstack_access_key str

BrowserStack access key.

required
get_browserstack_devices_url str

BrowserStack devices API URL.

required
ios_device_json_path str

Path to the JSON file for iOS devices (required if mobile_os is 'ios').

None
android_device_json_path str

Path to the JSON file for Android devices (required if mobile_os is 'android').

None

Returns:

Type Description
dict

A dictionary representing a randomly selected available device.

Raises:

Type Description
ValueError

If any of the required parameters are missing or if no common devices are found.

Exception

For any other errors while fetching devices.

Source code in libs\cafex_ui\src\cafex_ui\mobile_client\mobile_utils.py
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
def get_available_devices(
    self,
    mobile_os: str,
    browserstack_user_name: str,
    browserstack_access_key: str,
    get_browserstack_devices_url: str,
    ios_device_json_path: str = None,
    android_device_json_path: str = None,
) -> dict:
    """Retrieves a random available device from BrowserStack, filtering by
    devices specified in local JSON files.

    Args:
        mobile_os: The target mobile OS ('ios' or 'android').
        browserstack_user_name: BrowserStack username.
        browserstack_access_key: BrowserStack access key.
        get_browserstack_devices_url: BrowserStack devices API URL.
        ios_device_json_path: Path to the JSON file for iOS devices (required if mobile_os is 'ios').
        android_device_json_path: Path to the JSON file for Android devices (required if mobile_os is 'android').

    Returns:
        A dictionary representing a randomly selected available device.

    Raises:
        ValueError: If any of the required parameters are missing or if no common devices are found.
        Exception: For any other errors while fetching devices.
    """
    try:
        required_params = [
            mobile_os,
            browserstack_user_name,
            browserstack_access_key,
            get_browserstack_devices_url,
        ]
        if any(param is None for param in required_params):
            raise ValueError(
                "Missing required parameters: mobile_os, browserstack_user_name, browserstack_access_key, "
                "and get_browserstack_devices_url are mandatory."
            )

        if mobile_os.lower() == "android" and android_device_json_path is None:
            raise ValueError(
                "android_device_json_path is required when mobile_os is 'android'."
            )
        if mobile_os.lower() == "ios" and ios_device_json_path is None:
            raise ValueError("ios_device_json_path is required when mobile_os is 'ios'.")

        browser_stack_devices = self.get_browser_stack_device_list(
            mobile_os=mobile_os,
            browserstack_user_name=browserstack_user_name,
            browserstack_access_key=browserstack_access_key,
            get_browserstack_devices_url=get_browserstack_devices_url,
        )
        client_devices = self.get_device_list_from_json(
            mobile_os=mobile_os,
            ios_device_json_path=ios_device_json_path,
            android_device_json_path=android_device_json_path,
        )
        common_devices = [
            device for device in browser_stack_devices if device in client_devices
        ]

        if not common_devices:
            raise ValueError(
                "No common devices found between client usage and BrowserStack lists."
            )

        return random.choice(common_devices)
    except Exception as e:
        self.logger.exception("Error while fetching devices: %s", e)
        raise e

get_browser_stack_device_list(mobile_os, browserstack_user_name, browserstack_access_key, get_browserstack_devices_url)

Retrieves a list of available devices from BrowserStack.

Parameters:

Name Type Description Default
mobile_os str

The target mobile OS ('ios' or 'android').

required
browserstack_user_name str

BrowserStack username.

required
browserstack_access_key str

BrowserStack access key.

required
get_browserstack_devices_url str

BrowserStack devices API URL.

required

Returns:

Type Description
List[Dict[str, Any]]

A list of dictionaries, each representing a device.

Raises:

Type Description
Exception

If an error occurs while fetching devices from BrowserStack.

Source code in libs\cafex_ui\src\cafex_ui\mobile_client\mobile_utils.py
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
def get_browser_stack_device_list(
    self,
    mobile_os: str,
    browserstack_user_name: str,
    browserstack_access_key: str,
    get_browserstack_devices_url: str,
) -> List[Dict[str, Any]]:
    """Retrieves a list of available devices from BrowserStack.

    Args:
        mobile_os: The target mobile OS ('ios' or 'android').
        browserstack_user_name: BrowserStack username.
        browserstack_access_key: BrowserStack access key.
        get_browserstack_devices_url: BrowserStack devices API URL.

    Returns:
        A list of dictionaries, each representing a device.

    Raises:
        Exception: If an error occurs while fetching devices from BrowserStack.
    """
    try:
        bs_response = UISecurity().get_browser_stack_devices_list(
            browserstack_user_name=browserstack_user_name,
            browserstack_access_key=browserstack_access_key,
            browserstack_url=get_browserstack_devices_url,
        )
        if bs_response.status_code == 200:
            devices = bs_response.json()
            filtered_devices = [
                {k: v for k, v in device.items() if k not in ("os", "realMobile")}
                for device in devices
                if device["os"] == mobile_os.lower()
            ]
            return filtered_devices

        self.logger.error(
            "Failed to fetch devices from BrowserStack. Response: %s", bs_response.text
        )
        return []
    except Exception as e:
        self.logger.exception("Error fetching devices from BrowserStack: %s", e)
        raise e

get_device_list_from_file(json_file_path)

Reads a JSON file and returns a list of devices.

Parameters:

Name Type Description Default
json_file_path str

The path to the JSON file.

required

Returns:

Type Description
List[Dict[str, Any]]

A list of dictionaries, each representing a device.

Raises:

Type Description
FileNotFoundError

If the specified file does not exist.

ValueError

If the given file is not in JSON format.

Exception

For any other errors during file reading.

Source code in libs\cafex_ui\src\cafex_ui\mobile_client\mobile_utils.py
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
def get_device_list_from_file(self, json_file_path: str) -> List[Dict[str, Any]]:
    """Reads a JSON file and returns a list of devices.

    Args:
        json_file_path (str): The path to the JSON file.

    Returns:
        A list of dictionaries, each representing a device.

    Raises:
        FileNotFoundError: If the specified file does not exist.
        ValueError: If the given file is not in JSON format.
        Exception: For any other errors during file reading.
    """
    try:
        if not os.path.exists(json_file_path):
            raise FileNotFoundError(f"The device list file '{json_file_path}' was not found")

        if not json_file_path.endswith(".json"):
            raise ValueError("Given file is not in JSON format.")

        with open(json_file_path, "r", encoding="utf-8") as json_file:
            json_data = json.load(json_file)
        return json_data

    except (FileNotFoundError, ValueError) as e:
        self.logger.exception("Error reading device list from file: %s", e)
        raise e

    except Exception as e:
        self.logger.exception("Error reading device list from file: %s", e)
        raise e

get_device_list_from_json(mobile_os, ios_device_json_path, android_device_json_path)

Reads a JSON file containing a list of devices.

Parameters:

Name Type Description Default
mobile_os str

The target mobile OS ('ios' or 'android').

required
ios_device_json_path str

Path to the JSON file for iOS devices.

required
android_device_json_path str

Path to the JSON file for Android devices.

required

Returns:

Type Description
List[Dict[str, Any]]

A list of dictionaries, each representing a device.

Raises:

Type Description
FileNotFoundError

If the specified JSON file is not found.

ValueError

If the file is not in JSON format.

Exception

For any other errors during file reading.

Source code in libs\cafex_ui\src\cafex_ui\mobile_client\mobile_utils.py
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
def get_device_list_from_json(
    self, mobile_os: str, ios_device_json_path: str, android_device_json_path: str
) -> List[Dict[str, Any]]:
    """Reads a JSON file containing a list of devices.

    Args:
        mobile_os: The target mobile OS ('ios' or 'android').
        ios_device_json_path: Path to the JSON file for iOS devices.
        android_device_json_path: Path to the JSON file for Android devices.

    Returns:
        A list of dictionaries, each representing a device.

    Raises:
        FileNotFoundError: If the specified JSON file is not found.
        ValueError: If the file is not in JSON format.
        Exception: For any other errors during file reading.
    """
    try:
        file_path = (
            ios_device_json_path if mobile_os.lower() == "ios" else android_device_json_path
        )
        return self.get_device_list_from_file(file_path)
    except Exception as e:
        self.logger.exception("Error getting device list from JSON: %s", e)
        raise e