patchright-core 1.57.0 → 1.59.0
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.
- package/ThirdPartyNotices.txt +2688 -297
- package/browsers.json +23 -22
- package/lib/bootstrap.js +77 -0
- package/lib/cli/browserActions.js +308 -0
- package/lib/cli/driver.js +3 -2
- package/lib/cli/installActions.js +171 -0
- package/lib/cli/program.js +48 -413
- package/lib/client/android.js +4 -4
- package/lib/client/api.js +3 -0
- package/lib/client/browser.js +11 -5
- package/lib/client/browserContext.js +20 -23
- package/lib/client/browserType.js +23 -54
- package/lib/client/cdpSession.js +6 -2
- package/lib/client/channelOwner.js +1 -1
- package/lib/client/clientHelper.js +2 -1
- package/lib/client/clock.js +0 -1
- package/lib/client/{webSocket.js → connect.js} +57 -7
- package/lib/client/connection.js +8 -0
- package/lib/client/consoleMessage.js +3 -0
- package/lib/client/debugger.js +57 -0
- package/lib/client/dialog.js +8 -1
- package/lib/client/disposable.js +76 -0
- package/lib/client/electron.js +1 -0
- package/lib/client/elementHandle.js +1 -1
- package/lib/client/events.js +3 -0
- package/lib/client/fetch.js +2 -4
- package/lib/client/frame.js +9 -13
- package/lib/client/harRouter.js +13 -1
- package/lib/client/jsHandle.js +4 -8
- package/lib/client/locator.js +13 -36
- package/lib/client/network.js +14 -11
- package/lib/client/page.js +44 -50
- package/lib/client/screencast.js +88 -0
- package/lib/client/selectors.js +3 -1
- package/lib/client/tracing.js +11 -5
- package/lib/client/video.js +13 -20
- package/lib/client/worker.js +6 -6
- package/lib/generated/bindingsControllerSource.js +1 -1
- package/lib/generated/clockSource.js +1 -1
- package/lib/generated/injectedScriptSource.js +1 -1
- package/lib/generated/pollingRecorderSource.js +1 -1
- package/lib/generated/storageScriptSource.js +1 -1
- package/lib/generated/utilityScriptSource.js +1 -1
- package/lib/mcpBundle.js +78 -0
- package/lib/mcpBundleImpl.js +91 -0
- package/lib/protocol/serializers.js +5 -0
- package/lib/protocol/validator.js +228 -58
- package/lib/protocol/validatorPrimitives.js +1 -1
- package/lib/remote/playwrightConnection.js +10 -8
- package/lib/remote/playwrightPipeServer.js +100 -0
- package/lib/remote/playwrightServer.js +14 -10
- package/lib/remote/playwrightWebSocketServer.js +73 -0
- package/lib/remote/serverTransport.js +96 -0
- package/lib/server/android/android.js +2 -2
- package/lib/server/artifact.js +1 -1
- package/lib/server/bidi/bidiBrowser.js +80 -14
- package/lib/server/bidi/bidiChromium.js +23 -14
- package/lib/server/bidi/bidiConnection.js +1 -0
- package/lib/server/bidi/bidiDeserializer.js +116 -0
- package/lib/server/bidi/bidiExecutionContext.js +75 -29
- package/lib/server/bidi/bidiFirefox.js +6 -8
- package/lib/server/bidi/bidiNetworkManager.js +40 -12
- package/lib/server/bidi/bidiPage.js +67 -40
- package/lib/server/bidi/third_party/bidiProtocolCore.js +1 -0
- package/lib/server/bidi/third_party/firefoxPrefs.js +3 -1
- package/lib/server/browser.js +84 -21
- package/lib/server/browserContext.js +137 -77
- package/lib/server/browserType.js +26 -16
- package/lib/server/chromium/chromium.js +28 -31
- package/lib/server/chromium/chromiumSwitches.js +16 -4
- package/lib/server/chromium/crBrowser.js +40 -27
- package/lib/server/chromium/crConnection.js +0 -5
- package/lib/server/chromium/crDevTools.js +1 -2
- package/lib/server/chromium/crNetworkManager.js +54 -229
- package/lib/server/chromium/crPage.js +74 -260
- package/lib/server/chromium/crServiceWorker.js +7 -14
- package/lib/server/clock.js +33 -41
- package/lib/server/codegen/javascript.js +6 -29
- package/lib/server/console.js +5 -1
- package/lib/server/debugController.js +12 -6
- package/lib/server/debugger.js +40 -47
- package/lib/server/deviceDescriptorsSource.json +137 -137
- package/lib/server/dispatchers/browserContextDispatcher.js +30 -30
- package/lib/server/dispatchers/browserDispatcher.js +11 -5
- package/lib/server/dispatchers/browserTypeDispatcher.js +7 -0
- package/lib/server/dispatchers/cdpSessionDispatcher.js +4 -1
- package/lib/server/dispatchers/debuggerDispatcher.js +84 -0
- package/lib/server/dispatchers/dispatcher.js +7 -14
- package/lib/server/dispatchers/disposableDispatcher.js +39 -0
- package/lib/server/dispatchers/electronDispatcher.js +2 -1
- package/lib/server/dispatchers/frameDispatcher.js +7 -7
- package/lib/server/dispatchers/localUtilsDispatcher.js +37 -1
- package/lib/server/dispatchers/networkDispatchers.js +6 -5
- package/lib/server/dispatchers/pageDispatcher.js +101 -34
- package/lib/server/dispatchers/webSocketRouteDispatcher.js +4 -5
- package/lib/server/disposable.js +41 -0
- package/lib/server/dom.js +56 -29
- package/lib/server/download.js +3 -2
- package/lib/server/electron/electron.js +17 -9
- package/lib/server/firefox/ffBrowser.js +9 -29
- package/lib/server/firefox/ffConnection.js +0 -5
- package/lib/server/firefox/ffInput.js +21 -5
- package/lib/server/firefox/ffNetworkManager.js +4 -4
- package/lib/server/firefox/ffPage.js +27 -33
- package/lib/server/firefox/firefox.js +6 -8
- package/lib/server/frameSelectors.js +14 -169
- package/lib/server/frames.js +263 -551
- package/lib/server/har/harRecorder.js +2 -2
- package/lib/server/har/harTracer.js +5 -4
- package/lib/server/input.js +49 -4
- package/lib/server/instrumentation.js +8 -0
- package/lib/server/javascript.js +6 -22
- package/lib/server/launchApp.js +0 -1
- package/lib/server/localUtils.js +6 -6
- package/lib/server/network.js +59 -20
- package/lib/server/overlay.js +138 -0
- package/lib/server/page.js +179 -157
- package/lib/server/progress.js +32 -6
- package/lib/server/recorder/recorderApp.js +84 -104
- package/lib/server/recorder.js +76 -40
- package/lib/server/registry/browserFetcher.js +6 -4
- package/lib/server/registry/index.js +222 -226
- package/lib/server/registry/nativeDeps.js +1 -0
- package/lib/server/registry/oopDownloadBrowserMain.js +3 -0
- package/lib/server/screencast.js +137 -0
- package/lib/server/trace/recorder/snapshotter.js +2 -2
- package/lib/server/trace/recorder/snapshotterInjected.js +21 -1
- package/lib/server/trace/recorder/tracing.js +98 -47
- package/lib/server/trace/viewer/traceViewer.js +24 -21
- package/lib/server/usKeyboardLayout.js +7 -0
- package/lib/server/utils/comparators.js +1 -1
- package/lib/server/utils/disposable.js +32 -0
- package/lib/server/utils/eventsHelper.js +3 -1
- package/lib/server/utils/expectUtils.js +87 -2
- package/lib/server/utils/fileUtils.js +16 -2
- package/lib/server/utils/happyEyeballs.js +15 -12
- package/lib/server/utils/httpServer.js +10 -23
- package/lib/server/utils/network.js +39 -29
- package/lib/server/utils/processLauncher.js +8 -6
- package/lib/server/utils/zipFile.js +2 -2
- package/lib/server/videoRecorder.js +194 -0
- package/lib/server/webkit/webkit.js +4 -6
- package/lib/server/webkit/wkBrowser.js +1 -10
- package/lib/server/webkit/wkConnection.js +1 -6
- package/lib/server/webkit/wkInterceptableRequest.js +29 -1
- package/lib/server/webkit/wkPage.js +88 -57
- package/lib/server/webkit/wkWorkers.js +2 -1
- package/lib/serverRegistry.js +156 -0
- package/lib/tools/backend/browserBackend.js +79 -0
- package/lib/tools/backend/common.js +63 -0
- package/lib/tools/backend/config.js +41 -0
- package/lib/tools/backend/console.js +66 -0
- package/lib/tools/backend/context.js +296 -0
- package/lib/tools/backend/cookies.js +152 -0
- package/lib/tools/backend/devtools.js +69 -0
- package/lib/tools/backend/dialogs.js +59 -0
- package/lib/tools/backend/evaluate.js +64 -0
- package/lib/tools/backend/files.js +60 -0
- package/lib/tools/backend/form.js +64 -0
- package/lib/tools/backend/keyboard.js +155 -0
- package/lib/tools/backend/logFile.js +95 -0
- package/lib/tools/backend/mouse.js +168 -0
- package/lib/tools/backend/navigate.js +106 -0
- package/lib/tools/backend/network.js +135 -0
- package/lib/tools/backend/pdf.js +48 -0
- package/lib/tools/backend/response.js +305 -0
- package/lib/tools/backend/route.js +140 -0
- package/lib/tools/backend/runCode.js +77 -0
- package/lib/tools/backend/screenshot.js +88 -0
- package/lib/tools/backend/sessionLog.js +74 -0
- package/lib/tools/backend/snapshot.js +208 -0
- package/lib/tools/backend/storage.js +68 -0
- package/lib/tools/backend/tab.js +445 -0
- package/lib/tools/backend/tabs.js +67 -0
- package/lib/tools/backend/tool.js +47 -0
- package/lib/tools/backend/tools.js +102 -0
- package/lib/tools/backend/tracing.js +78 -0
- package/lib/tools/backend/utils.js +83 -0
- package/lib/tools/backend/verify.js +151 -0
- package/lib/tools/backend/video.js +98 -0
- package/lib/tools/backend/wait.js +63 -0
- package/lib/tools/backend/webstorage.js +223 -0
- package/lib/tools/cli-client/cli.js +6 -0
- package/lib/tools/cli-client/help.json +399 -0
- package/lib/tools/cli-client/minimist.js +128 -0
- package/lib/tools/cli-client/program.js +350 -0
- package/lib/tools/cli-client/registry.js +176 -0
- package/lib/tools/cli-client/session.js +289 -0
- package/lib/tools/cli-client/skill/SKILL.md +328 -0
- package/lib/tools/cli-client/skill/references/element-attributes.md +23 -0
- package/lib/tools/cli-client/skill/references/playwright-tests.md +39 -0
- package/lib/tools/cli-client/skill/references/request-mocking.md +87 -0
- package/lib/tools/cli-client/skill/references/running-code.md +231 -0
- package/lib/tools/cli-client/skill/references/session-management.md +169 -0
- package/lib/tools/cli-client/skill/references/storage-state.md +275 -0
- package/lib/tools/cli-client/skill/references/test-generation.md +88 -0
- package/lib/tools/cli-client/skill/references/tracing.md +139 -0
- package/lib/tools/cli-client/skill/references/video-recording.md +143 -0
- package/lib/tools/cli-daemon/command.js +73 -0
- package/lib/tools/cli-daemon/commands.js +956 -0
- package/lib/tools/cli-daemon/daemon.js +157 -0
- package/lib/tools/cli-daemon/helpGenerator.js +177 -0
- package/lib/tools/cli-daemon/program.js +129 -0
- package/lib/tools/dashboard/appIcon.png +0 -0
- package/lib/tools/dashboard/dashboardApp.js +284 -0
- package/lib/tools/dashboard/dashboardController.js +296 -0
- package/lib/tools/exports.js +60 -0
- package/lib/tools/mcp/browserFactory.js +233 -0
- package/lib/tools/mcp/cdpRelay.js +352 -0
- package/lib/tools/mcp/cli-stub.js +7 -0
- package/lib/tools/mcp/config.d.js +16 -0
- package/lib/tools/mcp/config.js +446 -0
- package/lib/tools/mcp/configIni.js +189 -0
- package/lib/tools/mcp/extensionContextFactory.js +55 -0
- package/lib/tools/mcp/index.js +62 -0
- package/lib/tools/mcp/log.js +35 -0
- package/lib/tools/mcp/program.js +107 -0
- package/lib/tools/mcp/protocol.js +28 -0
- package/lib/tools/mcp/watchdog.js +44 -0
- package/lib/tools/trace/SKILL.md +171 -0
- package/lib/tools/trace/installSkill.js +48 -0
- package/lib/tools/trace/traceActions.js +142 -0
- package/lib/tools/trace/traceAttachments.js +69 -0
- package/lib/tools/trace/traceCli.js +87 -0
- package/lib/tools/trace/traceConsole.js +97 -0
- package/lib/tools/trace/traceErrors.js +55 -0
- package/lib/tools/trace/traceOpen.js +69 -0
- package/lib/tools/trace/traceParser.js +96 -0
- package/lib/tools/trace/traceRequests.js +182 -0
- package/lib/tools/trace/traceScreenshot.js +68 -0
- package/lib/tools/trace/traceSnapshot.js +149 -0
- package/lib/tools/trace/traceUtils.js +153 -0
- package/lib/tools/utils/connect.js +32 -0
- package/lib/tools/utils/mcp/http.js +152 -0
- package/lib/tools/utils/mcp/server.js +230 -0
- package/lib/tools/utils/mcp/tool.js +47 -0
- package/lib/tools/utils/socketConnection.js +108 -0
- package/lib/utils/isomorphic/ariaSnapshot.js +60 -2
- package/lib/utils/isomorphic/formatUtils.js +64 -0
- package/lib/utils/isomorphic/jsonSchema.js +89 -0
- package/lib/utils/isomorphic/lruCache.js +51 -0
- package/lib/utils/isomorphic/mimeType.js +7 -2
- package/lib/utils/isomorphic/protocolFormatter.js +2 -2
- package/lib/utils/isomorphic/protocolMetainfo.js +127 -98
- package/lib/utils/isomorphic/stringUtils.js +49 -0
- package/lib/utils/isomorphic/timeoutRunner.js +3 -3
- package/lib/utils/isomorphic/trace/entries.js +16 -0
- package/lib/utils/isomorphic/trace/snapshotRenderer.js +492 -0
- package/lib/utils/isomorphic/trace/snapshotServer.js +120 -0
- package/lib/utils/isomorphic/trace/snapshotStorage.js +89 -0
- package/lib/utils/isomorphic/trace/traceLoader.js +132 -0
- package/lib/utils/isomorphic/trace/traceModel.js +366 -0
- package/lib/utils/isomorphic/trace/traceModernizer.js +401 -0
- package/lib/utils/isomorphic/trace/versions/traceV3.js +16 -0
- package/lib/utils/isomorphic/trace/versions/traceV4.js +16 -0
- package/lib/utils/isomorphic/trace/versions/traceV5.js +16 -0
- package/lib/utils/isomorphic/trace/versions/traceV6.js +16 -0
- package/lib/utils/isomorphic/trace/versions/traceV7.js +16 -0
- package/lib/utils/isomorphic/trace/versions/traceV8.js +16 -0
- package/lib/utils/isomorphic/urlMatch.js +54 -1
- package/lib/utils/isomorphic/utilityScriptSerializers.js +11 -0
- package/lib/utils/isomorphic/yaml.js +84 -0
- package/lib/utils.js +8 -2
- package/lib/utilsBundle.js +5 -26
- package/lib/utilsBundleImpl/index.js +172 -173
- package/lib/vite/dashboard/assets/index-BAOybkp8.js +50 -0
- package/lib/vite/dashboard/assets/index-CZAYOG76.css +1 -0
- package/lib/vite/dashboard/index.html +28 -0
- package/lib/vite/htmlReport/index.html +2 -70
- package/lib/vite/htmlReport/report.css +1 -0
- package/lib/vite/htmlReport/report.js +72 -0
- package/lib/vite/recorder/assets/codeMirrorModule-C8KMvO9L.js +32 -0
- package/lib/vite/recorder/assets/{codeMirrorModule-C3UTv-Ge.css → codeMirrorModule-DYBRYzYX.css} +1 -1
- package/lib/vite/recorder/assets/{index-Ri0uHF7I.css → index-BSjZa4pk.css} +1 -1
- package/lib/vite/recorder/assets/index-CqAYX1I3.js +193 -0
- package/lib/vite/recorder/index.html +2 -2
- package/lib/vite/traceViewer/assets/codeMirrorModule-DS0FLvoc.js +32 -0
- package/lib/vite/traceViewer/assets/defaultSettingsView-GTWI-W_B.js +262 -0
- package/lib/vite/traceViewer/{codeMirrorModule.C3UTv-Ge.css → codeMirrorModule.DYBRYzYX.css} +1 -1
- package/lib/vite/traceViewer/defaultSettingsView.B4dS75f0.css +1 -0
- package/lib/vite/traceViewer/index.CzXZzn5A.css +1 -0
- package/lib/vite/traceViewer/index.Dtstcb7U.js +2 -0
- package/lib/vite/traceViewer/index.html +4 -4
- package/lib/vite/traceViewer/sw.bundle.js +5 -3
- package/lib/vite/traceViewer/uiMode.Vipi55dB.js +6 -0
- package/lib/vite/traceViewer/uiMode.html +3 -3
- package/lib/zipBundleImpl.js +2 -2
- package/lib/zodBundle.js +39 -0
- package/lib/zodBundleImpl.js +40 -0
- package/package.json +7 -1
- package/types/protocol.d.ts +1696 -221
- package/types/types.d.ts +879 -112
- package/lib/server/bidi/third_party/bidiDeserializer.js +0 -98
- package/lib/server/chromium/videoRecorder.js +0 -115
- package/lib/server/pageBinding.js +0 -87
- package/lib/server/trace/test/inMemorySnapshotter.js +0 -87
- package/lib/utils/isomorphic/oldUtilityScriptSerializers.js +0 -248
- package/lib/vite/recorder/assets/codeMirrorModule-CBbSe-ZI.js +0 -25
- package/lib/vite/recorder/assets/index-CpZVd2nA.js +0 -193
- package/lib/vite/traceViewer/assets/codeMirrorModule-DHz0wP2C.js +0 -25
- package/lib/vite/traceViewer/assets/defaultSettingsView-WsZP88O6.js +0 -266
- package/lib/vite/traceViewer/defaultSettingsView.ConWv5KN.css +0 -1
- package/lib/vite/traceViewer/index.C4Y3Aw8n.css +0 -1
- package/lib/vite/traceViewer/index.C8xAeo93.js +0 -2
- package/lib/vite/traceViewer/uiMode.BltraIJB.js +0 -5
- /package/lib/{server/utils → utils/isomorphic}/imageUtils.js +0 -0
- /package/lib/utils/isomorphic/{traceUtils.js → trace/traceUtils.js} +0 -0
|
@@ -18,10 +18,16 @@ var __copyProps = (to, from, except, desc) => {
|
|
|
18
18
|
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
|
|
19
19
|
var expectUtils_exports = {};
|
|
20
20
|
__export(expectUtils_exports, {
|
|
21
|
-
|
|
21
|
+
callLogText: () => callLogText,
|
|
22
|
+
formatMatcherMessage: () => formatMatcherMessage,
|
|
23
|
+
printReceivedStringContainExpectedResult: () => printReceivedStringContainExpectedResult,
|
|
24
|
+
printReceivedStringContainExpectedSubstring: () => printReceivedStringContainExpectedSubstring,
|
|
25
|
+
serializeExpectedTextValues: () => serializeExpectedTextValues,
|
|
26
|
+
simpleMatcherUtils: () => simpleMatcherUtils
|
|
22
27
|
});
|
|
23
28
|
module.exports = __toCommonJS(expectUtils_exports);
|
|
24
29
|
var import_rtti = require("../../utils/isomorphic/rtti");
|
|
30
|
+
var import_utilsBundle = require("../../utilsBundle");
|
|
25
31
|
function serializeExpectedTextValues(items, options = {}) {
|
|
26
32
|
return items.map((i) => ({
|
|
27
33
|
string: (0, import_rtti.isString)(i) ? i : void 0,
|
|
@@ -32,7 +38,86 @@ function serializeExpectedTextValues(items, options = {}) {
|
|
|
32
38
|
normalizeWhiteSpace: options.normalizeWhiteSpace
|
|
33
39
|
}));
|
|
34
40
|
}
|
|
41
|
+
const printSubstring = (val) => val.replace(/"|\\/g, "\\$&");
|
|
42
|
+
const printReceivedStringContainExpectedSubstring = (utils, received, start, length) => utils.RECEIVED_COLOR(
|
|
43
|
+
'"' + printSubstring(received.slice(0, start)) + utils.INVERTED_COLOR(printSubstring(received.slice(start, start + length))) + printSubstring(received.slice(start + length)) + '"'
|
|
44
|
+
);
|
|
45
|
+
const printReceivedStringContainExpectedResult = (utils, received, result) => result === null ? utils.printReceived(received) : printReceivedStringContainExpectedSubstring(
|
|
46
|
+
utils,
|
|
47
|
+
received,
|
|
48
|
+
result.index,
|
|
49
|
+
result[0].length
|
|
50
|
+
);
|
|
51
|
+
function formatMatcherMessage(utils, details) {
|
|
52
|
+
const receiver = details.receiver ?? (details.locator ? "locator" : "page");
|
|
53
|
+
let message = utils.DIM_COLOR("expect(") + utils.RECEIVED_COLOR(receiver) + utils.DIM_COLOR(")" + (details.promise ? "." + details.promise : "") + (details.isNot ? ".not" : "") + ".") + details.matcherName + utils.DIM_COLOR("(") + utils.EXPECTED_COLOR(details.expectation) + utils.DIM_COLOR(")") + " failed\n\n";
|
|
54
|
+
const diffLines = details.printedDiff?.split("\n");
|
|
55
|
+
if (diffLines?.length === 2) {
|
|
56
|
+
details.printedExpected = diffLines[0];
|
|
57
|
+
details.printedReceived = diffLines[1];
|
|
58
|
+
details.printedDiff = void 0;
|
|
59
|
+
}
|
|
60
|
+
const align = !details.errorMessage && details.printedExpected?.startsWith("Expected:") && (!details.printedReceived || details.printedReceived.startsWith("Received:"));
|
|
61
|
+
if (details.locator)
|
|
62
|
+
message += `Locator: ${align ? " " : ""}${details.locator}
|
|
63
|
+
`;
|
|
64
|
+
if (details.printedExpected)
|
|
65
|
+
message += details.printedExpected + "\n";
|
|
66
|
+
if (details.printedReceived)
|
|
67
|
+
message += details.printedReceived + "\n";
|
|
68
|
+
if (details.timedOut && details.timeout)
|
|
69
|
+
message += `Timeout: ${align ? " " : ""}${details.timeout}ms
|
|
70
|
+
`;
|
|
71
|
+
if (details.printedDiff)
|
|
72
|
+
message += details.printedDiff + "\n";
|
|
73
|
+
if (details.errorMessage) {
|
|
74
|
+
message += details.errorMessage;
|
|
75
|
+
if (!details.errorMessage.endsWith("\n"))
|
|
76
|
+
message += "\n";
|
|
77
|
+
}
|
|
78
|
+
message += callLogText(utils, details.log);
|
|
79
|
+
return message;
|
|
80
|
+
}
|
|
81
|
+
const callLogText = (utils, log) => {
|
|
82
|
+
if (!log || !log.some((l) => !!l))
|
|
83
|
+
return "";
|
|
84
|
+
return `
|
|
85
|
+
Call log:
|
|
86
|
+
${utils.DIM_COLOR(log.join("\n"))}
|
|
87
|
+
`;
|
|
88
|
+
};
|
|
89
|
+
function printValue(value) {
|
|
90
|
+
try {
|
|
91
|
+
return JSON.stringify(value);
|
|
92
|
+
} catch {
|
|
93
|
+
return String(value);
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
function printReceived(value) {
|
|
97
|
+
return import_utilsBundle.colors.red(printValue(value));
|
|
98
|
+
}
|
|
99
|
+
function printExpected(value) {
|
|
100
|
+
return import_utilsBundle.colors.green(printValue(value));
|
|
101
|
+
}
|
|
102
|
+
const simpleMatcherUtils = {
|
|
103
|
+
DIM_COLOR: import_utilsBundle.colors.dim,
|
|
104
|
+
RECEIVED_COLOR: import_utilsBundle.colors.red,
|
|
105
|
+
EXPECTED_COLOR: import_utilsBundle.colors.green,
|
|
106
|
+
INVERTED_COLOR: import_utilsBundle.colors.inverse,
|
|
107
|
+
printReceived,
|
|
108
|
+
printExpected,
|
|
109
|
+
printDiffOrStringify: (expected, received, expectedLabel, receivedLabel) => {
|
|
110
|
+
const maxLength = Math.max(expectedLabel.length, receivedLabel.length) + 2;
|
|
111
|
+
return `${expectedLabel}: `.padEnd(maxLength) + printExpected(expected) + `
|
|
112
|
+
` + `${receivedLabel}: `.padEnd(maxLength) + printReceived(received);
|
|
113
|
+
}
|
|
114
|
+
};
|
|
35
115
|
// Annotate the CommonJS export names for ESM import in node:
|
|
36
116
|
0 && (module.exports = {
|
|
37
|
-
|
|
117
|
+
callLogText,
|
|
118
|
+
formatMatcherMessage,
|
|
119
|
+
printReceivedStringContainExpectedResult,
|
|
120
|
+
printReceivedStringContainExpectedSubstring,
|
|
121
|
+
serializeExpectedTextValues,
|
|
122
|
+
simpleMatcherUtils
|
|
38
123
|
});
|
|
@@ -32,6 +32,7 @@ __export(fileUtils_exports, {
|
|
|
32
32
|
canAccessFile: () => canAccessFile,
|
|
33
33
|
copyFileAndMakeWritable: () => copyFileAndMakeWritable,
|
|
34
34
|
existsAsync: () => existsAsync,
|
|
35
|
+
makeSocketPath: () => makeSocketPath,
|
|
35
36
|
mkdirIfNeeded: () => mkdirIfNeeded,
|
|
36
37
|
removeFolders: () => removeFolders,
|
|
37
38
|
sanitizeForFilePath: () => sanitizeForFilePath,
|
|
@@ -39,9 +40,10 @@ __export(fileUtils_exports, {
|
|
|
39
40
|
});
|
|
40
41
|
module.exports = __toCommonJS(fileUtils_exports);
|
|
41
42
|
var import_fs = __toESM(require("fs"));
|
|
43
|
+
var import_os = __toESM(require("os"));
|
|
42
44
|
var import_path = __toESM(require("path"));
|
|
45
|
+
var import_crypto = require("./crypto");
|
|
43
46
|
var import_manualPromise = require("../../utils/isomorphic/manualPromise");
|
|
44
|
-
var import_zipBundle = require("../../zipBundle");
|
|
45
47
|
const existsAsync = (path2) => new Promise((resolve) => import_fs.default.stat(path2, (err) => resolve(!err)));
|
|
46
48
|
async function mkdirIfNeeded(filePath) {
|
|
47
49
|
await import_fs.default.promises.mkdir(import_path.default.dirname(filePath), { recursive: true }).catch(() => {
|
|
@@ -72,6 +74,16 @@ function sanitizeForFilePath(s) {
|
|
|
72
74
|
function toPosixPath(aPath) {
|
|
73
75
|
return aPath.split(import_path.default.sep).join(import_path.default.posix.sep);
|
|
74
76
|
}
|
|
77
|
+
function makeSocketPath(domain, name) {
|
|
78
|
+
const userNameHash = (0, import_crypto.calculateSha1)(process.env.USERNAME || process.env.USER || "default").slice(0, 8);
|
|
79
|
+
if (process.platform === "win32")
|
|
80
|
+
return `\\\\.\\pipe\\pw-${userNameHash}-${domain}-${name}`;
|
|
81
|
+
const baseDir = process.env.PLAYWRIGHT_SOCKETS_DIR || import_path.default.join(import_os.default.tmpdir(), `pw-${userNameHash}`);
|
|
82
|
+
const dir = import_path.default.join(baseDir, domain);
|
|
83
|
+
const result = import_path.default.join(dir, `${name}.sock`);
|
|
84
|
+
import_fs.default.mkdirSync(dir, { recursive: true });
|
|
85
|
+
return result;
|
|
86
|
+
}
|
|
75
87
|
class SerializedFS {
|
|
76
88
|
constructor() {
|
|
77
89
|
this._buffers = /* @__PURE__ */ new Map();
|
|
@@ -165,7 +177,8 @@ class SerializedFS {
|
|
|
165
177
|
return;
|
|
166
178
|
}
|
|
167
179
|
case "zip": {
|
|
168
|
-
const
|
|
180
|
+
const { yazl } = await import("playwright-core/lib/zipBundle");
|
|
181
|
+
const zipFile = new yazl.ZipFile();
|
|
169
182
|
const result = new import_manualPromise.ManualPromise();
|
|
170
183
|
zipFile.on("error", (error) => result.reject(error));
|
|
171
184
|
for (const entry of op.entries)
|
|
@@ -184,6 +197,7 @@ class SerializedFS {
|
|
|
184
197
|
canAccessFile,
|
|
185
198
|
copyFileAndMakeWritable,
|
|
186
199
|
existsAsync,
|
|
200
|
+
makeSocketPath,
|
|
187
201
|
mkdirIfNeeded,
|
|
188
202
|
removeFolders,
|
|
189
203
|
sanitizeForFilePath,
|
|
@@ -166,20 +166,23 @@ async function createConnectionAsync(options, oncreate, useTLS) {
|
|
|
166
166
|
}
|
|
167
167
|
}
|
|
168
168
|
async function lookupAddresses(hostname) {
|
|
169
|
-
const
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
169
|
+
const [v4Result, v6Result] = await Promise.allSettled([
|
|
170
|
+
import_dns.default.promises.lookup(hostname, { all: true, family: 4 }),
|
|
171
|
+
import_dns.default.promises.lookup(hostname, { all: true, family: 6 })
|
|
172
|
+
]);
|
|
173
|
+
const v4Addresses = v4Result.status === "fulfilled" ? v4Result.value : [];
|
|
174
|
+
const v6Addresses = v6Result.status === "fulfilled" ? v6Result.value : [];
|
|
175
|
+
if (!v4Addresses.length && !v6Addresses.length) {
|
|
176
|
+
if (v4Result.status === "rejected")
|
|
177
|
+
throw v4Result.reason;
|
|
178
|
+
throw v6Result.reason;
|
|
176
179
|
}
|
|
177
180
|
const result = [];
|
|
178
|
-
for (let i = 0; i < Math.max(
|
|
179
|
-
if (
|
|
180
|
-
result.push(
|
|
181
|
-
if (
|
|
182
|
-
result.push(
|
|
181
|
+
for (let i = 0; i < Math.max(v4Addresses.length, v6Addresses.length); i++) {
|
|
182
|
+
if (v6Addresses[i])
|
|
183
|
+
result.push(v6Addresses[i]);
|
|
184
|
+
if (v4Addresses[i])
|
|
185
|
+
result.push(v4Addresses[i]);
|
|
183
186
|
}
|
|
184
187
|
return result;
|
|
185
188
|
}
|
|
@@ -36,7 +36,6 @@ var import_path = __toESM(require("path"));
|
|
|
36
36
|
var import_utilsBundle = require("../../utilsBundle");
|
|
37
37
|
var import_crypto = require("./crypto");
|
|
38
38
|
var import_assert = require("../../utils/isomorphic/assert");
|
|
39
|
-
var import_manualPromise = require("../../utils/isomorphic/manualPromise");
|
|
40
39
|
var import_network = require("./network");
|
|
41
40
|
class HttpServer {
|
|
42
41
|
constructor() {
|
|
@@ -59,28 +58,16 @@ class HttpServer {
|
|
|
59
58
|
port() {
|
|
60
59
|
return this._port;
|
|
61
60
|
}
|
|
62
|
-
|
|
63
|
-
const errorPromise = new import_manualPromise.ManualPromise();
|
|
64
|
-
const errorListener = (error) => errorPromise.reject(error);
|
|
65
|
-
this._server.on("error", errorListener);
|
|
66
|
-
try {
|
|
67
|
-
this._server.listen(port, host);
|
|
68
|
-
await Promise.race([
|
|
69
|
-
new Promise((cb) => this._server.once("listening", cb)),
|
|
70
|
-
errorPromise
|
|
71
|
-
]);
|
|
72
|
-
} finally {
|
|
73
|
-
this._server.removeListener("error", errorListener);
|
|
74
|
-
}
|
|
75
|
-
}
|
|
76
|
-
createWebSocket(transport, guid) {
|
|
61
|
+
createWebSocket(transportFactory, guid) {
|
|
77
62
|
(0, import_assert.assert)(!this._wsGuid, "can only create one main websocket transport per server");
|
|
78
63
|
this._wsGuid = guid || (0, import_crypto.createGuid)();
|
|
79
64
|
const wss = new import_utilsBundle.wsServer({ server: this._server, path: "/" + this._wsGuid });
|
|
80
|
-
wss.on("connection", (ws) => {
|
|
81
|
-
|
|
65
|
+
wss.on("connection", (ws, request) => {
|
|
66
|
+
const url = new URL(request.url ?? "/", "http://localhost");
|
|
67
|
+
const transport = transportFactory(url);
|
|
82
68
|
transport.sendEvent = (method, params) => ws.send(JSON.stringify({ method, params }));
|
|
83
69
|
transport.close = () => ws.close();
|
|
70
|
+
transport.onconnect();
|
|
84
71
|
ws.on("message", async (message) => {
|
|
85
72
|
const { id, method, params } = JSON.parse(String(message));
|
|
86
73
|
try {
|
|
@@ -100,17 +87,17 @@ class HttpServer {
|
|
|
100
87
|
async start(options = {}) {
|
|
101
88
|
(0, import_assert.assert)(!this._started, "server already started");
|
|
102
89
|
this._started = true;
|
|
103
|
-
const host = options.host
|
|
90
|
+
const host = options.host;
|
|
104
91
|
if (options.preferredPort) {
|
|
105
92
|
try {
|
|
106
|
-
await this.
|
|
93
|
+
await (0, import_network.startHttpServer)(this._server, { port: options.preferredPort, host });
|
|
107
94
|
} catch (e) {
|
|
108
95
|
if (!e || !e.message || !e.message.includes("EADDRINUSE"))
|
|
109
96
|
throw e;
|
|
110
|
-
await
|
|
97
|
+
await (0, import_network.startHttpServer)(this._server, { host });
|
|
111
98
|
}
|
|
112
99
|
} else {
|
|
113
|
-
await this.
|
|
100
|
+
await (0, import_network.startHttpServer)(this._server, { port: options.port, host });
|
|
114
101
|
}
|
|
115
102
|
const address = this._server.address();
|
|
116
103
|
(0, import_assert.assert)(address, "Could not bind server socket");
|
|
@@ -121,7 +108,7 @@ class HttpServer {
|
|
|
121
108
|
this._port = address.port;
|
|
122
109
|
const resolvedHost = address.family === "IPv4" ? address.address : `[${address.address}]`;
|
|
123
110
|
this._urlPrefixPrecise = `http://${resolvedHost}:${address.port}`;
|
|
124
|
-
this._urlPrefixHumanReadable = `http://${host}:${address.port}`;
|
|
111
|
+
this._urlPrefixHumanReadable = `http://${host ?? "localhost"}:${address.port}`;
|
|
125
112
|
}
|
|
126
113
|
}
|
|
127
114
|
async stop() {
|
|
@@ -33,24 +33,23 @@ __export(network_exports, {
|
|
|
33
33
|
createHttpServer: () => createHttpServer,
|
|
34
34
|
createHttpsServer: () => createHttpsServer,
|
|
35
35
|
createProxyAgent: () => createProxyAgent,
|
|
36
|
+
decorateServer: () => decorateServer,
|
|
36
37
|
fetchData: () => fetchData,
|
|
37
38
|
httpRequest: () => httpRequest,
|
|
38
|
-
isURLAvailable: () => isURLAvailable
|
|
39
|
+
isURLAvailable: () => isURLAvailable,
|
|
40
|
+
startHttpServer: () => startHttpServer
|
|
39
41
|
});
|
|
40
42
|
module.exports = __toCommonJS(network_exports);
|
|
41
43
|
var import_http = __toESM(require("http"));
|
|
42
44
|
var import_http2 = __toESM(require("http2"));
|
|
43
45
|
var import_https = __toESM(require("https"));
|
|
44
|
-
var import_url = __toESM(require("url"));
|
|
45
46
|
var import_utilsBundle = require("../../utilsBundle");
|
|
46
47
|
var import_happyEyeballs = require("./happyEyeballs");
|
|
47
48
|
var import_manualPromise = require("../../utils/isomorphic/manualPromise");
|
|
48
49
|
const NET_DEFAULT_TIMEOUT = 3e4;
|
|
49
50
|
function httpRequest(params, onResponse, onError) {
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
...parsedUrl,
|
|
53
|
-
agent: parsedUrl.protocol === "https:" ? import_happyEyeballs.httpsHappyEyeballsAgent : import_happyEyeballs.httpHappyEyeballsAgent,
|
|
51
|
+
let url = new URL(params.url);
|
|
52
|
+
const options = {
|
|
54
53
|
method: params.method || "GET",
|
|
55
54
|
headers: params.headers
|
|
56
55
|
};
|
|
@@ -58,21 +57,15 @@ function httpRequest(params, onResponse, onError) {
|
|
|
58
57
|
options.rejectUnauthorized = params.rejectUnauthorized;
|
|
59
58
|
const proxyURL = (0, import_utilsBundle.getProxyForUrl)(params.url);
|
|
60
59
|
if (proxyURL) {
|
|
60
|
+
const parsedProxyURL = normalizeProxyURL(proxyURL);
|
|
61
61
|
if (params.url.startsWith("http:")) {
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
path: parsedUrl.href,
|
|
65
|
-
host: parsedProxyURL.hostname,
|
|
66
|
-
port: parsedProxyURL.port,
|
|
67
|
-
protocol: parsedProxyURL.protocol || "http:",
|
|
68
|
-
headers: options.headers,
|
|
69
|
-
method: options.method
|
|
70
|
-
};
|
|
62
|
+
parsedProxyURL.pathname = url.toString();
|
|
63
|
+
url = parsedProxyURL;
|
|
71
64
|
} else {
|
|
72
|
-
options.agent = new import_utilsBundle.HttpsProxyAgent(
|
|
73
|
-
options.rejectUnauthorized = false;
|
|
65
|
+
options.agent = new import_utilsBundle.HttpsProxyAgent(parsedProxyURL);
|
|
74
66
|
}
|
|
75
67
|
}
|
|
68
|
+
options.agent ??= url.protocol === "https:" ? import_happyEyeballs.httpsHappyEyeballsAgent : import_happyEyeballs.httpHappyEyeballsAgent;
|
|
76
69
|
let cancelRequest;
|
|
77
70
|
const requestCallback = (res) => {
|
|
78
71
|
const statusCode = res.statusCode || 0;
|
|
@@ -83,7 +76,7 @@ function httpRequest(params, onResponse, onError) {
|
|
|
83
76
|
onResponse(res);
|
|
84
77
|
}
|
|
85
78
|
};
|
|
86
|
-
const request =
|
|
79
|
+
const request = url.protocol === "https:" ? import_https.default.request(url, options, requestCallback) : import_http.default.request(url, options, requestCallback);
|
|
87
80
|
request.on("error", onError);
|
|
88
81
|
if (params.socketTimeout !== void 0) {
|
|
89
82
|
request.setTimeout(params.socketTimeout, () => {
|
|
@@ -122,7 +115,7 @@ async function fetchData(progress, params, onError) {
|
|
|
122
115
|
throw error;
|
|
123
116
|
}
|
|
124
117
|
}
|
|
125
|
-
function shouldBypassProxy(
|
|
118
|
+
function shouldBypassProxy(url, bypass) {
|
|
126
119
|
if (!bypass)
|
|
127
120
|
return false;
|
|
128
121
|
const domains = bypass.split(",").map((s) => {
|
|
@@ -131,7 +124,7 @@ function shouldBypassProxy(url2, bypass) {
|
|
|
131
124
|
s = "." + s;
|
|
132
125
|
return s;
|
|
133
126
|
});
|
|
134
|
-
const domain = "." +
|
|
127
|
+
const domain = "." + url.hostname;
|
|
135
128
|
return domains.some((d) => domain.endsWith(d));
|
|
136
129
|
}
|
|
137
130
|
function normalizeProxyURL(proxy) {
|
|
@@ -177,20 +170,35 @@ function createHttp2Server(...args) {
|
|
|
177
170
|
decorateServer(server);
|
|
178
171
|
return server;
|
|
179
172
|
}
|
|
180
|
-
async function
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
173
|
+
async function startHttpServer(server, options) {
|
|
174
|
+
const { host = "localhost", port = 0 } = options;
|
|
175
|
+
const errorPromise = new import_manualPromise.ManualPromise();
|
|
176
|
+
const errorListener = (error) => errorPromise.reject(error);
|
|
177
|
+
server.on("error", errorListener);
|
|
178
|
+
try {
|
|
179
|
+
server.listen(port, host);
|
|
180
|
+
await Promise.race([
|
|
181
|
+
new Promise((cb) => server.once("listening", cb)),
|
|
182
|
+
errorPromise
|
|
183
|
+
]);
|
|
184
|
+
} finally {
|
|
185
|
+
server.removeListener("error", errorListener);
|
|
186
|
+
}
|
|
187
|
+
}
|
|
188
|
+
async function isURLAvailable(url, ignoreHTTPSErrors, onLog, onStdErr) {
|
|
189
|
+
let statusCode = await httpStatusCode(url, ignoreHTTPSErrors, onLog, onStdErr);
|
|
190
|
+
if (statusCode === 404 && url.pathname === "/") {
|
|
191
|
+
const indexUrl = new URL(url);
|
|
184
192
|
indexUrl.pathname = "/index.html";
|
|
185
193
|
statusCode = await httpStatusCode(indexUrl, ignoreHTTPSErrors, onLog, onStdErr);
|
|
186
194
|
}
|
|
187
195
|
return statusCode >= 200 && statusCode < 404;
|
|
188
196
|
}
|
|
189
|
-
async function httpStatusCode(
|
|
197
|
+
async function httpStatusCode(url, ignoreHTTPSErrors, onLog, onStdErr) {
|
|
190
198
|
return new Promise((resolve) => {
|
|
191
|
-
onLog?.(`HTTP GET: ${
|
|
199
|
+
onLog?.(`HTTP GET: ${url}`);
|
|
192
200
|
httpRequest({
|
|
193
|
-
url:
|
|
201
|
+
url: url.toString(),
|
|
194
202
|
headers: { Accept: "*/*" },
|
|
195
203
|
rejectUnauthorized: !ignoreHTTPSErrors
|
|
196
204
|
}, (res) => {
|
|
@@ -201,7 +209,7 @@ async function httpStatusCode(url2, ignoreHTTPSErrors, onLog, onStdErr) {
|
|
|
201
209
|
}, (error) => {
|
|
202
210
|
if (error.code === "DEPTH_ZERO_SELF_SIGNED_CERT")
|
|
203
211
|
onStdErr?.(`[WebServer] Self-signed certificate detected. Try adding ignoreHTTPSErrors: true to config.webServer.`);
|
|
204
|
-
onLog?.(`Error while checking if ${
|
|
212
|
+
onLog?.(`Error while checking if ${url} is available: ${error.message}`);
|
|
205
213
|
resolve(0);
|
|
206
214
|
});
|
|
207
215
|
});
|
|
@@ -227,7 +235,9 @@ function decorateServer(server) {
|
|
|
227
235
|
createHttpServer,
|
|
228
236
|
createHttpsServer,
|
|
229
237
|
createProxyAgent,
|
|
238
|
+
decorateServer,
|
|
230
239
|
fetchData,
|
|
231
240
|
httpRequest,
|
|
232
|
-
isURLAvailable
|
|
241
|
+
isURLAvailable,
|
|
242
|
+
startHttpServer
|
|
233
243
|
});
|
|
@@ -46,11 +46,12 @@ async function gracefullyCloseAll() {
|
|
|
46
46
|
await Promise.all(Array.from(gracefullyCloseSet).map((gracefullyClose) => gracefullyClose().catch((e) => {
|
|
47
47
|
})));
|
|
48
48
|
}
|
|
49
|
-
function gracefullyProcessExitDoNotHang(code) {
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
49
|
+
function gracefullyProcessExitDoNotHang(code, onExit) {
|
|
50
|
+
const beforeExit = onExit ? () => onExit().catch(() => {
|
|
51
|
+
}) : () => Promise.resolve();
|
|
52
|
+
const callback = () => beforeExit().then(() => process.exit(code));
|
|
53
|
+
setTimeout(callback, 3e4);
|
|
54
|
+
gracefullyCloseAll().then(callback);
|
|
54
55
|
}
|
|
55
56
|
function exitHandler() {
|
|
56
57
|
for (const kill of killSet)
|
|
@@ -109,6 +110,7 @@ async function launchProcess(options) {
|
|
|
109
110
|
// process group, making it possible to kill child process tree with `.kill(-pid)` command.
|
|
110
111
|
// @see https://nodejs.org/api/child_process.html#child_process_options_detached
|
|
111
112
|
detached: process.platform !== "win32",
|
|
113
|
+
windowsHide: true,
|
|
112
114
|
env: options.env,
|
|
113
115
|
cwd: options.cwd,
|
|
114
116
|
shell: options.shell,
|
|
@@ -191,7 +193,7 @@ async function launchProcess(options) {
|
|
|
191
193
|
options.log(`[pid=${spawnedProcess.pid}] <will force kill>`);
|
|
192
194
|
try {
|
|
193
195
|
if (process.platform === "win32") {
|
|
194
|
-
const taskkillProcess = childProcess.spawnSync(`taskkill /pid ${spawnedProcess.pid} /T /F`, { shell: true });
|
|
196
|
+
const taskkillProcess = childProcess.spawnSync(`taskkill /pid ${spawnedProcess.pid} /T /F`, { shell: true, windowsHide: true });
|
|
195
197
|
const [stdout2, stderr2] = [taskkillProcess.stdout.toString(), taskkillProcess.stderr.toString()];
|
|
196
198
|
if (stdout2)
|
|
197
199
|
options.log(`[pid=${spawnedProcess.pid}] taskkill stdout: ${stdout2}`);
|
|
@@ -21,7 +21,6 @@ __export(zipFile_exports, {
|
|
|
21
21
|
ZipFile: () => ZipFile
|
|
22
22
|
});
|
|
23
23
|
module.exports = __toCommonJS(zipFile_exports);
|
|
24
|
-
var import_zipBundle = require("../../zipBundle");
|
|
25
24
|
class ZipFile {
|
|
26
25
|
constructor(fileName) {
|
|
27
26
|
this._entries = /* @__PURE__ */ new Map();
|
|
@@ -29,8 +28,9 @@ class ZipFile {
|
|
|
29
28
|
this._openedPromise = this._open();
|
|
30
29
|
}
|
|
31
30
|
async _open() {
|
|
31
|
+
const { yauzl } = require("../../zipBundle");
|
|
32
32
|
await new Promise((fulfill, reject) => {
|
|
33
|
-
|
|
33
|
+
yauzl.open(this._fileName, { autoClose: false }, (e, z) => {
|
|
34
34
|
if (e) {
|
|
35
35
|
reject(e);
|
|
36
36
|
return;
|
|
@@ -0,0 +1,194 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __create = Object.create;
|
|
3
|
+
var __defProp = Object.defineProperty;
|
|
4
|
+
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
5
|
+
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
6
|
+
var __getProtoOf = Object.getPrototypeOf;
|
|
7
|
+
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
8
|
+
var __export = (target, all) => {
|
|
9
|
+
for (var name in all)
|
|
10
|
+
__defProp(target, name, { get: all[name], enumerable: true });
|
|
11
|
+
};
|
|
12
|
+
var __copyProps = (to, from, except, desc) => {
|
|
13
|
+
if (from && typeof from === "object" || typeof from === "function") {
|
|
14
|
+
for (let key of __getOwnPropNames(from))
|
|
15
|
+
if (!__hasOwnProp.call(to, key) && key !== except)
|
|
16
|
+
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
|
|
17
|
+
}
|
|
18
|
+
return to;
|
|
19
|
+
};
|
|
20
|
+
var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(
|
|
21
|
+
// If the importer is in node compatibility mode or this is not an ESM
|
|
22
|
+
// file that has been converted to a CommonJS file using a Babel-
|
|
23
|
+
// compatible transform (i.e. "__esModule" has not been set), then set
|
|
24
|
+
// "default" to the CommonJS "module.exports" for node compatibility.
|
|
25
|
+
isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target,
|
|
26
|
+
mod
|
|
27
|
+
));
|
|
28
|
+
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
|
|
29
|
+
var videoRecorder_exports = {};
|
|
30
|
+
__export(videoRecorder_exports, {
|
|
31
|
+
VideoRecorder: () => VideoRecorder,
|
|
32
|
+
startAutomaticVideoRecording: () => startAutomaticVideoRecording
|
|
33
|
+
});
|
|
34
|
+
module.exports = __toCommonJS(videoRecorder_exports);
|
|
35
|
+
var import_path = __toESM(require("path"));
|
|
36
|
+
var import_utils = require("../utils");
|
|
37
|
+
var import_processLauncher = require("./utils/processLauncher");
|
|
38
|
+
var import_utilsBundle = require("../utilsBundle");
|
|
39
|
+
var import_artifact = require("./artifact");
|
|
40
|
+
var import__ = require(".");
|
|
41
|
+
const fps = 25;
|
|
42
|
+
class VideoRecorder {
|
|
43
|
+
constructor(screencast) {
|
|
44
|
+
this._screencast = screencast;
|
|
45
|
+
}
|
|
46
|
+
start(options) {
|
|
47
|
+
(0, import_utils.assert)(!this._artifact);
|
|
48
|
+
const ffmpegPath = import__.registry.findExecutable("ffmpeg").executablePathOrDie(this._screencast.page.browserContext._browser.sdkLanguage());
|
|
49
|
+
const outputFile = options.fileName ?? import_path.default.join(this._screencast.page.browserContext._browser.options.artifactsDir, (0, import_utils.createGuid)() + ".webm");
|
|
50
|
+
this._client = {
|
|
51
|
+
onFrame: (frame) => this._videoRecorder.writeFrame(frame.buffer, frame.frameSwapWallTime / 1e3),
|
|
52
|
+
gracefulClose: () => this.stop(),
|
|
53
|
+
dispose: () => this.stop().catch((e) => import_utils.debugLogger.log("error", `Failed to stop video recorder: ${String(e)}`)),
|
|
54
|
+
size: options.size
|
|
55
|
+
};
|
|
56
|
+
const { size } = this._screencast.addClient(this._client);
|
|
57
|
+
const videoSize = options.size ?? size;
|
|
58
|
+
this._videoRecorder = new FfmpegVideoRecorder(ffmpegPath, videoSize, outputFile);
|
|
59
|
+
this._artifact = new import_artifact.Artifact(this._screencast.page.browserContext, outputFile);
|
|
60
|
+
return this._artifact;
|
|
61
|
+
}
|
|
62
|
+
async stop() {
|
|
63
|
+
if (!this._artifact)
|
|
64
|
+
return;
|
|
65
|
+
const artifact = this._artifact;
|
|
66
|
+
this._artifact = void 0;
|
|
67
|
+
const client = this._client;
|
|
68
|
+
this._client = void 0;
|
|
69
|
+
const videoRecorder = this._videoRecorder;
|
|
70
|
+
this._videoRecorder = void 0;
|
|
71
|
+
this._screencast.removeClient(client);
|
|
72
|
+
await videoRecorder.stop();
|
|
73
|
+
await artifact.reportFinished();
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
function startAutomaticVideoRecording(page) {
|
|
77
|
+
const recordVideo = page.browserContext._options.recordVideo;
|
|
78
|
+
if (!recordVideo)
|
|
79
|
+
return;
|
|
80
|
+
const recorder = new VideoRecorder(page.screencast);
|
|
81
|
+
if (page.browserContext._options.recordVideo?.showActions)
|
|
82
|
+
page.screencast.showActions(page.browserContext._options.recordVideo?.showActions);
|
|
83
|
+
const dir = recordVideo.dir ?? page.browserContext._browser.options.artifactsDir;
|
|
84
|
+
const artifact = recorder.start({ size: recordVideo.size, fileName: import_path.default.join(dir, page.guid + ".webm") });
|
|
85
|
+
page.video = artifact;
|
|
86
|
+
}
|
|
87
|
+
class FfmpegVideoRecorder {
|
|
88
|
+
constructor(ffmpegPath, size, outputFile) {
|
|
89
|
+
this._process = null;
|
|
90
|
+
this._gracefullyClose = null;
|
|
91
|
+
this._lastWritePromise = Promise.resolve();
|
|
92
|
+
this._firstFrameTimestamp = 0;
|
|
93
|
+
this._lastFrame = null;
|
|
94
|
+
this._lastWriteNodeTime = 0;
|
|
95
|
+
this._frameQueue = [];
|
|
96
|
+
this._isStopped = false;
|
|
97
|
+
if (!outputFile.endsWith(".webm"))
|
|
98
|
+
throw new Error("File must have .webm extension");
|
|
99
|
+
this._outputFile = outputFile;
|
|
100
|
+
this._ffmpegPath = ffmpegPath;
|
|
101
|
+
this._size = size;
|
|
102
|
+
this._launchPromise = this._launch().catch((e) => e);
|
|
103
|
+
}
|
|
104
|
+
async _launch() {
|
|
105
|
+
await (0, import_utils.mkdirIfNeeded)(this._outputFile);
|
|
106
|
+
const w = this._size.width;
|
|
107
|
+
const h = this._size.height;
|
|
108
|
+
const args = `-loglevel error -f image2pipe -avioflags direct -fpsprobesize 0 -probesize 32 -analyzeduration 0 -c:v mjpeg -i pipe:0 -y -an -r ${fps} -c:v vp8 -qmin 0 -qmax 50 -crf 8 -deadline realtime -speed 8 -b:v 1M -threads 1 -vf pad=${w}:${h}:0:0:gray,crop=${w}:${h}:0:0`.split(" ");
|
|
109
|
+
args.push(this._outputFile);
|
|
110
|
+
const { launchedProcess, gracefullyClose } = await (0, import_processLauncher.launchProcess)({
|
|
111
|
+
command: this._ffmpegPath,
|
|
112
|
+
args,
|
|
113
|
+
stdio: "stdin",
|
|
114
|
+
log: (message) => import_utils.debugLogger.log("browser", message),
|
|
115
|
+
tempDirectories: [],
|
|
116
|
+
attemptToGracefullyClose: async () => {
|
|
117
|
+
import_utils.debugLogger.log("browser", "Closing stdin...");
|
|
118
|
+
launchedProcess.stdin.end();
|
|
119
|
+
},
|
|
120
|
+
onExit: (exitCode, signal) => {
|
|
121
|
+
import_utils.debugLogger.log("browser", `ffmpeg onkill exitCode=${exitCode} signal=${signal}`);
|
|
122
|
+
}
|
|
123
|
+
});
|
|
124
|
+
launchedProcess.stdin.on("finish", () => {
|
|
125
|
+
import_utils.debugLogger.log("browser", "ffmpeg finished input.");
|
|
126
|
+
});
|
|
127
|
+
launchedProcess.stdin.on("error", () => {
|
|
128
|
+
import_utils.debugLogger.log("browser", "ffmpeg error.");
|
|
129
|
+
});
|
|
130
|
+
this._process = launchedProcess;
|
|
131
|
+
this._gracefullyClose = gracefullyClose;
|
|
132
|
+
}
|
|
133
|
+
writeFrame(frame, timestamp) {
|
|
134
|
+
this._launchPromise.then((error) => {
|
|
135
|
+
if (error)
|
|
136
|
+
return;
|
|
137
|
+
this._writeFrame(frame, timestamp);
|
|
138
|
+
});
|
|
139
|
+
}
|
|
140
|
+
_writeFrame(frame, timestamp) {
|
|
141
|
+
(0, import_utils.assert)(this._process);
|
|
142
|
+
if (this._isStopped)
|
|
143
|
+
return;
|
|
144
|
+
if (!this._firstFrameTimestamp)
|
|
145
|
+
this._firstFrameTimestamp = timestamp;
|
|
146
|
+
const frameNumber = Math.floor((timestamp - this._firstFrameTimestamp) * fps);
|
|
147
|
+
if (this._lastFrame) {
|
|
148
|
+
const repeatCount = frameNumber - this._lastFrame.frameNumber;
|
|
149
|
+
for (let i = 0; i < repeatCount; ++i)
|
|
150
|
+
this._frameQueue.push(this._lastFrame.buffer);
|
|
151
|
+
this._lastWritePromise = this._lastWritePromise.then(() => this._sendFrames());
|
|
152
|
+
}
|
|
153
|
+
this._lastFrame = { buffer: frame, timestamp, frameNumber };
|
|
154
|
+
this._lastWriteNodeTime = (0, import_utils.monotonicTime)();
|
|
155
|
+
}
|
|
156
|
+
async _sendFrames() {
|
|
157
|
+
while (this._frameQueue.length)
|
|
158
|
+
await this._sendFrame(this._frameQueue.shift());
|
|
159
|
+
}
|
|
160
|
+
async _sendFrame(frame) {
|
|
161
|
+
return new Promise((f) => this._process.stdin.write(frame, f)).then((error) => {
|
|
162
|
+
if (error)
|
|
163
|
+
import_utils.debugLogger.log("browser", `ffmpeg failed to write: ${String(error)}`);
|
|
164
|
+
});
|
|
165
|
+
}
|
|
166
|
+
async stop() {
|
|
167
|
+
const error = await this._launchPromise;
|
|
168
|
+
if (error)
|
|
169
|
+
throw error;
|
|
170
|
+
if (this._isStopped)
|
|
171
|
+
return;
|
|
172
|
+
if (!this._lastFrame) {
|
|
173
|
+
this._writeFrame(createWhiteImage(this._size.width, this._size.height), (0, import_utils.monotonicTime)());
|
|
174
|
+
}
|
|
175
|
+
const addTime = Math.max(((0, import_utils.monotonicTime)() - this._lastWriteNodeTime) / 1e3, 1);
|
|
176
|
+
this._writeFrame(Buffer.from([]), this._lastFrame.timestamp + addTime);
|
|
177
|
+
this._isStopped = true;
|
|
178
|
+
try {
|
|
179
|
+
await this._lastWritePromise;
|
|
180
|
+
await this._gracefullyClose();
|
|
181
|
+
} catch (e) {
|
|
182
|
+
import_utils.debugLogger.log("error", `ffmpeg failed to stop: ${String(e)}`);
|
|
183
|
+
}
|
|
184
|
+
}
|
|
185
|
+
}
|
|
186
|
+
function createWhiteImage(width, height) {
|
|
187
|
+
const data = Buffer.alloc(width * height * 4, 255);
|
|
188
|
+
return import_utilsBundle.jpegjs.encode({ data, width, height }, 80).data;
|
|
189
|
+
}
|
|
190
|
+
// Annotate the CommonJS export names for ESM import in node:
|
|
191
|
+
0 && (module.exports = {
|
|
192
|
+
VideoRecorder,
|
|
193
|
+
startAutomaticVideoRecording
|
|
194
|
+
});
|