pymobiledevice3 5.0.4__py3-none-any.whl → 7.0.6__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.
- misc/understanding_idevice_protocol_layers.md +10 -5
- pymobiledevice3/__main__.py +171 -46
- pymobiledevice3/_version.py +2 -2
- pymobiledevice3/bonjour.py +22 -21
- pymobiledevice3/cli/activation.py +24 -22
- pymobiledevice3/cli/afc.py +49 -41
- pymobiledevice3/cli/amfi.py +13 -18
- pymobiledevice3/cli/apps.py +71 -65
- pymobiledevice3/cli/backup.py +134 -93
- pymobiledevice3/cli/bonjour.py +31 -29
- pymobiledevice3/cli/cli_common.py +175 -232
- pymobiledevice3/cli/companion_proxy.py +12 -12
- pymobiledevice3/cli/crash.py +95 -52
- pymobiledevice3/cli/developer/__init__.py +62 -0
- pymobiledevice3/cli/developer/accessibility/__init__.py +65 -0
- pymobiledevice3/cli/developer/accessibility/settings.py +43 -0
- pymobiledevice3/cli/developer/arbitration.py +50 -0
- pymobiledevice3/cli/developer/condition.py +33 -0
- pymobiledevice3/cli/developer/core_device.py +294 -0
- pymobiledevice3/cli/developer/debugserver.py +244 -0
- pymobiledevice3/cli/developer/dvt/__init__.py +438 -0
- pymobiledevice3/cli/developer/dvt/core_profile_session.py +295 -0
- pymobiledevice3/cli/developer/dvt/simulate_location.py +56 -0
- pymobiledevice3/cli/developer/dvt/sysmon/__init__.py +69 -0
- pymobiledevice3/cli/developer/dvt/sysmon/process.py +188 -0
- pymobiledevice3/cli/developer/fetch_symbols.py +108 -0
- pymobiledevice3/cli/developer/simulate_location.py +51 -0
- pymobiledevice3/cli/diagnostics/__init__.py +75 -0
- pymobiledevice3/cli/diagnostics/battery.py +47 -0
- pymobiledevice3/cli/idam.py +42 -0
- pymobiledevice3/cli/lockdown.py +70 -75
- pymobiledevice3/cli/mounter.py +99 -57
- pymobiledevice3/cli/notification.py +38 -26
- pymobiledevice3/cli/pcap.py +36 -20
- pymobiledevice3/cli/power_assertion.py +15 -16
- pymobiledevice3/cli/processes.py +11 -17
- pymobiledevice3/cli/profile.py +120 -75
- pymobiledevice3/cli/provision.py +27 -26
- pymobiledevice3/cli/remote.py +109 -100
- pymobiledevice3/cli/restore.py +134 -129
- pymobiledevice3/cli/springboard.py +50 -50
- pymobiledevice3/cli/syslog.py +145 -65
- pymobiledevice3/cli/usbmux.py +66 -27
- pymobiledevice3/cli/version.py +2 -5
- pymobiledevice3/cli/webinspector.py +232 -156
- pymobiledevice3/exceptions.py +6 -2
- pymobiledevice3/lockdown.py +5 -1
- pymobiledevice3/lockdown_service_provider.py +5 -0
- pymobiledevice3/remote/remote_service_discovery.py +18 -10
- pymobiledevice3/restore/device.py +28 -4
- pymobiledevice3/restore/restore.py +2 -2
- pymobiledevice3/service_connection.py +15 -12
- pymobiledevice3/services/afc.py +731 -220
- pymobiledevice3/services/device_link.py +45 -31
- pymobiledevice3/services/idam.py +20 -0
- pymobiledevice3/services/lockdown_service.py +12 -9
- pymobiledevice3/services/mobile_config.py +1 -0
- pymobiledevice3/services/mobilebackup2.py +6 -3
- pymobiledevice3/services/os_trace.py +97 -55
- pymobiledevice3/services/remote_fetch_symbols.py +13 -8
- pymobiledevice3/services/screenshot.py +2 -2
- pymobiledevice3/services/web_protocol/alert.py +8 -8
- pymobiledevice3/services/web_protocol/automation_session.py +87 -79
- pymobiledevice3/services/web_protocol/cdp_screencast.py +2 -1
- pymobiledevice3/services/web_protocol/driver.py +71 -70
- pymobiledevice3/services/web_protocol/element.py +58 -56
- pymobiledevice3/services/web_protocol/selenium_api.py +47 -47
- pymobiledevice3/services/web_protocol/session_protocol.py +3 -2
- pymobiledevice3/services/web_protocol/switch_to.py +23 -19
- pymobiledevice3/services/webinspector.py +42 -67
- {pymobiledevice3-5.0.4.dist-info → pymobiledevice3-7.0.6.dist-info}/METADATA +5 -3
- {pymobiledevice3-5.0.4.dist-info → pymobiledevice3-7.0.6.dist-info}/RECORD +76 -61
- pymobiledevice3/cli/completions.py +0 -50
- pymobiledevice3/cli/developer.py +0 -1539
- pymobiledevice3/cli/diagnostics.py +0 -110
- {pymobiledevice3-5.0.4.dist-info → pymobiledevice3-7.0.6.dist-info}/WHEEL +0 -0
- {pymobiledevice3-5.0.4.dist-info → pymobiledevice3-7.0.6.dist-info}/entry_points.txt +0 -0
- {pymobiledevice3-5.0.4.dist-info → pymobiledevice3-7.0.6.dist-info}/licenses/LICENSE +0 -0
- {pymobiledevice3-5.0.4.dist-info → pymobiledevice3-7.0.6.dist-info}/top_level.txt +0 -0
|
@@ -31,89 +31,91 @@ class WebElement(SeleniumApi):
|
|
|
31
31
|
self.id_ = id_
|
|
32
32
|
self.node_id = id_[f"session-node-{self.session.id_}"]
|
|
33
33
|
|
|
34
|
-
def clear(self):
|
|
34
|
+
async def clear(self):
|
|
35
35
|
"""Clears the text if it's a text entry element."""
|
|
36
36
|
if not self.is_editable():
|
|
37
37
|
return
|
|
38
|
-
rect, center, _is_obscured = self._compute_layout()
|
|
38
|
+
rect, center, _is_obscured = await self._compute_layout()
|
|
39
39
|
if rect is None or center is None:
|
|
40
40
|
return
|
|
41
|
-
self._evaluate_js_function(ELEMENT_CLEAR)
|
|
41
|
+
await self._evaluate_js_function(ELEMENT_CLEAR)
|
|
42
42
|
|
|
43
|
-
def click(self):
|
|
43
|
+
async def click(self):
|
|
44
44
|
"""Clicks the element."""
|
|
45
|
-
rect, center, is_obscured = self._compute_layout(use_viewport=True)
|
|
45
|
+
rect, center, is_obscured = await self._compute_layout(use_viewport=True)
|
|
46
46
|
if rect is None or is_obscured or center is None:
|
|
47
47
|
return
|
|
48
48
|
if self.tag_name == "option":
|
|
49
|
-
self._select_option_element()
|
|
49
|
+
await self._select_option_element()
|
|
50
50
|
else:
|
|
51
|
-
self.session.perform_mouse_interaction(
|
|
51
|
+
await self.session.perform_mouse_interaction(
|
|
52
|
+
center.x, center.y, MouseButton.LEFT, MouseInteraction.SINGLE_CLICK
|
|
53
|
+
)
|
|
52
54
|
|
|
53
|
-
def find_element(self, by=By.ID, value=None):
|
|
55
|
+
async def find_element(self, by=By.ID, value=None):
|
|
54
56
|
"""Find an element given a By strategy and locator."""
|
|
55
|
-
elem = self.session.find_elements(by, value, root=self.id_)
|
|
57
|
+
elem = await self.session.find_elements(by, value, root=self.id_)
|
|
56
58
|
return None if elem is None else WebElement(self.session, elem)
|
|
57
59
|
|
|
58
|
-
def find_elements(self, by=By.ID, value=None):
|
|
60
|
+
async def find_elements(self, by=By.ID, value=None):
|
|
59
61
|
"""Find elements given a By strategy and locator."""
|
|
60
|
-
elements = self.session.find_elements(by, value, single=False, root=self.id_)
|
|
62
|
+
elements = await self.session.find_elements(by, value, single=False, root=self.id_)
|
|
61
63
|
return [WebElement(self.session, elem) for elem in elements]
|
|
62
64
|
|
|
63
|
-
def get_attribute(self, name: str) -> str:
|
|
65
|
+
async def get_attribute(self, name: str) -> str:
|
|
64
66
|
"""Gets the given attribute or property of the element."""
|
|
65
|
-
return self.session.execute_script(f"return ({GET_ATTRIBUTE}).apply(null, arguments);", self.id_, name)
|
|
67
|
+
return await self.session.execute_script(f"return ({GET_ATTRIBUTE}).apply(null, arguments);", self.id_, name)
|
|
66
68
|
|
|
67
|
-
def get_dom_attribute(self, name: str) -> str:
|
|
69
|
+
async def get_dom_attribute(self, name: str) -> str:
|
|
68
70
|
"""Gets the given attribute of the element."""
|
|
69
|
-
return self._evaluate_js_function(ELEMENT_ATTRIBUTE, name)
|
|
71
|
+
return await self._evaluate_js_function(ELEMENT_ATTRIBUTE, name)
|
|
70
72
|
|
|
71
|
-
def get_property(self, name: str) -> str:
|
|
73
|
+
async def get_property(self, name: str) -> str:
|
|
72
74
|
"""Gets the given property of the element."""
|
|
73
|
-
return self._evaluate_js_function(f"function(element) {{ return element.{name}; }}")
|
|
75
|
+
return await self._evaluate_js_function(f"function(element) {{ return element.{name}; }}")
|
|
74
76
|
|
|
75
|
-
def is_displayed(self) -> bool:
|
|
77
|
+
async def is_displayed(self) -> bool:
|
|
76
78
|
"""Whether the element is visible to a user."""
|
|
77
|
-
return self.session.execute_script(f"return ({IS_DISPLAYED}).apply(null, arguments);", self.id_)
|
|
79
|
+
return await self.session.execute_script(f"return ({IS_DISPLAYED}).apply(null, arguments);", self.id_)
|
|
78
80
|
|
|
79
|
-
def is_enabled(self) -> bool:
|
|
81
|
+
async def is_enabled(self) -> bool:
|
|
80
82
|
"""Returns whether the element is enabled."""
|
|
81
|
-
return self._evaluate_js_function(IS_ENABLED)
|
|
83
|
+
return await self._evaluate_js_function(IS_ENABLED)
|
|
82
84
|
|
|
83
|
-
def is_selected(self) -> bool:
|
|
85
|
+
async def is_selected(self) -> bool:
|
|
84
86
|
"""Returns whether the element is selected. Can be used to check if a checkbox or radio button is selected."""
|
|
85
|
-
return bool(self.get_dom_attribute("selected"))
|
|
87
|
+
return bool(await self.get_dom_attribute("selected"))
|
|
86
88
|
|
|
87
89
|
@property
|
|
88
|
-
def location(self) -> Point:
|
|
90
|
+
async def location(self) -> Point:
|
|
89
91
|
"""The location of the element in the renderable canvas."""
|
|
90
|
-
rect = self.rect
|
|
92
|
+
rect = await self.rect
|
|
91
93
|
return Point(x=rect.x, y=rect.y)
|
|
92
94
|
|
|
93
95
|
@property
|
|
94
|
-
def location_once_scrolled_into_view(self) -> Point:
|
|
96
|
+
async def location_once_scrolled_into_view(self) -> Point:
|
|
95
97
|
"""Returns the top lefthand corner location on the screen, or ``None`` if the element is not visible."""
|
|
96
|
-
rect = self.session.execute_script(
|
|
98
|
+
rect = await self.session.execute_script(
|
|
97
99
|
"arguments[0].scrollIntoView(true); return arguments[0].getBoundingClientRect(); ", self.id_
|
|
98
100
|
)
|
|
99
101
|
return Point(x=round(rect["x"]), y=round(rect["y"]))
|
|
100
102
|
|
|
101
103
|
@property
|
|
102
|
-
def rect(self) -> Rect:
|
|
104
|
+
async def rect(self) -> Rect:
|
|
103
105
|
"""The size and location of the element."""
|
|
104
|
-
return self._compute_layout(scroll_if_needed=False)[0]
|
|
106
|
+
return (await self._compute_layout(scroll_if_needed=False))[0]
|
|
105
107
|
|
|
106
108
|
@property
|
|
107
|
-
def screenshot_as_base64(self) -> str:
|
|
109
|
+
async def screenshot_as_base64(self) -> str:
|
|
108
110
|
"""Gets the screenshot of the current element as a base64 encoded string."""
|
|
109
|
-
return self.session.screenshot_as_base64(scroll=True, node_id=self.node_id)
|
|
111
|
+
return await self.session.screenshot_as_base64(scroll=True, node_id=self.node_id)
|
|
110
112
|
|
|
111
|
-
def send_keys(self, value):
|
|
113
|
+
async def send_keys(self, value):
|
|
112
114
|
"""
|
|
113
115
|
Simulates typing into the element.
|
|
114
116
|
:param value: A string for typing, or setting form fields.
|
|
115
117
|
"""
|
|
116
|
-
self._evaluate_js_function(FOCUS)
|
|
118
|
+
await self._evaluate_js_function(FOCUS)
|
|
117
119
|
interactions = []
|
|
118
120
|
sticky_modifier = set()
|
|
119
121
|
for key in value:
|
|
@@ -132,59 +134,59 @@ class WebElement(SeleniumApi):
|
|
|
132
134
|
interactions.append({"type": KeyboardInteractionType.INSERT_BY_KEY, "text": key})
|
|
133
135
|
for modifier in sticky_modifier:
|
|
134
136
|
interactions.append({"type": KeyboardInteractionType.KEY_RELEASE, "key": MODIFIER_TO_KEY[modifier]})
|
|
135
|
-
self.session.perform_keyboard_interactions(interactions)
|
|
137
|
+
await self.session.perform_keyboard_interactions(interactions)
|
|
136
138
|
|
|
137
139
|
@property
|
|
138
|
-
def size(self) -> Size:
|
|
140
|
+
async def size(self) -> Size:
|
|
139
141
|
"""The size of the element."""
|
|
140
|
-
rect = self.rect
|
|
142
|
+
rect = await self.rect
|
|
141
143
|
return Size(height=rect.height, width=rect.width)
|
|
142
144
|
|
|
143
|
-
def submit(self):
|
|
145
|
+
async def submit(self):
|
|
144
146
|
"""Submits a form."""
|
|
145
|
-
form = self.find_element(By.XPATH, "./ancestor-or-self::form")
|
|
147
|
+
form = await self.find_element(By.XPATH, "./ancestor-or-self::form")
|
|
146
148
|
submit_code = (
|
|
147
149
|
"var e = arguments[0].ownerDocument.createEvent('Event');"
|
|
148
150
|
"e.initEvent('submit', true, true);"
|
|
149
151
|
"if (arguments[0].dispatchEvent(e)) { arguments[0].submit() }"
|
|
150
152
|
)
|
|
151
|
-
self.session.execute_script(submit_code, form.id_)
|
|
153
|
+
await self.session.execute_script(submit_code, form.id_)
|
|
152
154
|
|
|
153
155
|
@property
|
|
154
|
-
def tag_name(self) -> str:
|
|
156
|
+
async def tag_name(self) -> str:
|
|
155
157
|
"""This element's ``tagName`` property."""
|
|
156
|
-
return self._evaluate_js_function("function(element) { return element.tagName.toLowerCase() }")
|
|
158
|
+
return await self._evaluate_js_function("function(element) { return element.tagName.toLowerCase() }")
|
|
157
159
|
|
|
158
160
|
@property
|
|
159
|
-
def text(self) -> str:
|
|
161
|
+
async def text(self) -> str:
|
|
160
162
|
"""The text of the element."""
|
|
161
|
-
return self._evaluate_js_function(
|
|
163
|
+
return await self._evaluate_js_function(
|
|
162
164
|
'function(element) { return element.innerText.replace(/^[^\\S\\xa0]+|[^\\S\\xa0]+$/g, "") }'
|
|
163
165
|
)
|
|
164
166
|
|
|
165
|
-
def touch(self):
|
|
167
|
+
async def touch(self) -> None:
|
|
166
168
|
"""Simulate touch interaction on the element."""
|
|
167
|
-
_rect, center, _is_obscured = self._compute_layout(use_viewport=True)
|
|
168
|
-
self.session.perform_interaction_sequence(
|
|
169
|
+
_rect, center, _is_obscured = await self._compute_layout(use_viewport=True)
|
|
170
|
+
await self.session.perform_interaction_sequence(
|
|
169
171
|
[{"sourceId": self.session.id_, "sourceType": "Touch"}],
|
|
170
172
|
[{"states": [{"sourceId": self.session.id_, "location": {"x": center.x, "y": center.y}}]}],
|
|
171
173
|
)
|
|
172
174
|
|
|
173
|
-
def value_of_css_property(self, property_name) -> str:
|
|
175
|
+
async def value_of_css_property(self, property_name) -> str:
|
|
174
176
|
"""The value of a CSS property."""
|
|
175
|
-
return self._evaluate_js_function(
|
|
177
|
+
return await self._evaluate_js_function(
|
|
176
178
|
"function(element) {"
|
|
177
179
|
f' return document.defaultView.getComputedStyle(element).getPropertyValue("{property_name}"); '
|
|
178
180
|
"}"
|
|
179
181
|
)
|
|
180
182
|
|
|
181
|
-
def is_editable(self) -> bool:
|
|
183
|
+
async def is_editable(self) -> bool:
|
|
182
184
|
"""Returns whether the element is editable."""
|
|
183
|
-
return self._evaluate_js_function(IS_EDITABLE)
|
|
185
|
+
return await self._evaluate_js_function(IS_EDITABLE)
|
|
184
186
|
|
|
185
|
-
def _compute_layout(self, scroll_if_needed=True, use_viewport=False):
|
|
187
|
+
async def _compute_layout(self, scroll_if_needed=True, use_viewport=False):
|
|
186
188
|
try:
|
|
187
|
-
result = self.session.compute_element_layout(
|
|
189
|
+
result = await self.session.compute_element_layout(
|
|
188
190
|
self.node_id, scroll_if_needed, "LayoutViewport" if use_viewport else "Page"
|
|
189
191
|
)
|
|
190
192
|
except WirError:
|
|
@@ -196,11 +198,11 @@ class WebElement(SeleniumApi):
|
|
|
196
198
|
center = Point(x=result["inViewCenterPoint"]["x"], y=result["inViewCenterPoint"]["y"])
|
|
197
199
|
return rect, center, result["isObscured"]
|
|
198
200
|
|
|
199
|
-
def _select_option_element(self):
|
|
200
|
-
self.session.select_option_element(self.node_id)
|
|
201
|
+
async def _select_option_element(self):
|
|
202
|
+
await self.session.select_option_element(self.node_id)
|
|
201
203
|
|
|
202
|
-
def _evaluate_js_function(self, function, *args):
|
|
203
|
-
return self.session.evaluate_js_function(function, self.id_, *args)
|
|
204
|
+
async def _evaluate_js_function(self, function, *args):
|
|
205
|
+
return await self.session.evaluate_js_function(function, self.id_, *args)
|
|
204
206
|
|
|
205
207
|
def __eq__(self, other):
|
|
206
208
|
return self.id_ == other.id_
|
|
@@ -6,68 +6,68 @@ from pymobiledevice3.services.web_protocol.automation_session import By
|
|
|
6
6
|
|
|
7
7
|
class SeleniumApi(ABC):
|
|
8
8
|
@abstractmethod
|
|
9
|
-
def find_element(self, by=By.ID, value=None):
|
|
9
|
+
async def find_element(self, by=By.ID, value=None):
|
|
10
10
|
pass
|
|
11
11
|
|
|
12
12
|
@abstractmethod
|
|
13
|
-
def find_elements(self, by=By.ID, value=None):
|
|
13
|
+
async def find_elements(self, by=By.ID, value=None):
|
|
14
14
|
pass
|
|
15
15
|
|
|
16
16
|
@property
|
|
17
17
|
@abstractmethod
|
|
18
|
-
def screenshot_as_base64(self):
|
|
18
|
+
async def screenshot_as_base64(self) -> str:
|
|
19
19
|
pass
|
|
20
20
|
|
|
21
|
-
def find_element_by_class_name(self, name):
|
|
22
|
-
return self.find_element(By.CLASS_NAME, name)
|
|
21
|
+
async def find_element_by_class_name(self, name):
|
|
22
|
+
return await self.find_element(By.CLASS_NAME, name)
|
|
23
23
|
|
|
24
|
-
def find_element_by_css_selector(self, css_selector):
|
|
25
|
-
return self.find_element(By.CSS_SELECTOR, css_selector)
|
|
24
|
+
async def find_element_by_css_selector(self, css_selector):
|
|
25
|
+
return await self.find_element(By.CSS_SELECTOR, css_selector)
|
|
26
26
|
|
|
27
|
-
def find_element_by_id(self, id_):
|
|
28
|
-
return self.find_element(value=id_)
|
|
27
|
+
async def find_element_by_id(self, id_):
|
|
28
|
+
return await self.find_element(value=id_)
|
|
29
29
|
|
|
30
|
-
def find_element_by_link_text(self, link_text):
|
|
31
|
-
return self.find_element(By.LINK_TEXT, link_text)
|
|
30
|
+
async def find_element_by_link_text(self, link_text):
|
|
31
|
+
return await self.find_element(By.LINK_TEXT, link_text)
|
|
32
32
|
|
|
33
|
-
def find_element_by_name(self, name):
|
|
34
|
-
return self.find_element(By.NAME, name)
|
|
33
|
+
async def find_element_by_name(self, name):
|
|
34
|
+
return await self.find_element(By.NAME, name)
|
|
35
35
|
|
|
36
|
-
def find_element_by_partial_link_text(self, link_text):
|
|
37
|
-
return self.find_element(By.PARTIAL_LINK_TEXT, link_text)
|
|
36
|
+
async def find_element_by_partial_link_text(self, link_text):
|
|
37
|
+
return await self.find_element(By.PARTIAL_LINK_TEXT, link_text)
|
|
38
38
|
|
|
39
|
-
def find_element_by_tag_name(self, name):
|
|
40
|
-
return self.find_element(By.TAG_NAME, name)
|
|
39
|
+
async def find_element_by_tag_name(self, name):
|
|
40
|
+
return await self.find_element(By.TAG_NAME, name)
|
|
41
41
|
|
|
42
|
-
def find_element_by_xpath(self, xpath):
|
|
43
|
-
return self.find_element(By.XPATH, xpath)
|
|
42
|
+
async def find_element_by_xpath(self, xpath):
|
|
43
|
+
return await self.find_element(By.XPATH, xpath)
|
|
44
44
|
|
|
45
|
-
def find_elements_by_class_name(self, name):
|
|
46
|
-
return self.find_elements(By.CLASS_NAME, name)
|
|
45
|
+
async def find_elements_by_class_name(self, name):
|
|
46
|
+
return await self.find_elements(By.CLASS_NAME, name)
|
|
47
47
|
|
|
48
|
-
def find_elements_by_css_selector(self, css_selector):
|
|
49
|
-
return self.find_elements(By.CSS_SELECTOR, css_selector)
|
|
48
|
+
async def find_elements_by_css_selector(self, css_selector):
|
|
49
|
+
return await self.find_elements(By.CSS_SELECTOR, css_selector)
|
|
50
50
|
|
|
51
|
-
def find_elements_by_id(self, id_):
|
|
52
|
-
return self.find_elements(value=id_)
|
|
51
|
+
async def find_elements_by_id(self, id_):
|
|
52
|
+
return await self.find_elements(value=id_)
|
|
53
53
|
|
|
54
|
-
def find_elements_by_link_text(self, link_text):
|
|
55
|
-
return self.find_elements(By.LINK_TEXT, link_text)
|
|
54
|
+
async def find_elements_by_link_text(self, link_text):
|
|
55
|
+
return await self.find_elements(By.LINK_TEXT, link_text)
|
|
56
56
|
|
|
57
|
-
def find_elements_by_name(self, name):
|
|
58
|
-
return self.find_elements(By.NAME, name)
|
|
57
|
+
async def find_elements_by_name(self, name):
|
|
58
|
+
return await self.find_elements(By.NAME, name)
|
|
59
59
|
|
|
60
|
-
def find_elements_by_partial_link_text(self, link_text):
|
|
61
|
-
return self.find_elements(By.PARTIAL_LINK_TEXT, link_text)
|
|
60
|
+
async def find_elements_by_partial_link_text(self, link_text):
|
|
61
|
+
return await self.find_elements(By.PARTIAL_LINK_TEXT, link_text)
|
|
62
62
|
|
|
63
|
-
def find_elements_by_tag_name(self, name):
|
|
64
|
-
return self.find_elements(By.TAG_NAME, name)
|
|
63
|
+
async def find_elements_by_tag_name(self, name):
|
|
64
|
+
return await self.find_elements(By.TAG_NAME, name)
|
|
65
65
|
|
|
66
|
-
def find_elements_by_xpath(self, xpath):
|
|
67
|
-
return self.find_elements(By.XPATH, xpath)
|
|
66
|
+
async def find_elements_by_xpath(self, xpath):
|
|
67
|
+
return await self.find_elements(By.XPATH, xpath)
|
|
68
68
|
|
|
69
|
-
def screenshot(self, filename):
|
|
70
|
-
png = self.screenshot_as_png()
|
|
69
|
+
async def screenshot(self, filename):
|
|
70
|
+
png = await self.screenshot_as_png()
|
|
71
71
|
try:
|
|
72
72
|
with open(filename, "wb") as f:
|
|
73
73
|
f.write(png)
|
|
@@ -75,17 +75,17 @@ class SeleniumApi(ABC):
|
|
|
75
75
|
return False
|
|
76
76
|
return True
|
|
77
77
|
|
|
78
|
-
def screenshot_as_png(self):
|
|
79
|
-
return b64decode(self.screenshot_as_base64.encode("ascii"))
|
|
78
|
+
async def screenshot_as_png(self) -> bytes:
|
|
79
|
+
return b64decode((await self.screenshot_as_base64).encode("ascii"))
|
|
80
80
|
|
|
81
|
-
def get_screenshot_as_base64(self):
|
|
82
|
-
return self.screenshot_as_base64
|
|
81
|
+
async def get_screenshot_as_base64(self) -> str:
|
|
82
|
+
return await self.screenshot_as_base64
|
|
83
83
|
|
|
84
|
-
def get_screenshot_as_file(self, filename):
|
|
85
|
-
return self.screenshot(filename)
|
|
84
|
+
async def get_screenshot_as_file(self, filename):
|
|
85
|
+
return await self.screenshot(filename)
|
|
86
86
|
|
|
87
|
-
def get_screenshot_as_png(self):
|
|
88
|
-
return self.screenshot_as_png()
|
|
87
|
+
async def get_screenshot_as_png(self):
|
|
88
|
+
return await self.screenshot_as_png()
|
|
89
89
|
|
|
90
|
-
def save_screenshot(self, filename) -> bool:
|
|
91
|
-
return self.screenshot(filename)
|
|
90
|
+
async def save_screenshot(self, filename) -> bool:
|
|
91
|
+
return await self.screenshot(filename)
|
|
@@ -37,6 +37,7 @@ class SessionProtocol:
|
|
|
37
37
|
return response["result"]
|
|
38
38
|
elif "error" in response:
|
|
39
39
|
raise WirError(response["error"]["message"])
|
|
40
|
+
raise WirError(f"Unknown response: {response}")
|
|
40
41
|
|
|
41
42
|
async def send_receive(self, method, wait_for_response=True, **kwargs):
|
|
42
43
|
wir_id = await self.send_command(method, **kwargs)
|
|
@@ -50,8 +51,8 @@ class SessionProtocol:
|
|
|
50
51
|
await asyncio.sleep(0)
|
|
51
52
|
return self.inspector.wir_message_results.pop(id_)
|
|
52
53
|
|
|
53
|
-
def sync_send_receive(self, method, wait_for_response=True, **kwargs):
|
|
54
|
-
return self.
|
|
54
|
+
async def sync_send_receive(self, method, wait_for_response=True, **kwargs):
|
|
55
|
+
return await self.send_receive(method, wait_for_response, **kwargs)
|
|
55
56
|
|
|
56
57
|
def __getattr__(self, item):
|
|
57
58
|
return partial(self.sync_send_receive, method=item)
|
|
@@ -11,10 +11,12 @@ class SwitchTo:
|
|
|
11
11
|
self.session = session
|
|
12
12
|
|
|
13
13
|
@property
|
|
14
|
-
def active_element(self) -> WebElement:
|
|
14
|
+
async def active_element(self) -> WebElement:
|
|
15
15
|
"""Returns the element with focus, or BODY if nothing has focus."""
|
|
16
|
-
self.session.wait_for_navigation_to_complete()
|
|
17
|
-
elem = self.session.evaluate_js_function(
|
|
16
|
+
await self.session.wait_for_navigation_to_complete()
|
|
17
|
+
elem = await self.session.evaluate_js_function(
|
|
18
|
+
"function() { return document.activeElement; }", include_frame=False
|
|
19
|
+
)
|
|
18
20
|
return WebElement(self.session, elem)
|
|
19
21
|
|
|
20
22
|
@property
|
|
@@ -22,11 +24,11 @@ class SwitchTo:
|
|
|
22
24
|
"""Switches focus to an alert on the page."""
|
|
23
25
|
return Alert(self.session)
|
|
24
26
|
|
|
25
|
-
def default_content(self):
|
|
27
|
+
async def default_content(self):
|
|
26
28
|
"""Switch focus to the default frame."""
|
|
27
|
-
self.session.switch_to_browsing_context("")
|
|
29
|
+
await self.session.switch_to_browsing_context("")
|
|
28
30
|
|
|
29
|
-
def frame(self, frame_reference):
|
|
31
|
+
async def frame(self, frame_reference):
|
|
30
32
|
"""
|
|
31
33
|
Switches focus to the specified frame, by index, name, or web element.
|
|
32
34
|
:param frame_reference: The name of the window to switch to, an integer representing the index,
|
|
@@ -35,32 +37,34 @@ class SwitchTo:
|
|
|
35
37
|
if isinstance(frame_reference, (int, WebElement)):
|
|
36
38
|
frame = frame_reference
|
|
37
39
|
elif isinstance(frame_reference, str):
|
|
38
|
-
elem = self.session.find_elements(By.ID, frame_reference)
|
|
40
|
+
elem = await self.session.find_elements(By.ID, frame_reference)
|
|
39
41
|
if elem is None:
|
|
40
|
-
elem = self.session.find_elements(By.NAME, frame_reference)
|
|
42
|
+
elem = await self.session.find_elements(By.NAME, frame_reference)
|
|
41
43
|
frame = WebElement(self.session, elem)
|
|
42
44
|
else:
|
|
43
45
|
raise TypeError()
|
|
44
46
|
|
|
45
|
-
self.session.wait_for_navigation_to_complete()
|
|
47
|
+
await self.session.wait_for_navigation_to_complete()
|
|
46
48
|
if isinstance(frame, int):
|
|
47
|
-
self.session.switch_to_frame(frame_ordinal=frame)
|
|
49
|
+
await self.session.switch_to_frame(frame_ordinal=frame)
|
|
48
50
|
else:
|
|
49
|
-
self.session.switch_to_frame(frame_handle=frame)
|
|
51
|
+
await self.session.switch_to_frame(frame_handle=frame)
|
|
50
52
|
|
|
51
|
-
def new_window(self, type_=""):
|
|
53
|
+
async def new_window(self, type_=""):
|
|
52
54
|
"""Switches to a new top-level browsing context."""
|
|
53
|
-
self.session.switch_to_window(self.session.create_window(type_))
|
|
55
|
+
await self.session.switch_to_window(self.session.create_window(type_))
|
|
54
56
|
|
|
55
|
-
def parent_frame(self):
|
|
57
|
+
async def parent_frame(self):
|
|
56
58
|
"""
|
|
57
59
|
Switches focus to the parent context. If the current context is the top
|
|
58
60
|
level browsing context, the context remains unchanged.
|
|
59
61
|
"""
|
|
60
|
-
self.session.wait_for_navigation_to_complete()
|
|
61
|
-
self.session.switch_to_browsing_context_frame(
|
|
62
|
-
|
|
62
|
+
await self.session.wait_for_navigation_to_complete()
|
|
63
|
+
await self.session.switch_to_browsing_context_frame(
|
|
64
|
+
self.session.top_level_handle, self.session.current_parent_handle
|
|
65
|
+
)
|
|
66
|
+
await self.session.switch_to_browsing_context(self.session.current_parent_handle)
|
|
63
67
|
|
|
64
|
-
def window(self, window_name):
|
|
68
|
+
async def window(self, window_name):
|
|
65
69
|
"""Switches focus to the specified window."""
|
|
66
|
-
self.session.switch_to_window(window_name)
|
|
70
|
+
await self.session.switch_to_window(window_name)
|