seleniumbase 4.24.10__py3-none-any.whl → 4.33.15__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- sbase/__init__.py +1 -0
- sbase/steps.py +7 -0
- seleniumbase/__init__.py +16 -7
- seleniumbase/__version__.py +1 -1
- seleniumbase/behave/behave_sb.py +97 -32
- seleniumbase/common/decorators.py +16 -7
- seleniumbase/config/proxy_list.py +3 -3
- seleniumbase/config/settings.py +4 -0
- seleniumbase/console_scripts/logo_helper.py +47 -8
- seleniumbase/console_scripts/run.py +345 -335
- seleniumbase/console_scripts/sb_behave_gui.py +5 -12
- seleniumbase/console_scripts/sb_caseplans.py +6 -13
- seleniumbase/console_scripts/sb_commander.py +5 -12
- seleniumbase/console_scripts/sb_install.py +62 -54
- seleniumbase/console_scripts/sb_mkchart.py +13 -20
- seleniumbase/console_scripts/sb_mkdir.py +11 -17
- seleniumbase/console_scripts/sb_mkfile.py +69 -43
- seleniumbase/console_scripts/sb_mkpres.py +13 -20
- seleniumbase/console_scripts/sb_mkrec.py +88 -21
- seleniumbase/console_scripts/sb_objectify.py +30 -30
- seleniumbase/console_scripts/sb_print.py +5 -12
- seleniumbase/console_scripts/sb_recorder.py +16 -11
- seleniumbase/core/browser_launcher.py +1658 -221
- seleniumbase/core/detect_b_ver.py +7 -8
- seleniumbase/core/log_helper.py +42 -27
- seleniumbase/core/mysql.py +1 -4
- seleniumbase/core/proxy_helper.py +35 -30
- seleniumbase/core/recorder_helper.py +24 -5
- seleniumbase/core/sb_cdp.py +1951 -0
- seleniumbase/core/sb_driver.py +162 -8
- seleniumbase/core/settings_parser.py +6 -0
- seleniumbase/core/style_sheet.py +10 -0
- seleniumbase/extensions/recorder.zip +0 -0
- seleniumbase/fixtures/base_case.py +1234 -632
- seleniumbase/fixtures/constants.py +10 -1
- seleniumbase/fixtures/js_utils.py +171 -144
- seleniumbase/fixtures/page_actions.py +177 -13
- seleniumbase/fixtures/page_utils.py +25 -53
- seleniumbase/fixtures/shared_utils.py +97 -11
- seleniumbase/js_code/active_css_js.py +1 -1
- seleniumbase/js_code/recorder_js.py +1 -1
- seleniumbase/plugins/base_plugin.py +2 -3
- seleniumbase/plugins/driver_manager.py +340 -65
- seleniumbase/plugins/pytest_plugin.py +276 -47
- seleniumbase/plugins/sb_manager.py +412 -99
- seleniumbase/plugins/selenium_plugin.py +122 -17
- seleniumbase/translate/translator.py +0 -7
- seleniumbase/undetected/__init__.py +59 -52
- seleniumbase/undetected/cdp.py +0 -1
- seleniumbase/undetected/cdp_driver/__init__.py +1 -0
- seleniumbase/undetected/cdp_driver/_contradict.py +110 -0
- seleniumbase/undetected/cdp_driver/browser.py +829 -0
- seleniumbase/undetected/cdp_driver/cdp_util.py +458 -0
- seleniumbase/undetected/cdp_driver/config.py +334 -0
- seleniumbase/undetected/cdp_driver/connection.py +639 -0
- seleniumbase/undetected/cdp_driver/element.py +1168 -0
- seleniumbase/undetected/cdp_driver/tab.py +1323 -0
- seleniumbase/undetected/dprocess.py +4 -7
- seleniumbase/undetected/options.py +6 -8
- seleniumbase/undetected/patcher.py +11 -13
- seleniumbase/undetected/reactor.py +0 -1
- seleniumbase/undetected/webelement.py +16 -3
- {seleniumbase-4.24.10.dist-info → seleniumbase-4.33.15.dist-info}/LICENSE +1 -1
- {seleniumbase-4.24.10.dist-info → seleniumbase-4.33.15.dist-info}/METADATA +299 -252
- {seleniumbase-4.24.10.dist-info → seleniumbase-4.33.15.dist-info}/RECORD +68 -70
- {seleniumbase-4.24.10.dist-info → seleniumbase-4.33.15.dist-info}/WHEEL +1 -1
- sbase/ReadMe.txt +0 -2
- seleniumbase/ReadMe.md +0 -25
- seleniumbase/common/ReadMe.md +0 -71
- seleniumbase/console_scripts/ReadMe.md +0 -731
- seleniumbase/drivers/ReadMe.md +0 -27
- seleniumbase/extensions/ReadMe.md +0 -12
- seleniumbase/masterqa/ReadMe.md +0 -61
- seleniumbase/resources/ReadMe.md +0 -31
- seleniumbase/resources/favicon.ico +0 -0
- seleniumbase/utilities/selenium_grid/ReadMe.md +0 -84
- seleniumbase/utilities/selenium_ide/ReadMe.md +0 -111
- {seleniumbase-4.24.10.dist-info → seleniumbase-4.33.15.dist-info}/entry_points.txt +0 -0
- {seleniumbase-4.24.10.dist-info → seleniumbase-4.33.15.dist-info}/top_level.txt +0 -0
@@ -0,0 +1,1951 @@
|
|
1
|
+
"""Add CDP methods to extend the driver"""
|
2
|
+
import fasteners
|
3
|
+
import os
|
4
|
+
import re
|
5
|
+
import sys
|
6
|
+
import time
|
7
|
+
from contextlib import suppress
|
8
|
+
from seleniumbase import config as sb_config
|
9
|
+
from seleniumbase.config import settings
|
10
|
+
from seleniumbase.fixtures import constants
|
11
|
+
from seleniumbase.fixtures import js_utils
|
12
|
+
from seleniumbase.fixtures import page_utils
|
13
|
+
from seleniumbase.fixtures import shared_utils
|
14
|
+
|
15
|
+
|
16
|
+
class CDPMethods():
|
17
|
+
def __init__(self, loop, page, driver):
|
18
|
+
self.loop = loop
|
19
|
+
self.page = page
|
20
|
+
self.driver = driver
|
21
|
+
|
22
|
+
def __slow_mode_pause_if_set(self):
|
23
|
+
if (
|
24
|
+
(hasattr(sb_config, "demo_mode") and sb_config.demo_mode)
|
25
|
+
or "--demo" in sys.argv
|
26
|
+
):
|
27
|
+
time.sleep(0.48)
|
28
|
+
elif (
|
29
|
+
(hasattr(sb_config, "slow_mode") and sb_config.slow_mode)
|
30
|
+
or "--slow" in sys.argv
|
31
|
+
):
|
32
|
+
time.sleep(0.24)
|
33
|
+
|
34
|
+
def __add_light_pause(self):
|
35
|
+
time.sleep(0.007)
|
36
|
+
|
37
|
+
def __convert_to_css_if_xpath(self, selector):
|
38
|
+
if page_utils.is_xpath_selector(selector):
|
39
|
+
with suppress(Exception):
|
40
|
+
css = js_utils.convert_to_css_selector(selector, "xpath")
|
41
|
+
if css:
|
42
|
+
return css
|
43
|
+
return selector
|
44
|
+
|
45
|
+
def __add_sync_methods(self, element):
|
46
|
+
if not element:
|
47
|
+
return element
|
48
|
+
element.clear_input = lambda: self.__clear_input(element)
|
49
|
+
element.click = lambda: self.__click(element)
|
50
|
+
element.flash = lambda *args, **kwargs: self.__flash(
|
51
|
+
element, *args, **kwargs
|
52
|
+
)
|
53
|
+
element.focus = lambda: self.__focus(element)
|
54
|
+
element.highlight_overlay = lambda: self.__highlight_overlay(element)
|
55
|
+
element.mouse_click = lambda: self.__mouse_click(element)
|
56
|
+
element.mouse_drag = (
|
57
|
+
lambda destination: self.__mouse_drag(element, destination)
|
58
|
+
)
|
59
|
+
element.mouse_move = lambda: self.__mouse_move(element)
|
60
|
+
element.query_selector = (
|
61
|
+
lambda selector: self.__query_selector(element, selector)
|
62
|
+
)
|
63
|
+
element.querySelector = element.query_selector
|
64
|
+
element.query_selector_all = (
|
65
|
+
lambda selector: self.__query_selector_all(element, selector)
|
66
|
+
)
|
67
|
+
element.querySelectorAll = element.query_selector_all
|
68
|
+
element.remove_from_dom = lambda: self.__remove_from_dom(element)
|
69
|
+
element.save_screenshot = (
|
70
|
+
lambda *args, **kwargs: self.__save_screenshot(
|
71
|
+
element, *args, **kwargs)
|
72
|
+
)
|
73
|
+
element.save_to_dom = lambda: self.__save_to_dom(element)
|
74
|
+
element.scroll_into_view = lambda: self.__scroll_into_view(element)
|
75
|
+
element.select_option = lambda: self.__select_option(element)
|
76
|
+
element.send_file = (
|
77
|
+
lambda *file_paths: self.__send_file(element, *file_paths)
|
78
|
+
)
|
79
|
+
element.send_keys = lambda text: self.__send_keys(element, text)
|
80
|
+
element.set_text = lambda value: self.__set_text(element, value)
|
81
|
+
element.set_value = lambda value: self.__set_value(element, value)
|
82
|
+
element.type = lambda text: self.__type(element, text)
|
83
|
+
element.get_position = lambda: self.__get_position(element)
|
84
|
+
element.get_html = lambda: self.__get_html(element)
|
85
|
+
element.get_js_attributes = lambda: self.__get_js_attributes(element)
|
86
|
+
element.get_attribute = (
|
87
|
+
lambda attribute: self.__get_attribute(element, attribute)
|
88
|
+
)
|
89
|
+
return element
|
90
|
+
|
91
|
+
def get(self, url):
|
92
|
+
url = shared_utils.fix_url_as_needed(url)
|
93
|
+
driver = self.driver
|
94
|
+
if hasattr(driver, "cdp_base"):
|
95
|
+
driver = driver.cdp_base
|
96
|
+
self.page = self.loop.run_until_complete(driver.get(url))
|
97
|
+
url_protocol = url.split(":")[0]
|
98
|
+
safe_url = True
|
99
|
+
if url_protocol not in ["about", "data", "chrome"]:
|
100
|
+
safe_url = False
|
101
|
+
if not safe_url:
|
102
|
+
time.sleep(constants.UC.CDP_MODE_OPEN_WAIT)
|
103
|
+
if shared_utils.is_windows():
|
104
|
+
time.sleep(constants.UC.EXTRA_WINDOWS_WAIT)
|
105
|
+
else:
|
106
|
+
time.sleep(0.012)
|
107
|
+
self.__slow_mode_pause_if_set()
|
108
|
+
self.loop.run_until_complete(self.page.wait())
|
109
|
+
|
110
|
+
def open(self, url):
|
111
|
+
self.get(url)
|
112
|
+
|
113
|
+
def reload(self, ignore_cache=True, script_to_evaluate_on_load=None):
|
114
|
+
self.loop.run_until_complete(
|
115
|
+
self.page.reload(
|
116
|
+
ignore_cache=ignore_cache,
|
117
|
+
script_to_evaluate_on_load=script_to_evaluate_on_load,
|
118
|
+
)
|
119
|
+
)
|
120
|
+
|
121
|
+
def refresh(self, *args, **kwargs):
|
122
|
+
self.reload(*args, **kwargs)
|
123
|
+
|
124
|
+
def get_event_loop(self):
|
125
|
+
return self.loop
|
126
|
+
|
127
|
+
def add_handler(self, event, handler):
|
128
|
+
self.page.add_handler(event, handler)
|
129
|
+
|
130
|
+
def find_element(self, selector, best_match=False, timeout=None):
|
131
|
+
"""Similar to select(), but also finds elements by text content.
|
132
|
+
When using text-based searches, if best_match=False, then will
|
133
|
+
find the first element with the text. If best_match=True, then
|
134
|
+
if multiple elements have that text, then will use the element
|
135
|
+
with the closest text-length to the text being searched for."""
|
136
|
+
if not timeout:
|
137
|
+
timeout = settings.SMALL_TIMEOUT
|
138
|
+
self.__add_light_pause()
|
139
|
+
selector = self.__convert_to_css_if_xpath(selector)
|
140
|
+
early_failure = False
|
141
|
+
if (":contains(") in selector:
|
142
|
+
selector, _ = page_utils.recalculate_selector(
|
143
|
+
selector, by="css selector", xp_ok=True
|
144
|
+
)
|
145
|
+
failure = False
|
146
|
+
try:
|
147
|
+
if early_failure:
|
148
|
+
raise Exception("Failed!")
|
149
|
+
element = self.loop.run_until_complete(
|
150
|
+
self.page.find(
|
151
|
+
selector, best_match=best_match, timeout=timeout
|
152
|
+
)
|
153
|
+
)
|
154
|
+
except Exception:
|
155
|
+
failure = True
|
156
|
+
plural = "s"
|
157
|
+
if timeout == 1:
|
158
|
+
plural = ""
|
159
|
+
message = "\n Element {%s} was not found after %s second%s!" % (
|
160
|
+
selector,
|
161
|
+
timeout,
|
162
|
+
plural,
|
163
|
+
)
|
164
|
+
if failure:
|
165
|
+
raise Exception(message)
|
166
|
+
element = self.__add_sync_methods(element)
|
167
|
+
self.__slow_mode_pause_if_set()
|
168
|
+
return element
|
169
|
+
|
170
|
+
def find_element_by_text(self, text, tag_name=None, timeout=None):
|
171
|
+
"""Returns an element by matching text.
|
172
|
+
Optionally, provide a tag_name to narrow down the search to an
|
173
|
+
element with the given tag. (Eg: a, button, div, script, span)"""
|
174
|
+
if not timeout:
|
175
|
+
timeout = settings.SMALL_TIMEOUT
|
176
|
+
self.__add_light_pause()
|
177
|
+
time_now = time.time()
|
178
|
+
self.assert_text(text, timeout=timeout)
|
179
|
+
spent = int(time.time() - time_now)
|
180
|
+
remaining = 1 + timeout - spent
|
181
|
+
if tag_name:
|
182
|
+
self.assert_element(tag_name, timeout=remaining)
|
183
|
+
elements = self.loop.run_until_complete(
|
184
|
+
self.page.find_elements_by_text(text=text)
|
185
|
+
)
|
186
|
+
if tag_name:
|
187
|
+
tag_name = tag_name.lower().strip()
|
188
|
+
for element in elements:
|
189
|
+
if element and not tag_name:
|
190
|
+
element = self.__add_sync_methods(element)
|
191
|
+
return self.__add_sync_methods(element)
|
192
|
+
elif (
|
193
|
+
element
|
194
|
+
and tag_name in element.tag_name.lower()
|
195
|
+
and text.strip() in element.text
|
196
|
+
):
|
197
|
+
element = self.__add_sync_methods(element)
|
198
|
+
return self.__add_sync_methods(element)
|
199
|
+
elif (
|
200
|
+
element.parent
|
201
|
+
and tag_name in element.parent.tag_name.lower()
|
202
|
+
and text.strip() in element.parent.text
|
203
|
+
):
|
204
|
+
element = self.__add_sync_methods(element.parent)
|
205
|
+
return self.__add_sync_methods(element)
|
206
|
+
elif (
|
207
|
+
element.parent.parent
|
208
|
+
and tag_name in element.parent.parent.tag_name.lower()
|
209
|
+
and text.strip() in element.parent.parent.text
|
210
|
+
):
|
211
|
+
element = self.__add_sync_methods(element.parent.parent)
|
212
|
+
return self.__add_sync_methods(element)
|
213
|
+
plural = "s"
|
214
|
+
if timeout == 1:
|
215
|
+
plural = ""
|
216
|
+
raise Exception(
|
217
|
+
"Text {%s} with tag {%s} was not found after %s second%s!"
|
218
|
+
% (text, tag_name, timeout, plural)
|
219
|
+
)
|
220
|
+
|
221
|
+
def find_all(self, selector, timeout=None):
|
222
|
+
if not timeout:
|
223
|
+
timeout = settings.SMALL_TIMEOUT
|
224
|
+
self.__add_light_pause()
|
225
|
+
selector = self.__convert_to_css_if_xpath(selector)
|
226
|
+
elements = self.loop.run_until_complete(
|
227
|
+
self.page.find_all(selector, timeout=timeout)
|
228
|
+
)
|
229
|
+
updated_elements = []
|
230
|
+
for element in elements:
|
231
|
+
element = self.__add_sync_methods(element)
|
232
|
+
updated_elements.append(element)
|
233
|
+
return updated_elements
|
234
|
+
|
235
|
+
def find_elements_by_text(self, text, tag_name=None):
|
236
|
+
"""Returns a list of elements by matching text.
|
237
|
+
Optionally, provide a tag_name to narrow down the search to only
|
238
|
+
elements with the given tag. (Eg: a, button, div, script, span)"""
|
239
|
+
self.__add_light_pause()
|
240
|
+
elements = self.loop.run_until_complete(
|
241
|
+
self.page.find_elements_by_text(text=text)
|
242
|
+
)
|
243
|
+
updated_elements = []
|
244
|
+
if tag_name:
|
245
|
+
tag_name = tag_name.lower().strip()
|
246
|
+
for element in elements:
|
247
|
+
if element and not tag_name:
|
248
|
+
element = self.__add_sync_methods(element)
|
249
|
+
if element not in updated_elements:
|
250
|
+
updated_elements.append(element)
|
251
|
+
elif (
|
252
|
+
element
|
253
|
+
and tag_name in element.tag_name.lower()
|
254
|
+
and text.strip() in element.text
|
255
|
+
):
|
256
|
+
element = self.__add_sync_methods(element)
|
257
|
+
if element not in updated_elements:
|
258
|
+
updated_elements.append(element)
|
259
|
+
elif (
|
260
|
+
element.parent
|
261
|
+
and tag_name in element.parent.tag_name.lower()
|
262
|
+
and text.strip() in element.parent.text
|
263
|
+
):
|
264
|
+
element = self.__add_sync_methods(element.parent)
|
265
|
+
if element not in updated_elements:
|
266
|
+
updated_elements.append(element)
|
267
|
+
elif (
|
268
|
+
element.parent.parent
|
269
|
+
and tag_name in element.parent.parent.tag_name.lower()
|
270
|
+
and text.strip() in element.parent.parent.text
|
271
|
+
):
|
272
|
+
element = self.__add_sync_methods(element.parent.parent)
|
273
|
+
if element not in updated_elements:
|
274
|
+
updated_elements.append(element)
|
275
|
+
return updated_elements
|
276
|
+
|
277
|
+
def select(self, selector, timeout=None):
|
278
|
+
"""Similar to find_element(), but without text-based search."""
|
279
|
+
if not timeout:
|
280
|
+
timeout = settings.SMALL_TIMEOUT
|
281
|
+
self.__add_light_pause()
|
282
|
+
selector = self.__convert_to_css_if_xpath(selector)
|
283
|
+
if (":contains(" in selector):
|
284
|
+
tag_name = selector.split(":contains(")[0].split(" ")[-1]
|
285
|
+
text = selector.split(":contains(")[1].split(")")[0][1:-1]
|
286
|
+
with suppress(Exception):
|
287
|
+
self.loop.run_until_complete(
|
288
|
+
self.page.select(tag_name, timeout=5)
|
289
|
+
)
|
290
|
+
self.loop.run_until_complete(self.page.find(text, timeout=5))
|
291
|
+
element = self.find_elements_by_text(text, tag_name=tag_name)[0]
|
292
|
+
return self.__add_sync_methods(element)
|
293
|
+
failure = False
|
294
|
+
try:
|
295
|
+
element = self.loop.run_until_complete(
|
296
|
+
self.page.select(selector, timeout=timeout)
|
297
|
+
)
|
298
|
+
except Exception:
|
299
|
+
failure = True
|
300
|
+
plural = "s"
|
301
|
+
if timeout == 1:
|
302
|
+
plural = ""
|
303
|
+
message = "\n Element {%s} was not found after %s second%s!" % (
|
304
|
+
selector,
|
305
|
+
timeout,
|
306
|
+
plural,
|
307
|
+
)
|
308
|
+
if failure:
|
309
|
+
raise Exception(message)
|
310
|
+
element = self.__add_sync_methods(element)
|
311
|
+
self.__slow_mode_pause_if_set()
|
312
|
+
return element
|
313
|
+
|
314
|
+
def select_all(self, selector, timeout=None):
|
315
|
+
if not timeout:
|
316
|
+
timeout = settings.SMALL_TIMEOUT
|
317
|
+
self.__add_light_pause()
|
318
|
+
selector = self.__convert_to_css_if_xpath(selector)
|
319
|
+
elements = self.loop.run_until_complete(
|
320
|
+
self.page.select_all(selector, timeout=timeout)
|
321
|
+
)
|
322
|
+
updated_elements = []
|
323
|
+
for element in elements:
|
324
|
+
element = self.__add_sync_methods(element)
|
325
|
+
updated_elements.append(element)
|
326
|
+
return updated_elements
|
327
|
+
|
328
|
+
def find_elements(self, selector, timeout=None):
|
329
|
+
if not timeout:
|
330
|
+
timeout = settings.SMALL_TIMEOUT
|
331
|
+
return self.select_all(selector, timeout=timeout)
|
332
|
+
|
333
|
+
def find_visible_elements(self, selector, timeout=None):
|
334
|
+
if not timeout:
|
335
|
+
timeout = settings.SMALL_TIMEOUT
|
336
|
+
visible_elements = []
|
337
|
+
elements = self.select_all(selector, timeout=timeout)
|
338
|
+
for element in elements:
|
339
|
+
with suppress(Exception):
|
340
|
+
position = element.get_position()
|
341
|
+
if (position.width != 0 or position.height != 0):
|
342
|
+
visible_elements.append(element)
|
343
|
+
return visible_elements
|
344
|
+
|
345
|
+
def click_nth_element(self, selector, number):
|
346
|
+
elements = self.select_all(selector)
|
347
|
+
if len(elements) < number:
|
348
|
+
raise Exception(
|
349
|
+
"Not enough matching {%s} elements to "
|
350
|
+
"click number %s!" % (selector, number)
|
351
|
+
)
|
352
|
+
number = number - 1
|
353
|
+
if number < 0:
|
354
|
+
number = 0
|
355
|
+
element = elements[number]
|
356
|
+
element.scroll_into_view()
|
357
|
+
element.click()
|
358
|
+
|
359
|
+
def click_nth_visible_element(self, selector, number):
|
360
|
+
"""Finds all matching page elements and clicks the nth visible one.
|
361
|
+
Example: self.click_nth_visible_element('[type="checkbox"]', 5)
|
362
|
+
(Clicks the 5th visible checkbox on the page.)"""
|
363
|
+
elements = self.find_visible_elements(selector)
|
364
|
+
if len(elements) < number:
|
365
|
+
raise Exception(
|
366
|
+
"Not enough matching {%s} elements to "
|
367
|
+
"click number %s!" % (selector, number)
|
368
|
+
)
|
369
|
+
number = number - 1
|
370
|
+
if number < 0:
|
371
|
+
number = 0
|
372
|
+
element = elements[number]
|
373
|
+
element.scroll_into_view()
|
374
|
+
element.click()
|
375
|
+
|
376
|
+
def click_link(self, link_text):
|
377
|
+
self.find_elements_by_text(link_text, "a")[0].click()
|
378
|
+
|
379
|
+
def go_back(self):
|
380
|
+
self.loop.run_until_complete(self.page.back())
|
381
|
+
|
382
|
+
def go_forward(self):
|
383
|
+
self.loop.run_until_complete(self.page.forward())
|
384
|
+
|
385
|
+
def get_navigation_history(self):
|
386
|
+
return self.loop.run_until_complete(self.page.get_navigation_history())
|
387
|
+
|
388
|
+
def __clear_input(self, element):
|
389
|
+
return (
|
390
|
+
self.loop.run_until_complete(element.clear_input_async())
|
391
|
+
)
|
392
|
+
|
393
|
+
def __click(self, element):
|
394
|
+
result = (
|
395
|
+
self.loop.run_until_complete(element.click_async())
|
396
|
+
)
|
397
|
+
self.loop.run_until_complete(self.page.wait())
|
398
|
+
return result
|
399
|
+
|
400
|
+
def __flash(self, element, *args, **kwargs):
|
401
|
+
element.scroll_into_view()
|
402
|
+
if len(args) < 3 and "x_offset" not in kwargs:
|
403
|
+
x_offset = self.__get_x_scroll_offset()
|
404
|
+
kwargs["x_offset"] = x_offset
|
405
|
+
if len(args) < 3 and "y_offset" not in kwargs:
|
406
|
+
y_offset = self.__get_y_scroll_offset()
|
407
|
+
kwargs["y_offset"] = y_offset
|
408
|
+
return (
|
409
|
+
self.loop.run_until_complete(
|
410
|
+
element.flash_async(*args, **kwargs)
|
411
|
+
)
|
412
|
+
)
|
413
|
+
|
414
|
+
def __focus(self, element):
|
415
|
+
return (
|
416
|
+
self.loop.run_until_complete(element.focus_async())
|
417
|
+
)
|
418
|
+
|
419
|
+
def __highlight_overlay(self, element):
|
420
|
+
return (
|
421
|
+
self.loop.run_until_complete(element.highlight_overlay_async())
|
422
|
+
)
|
423
|
+
|
424
|
+
def __mouse_click(self, element):
|
425
|
+
result = (
|
426
|
+
self.loop.run_until_complete(element.mouse_click_async())
|
427
|
+
)
|
428
|
+
self.loop.run_until_complete(self.page.wait())
|
429
|
+
return result
|
430
|
+
|
431
|
+
def __mouse_drag(self, element, destination):
|
432
|
+
return (
|
433
|
+
self.loop.run_until_complete(element.mouse_drag_async(destination))
|
434
|
+
)
|
435
|
+
|
436
|
+
def __mouse_move(self, element):
|
437
|
+
return (
|
438
|
+
self.loop.run_until_complete(element.mouse_move_async())
|
439
|
+
)
|
440
|
+
|
441
|
+
def __query_selector(self, element, selector):
|
442
|
+
selector = self.__convert_to_css_if_xpath(selector)
|
443
|
+
element2 = self.loop.run_until_complete(
|
444
|
+
element.query_selector_async(selector)
|
445
|
+
)
|
446
|
+
element2 = self.__add_sync_methods(element2)
|
447
|
+
return element2
|
448
|
+
|
449
|
+
def __query_selector_all(self, element, selector):
|
450
|
+
selector = self.__convert_to_css_if_xpath(selector)
|
451
|
+
elements = self.loop.run_until_complete(
|
452
|
+
element.query_selector_all_async(selector)
|
453
|
+
)
|
454
|
+
updated_elements = []
|
455
|
+
for element in elements:
|
456
|
+
element = self.__add_sync_methods(element)
|
457
|
+
updated_elements.append(element)
|
458
|
+
self.__slow_mode_pause_if_set()
|
459
|
+
return updated_elements
|
460
|
+
|
461
|
+
def __remove_from_dom(self, element):
|
462
|
+
return (
|
463
|
+
self.loop.run_until_complete(element.remove_from_dom_async())
|
464
|
+
)
|
465
|
+
|
466
|
+
def __save_screenshot(self, element, *args, **kwargs):
|
467
|
+
return (
|
468
|
+
self.loop.run_until_complete(
|
469
|
+
element.save_screenshot_async(*args, **kwargs)
|
470
|
+
)
|
471
|
+
)
|
472
|
+
|
473
|
+
def __save_to_dom(self, element):
|
474
|
+
return (
|
475
|
+
self.loop.run_until_complete(element.save_to_dom_async())
|
476
|
+
)
|
477
|
+
|
478
|
+
def __scroll_into_view(self, element):
|
479
|
+
self.loop.run_until_complete(element.scroll_into_view_async())
|
480
|
+
self.__add_light_pause()
|
481
|
+
return None
|
482
|
+
|
483
|
+
def __select_option(self, element):
|
484
|
+
return (
|
485
|
+
self.loop.run_until_complete(element.select_option_async())
|
486
|
+
)
|
487
|
+
|
488
|
+
def __send_file(self, element, *file_paths):
|
489
|
+
return (
|
490
|
+
self.loop.run_until_complete(element.send_file_async(*file_paths))
|
491
|
+
)
|
492
|
+
|
493
|
+
def __send_keys(self, element, text):
|
494
|
+
return (
|
495
|
+
self.loop.run_until_complete(element.send_keys_async(text))
|
496
|
+
)
|
497
|
+
|
498
|
+
def __set_text(self, element, value):
|
499
|
+
return (
|
500
|
+
self.loop.run_until_complete(element.set_text_async(value))
|
501
|
+
)
|
502
|
+
|
503
|
+
def __set_value(self, element, value):
|
504
|
+
return (
|
505
|
+
self.loop.run_until_complete(element.set_value_async(value))
|
506
|
+
)
|
507
|
+
|
508
|
+
def __type(self, element, text):
|
509
|
+
with suppress(Exception):
|
510
|
+
element.clear_input()
|
511
|
+
element.send_keys(text)
|
512
|
+
|
513
|
+
def __get_position(self, element):
|
514
|
+
return (
|
515
|
+
self.loop.run_until_complete(element.get_position_async())
|
516
|
+
)
|
517
|
+
|
518
|
+
def __get_html(self, element):
|
519
|
+
return (
|
520
|
+
self.loop.run_until_complete(element.get_html_async())
|
521
|
+
)
|
522
|
+
|
523
|
+
def __get_js_attributes(self, element):
|
524
|
+
return (
|
525
|
+
self.loop.run_until_complete(element.get_js_attributes_async())
|
526
|
+
)
|
527
|
+
|
528
|
+
def __get_attribute(self, element, attribute):
|
529
|
+
try:
|
530
|
+
return element.get_js_attributes()[attribute]
|
531
|
+
except Exception:
|
532
|
+
if not attribute:
|
533
|
+
raise
|
534
|
+
try:
|
535
|
+
attribute_str = element.get_js_attributes()
|
536
|
+
locate = ' %s="' % attribute
|
537
|
+
if locate in attribute_str.outerHTML:
|
538
|
+
outer_html = attribute_str.outerHTML
|
539
|
+
attr_start = outer_html.find(locate) + len(locate)
|
540
|
+
attr_end = outer_html.find('"', attr_start)
|
541
|
+
value = outer_html[attr_start:attr_end]
|
542
|
+
return value
|
543
|
+
except Exception:
|
544
|
+
pass
|
545
|
+
return None
|
546
|
+
|
547
|
+
def __get_x_scroll_offset(self):
|
548
|
+
x_scroll_offset = self.loop.run_until_complete(
|
549
|
+
self.page.evaluate("window.pageXOffset")
|
550
|
+
)
|
551
|
+
return x_scroll_offset or 0
|
552
|
+
|
553
|
+
def __get_y_scroll_offset(self):
|
554
|
+
y_scroll_offset = self.loop.run_until_complete(
|
555
|
+
self.page.evaluate("window.pageYOffset")
|
556
|
+
)
|
557
|
+
return y_scroll_offset or 0
|
558
|
+
|
559
|
+
def tile_windows(self, windows=None, max_columns=0):
|
560
|
+
"""Tile windows and return the grid of tiled windows."""
|
561
|
+
driver = self.driver
|
562
|
+
if hasattr(driver, "cdp_base"):
|
563
|
+
driver = driver.cdp_base
|
564
|
+
return self.loop.run_until_complete(
|
565
|
+
driver.tile_windows(windows, max_columns)
|
566
|
+
)
|
567
|
+
|
568
|
+
def get_all_cookies(self, *args, **kwargs):
|
569
|
+
driver = self.driver
|
570
|
+
if hasattr(driver, "cdp_base"):
|
571
|
+
driver = driver.cdp_base
|
572
|
+
return self.loop.run_until_complete(
|
573
|
+
driver.cookies.get_all(*args, **kwargs)
|
574
|
+
)
|
575
|
+
|
576
|
+
def set_all_cookies(self, *args, **kwargs):
|
577
|
+
driver = self.driver
|
578
|
+
if hasattr(driver, "cdp_base"):
|
579
|
+
driver = driver.cdp_base
|
580
|
+
return self.loop.run_until_complete(
|
581
|
+
driver.cookies.set_all(*args, **kwargs)
|
582
|
+
)
|
583
|
+
|
584
|
+
def save_cookies(self, *args, **kwargs):
|
585
|
+
driver = self.driver
|
586
|
+
if hasattr(driver, "cdp_base"):
|
587
|
+
driver = driver.cdp_base
|
588
|
+
return self.loop.run_until_complete(
|
589
|
+
driver.cookies.save(*args, **kwargs)
|
590
|
+
)
|
591
|
+
|
592
|
+
def load_cookies(self, *args, **kwargs):
|
593
|
+
driver = self.driver
|
594
|
+
if hasattr(driver, "cdp_base"):
|
595
|
+
driver = driver.cdp_base
|
596
|
+
return self.loop.run_until_complete(
|
597
|
+
driver.cookies.load(*args, **kwargs)
|
598
|
+
)
|
599
|
+
|
600
|
+
def clear_cookies(self):
|
601
|
+
driver = self.driver
|
602
|
+
if hasattr(driver, "cdp_base"):
|
603
|
+
driver = driver.cdp_base
|
604
|
+
return self.loop.run_until_complete(
|
605
|
+
driver.cookies.clear()
|
606
|
+
)
|
607
|
+
|
608
|
+
def sleep(self, seconds):
|
609
|
+
time.sleep(seconds)
|
610
|
+
|
611
|
+
def bring_active_window_to_front(self):
|
612
|
+
self.loop.run_until_complete(self.page.bring_to_front())
|
613
|
+
self.__add_light_pause()
|
614
|
+
|
615
|
+
def get_active_element(self):
|
616
|
+
return self.loop.run_until_complete(
|
617
|
+
self.page.js_dumps("document.activeElement")
|
618
|
+
)
|
619
|
+
|
620
|
+
def get_active_element_css(self):
|
621
|
+
from seleniumbase.js_code import active_css_js
|
622
|
+
|
623
|
+
js_code = active_css_js.get_active_element_css
|
624
|
+
js_code = js_code.replace("return getBestSelector", "getBestSelector")
|
625
|
+
return self.loop.run_until_complete(
|
626
|
+
self.page.evaluate(js_code)
|
627
|
+
)
|
628
|
+
|
629
|
+
def click(self, selector, timeout=None):
|
630
|
+
if not timeout:
|
631
|
+
timeout = settings.SMALL_TIMEOUT
|
632
|
+
self.__slow_mode_pause_if_set()
|
633
|
+
element = self.find_element(selector, timeout=timeout)
|
634
|
+
element.scroll_into_view()
|
635
|
+
element.click()
|
636
|
+
self.__slow_mode_pause_if_set()
|
637
|
+
self.loop.run_until_complete(self.page.wait())
|
638
|
+
|
639
|
+
def click_active_element(self):
|
640
|
+
self.loop.run_until_complete(
|
641
|
+
self.page.evaluate("document.activeElement.click()")
|
642
|
+
)
|
643
|
+
self.__slow_mode_pause_if_set()
|
644
|
+
self.loop.run_until_complete(self.page.wait())
|
645
|
+
|
646
|
+
def click_if_visible(self, selector):
|
647
|
+
if self.is_element_visible(selector):
|
648
|
+
with suppress(Exception):
|
649
|
+
element = self.find_element(selector, timeout=0)
|
650
|
+
element.scroll_into_view()
|
651
|
+
element.click()
|
652
|
+
self.__slow_mode_pause_if_set()
|
653
|
+
self.loop.run_until_complete(self.page.wait())
|
654
|
+
|
655
|
+
def click_visible_elements(self, selector, limit=0):
|
656
|
+
"""Finds all matching page elements and clicks visible ones in order.
|
657
|
+
If a click reloads or opens a new page, the clicking will stop.
|
658
|
+
If no matching elements appear, an Exception will be raised.
|
659
|
+
If "limit" is set and > 0, will only click that many elements.
|
660
|
+
Also clicks elements that become visible from previous clicks.
|
661
|
+
Works best for actions such as clicking all checkboxes on a page.
|
662
|
+
Example: self.click_visible_elements('input[type="checkbox"]')"""
|
663
|
+
elements = self.select_all(selector)
|
664
|
+
click_count = 0
|
665
|
+
for element in elements:
|
666
|
+
if limit and limit > 0 and click_count >= limit:
|
667
|
+
return
|
668
|
+
try:
|
669
|
+
width = 0
|
670
|
+
height = 0
|
671
|
+
try:
|
672
|
+
position = element.get_position()
|
673
|
+
width = position.width
|
674
|
+
height = position.height
|
675
|
+
except Exception:
|
676
|
+
continue
|
677
|
+
if (width != 0 or height != 0):
|
678
|
+
element.scroll_into_view()
|
679
|
+
element.click()
|
680
|
+
click_count += 1
|
681
|
+
time.sleep(0.042)
|
682
|
+
self.__slow_mode_pause_if_set()
|
683
|
+
self.loop.run_until_complete(self.page.wait())
|
684
|
+
except Exception:
|
685
|
+
break
|
686
|
+
|
687
|
+
def mouse_click(self, selector, timeout=None):
|
688
|
+
"""(Attempt simulating a mouse click)"""
|
689
|
+
if not timeout:
|
690
|
+
timeout = settings.SMALL_TIMEOUT
|
691
|
+
self.__slow_mode_pause_if_set()
|
692
|
+
element = self.find_element(selector, timeout=timeout)
|
693
|
+
element.scroll_into_view()
|
694
|
+
element.mouse_click()
|
695
|
+
self.__slow_mode_pause_if_set()
|
696
|
+
self.loop.run_until_complete(self.page.wait())
|
697
|
+
|
698
|
+
def nested_click(self, parent_selector, selector):
|
699
|
+
"""
|
700
|
+
Find parent element and click on child element inside it.
|
701
|
+
(This can be used to click on elements inside an iframe.)
|
702
|
+
"""
|
703
|
+
element = self.find_element(parent_selector)
|
704
|
+
element.query_selector(selector).mouse_click()
|
705
|
+
self.__slow_mode_pause_if_set()
|
706
|
+
self.loop.run_until_complete(self.page.wait())
|
707
|
+
|
708
|
+
def get_nested_element(self, parent_selector, selector):
|
709
|
+
"""(Can be used to find an element inside an iframe)"""
|
710
|
+
element = self.find_element(parent_selector)
|
711
|
+
return element.query_selector(selector)
|
712
|
+
|
713
|
+
def select_option_by_text(self, dropdown_selector, option):
|
714
|
+
element = self.find_element(dropdown_selector)
|
715
|
+
element.scroll_into_view()
|
716
|
+
options = element.query_selector_all("option")
|
717
|
+
for found_option in options:
|
718
|
+
if found_option.text.strip() == option.strip():
|
719
|
+
found_option.select_option()
|
720
|
+
return
|
721
|
+
raise Exception(
|
722
|
+
"Unable to find text option {%s} in dropdown {%s}!"
|
723
|
+
% (dropdown_selector, option)
|
724
|
+
)
|
725
|
+
|
726
|
+
def flash(
|
727
|
+
self,
|
728
|
+
selector, # The CSS Selector to flash
|
729
|
+
duration=1, # (seconds) flash duration
|
730
|
+
color="44CC88", # RGB hex flash color
|
731
|
+
pause=0, # (seconds) If 0, the next action starts during flash
|
732
|
+
):
|
733
|
+
"""Paint a quickly-vanishing dot over an element."""
|
734
|
+
selector = self.__convert_to_css_if_xpath(selector)
|
735
|
+
element = self.find_element(selector)
|
736
|
+
element.scroll_into_view()
|
737
|
+
x_offset = self.__get_x_scroll_offset()
|
738
|
+
y_offset = self.__get_y_scroll_offset()
|
739
|
+
element.flash(duration, color, x_offset, y_offset)
|
740
|
+
if pause and isinstance(pause, (int, float)):
|
741
|
+
time.sleep(pause)
|
742
|
+
|
743
|
+
def highlight(self, selector):
|
744
|
+
"""Highlight an element with multi-colors."""
|
745
|
+
selector = self.__convert_to_css_if_xpath(selector)
|
746
|
+
element = self.find_element(selector)
|
747
|
+
element.scroll_into_view()
|
748
|
+
x_offset = self.__get_x_scroll_offset()
|
749
|
+
y_offset = self.__get_y_scroll_offset()
|
750
|
+
element.flash(0.46, "44CC88", x_offset, y_offset)
|
751
|
+
time.sleep(0.15)
|
752
|
+
element.flash(0.42, "8844CC", x_offset, y_offset)
|
753
|
+
time.sleep(0.15)
|
754
|
+
element.flash(0.38, "CC8844", x_offset, y_offset)
|
755
|
+
time.sleep(0.15)
|
756
|
+
element.flash(0.30, "44CC88", x_offset, y_offset)
|
757
|
+
time.sleep(0.30)
|
758
|
+
|
759
|
+
def focus(self, selector):
|
760
|
+
element = self.find_element(selector)
|
761
|
+
element.scroll_into_view()
|
762
|
+
element.focus()
|
763
|
+
|
764
|
+
def highlight_overlay(self, selector):
|
765
|
+
self.find_element(selector).highlight_overlay()
|
766
|
+
|
767
|
+
def remove_element(self, selector):
|
768
|
+
self.select(selector).remove_from_dom()
|
769
|
+
|
770
|
+
def remove_from_dom(self, selector):
|
771
|
+
self.select(selector).remove_from_dom()
|
772
|
+
|
773
|
+
def remove_elements(self, selector):
|
774
|
+
"""Remove all elements on the page that match the selector."""
|
775
|
+
css_selector = self.__convert_to_css_if_xpath(selector)
|
776
|
+
css_selector = re.escape(css_selector) # Add "\\" to special chars
|
777
|
+
css_selector = js_utils.escape_quotes_if_needed(css_selector)
|
778
|
+
js_code = (
|
779
|
+
"""var $elements = document.querySelectorAll('%s');
|
780
|
+
var index = 0, length = $elements.length;
|
781
|
+
for(; index < length; index++){
|
782
|
+
$elements[index].remove();}"""
|
783
|
+
% css_selector
|
784
|
+
)
|
785
|
+
with suppress(Exception):
|
786
|
+
self.loop.run_until_complete(self.page.evaluate(js_code))
|
787
|
+
|
788
|
+
def send_keys(self, selector, text, timeout=None):
|
789
|
+
if not timeout:
|
790
|
+
timeout = settings.SMALL_TIMEOUT
|
791
|
+
self.__slow_mode_pause_if_set()
|
792
|
+
element = self.select(selector, timeout=timeout)
|
793
|
+
element.scroll_into_view()
|
794
|
+
if text.endswith("\n") or text.endswith("\r"):
|
795
|
+
text = text[:-1] + "\r\n"
|
796
|
+
element.send_keys(text)
|
797
|
+
self.__slow_mode_pause_if_set()
|
798
|
+
self.loop.run_until_complete(self.page.wait())
|
799
|
+
|
800
|
+
def press_keys(self, selector, text, timeout=None):
|
801
|
+
"""Similar to send_keys(), but presses keys at human speed."""
|
802
|
+
if not timeout:
|
803
|
+
timeout = settings.SMALL_TIMEOUT
|
804
|
+
self.__slow_mode_pause_if_set()
|
805
|
+
element = self.select(selector, timeout=timeout)
|
806
|
+
element.scroll_into_view()
|
807
|
+
submit = False
|
808
|
+
if text.endswith("\n") or text.endswith("\r"):
|
809
|
+
submit = True
|
810
|
+
text = text[:-1]
|
811
|
+
for key in text:
|
812
|
+
element.send_keys(key)
|
813
|
+
time.sleep(0.044)
|
814
|
+
if submit:
|
815
|
+
element.send_keys("\r\n")
|
816
|
+
time.sleep(0.044)
|
817
|
+
self.__slow_mode_pause_if_set()
|
818
|
+
self.loop.run_until_complete(self.page.wait())
|
819
|
+
|
820
|
+
def type(self, selector, text, timeout=None):
|
821
|
+
"""Similar to send_keys(), but clears the text field first."""
|
822
|
+
if not timeout:
|
823
|
+
timeout = settings.SMALL_TIMEOUT
|
824
|
+
self.__slow_mode_pause_if_set()
|
825
|
+
element = self.select(selector, timeout=timeout)
|
826
|
+
element.scroll_into_view()
|
827
|
+
with suppress(Exception):
|
828
|
+
element.clear_input()
|
829
|
+
if text.endswith("\n") or text.endswith("\r"):
|
830
|
+
text = text[:-1] + "\r\n"
|
831
|
+
element.send_keys(text)
|
832
|
+
self.__slow_mode_pause_if_set()
|
833
|
+
self.loop.run_until_complete(self.page.wait())
|
834
|
+
|
835
|
+
def set_value(self, selector, text, timeout=None):
|
836
|
+
"""Similar to send_keys(), but clears the text field first."""
|
837
|
+
if not timeout:
|
838
|
+
timeout = settings.SMALL_TIMEOUT
|
839
|
+
self.__slow_mode_pause_if_set()
|
840
|
+
selector = self.__convert_to_css_if_xpath(selector)
|
841
|
+
element = self.select(selector, timeout=timeout)
|
842
|
+
element.scroll_into_view()
|
843
|
+
press_enter = False
|
844
|
+
if text.endswith("\n"):
|
845
|
+
text = text[:-1]
|
846
|
+
press_enter = True
|
847
|
+
value = js_utils.escape_quotes_if_needed(re.escape(text))
|
848
|
+
css_selector = re.escape(selector)
|
849
|
+
css_selector = js_utils.escape_quotes_if_needed(css_selector)
|
850
|
+
set_value_script = (
|
851
|
+
"""m_elm = document.querySelector('%s');"""
|
852
|
+
"""m_elm.value = '%s';""" % (css_selector, value)
|
853
|
+
)
|
854
|
+
self.loop.run_until_complete(self.page.evaluate(set_value_script))
|
855
|
+
the_type = self.get_element_attribute(selector, "type")
|
856
|
+
if the_type == "range":
|
857
|
+
# Some input sliders need a mouse event to trigger listeners.
|
858
|
+
with suppress(Exception):
|
859
|
+
mouse_move_script = (
|
860
|
+
"""m_elm = document.querySelector('%s');"""
|
861
|
+
"""m_evt = new Event('mousemove');"""
|
862
|
+
"""m_elm.dispatchEvent(m_evt);""" % css_selector
|
863
|
+
)
|
864
|
+
self.loop.run_until_complete(
|
865
|
+
self.page.evaluate(mouse_move_script)
|
866
|
+
)
|
867
|
+
elif press_enter:
|
868
|
+
self.__add_light_pause()
|
869
|
+
self.send_keys(selector, "\n")
|
870
|
+
self.__slow_mode_pause_if_set()
|
871
|
+
self.loop.run_until_complete(self.page.wait())
|
872
|
+
|
873
|
+
def evaluate(self, expression):
|
874
|
+
"""Run a JavaScript expression and return the result."""
|
875
|
+
if expression.startswith("return "):
|
876
|
+
expression = expression[len("return "):]
|
877
|
+
return self.loop.run_until_complete(
|
878
|
+
self.page.evaluate(expression)
|
879
|
+
)
|
880
|
+
|
881
|
+
def js_dumps(self, obj_name):
|
882
|
+
"""Similar to evaluate(), but for dictionary results."""
|
883
|
+
if obj_name.startswith("return "):
|
884
|
+
obj_name = obj_name[len("return "):]
|
885
|
+
return self.loop.run_until_complete(
|
886
|
+
self.page.js_dumps(obj_name)
|
887
|
+
)
|
888
|
+
|
889
|
+
def maximize(self):
|
890
|
+
if self.get_window()[1].window_state.value == "maximized":
|
891
|
+
return
|
892
|
+
elif self.get_window()[1].window_state.value == "minimized":
|
893
|
+
self.loop.run_until_complete(self.page.maximize())
|
894
|
+
time.sleep(0.044)
|
895
|
+
return self.loop.run_until_complete(self.page.maximize())
|
896
|
+
|
897
|
+
def minimize(self):
|
898
|
+
if self.get_window()[1].window_state.value != "minimized":
|
899
|
+
return self.loop.run_until_complete(self.page.minimize())
|
900
|
+
|
901
|
+
def medimize(self):
|
902
|
+
if self.get_window()[1].window_state.value == "minimized":
|
903
|
+
self.loop.run_until_complete(self.page.medimize())
|
904
|
+
time.sleep(0.044)
|
905
|
+
return self.loop.run_until_complete(self.page.medimize())
|
906
|
+
|
907
|
+
def set_window_rect(self, x, y, width, height):
|
908
|
+
if self.get_window()[1].window_state.value == "minimized":
|
909
|
+
self.loop.run_until_complete(
|
910
|
+
self.page.set_window_size(
|
911
|
+
left=x, top=y, width=width, height=height)
|
912
|
+
)
|
913
|
+
time.sleep(0.044)
|
914
|
+
return self.loop.run_until_complete(
|
915
|
+
self.page.set_window_size(
|
916
|
+
left=x, top=y, width=width, height=height)
|
917
|
+
)
|
918
|
+
|
919
|
+
def reset_window_size(self):
|
920
|
+
x = settings.WINDOW_START_X
|
921
|
+
y = settings.WINDOW_START_Y
|
922
|
+
width = settings.CHROME_START_WIDTH
|
923
|
+
height = settings.CHROME_START_HEIGHT
|
924
|
+
self.set_window_rect(x, y, width, height)
|
925
|
+
self.__add_light_pause()
|
926
|
+
|
927
|
+
def get_window(self):
|
928
|
+
return self.loop.run_until_complete(
|
929
|
+
self.page.get_window()
|
930
|
+
)
|
931
|
+
|
932
|
+
def get_text(self, selector):
|
933
|
+
return self.find_element(selector).text_all
|
934
|
+
|
935
|
+
def get_title(self):
|
936
|
+
return self.loop.run_until_complete(
|
937
|
+
self.page.evaluate("document.title")
|
938
|
+
)
|
939
|
+
|
940
|
+
def get_current_url(self):
|
941
|
+
return self.loop.run_until_complete(
|
942
|
+
self.page.evaluate("window.location.href")
|
943
|
+
)
|
944
|
+
|
945
|
+
def get_origin(self):
|
946
|
+
return self.loop.run_until_complete(
|
947
|
+
self.page.evaluate("window.location.origin")
|
948
|
+
)
|
949
|
+
|
950
|
+
def get_page_source(self):
|
951
|
+
try:
|
952
|
+
source = self.loop.run_until_complete(
|
953
|
+
self.page.evaluate("document.documentElement.outerHTML")
|
954
|
+
)
|
955
|
+
except Exception:
|
956
|
+
time.sleep(constants.UC.CDP_MODE_OPEN_WAIT)
|
957
|
+
source = self.loop.run_until_complete(
|
958
|
+
self.page.evaluate("document.documentElement.outerHTML")
|
959
|
+
)
|
960
|
+
return source
|
961
|
+
|
962
|
+
def get_user_agent(self):
|
963
|
+
return self.loop.run_until_complete(
|
964
|
+
self.page.evaluate("navigator.userAgent")
|
965
|
+
)
|
966
|
+
|
967
|
+
def get_cookie_string(self):
|
968
|
+
return self.loop.run_until_complete(
|
969
|
+
self.page.evaluate("document.cookie")
|
970
|
+
)
|
971
|
+
|
972
|
+
def get_locale_code(self):
|
973
|
+
return self.loop.run_until_complete(
|
974
|
+
self.page.evaluate("navigator.language || navigator.languages[0]")
|
975
|
+
)
|
976
|
+
|
977
|
+
def get_screen_rect(self):
|
978
|
+
coordinates = self.loop.run_until_complete(
|
979
|
+
self.page.js_dumps("window.screen")
|
980
|
+
)
|
981
|
+
return coordinates
|
982
|
+
|
983
|
+
def get_window_rect(self):
|
984
|
+
coordinates = {}
|
985
|
+
innerWidth = self.loop.run_until_complete(
|
986
|
+
self.page.evaluate("window.innerWidth")
|
987
|
+
)
|
988
|
+
innerHeight = self.loop.run_until_complete(
|
989
|
+
self.page.evaluate("window.innerHeight")
|
990
|
+
)
|
991
|
+
outerWidth = self.loop.run_until_complete(
|
992
|
+
self.page.evaluate("window.outerWidth")
|
993
|
+
)
|
994
|
+
outerHeight = self.loop.run_until_complete(
|
995
|
+
self.page.evaluate("window.outerHeight")
|
996
|
+
)
|
997
|
+
pageXOffset = self.loop.run_until_complete(
|
998
|
+
self.page.evaluate("window.pageXOffset")
|
999
|
+
)
|
1000
|
+
pageYOffset = self.loop.run_until_complete(
|
1001
|
+
self.page.evaluate("window.pageYOffset")
|
1002
|
+
)
|
1003
|
+
scrollX = self.loop.run_until_complete(
|
1004
|
+
self.page.evaluate("window.scrollX")
|
1005
|
+
)
|
1006
|
+
scrollY = self.loop.run_until_complete(
|
1007
|
+
self.page.evaluate("window.scrollY")
|
1008
|
+
)
|
1009
|
+
screenLeft = self.loop.run_until_complete(
|
1010
|
+
self.page.evaluate("window.screenLeft")
|
1011
|
+
)
|
1012
|
+
screenTop = self.loop.run_until_complete(
|
1013
|
+
self.page.evaluate("window.screenTop")
|
1014
|
+
)
|
1015
|
+
x = self.loop.run_until_complete(
|
1016
|
+
self.page.evaluate("window.screenX")
|
1017
|
+
)
|
1018
|
+
y = self.loop.run_until_complete(
|
1019
|
+
self.page.evaluate("window.screenY")
|
1020
|
+
)
|
1021
|
+
coordinates["innerWidth"] = innerWidth
|
1022
|
+
coordinates["innerHeight"] = innerHeight
|
1023
|
+
coordinates["outerWidth"] = outerWidth
|
1024
|
+
coordinates["outerHeight"] = outerHeight
|
1025
|
+
coordinates["width"] = outerWidth
|
1026
|
+
coordinates["height"] = outerHeight
|
1027
|
+
coordinates["pageXOffset"] = pageXOffset if pageXOffset else 0
|
1028
|
+
coordinates["pageYOffset"] = pageYOffset if pageYOffset else 0
|
1029
|
+
coordinates["scrollX"] = scrollX if scrollX else 0
|
1030
|
+
coordinates["scrollY"] = scrollY if scrollY else 0
|
1031
|
+
coordinates["screenLeft"] = screenLeft if screenLeft else 0
|
1032
|
+
coordinates["screenTop"] = screenTop if screenTop else 0
|
1033
|
+
coordinates["x"] = x if x else 0
|
1034
|
+
coordinates["y"] = y if y else 0
|
1035
|
+
return coordinates
|
1036
|
+
|
1037
|
+
def get_window_size(self):
|
1038
|
+
coordinates = {}
|
1039
|
+
outerWidth = self.loop.run_until_complete(
|
1040
|
+
self.page.evaluate("window.outerWidth")
|
1041
|
+
)
|
1042
|
+
outerHeight = self.loop.run_until_complete(
|
1043
|
+
self.page.evaluate("window.outerHeight")
|
1044
|
+
)
|
1045
|
+
coordinates["width"] = outerWidth
|
1046
|
+
coordinates["height"] = outerHeight
|
1047
|
+
return coordinates
|
1048
|
+
|
1049
|
+
def get_window_position(self):
|
1050
|
+
coordinates = {}
|
1051
|
+
x = self.loop.run_until_complete(
|
1052
|
+
self.page.evaluate("window.screenX")
|
1053
|
+
)
|
1054
|
+
y = self.loop.run_until_complete(
|
1055
|
+
self.page.evaluate("window.screenY")
|
1056
|
+
)
|
1057
|
+
coordinates["x"] = x if x else 0
|
1058
|
+
coordinates["y"] = y if y else 0
|
1059
|
+
return coordinates
|
1060
|
+
|
1061
|
+
def get_element_rect(self, selector, timeout=None):
|
1062
|
+
if not timeout:
|
1063
|
+
timeout = settings.SMALL_TIMEOUT
|
1064
|
+
selector = self.__convert_to_css_if_xpath(selector)
|
1065
|
+
self.select(selector, timeout=timeout)
|
1066
|
+
self.__add_light_pause()
|
1067
|
+
coordinates = self.loop.run_until_complete(
|
1068
|
+
self.page.js_dumps(
|
1069
|
+
"""document.querySelector"""
|
1070
|
+
"""('%s').getBoundingClientRect()"""
|
1071
|
+
% js_utils.escape_quotes_if_needed(re.escape(selector))
|
1072
|
+
)
|
1073
|
+
)
|
1074
|
+
return coordinates
|
1075
|
+
|
1076
|
+
def get_element_size(self, selector, timeout=None):
|
1077
|
+
if not timeout:
|
1078
|
+
timeout = settings.SMALL_TIMEOUT
|
1079
|
+
element_rect = self.get_element_rect(selector, timeout=timeout)
|
1080
|
+
coordinates = {}
|
1081
|
+
coordinates["width"] = element_rect["width"]
|
1082
|
+
coordinates["height"] = element_rect["height"]
|
1083
|
+
return coordinates
|
1084
|
+
|
1085
|
+
def get_element_position(self, selector, timeout=None):
|
1086
|
+
if not timeout:
|
1087
|
+
timeout = settings.SMALL_TIMEOUT
|
1088
|
+
element_rect = self.get_element_rect(selector, timeout=timeout)
|
1089
|
+
coordinates = {}
|
1090
|
+
coordinates["x"] = element_rect["x"]
|
1091
|
+
coordinates["y"] = element_rect["y"]
|
1092
|
+
return coordinates
|
1093
|
+
|
1094
|
+
def get_gui_element_rect(self, selector, timeout=None):
|
1095
|
+
"""(Coordinates are relative to the screen. Not the window.)"""
|
1096
|
+
if not timeout:
|
1097
|
+
timeout = settings.SMALL_TIMEOUT
|
1098
|
+
element_rect = self.get_element_rect(selector, timeout=timeout)
|
1099
|
+
e_width = element_rect["width"]
|
1100
|
+
e_height = element_rect["height"]
|
1101
|
+
window_rect = self.get_window_rect()
|
1102
|
+
w_bottom_y = window_rect["y"] + window_rect["height"]
|
1103
|
+
viewport_height = window_rect["innerHeight"]
|
1104
|
+
x = window_rect["x"] + element_rect["x"]
|
1105
|
+
y = w_bottom_y - viewport_height + element_rect["y"]
|
1106
|
+
y_scroll_offset = window_rect["pageYOffset"]
|
1107
|
+
y = y - y_scroll_offset
|
1108
|
+
x = x + window_rect["scrollX"]
|
1109
|
+
y = y + window_rect["scrollY"]
|
1110
|
+
return ({"height": e_height, "width": e_width, "x": x, "y": y})
|
1111
|
+
|
1112
|
+
def get_gui_element_center(self, selector, timeout=None):
|
1113
|
+
"""(Coordinates are relative to the screen. Not the window.)"""
|
1114
|
+
if not timeout:
|
1115
|
+
timeout = settings.SMALL_TIMEOUT
|
1116
|
+
element_rect = self.get_gui_element_rect(selector, timeout=timeout)
|
1117
|
+
e_width = element_rect["width"]
|
1118
|
+
e_height = element_rect["height"]
|
1119
|
+
e_x = element_rect["x"]
|
1120
|
+
e_y = element_rect["y"]
|
1121
|
+
return ((e_x + e_width / 2.0) + 0.5, (e_y + e_height / 2.0) + 0.5)
|
1122
|
+
|
1123
|
+
def get_document(self):
|
1124
|
+
return self.loop.run_until_complete(
|
1125
|
+
self.page.get_document()
|
1126
|
+
)
|
1127
|
+
|
1128
|
+
def get_flattened_document(self):
|
1129
|
+
return self.loop.run_until_complete(
|
1130
|
+
self.page.get_flattened_document()
|
1131
|
+
)
|
1132
|
+
|
1133
|
+
def get_element_attributes(self, selector):
|
1134
|
+
selector = self.__convert_to_css_if_xpath(selector)
|
1135
|
+
return self.loop.run_until_complete(
|
1136
|
+
self.page.js_dumps(
|
1137
|
+
"""document.querySelector('%s')"""
|
1138
|
+
% js_utils.escape_quotes_if_needed(re.escape(selector))
|
1139
|
+
)
|
1140
|
+
)
|
1141
|
+
|
1142
|
+
def get_element_attribute(self, selector, attribute):
|
1143
|
+
attributes = self.get_element_attributes(selector)
|
1144
|
+
with suppress(Exception):
|
1145
|
+
return attributes[attribute]
|
1146
|
+
locate = ' %s="' % attribute
|
1147
|
+
value = self.get_attribute(selector, attribute)
|
1148
|
+
if not value and locate not in attributes:
|
1149
|
+
raise KeyError(attribute)
|
1150
|
+
return value
|
1151
|
+
|
1152
|
+
def get_attribute(self, selector, attribute):
|
1153
|
+
return self.find_element(selector).get_attribute(attribute)
|
1154
|
+
|
1155
|
+
def get_element_html(self, selector):
|
1156
|
+
selector = self.__convert_to_css_if_xpath(selector)
|
1157
|
+
return self.loop.run_until_complete(
|
1158
|
+
self.page.evaluate(
|
1159
|
+
"""document.querySelector('%s').outerHTML"""
|
1160
|
+
% js_utils.escape_quotes_if_needed(re.escape(selector))
|
1161
|
+
)
|
1162
|
+
)
|
1163
|
+
|
1164
|
+
def set_locale(self, locale):
|
1165
|
+
"""(Settings will take effect on the next page load)"""
|
1166
|
+
self.loop.run_until_complete(self.page.set_locale(locale))
|
1167
|
+
|
1168
|
+
def set_attributes(self, selector, attribute, value):
|
1169
|
+
"""This method uses JavaScript to set/update a common attribute.
|
1170
|
+
All matching selectors from querySelectorAll() are used.
|
1171
|
+
Example => (Make all links on a website redirect to Google):
|
1172
|
+
self.set_attributes("a", "href", "https://google.com")"""
|
1173
|
+
attribute = re.escape(attribute)
|
1174
|
+
attribute = js_utils.escape_quotes_if_needed(attribute)
|
1175
|
+
value = re.escape(value)
|
1176
|
+
value = js_utils.escape_quotes_if_needed(value)
|
1177
|
+
css_selector = self.__convert_to_css_if_xpath(selector)
|
1178
|
+
css_selector = re.escape(css_selector) # Add "\\" to special chars
|
1179
|
+
css_selector = js_utils.escape_quotes_if_needed(css_selector)
|
1180
|
+
js_code = """var $elements = document.querySelectorAll('%s');
|
1181
|
+
var index = 0, length = $elements.length;
|
1182
|
+
for(; index < length; index++){
|
1183
|
+
$elements[index].setAttribute('%s','%s');}""" % (
|
1184
|
+
css_selector,
|
1185
|
+
attribute,
|
1186
|
+
value,
|
1187
|
+
)
|
1188
|
+
with suppress(Exception):
|
1189
|
+
self.loop.run_until_complete(self.page.evaluate(js_code))
|
1190
|
+
|
1191
|
+
def __make_sure_pyautogui_lock_is_writable(self):
|
1192
|
+
with suppress(Exception):
|
1193
|
+
shared_utils.make_writable(constants.MultiBrowser.PYAUTOGUILOCK)
|
1194
|
+
|
1195
|
+
def __verify_pyautogui_has_a_headed_browser(self):
|
1196
|
+
"""PyAutoGUI requires a headed browser so that it can
|
1197
|
+
focus on the correct element when performing actions."""
|
1198
|
+
driver = self.driver
|
1199
|
+
if hasattr(driver, "cdp_base"):
|
1200
|
+
driver = driver.cdp_base
|
1201
|
+
if driver.config.headless:
|
1202
|
+
raise Exception(
|
1203
|
+
"PyAutoGUI can't be used in headless mode!"
|
1204
|
+
)
|
1205
|
+
|
1206
|
+
def __install_pyautogui_if_missing(self):
|
1207
|
+
self.__verify_pyautogui_has_a_headed_browser()
|
1208
|
+
driver = self.driver
|
1209
|
+
if hasattr(driver, "cdp_base"):
|
1210
|
+
driver = driver.cdp_base
|
1211
|
+
pip_find_lock = fasteners.InterProcessLock(
|
1212
|
+
constants.PipInstall.FINDLOCK
|
1213
|
+
)
|
1214
|
+
with pip_find_lock: # Prevent issues with multiple processes
|
1215
|
+
with suppress(Exception):
|
1216
|
+
shared_utils.make_writable(constants.PipInstall.FINDLOCK)
|
1217
|
+
try:
|
1218
|
+
import pyautogui
|
1219
|
+
with suppress(Exception):
|
1220
|
+
use_pyautogui_ver = constants.PyAutoGUI.VER
|
1221
|
+
if pyautogui.__version__ != use_pyautogui_ver:
|
1222
|
+
del pyautogui
|
1223
|
+
shared_utils.pip_install(
|
1224
|
+
"pyautogui", version=use_pyautogui_ver
|
1225
|
+
)
|
1226
|
+
import pyautogui
|
1227
|
+
except Exception:
|
1228
|
+
print("\nPyAutoGUI required! Installing now...")
|
1229
|
+
shared_utils.pip_install(
|
1230
|
+
"pyautogui", version=constants.PyAutoGUI.VER
|
1231
|
+
)
|
1232
|
+
try:
|
1233
|
+
import pyautogui
|
1234
|
+
except Exception:
|
1235
|
+
if (
|
1236
|
+
shared_utils.is_linux()
|
1237
|
+
and (not sb_config.headed or sb_config.xvfb)
|
1238
|
+
and not driver.config.headless
|
1239
|
+
):
|
1240
|
+
from sbvirtualdisplay import Display
|
1241
|
+
xvfb_width = 1366
|
1242
|
+
xvfb_height = 768
|
1243
|
+
if (
|
1244
|
+
hasattr(sb_config, "_xvfb_width")
|
1245
|
+
and sb_config._xvfb_width
|
1246
|
+
and isinstance(sb_config._xvfb_width, int)
|
1247
|
+
and hasattr(sb_config, "_xvfb_height")
|
1248
|
+
and sb_config._xvfb_height
|
1249
|
+
and isinstance(sb_config._xvfb_height, int)
|
1250
|
+
):
|
1251
|
+
xvfb_width = sb_config._xvfb_width
|
1252
|
+
xvfb_height = sb_config._xvfb_height
|
1253
|
+
if xvfb_width < 1024:
|
1254
|
+
xvfb_width = 1024
|
1255
|
+
sb_config._xvfb_width = xvfb_width
|
1256
|
+
if xvfb_height < 768:
|
1257
|
+
xvfb_height = 768
|
1258
|
+
sb_config._xvfb_height = xvfb_height
|
1259
|
+
with suppress(Exception):
|
1260
|
+
xvfb_display = Display(
|
1261
|
+
visible=True,
|
1262
|
+
size=(xvfb_width, xvfb_height),
|
1263
|
+
backend="xvfb",
|
1264
|
+
use_xauth=True,
|
1265
|
+
)
|
1266
|
+
xvfb_display.start()
|
1267
|
+
|
1268
|
+
def __get_configured_pyautogui(self, pyautogui_copy):
|
1269
|
+
if (
|
1270
|
+
shared_utils.is_linux()
|
1271
|
+
and hasattr(pyautogui_copy, "_pyautogui_x11")
|
1272
|
+
and "DISPLAY" in os.environ.keys()
|
1273
|
+
):
|
1274
|
+
if (
|
1275
|
+
hasattr(sb_config, "_pyautogui_x11_display")
|
1276
|
+
and sb_config._pyautogui_x11_display
|
1277
|
+
and hasattr(pyautogui_copy._pyautogui_x11, "_display")
|
1278
|
+
and (
|
1279
|
+
sb_config._pyautogui_x11_display
|
1280
|
+
== pyautogui_copy._pyautogui_x11._display
|
1281
|
+
)
|
1282
|
+
):
|
1283
|
+
pass
|
1284
|
+
else:
|
1285
|
+
import Xlib.display
|
1286
|
+
pyautogui_copy._pyautogui_x11._display = (
|
1287
|
+
Xlib.display.Display(os.environ['DISPLAY'])
|
1288
|
+
)
|
1289
|
+
sb_config._pyautogui_x11_display = (
|
1290
|
+
pyautogui_copy._pyautogui_x11._display
|
1291
|
+
)
|
1292
|
+
return pyautogui_copy
|
1293
|
+
|
1294
|
+
def gui_press_key(self, key):
|
1295
|
+
self.__install_pyautogui_if_missing()
|
1296
|
+
import pyautogui
|
1297
|
+
pyautogui = self.__get_configured_pyautogui(pyautogui)
|
1298
|
+
gui_lock = fasteners.InterProcessLock(
|
1299
|
+
constants.MultiBrowser.PYAUTOGUILOCK
|
1300
|
+
)
|
1301
|
+
with gui_lock:
|
1302
|
+
self.__make_sure_pyautogui_lock_is_writable()
|
1303
|
+
pyautogui.press(key)
|
1304
|
+
time.sleep(0.044)
|
1305
|
+
self.__slow_mode_pause_if_set()
|
1306
|
+
self.loop.run_until_complete(self.page.wait())
|
1307
|
+
|
1308
|
+
def gui_press_keys(self, keys):
|
1309
|
+
self.__install_pyautogui_if_missing()
|
1310
|
+
import pyautogui
|
1311
|
+
pyautogui = self.__get_configured_pyautogui(pyautogui)
|
1312
|
+
gui_lock = fasteners.InterProcessLock(
|
1313
|
+
constants.MultiBrowser.PYAUTOGUILOCK
|
1314
|
+
)
|
1315
|
+
with gui_lock:
|
1316
|
+
self.__make_sure_pyautogui_lock_is_writable()
|
1317
|
+
for key in keys:
|
1318
|
+
pyautogui.press(key)
|
1319
|
+
time.sleep(0.044)
|
1320
|
+
self.__slow_mode_pause_if_set()
|
1321
|
+
self.loop.run_until_complete(self.page.wait())
|
1322
|
+
|
1323
|
+
def gui_write(self, text):
|
1324
|
+
self.__install_pyautogui_if_missing()
|
1325
|
+
import pyautogui
|
1326
|
+
pyautogui = self.__get_configured_pyautogui(pyautogui)
|
1327
|
+
gui_lock = fasteners.InterProcessLock(
|
1328
|
+
constants.MultiBrowser.PYAUTOGUILOCK
|
1329
|
+
)
|
1330
|
+
with gui_lock:
|
1331
|
+
self.__make_sure_pyautogui_lock_is_writable()
|
1332
|
+
pyautogui.write(text)
|
1333
|
+
self.__slow_mode_pause_if_set()
|
1334
|
+
self.loop.run_until_complete(self.page.wait())
|
1335
|
+
|
1336
|
+
def __gui_click_x_y(self, x, y, timeframe=0.25, uc_lock=False):
|
1337
|
+
self.__install_pyautogui_if_missing()
|
1338
|
+
import pyautogui
|
1339
|
+
pyautogui = self.__get_configured_pyautogui(pyautogui)
|
1340
|
+
screen_width, screen_height = pyautogui.size()
|
1341
|
+
if x < 0 or y < 0 or x > screen_width or y > screen_height:
|
1342
|
+
raise Exception(
|
1343
|
+
"PyAutoGUI cannot click on point (%s, %s)"
|
1344
|
+
" outside screen. (Width: %s, Height: %s)"
|
1345
|
+
% (x, y, screen_width, screen_height)
|
1346
|
+
)
|
1347
|
+
if uc_lock:
|
1348
|
+
gui_lock = fasteners.InterProcessLock(
|
1349
|
+
constants.MultiBrowser.PYAUTOGUILOCK
|
1350
|
+
)
|
1351
|
+
with gui_lock: # Prevent issues with multiple processes
|
1352
|
+
self.__make_sure_pyautogui_lock_is_writable()
|
1353
|
+
pyautogui.moveTo(x, y, timeframe, pyautogui.easeOutQuad)
|
1354
|
+
if timeframe >= 0.25:
|
1355
|
+
time.sleep(0.056) # Wait if moving at human-speed
|
1356
|
+
if "--debug" in sys.argv:
|
1357
|
+
print(" <DEBUG> pyautogui.click(%s, %s)" % (x, y))
|
1358
|
+
pyautogui.click(x=x, y=y)
|
1359
|
+
else:
|
1360
|
+
# Called from a method where the gui_lock is already active
|
1361
|
+
pyautogui.moveTo(x, y, timeframe, pyautogui.easeOutQuad)
|
1362
|
+
if timeframe >= 0.25:
|
1363
|
+
time.sleep(0.056) # Wait if moving at human-speed
|
1364
|
+
if "--debug" in sys.argv:
|
1365
|
+
print(" <DEBUG> pyautogui.click(%s, %s)" % (x, y))
|
1366
|
+
pyautogui.click(x=x, y=y)
|
1367
|
+
|
1368
|
+
def gui_click_x_y(self, x, y, timeframe=0.25):
|
1369
|
+
gui_lock = fasteners.InterProcessLock(
|
1370
|
+
constants.MultiBrowser.PYAUTOGUILOCK
|
1371
|
+
)
|
1372
|
+
with gui_lock: # Prevent issues with multiple processes
|
1373
|
+
self.__make_sure_pyautogui_lock_is_writable()
|
1374
|
+
self.__install_pyautogui_if_missing()
|
1375
|
+
import pyautogui
|
1376
|
+
pyautogui = self.__get_configured_pyautogui(pyautogui)
|
1377
|
+
width_ratio = 1.0
|
1378
|
+
if shared_utils.is_windows():
|
1379
|
+
window_rect = self.get_window_rect()
|
1380
|
+
width = window_rect["width"]
|
1381
|
+
height = window_rect["height"]
|
1382
|
+
win_x = window_rect["x"]
|
1383
|
+
win_y = window_rect["y"]
|
1384
|
+
scr_width = pyautogui.size().width
|
1385
|
+
self.maximize()
|
1386
|
+
self.__add_light_pause()
|
1387
|
+
win_width = self.get_window_size()["width"]
|
1388
|
+
width_ratio = round(float(scr_width) / float(win_width), 2)
|
1389
|
+
width_ratio += 0.01
|
1390
|
+
if width_ratio < 0.45 or width_ratio > 2.55:
|
1391
|
+
width_ratio = 1.01
|
1392
|
+
sb_config._saved_width_ratio = width_ratio
|
1393
|
+
self.minimize()
|
1394
|
+
self.__add_light_pause()
|
1395
|
+
self.set_window_rect(win_x, win_y, width, height)
|
1396
|
+
self.__add_light_pause()
|
1397
|
+
x = x * width_ratio
|
1398
|
+
y = y * width_ratio
|
1399
|
+
self.bring_active_window_to_front()
|
1400
|
+
self.__gui_click_x_y(x, y, timeframe=timeframe, uc_lock=False)
|
1401
|
+
|
1402
|
+
def gui_click_element(self, selector, timeframe=0.25):
|
1403
|
+
self.__slow_mode_pause_if_set()
|
1404
|
+
x, y = self.get_gui_element_center(selector)
|
1405
|
+
self.__add_light_pause()
|
1406
|
+
self.gui_click_x_y(x, y, timeframe=timeframe)
|
1407
|
+
self.__slow_mode_pause_if_set()
|
1408
|
+
self.loop.run_until_complete(self.page.wait())
|
1409
|
+
|
1410
|
+
def __gui_drag_drop(self, x1, y1, x2, y2, timeframe=0.25, uc_lock=False):
|
1411
|
+
self.__install_pyautogui_if_missing()
|
1412
|
+
import pyautogui
|
1413
|
+
pyautogui = self.__get_configured_pyautogui(pyautogui)
|
1414
|
+
screen_width, screen_height = pyautogui.size()
|
1415
|
+
if x1 < 0 or y1 < 0 or x1 > screen_width or y1 > screen_height:
|
1416
|
+
raise Exception(
|
1417
|
+
"PyAutoGUI cannot drag-drop from point (%s, %s)"
|
1418
|
+
" outside screen. (Width: %s, Height: %s)"
|
1419
|
+
% (x1, y1, screen_width, screen_height)
|
1420
|
+
)
|
1421
|
+
if x2 < 0 or y2 < 0 or x2 > screen_width or y2 > screen_height:
|
1422
|
+
raise Exception(
|
1423
|
+
"PyAutoGUI cannot drag-drop to point (%s, %s)"
|
1424
|
+
" outside screen. (Width: %s, Height: %s)"
|
1425
|
+
% (x2, y2, screen_width, screen_height)
|
1426
|
+
)
|
1427
|
+
if uc_lock:
|
1428
|
+
gui_lock = fasteners.InterProcessLock(
|
1429
|
+
constants.MultiBrowser.PYAUTOGUILOCK
|
1430
|
+
)
|
1431
|
+
with gui_lock: # Prevent issues with multiple processes
|
1432
|
+
pyautogui.moveTo(x1, y1, 0.25, pyautogui.easeOutQuad)
|
1433
|
+
self.__add_light_pause()
|
1434
|
+
if "--debug" in sys.argv:
|
1435
|
+
print(" <DEBUG> pyautogui.moveTo(%s, %s)" % (x1, y1))
|
1436
|
+
pyautogui.dragTo(x2, y2, button="left", duration=timeframe)
|
1437
|
+
else:
|
1438
|
+
# Called from a method where the gui_lock is already active
|
1439
|
+
pyautogui.moveTo(x1, y1, 0.25, pyautogui.easeOutQuad)
|
1440
|
+
self.__add_light_pause()
|
1441
|
+
if "--debug" in sys.argv:
|
1442
|
+
print(" <DEBUG> pyautogui.dragTo(%s, %s)" % (x2, y2))
|
1443
|
+
pyautogui.dragTo(x2, y2, button="left", duration=timeframe)
|
1444
|
+
|
1445
|
+
def gui_drag_drop_points(self, x1, y1, x2, y2, timeframe=0.35):
|
1446
|
+
gui_lock = fasteners.InterProcessLock(
|
1447
|
+
constants.MultiBrowser.PYAUTOGUILOCK
|
1448
|
+
)
|
1449
|
+
with gui_lock: # Prevent issues with multiple processes
|
1450
|
+
self.__install_pyautogui_if_missing()
|
1451
|
+
import pyautogui
|
1452
|
+
pyautogui = self.__get_configured_pyautogui(pyautogui)
|
1453
|
+
width_ratio = 1.0
|
1454
|
+
if shared_utils.is_windows():
|
1455
|
+
window_rect = self.get_window_rect()
|
1456
|
+
width = window_rect["width"]
|
1457
|
+
height = window_rect["height"]
|
1458
|
+
win_x = window_rect["x"]
|
1459
|
+
win_y = window_rect["y"]
|
1460
|
+
scr_width = pyautogui.size().width
|
1461
|
+
self.maximize()
|
1462
|
+
self.__add_light_pause()
|
1463
|
+
win_width = self.get_window_size()["width"]
|
1464
|
+
width_ratio = round(float(scr_width) / float(win_width), 2)
|
1465
|
+
width_ratio += 0.01
|
1466
|
+
if width_ratio < 0.45 or width_ratio > 2.55:
|
1467
|
+
width_ratio = 1.01
|
1468
|
+
sb_config._saved_width_ratio = width_ratio
|
1469
|
+
self.minimize()
|
1470
|
+
self.__add_light_pause()
|
1471
|
+
self.set_window_rect(win_x, win_y, width, height)
|
1472
|
+
self.__add_light_pause()
|
1473
|
+
x1 = x1 * width_ratio
|
1474
|
+
y1 = y1 * width_ratio
|
1475
|
+
x2 = x2 * width_ratio
|
1476
|
+
y2 = y2 * width_ratio
|
1477
|
+
self.bring_active_window_to_front()
|
1478
|
+
self.__gui_drag_drop(
|
1479
|
+
x1, y1, x2, y2, timeframe=timeframe, uc_lock=False
|
1480
|
+
)
|
1481
|
+
self.__slow_mode_pause_if_set()
|
1482
|
+
self.loop.run_until_complete(self.page.wait())
|
1483
|
+
|
1484
|
+
def gui_drag_and_drop(self, drag_selector, drop_selector, timeframe=0.35):
|
1485
|
+
self.__slow_mode_pause_if_set()
|
1486
|
+
self.bring_active_window_to_front()
|
1487
|
+
x1, y1 = self.get_gui_element_center(drag_selector)
|
1488
|
+
self.__add_light_pause()
|
1489
|
+
x2, y2 = self.get_gui_element_center(drop_selector)
|
1490
|
+
self.__add_light_pause()
|
1491
|
+
self.gui_drag_drop_points(x1, y1, x2, y2, timeframe=timeframe)
|
1492
|
+
|
1493
|
+
def __gui_hover_x_y(self, x, y, timeframe=0.25, uc_lock=False):
|
1494
|
+
self.__install_pyautogui_if_missing()
|
1495
|
+
import pyautogui
|
1496
|
+
pyautogui = self.__get_configured_pyautogui(pyautogui)
|
1497
|
+
screen_width, screen_height = pyautogui.size()
|
1498
|
+
if x < 0 or y < 0 or x > screen_width or y > screen_height:
|
1499
|
+
raise Exception(
|
1500
|
+
"PyAutoGUI cannot hover on point (%s, %s)"
|
1501
|
+
" outside screen. (Width: %s, Height: %s)"
|
1502
|
+
% (x, y, screen_width, screen_height)
|
1503
|
+
)
|
1504
|
+
if uc_lock:
|
1505
|
+
gui_lock = fasteners.InterProcessLock(
|
1506
|
+
constants.MultiBrowser.PYAUTOGUILOCK
|
1507
|
+
)
|
1508
|
+
with gui_lock: # Prevent issues with multiple processes
|
1509
|
+
pyautogui.moveTo(x, y, timeframe, pyautogui.easeOutQuad)
|
1510
|
+
time.sleep(0.056)
|
1511
|
+
if "--debug" in sys.argv:
|
1512
|
+
print(" <DEBUG> pyautogui.moveTo(%s, %s)" % (x, y))
|
1513
|
+
else:
|
1514
|
+
# Called from a method where the gui_lock is already active
|
1515
|
+
pyautogui.moveTo(x, y, timeframe, pyautogui.easeOutQuad)
|
1516
|
+
time.sleep(0.056)
|
1517
|
+
if "--debug" in sys.argv:
|
1518
|
+
print(" <DEBUG> pyautogui.moveTo(%s, %s)" % (x, y))
|
1519
|
+
|
1520
|
+
def gui_hover_x_y(self, x, y, timeframe=0.25):
|
1521
|
+
gui_lock = fasteners.InterProcessLock(
|
1522
|
+
constants.MultiBrowser.PYAUTOGUILOCK
|
1523
|
+
)
|
1524
|
+
with gui_lock: # Prevent issues with multiple processes
|
1525
|
+
self.__install_pyautogui_if_missing()
|
1526
|
+
import pyautogui
|
1527
|
+
pyautogui = self.__get_configured_pyautogui(pyautogui)
|
1528
|
+
width_ratio = 1.0
|
1529
|
+
if (
|
1530
|
+
shared_utils.is_windows()
|
1531
|
+
and (
|
1532
|
+
not hasattr(sb_config, "_saved_width_ratio")
|
1533
|
+
or not sb_config._saved_width_ratio
|
1534
|
+
)
|
1535
|
+
):
|
1536
|
+
window_rect = self.get_window_rect()
|
1537
|
+
width = window_rect["width"]
|
1538
|
+
height = window_rect["height"]
|
1539
|
+
win_x = window_rect["x"]
|
1540
|
+
win_y = window_rect["y"]
|
1541
|
+
if (
|
1542
|
+
hasattr(sb_config, "_saved_width_ratio")
|
1543
|
+
and sb_config._saved_width_ratio
|
1544
|
+
):
|
1545
|
+
width_ratio = sb_config._saved_width_ratio
|
1546
|
+
else:
|
1547
|
+
scr_width = pyautogui.size().width
|
1548
|
+
self.maximize()
|
1549
|
+
self.__add_light_pause()
|
1550
|
+
win_width = self.get_window_size()["width"]
|
1551
|
+
width_ratio = round(float(scr_width) / float(win_width), 2)
|
1552
|
+
width_ratio += 0.01
|
1553
|
+
if width_ratio < 0.45 or width_ratio > 2.55:
|
1554
|
+
width_ratio = 1.01
|
1555
|
+
sb_config._saved_width_ratio = width_ratio
|
1556
|
+
self.set_window_rect(win_x, win_y, width, height)
|
1557
|
+
self.__add_light_pause()
|
1558
|
+
self.bring_active_window_to_front()
|
1559
|
+
elif (
|
1560
|
+
shared_utils.is_windows()
|
1561
|
+
and hasattr(sb_config, "_saved_width_ratio")
|
1562
|
+
and sb_config._saved_width_ratio
|
1563
|
+
):
|
1564
|
+
width_ratio = sb_config._saved_width_ratio
|
1565
|
+
self.bring_active_window_to_front()
|
1566
|
+
if shared_utils.is_windows():
|
1567
|
+
x = x * width_ratio
|
1568
|
+
y = y * width_ratio
|
1569
|
+
self.__gui_hover_x_y(x, y, timeframe=timeframe, uc_lock=False)
|
1570
|
+
return
|
1571
|
+
self.bring_active_window_to_front()
|
1572
|
+
self.__gui_hover_x_y(x, y, timeframe=timeframe, uc_lock=False)
|
1573
|
+
|
1574
|
+
def gui_hover_element(self, selector, timeframe=0.25):
|
1575
|
+
self.__slow_mode_pause_if_set()
|
1576
|
+
element_rect = self.get_gui_element_rect(selector)
|
1577
|
+
width = element_rect["width"]
|
1578
|
+
height = element_rect["height"]
|
1579
|
+
if width > 0 and height > 0:
|
1580
|
+
x, y = self.get_gui_element_center(selector)
|
1581
|
+
self.bring_active_window_to_front()
|
1582
|
+
self.__gui_hover_x_y(x, y, timeframe=timeframe)
|
1583
|
+
self.__slow_mode_pause_if_set()
|
1584
|
+
self.loop.run_until_complete(self.page.wait())
|
1585
|
+
|
1586
|
+
def gui_hover_and_click(self, hover_selector, click_selector):
|
1587
|
+
gui_lock = fasteners.InterProcessLock(
|
1588
|
+
constants.MultiBrowser.PYAUTOGUILOCK
|
1589
|
+
)
|
1590
|
+
with gui_lock:
|
1591
|
+
self.__make_sure_pyautogui_lock_is_writable()
|
1592
|
+
self.bring_active_window_to_front()
|
1593
|
+
self.gui_hover_element(hover_selector)
|
1594
|
+
time.sleep(0.15)
|
1595
|
+
self.gui_hover_element(click_selector)
|
1596
|
+
self.click(click_selector)
|
1597
|
+
|
1598
|
+
def internalize_links(self):
|
1599
|
+
"""All `target="_blank"` links become `target="_self"`.
|
1600
|
+
This prevents those links from opening in a new tab."""
|
1601
|
+
self.set_attributes('[target="_blank"]', "target", "_self")
|
1602
|
+
|
1603
|
+
def is_checked(self, selector):
|
1604
|
+
"""Return True if checkbox (or radio button) is checked."""
|
1605
|
+
selector = self.__convert_to_css_if_xpath(selector)
|
1606
|
+
self.find_element(selector, timeout=settings.SMALL_TIMEOUT)
|
1607
|
+
return self.get_element_attribute(selector, "checked")
|
1608
|
+
|
1609
|
+
def is_selected(self, selector):
|
1610
|
+
selector = self.__convert_to_css_if_xpath(selector)
|
1611
|
+
return self.is_checked(selector)
|
1612
|
+
|
1613
|
+
def check_if_unchecked(self, selector):
|
1614
|
+
selector = self.__convert_to_css_if_xpath(selector)
|
1615
|
+
if not self.is_checked(selector):
|
1616
|
+
self.click(selector)
|
1617
|
+
|
1618
|
+
def select_if_unselected(self, selector):
|
1619
|
+
selector = self.__convert_to_css_if_xpath(selector)
|
1620
|
+
self.check_if_unchecked(selector)
|
1621
|
+
|
1622
|
+
def uncheck_if_checked(self, selector):
|
1623
|
+
selector = self.__convert_to_css_if_xpath(selector)
|
1624
|
+
if self.is_checked(selector):
|
1625
|
+
self.click(selector)
|
1626
|
+
|
1627
|
+
def unselect_if_selected(self, selector):
|
1628
|
+
selector = self.__convert_to_css_if_xpath(selector)
|
1629
|
+
self.uncheck_if_checked(selector)
|
1630
|
+
|
1631
|
+
def is_element_present(self, selector):
|
1632
|
+
try:
|
1633
|
+
self.select(selector, timeout=0.01)
|
1634
|
+
return True
|
1635
|
+
except Exception:
|
1636
|
+
return False
|
1637
|
+
|
1638
|
+
def is_element_visible(self, selector):
|
1639
|
+
selector = self.__convert_to_css_if_xpath(selector)
|
1640
|
+
element = None
|
1641
|
+
if ":contains(" not in selector:
|
1642
|
+
try:
|
1643
|
+
element = self.select(selector, timeout=0.01)
|
1644
|
+
except Exception:
|
1645
|
+
return False
|
1646
|
+
if not element:
|
1647
|
+
return False
|
1648
|
+
try:
|
1649
|
+
position = element.get_position()
|
1650
|
+
return (position.width != 0 or position.height != 0)
|
1651
|
+
except Exception:
|
1652
|
+
return False
|
1653
|
+
else:
|
1654
|
+
with suppress(Exception):
|
1655
|
+
tag_name = selector.split(":contains(")[0].split(" ")[-1]
|
1656
|
+
text = selector.split(":contains(")[1].split(")")[0][1:-1]
|
1657
|
+
self.loop.run_until_complete(
|
1658
|
+
self.page.select(tag_name, timeout=0.1)
|
1659
|
+
)
|
1660
|
+
self.loop.run_until_complete(self.page.find(text, timeout=0.1))
|
1661
|
+
return True
|
1662
|
+
return False
|
1663
|
+
|
1664
|
+
def wait_for_element_visible(self, selector, timeout=None):
|
1665
|
+
if not timeout:
|
1666
|
+
timeout = settings.SMALL_TIMEOUT
|
1667
|
+
try:
|
1668
|
+
self.select(selector, timeout=timeout)
|
1669
|
+
except Exception:
|
1670
|
+
raise Exception("Element {%s} was not found!" % selector)
|
1671
|
+
for i in range(30):
|
1672
|
+
if self.is_element_visible(selector):
|
1673
|
+
return self.select(selector)
|
1674
|
+
time.sleep(0.1)
|
1675
|
+
raise Exception("Element {%s} was not visible!" % selector)
|
1676
|
+
|
1677
|
+
def assert_element(self, selector, timeout=None):
|
1678
|
+
"""Same as assert_element_visible()"""
|
1679
|
+
if not timeout:
|
1680
|
+
timeout = settings.SMALL_TIMEOUT
|
1681
|
+
try:
|
1682
|
+
self.select(selector, timeout=timeout)
|
1683
|
+
except Exception:
|
1684
|
+
raise Exception("Element {%s} was not found!" % selector)
|
1685
|
+
for i in range(30):
|
1686
|
+
if self.is_element_visible(selector):
|
1687
|
+
return True
|
1688
|
+
time.sleep(0.1)
|
1689
|
+
raise Exception("Element {%s} was not visible!" % selector)
|
1690
|
+
|
1691
|
+
def assert_element_visible(self, selector, timeout=None):
|
1692
|
+
"""Same as assert_element()"""
|
1693
|
+
if not timeout:
|
1694
|
+
timeout = settings.SMALL_TIMEOUT
|
1695
|
+
try:
|
1696
|
+
self.select(selector, timeout=timeout)
|
1697
|
+
except Exception:
|
1698
|
+
raise Exception("Element {%s} was not found!" % selector)
|
1699
|
+
for i in range(30):
|
1700
|
+
if self.is_element_visible(selector):
|
1701
|
+
return True
|
1702
|
+
time.sleep(0.1)
|
1703
|
+
raise Exception("Element {%s} was not visible!" % selector)
|
1704
|
+
|
1705
|
+
def assert_element_present(self, selector, timeout=None):
|
1706
|
+
"""Assert element is present in the DOM. (Visibility NOT required)"""
|
1707
|
+
if not timeout:
|
1708
|
+
timeout = settings.SMALL_TIMEOUT
|
1709
|
+
try:
|
1710
|
+
self.select(selector, timeout=timeout)
|
1711
|
+
except Exception:
|
1712
|
+
raise Exception("Element {%s} was not found!" % selector)
|
1713
|
+
return True
|
1714
|
+
|
1715
|
+
def assert_element_absent(self, selector, timeout=None):
|
1716
|
+
"""Assert element is not present in the DOM."""
|
1717
|
+
if not timeout:
|
1718
|
+
timeout = settings.SMALL_TIMEOUT
|
1719
|
+
start_ms = time.time() * 1000.0
|
1720
|
+
stop_ms = start_ms + (timeout * 1000.0)
|
1721
|
+
for i in range(int(timeout * 10)):
|
1722
|
+
if not self.is_element_present(selector):
|
1723
|
+
return True
|
1724
|
+
now_ms = time.time() * 1000.0
|
1725
|
+
if now_ms >= stop_ms:
|
1726
|
+
break
|
1727
|
+
time.sleep(0.1)
|
1728
|
+
plural = "s"
|
1729
|
+
if timeout == 1:
|
1730
|
+
plural = ""
|
1731
|
+
raise Exception(
|
1732
|
+
"Element {%s} was still present after %s second%s!"
|
1733
|
+
% (selector, timeout, plural)
|
1734
|
+
)
|
1735
|
+
|
1736
|
+
def assert_element_not_visible(self, selector, timeout=None):
|
1737
|
+
"""Assert element is not visible on page. (May still be in DOM)"""
|
1738
|
+
if not timeout:
|
1739
|
+
timeout = settings.SMALL_TIMEOUT
|
1740
|
+
start_ms = time.time() * 1000.0
|
1741
|
+
stop_ms = start_ms + (timeout * 1000.0)
|
1742
|
+
for i in range(int(timeout * 10)):
|
1743
|
+
if not self.is_element_present(selector):
|
1744
|
+
return True
|
1745
|
+
elif not self.is_element_visible(selector):
|
1746
|
+
return True
|
1747
|
+
now_ms = time.time() * 1000.0
|
1748
|
+
if now_ms >= stop_ms:
|
1749
|
+
break
|
1750
|
+
time.sleep(0.1)
|
1751
|
+
plural = "s"
|
1752
|
+
if timeout == 1:
|
1753
|
+
plural = ""
|
1754
|
+
raise Exception(
|
1755
|
+
"Element {%s} was still visible after %s second%s!"
|
1756
|
+
% (selector, timeout, plural)
|
1757
|
+
)
|
1758
|
+
|
1759
|
+
def assert_element_attribute(self, selector, attribute, value=None):
|
1760
|
+
attributes = self.get_element_attributes(selector)
|
1761
|
+
if attribute not in attributes:
|
1762
|
+
raise Exception(
|
1763
|
+
"Attribute {%s} was not found in element {%s}!"
|
1764
|
+
% (attribute, selector)
|
1765
|
+
)
|
1766
|
+
if value and attributes[attribute] != value:
|
1767
|
+
raise Exception(
|
1768
|
+
"Expected value {%s} of attribute {%s} "
|
1769
|
+
"was not found in element {%s}! "
|
1770
|
+
"(Actual value was {%s})"
|
1771
|
+
% (value, attribute, selector, attributes[attribute])
|
1772
|
+
)
|
1773
|
+
|
1774
|
+
def assert_title(self, title):
|
1775
|
+
expected = title.strip()
|
1776
|
+
actual = self.get_title().strip()
|
1777
|
+
error = (
|
1778
|
+
"Expected page title [%s] does not match the actual title [%s]!"
|
1779
|
+
)
|
1780
|
+
try:
|
1781
|
+
if expected != actual:
|
1782
|
+
raise Exception(error % (expected, actual))
|
1783
|
+
except Exception:
|
1784
|
+
time.sleep(2)
|
1785
|
+
actual = self.get_title().strip()
|
1786
|
+
if expected != actual:
|
1787
|
+
raise Exception(error % (expected, actual))
|
1788
|
+
|
1789
|
+
def assert_title_contains(self, substring):
|
1790
|
+
expected = substring.strip()
|
1791
|
+
actual = self.get_title().strip()
|
1792
|
+
error = (
|
1793
|
+
"Expected title substring [%s] does not appear "
|
1794
|
+
"in the actual page title [%s]!"
|
1795
|
+
)
|
1796
|
+
try:
|
1797
|
+
if expected not in actual:
|
1798
|
+
raise Exception(error % (expected, actual))
|
1799
|
+
except Exception:
|
1800
|
+
time.sleep(2)
|
1801
|
+
actual = self.get_title().strip()
|
1802
|
+
if expected not in actual:
|
1803
|
+
raise Exception(error % (expected, actual))
|
1804
|
+
|
1805
|
+
def assert_url(self, url):
|
1806
|
+
expected = url.strip()
|
1807
|
+
actual = self.get_current_url().strip()
|
1808
|
+
error = "Expected URL [%s] does not match the actual URL [%s]!"
|
1809
|
+
try:
|
1810
|
+
if expected != actual:
|
1811
|
+
raise Exception(error % (expected, actual))
|
1812
|
+
except Exception:
|
1813
|
+
time.sleep(2)
|
1814
|
+
actual = self.get_current_url().strip()
|
1815
|
+
if expected != actual:
|
1816
|
+
raise Exception(error % (expected, actual))
|
1817
|
+
|
1818
|
+
def assert_url_contains(self, substring):
|
1819
|
+
expected = substring.strip()
|
1820
|
+
actual = self.get_current_url().strip()
|
1821
|
+
error = (
|
1822
|
+
"Expected URL substring [%s] does not appear "
|
1823
|
+
"in the full URL [%s]!"
|
1824
|
+
)
|
1825
|
+
try:
|
1826
|
+
if expected not in actual:
|
1827
|
+
raise Exception(error % (expected, actual))
|
1828
|
+
except Exception:
|
1829
|
+
time.sleep(2)
|
1830
|
+
actual = self.get_current_url().strip()
|
1831
|
+
if expected not in actual:
|
1832
|
+
raise Exception(error % (expected, actual))
|
1833
|
+
|
1834
|
+
def assert_text(self, text, selector="body", timeout=None):
|
1835
|
+
if not timeout:
|
1836
|
+
timeout = settings.SMALL_TIMEOUT
|
1837
|
+
start_ms = time.time() * 1000.0
|
1838
|
+
stop_ms = start_ms + (timeout * 1000.0)
|
1839
|
+
text = text.strip()
|
1840
|
+
element = None
|
1841
|
+
try:
|
1842
|
+
element = self.find_element(selector, timeout=timeout)
|
1843
|
+
except Exception:
|
1844
|
+
raise Exception("Element {%s} not found!" % selector)
|
1845
|
+
for i in range(int(timeout * 10)):
|
1846
|
+
with suppress(Exception):
|
1847
|
+
element = self.find_element(selector, timeout=0.1)
|
1848
|
+
if text in element.text_all:
|
1849
|
+
return True
|
1850
|
+
now_ms = time.time() * 1000.0
|
1851
|
+
if now_ms >= stop_ms:
|
1852
|
+
break
|
1853
|
+
time.sleep(0.1)
|
1854
|
+
raise Exception(
|
1855
|
+
"Text {%s} not found in {%s}! Actual text: {%s}"
|
1856
|
+
% (text, selector, element.text_all)
|
1857
|
+
)
|
1858
|
+
|
1859
|
+
def assert_exact_text(self, text, selector="body", timeout=None):
|
1860
|
+
if not timeout:
|
1861
|
+
timeout = settings.SMALL_TIMEOUT
|
1862
|
+
start_ms = time.time() * 1000.0
|
1863
|
+
stop_ms = start_ms + (timeout * 1000.0)
|
1864
|
+
text = text.strip()
|
1865
|
+
element = None
|
1866
|
+
try:
|
1867
|
+
element = self.select(selector, timeout=timeout)
|
1868
|
+
except Exception:
|
1869
|
+
raise Exception("Element {%s} not found!" % selector)
|
1870
|
+
for i in range(int(timeout * 10)):
|
1871
|
+
with suppress(Exception):
|
1872
|
+
element = self.select(selector, timeout=0.1)
|
1873
|
+
if (
|
1874
|
+
self.is_element_visible(selector)
|
1875
|
+
and text.strip() == element.text_all.strip()
|
1876
|
+
):
|
1877
|
+
return True
|
1878
|
+
now_ms = time.time() * 1000.0
|
1879
|
+
if now_ms >= stop_ms:
|
1880
|
+
break
|
1881
|
+
time.sleep(0.1)
|
1882
|
+
raise Exception(
|
1883
|
+
"Expected Text {%s}, is not equal to {%s} in {%s}!"
|
1884
|
+
% (text, element.text_all, selector)
|
1885
|
+
)
|
1886
|
+
|
1887
|
+
def assert_true(self, expression):
|
1888
|
+
if not expression:
|
1889
|
+
raise AssertionError("%s is not true" % expression)
|
1890
|
+
|
1891
|
+
def assert_false(self, expression):
|
1892
|
+
if expression:
|
1893
|
+
raise AssertionError("%s is not false" % expression)
|
1894
|
+
|
1895
|
+
def assert_equal(self, first, second):
|
1896
|
+
if first != second:
|
1897
|
+
raise AssertionError("%s is not equal to %s" % (first, second))
|
1898
|
+
|
1899
|
+
def assert_not_equal(self, first, second):
|
1900
|
+
if first == second:
|
1901
|
+
raise AssertionError("%s is equal to %s" % (first, second))
|
1902
|
+
|
1903
|
+
def assert_in(self, first, second):
|
1904
|
+
if first not in second:
|
1905
|
+
raise AssertionError("%s is not in %s" % (first, second))
|
1906
|
+
|
1907
|
+
def assert_not_in(self, first, second):
|
1908
|
+
if first in second:
|
1909
|
+
raise AssertionError("%s is in %s" % (first, second))
|
1910
|
+
|
1911
|
+
def scroll_into_view(self, selector):
|
1912
|
+
self.find_element(selector).scroll_into_view()
|
1913
|
+
self.loop.run_until_complete(self.page.wait())
|
1914
|
+
|
1915
|
+
def scroll_to_y(self, y):
|
1916
|
+
y = int(y)
|
1917
|
+
js_code = "window.scrollTo(0, %s);" % y
|
1918
|
+
with suppress(Exception):
|
1919
|
+
self.loop.run_until_complete(self.page.evaluate(js_code))
|
1920
|
+
self.loop.run_until_complete(self.page.wait())
|
1921
|
+
|
1922
|
+
def scroll_to_top(self):
|
1923
|
+
js_code = "window.scrollTo(0, 0);"
|
1924
|
+
with suppress(Exception):
|
1925
|
+
self.loop.run_until_complete(self.page.evaluate(js_code))
|
1926
|
+
self.loop.run_until_complete(self.page.wait())
|
1927
|
+
|
1928
|
+
def scroll_to_bottom(self):
|
1929
|
+
js_code = "window.scrollTo(0, 10000);"
|
1930
|
+
with suppress(Exception):
|
1931
|
+
self.loop.run_until_complete(self.page.evaluate(js_code))
|
1932
|
+
self.loop.run_until_complete(self.page.wait())
|
1933
|
+
|
1934
|
+
def scroll_up(self, amount=25):
|
1935
|
+
self.loop.run_until_complete(self.page.scroll_up(amount))
|
1936
|
+
self.loop.run_until_complete(self.page.wait())
|
1937
|
+
|
1938
|
+
def scroll_down(self, amount=25):
|
1939
|
+
self.loop.run_until_complete(self.page.scroll_down(amount))
|
1940
|
+
self.loop.run_until_complete(self.page.wait())
|
1941
|
+
|
1942
|
+
def save_screenshot(self, name, folder=None, selector=None):
|
1943
|
+
filename = name
|
1944
|
+
if folder:
|
1945
|
+
filename = os.path.join(folder, name)
|
1946
|
+
if not selector:
|
1947
|
+
self.loop.run_until_complete(
|
1948
|
+
self.page.save_screenshot(filename)
|
1949
|
+
)
|
1950
|
+
else:
|
1951
|
+
self.select(selector).save_screenshot(filename)
|