-
Notifications
You must be signed in to change notification settings - Fork 61
/
utils.py
666 lines (568 loc) · 32.1 KB
/
utils.py
1
2
3
4
5
6
7
8
9
10
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
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
# -*- coding: utf-8 -*-
u"""
Copyright 2015 Telefónica Investigación y Desarrollo, S.A.U.
This file is part of Toolium.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
"""
# Python 2.7
from __future__ import division
import logging
import os
import time
from io import open
import requests
from datetime import datetime
from selenium.common.exceptions import NoSuchElementException, TimeoutException, StaleElementReferenceException
from selenium.webdriver.remote.webelement import WebElement
from selenium.webdriver.support.ui import WebDriverWait
from six.moves.urllib.parse import urlparse # Python 2 and 3 compatibility
from toolium.driver_wrappers_pool import DriverWrappersPool
from toolium.path_utils import get_valid_filename, makedirs_safe
class Utils(object):
_window_size = None #: dict with window width and height
def __init__(self, driver_wrapper=None):
"""Initialize Utils instance
:param driver_wrapper: driver wrapper instance
"""
self.driver_wrapper = driver_wrapper if driver_wrapper else DriverWrappersPool.get_default_wrapper()
# Configure logger
self.logger = logging.getLogger(__name__)
def set_implicitly_wait(self):
"""Read implicitly timeout from configuration properties and configure driver implicitly wait"""
implicitly_wait = self.driver_wrapper.config.get_optional('Driver', 'implicitly_wait')
if implicitly_wait:
self.driver_wrapper.driver.implicitly_wait(implicitly_wait)
def get_explicitly_wait(self):
"""Read explicitly timeout from configuration properties
:returns: configured explicitly timeout (default timeout 10 seconds)
"""
return int(self.driver_wrapper.config.get_optional('Driver', 'explicitly_wait', '10'))
def capture_screenshot(self, name):
"""Capture screenshot and save it in screenshots folder
:param name: screenshot name suffix
:returns: screenshot path
"""
filename = '{0:0=2d}_{1}'.format(DriverWrappersPool.screenshots_number, name)
filename = '{}.png'.format(get_valid_filename(filename))
filepath = os.path.join(DriverWrappersPool.screenshots_directory, filename)
makedirs_safe(DriverWrappersPool.screenshots_directory)
if self.driver_wrapper.driver.get_screenshot_as_file(filepath):
self.logger.info('Screenshot saved in %s', filepath)
DriverWrappersPool.screenshots_number += 1
return filepath
return None
def save_webdriver_logs(self, test_name):
"""Get webdriver logs and write them to log files
:param test_name: test that has generated these logs
"""
try:
log_types = self.driver_wrapper.driver.log_types
except Exception:
# geckodriver does not implement log_types, but it implements get_log for client and server
log_types = ['client', 'server']
self.logger.debug("Reading logs from '%s' and writing them to log files", ', '.join(log_types))
for log_type in log_types:
try:
self.save_webdriver_logs_by_type(log_type, test_name)
except Exception:
# Capture exceptions to avoid errors in teardown method
pass
def save_webdriver_logs_by_type(self, log_type, test_name):
"""Get webdriver logs of the specified type and write them to a log file
:param log_type: browser, client, driver, performance, server, syslog, crashlog or logcat
:param test_name: test that has generated these logs
"""
try:
logs = self.driver_wrapper.driver.get_log(log_type)
except Exception:
return
if len(logs) > 0:
log_file_name = '{}_{}.txt'.format(get_valid_filename(test_name), log_type)
log_file_name = os.path.join(DriverWrappersPool.logs_directory, log_file_name)
with open(log_file_name, 'a+', encoding='utf-8') as log_file:
driver_type = self.driver_wrapper.config.get('Driver', 'type')
log_file.write(
u"\n{} '{}' test logs with driver = {}\n\n".format(datetime.now(), test_name, driver_type))
for entry in logs:
timestamp = datetime.fromtimestamp(float(entry['timestamp']) / 1000.).strftime(
'%Y-%m-%d %H:%M:%S.%f')
log_file.write(u'{}\t{}\t{}\n'.format(timestamp, entry['level'], entry['message'].rstrip()))
def discard_logcat_logs(self):
"""Discard previous logcat logs"""
if self.driver_wrapper.is_android_test():
try:
self.driver_wrapper.driver.get_log('logcat')
except Exception:
pass
def _expected_condition_find_element(self, element):
"""Tries to find the element, but does not thrown an exception if the element is not found
:param element: PageElement or element locator as a tuple (locator_type, locator_value) to be found
:returns: the web element if it has been found or False
:rtype: selenium.webdriver.remote.webelement.WebElement or appium.webdriver.webelement.WebElement
"""
from toolium.pageelements.page_element import PageElement
web_element = False
try:
if isinstance(element, PageElement):
# Use _find_web_element() instead of web_element to avoid logging error message
element._web_element = None
element._find_web_element()
web_element = element._web_element
elif isinstance(element, tuple):
web_element = self.driver_wrapper.driver.find_element(*element)
except NoSuchElementException:
pass
return web_element
def _expected_condition_find_element_visible(self, element):
"""Tries to find the element and checks that it is visible, but does not thrown an exception if the element is
not found
:param element: PageElement or element locator as a tuple (locator_type, locator_value) to be found
:returns: the web element if it is visible or False
:rtype: selenium.webdriver.remote.webelement.WebElement or appium.webdriver.webelement.WebElement
"""
web_element = self._expected_condition_find_element(element)
try:
return web_element if web_element and web_element.is_displayed() else False
except StaleElementReferenceException:
return False
def _expected_condition_find_element_not_visible(self, element):
"""Tries to find the element and checks that it is visible, but does not thrown an exception if the element is
not found
:param element: PageElement or element locator as a tuple (locator_type, locator_value) to be found
:returns: True if the web element is not found or it is not visible
"""
web_element = self._expected_condition_find_element(element)
try:
return True if not web_element or not web_element.is_displayed() else False
except StaleElementReferenceException:
return False
def _expected_condition_find_first_element(self, elements):
"""Try to find sequentially the elements of the list and return the first element found
:param elements: list of PageElements or element locators as a tuple (locator_type, locator_value) to be found
sequentially
:returns: first element found or None
:rtype: toolium.pageelements.PageElement or tuple
"""
from toolium.pageelements.page_element import PageElement
element_found = None
for element in elements:
try:
if isinstance(element, PageElement):
element._web_element = None
element._find_web_element()
else:
self.driver_wrapper.driver.find_element(*element)
element_found = element
break
except (NoSuchElementException, TypeError):
pass
return element_found
def _expected_condition_find_element_clickable(self, element):
"""Tries to find the element and checks that it is clickable, but does not thrown an exception if the element
is not found
:param element: PageElement or element locator as a tuple (locator_type, locator_value) to be found
:returns: the web element if it is clickable or False
:rtype: selenium.webdriver.remote.webelement.WebElement or appium.webdriver.webelement.WebElement
"""
web_element = self._expected_condition_find_element_visible(element)
try:
return web_element if web_element and web_element.is_enabled() else False
except StaleElementReferenceException:
return False
def _expected_condition_find_element_stopped(self, element_times):
"""Tries to find the element and checks that it has stopped moving, but does not thrown an exception if the element
is not found
:param element_times: Tuple with 2 items where:
[0] element: PageElement or element locator as a tuple (locator_type, locator_value) to be found
[1] times: number of iterations checking the element's location that must be the same for all of them
in order to considering the element has stopped
:returns: the web element if it is clickable or False
:rtype: selenium.webdriver.remote.webelement.WebElement or appium.webdriver.webelement.WebElement
"""
element, times = element_times
web_element = self._expected_condition_find_element(element)
try:
locations_list = [tuple(web_element.location.values()) for i in range(int(times)) if not time.sleep(0.001)]
return web_element if set(locations_list) == set(locations_list[-1:]) else False
except StaleElementReferenceException:
return False
def _expected_condition_find_element_containing_text(self, element_text_pair):
"""Tries to find the element and checks that it contains the specified text, but does not thrown an exception if the element is
not found
:param element_text_pair: Tuple with 2 items where:
[0] element: PageElement or element locator as a tuple (locator_type, locator_value) to be found
[1] text: text to be contained into the element
:returns: the web element if it contains the text or False
:rtype: selenium.webdriver.remote.webelement.WebElement or appium.webdriver.webelement.WebElement
"""
element, text = element_text_pair
web_element = self._expected_condition_find_element(element)
try:
return web_element if web_element and text in web_element.text else False
except StaleElementReferenceException:
return False
def _expected_condition_find_element_not_containing_text(self, element_text_pair):
"""Tries to find the element and checks that it does not contain the specified text,
but does not thrown an exception if the element is found
:param element_text_pair: Tuple with 2 items where:
[0] element: PageElement or element locator as a tuple (locator_type, locator_value) to be found
[1] text: text to not be contained into the element
:returns: the web element if it does not contain the text or False
:rtype: selenium.webdriver.remote.webelement.WebElement or appium.webdriver.webelement.WebElement
"""
element, text = element_text_pair
web_element = self._expected_condition_find_element(element)
try:
return web_element if web_element and text not in web_element.text else False
except StaleElementReferenceException:
return False
def _expected_condition_value_in_element_attribute(self, element_attribute_value):
"""Tries to find the element and checks that it contains the requested attribute with the expected value,
but does not thrown an exception if the element is not found
:param element_attribute_value: Tuple with 3 items where:
[0] element: PageElement or element locator as a tuple (locator_type, locator_value) to be found
[1] attribute: element's attribute where to check its value
[2] value: expected value for the element's attribute
:returns: the web element if it contains the expected value for the requested attribute or False
:rtype: selenium.webdriver.remote.webelement.WebElement or appium.webdriver.webelement.WebElement
"""
element, attribute, value = element_attribute_value
web_element = self._expected_condition_find_element(element)
try:
return web_element if web_element and web_element.get_attribute(attribute) == value else False
except StaleElementReferenceException:
return False
def _wait_until(self, condition_method, condition_input, timeout=None):
"""
Common method to wait until condition met
:param condition_method: method to check the condition
:param condition_input: parameter that will be passed to the condition method
:param timeout: max time to wait
:returns: condition method response
"""
# Remove implicitly wait timeout
self.driver_wrapper.driver.implicitly_wait(0)
# Get explicitly wait timeout
timeout = timeout if timeout else self.get_explicitly_wait()
# Wait for condition
condition_response = WebDriverWait(self.driver_wrapper.driver, timeout).until(
lambda s: condition_method(condition_input))
# Restore implicitly wait timeout from properties
self.set_implicitly_wait()
return condition_response
def wait_until_element_present(self, element, timeout=None):
"""Search element and wait until it is found
:param element: PageElement or element locator as a tuple (locator_type, locator_value) to be found
:param timeout: max time to wait
:returns: the web element if it is present
:rtype: selenium.webdriver.remote.webelement.WebElement or appium.webdriver.webelement.WebElement
:raises TimeoutException: If the element is not found after the timeout
"""
return self._wait_until(self._expected_condition_find_element, element, timeout)
def wait_until_element_visible(self, element, timeout=None):
"""Search element and wait until it is visible
:param element: PageElement or element locator as a tuple (locator_type, locator_value) to be found
:param timeout: max time to wait
:returns: the web element if it is visible
:rtype: selenium.webdriver.remote.webelement.WebElement or appium.webdriver.webelement.WebElement
:raises TimeoutException: If the element is still not visible after the timeout
"""
return self._wait_until(self._expected_condition_find_element_visible, element, timeout)
def wait_until_element_not_visible(self, element, timeout=None):
"""Search element and wait until it is not visible
:param element: PageElement or element locator as a tuple (locator_type, locator_value) to be found
:param timeout: max time to wait
:returns: the web element if it exists but is not visible
:rtype: selenium.webdriver.remote.webelement.WebElement or appium.webdriver.webelement.WebElement
:raises TimeoutException: If the element is still visible after the timeout
"""
return self._wait_until(self._expected_condition_find_element_not_visible, element, timeout)
def wait_until_first_element_is_found(self, elements, timeout=None):
"""Search list of elements and wait until one of them is found
:param elements: list of PageElements or element locators as a tuple (locator_type, locator_value) to be found
sequentially
:param timeout: max time to wait
:returns: first element found
:rtype: toolium.pageelements.PageElement or tuple
:raises TimeoutException: If no element in the list is found after the timeout
"""
try:
return self._wait_until(self._expected_condition_find_first_element, elements, timeout)
except TimeoutException as exception:
msg = 'None of the page elements has been found after %s seconds'
timeout = timeout if timeout else self.get_explicitly_wait()
self.logger.error(msg, timeout)
exception.msg += "\n {}".format(msg % timeout)
raise exception
def wait_until_element_clickable(self, element, timeout=None):
"""Search element and wait until it is clickable
:param element: PageElement or element locator as a tuple (locator_type, locator_value) to be found
:param timeout: max time to wait
:returns: the web element if it is clickable
:rtype: selenium.webdriver.remote.webelement.WebElement or appium.webdriver.webelement.WebElement
:raises TimeoutException: If the element is not clickable after the timeout
"""
return self._wait_until(self._expected_condition_find_element_clickable, element, timeout)
def wait_until_element_stops(self, element, times=1000, timeout=None):
"""Search element and wait until it has stopped moving
:param element: PageElement or element locator as a tuple (locator_type, locator_value) to be found
:param times: number of iterations checking the element's location that must be the same for all of them
in order to considering the element has stopped
:returns: the web element if the element is stopped
:rtype: selenium.webdriver.remote.webelement.WebElement or appium.webdriver.webelement.WebElement
:raises TimeoutException: If the element does not stop after the timeout
"""
return self._wait_until(self._expected_condition_find_element_stopped, (element, times), timeout)
def wait_until_element_contains_text(self, element, text, timeout=None):
"""Search element and wait until it contains the expected text
:param element: PageElement or element locator as a tuple (locator_type, locator_value) to be found
:param text: text expected to be contained into the element
:param timeout: max time to wait
:returns: the web element if it contains the expected text
:rtype: selenium.webdriver.remote.webelement.WebElement or appium.webdriver.webelement.WebElement
:raises TimeoutException: If the element does not contain the expected text after the timeout
"""
return self._wait_until(self._expected_condition_find_element_containing_text, (element, text), timeout)
def wait_until_element_not_contain_text(self, element, text, timeout=None):
"""Search element and wait until it does not contain the expected text
:param element: PageElement or element locator as a tuple (locator_type, locator_value) to be found
:param text: text expected to be contained into the element
:param timeout: max time to wait
:returns: the web element if it does not contain the given text
:rtype: selenium.webdriver.remote.webelement.WebElement or appium.webdriver.webelement.WebElement
:raises TimeoutException: If the element contains the expected text after the timeout
"""
return self._wait_until(self._expected_condition_find_element_not_containing_text, (element, text), timeout)
def wait_until_element_attribute_is(self, element, attribute, value, timeout=None):
"""Search element and wait until the requested attribute contains the expected value
:param element: PageElement or element locator as a tuple (locator_type, locator_value) to be found
:param attribute: attribute belonging to the element
:param value: expected value for the attribute of the element
:param timeout: max time to wait
:returns: the web element if the element's attribute contains the expected value
:rtype: selenium.webdriver.remote.webelement.WebElement or appium.webdriver.webelement.WebElement
:raises TimeoutException: If the element's attribute does not contain the expected value after the timeout
"""
return self._wait_until(self._expected_condition_value_in_element_attribute, (element, attribute, value), timeout)
def get_remote_node(self):
"""Return the remote node that it's executing the actual test session
:returns: tuple with server type (local, grid, ggr, selenium) and remote node name
"""
logging.getLogger("requests").setLevel(logging.WARNING)
remote_node = None
server_type = 'local'
if self.driver_wrapper.config.getboolean_optional('Server', 'enabled'):
# Request session info from grid hub
session_id = self.driver_wrapper.driver.session_id
self.logger.debug("Trying to identify remote node")
try:
# Request session info from grid hub and extract remote node
url = '{}/grid/api/testsession?session={}'.format(self.get_server_url(),
session_id)
proxy_id = requests.get(url).json()['proxyId']
remote_node = urlparse(proxy_id).hostname if urlparse(proxy_id).hostname else proxy_id
server_type = 'grid'
self.logger.debug("Test running in remote node %s", remote_node)
except (ValueError, KeyError):
try:
# Request session info from GGR and extract remote node
from toolium.selenoid import Selenoid
remote_node = Selenoid(self.driver_wrapper).get_selenoid_info()['Name']
server_type = 'ggr'
self.logger.debug("Test running in a GGR remote node %s", remote_node)
except Exception:
try:
# The remote node is a Selenoid node
url = '{}/status'.format(self.get_server_url())
requests.get(url).json()['total']
remote_node = self.driver_wrapper.config.get('Server', 'host')
server_type = 'selenoid'
self.logger.debug("Test running in a Selenoid node %s", remote_node)
except Exception:
# The remote node is not a grid node or the session has been closed
remote_node = self.driver_wrapper.config.get('Server', 'host')
server_type = 'selenium'
self.logger.debug("Test running in a Selenium node %s", remote_node)
return server_type, remote_node
def get_server_url(self):
"""Return the configured server url
:returns: server url
"""
server_host = self.driver_wrapper.config.get('Server', 'host')
server_port = self.driver_wrapper.config.get('Server', 'port')
server_username = self.driver_wrapper.config.get_optional('Server', 'username')
server_password = self.driver_wrapper.config.get_optional('Server', 'password')
server_auth = '{}:{}@'.format(server_username, server_password) if server_username and server_password else ''
server_url = 'http://{}{}:{}'.format(server_auth, server_host, server_port)
return server_url
def download_remote_video(self, remote_node, session_id, video_name):
"""Download the video recorded in the remote node during the specified test session and save it in videos folder
:param remote_node: remote node name
:param session_id: test session id
:param video_name: video name
"""
try:
video_url = self._get_remote_video_url(remote_node, session_id)
except requests.exceptions.ConnectionError:
self.logger.warning("Remote server seems not to have video capabilities")
return
if not video_url:
self.logger.warning("Test video not found in node '%s'", remote_node)
return
self._download_video(video_url, video_name)
def _get_remote_node_url(self, remote_node):
"""Get grid-extras url of a node
:param remote_node: remote node name
:returns: grid-extras url
"""
logging.getLogger("requests").setLevel(logging.WARNING)
gridextras_port = 3000
return 'http://{}:{}'.format(remote_node, gridextras_port)
def _get_remote_video_url(self, remote_node, session_id):
"""Get grid-extras url to download videos
:param remote_node: remote node name
:param session_id: test session id
:returns: grid-extras url to download videos
"""
url = '{}/video'.format(self._get_remote_node_url(remote_node))
timeout = time.time() + 5 # 5 seconds from now
# Requests videos list until timeout or the video url is found
video_url = None
while time.time() < timeout:
response = requests.get(url).json()
try:
video_url = response['available_videos'][session_id]['download_url']
break
except KeyError:
time.sleep(1)
return video_url
def _download_video(self, video_url, video_name):
"""Download a video from the remote node
:param video_url: video url
:param video_name: video name
"""
filename = '{0:0=2d}_{1}'.format(DriverWrappersPool.videos_number, video_name)
filename = '{}.mp4'.format(get_valid_filename(filename))
filepath = os.path.join(DriverWrappersPool.videos_directory, filename)
makedirs_safe(DriverWrappersPool.videos_directory)
response = requests.get(video_url)
open(filepath, 'wb').write(response.content)
self.logger.info("Video saved in '%s'", filepath)
DriverWrappersPool.videos_number += 1
def is_remote_video_enabled(self, remote_node):
"""Check if the remote node has the video recorder enabled
:param remote_node: remote node name
:returns: true if it has the video recorder enabled
"""
enabled = False
if remote_node:
url = '{}/config'.format(self._get_remote_node_url(remote_node))
try:
response = requests.get(url, timeout=5).json()
record_videos = response['config_runtime']['theConfigMap']['video_recording_options'][
'record_test_videos']
except (requests.exceptions.ConnectionError, requests.exceptions.ReadTimeout, KeyError):
record_videos = 'false'
if record_videos == 'true':
# Wait to the video recorder start
time.sleep(1)
enabled = True
return enabled
def get_center(self, element):
"""Get center coordinates of an element
:param element: either a WebElement, PageElement or element locator as a tuple (locator_type, locator_value)
:returns: dict with center coordinates
"""
web_element = self.get_web_element(element)
location = web_element.location
size = web_element.size
return {'x': location['x'] + (size['width'] / 2), 'y': location['y'] + (size['height'] / 2)}
def get_safari_navigation_bar_height(self):
"""Get the height of Safari navigation bar
:returns: height of navigation bar
"""
status_bar_height = 0
if self.driver_wrapper.is_ios_test() and self.driver_wrapper.is_web_test():
# ios 7.1, 8.3
status_bar_height = 64
return status_bar_height
def get_window_size(self):
"""Generic method to get window size using a javascript workaround for Android web tests
:returns: dict with window width and height
"""
if not self._window_size:
if self.driver_wrapper.is_android_web_test() and self.driver_wrapper.driver.current_context != 'NATIVE_APP':
window_width = self.driver_wrapper.driver.execute_script("return window.innerWidth")
window_height = self.driver_wrapper.driver.execute_script("return window.innerHeight")
self._window_size = {'width': window_width, 'height': window_height}
else:
self._window_size = self.driver_wrapper.driver.get_window_size()
return self._window_size
def get_native_coords(self, coords):
"""Convert web coords into native coords. Assumes that the initial context is WEBVIEW and switches to
NATIVE_APP context.
:param coords: dict with web coords, e.g. {'x': 10, 'y': 10}
:returns: dict with native coords
"""
web_window_size = self.get_window_size()
self.driver_wrapper.driver.switch_to.context('NATIVE_APP')
native_window_size = self.driver_wrapper.driver.get_window_size()
scale = native_window_size['width'] / web_window_size['width']
offset_y = self.get_safari_navigation_bar_height()
native_coords = {'x': coords['x'] * scale, 'y': coords['y'] * scale + offset_y}
self.logger.debug('Converted web coords %s into native coords %s', coords, native_coords)
return native_coords
def swipe(self, element, x, y, duration=None):
"""Swipe over an element
:param element: either a WebElement, PageElement or element locator as a tuple (locator_type, locator_value)
:param x: horizontal movement
:param y: vertical movement
:param duration: time to take the swipe, in ms
"""
if not self.driver_wrapper.is_mobile_test():
raise Exception('Swipe method is not implemented in Selenium')
# Get center coordinates of element
center = self.get_center(element)
initial_context = self.driver_wrapper.driver.current_context
if self.driver_wrapper.is_web_test() or initial_context != 'NATIVE_APP':
center = self.get_native_coords(center)
# Android needs absolute end coordinates and ios needs movement
end_x = x if self.driver_wrapper.is_ios_test() else center['x'] + x
end_y = y if self.driver_wrapper.is_ios_test() else center['y'] + y
self.driver_wrapper.driver.swipe(center['x'], center['y'], end_x, end_y, duration)
if self.driver_wrapper.is_web_test() or initial_context != 'NATIVE_APP':
self.driver_wrapper.driver.switch_to.context(initial_context)
def get_web_element(self, element):
"""Return the web element from a page element or its locator
:param element: either a WebElement, PageElement or element locator as a tuple (locator_type, locator_value)
:returns: WebElement object
"""
from toolium.pageelements.page_element import PageElement
if isinstance(element, WebElement):
web_element = element
elif isinstance(element, PageElement):
web_element = element.web_element
elif isinstance(element, tuple):
web_element = self.driver_wrapper.driver.find_element(*element)
else:
web_element = None
return web_element
def get_first_webview_context(self):
"""Return the first WEBVIEW context or raise an exception if it is not found
:returns: first WEBVIEW context
"""
for context in self.driver_wrapper.driver.contexts:
if context.startswith('WEBVIEW'):
return context
raise Exception('No WEBVIEW context has been found')
def switch_to_first_webview_context(self):
"""Switch to the first WEBVIEW context"""
self.driver_wrapper.driver.switch_to.context(self.get_first_webview_context())