playwright-browserstack 1.62.0__py3-none-win32.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.
Files changed (192) hide show
  1. playwright/__init__.py +19 -0
  2. playwright/__main__.py +33 -0
  3. playwright/_impl/__init__.py +0 -0
  4. playwright/_impl/__pyinstaller/__init__.py +20 -0
  5. playwright/_impl/__pyinstaller/hook-playwright.async_api.py +17 -0
  6. playwright/_impl/__pyinstaller/hook-playwright.sync_api.py +17 -0
  7. playwright/_impl/_android.py +704 -0
  8. playwright/_impl/_api_structures.py +365 -0
  9. playwright/_impl/_artifact.py +87 -0
  10. playwright/_impl/_assertions.py +1124 -0
  11. playwright/_impl/_async_base.py +105 -0
  12. playwright/_impl/_browser.py +293 -0
  13. playwright/_impl/_browser_context.py +754 -0
  14. playwright/_impl/_browser_type.py +368 -0
  15. playwright/_impl/_cdp_session.py +46 -0
  16. playwright/_impl/_clock.py +104 -0
  17. playwright/_impl/_connection.py +764 -0
  18. playwright/_impl/_console_message.py +98 -0
  19. playwright/_impl/_credentials.py +57 -0
  20. playwright/_impl/_debugger.py +54 -0
  21. playwright/_impl/_dialog.py +63 -0
  22. playwright/_impl/_disposable.py +96 -0
  23. playwright/_impl/_download.py +64 -0
  24. playwright/_impl/_driver.py +41 -0
  25. playwright/_impl/_element_handle.py +480 -0
  26. playwright/_impl/_errors.py +64 -0
  27. playwright/_impl/_event_context_manager.py +33 -0
  28. playwright/_impl/_fetch.py +626 -0
  29. playwright/_impl/_file_chooser.py +57 -0
  30. playwright/_impl/_form_data.py +34 -0
  31. playwright/_impl/_frame.py +985 -0
  32. playwright/_impl/_glob.py +65 -0
  33. playwright/_impl/_greenlets.py +49 -0
  34. playwright/_impl/_har_router.py +122 -0
  35. playwright/_impl/_helper.py +601 -0
  36. playwright/_impl/_impl_to_api_mapping.py +158 -0
  37. playwright/_impl/_input.py +111 -0
  38. playwright/_impl/_js_handle.py +336 -0
  39. playwright/_impl/_json_pipe.py +97 -0
  40. playwright/_impl/_local_utils.py +95 -0
  41. playwright/_impl/_locator.py +1024 -0
  42. playwright/_impl/_map.py +31 -0
  43. playwright/_impl/_network.py +1017 -0
  44. playwright/_impl/_object_factory.py +116 -0
  45. playwright/_impl/_page.py +1637 -0
  46. playwright/_impl/_path_utils.py +27 -0
  47. playwright/_impl/_playwright.py +68 -0
  48. playwright/_impl/_screencast.py +177 -0
  49. playwright/_impl/_selectors.py +67 -0
  50. playwright/_impl/_set_input_files_helpers.py +158 -0
  51. playwright/_impl/_str_utils.py +76 -0
  52. playwright/_impl/_stream.py +46 -0
  53. playwright/_impl/_sync_base.py +151 -0
  54. playwright/_impl/_tracing.py +281 -0
  55. playwright/_impl/_transport.py +184 -0
  56. playwright/_impl/_video.py +56 -0
  57. playwright/_impl/_waiter.py +197 -0
  58. playwright/_impl/_web_error.py +48 -0
  59. playwright/_impl/_web_storage.py +58 -0
  60. playwright/_impl/_writable_stream.py +42 -0
  61. playwright/_repo_version.py +24 -0
  62. playwright/async_api/__init__.py +275 -0
  63. playwright/async_api/_android_generated.py +462 -0
  64. playwright/async_api/_context_manager.py +57 -0
  65. playwright/async_api/_generated.py +23875 -0
  66. playwright/driver/LICENSE +2946 -0
  67. playwright/driver/README.md +1 -0
  68. playwright/driver/node.exe +0 -0
  69. playwright/driver/package/LICENSE +202 -0
  70. playwright/driver/package/NOTICE +5 -0
  71. playwright/driver/package/README.md +3 -0
  72. playwright/driver/package/ThirdPartyNotices.txt +13 -0
  73. playwright/driver/package/bin/install_media_pack.ps1 +5 -0
  74. playwright/driver/package/bin/install_webkit_wsl.ps1 +33 -0
  75. playwright/driver/package/bin/reinstall_chrome_beta_linux.sh +42 -0
  76. playwright/driver/package/bin/reinstall_chrome_beta_mac.sh +13 -0
  77. playwright/driver/package/bin/reinstall_chrome_beta_win.ps1 +24 -0
  78. playwright/driver/package/bin/reinstall_chrome_stable_linux.sh +42 -0
  79. playwright/driver/package/bin/reinstall_chrome_stable_mac.sh +12 -0
  80. playwright/driver/package/bin/reinstall_chrome_stable_win.ps1 +24 -0
  81. playwright/driver/package/bin/reinstall_msedge_beta_linux.sh +48 -0
  82. playwright/driver/package/bin/reinstall_msedge_beta_mac.sh +11 -0
  83. playwright/driver/package/bin/reinstall_msedge_beta_win.ps1 +23 -0
  84. playwright/driver/package/bin/reinstall_msedge_dev_linux.sh +48 -0
  85. playwright/driver/package/bin/reinstall_msedge_dev_mac.sh +11 -0
  86. playwright/driver/package/bin/reinstall_msedge_dev_win.ps1 +23 -0
  87. playwright/driver/package/bin/reinstall_msedge_stable_linux.sh +48 -0
  88. playwright/driver/package/bin/reinstall_msedge_stable_mac.sh +11 -0
  89. playwright/driver/package/bin/reinstall_msedge_stable_win.ps1 +24 -0
  90. playwright/driver/package/browsers.json +75 -0
  91. playwright/driver/package/cli.js +21 -0
  92. playwright/driver/package/index.d.ts +17 -0
  93. playwright/driver/package/index.js +17 -0
  94. playwright/driver/package/index.mjs +28 -0
  95. playwright/driver/package/lib/bootstrap.js +88 -0
  96. playwright/driver/package/lib/coreBundle.js +74830 -0
  97. playwright/driver/package/lib/entry/cliDaemon.js +5 -0
  98. playwright/driver/package/lib/entry/dashboardApp.js +3 -0
  99. playwright/driver/package/lib/entry/mcp.js +10 -0
  100. playwright/driver/package/lib/entry/oopBrowserDownload.js +3 -0
  101. playwright/driver/package/lib/package.js +50 -0
  102. playwright/driver/package/lib/server/chromium/appIcon.png +0 -0
  103. playwright/driver/package/lib/server/electron/loader.js +118 -0
  104. playwright/driver/package/lib/serverRegistry.js +7347 -0
  105. playwright/driver/package/lib/serverRegistry.js.LICENSE +354 -0
  106. playwright/driver/package/lib/tools/cli-client/channelSessions.js +141 -0
  107. playwright/driver/package/lib/tools/cli-client/cli.js +6 -0
  108. playwright/driver/package/lib/tools/cli-client/help.json +708 -0
  109. playwright/driver/package/lib/tools/cli-client/minimist.js +128 -0
  110. playwright/driver/package/lib/tools/cli-client/output.js +343 -0
  111. playwright/driver/package/lib/tools/cli-client/program.js +404 -0
  112. playwright/driver/package/lib/tools/cli-client/registry.js +176 -0
  113. playwright/driver/package/lib/tools/cli-client/session.js +258 -0
  114. playwright/driver/package/lib/tools/dashboard/appIcon.png +0 -0
  115. playwright/driver/package/lib/tools/skills/playwright-cli/SKILL.md +420 -0
  116. playwright/driver/package/lib/tools/skills/playwright-cli/references/element-attributes.md +23 -0
  117. playwright/driver/package/lib/tools/skills/playwright-cli/references/playwright-tests.md +39 -0
  118. playwright/driver/package/lib/tools/skills/playwright-cli/references/request-mocking.md +87 -0
  119. playwright/driver/package/lib/tools/skills/playwright-cli/references/running-code.md +241 -0
  120. playwright/driver/package/lib/tools/skills/playwright-cli/references/session-management.md +225 -0
  121. playwright/driver/package/lib/tools/skills/playwright-cli/references/storage-state.md +275 -0
  122. playwright/driver/package/lib/tools/skills/playwright-cli/references/test-generation.md +433 -0
  123. playwright/driver/package/lib/tools/skills/playwright-cli/references/tracing.md +139 -0
  124. playwright/driver/package/lib/tools/skills/playwright-cli/references/video-recording.md +143 -0
  125. playwright/driver/package/lib/tools/skills/playwright-component-testing/SKILL.md +143 -0
  126. playwright/driver/package/lib/tools/skills/playwright-component-testing/references/gallery-spec.md +144 -0
  127. playwright/driver/package/lib/tools/skills/playwright-component-testing/references/migration.md +85 -0
  128. playwright/driver/package/lib/tools/skills/playwright-component-testing/references/react.md +67 -0
  129. playwright/driver/package/lib/tools/skills/playwright-component-testing/references/vue.md +75 -0
  130. playwright/driver/package/lib/tools/skills/playwright-trace/SKILL.md +171 -0
  131. playwright/driver/package/lib/tools/utils/extension.js +101 -0
  132. playwright/driver/package/lib/tools/utils/socketConnection.js +108 -0
  133. playwright/driver/package/lib/utilsBundle.js +90764 -0
  134. playwright/driver/package/lib/utilsBundle.js.LICENSE +2179 -0
  135. playwright/driver/package/lib/vite/dashboard/assets/codicon-DCmgc-ay.ttf +0 -0
  136. playwright/driver/package/lib/vite/dashboard/assets/firefox-1bWoP6pv.svg +1 -0
  137. playwright/driver/package/lib/vite/dashboard/assets/firefox-beta-k3eOH_eK.svg +1 -0
  138. playwright/driver/package/lib/vite/dashboard/assets/firefox-nightly-Cp5nfeDT.svg +1 -0
  139. playwright/driver/package/lib/vite/dashboard/assets/index-CyWAfh-p.js +11 -0
  140. playwright/driver/package/lib/vite/dashboard/assets/index-DhC616m4.css +1 -0
  141. playwright/driver/package/lib/vite/dashboard/assets/safari-na3_-uQk.svg +1 -0
  142. playwright/driver/package/lib/vite/dashboard/index.html +29 -0
  143. playwright/driver/package/lib/vite/dashboard/playwright-logo.svg +24 -0
  144. playwright/driver/package/lib/vite/htmlReport/index.html +16 -0
  145. playwright/driver/package/lib/vite/htmlReport/report.css +2 -0
  146. playwright/driver/package/lib/vite/htmlReport/report.js +32 -0
  147. playwright/driver/package/lib/vite/recorder/assets/codeMirrorModule--QdMvsKi.css +1 -0
  148. playwright/driver/package/lib/vite/recorder/assets/codeMirrorModule-CwYYHbcZ.js +32 -0
  149. playwright/driver/package/lib/vite/recorder/assets/codicon-DCmgc-ay.ttf +0 -0
  150. playwright/driver/package/lib/vite/recorder/assets/index-DYjdXIbE.js +129 -0
  151. playwright/driver/package/lib/vite/recorder/assets/index-l_lX622x.css +1 -0
  152. playwright/driver/package/lib/vite/recorder/index.html +29 -0
  153. playwright/driver/package/lib/vite/recorder/playwright-logo.svg +9 -0
  154. playwright/driver/package/lib/vite/traceViewer/assets/codeMirrorModule-By56iMx7.js +32 -0
  155. playwright/driver/package/lib/vite/traceViewer/assets/defaultSettingsView-B34OrIms.js +181 -0
  156. playwright/driver/package/lib/vite/traceViewer/assets/urlMatch-L3liM589.js +1 -0
  157. playwright/driver/package/lib/vite/traceViewer/assets/xtermModule-COQkjINf.js +7 -0
  158. playwright/driver/package/lib/vite/traceViewer/codeMirrorModule.-QdMvsKi.css +1 -0
  159. playwright/driver/package/lib/vite/traceViewer/codicon.DCmgc-ay.ttf +0 -0
  160. playwright/driver/package/lib/vite/traceViewer/defaultSettingsView.BLFoOugd.css +1 -0
  161. playwright/driver/package/lib/vite/traceViewer/index.B_TqY17P.css +1 -0
  162. playwright/driver/package/lib/vite/traceViewer/index.Dl36UVQT.js +1 -0
  163. playwright/driver/package/lib/vite/traceViewer/index.html +44 -0
  164. playwright/driver/package/lib/vite/traceViewer/manifest.webmanifest +16 -0
  165. playwright/driver/package/lib/vite/traceViewer/playwright-logo.svg +9 -0
  166. playwright/driver/package/lib/vite/traceViewer/snapshot.B_Jk1wbt.js +1 -0
  167. playwright/driver/package/lib/vite/traceViewer/snapshot.html +10 -0
  168. playwright/driver/package/lib/vite/traceViewer/sw.bundle.js +4 -0
  169. playwright/driver/package/lib/vite/traceViewer/uiMode.C7UW1sC9.css +1 -0
  170. playwright/driver/package/lib/vite/traceViewer/uiMode.D962mr9b.js +5 -0
  171. playwright/driver/package/lib/vite/traceViewer/uiMode.html +18 -0
  172. playwright/driver/package/lib/vite/traceViewer/xtermModule.kHJ-D0s7.css +1 -0
  173. playwright/driver/package/lib/webp_codec.LICENSE +173 -0
  174. playwright/driver/package/lib/webp_codec.wasm +0 -0
  175. playwright/driver/package/lib/xdg-open +1267 -0
  176. playwright/driver/package/package.json +34 -0
  177. playwright/driver/package/types/protocol.d.ts +24842 -0
  178. playwright/driver/package/types/structs.d.ts +47 -0
  179. playwright/driver/package/types/types.d.ts +26188 -0
  180. playwright/py.typed +0 -0
  181. playwright/sync_api/__init__.py +275 -0
  182. playwright/sync_api/_android_generated.py +494 -0
  183. playwright/sync_api/_context_manager.py +98 -0
  184. playwright/sync_api/_generated.py +24045 -0
  185. playwright_browserstack-1.62.0.dist-info/METADATA +82 -0
  186. playwright_browserstack-1.62.0.dist-info/RECORD +192 -0
  187. playwright_browserstack-1.62.0.dist-info/WHEEL +5 -0
  188. playwright_browserstack-1.62.0.dist-info/entry_points.txt +5 -0
  189. playwright_browserstack-1.62.0.dist-info/licenses/LICENSE +202 -0
  190. playwright_browserstack-1.62.0.dist-info/licenses/LICENSE.txt +51 -0
  191. playwright_browserstack-1.62.0.dist-info/licenses/NOTICE +18 -0
  192. playwright_browserstack-1.62.0.dist-info/top_level.txt +1 -0
@@ -0,0 +1,27 @@
1
+ # Copyright (c) Microsoft Corporation.
2
+ #
3
+ # Licensed under the Apache License, Version 2.0 (the "License");
4
+ # you may not use this file except in compliance with the License.
5
+ # You may obtain a copy of the License at
6
+ #
7
+ # http://www.apache.org/licenses/LICENSE-2.0
8
+ #
9
+ # Unless required by applicable law or agreed to in writing, software
10
+ # distributed under the License is distributed on an "AS IS" BASIS,
11
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12
+ # See the License for the specific language governing permissions and
13
+ # limitations under the License.
14
+
15
+ import inspect
16
+ from pathlib import Path
17
+ from types import FrameType
18
+ from typing import cast
19
+
20
+
21
+ def get_file_dirname() -> Path:
22
+ """Returns the callee (`__file__`) directory name"""
23
+ frame = cast(FrameType, inspect.currentframe()).f_back
24
+ module = inspect.getmodule(frame)
25
+ assert module
26
+ assert module.__file__
27
+ return Path(module.__file__).parent.absolute()
@@ -0,0 +1,68 @@
1
+ # Copyright (c) Microsoft Corporation.
2
+ # Modifications Copyright 2025 BrowserStack
3
+ #
4
+ # Licensed under the Apache License, Version 2.0 (the "License");
5
+ # you may not use this file except in compliance with the License.
6
+ # You may obtain a copy of the License at
7
+ #
8
+ # http://www.apache.org/licenses/LICENSE-2.0
9
+ #
10
+ # Unless required by applicable law or agreed to in writing, software
11
+ # distributed under the License is distributed on an "AS IS" BASIS,
12
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13
+ # See the License for the specific language governing permissions and
14
+ # limitations under the License.
15
+
16
+ from typing import Dict
17
+
18
+ from playwright._impl._android import Android
19
+ from playwright._impl._browser_type import BrowserType
20
+ from playwright._impl._connection import ChannelOwner, from_channel
21
+ from playwright._impl._fetch import APIRequest
22
+ from playwright._impl._selectors import Selectors
23
+
24
+
25
+ class Playwright(ChannelOwner):
26
+ devices: Dict
27
+ selectors: Selectors
28
+ chromium: BrowserType
29
+ firefox: BrowserType
30
+ webkit: BrowserType
31
+ request: APIRequest
32
+
33
+ def __init__(
34
+ self, parent: ChannelOwner, type: str, guid: str, initializer: Dict
35
+ ) -> None:
36
+ super().__init__(parent, type, guid, initializer)
37
+ self.request = APIRequest(self)
38
+ self.chromium = from_channel(initializer["chromium"])
39
+ self.chromium._playwright = self
40
+ self.firefox = from_channel(initializer["firefox"])
41
+ self.firefox._playwright = self
42
+ self.webkit = from_channel(initializer["webkit"])
43
+ self.webkit._playwright = self
44
+
45
+ self.selectors = Selectors(self._loop, self._dispatcher_fiber)
46
+
47
+ self.devices = self._connection.local_utils.devices
48
+ # Deliberately not annotated at class level: scripts/generate_api.py
49
+ # discovers members via get_type_hints(), and an `android` annotation
50
+ # makes it emit a property into _generated.py that references the
51
+ # undefined name "Android" there. See RELEASING.md section 9.
52
+ self.android: "Android" = from_channel(initializer["android"])
53
+ self.android._playwright = self
54
+
55
+ def __getitem__(self, value: str) -> "BrowserType":
56
+ if value == "chromium":
57
+ return self.chromium
58
+ elif value == "firefox":
59
+ return self.firefox
60
+ elif value == "webkit":
61
+ return self.webkit
62
+ raise ValueError("Invalid browser " + value)
63
+
64
+ def _set_selectors(self, selectors: Selectors) -> None:
65
+ self.selectors = selectors
66
+
67
+ async def stop(self) -> None:
68
+ pass
@@ -0,0 +1,177 @@
1
+ # Copyright (c) Microsoft Corporation.
2
+ #
3
+ # Licensed under the Apache License, Version 2.0 (the "License");
4
+ # you may not use this file except in compliance with the License.
5
+ # You may obtain a copy of the License at
6
+ #
7
+ # http://www.apache.org/licenses/LICENSE-2.0
8
+ #
9
+ # Unless required by applicable law or agreed to in writing, software
10
+ # distributed under the License is distributed on an "AS IS" BASIS,
11
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12
+ # See the License for the specific language governing permissions and
13
+ # limitations under the License.
14
+
15
+ import base64
16
+ from pathlib import Path
17
+ from typing import TYPE_CHECKING, Any, Callable, Literal, Optional, Union
18
+
19
+ from playwright._impl._api_structures import ScreencastFrame, ScreencastSize
20
+ from playwright._impl._artifact import Artifact
21
+ from playwright._impl._connection import from_nullable_channel
22
+ from playwright._impl._disposable import DisposableStub
23
+ from playwright._impl._errors import Error
24
+ from playwright._impl._helper import locals_to_params
25
+
26
+ if TYPE_CHECKING: # pragma: no cover
27
+ from playwright._impl._page import Page
28
+
29
+
30
+ ScreencastFrameCallback = Callable[[ScreencastFrame], Any]
31
+ ScreencastPosition = Literal[
32
+ "bottom",
33
+ "bottom-left",
34
+ "bottom-right",
35
+ "top",
36
+ "top-left",
37
+ "top-right",
38
+ ]
39
+ ScreencastCursor = Literal[
40
+ "none",
41
+ "pointer",
42
+ ]
43
+
44
+
45
+ class Screencast:
46
+ def __init__(self, page: "Page") -> None:
47
+ self._page = page
48
+ self._loop = page._loop
49
+ self._dispatcher_fiber = page._dispatcher_fiber
50
+ self._started = False
51
+ self._save_path: Optional[Union[str, Path]] = None
52
+ self._on_frame: Optional[ScreencastFrameCallback] = None
53
+ self._artifact: Optional[Artifact] = None
54
+ page._channel.on("screencastFrame", self._dispatch_frame)
55
+
56
+ def _ack_frame(self, frame_id: int) -> None:
57
+ try:
58
+ self._page._channel.send_no_reply(
59
+ "screencastFrameAck", None, {"frameId": frame_id}
60
+ )
61
+ except (Error, OSError):
62
+ pass
63
+
64
+ async def _await_callback_and_ack(self, result: Any, frame_id: int) -> None:
65
+ try:
66
+ await result
67
+ finally:
68
+ self._ack_frame(frame_id)
69
+
70
+ def _dispatch_frame(self, params: dict) -> Any:
71
+ result = None
72
+ task = None
73
+ try:
74
+ if not self._on_frame:
75
+ return
76
+ data = params["data"]
77
+ if isinstance(data, str):
78
+ data = base64.b64decode(data)
79
+ result = self._on_frame(
80
+ {
81
+ "data": data,
82
+ "timestamp": params.get("timestamp", 0),
83
+ "viewportWidth": params["viewportWidth"],
84
+ "viewportHeight": params["viewportHeight"],
85
+ }
86
+ )
87
+ finally:
88
+ if hasattr(result, "__await__"):
89
+ task = self._loop.create_task(
90
+ self._await_callback_and_ack(result, params["frameId"])
91
+ )
92
+ else:
93
+ self._ack_frame(params["frameId"])
94
+ return task
95
+
96
+ async def start(
97
+ self,
98
+ onFrame: ScreencastFrameCallback = None,
99
+ path: Union[str, Path] = None,
100
+ quality: int = None,
101
+ size: ScreencastSize = None,
102
+ ) -> DisposableStub:
103
+ if self._started:
104
+ raise Error("Screencast is already started")
105
+ self._started = True
106
+ self._on_frame = onFrame
107
+ result = await self._page._channel.send_return_as_dict(
108
+ "screencastStart",
109
+ None,
110
+ {
111
+ "size": size,
112
+ "quality": quality,
113
+ "sendFrames": bool(onFrame),
114
+ "record": bool(path),
115
+ },
116
+ )
117
+ artifact_channel = (result or {}).get("artifact")
118
+ if artifact_channel:
119
+ self._artifact = from_nullable_channel(artifact_channel)
120
+ self._save_path = path
121
+ return DisposableStub(lambda: self.stop(), self._page)
122
+
123
+ async def stop(self) -> None:
124
+ self._started = False
125
+ self._on_frame = None
126
+ await self._page._channel.send("screencastStop", None)
127
+ if self._save_path and self._artifact:
128
+ await self._artifact.save_as(self._save_path)
129
+ self._artifact = None
130
+ self._save_path = None
131
+
132
+ async def show_actions(
133
+ self,
134
+ duration: float = None,
135
+ position: ScreencastPosition = None,
136
+ fontSize: int = None,
137
+ cursor: ScreencastCursor = None,
138
+ ) -> DisposableStub:
139
+ await self._page._channel.send(
140
+ "screencastShowActions", None, locals_to_params(locals())
141
+ )
142
+ return DisposableStub(lambda: self.hide_actions(), self._page)
143
+
144
+ async def hide_actions(self) -> None:
145
+ await self._page._channel.send("screencastHideActions", None)
146
+
147
+ async def show_overlay(self, html: str, duration: float = None) -> DisposableStub:
148
+ result = await self._page._channel.send_return_as_dict(
149
+ "screencastShowOverlay", None, locals_to_params(locals())
150
+ )
151
+ overlay_id = (result or {}).get("id")
152
+ return DisposableStub(
153
+ lambda: self._page._channel.send(
154
+ "screencastRemoveOverlay", None, {"id": overlay_id}
155
+ ),
156
+ self._page,
157
+ )
158
+
159
+ async def show_chapter(
160
+ self,
161
+ title: str,
162
+ description: str = None,
163
+ duration: float = None,
164
+ ) -> None:
165
+ await self._page._channel.send(
166
+ "screencastChapter", None, locals_to_params(locals())
167
+ )
168
+
169
+ async def show_overlays(self) -> None:
170
+ await self._page._channel.send(
171
+ "screencastSetOverlayVisible", None, {"visible": True}
172
+ )
173
+
174
+ async def hide_overlays(self) -> None:
175
+ await self._page._channel.send(
176
+ "screencastSetOverlayVisible", None, {"visible": False}
177
+ )
@@ -0,0 +1,67 @@
1
+ # Copyright (c) Microsoft Corporation.
2
+ #
3
+ # Licensed under the Apache License, Version 2.0 (the "License");
4
+ # you may not use this file except in compliance with the License.
5
+ # You may obtain a copy of the License at
6
+ #
7
+ # http://www.apache.org/licenses/LICENSE-2.0
8
+ #
9
+ # Unless required by applicable law or agreed to in writing, software
10
+ # distributed under the License is distributed on an "AS IS" BASIS,
11
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12
+ # See the License for the specific language governing permissions and
13
+ # limitations under the License.
14
+
15
+ import asyncio
16
+ from pathlib import Path
17
+ from typing import Any, Dict, List, Optional, Set, Union
18
+
19
+ from playwright._impl._browser_context import BrowserContext
20
+ from playwright._impl._errors import Error
21
+ from playwright._impl._helper import async_readfile
22
+ from playwright._impl._locator import set_test_id_attribute_name
23
+
24
+
25
+ class Selectors:
26
+ def __init__(self, loop: asyncio.AbstractEventLoop, dispatcher_fiber: Any) -> None:
27
+ self._loop = loop
28
+ self._contexts_for_selectors: Set[BrowserContext] = set()
29
+ self._selector_engines: List[Dict] = []
30
+ self._dispatcher_fiber = dispatcher_fiber
31
+ self._test_id_attribute_name: Optional[str] = None
32
+
33
+ async def register(
34
+ self,
35
+ name: str,
36
+ script: str = None,
37
+ path: Union[str, Path] = None,
38
+ contentScript: bool = None,
39
+ ) -> None:
40
+ if any(engine for engine in self._selector_engines if engine["name"] == name):
41
+ raise Error(
42
+ f'Selectors.register: "{name}" selector engine has been already registered'
43
+ )
44
+ if not script and not path:
45
+ raise Error("Either source or path should be specified")
46
+ if path:
47
+ script = (await async_readfile(path)).decode()
48
+ engine: Dict[str, Any] = dict(name=name, source=script)
49
+ if contentScript:
50
+ engine["contentScript"] = contentScript
51
+ for context in self._contexts_for_selectors:
52
+ await context._channel.send(
53
+ "registerSelectorEngine",
54
+ None,
55
+ {"selectorEngine": engine},
56
+ )
57
+ self._selector_engines.append(engine)
58
+
59
+ def set_test_id_attribute(self, attributeName: str) -> None:
60
+ set_test_id_attribute_name(attributeName)
61
+ self._test_id_attribute_name = attributeName
62
+ for context in self._contexts_for_selectors:
63
+ context._channel.send_no_reply(
64
+ "setTestIdAttributeName",
65
+ None,
66
+ {"testIdAttributeName": attributeName},
67
+ )
@@ -0,0 +1,158 @@
1
+ # Copyright (c) Microsoft Corporation.
2
+ #
3
+ # Licensed under the Apache License, Version 2.0 (the "License");
4
+ # you may not use this file except in compliance with the License.
5
+ # You may obtain a copy of the License at
6
+ #
7
+ # http://www.apache.org/licenses/LICENSE-2.0
8
+ #
9
+ # Unless required by applicable law or agreed to in writing, software
10
+ # distributed under the License is distributed on an "AS IS" BASIS,
11
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12
+ # See the License for the specific language governing permissions and
13
+ # limitations under the License.
14
+ import base64
15
+ import collections.abc
16
+ import os
17
+ import stat
18
+ from pathlib import Path
19
+ from typing import (
20
+ TYPE_CHECKING,
21
+ Dict,
22
+ List,
23
+ Optional,
24
+ Sequence,
25
+ Tuple,
26
+ TypedDict,
27
+ Union,
28
+ cast,
29
+ )
30
+
31
+ from playwright._impl._connection import Channel, from_channel
32
+ from playwright._impl._helper import Error
33
+ from playwright._impl._writable_stream import WritableStream
34
+
35
+ if TYPE_CHECKING: # pragma: no cover
36
+ from playwright._impl._browser_context import BrowserContext
37
+
38
+ from playwright._impl._api_structures import FilePayload
39
+
40
+ SIZE_LIMIT_IN_BYTES = 50 * 1024 * 1024
41
+
42
+
43
+ class InputFilesList(TypedDict, total=False):
44
+ streams: Optional[List[Channel]]
45
+ directoryStream: Optional[Channel]
46
+ localDirectory: Optional[str]
47
+ localPaths: Optional[List[str]]
48
+ payloads: Optional[List[Dict[str, Union[str, bytes]]]]
49
+
50
+
51
+ def _list_files(directory: str) -> List[str]:
52
+ files = []
53
+ for root, _, filenames in os.walk(directory):
54
+ for filename in filenames:
55
+ files.append(os.path.join(root, filename))
56
+ return files
57
+
58
+
59
+ async def convert_input_files(
60
+ files: Union[
61
+ str, Path, FilePayload, Sequence[Union[str, Path]], Sequence[FilePayload]
62
+ ],
63
+ context: "BrowserContext",
64
+ ) -> InputFilesList:
65
+ items = (
66
+ files
67
+ if isinstance(files, collections.abc.Sequence) and not isinstance(files, str)
68
+ else [files]
69
+ )
70
+
71
+ if any([isinstance(item, (str, Path)) for item in items]):
72
+ if not all([isinstance(item, (str, Path)) for item in items]):
73
+ raise Error("File paths cannot be mixed with buffers")
74
+
75
+ (local_paths, local_directory) = resolve_paths_and_directory_for_input_files(
76
+ cast(Sequence[Union[str, Path]], items)
77
+ )
78
+
79
+ if context._channel._connection.is_remote:
80
+ files_to_stream = cast(
81
+ List[str],
82
+ (_list_files(local_directory) if local_directory else local_paths),
83
+ )
84
+ streams = []
85
+ result = await context._connection.wrap_api_call(
86
+ lambda: context._channel.send_return_as_dict(
87
+ "createTempFiles",
88
+ None,
89
+ {
90
+ "rootDirName": (
91
+ os.path.basename(local_directory)
92
+ if local_directory
93
+ else None
94
+ ),
95
+ "items": list(
96
+ map(
97
+ lambda file: dict(
98
+ name=(
99
+ os.path.relpath(file, local_directory)
100
+ if local_directory
101
+ else os.path.basename(file)
102
+ ),
103
+ lastModifiedMs=int(os.path.getmtime(file) * 1000),
104
+ ),
105
+ files_to_stream,
106
+ )
107
+ ),
108
+ },
109
+ )
110
+ )
111
+ for i, file in enumerate(result["writableStreams"]):
112
+ stream: WritableStream = from_channel(file)
113
+ await stream.copy(files_to_stream[i])
114
+ streams.append(stream._channel)
115
+ return InputFilesList(
116
+ streams=None if local_directory else streams,
117
+ directoryStream=result.get("rootDir"),
118
+ )
119
+ return InputFilesList(localPaths=local_paths, localDirectory=local_directory)
120
+
121
+ file_payload_exceeds_size_limit = (
122
+ sum([len(f.get("buffer", "")) for f in items if not isinstance(f, (str, Path))])
123
+ > SIZE_LIMIT_IN_BYTES
124
+ )
125
+ if file_payload_exceeds_size_limit:
126
+ raise Error(
127
+ "Cannot set buffer larger than 50Mb, please write it to a file and pass its path instead."
128
+ )
129
+
130
+ return InputFilesList(
131
+ payloads=[
132
+ {
133
+ "name": item["name"],
134
+ "mimeType": item["mimeType"],
135
+ "buffer": base64.b64encode(item["buffer"]).decode(),
136
+ }
137
+ for item in cast(List[FilePayload], items)
138
+ ]
139
+ )
140
+
141
+
142
+ def resolve_paths_and_directory_for_input_files(
143
+ items: Sequence[Union[str, Path]],
144
+ ) -> Tuple[Optional[List[str]], Optional[str]]:
145
+ local_paths: Optional[List[str]] = None
146
+ local_directory: Optional[str] = None
147
+ for item in items:
148
+ item_stat = os.stat(item) # Raises FileNotFoundError if doesn't exist
149
+ if stat.S_ISDIR(item_stat.st_mode):
150
+ if local_directory:
151
+ raise Error("Multiple directories are not supported")
152
+ local_directory = str(Path(item).resolve())
153
+ else:
154
+ local_paths = local_paths or []
155
+ local_paths.append(str(Path(item).resolve()))
156
+ if local_paths and local_directory:
157
+ raise Error("File paths must be all files or a single directory")
158
+ return (local_paths, local_directory)
@@ -0,0 +1,76 @@
1
+ # Copyright (c) Microsoft Corporation.
2
+ #
3
+ # Licensed under the Apache License, Version 2.0 (the "License");
4
+ # you may not use this file except in compliance with the License.
5
+ # You may obtain a copy of the License at
6
+ #
7
+ # http://www.apache.org/licenses/LICENSE-2.0
8
+ #
9
+ # Unless required by applicable law or agreed to in writing, software
10
+ # distributed under the License is distributed on an "AS IS" BASIS,
11
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12
+ # See the License for the specific language governing permissions and
13
+ # limitations under the License.
14
+
15
+ import json
16
+ import re
17
+ from typing import Pattern, Union
18
+
19
+
20
+ def escape_regex_flags(pattern: Pattern) -> str:
21
+ flags = ""
22
+ if pattern.flags != 0:
23
+ flags = ""
24
+ if (pattern.flags & int(re.IGNORECASE)) != 0:
25
+ flags += "i"
26
+ if (pattern.flags & int(re.DOTALL)) != 0:
27
+ flags += "s"
28
+ if (pattern.flags & int(re.MULTILINE)) != 0:
29
+ flags += "m"
30
+ assert (
31
+ pattern.flags
32
+ & ~(int(re.MULTILINE) | int(re.IGNORECASE) | int(re.DOTALL) | int(re.UNICODE))
33
+ == 0
34
+ ), "Unexpected re.Pattern flag, only MULTILINE, IGNORECASE and DOTALL are supported."
35
+ return flags
36
+
37
+
38
+ def escape_for_regex(text: str) -> str:
39
+ return re.sub(r"[.*+?^>${}()|[\]\\]", "\\$&", text)
40
+
41
+
42
+ def escape_regex_for_selector(text: Pattern) -> str:
43
+ # Even number of backslashes followed by the quote -> insert a backslash.
44
+ return (
45
+ "/"
46
+ + re.sub(r'(^|[^\\])(\\\\)*(["\'`])', r"\1\2\\\3", text.pattern).replace(
47
+ ">>", "\\>\\>"
48
+ )
49
+ + "/"
50
+ + escape_regex_flags(text)
51
+ )
52
+
53
+
54
+ def escape_for_text_selector(
55
+ text: Union[str, Pattern[str]], exact: bool = None, case_sensitive: bool = None
56
+ ) -> str:
57
+ if isinstance(text, Pattern):
58
+ return escape_regex_for_selector(text)
59
+ return json.dumps(text) + ("s" if exact else "i")
60
+
61
+
62
+ def escape_for_attribute_selector(
63
+ value: Union[str, Pattern], exact: bool = None
64
+ ) -> str:
65
+ if isinstance(value, Pattern):
66
+ return escape_regex_for_selector(value)
67
+ # TODO: this should actually be
68
+ # cssEscape(value).replace(/\\ /g, ' ')
69
+ # However, our attribute selectors do not conform to CSS parsing spec,
70
+ # so we escape them differently.
71
+ return (
72
+ '"'
73
+ + value.replace("\\", "\\\\").replace('"', '\\"')
74
+ + '"'
75
+ + ("s" if exact else "i")
76
+ )
@@ -0,0 +1,46 @@
1
+ # Copyright (c) Microsoft Corporation.
2
+ #
3
+ # Licensed under the Apache License, Version 2.0 (the "License");
4
+ # you may not use this file except in compliance with the License.
5
+ # You may obtain a copy of the License at
6
+ #
7
+ # http://www.apache.org/licenses/LICENSE-2.0
8
+ #
9
+ # Unless required by applicable law or agreed to in writing, software
10
+ # distributed under the License is distributed on an "AS IS" BASIS,
11
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12
+ # See the License for the specific language governing permissions and
13
+ # limitations under the License.
14
+
15
+ import base64
16
+ from pathlib import Path
17
+ from typing import Dict, Union
18
+
19
+ from playwright._impl._connection import ChannelOwner
20
+
21
+
22
+ class Stream(ChannelOwner):
23
+ def __init__(
24
+ self, parent: ChannelOwner, type: str, guid: str, initializer: Dict
25
+ ) -> None:
26
+ super().__init__(parent, type, guid, initializer)
27
+
28
+ async def save_as(self, path: Union[str, Path]) -> None:
29
+ file = await self._loop.run_in_executor(None, lambda: open(path, "wb"))
30
+ while True:
31
+ binary = await self._channel.send("read", None, {"size": 1024 * 1024})
32
+ if not binary:
33
+ break
34
+ await self._loop.run_in_executor(
35
+ None, lambda: file.write(base64.b64decode(binary))
36
+ )
37
+ await self._loop.run_in_executor(None, lambda: file.close())
38
+
39
+ async def read_all(self) -> bytes:
40
+ binary = b""
41
+ while True:
42
+ chunk = await self._channel.send("read", None, {"size": 1024 * 1024})
43
+ if not chunk:
44
+ break
45
+ binary += base64.b64decode(chunk)
46
+ return binary