mops 3.1.0__tar.gz → 3.2.0__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {mops-3.1.0 → mops-3.2.0}/PKG-INFO +1 -1
- {mops-3.1.0 → mops-3.2.0}/mops/__init__.py +1 -1
- {mops-3.1.0 → mops-3.2.0}/mops/abstraction/element_abc.py +45 -9
- {mops-3.1.0 → mops-3.2.0}/mops/base/element.py +143 -11
- {mops-3.1.0 → mops-3.2.0}/mops/exceptions.py +7 -0
- {mops-3.1.0 → mops-3.2.0}/mops/js_scripts.py +24 -14
- {mops-3.1.0 → mops-3.2.0}/mops/playwright/play_element.py +4 -94
- {mops-3.1.0 → mops-3.2.0}/mops/selenium/core/core_element.py +31 -118
- mops-3.2.0/mops/utils/decorators.py +130 -0
- {mops-3.1.0 → mops-3.2.0}/mops/utils/internal_utils.py +2 -36
- {mops-3.1.0 → mops-3.2.0}/mops.egg-info/PKG-INFO +1 -1
- {mops-3.1.0 → mops-3.2.0}/mops.egg-info/SOURCES.txt +1 -0
- {mops-3.1.0 → mops-3.2.0}/README.md +0 -0
- {mops-3.1.0 → mops-3.2.0}/mops/abstraction/driver_wrapper_abc.py +0 -0
- {mops-3.1.0 → mops-3.2.0}/mops/abstraction/mixin_abc.py +0 -0
- {mops-3.1.0 → mops-3.2.0}/mops/abstraction/page_abc.py +0 -0
- {mops-3.1.0 → mops-3.2.0}/mops/base/driver_wrapper.py +0 -0
- {mops-3.1.0 → mops-3.2.0}/mops/base/group.py +0 -0
- {mops-3.1.0 → mops-3.2.0}/mops/base/page.py +0 -0
- {mops-3.1.0 → mops-3.2.0}/mops/keyboard_keys.py +0 -0
- {mops-3.1.0 → mops-3.2.0}/mops/mixins/driver_mixin.py +0 -0
- {mops-3.1.0 → mops-3.2.0}/mops/mixins/internal_mixin.py +0 -0
- {mops-3.1.0 → mops-3.2.0}/mops/mixins/native_context.py +0 -0
- {mops-3.1.0 → mops-3.2.0}/mops/mixins/objects/box.py +0 -0
- {mops-3.1.0 → mops-3.2.0}/mops/mixins/objects/driver.py +0 -0
- {mops-3.1.0 → mops-3.2.0}/mops/mixins/objects/location.py +0 -0
- {mops-3.1.0 → mops-3.2.0}/mops/mixins/objects/locator.py +0 -0
- {mops-3.1.0 → mops-3.2.0}/mops/mixins/objects/locator_type.py +0 -0
- {mops-3.1.0 → mops-3.2.0}/mops/mixins/objects/scrolls.py +0 -0
- {mops-3.1.0 → mops-3.2.0}/mops/mixins/objects/size.py +0 -0
- {mops-3.1.0 → mops-3.2.0}/mops/mixins/objects/wait_result.py +0 -0
- {mops-3.1.0 → mops-3.2.0}/mops/playwright/play_driver.py +0 -0
- {mops-3.1.0 → mops-3.2.0}/mops/playwright/play_page.py +0 -0
- {mops-3.1.0 → mops-3.2.0}/mops/selenium/core/core_driver.py +0 -0
- {mops-3.1.0 → mops-3.2.0}/mops/selenium/core/core_page.py +0 -0
- {mops-3.1.0 → mops-3.2.0}/mops/selenium/driver/mobile_driver.py +0 -0
- {mops-3.1.0 → mops-3.2.0}/mops/selenium/driver/web_driver.py +0 -0
- {mops-3.1.0 → mops-3.2.0}/mops/selenium/elements/mobile_element.py +0 -0
- {mops-3.1.0 → mops-3.2.0}/mops/selenium/elements/web_element.py +0 -0
- {mops-3.1.0 → mops-3.2.0}/mops/selenium/pages/mobile_page.py +0 -0
- {mops-3.1.0 → mops-3.2.0}/mops/selenium/pages/web_page.py +0 -0
- {mops-3.1.0 → mops-3.2.0}/mops/selenium/sel_utils.py +0 -0
- {mops-3.1.0 → mops-3.2.0}/mops/shared_utils.py +0 -0
- {mops-3.1.0 → mops-3.2.0}/mops/utils/logs.py +0 -0
- {mops-3.1.0 → mops-3.2.0}/mops/utils/previous_object_driver.py +0 -0
- {mops-3.1.0 → mops-3.2.0}/mops/utils/selector_synchronizer.py +0 -0
- {mops-3.1.0 → mops-3.2.0}/mops/visual_comparison.py +0 -0
- {mops-3.1.0 → mops-3.2.0}/mops.egg-info/dependency_links.txt +0 -0
- {mops-3.1.0 → mops-3.2.0}/mops.egg-info/requires.txt +0 -0
- {mops-3.1.0 → mops-3.2.0}/mops.egg-info/top_level.txt +0 -0
- {mops-3.1.0 → mops-3.2.0}/pyproject.toml +0 -0
- {mops-3.1.0 → mops-3.2.0}/setup.cfg +0 -0
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
__version__ = '3.
|
|
1
|
+
__version__ = '3.2.0'
|
|
2
2
|
__project_name__ = 'mops'
|
|
@@ -143,10 +143,19 @@ class ElementABC(MixinABC, ABC):
|
|
|
143
143
|
"""
|
|
144
144
|
raise NotImplementedError()
|
|
145
145
|
|
|
146
|
-
def wait_visibility(
|
|
146
|
+
def wait_visibility(
|
|
147
|
+
self,
|
|
148
|
+
*,
|
|
149
|
+
timeout: int = WAIT_EL,
|
|
150
|
+
silent: bool = False,
|
|
151
|
+
continuous: Union[bool, int, float] = False,
|
|
152
|
+
) -> Element:
|
|
147
153
|
"""
|
|
148
154
|
Waits until the element becomes visible.
|
|
149
|
-
|
|
155
|
+
**Note:** The method requires the use of named arguments.
|
|
156
|
+
|
|
157
|
+
A continuous visibility verification may be applied for given
|
|
158
|
+
or default amount of time after the first condition is met.
|
|
150
159
|
|
|
151
160
|
**Selenium:**
|
|
152
161
|
|
|
@@ -163,14 +172,26 @@ class ElementABC(MixinABC, ABC):
|
|
|
163
172
|
:type timeout: int
|
|
164
173
|
:param silent: If :obj:`True`, suppresses logging.
|
|
165
174
|
:type silent: bool
|
|
175
|
+
:param continuous: If :obj:`True`, a continuous visibility verification applied for another 2.5 seconds.
|
|
176
|
+
An :obj:`int` or :obj:`float` modifies the continuous wait timeout.
|
|
177
|
+
:type continuous: typing.Union[int, float, bool]
|
|
166
178
|
:return: :class:`Element`
|
|
167
179
|
"""
|
|
168
180
|
raise NotImplementedError()
|
|
169
181
|
|
|
170
|
-
def wait_hidden(
|
|
182
|
+
def wait_hidden(
|
|
183
|
+
self,
|
|
184
|
+
*,
|
|
185
|
+
timeout: int = WAIT_EL,
|
|
186
|
+
silent: bool = False,
|
|
187
|
+
continuous: Union[bool, int, float] = False,
|
|
188
|
+
) -> Element:
|
|
171
189
|
"""
|
|
172
190
|
Waits until the element becomes hidden.
|
|
173
|
-
|
|
191
|
+
**Note:** The method requires the use of named arguments.
|
|
192
|
+
|
|
193
|
+
A continuous invisibility verification may be applied for given
|
|
194
|
+
or default amount of time after the first condition is met.
|
|
174
195
|
|
|
175
196
|
**Selenium:**
|
|
176
197
|
|
|
@@ -187,6 +208,9 @@ class ElementABC(MixinABC, ABC):
|
|
|
187
208
|
:type timeout: int
|
|
188
209
|
:param silent: If :obj:`True`, suppresses logging.
|
|
189
210
|
:type silent: bool
|
|
211
|
+
:param continuous: If :obj:`True`, a continuous invisibility verification applied for another 2.5 seconds.
|
|
212
|
+
An :obj:`int` or :obj:`float` modifies the continuous wait timeout.
|
|
213
|
+
:type continuous: typing.Union[int, float, bool]
|
|
190
214
|
:return: :class:`Element`
|
|
191
215
|
"""
|
|
192
216
|
raise NotImplementedError()
|
|
@@ -194,7 +218,7 @@ class ElementABC(MixinABC, ABC):
|
|
|
194
218
|
def wait_availability(self, *, timeout: int = WAIT_EL, silent: bool = False) -> Element:
|
|
195
219
|
"""
|
|
196
220
|
Waits until the element becomes available in DOM tree. \n
|
|
197
|
-
|
|
221
|
+
**Note:** The method requires the use of named arguments.
|
|
198
222
|
|
|
199
223
|
**Selenium:**
|
|
200
224
|
|
|
@@ -574,12 +598,15 @@ class ElementABC(MixinABC, ABC):
|
|
|
574
598
|
self,
|
|
575
599
|
*,
|
|
576
600
|
timeout: Union[int, float] = QUARTER_WAIT_EL,
|
|
577
|
-
silent: bool = False
|
|
601
|
+
silent: bool = False,
|
|
602
|
+
continuous: Union[bool, int, float] = False,
|
|
578
603
|
) -> Element:
|
|
579
604
|
"""
|
|
580
605
|
Wait for the element to become visible, without raising an error if it does not.
|
|
606
|
+
**Note:** The method requires the use of named arguments.
|
|
581
607
|
|
|
582
|
-
|
|
608
|
+
A continuous visibility verification may be applied for given
|
|
609
|
+
or default amount of time after the first condition is met.
|
|
583
610
|
|
|
584
611
|
**Selenium & Playwright:**
|
|
585
612
|
|
|
@@ -596,6 +623,9 @@ class ElementABC(MixinABC, ABC):
|
|
|
596
623
|
:type timeout: typing.Union[int, float]
|
|
597
624
|
:param silent: If :obj:`True`, suppresses logging.
|
|
598
625
|
:type silent: bool
|
|
626
|
+
:param continuous: If :obj:`True`, a continuous visibility verification applied for another 2.5 seconds.
|
|
627
|
+
An :obj:`int` or :obj:`float` modifies the continuous wait timeout.
|
|
628
|
+
:type continuous: typing.Union[int, float, bool]
|
|
599
629
|
:return: :class:`Element`
|
|
600
630
|
"""
|
|
601
631
|
raise NotImplementedError()
|
|
@@ -604,12 +634,15 @@ class ElementABC(MixinABC, ABC):
|
|
|
604
634
|
self,
|
|
605
635
|
*,
|
|
606
636
|
timeout: Union[int, float] = QUARTER_WAIT_EL,
|
|
607
|
-
silent: bool = False
|
|
637
|
+
silent: bool = False,
|
|
638
|
+
continuous: Union[bool, int, float] = False,
|
|
608
639
|
) -> Element:
|
|
609
640
|
"""
|
|
610
641
|
Wait for the element to become hidden, without raising an error if it does not.
|
|
642
|
+
**Note:** The method requires the use of named arguments.
|
|
611
643
|
|
|
612
|
-
|
|
644
|
+
A continuous invisibility verification may be applied for given
|
|
645
|
+
or default amount of time after the first condition is met.
|
|
613
646
|
|
|
614
647
|
**Selenium & Playwright:**
|
|
615
648
|
|
|
@@ -626,6 +659,9 @@ class ElementABC(MixinABC, ABC):
|
|
|
626
659
|
:type timeout: typing.Union[int, float]
|
|
627
660
|
:param silent: If :obj:`True`, suppresses logging.
|
|
628
661
|
:type silent: bool
|
|
662
|
+
:param continuous: If :obj:`True`, a continuous invisibility verification applied for another 2.5 seconds.
|
|
663
|
+
An :obj:`int` or :obj:`float` modifies the continuous wait timeout.
|
|
664
|
+
:type continuous: typing.Union[int, float, bool]
|
|
629
665
|
:return: :class:`Element`
|
|
630
666
|
"""
|
|
631
667
|
raise NotImplementedError()
|
|
@@ -35,8 +35,8 @@ from mops.utils.internal_utils import (
|
|
|
35
35
|
set_parent_for_attr,
|
|
36
36
|
is_page,
|
|
37
37
|
QUARTER_WAIT_EL,
|
|
38
|
-
wait_condition,
|
|
39
38
|
)
|
|
39
|
+
from mops.utils.decorators import wait_condition, wait_continuous
|
|
40
40
|
|
|
41
41
|
if TYPE_CHECKING:
|
|
42
42
|
from mops.base.group import Group
|
|
@@ -193,11 +193,61 @@ class Element(DriverMixin, InternalMixin, Logging, ElementABC):
|
|
|
193
193
|
|
|
194
194
|
# Elements waits
|
|
195
195
|
|
|
196
|
-
|
|
196
|
+
@wait_continuous
|
|
197
|
+
@wait_condition
|
|
198
|
+
def wait_visibility(
|
|
199
|
+
self,
|
|
200
|
+
*,
|
|
201
|
+
timeout: int = WAIT_EL,
|
|
202
|
+
silent: bool = False,
|
|
203
|
+
continuous: Union[bool, int, float] = False,
|
|
204
|
+
) -> Element:
|
|
205
|
+
"""
|
|
206
|
+
Waits until the element becomes visible.
|
|
207
|
+
**Note:** The method requires the use of named arguments.
|
|
208
|
+
|
|
209
|
+
A continuous visibility verification may be applied for given
|
|
210
|
+
or default amount of time after the first condition is met.
|
|
211
|
+
|
|
212
|
+
**Selenium:**
|
|
213
|
+
|
|
214
|
+
- Applied :func:`wait_condition` decorator integrates a 0.1 seconds delay for each iteration
|
|
215
|
+
during the waiting process.
|
|
216
|
+
|
|
217
|
+
**Appium:**
|
|
218
|
+
|
|
219
|
+
- Applied :func:`wait_condition` decorator integrates an exponential delay
|
|
220
|
+
(starting at 0.1 seconds, up to a maximum of 1.6 seconds) which increases
|
|
221
|
+
with each iteration during the waiting process.
|
|
222
|
+
|
|
223
|
+
:param timeout: The maximum time to wait for the condition (in seconds). Default: :obj:`WAIT_EL`.
|
|
224
|
+
:type timeout: int
|
|
225
|
+
:param silent: If :obj:`True`, suppresses logging.
|
|
226
|
+
:type silent: bool
|
|
227
|
+
:param continuous: If :obj:`True`, a continuous visibility verification applied for another 2.5 seconds.
|
|
228
|
+
An :obj:`int` or :obj:`float` modifies the continuous wait timeout.
|
|
229
|
+
:type continuous: typing.Union[int, float, bool]
|
|
230
|
+
:return: :class:`Element`
|
|
231
|
+
"""
|
|
232
|
+
return Result( # noqa
|
|
233
|
+
execution_result=self.is_displayed(silent=True),
|
|
234
|
+
log=f'Wait until "{self.name}" becomes visible',
|
|
235
|
+
exc=TimeoutException(f'"{self.name}" not visible', info=self)
|
|
236
|
+
)
|
|
237
|
+
|
|
238
|
+
def wait_visibility_without_error(
|
|
239
|
+
self,
|
|
240
|
+
*,
|
|
241
|
+
timeout: Union[int, float] = QUARTER_WAIT_EL,
|
|
242
|
+
silent: bool = False,
|
|
243
|
+
continuous: Union[bool, int, float] = False,
|
|
244
|
+
) -> Element:
|
|
197
245
|
"""
|
|
198
246
|
Wait for the element to become visible, without raising an error if it does not.
|
|
247
|
+
**Note:** The method requires the use of named arguments.
|
|
199
248
|
|
|
200
|
-
|
|
249
|
+
A continuous visibility verification may be applied for given
|
|
250
|
+
or default amount of time after the first condition is met.
|
|
201
251
|
|
|
202
252
|
**Selenium & Playwright:**
|
|
203
253
|
|
|
@@ -214,28 +264,77 @@ class Element(DriverMixin, InternalMixin, Logging, ElementABC):
|
|
|
214
264
|
:type timeout: typing.Union[int, float]
|
|
215
265
|
:param silent: If :obj:`True`, suppresses logging.
|
|
216
266
|
:type silent: bool
|
|
267
|
+
:param continuous: If :obj:`True`, a continuous visibility verification applied for another 2.5 seconds.
|
|
268
|
+
An :obj:`int` or :obj:`float` modifies the continuous wait timeout.
|
|
269
|
+
:type continuous: typing.Union[int, float, bool]
|
|
217
270
|
:return: :class:`Element`
|
|
218
271
|
"""
|
|
219
272
|
if not silent:
|
|
220
|
-
|
|
273
|
+
strategy = 'continuous visible' if continuous else 'hidden'
|
|
274
|
+
self.log(f'Wait until "{self.name}" becomes {strategy} without error exception')
|
|
221
275
|
|
|
222
276
|
try:
|
|
223
|
-
self.wait_visibility(timeout=timeout, silent=True)
|
|
224
|
-
except (TimeoutException, WebDriverException) as exception:
|
|
277
|
+
self.wait_visibility(timeout=timeout, silent=True, continuous=continuous)
|
|
278
|
+
except (TimeoutException, WebDriverException, ContinuousWaitException) as exception:
|
|
225
279
|
if not silent:
|
|
226
280
|
self.log(f'Ignored exception: "{exception.msg}"')
|
|
227
281
|
return self
|
|
228
282
|
|
|
283
|
+
@wait_continuous
|
|
284
|
+
@wait_condition
|
|
285
|
+
def wait_hidden(
|
|
286
|
+
self,
|
|
287
|
+
*,
|
|
288
|
+
timeout: int = WAIT_EL,
|
|
289
|
+
silent: bool = False,
|
|
290
|
+
continuous: Union[bool, int, float] = False,
|
|
291
|
+
) -> Element:
|
|
292
|
+
"""
|
|
293
|
+
Waits until the element becomes hidden.
|
|
294
|
+
**Note:** The method requires the use of named arguments.
|
|
295
|
+
|
|
296
|
+
A continuous invisibility verification may be applied for given
|
|
297
|
+
or default amount of time after the first condition is met.
|
|
298
|
+
|
|
299
|
+
**Selenium:**
|
|
300
|
+
|
|
301
|
+
- Applied :func:`wait_condition` decorator integrates a 0.1 seconds delay for each iteration
|
|
302
|
+
during the waiting process.
|
|
303
|
+
|
|
304
|
+
**Appium:**
|
|
305
|
+
|
|
306
|
+
- Applied :func:`wait_condition` decorator integrates an exponential delay
|
|
307
|
+
(starting at 0.1 seconds, up to a maximum of 1.6 seconds) which increases
|
|
308
|
+
with each iteration during the waiting process.
|
|
309
|
+
|
|
310
|
+
:param timeout: The maximum time to wait for the condition (in seconds). Default: :obj:`WAIT_EL`.
|
|
311
|
+
:type timeout: int
|
|
312
|
+
:param silent: If :obj:`True`, suppresses logging.
|
|
313
|
+
:type silent: bool
|
|
314
|
+
:param continuous: If :obj:`True`, a continuous invisibility verification applied for another 2.5 seconds.
|
|
315
|
+
An :obj:`int` or :obj:`float` modifies the continuous wait timeout.
|
|
316
|
+
:type continuous: typing.Union[int, float, bool]
|
|
317
|
+
:return: :class:`Element`
|
|
318
|
+
"""
|
|
319
|
+
return Result( # noqa
|
|
320
|
+
execution_result=self.is_hidden(silent=True),
|
|
321
|
+
log=f'Wait until "{self.name}" becomes hidden',
|
|
322
|
+
exc=TimeoutException(f'"{self.name}" still visible', info=self),
|
|
323
|
+
)
|
|
324
|
+
|
|
229
325
|
def wait_hidden_without_error(
|
|
230
326
|
self,
|
|
231
327
|
*,
|
|
232
328
|
timeout: Union[int, float] = QUARTER_WAIT_EL,
|
|
233
|
-
silent: bool = False
|
|
329
|
+
silent: bool = False,
|
|
330
|
+
continuous: Union[bool, int, float] = False,
|
|
234
331
|
) -> Element:
|
|
235
332
|
"""
|
|
236
333
|
Wait for the element to become hidden, without raising an error if it does not.
|
|
334
|
+
**Note:** The method requires the use of named arguments.
|
|
237
335
|
|
|
238
|
-
|
|
336
|
+
A continuous invisibility verification may be applied for given
|
|
337
|
+
or default amount of time after the first condition is met.
|
|
239
338
|
|
|
240
339
|
**Selenium & Playwright:**
|
|
241
340
|
|
|
@@ -252,18 +351,51 @@ class Element(DriverMixin, InternalMixin, Logging, ElementABC):
|
|
|
252
351
|
:type timeout: typing.Union[int, float]
|
|
253
352
|
:param silent: If :obj:`True`, suppresses logging.
|
|
254
353
|
:type silent: bool
|
|
354
|
+
:param continuous: If :obj:`True`, a continuous invisibility verification applied for another 2.5 seconds.
|
|
355
|
+
An :obj:`int` or :obj:`float` modifies the continuous wait timeout.
|
|
356
|
+
:type continuous: typing.Union[int, float, bool]
|
|
255
357
|
:return: :class:`Element`
|
|
256
358
|
"""
|
|
257
359
|
if not silent:
|
|
258
|
-
|
|
360
|
+
strategy = 'continuous hidden' if continuous else 'hidden'
|
|
361
|
+
self.log(f'Wait until "{self.name}" becomes {strategy} without error exception')
|
|
259
362
|
|
|
260
363
|
try:
|
|
261
|
-
self.wait_hidden(timeout=timeout, silent=
|
|
262
|
-
except (TimeoutException, WebDriverException) as exception:
|
|
364
|
+
self.wait_hidden(timeout=timeout, silent=silent, continuous=continuous)
|
|
365
|
+
except (TimeoutException, WebDriverException, ContinuousWaitException) as exception:
|
|
263
366
|
if not silent:
|
|
264
367
|
self.log(f'Ignored exception: "{exception.msg}"')
|
|
265
368
|
return self
|
|
266
369
|
|
|
370
|
+
@wait_condition
|
|
371
|
+
def wait_availability(self, *, timeout: int = WAIT_EL, silent: bool = False) -> Element:
|
|
372
|
+
"""
|
|
373
|
+
Waits until the element becomes available in DOM tree. \n
|
|
374
|
+
**Note:** The method requires the use of named arguments.
|
|
375
|
+
|
|
376
|
+
**Selenium:**
|
|
377
|
+
|
|
378
|
+
- Applied :func:`wait_condition` decorator integrates a 0.1 seconds delay for each iteration
|
|
379
|
+
during the waiting process.
|
|
380
|
+
|
|
381
|
+
**Appium:**
|
|
382
|
+
|
|
383
|
+
- Applied :func:`wait_condition` decorator integrates an exponential delay
|
|
384
|
+
(starting at 0.1 seconds, up to a maximum of 1.6 seconds) which increases
|
|
385
|
+
with each iteration during the waiting process.
|
|
386
|
+
|
|
387
|
+
:param timeout: The maximum time to wait for the condition (in seconds). Default: :obj:`WAIT_EL`.
|
|
388
|
+
:type timeout: int
|
|
389
|
+
:param silent: If :obj:`True`, suppresses logging.
|
|
390
|
+
:type silent: bool
|
|
391
|
+
:return: :class:`Element`
|
|
392
|
+
"""
|
|
393
|
+
return Result( # noqa
|
|
394
|
+
execution_result=self.is_available(),
|
|
395
|
+
log=f'Wait until presence of "{self.name}"',
|
|
396
|
+
exc=TimeoutException(f'"{self.name}" not available in DOM', info=self),
|
|
397
|
+
)
|
|
398
|
+
|
|
267
399
|
@wait_condition
|
|
268
400
|
def wait_for_text(
|
|
269
401
|
self,
|
|
@@ -42,27 +42,37 @@ for (var i=0, max=elements.length; i < max; i++) {
|
|
|
42
42
|
|
|
43
43
|
add_element_over_js = """
|
|
44
44
|
function appendElement(given_obj) {
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
45
|
+
const rect = given_obj.getBoundingClientRect();
|
|
46
|
+
const driverWrapperObj = document.createElement("div");
|
|
47
|
+
|
|
48
|
+
driverWrapperObj.style.zIndex = 9999999;
|
|
49
|
+
driverWrapperObj.setAttribute("class", "driver-wrapper-comparison-support-element");
|
|
50
|
+
driverWrapperObj.style.backgroundColor = "#000";
|
|
51
|
+
|
|
52
|
+
// Determine position type based on scroll position
|
|
53
|
+
if (window.scrollY === 0) {
|
|
54
|
+
driverWrapperObj.style.position = "fixed";
|
|
55
|
+
driverWrapperObj.style.top = rect.top + "px";
|
|
56
|
+
driverWrapperObj.style.left = rect.left + "px";
|
|
57
|
+
} else {
|
|
58
|
+
driverWrapperObj.style.position = "absolute";
|
|
59
|
+
driverWrapperObj.style.top = (rect.top + window.scrollY) + "px";
|
|
60
|
+
driverWrapperObj.style.left = (rect.left + window.scrollX) + "px";
|
|
61
|
+
}
|
|
50
62
|
|
|
51
|
-
|
|
52
|
-
|
|
63
|
+
driverWrapperObj.style.width = rect.width + "px";
|
|
64
|
+
driverWrapperObj.style.height = rect.height + "px";
|
|
53
65
|
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
driver_wrapper_obj.style.top = (given_obj.top + window.scrollY) + "px";
|
|
57
|
-
driver_wrapper_obj.style.left = (given_obj.left + window.scrollX) + "px";
|
|
58
|
-
|
|
59
|
-
document.body.appendChild(driver_wrapper_obj);
|
|
60
|
-
};
|
|
66
|
+
document.body.appendChild(driverWrapperObj);
|
|
67
|
+
}
|
|
61
68
|
|
|
62
69
|
return appendElement(arguments[0]);
|
|
63
70
|
"""
|
|
64
71
|
|
|
65
72
|
|
|
73
|
+
hide_caret_js_script = 'document.activeElement.blur();'
|
|
74
|
+
|
|
75
|
+
|
|
66
76
|
add_driver_index_comment_js = """
|
|
67
77
|
function addComment(driver_index) {
|
|
68
78
|
comment = document.createComment(" " + driver_index + " ");
|
|
@@ -13,6 +13,7 @@ from playwright.sync_api import Locator, Page, Browser, BrowserContext
|
|
|
13
13
|
|
|
14
14
|
from mops.mixins.objects.size import Size
|
|
15
15
|
from mops.mixins.objects.location import Location
|
|
16
|
+
from mops.utils.decorators import retry
|
|
16
17
|
from mops.utils.selector_synchronizer import get_platform_locator, set_playwright_locator
|
|
17
18
|
from mops.abstraction.element_abc import ElementABC
|
|
18
19
|
from mops.exceptions import TimeoutException, InvalidSelectorException
|
|
@@ -241,100 +242,6 @@ class PlayElement(ElementABC, Logging, ABC):
|
|
|
241
242
|
|
|
242
243
|
return self
|
|
243
244
|
|
|
244
|
-
# Element waits
|
|
245
|
-
|
|
246
|
-
def wait_visibility(self, *, timeout: int = WAIT_EL, silent: bool = False) -> PlayElement:
|
|
247
|
-
"""
|
|
248
|
-
Waits until the element becomes visible.
|
|
249
|
-
**Note:** The method requires the use of named arguments.
|
|
250
|
-
|
|
251
|
-
**Selenium:**
|
|
252
|
-
|
|
253
|
-
- Applied :func:`wait_condition` decorator integrates a 0.1 seconds delay for each iteration
|
|
254
|
-
during the waiting process.
|
|
255
|
-
|
|
256
|
-
**Appium:**
|
|
257
|
-
|
|
258
|
-
- Applied :func:`wait_condition` decorator integrates an exponential delay
|
|
259
|
-
(starting at 0.1 seconds, up to a maximum of 1.6 seconds) which increases
|
|
260
|
-
with each iteration during the waiting process.
|
|
261
|
-
|
|
262
|
-
:param timeout: The maximum time to wait for the condition (in seconds). Default: :obj:`WAIT_EL`.
|
|
263
|
-
:type timeout: int
|
|
264
|
-
:param silent: If :obj:`True`, suppresses logging.
|
|
265
|
-
:type silent: bool
|
|
266
|
-
:return: :class:`PlayElement`
|
|
267
|
-
"""
|
|
268
|
-
if not silent:
|
|
269
|
-
self.log(f'Wait until "{self.name}" becomes visible')
|
|
270
|
-
|
|
271
|
-
try:
|
|
272
|
-
self._first_element.wait_for(state='visible', timeout=get_timeout_in_ms(timeout))
|
|
273
|
-
except PlayTimeoutError:
|
|
274
|
-
raise TimeoutException(f'"{self.name}" not visible', timeout=timeout, info=self)
|
|
275
|
-
return self
|
|
276
|
-
|
|
277
|
-
def wait_hidden(self, *, timeout: int = WAIT_EL, silent: bool = False) -> PlayElement:
|
|
278
|
-
"""
|
|
279
|
-
Waits until the element becomes hidden.
|
|
280
|
-
**Note:** The method requires the use of named arguments.
|
|
281
|
-
|
|
282
|
-
**Selenium:**
|
|
283
|
-
|
|
284
|
-
- Applied :func:`wait_condition` decorator integrates a 0.1 seconds delay for each iteration
|
|
285
|
-
during the waiting process.
|
|
286
|
-
|
|
287
|
-
**Appium:**
|
|
288
|
-
|
|
289
|
-
- Applied :func:`wait_condition` decorator integrates an exponential delay
|
|
290
|
-
(starting at 0.1 seconds, up to a maximum of 1.6 seconds) which increases
|
|
291
|
-
with each iteration during the waiting process.
|
|
292
|
-
|
|
293
|
-
:param timeout: The maximum time to wait for the condition (in seconds). Default: :obj:`WAIT_EL`.
|
|
294
|
-
:type timeout: int
|
|
295
|
-
:param silent: If :obj:`True`, suppresses logging.
|
|
296
|
-
:type silent: bool
|
|
297
|
-
:return: :class:`PlayElement`
|
|
298
|
-
"""
|
|
299
|
-
if not silent:
|
|
300
|
-
self.log(f'Wait until "{self.name}" becomes hidden')
|
|
301
|
-
try:
|
|
302
|
-
self._first_element.wait_for(state='hidden', timeout=get_timeout_in_ms(timeout))
|
|
303
|
-
except PlayTimeoutError:
|
|
304
|
-
raise TimeoutException(f'"{self.name}" still visible', timeout=timeout, info=self)
|
|
305
|
-
return self
|
|
306
|
-
|
|
307
|
-
def wait_availability(self, *, timeout: int = WAIT_EL, silent: bool = False) -> PlayElement:
|
|
308
|
-
"""
|
|
309
|
-
Waits until the element becomes available in DOM tree. \n
|
|
310
|
-
**Note:** The method requires the use of named arguments.
|
|
311
|
-
|
|
312
|
-
**Selenium:**
|
|
313
|
-
|
|
314
|
-
- Applied :func:`wait_condition` decorator integrates a 0.1 seconds delay for each iteration
|
|
315
|
-
during the waiting process.
|
|
316
|
-
|
|
317
|
-
**Appium:**
|
|
318
|
-
|
|
319
|
-
- Applied :func:`wait_condition` decorator integrates an exponential delay
|
|
320
|
-
(starting at 0.1 seconds, up to a maximum of 1.6 seconds) which increases
|
|
321
|
-
with each iteration during the waiting process.
|
|
322
|
-
|
|
323
|
-
:param timeout: The maximum time to wait for the condition (in seconds). Default: :obj:`WAIT_EL`.
|
|
324
|
-
:type timeout: int
|
|
325
|
-
:param silent: If :obj:`True`, suppresses logging.
|
|
326
|
-
:type silent: bool
|
|
327
|
-
:return: :class:`PlayElement`
|
|
328
|
-
"""
|
|
329
|
-
if not silent:
|
|
330
|
-
self.log(f'Wait until presence of "{self.name}"')
|
|
331
|
-
|
|
332
|
-
try:
|
|
333
|
-
self._first_element.wait_for(state='attached', timeout=get_timeout_in_ms(timeout))
|
|
334
|
-
except PlayTimeoutError:
|
|
335
|
-
raise TimeoutException(f'"{self.name}" not available in DOM', timeout=timeout, info=self)
|
|
336
|
-
return self
|
|
337
|
-
|
|
338
245
|
# Element state
|
|
339
246
|
|
|
340
247
|
def scroll_into_view(
|
|
@@ -495,6 +402,7 @@ class PlayElement(ElementABC, Logging, ABC):
|
|
|
495
402
|
|
|
496
403
|
return len(self.all_elements)
|
|
497
404
|
|
|
405
|
+
@retry(AttributeError)
|
|
498
406
|
def get_rect(self) -> dict:
|
|
499
407
|
"""
|
|
500
408
|
Retrieve the size and position of the element as a dictionary.
|
|
@@ -505,6 +413,7 @@ class PlayElement(ElementABC, Logging, ABC):
|
|
|
505
413
|
return dict(sorted_items)
|
|
506
414
|
|
|
507
415
|
@property
|
|
416
|
+
@retry(TypeError)
|
|
508
417
|
def size(self) -> Size:
|
|
509
418
|
"""
|
|
510
419
|
Get the size of the current element, including width and height.
|
|
@@ -515,6 +424,7 @@ class PlayElement(ElementABC, Logging, ABC):
|
|
|
515
424
|
return Size(width=box['width'], height=box['height'])
|
|
516
425
|
|
|
517
426
|
@property
|
|
427
|
+
@retry(TypeError)
|
|
518
428
|
def location(self) -> Location:
|
|
519
429
|
"""
|
|
520
430
|
Get the location of the current element, including the x and y coordinates.
|
|
@@ -7,7 +7,6 @@ from typing import Union, List, Any, Callable, TYPE_CHECKING
|
|
|
7
7
|
from PIL import Image
|
|
8
8
|
|
|
9
9
|
from mops.mixins.internal_mixin import get_element_info
|
|
10
|
-
from mops.mixins.objects.wait_result import Result
|
|
11
10
|
from selenium.webdriver.remote.webdriver import WebDriver as SeleniumWebDriver
|
|
12
11
|
from selenium.webdriver.remote.webelement import WebElement as SeleniumWebElement
|
|
13
12
|
from appium.webdriver.webelement import WebElement as AppiumWebElement
|
|
@@ -16,21 +15,20 @@ from selenium.common.exceptions import (
|
|
|
16
15
|
InvalidArgumentException as SeleniumInvalidArgumentException,
|
|
17
16
|
InvalidSelectorException as SeleniumInvalidSelectorException,
|
|
18
17
|
NoSuchElementException as SeleniumNoSuchElementException,
|
|
19
|
-
ElementNotInteractableException as SeleniumElementNotInteractableException,
|
|
20
|
-
ElementClickInterceptedException as SeleniumElementClickInterceptedException,
|
|
21
18
|
StaleElementReferenceException as SeleniumStaleElementReferenceException,
|
|
19
|
+
WebDriverException as SeleniumWebDriverException,
|
|
22
20
|
)
|
|
23
21
|
from mops.abstraction.element_abc import ElementABC
|
|
24
22
|
from mops.selenium.sel_utils import ActionChains
|
|
25
|
-
from mops.js_scripts import get_element_size_js, get_element_position_on_screen_js
|
|
23
|
+
from mops.js_scripts import get_element_size_js, get_element_position_on_screen_js, hide_caret_js_script
|
|
26
24
|
from mops.keyboard_keys import KeyboardKeys
|
|
27
25
|
from mops.mixins.objects.location import Location
|
|
28
26
|
from mops.mixins.objects.scrolls import ScrollTo, ScrollTypes, scroll_into_view_blocks
|
|
29
27
|
from mops.mixins.objects.size import Size
|
|
30
28
|
from mops.shared_utils import cut_log_data, _scaled_screenshot
|
|
31
|
-
from mops.utils.internal_utils import WAIT_EL, safe_call, get_dict,
|
|
29
|
+
from mops.utils.internal_utils import WAIT_EL, safe_call, get_dict, is_group
|
|
30
|
+
from mops.utils.decorators import retry
|
|
32
31
|
from mops.exceptions import (
|
|
33
|
-
TimeoutException,
|
|
34
32
|
InvalidSelectorException,
|
|
35
33
|
DriverWrapperException,
|
|
36
34
|
NoSuchElementException,
|
|
@@ -80,6 +78,7 @@ class CoreElement(ElementABC, ABC):
|
|
|
80
78
|
|
|
81
79
|
# Element interaction
|
|
82
80
|
|
|
81
|
+
@retry(ElementNotInteractableException)
|
|
83
82
|
def click(self, *, force_wait: bool = True, **kwargs) -> CoreElement:
|
|
84
83
|
"""
|
|
85
84
|
Clicks on the element.
|
|
@@ -101,26 +100,17 @@ class CoreElement(ElementABC, ABC):
|
|
|
101
100
|
"""
|
|
102
101
|
self.log(f'Click into "{self.name}"')
|
|
103
102
|
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
return self
|
|
113
|
-
except (
|
|
114
|
-
SeleniumElementNotInteractableException,
|
|
115
|
-
SeleniumElementClickInterceptedException,
|
|
116
|
-
SeleniumStaleElementReferenceException,
|
|
117
|
-
) as exc:
|
|
118
|
-
selenium_exc_msg = exc.msg
|
|
119
|
-
finally:
|
|
120
|
-
self.element = None
|
|
103
|
+
if force_wait:
|
|
104
|
+
self.wait_visibility(silent=True)
|
|
105
|
+
|
|
106
|
+
try:
|
|
107
|
+
self.wait_enabled(silent=True).element.click()
|
|
108
|
+
return self
|
|
109
|
+
except SeleniumWebDriverException as exc:
|
|
110
|
+
selenium_exc_msg = exc.msg
|
|
121
111
|
|
|
122
112
|
raise ElementNotInteractableException(
|
|
123
|
-
f'Element "{self.name}" not interactable
|
|
113
|
+
f'Element "{self.name}" not interactable. {self.get_element_info()}. '
|
|
124
114
|
f'Original error: {selenium_exc_msg}'
|
|
125
115
|
)
|
|
126
116
|
|
|
@@ -140,6 +130,7 @@ class CoreElement(ElementABC, ABC):
|
|
|
140
130
|
self.log(f'Type text "{cut_log_data(text)}" into "{self.name}"')
|
|
141
131
|
|
|
142
132
|
self.element.send_keys(text)
|
|
133
|
+
|
|
143
134
|
return self
|
|
144
135
|
|
|
145
136
|
def type_slowly(self, text: str, sleep_gap: float = 0.05, silent: bool = False) -> CoreElement:
|
|
@@ -163,6 +154,7 @@ class CoreElement(ElementABC, ABC):
|
|
|
163
154
|
for letter in str(text):
|
|
164
155
|
element.send_keys(letter)
|
|
165
156
|
time.sleep(sleep_gap)
|
|
157
|
+
|
|
166
158
|
return self
|
|
167
159
|
|
|
168
160
|
def clear_text(self, silent: bool = False) -> CoreElement:
|
|
@@ -177,6 +169,7 @@ class CoreElement(ElementABC, ABC):
|
|
|
177
169
|
self.log(f'Clear text in "{self.name}"')
|
|
178
170
|
|
|
179
171
|
self.element.clear()
|
|
172
|
+
|
|
180
173
|
return self
|
|
181
174
|
|
|
182
175
|
def check(self) -> CoreElement:
|
|
@@ -211,95 +204,6 @@ class CoreElement(ElementABC, ABC):
|
|
|
211
204
|
|
|
212
205
|
return self
|
|
213
206
|
|
|
214
|
-
# Element waits
|
|
215
|
-
|
|
216
|
-
@wait_condition
|
|
217
|
-
def wait_visibility(self, *, timeout: int = WAIT_EL, silent: bool = False) -> CoreElement:
|
|
218
|
-
"""
|
|
219
|
-
Waits until the element becomes visible.
|
|
220
|
-
**Note:** The method requires the use of named arguments.
|
|
221
|
-
|
|
222
|
-
**Selenium:**
|
|
223
|
-
|
|
224
|
-
- Applied :func:`wait_condition` decorator integrates a 0.1 seconds delay for each iteration
|
|
225
|
-
during the waiting process.
|
|
226
|
-
|
|
227
|
-
**Appium:**
|
|
228
|
-
|
|
229
|
-
- Applied :func:`wait_condition` decorator integrates an exponential delay
|
|
230
|
-
(starting at 0.1 seconds, up to a maximum of 1.6 seconds) which increases
|
|
231
|
-
with each iteration during the waiting process.
|
|
232
|
-
|
|
233
|
-
:param timeout: The maximum time to wait for the condition (in seconds). Default: :obj:`WAIT_EL`.
|
|
234
|
-
:type timeout: int
|
|
235
|
-
:param silent: If :obj:`True`, suppresses logging.
|
|
236
|
-
:type silent: bool
|
|
237
|
-
:return: :class:`CoreElement`
|
|
238
|
-
"""
|
|
239
|
-
return Result( # noqa
|
|
240
|
-
execution_result=self.is_displayed(silent=True),
|
|
241
|
-
log=f'Wait until "{self.name}" becomes visible',
|
|
242
|
-
exc=TimeoutException(f'"{self.name}" not visible', info=self)
|
|
243
|
-
)
|
|
244
|
-
|
|
245
|
-
@wait_condition
|
|
246
|
-
def wait_hidden(self, *, timeout: int = WAIT_EL, silent: bool = False) -> CoreElement:
|
|
247
|
-
"""
|
|
248
|
-
Waits until the element becomes hidden.
|
|
249
|
-
**Note:** The method requires the use of named arguments.
|
|
250
|
-
|
|
251
|
-
**Selenium:**
|
|
252
|
-
|
|
253
|
-
- Applied :func:`wait_condition` decorator integrates a 0.1 seconds delay for each iteration
|
|
254
|
-
during the waiting process.
|
|
255
|
-
|
|
256
|
-
**Appium:**
|
|
257
|
-
|
|
258
|
-
- Applied :func:`wait_condition` decorator integrates an exponential delay
|
|
259
|
-
(starting at 0.1 seconds, up to a maximum of 1.6 seconds) which increases
|
|
260
|
-
with each iteration during the waiting process.
|
|
261
|
-
|
|
262
|
-
:param timeout: The maximum time to wait for the condition (in seconds). Default: :obj:`WAIT_EL`.
|
|
263
|
-
:type timeout: int
|
|
264
|
-
:param silent: If :obj:`True`, suppresses logging.
|
|
265
|
-
:type silent: bool
|
|
266
|
-
:return: :class:`CoreElement`
|
|
267
|
-
"""
|
|
268
|
-
return Result( # noqa
|
|
269
|
-
execution_result=self.is_hidden(silent=True),
|
|
270
|
-
log=f'Wait until "{self.name}" becomes hidden',
|
|
271
|
-
exc=TimeoutException(f'"{self.name}" still visible', info=self),
|
|
272
|
-
)
|
|
273
|
-
|
|
274
|
-
@wait_condition
|
|
275
|
-
def wait_availability(self, *, timeout: int = WAIT_EL, silent: bool = False) -> CoreElement:
|
|
276
|
-
"""
|
|
277
|
-
Waits until the element becomes available in DOM tree. \n
|
|
278
|
-
**Note:** The method requires the use of named arguments.
|
|
279
|
-
|
|
280
|
-
**Selenium:**
|
|
281
|
-
|
|
282
|
-
- Applied :func:`wait_condition` decorator integrates a 0.1 seconds delay for each iteration
|
|
283
|
-
during the waiting process.
|
|
284
|
-
|
|
285
|
-
**Appium:**
|
|
286
|
-
|
|
287
|
-
- Applied :func:`wait_condition` decorator integrates an exponential delay
|
|
288
|
-
(starting at 0.1 seconds, up to a maximum of 1.6 seconds) which increases
|
|
289
|
-
with each iteration during the waiting process.
|
|
290
|
-
|
|
291
|
-
:param timeout: The maximum time to wait for the condition (in seconds). Default: :obj:`WAIT_EL`.
|
|
292
|
-
:type timeout: int
|
|
293
|
-
:param silent: If :obj:`True`, suppresses logging.
|
|
294
|
-
:type silent: bool
|
|
295
|
-
:return: :class:`CoreElement`
|
|
296
|
-
"""
|
|
297
|
-
return Result( # noqa
|
|
298
|
-
execution_result=self.is_available(),
|
|
299
|
-
log=f'Wait until presence of "{self.name}"',
|
|
300
|
-
exc=TimeoutException(f'"{self.name}" not available in DOM', info=self),
|
|
301
|
-
)
|
|
302
|
-
|
|
303
207
|
# Element state
|
|
304
208
|
|
|
305
209
|
def scroll_into_view(
|
|
@@ -354,9 +258,12 @@ class CoreElement(ElementABC, ABC):
|
|
|
354
258
|
|
|
355
259
|
:return: :class:`bytes` - screenshot binary
|
|
356
260
|
"""
|
|
261
|
+
self.execute_script(hide_caret_js_script)
|
|
262
|
+
|
|
357
263
|
return self.element.screenshot_as_png
|
|
358
264
|
|
|
359
265
|
@property
|
|
266
|
+
@retry(SeleniumStaleElementReferenceException)
|
|
360
267
|
def text(self) -> str:
|
|
361
268
|
"""
|
|
362
269
|
Returns the text of the element.
|
|
@@ -410,14 +317,14 @@ class CoreElement(ElementABC, ABC):
|
|
|
410
317
|
:type silent: bool
|
|
411
318
|
:return: :class:`bool`
|
|
412
319
|
"""
|
|
413
|
-
if not silent:
|
|
414
|
-
self.log(f'Check displaying of "{self.name}"')
|
|
415
|
-
|
|
416
320
|
is_displayed = self.is_available()
|
|
417
321
|
|
|
418
322
|
if is_displayed:
|
|
419
323
|
is_displayed = safe_call(self._cached_element.is_displayed)
|
|
420
324
|
|
|
325
|
+
if not silent:
|
|
326
|
+
self.log(f'Check displaying of "{self.name}" - {is_displayed}')
|
|
327
|
+
|
|
421
328
|
return is_displayed
|
|
422
329
|
|
|
423
330
|
def is_hidden(self, silent: bool = False) -> bool:
|
|
@@ -428,11 +335,14 @@ class CoreElement(ElementABC, ABC):
|
|
|
428
335
|
:type silent: bool
|
|
429
336
|
:return: :class:`bool`
|
|
430
337
|
"""
|
|
338
|
+
status = not self.is_displayed(silent=True)
|
|
339
|
+
|
|
431
340
|
if not silent:
|
|
432
|
-
self.log(f'Check invisibility of "{self.name}"')
|
|
341
|
+
self.log(f'Check invisibility of "{self.name}" - {status}')
|
|
433
342
|
|
|
434
|
-
return
|
|
343
|
+
return status
|
|
435
344
|
|
|
345
|
+
@retry(SeleniumStaleElementReferenceException)
|
|
436
346
|
def get_attribute(self, attribute: str, silent: bool = False) -> str:
|
|
437
347
|
"""
|
|
438
348
|
Retrieve a specific attribute from the current element.
|
|
@@ -460,6 +370,7 @@ class CoreElement(ElementABC, ABC):
|
|
|
460
370
|
self.log(f'Get all texts from "{self.name}"')
|
|
461
371
|
|
|
462
372
|
self.wait_visibility(silent=True)
|
|
373
|
+
|
|
463
374
|
return list(element_item.text for element_item in self.all_elements)
|
|
464
375
|
|
|
465
376
|
def get_elements_count(self, silent: bool = False) -> int:
|
|
@@ -485,6 +396,7 @@ class CoreElement(ElementABC, ABC):
|
|
|
485
396
|
return dict(sorted_items)
|
|
486
397
|
|
|
487
398
|
@property
|
|
399
|
+
@retry(SeleniumStaleElementReferenceException)
|
|
488
400
|
def size(self) -> Size:
|
|
489
401
|
"""
|
|
490
402
|
Get the size of the current element, including width and height.
|
|
@@ -494,6 +406,7 @@ class CoreElement(ElementABC, ABC):
|
|
|
494
406
|
return Size(**self.execute_script(get_element_size_js))
|
|
495
407
|
|
|
496
408
|
@property
|
|
409
|
+
@retry(SeleniumStaleElementReferenceException)
|
|
497
410
|
def location(self) -> Location:
|
|
498
411
|
"""
|
|
499
412
|
Get the location of the current element, including the x and y coordinates.
|
|
@@ -0,0 +1,130 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
import time
|
|
4
|
+
from functools import wraps
|
|
5
|
+
from typing import Callable, Union, Any, TYPE_CHECKING
|
|
6
|
+
|
|
7
|
+
from mops.exceptions import ContinuousWaitException
|
|
8
|
+
from mops.mixins.objects.wait_result import Result
|
|
9
|
+
from mops.utils.internal_utils import HALF_WAIT_EL, WAIT_EL, validate_timeout, validate_silent, WAIT_METHODS_DELAY, \
|
|
10
|
+
increase_delay, QUARTER_WAIT_EL
|
|
11
|
+
from mops.utils.logs import autolog, LogLevel
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
if TYPE_CHECKING:
|
|
15
|
+
from mops.base.element import Element
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
def retry(exceptions, timeout: int = HALF_WAIT_EL):
|
|
19
|
+
"""
|
|
20
|
+
A decorator to retry a function when specified exceptions occur.
|
|
21
|
+
|
|
22
|
+
:param exceptions: Exception or tuple of exception classes to catch and retry on.
|
|
23
|
+
:param timeout: The maximum time (in seconds) to keep retrying before giving up.
|
|
24
|
+
"""
|
|
25
|
+
def decorator(func):
|
|
26
|
+
@wraps(func)
|
|
27
|
+
def wrapper(*args, **kwargs):
|
|
28
|
+
timestamp = None
|
|
29
|
+
|
|
30
|
+
while True:
|
|
31
|
+
try:
|
|
32
|
+
return func(*args, **kwargs)
|
|
33
|
+
except exceptions as exc:
|
|
34
|
+
if not timestamp:
|
|
35
|
+
timestamp = time.time()
|
|
36
|
+
elif time.time() - timestamp >= timeout:
|
|
37
|
+
raise exc
|
|
38
|
+
autolog(
|
|
39
|
+
f'Caught "{exc.__class__.__name__}" while executing "{func.__name__}", retrying...',
|
|
40
|
+
level=LogLevel.WARNING
|
|
41
|
+
)
|
|
42
|
+
return wrapper
|
|
43
|
+
return decorator
|
|
44
|
+
|
|
45
|
+
|
|
46
|
+
def wait_condition(method: Callable):
|
|
47
|
+
|
|
48
|
+
@wraps(method)
|
|
49
|
+
def wrapper(
|
|
50
|
+
self: Element,
|
|
51
|
+
*args: Any,
|
|
52
|
+
timeout: Union[int, float] = WAIT_EL,
|
|
53
|
+
silent: bool = False,
|
|
54
|
+
continuous: bool = False,
|
|
55
|
+
**kwargs: Any,
|
|
56
|
+
):
|
|
57
|
+
validate_timeout(timeout)
|
|
58
|
+
validate_silent(silent)
|
|
59
|
+
|
|
60
|
+
should_increase_delay = self.driver_wrapper.is_appium
|
|
61
|
+
delay = WAIT_METHODS_DELAY
|
|
62
|
+
is_log_needed = not silent
|
|
63
|
+
start_time = time.time()
|
|
64
|
+
|
|
65
|
+
if continuous:
|
|
66
|
+
return method(self, *args, **kwargs)
|
|
67
|
+
|
|
68
|
+
while time.time() - start_time < timeout:
|
|
69
|
+
result: Result = method(self, *args, **kwargs)
|
|
70
|
+
|
|
71
|
+
if is_log_needed:
|
|
72
|
+
self.log(result.log)
|
|
73
|
+
is_log_needed = False
|
|
74
|
+
|
|
75
|
+
if result.execution_result:
|
|
76
|
+
return self
|
|
77
|
+
|
|
78
|
+
time.sleep(delay)
|
|
79
|
+
|
|
80
|
+
if should_increase_delay:
|
|
81
|
+
delay = increase_delay(delay)
|
|
82
|
+
|
|
83
|
+
result.exc._timeout = timeout # noqa
|
|
84
|
+
raise result.exc
|
|
85
|
+
|
|
86
|
+
return wrapper
|
|
87
|
+
|
|
88
|
+
|
|
89
|
+
def wait_continuous(method: Callable):
|
|
90
|
+
|
|
91
|
+
@wraps(method)
|
|
92
|
+
def wrapper(
|
|
93
|
+
self: Element,
|
|
94
|
+
*args: Any,
|
|
95
|
+
silent: bool = False,
|
|
96
|
+
continuous: Union[int, float, bool] = False,
|
|
97
|
+
**kwargs: Any
|
|
98
|
+
):
|
|
99
|
+
result: Element = method(self, *args, silent=silent, continuous=False, **kwargs) # Wait for initial condition
|
|
100
|
+
|
|
101
|
+
if not continuous:
|
|
102
|
+
return result
|
|
103
|
+
|
|
104
|
+
should_increase_delay = self.driver_wrapper.is_appium
|
|
105
|
+
delay = WAIT_METHODS_DELAY
|
|
106
|
+
start_time = time.time()
|
|
107
|
+
is_log_needed = not silent
|
|
108
|
+
timeout = continuous if type(continuous) in (int, float) else QUARTER_WAIT_EL
|
|
109
|
+
|
|
110
|
+
while time.time() - start_time < timeout:
|
|
111
|
+
result: Result = method(self, *args, silent=silent, continuous=True, **kwargs)
|
|
112
|
+
|
|
113
|
+
if is_log_needed:
|
|
114
|
+
self.log(f'Starting continuous "{method.__name__}" for the "{self.name}" for next {timeout} seconds')
|
|
115
|
+
is_log_needed = False
|
|
116
|
+
|
|
117
|
+
if not result.execution_result:
|
|
118
|
+
raise ContinuousWaitException(
|
|
119
|
+
f'The continuous "{method.__name__}" of the "{self.name}" is no met '
|
|
120
|
+
f'after {(time.time() - start_time):.2f} seconds'
|
|
121
|
+
)
|
|
122
|
+
|
|
123
|
+
time.sleep(delay)
|
|
124
|
+
|
|
125
|
+
if should_increase_delay:
|
|
126
|
+
delay = increase_delay(delay)
|
|
127
|
+
|
|
128
|
+
return self
|
|
129
|
+
|
|
130
|
+
return wrapper
|
|
@@ -2,18 +2,15 @@ from __future__ import annotations
|
|
|
2
2
|
|
|
3
3
|
import sys
|
|
4
4
|
import inspect
|
|
5
|
-
import time
|
|
6
5
|
from copy import copy
|
|
7
|
-
from functools import lru_cache
|
|
6
|
+
from functools import lru_cache
|
|
8
7
|
from typing import Any, Union, Callable
|
|
9
8
|
|
|
10
9
|
from mops.mixins.objects.size import Size
|
|
11
|
-
from mops.mixins.objects.wait_result import Result
|
|
12
10
|
from selenium.common.exceptions import StaleElementReferenceException as SeleniumStaleElementReferenceException
|
|
13
11
|
|
|
14
12
|
from mops.exceptions import NoSuchElementException, InvalidSelectorException, TimeoutException, NoSuchParentException
|
|
15
13
|
|
|
16
|
-
|
|
17
14
|
WAIT_METHODS_DELAY = 0.1
|
|
18
15
|
WAIT_UNIT = 1
|
|
19
16
|
WAIT_EL = 10
|
|
@@ -24,7 +21,7 @@ WAIT_PAGE = 15
|
|
|
24
21
|
|
|
25
22
|
all_tags = {'h1', 'h2', 'h3', 'h4', 'h5', 'head', 'body', 'input', 'section', 'button', 'a', 'link', 'header', 'div',
|
|
26
23
|
'textarea', 'svg', 'circle', 'iframe', 'label', 'p', 'tr', 'th', 'table', 'tbody', 'td', 'select', 'nav',
|
|
27
|
-
'li', 'form', 'footer', 'frame', 'area', 'span'}
|
|
24
|
+
'li', 'form', 'footer', 'frame', 'area', 'span', 'video'}
|
|
28
25
|
|
|
29
26
|
|
|
30
27
|
def get_dict(obj: Any):
|
|
@@ -297,34 +294,3 @@ def increase_delay(delay, max_delay: Union[int, float] = 1.5) -> Union[int, floa
|
|
|
297
294
|
if delay < max_delay:
|
|
298
295
|
return delay + delay
|
|
299
296
|
return delay
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
def wait_condition(method: Callable):
|
|
303
|
-
|
|
304
|
-
@wraps(method)
|
|
305
|
-
def wrapper(self, *args, timeout: Union[int, float] = WAIT_EL, silent: bool = False, **kwargs):
|
|
306
|
-
validate_timeout(timeout)
|
|
307
|
-
validate_silent(silent)
|
|
308
|
-
|
|
309
|
-
start_time = time.time()
|
|
310
|
-
result: Result = method(self, *args, **kwargs)
|
|
311
|
-
|
|
312
|
-
if not silent:
|
|
313
|
-
self.log(result.log)
|
|
314
|
-
|
|
315
|
-
should_increase_delay = self.driver_wrapper.is_appium
|
|
316
|
-
delay = WAIT_METHODS_DELAY
|
|
317
|
-
|
|
318
|
-
while time.time() - start_time < timeout and not result.execution_result:
|
|
319
|
-
time.sleep(delay)
|
|
320
|
-
result: Result = method(self, *args, **kwargs)
|
|
321
|
-
if should_increase_delay:
|
|
322
|
-
delay = increase_delay(delay)
|
|
323
|
-
|
|
324
|
-
if result.execution_result:
|
|
325
|
-
return self
|
|
326
|
-
|
|
327
|
-
result.exc._timeout = timeout # noqa
|
|
328
|
-
raise result.exc
|
|
329
|
-
|
|
330
|
-
return wrapper
|
|
@@ -43,6 +43,7 @@ mops/selenium/elements/mobile_element.py
|
|
|
43
43
|
mops/selenium/elements/web_element.py
|
|
44
44
|
mops/selenium/pages/mobile_page.py
|
|
45
45
|
mops/selenium/pages/web_page.py
|
|
46
|
+
mops/utils/decorators.py
|
|
46
47
|
mops/utils/internal_utils.py
|
|
47
48
|
mops/utils/logs.py
|
|
48
49
|
mops/utils/previous_object_driver.py
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|