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.
- playwright/__init__.py +19 -0
- playwright/__main__.py +33 -0
- playwright/_impl/__init__.py +0 -0
- playwright/_impl/__pyinstaller/__init__.py +20 -0
- playwright/_impl/__pyinstaller/hook-playwright.async_api.py +17 -0
- playwright/_impl/__pyinstaller/hook-playwright.sync_api.py +17 -0
- playwright/_impl/_android.py +704 -0
- playwright/_impl/_api_structures.py +365 -0
- playwright/_impl/_artifact.py +87 -0
- playwright/_impl/_assertions.py +1124 -0
- playwright/_impl/_async_base.py +105 -0
- playwright/_impl/_browser.py +293 -0
- playwright/_impl/_browser_context.py +754 -0
- playwright/_impl/_browser_type.py +368 -0
- playwright/_impl/_cdp_session.py +46 -0
- playwright/_impl/_clock.py +104 -0
- playwright/_impl/_connection.py +764 -0
- playwright/_impl/_console_message.py +98 -0
- playwright/_impl/_credentials.py +57 -0
- playwright/_impl/_debugger.py +54 -0
- playwright/_impl/_dialog.py +63 -0
- playwright/_impl/_disposable.py +96 -0
- playwright/_impl/_download.py +64 -0
- playwright/_impl/_driver.py +41 -0
- playwright/_impl/_element_handle.py +480 -0
- playwright/_impl/_errors.py +64 -0
- playwright/_impl/_event_context_manager.py +33 -0
- playwright/_impl/_fetch.py +626 -0
- playwright/_impl/_file_chooser.py +57 -0
- playwright/_impl/_form_data.py +34 -0
- playwright/_impl/_frame.py +985 -0
- playwright/_impl/_glob.py +65 -0
- playwright/_impl/_greenlets.py +49 -0
- playwright/_impl/_har_router.py +122 -0
- playwright/_impl/_helper.py +601 -0
- playwright/_impl/_impl_to_api_mapping.py +158 -0
- playwright/_impl/_input.py +111 -0
- playwright/_impl/_js_handle.py +336 -0
- playwright/_impl/_json_pipe.py +97 -0
- playwright/_impl/_local_utils.py +95 -0
- playwright/_impl/_locator.py +1024 -0
- playwright/_impl/_map.py +31 -0
- playwright/_impl/_network.py +1017 -0
- playwright/_impl/_object_factory.py +116 -0
- playwright/_impl/_page.py +1637 -0
- playwright/_impl/_path_utils.py +27 -0
- playwright/_impl/_playwright.py +68 -0
- playwright/_impl/_screencast.py +177 -0
- playwright/_impl/_selectors.py +67 -0
- playwright/_impl/_set_input_files_helpers.py +158 -0
- playwright/_impl/_str_utils.py +76 -0
- playwright/_impl/_stream.py +46 -0
- playwright/_impl/_sync_base.py +151 -0
- playwright/_impl/_tracing.py +281 -0
- playwright/_impl/_transport.py +184 -0
- playwright/_impl/_video.py +56 -0
- playwright/_impl/_waiter.py +197 -0
- playwright/_impl/_web_error.py +48 -0
- playwright/_impl/_web_storage.py +58 -0
- playwright/_impl/_writable_stream.py +42 -0
- playwright/_repo_version.py +24 -0
- playwright/async_api/__init__.py +275 -0
- playwright/async_api/_android_generated.py +462 -0
- playwright/async_api/_context_manager.py +57 -0
- playwright/async_api/_generated.py +23875 -0
- playwright/driver/LICENSE +2946 -0
- playwright/driver/README.md +1 -0
- playwright/driver/node.exe +0 -0
- playwright/driver/package/LICENSE +202 -0
- playwright/driver/package/NOTICE +5 -0
- playwright/driver/package/README.md +3 -0
- playwright/driver/package/ThirdPartyNotices.txt +13 -0
- playwright/driver/package/bin/install_media_pack.ps1 +5 -0
- playwright/driver/package/bin/install_webkit_wsl.ps1 +33 -0
- playwright/driver/package/bin/reinstall_chrome_beta_linux.sh +42 -0
- playwright/driver/package/bin/reinstall_chrome_beta_mac.sh +13 -0
- playwright/driver/package/bin/reinstall_chrome_beta_win.ps1 +24 -0
- playwright/driver/package/bin/reinstall_chrome_stable_linux.sh +42 -0
- playwright/driver/package/bin/reinstall_chrome_stable_mac.sh +12 -0
- playwright/driver/package/bin/reinstall_chrome_stable_win.ps1 +24 -0
- playwright/driver/package/bin/reinstall_msedge_beta_linux.sh +48 -0
- playwright/driver/package/bin/reinstall_msedge_beta_mac.sh +11 -0
- playwright/driver/package/bin/reinstall_msedge_beta_win.ps1 +23 -0
- playwright/driver/package/bin/reinstall_msedge_dev_linux.sh +48 -0
- playwright/driver/package/bin/reinstall_msedge_dev_mac.sh +11 -0
- playwright/driver/package/bin/reinstall_msedge_dev_win.ps1 +23 -0
- playwright/driver/package/bin/reinstall_msedge_stable_linux.sh +48 -0
- playwright/driver/package/bin/reinstall_msedge_stable_mac.sh +11 -0
- playwright/driver/package/bin/reinstall_msedge_stable_win.ps1 +24 -0
- playwright/driver/package/browsers.json +75 -0
- playwright/driver/package/cli.js +21 -0
- playwright/driver/package/index.d.ts +17 -0
- playwright/driver/package/index.js +17 -0
- playwright/driver/package/index.mjs +28 -0
- playwright/driver/package/lib/bootstrap.js +88 -0
- playwright/driver/package/lib/coreBundle.js +74830 -0
- playwright/driver/package/lib/entry/cliDaemon.js +5 -0
- playwright/driver/package/lib/entry/dashboardApp.js +3 -0
- playwright/driver/package/lib/entry/mcp.js +10 -0
- playwright/driver/package/lib/entry/oopBrowserDownload.js +3 -0
- playwright/driver/package/lib/package.js +50 -0
- playwright/driver/package/lib/server/chromium/appIcon.png +0 -0
- playwright/driver/package/lib/server/electron/loader.js +118 -0
- playwright/driver/package/lib/serverRegistry.js +7347 -0
- playwright/driver/package/lib/serverRegistry.js.LICENSE +354 -0
- playwright/driver/package/lib/tools/cli-client/channelSessions.js +141 -0
- playwright/driver/package/lib/tools/cli-client/cli.js +6 -0
- playwright/driver/package/lib/tools/cli-client/help.json +708 -0
- playwright/driver/package/lib/tools/cli-client/minimist.js +128 -0
- playwright/driver/package/lib/tools/cli-client/output.js +343 -0
- playwright/driver/package/lib/tools/cli-client/program.js +404 -0
- playwright/driver/package/lib/tools/cli-client/registry.js +176 -0
- playwright/driver/package/lib/tools/cli-client/session.js +258 -0
- playwright/driver/package/lib/tools/dashboard/appIcon.png +0 -0
- playwright/driver/package/lib/tools/skills/playwright-cli/SKILL.md +420 -0
- playwright/driver/package/lib/tools/skills/playwright-cli/references/element-attributes.md +23 -0
- playwright/driver/package/lib/tools/skills/playwright-cli/references/playwright-tests.md +39 -0
- playwright/driver/package/lib/tools/skills/playwright-cli/references/request-mocking.md +87 -0
- playwright/driver/package/lib/tools/skills/playwright-cli/references/running-code.md +241 -0
- playwright/driver/package/lib/tools/skills/playwright-cli/references/session-management.md +225 -0
- playwright/driver/package/lib/tools/skills/playwright-cli/references/storage-state.md +275 -0
- playwright/driver/package/lib/tools/skills/playwright-cli/references/test-generation.md +433 -0
- playwright/driver/package/lib/tools/skills/playwright-cli/references/tracing.md +139 -0
- playwright/driver/package/lib/tools/skills/playwright-cli/references/video-recording.md +143 -0
- playwright/driver/package/lib/tools/skills/playwright-component-testing/SKILL.md +143 -0
- playwright/driver/package/lib/tools/skills/playwright-component-testing/references/gallery-spec.md +144 -0
- playwright/driver/package/lib/tools/skills/playwright-component-testing/references/migration.md +85 -0
- playwright/driver/package/lib/tools/skills/playwright-component-testing/references/react.md +67 -0
- playwright/driver/package/lib/tools/skills/playwright-component-testing/references/vue.md +75 -0
- playwright/driver/package/lib/tools/skills/playwright-trace/SKILL.md +171 -0
- playwright/driver/package/lib/tools/utils/extension.js +101 -0
- playwright/driver/package/lib/tools/utils/socketConnection.js +108 -0
- playwright/driver/package/lib/utilsBundle.js +90764 -0
- playwright/driver/package/lib/utilsBundle.js.LICENSE +2179 -0
- playwright/driver/package/lib/vite/dashboard/assets/codicon-DCmgc-ay.ttf +0 -0
- playwright/driver/package/lib/vite/dashboard/assets/firefox-1bWoP6pv.svg +1 -0
- playwright/driver/package/lib/vite/dashboard/assets/firefox-beta-k3eOH_eK.svg +1 -0
- playwright/driver/package/lib/vite/dashboard/assets/firefox-nightly-Cp5nfeDT.svg +1 -0
- playwright/driver/package/lib/vite/dashboard/assets/index-CyWAfh-p.js +11 -0
- playwright/driver/package/lib/vite/dashboard/assets/index-DhC616m4.css +1 -0
- playwright/driver/package/lib/vite/dashboard/assets/safari-na3_-uQk.svg +1 -0
- playwright/driver/package/lib/vite/dashboard/index.html +29 -0
- playwright/driver/package/lib/vite/dashboard/playwright-logo.svg +24 -0
- playwright/driver/package/lib/vite/htmlReport/index.html +16 -0
- playwright/driver/package/lib/vite/htmlReport/report.css +2 -0
- playwright/driver/package/lib/vite/htmlReport/report.js +32 -0
- playwright/driver/package/lib/vite/recorder/assets/codeMirrorModule--QdMvsKi.css +1 -0
- playwright/driver/package/lib/vite/recorder/assets/codeMirrorModule-CwYYHbcZ.js +32 -0
- playwright/driver/package/lib/vite/recorder/assets/codicon-DCmgc-ay.ttf +0 -0
- playwright/driver/package/lib/vite/recorder/assets/index-DYjdXIbE.js +129 -0
- playwright/driver/package/lib/vite/recorder/assets/index-l_lX622x.css +1 -0
- playwright/driver/package/lib/vite/recorder/index.html +29 -0
- playwright/driver/package/lib/vite/recorder/playwright-logo.svg +9 -0
- playwright/driver/package/lib/vite/traceViewer/assets/codeMirrorModule-By56iMx7.js +32 -0
- playwright/driver/package/lib/vite/traceViewer/assets/defaultSettingsView-B34OrIms.js +181 -0
- playwright/driver/package/lib/vite/traceViewer/assets/urlMatch-L3liM589.js +1 -0
- playwright/driver/package/lib/vite/traceViewer/assets/xtermModule-COQkjINf.js +7 -0
- playwright/driver/package/lib/vite/traceViewer/codeMirrorModule.-QdMvsKi.css +1 -0
- playwright/driver/package/lib/vite/traceViewer/codicon.DCmgc-ay.ttf +0 -0
- playwright/driver/package/lib/vite/traceViewer/defaultSettingsView.BLFoOugd.css +1 -0
- playwright/driver/package/lib/vite/traceViewer/index.B_TqY17P.css +1 -0
- playwright/driver/package/lib/vite/traceViewer/index.Dl36UVQT.js +1 -0
- playwright/driver/package/lib/vite/traceViewer/index.html +44 -0
- playwright/driver/package/lib/vite/traceViewer/manifest.webmanifest +16 -0
- playwright/driver/package/lib/vite/traceViewer/playwright-logo.svg +9 -0
- playwright/driver/package/lib/vite/traceViewer/snapshot.B_Jk1wbt.js +1 -0
- playwright/driver/package/lib/vite/traceViewer/snapshot.html +10 -0
- playwright/driver/package/lib/vite/traceViewer/sw.bundle.js +4 -0
- playwright/driver/package/lib/vite/traceViewer/uiMode.C7UW1sC9.css +1 -0
- playwright/driver/package/lib/vite/traceViewer/uiMode.D962mr9b.js +5 -0
- playwright/driver/package/lib/vite/traceViewer/uiMode.html +18 -0
- playwright/driver/package/lib/vite/traceViewer/xtermModule.kHJ-D0s7.css +1 -0
- playwright/driver/package/lib/webp_codec.LICENSE +173 -0
- playwright/driver/package/lib/webp_codec.wasm +0 -0
- playwright/driver/package/lib/xdg-open +1267 -0
- playwright/driver/package/package.json +34 -0
- playwright/driver/package/types/protocol.d.ts +24842 -0
- playwright/driver/package/types/structs.d.ts +47 -0
- playwright/driver/package/types/types.d.ts +26188 -0
- playwright/py.typed +0 -0
- playwright/sync_api/__init__.py +275 -0
- playwright/sync_api/_android_generated.py +494 -0
- playwright/sync_api/_context_manager.py +98 -0
- playwright/sync_api/_generated.py +24045 -0
- playwright_browserstack-1.62.0.dist-info/METADATA +82 -0
- playwright_browserstack-1.62.0.dist-info/RECORD +192 -0
- playwright_browserstack-1.62.0.dist-info/WHEEL +5 -0
- playwright_browserstack-1.62.0.dist-info/entry_points.txt +5 -0
- playwright_browserstack-1.62.0.dist-info/licenses/LICENSE +202 -0
- playwright_browserstack-1.62.0.dist-info/licenses/LICENSE.txt +51 -0
- playwright_browserstack-1.62.0.dist-info/licenses/NOTICE +18 -0
- 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
|