rebrowser-playwright-core 1.47.100 → 1.49.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/README.md +1 -1
- package/ThirdPartyNotices.txt +70 -384
- package/bin/reinstall_chrome_beta_linux.sh +10 -8
- package/bin/reinstall_chrome_stable_linux.sh +10 -8
- package/bin/reinstall_msedge_beta_linux.sh +11 -9
- package/bin/reinstall_msedge_dev_linux.sh +11 -9
- package/bin/reinstall_msedge_stable_linux.sh +11 -9
- package/browsers.json +24 -12
- package/lib/cli/program.js +49 -19
- package/lib/client/api.js +6 -0
- package/lib/client/browserContext.js +20 -2
- package/lib/client/channelOwner.js +6 -2
- package/lib/client/connection.js +3 -0
- package/lib/client/fetch.js +16 -21
- package/lib/client/jsHandle.js +0 -8
- package/lib/client/localUtils.js +1 -0
- package/lib/client/locator.js +7 -0
- package/lib/client/network.js +180 -17
- package/lib/client/page.js +25 -2
- package/lib/client/playwright.js +6 -3
- package/lib/client/tracing.js +29 -20
- package/lib/client/waiter.js +15 -11
- package/lib/generated/consoleApiSource.js +1 -1
- package/lib/generated/injectedScriptSource.js +1 -1
- package/lib/generated/pollingRecorderSource.js +7 -0
- package/lib/generated/webSocketMockSource.js +7 -0
- package/lib/protocol/validator.js +103 -17
- package/lib/server/ariaSnapshot.js +33 -0
- package/lib/server/bidi/bidiBrowser.js +23 -8
- package/lib/server/bidi/bidiChromium.js +124 -0
- package/lib/server/bidi/bidiConnection.js +1 -3
- package/lib/server/bidi/bidiExecutionContext.js +0 -6
- package/lib/server/bidi/bidiFirefox.js +15 -21
- package/lib/server/bidi/bidiInput.js +16 -32
- package/lib/server/bidi/bidiNetworkManager.js +39 -5
- package/lib/server/bidi/bidiOverCdp.js +103 -0
- package/lib/server/bidi/bidiPage.js +98 -25
- package/lib/server/bidi/bidiPdf.js +140 -0
- package/lib/server/bidi/third_party/firefoxPrefs.js +221 -0
- package/lib/server/browser.js +13 -2
- package/lib/server/browserContext.js +6 -23
- package/lib/server/browserType.js +43 -12
- package/lib/server/chromium/chromium.js +9 -17
- package/lib/server/chromium/chromiumSwitches.js +3 -1
- package/lib/server/chromium/crBrowser.js +4 -3
- package/lib/server/chromium/crConnection.js +153 -0
- package/lib/server/chromium/crExecutionContext.js +0 -20
- package/lib/server/chromium/crPage.js +13 -7
- package/lib/server/chromium/videoRecorder.js +1 -1
- package/lib/server/codegen/csharp.js +12 -17
- package/lib/server/codegen/java.js +5 -8
- package/lib/server/codegen/javascript.js +29 -7
- package/lib/server/codegen/language.js +14 -2
- package/lib/server/codegen/python.js +13 -17
- package/lib/server/cookieStore.js +73 -0
- package/lib/server/debugController.js +10 -7
- package/lib/server/deviceDescriptorsSource.json +127 -127
- package/lib/server/dialog.js +1 -0
- package/lib/server/dispatchers/androidDispatcher.js +13 -2
- package/lib/server/dispatchers/browserContextDispatcher.js +19 -2
- package/lib/server/dispatchers/debugControllerDispatcher.js +1 -1
- package/lib/server/dispatchers/dispatcher.js +1 -1
- package/lib/server/dispatchers/frameDispatcher.js +8 -1
- package/lib/server/dispatchers/jsHandleDispatcher.js +0 -5
- package/lib/server/dispatchers/pageDispatcher.js +9 -0
- package/lib/server/dispatchers/playwrightDispatcher.js +2 -1
- package/lib/server/dispatchers/tracingDispatcher.js +10 -0
- package/lib/server/dispatchers/webSocketRouteDispatcher.js +189 -0
- package/lib/server/dom.js +79 -36
- package/lib/server/download.js +9 -2
- package/lib/server/fetch.js +105 -96
- package/lib/server/firefox/ffBrowser.js +6 -4
- package/lib/server/firefox/ffExecutionContext.js +0 -15
- package/lib/server/firefox/ffNetworkManager.js +1 -1
- package/lib/server/firefox/ffPage.js +6 -3
- package/lib/server/firefox/firefox.js +2 -13
- package/lib/server/frameSelectors.js +1 -1
- package/lib/server/frames.js +68 -67
- package/lib/server/har/harTracer.js +11 -0
- package/lib/server/index.js +0 -6
- package/lib/server/input.js +0 -1
- package/lib/server/javascript.js +0 -13
- package/lib/server/page.js +53 -9
- package/lib/server/playwright.js +5 -2
- package/lib/server/progress.js +0 -3
- package/lib/server/recorder/contextRecorder.js +33 -50
- package/lib/server/recorder/recorderApp.js +22 -25
- package/lib/server/recorder/recorderCollection.js +68 -79
- package/lib/server/recorder/recorderInTraceViewer.js +144 -0
- package/lib/server/recorder/recorderRunner.js +75 -97
- package/lib/server/recorder/recorderUtils.js +48 -6
- package/lib/server/recorder.js +87 -47
- package/lib/server/registry/index.js +154 -26
- package/lib/server/registry/nativeDeps.js +6 -3
- package/lib/server/socksClientCertificatesInterceptor.js +15 -3
- package/lib/server/trace/recorder/snapshotter.js +2 -12
- package/lib/server/trace/recorder/snapshotterInjected.js +21 -3
- package/lib/server/trace/recorder/tracing.js +127 -17
- package/lib/server/trace/test/inMemorySnapshotter.js +4 -4
- package/lib/server/trace/viewer/traceViewer.js +4 -6
- package/lib/server/webkit/webkit.js +1 -1
- package/lib/server/webkit/wkBrowser.js +8 -7
- package/lib/server/webkit/wkExecutionContext.js +0 -16
- package/lib/server/webkit/wkPage.js +11 -4
- package/lib/utils/comparators.js +12 -30
- package/lib/utils/crypto.js +1 -4
- package/lib/utils/happy-eyeballs.js +13 -0
- package/lib/utils/hostPlatform.js +8 -2
- package/lib/utils/httpServer.js +1 -0
- package/lib/utils/isomorphic/ariaSnapshot.js +267 -0
- package/lib/utils/isomorphic/locatorGenerators.js +30 -21
- package/lib/utils/isomorphic/locatorParser.js +2 -2
- package/lib/utils/isomorphic/recorderUtils.js +227 -0
- package/lib/utils/isomorphic/stringUtils.js +30 -0
- package/lib/utils/isomorphic/urlMatch.js +5 -1
- package/lib/utils/network.js +1 -1
- package/lib/utils/sequence.js +64 -0
- package/lib/utils/stackTrace.js +16 -3
- package/lib/utils/zones.js +32 -23
- package/lib/utilsBundle.js +4 -5
- package/lib/utilsBundleImpl/index.js +190 -33
- package/lib/vite/htmlReport/index.html +19 -16
- package/lib/vite/recorder/assets/codeMirrorModule-AFvV6hAs.js +24 -0
- package/lib/vite/recorder/assets/index-_cTWgVuJ.js +184 -0
- package/lib/vite/recorder/assets/{index-B-MT5gKo.css → index-iA1aAGZg.css} +1 -1
- package/lib/vite/recorder/index.html +2 -2
- package/lib/vite/traceViewer/assets/codeMirrorModule-KatbITLF.js +24 -0
- package/lib/vite/traceViewer/assets/inspectorTab-DdpLd2bb.js +68 -0
- package/lib/vite/traceViewer/assets/workbench-CdYbzWFD.js +9 -0
- package/lib/vite/traceViewer/embedded.6m3UZh7r.js +2 -0
- package/lib/vite/traceViewer/embedded.html +5 -3
- package/lib/vite/traceViewer/index.WUV-8boJ.js +2 -0
- package/lib/vite/traceViewer/index.html +24 -5
- package/lib/vite/traceViewer/inspectorTab.DEOUW62d.css +1 -0
- package/lib/vite/traceViewer/recorder.B_SY1GJM.css +0 -0
- package/lib/vite/traceViewer/recorder.OT2tVHgn.js +2 -0
- package/lib/vite/traceViewer/recorder.html +17 -0
- package/lib/vite/traceViewer/sw.bundle.js +3 -3
- package/lib/vite/traceViewer/uiMode.BZBTyvGn.js +5 -0
- package/lib/vite/traceViewer/uiMode.html +6 -4
- package/lib/vite/traceViewer/{uiMode.D3cNFP6u.css → uiMode.voC1ZiOQ.css} +1 -1
- package/lib/vite/traceViewer/workbench.C-zR9ysA.css +1 -0
- package/package.json +1 -1
- package/types/protocol.d.ts +135 -16
- package/types/types.d.ts +2149 -965
- package/lib/generated/recorderSource.js +0 -7
- package/lib/third_party/diff_match_patch.js +0 -2222
- package/lib/vite/recorder/assets/codeMirrorModule-aW6Vz3gf.js +0 -24
- package/lib/vite/recorder/assets/index-DCpXOVIh.js +0 -47
- package/lib/vite/traceViewer/assets/codeMirrorModule-eFKCUIu2.js +0 -24
- package/lib/vite/traceViewer/assets/workbench-D0cirtfA.js +0 -72
- package/lib/vite/traceViewer/embedded.BkuvsLJp.js +0 -2
- package/lib/vite/traceViewer/index.C968kAiC.js +0 -2
- package/lib/vite/traceViewer/uiMode.BkFnr4V0.js +0 -10
- package/lib/vite/traceViewer/workbench.DjbIuxix.css +0 -1
- /package/lib/server/recorder/{recorderActions.js → recorderFrontend.js} +0 -0
|
@@ -5,7 +5,6 @@ Object.defineProperty(exports, "__esModule", {
|
|
|
5
5
|
});
|
|
6
6
|
exports.BrowserContext = void 0;
|
|
7
7
|
exports.assertBrowserContextIsNotOwned = assertBrowserContextIsNotOwned;
|
|
8
|
-
exports.createClientCertificatesProxyIfNeeded = createClientCertificatesProxyIfNeeded;
|
|
9
8
|
exports.normalizeProxySettings = normalizeProxySettings;
|
|
10
9
|
exports.validateBrowserContextOptions = validateBrowserContextOptions;
|
|
11
10
|
exports.verifyClientCertificates = verifyClientCertificates;
|
|
@@ -26,7 +25,6 @@ var _recorder = require("./recorder");
|
|
|
26
25
|
var consoleApiSource = _interopRequireWildcard(require("../generated/consoleApiSource"));
|
|
27
26
|
var _fetch = require("./fetch");
|
|
28
27
|
var _clock = require("./clock");
|
|
29
|
-
var _socksClientCertificatesInterceptor = require("./socksClientCertificatesInterceptor");
|
|
30
28
|
var _recorderApp = require("./recorder/recorderApp");
|
|
31
29
|
function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
|
|
32
30
|
function _getRequireWildcardCache(e) { if ("function" != typeof WeakMap) return null; var r = new WeakMap(), t = new WeakMap(); return (_getRequireWildcardCache = function (e) { return e ? t : r; })(e); }
|
|
@@ -104,14 +102,14 @@ class BrowserContext extends _instrumentation.SdkObject {
|
|
|
104
102
|
this._debugger = new _debugger.Debugger(this);
|
|
105
103
|
|
|
106
104
|
// When PWDEBUG=1, show inspector for each context.
|
|
107
|
-
if ((0, _utils.debugMode)() === 'inspector') await _recorder.Recorder.show(this, _recorderApp.RecorderApp.factory(this), {
|
|
105
|
+
if ((0, _utils.debugMode)() === 'inspector') await _recorder.Recorder.show('actions', this, _recorderApp.RecorderApp.factory(this), {
|
|
108
106
|
pauseOnNextStatement: true
|
|
109
107
|
});
|
|
110
108
|
|
|
111
109
|
// When paused, show inspector.
|
|
112
|
-
if (this._debugger.isPaused()) _recorder.Recorder.
|
|
110
|
+
if (this._debugger.isPaused()) _recorder.Recorder.showInspectorNoReply(this, _recorderApp.RecorderApp.factory(this));
|
|
113
111
|
this._debugger.on(_debugger.Debugger.Events.PausedStateChanged, () => {
|
|
114
|
-
if (this._debugger.isPaused()) _recorder.Recorder.
|
|
112
|
+
if (this._debugger.isPaused()) _recorder.Recorder.showInspectorNoReply(this, _recorderApp.RecorderApp.factory(this));
|
|
115
113
|
});
|
|
116
114
|
if ((0, _utils.debugMode)() === 'console') await this.extendInjectedScript(consoleApiSource.source);
|
|
117
115
|
if (this._options.serviceWorkers === 'block') await this.addInitScript(`\nif (navigator.serviceWorker) navigator.serviceWorker.register = async () => { console.warn('Service Worker registration blocked by Playwright'); };\n`);
|
|
@@ -425,8 +423,7 @@ class BrowserContext extends _instrumentation.SdkObject {
|
|
|
425
423
|
const page = await this.newPage(internalMetadata);
|
|
426
424
|
await page._setServerRequestInterceptor(handler => {
|
|
427
425
|
handler.fulfill({
|
|
428
|
-
body: '<html></html>'
|
|
429
|
-
requestUrl: handler.request().url()
|
|
426
|
+
body: '<html></html>'
|
|
430
427
|
}).catch(() => {});
|
|
431
428
|
return true;
|
|
432
429
|
});
|
|
@@ -464,8 +461,7 @@ class BrowserContext extends _instrumentation.SdkObject {
|
|
|
464
461
|
}));
|
|
465
462
|
await page._setServerRequestInterceptor(handler => {
|
|
466
463
|
handler.fulfill({
|
|
467
|
-
body: '<html></html>'
|
|
468
|
-
requestUrl: handler.request().url()
|
|
464
|
+
body: '<html></html>'
|
|
469
465
|
}).catch(() => {});
|
|
470
466
|
return true;
|
|
471
467
|
});
|
|
@@ -495,8 +491,7 @@ class BrowserContext extends _instrumentation.SdkObject {
|
|
|
495
491
|
const page = await this.newPage(internalMetadata);
|
|
496
492
|
await page._setServerRequestInterceptor(handler => {
|
|
497
493
|
handler.fulfill({
|
|
498
|
-
body: '<html></html>'
|
|
499
|
-
requestUrl: handler.request().url()
|
|
494
|
+
body: '<html></html>'
|
|
500
495
|
}).catch(() => {});
|
|
501
496
|
return true;
|
|
502
497
|
});
|
|
@@ -574,18 +569,6 @@ function assertBrowserContextIsNotOwned(context) {
|
|
|
574
569
|
if (page._ownedContext) throw new Error('Please use browser.newContext() for multi-page scripts that share the context.');
|
|
575
570
|
}
|
|
576
571
|
}
|
|
577
|
-
async function createClientCertificatesProxyIfNeeded(options, browserOptions) {
|
|
578
|
-
var _options$clientCertif, _options$proxy, _options$proxy2, _browserOptions$proxy, _browserOptions$proxy2;
|
|
579
|
-
if (!((_options$clientCertif = options.clientCertificates) !== null && _options$clientCertif !== void 0 && _options$clientCertif.length)) return;
|
|
580
|
-
if ((_options$proxy = options.proxy) !== null && _options$proxy !== void 0 && _options$proxy.server && ((_options$proxy2 = options.proxy) === null || _options$proxy2 === void 0 ? void 0 : _options$proxy2.server) !== 'per-context' || browserOptions !== null && browserOptions !== void 0 && (_browserOptions$proxy = browserOptions.proxy) !== null && _browserOptions$proxy !== void 0 && _browserOptions$proxy.server && (browserOptions === null || browserOptions === void 0 || (_browserOptions$proxy2 = browserOptions.proxy) === null || _browserOptions$proxy2 === void 0 ? void 0 : _browserOptions$proxy2.server) !== 'http://per-context') throw new Error('Cannot specify both proxy and clientCertificates');
|
|
581
|
-
verifyClientCertificates(options.clientCertificates);
|
|
582
|
-
const clientCertificatesProxy = new _socksClientCertificatesInterceptor.ClientCertificatesProxy(options);
|
|
583
|
-
options.proxy = {
|
|
584
|
-
server: await clientCertificatesProxy.listen()
|
|
585
|
-
};
|
|
586
|
-
options.ignoreHTTPSErrors = true;
|
|
587
|
-
return clientCertificatesProxy;
|
|
588
|
-
}
|
|
589
572
|
function validateBrowserContextOptions(options, browserOptions) {
|
|
590
573
|
if (options.noDefaultViewport && options.deviceScaleFactor !== undefined) throw new Error(`"deviceScaleFactor" option is not supported with null "viewport"`);
|
|
591
574
|
if (options.noDefaultViewport && !!options.isMobile) throw new Error(`"isMobile" option is not supported with null "viewport"`);
|
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
Object.defineProperty(exports, "__esModule", {
|
|
4
4
|
value: true
|
|
5
5
|
});
|
|
6
|
-
exports.kNoXServerRunningError = exports.BrowserType = void 0;
|
|
6
|
+
exports.kNoXServerRunningError = exports.BrowserType = exports.BrowserReadyState = void 0;
|
|
7
7
|
var _fs = _interopRequireDefault(require("fs"));
|
|
8
8
|
var os = _interopRequireWildcard(require("os"));
|
|
9
9
|
var _path = _interopRequireDefault(require("path"));
|
|
@@ -20,6 +20,7 @@ var _helper = require("./helper");
|
|
|
20
20
|
var _debugLogger = require("../utils/debugLogger");
|
|
21
21
|
var _instrumentation = require("./instrumentation");
|
|
22
22
|
var _protocolError = require("./protocolError");
|
|
23
|
+
var _socksClientCertificatesInterceptor = require("./socksClientCertificatesInterceptor");
|
|
23
24
|
function _getRequireWildcardCache(e) { if ("function" != typeof WeakMap) return null; var r = new WeakMap(), t = new WeakMap(); return (_getRequireWildcardCache = function (e) { return e ? t : r; })(e); }
|
|
24
25
|
function _interopRequireWildcard(e, r) { if (!r && e && e.__esModule) return e; if (null === e || "object" != typeof e && "function" != typeof e) return { default: e }; var t = _getRequireWildcardCache(r); if (t && t.has(e)) return t.get(e); var n = { __proto__: null }, a = Object.defineProperty && Object.getOwnPropertyDescriptor; for (var u in e) if ("default" !== u && Object.prototype.hasOwnProperty.call(e, u)) { var i = a ? Object.getOwnPropertyDescriptor(e, u) : null; i && (i.get || i.set) ? Object.defineProperty(n, u, i) : n[u] = e[u]; } return n.default = e, t && t.set(e, n), n; }
|
|
25
26
|
function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
|
|
@@ -40,6 +41,22 @@ function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { de
|
|
|
40
41
|
*/
|
|
41
42
|
|
|
42
43
|
const kNoXServerRunningError = exports.kNoXServerRunningError = 'Looks like you launched a headed browser without having a XServer running.\n' + 'Set either \'headless: true\' or use \'xvfb-run <your-playwright-app>\' before running Playwright.\n\n<3 Playwright Team';
|
|
44
|
+
class BrowserReadyState {
|
|
45
|
+
constructor() {
|
|
46
|
+
this._wsEndpoint = new _utils.ManualPromise();
|
|
47
|
+
}
|
|
48
|
+
onBrowserExit() {
|
|
49
|
+
// Unblock launch when browser prematurely exits.
|
|
50
|
+
this._wsEndpoint.resolve(undefined);
|
|
51
|
+
}
|
|
52
|
+
async waitUntilReady() {
|
|
53
|
+
const wsEndpoint = await this._wsEndpoint;
|
|
54
|
+
return {
|
|
55
|
+
wsEndpoint
|
|
56
|
+
};
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
exports.BrowserReadyState = BrowserReadyState;
|
|
43
60
|
class BrowserType extends _instrumentation.SdkObject {
|
|
44
61
|
constructor(parent, browserName) {
|
|
45
62
|
super(parent, 'browser-type');
|
|
@@ -69,24 +86,33 @@ class BrowserType extends _instrumentation.SdkObject {
|
|
|
69
86
|
return browser;
|
|
70
87
|
}
|
|
71
88
|
async launchPersistentContext(metadata, userDataDir, options) {
|
|
72
|
-
|
|
73
|
-
if (this._useBidi)
|
|
89
|
+
const launchOptions = this._validateLaunchOptions(options);
|
|
90
|
+
if (this._useBidi) launchOptions.useWebSocket = true;
|
|
74
91
|
const controller = new _progress.ProgressController(metadata, this);
|
|
75
|
-
const persistent = {
|
|
76
|
-
...options
|
|
77
|
-
};
|
|
78
92
|
controller.setLogName('browser');
|
|
79
93
|
const browser = await controller.run(async progress => {
|
|
94
|
+
var _options$clientCertif;
|
|
80
95
|
// Note: Any initial TLS requests will fail since we rely on the Page/Frames initialize which sets ignoreHTTPSErrors.
|
|
81
|
-
|
|
82
|
-
if (
|
|
83
|
-
|
|
84
|
-
|
|
96
|
+
let clientCertificatesProxy;
|
|
97
|
+
if ((_options$clientCertif = options.clientCertificates) !== null && _options$clientCertif !== void 0 && _options$clientCertif.length) {
|
|
98
|
+
var _clientCertificatesPr;
|
|
99
|
+
clientCertificatesProxy = new _socksClientCertificatesInterceptor.ClientCertificatesProxy(options);
|
|
100
|
+
launchOptions.proxyOverride = await ((_clientCertificatesPr = clientCertificatesProxy) === null || _clientCertificatesPr === void 0 ? void 0 : _clientCertificatesPr.listen());
|
|
101
|
+
options = {
|
|
102
|
+
...options
|
|
103
|
+
};
|
|
104
|
+
options.internalIgnoreHTTPSErrors = true;
|
|
105
|
+
}
|
|
106
|
+
progress.cleanupWhenAborted(() => {
|
|
107
|
+
var _clientCertificatesPr2;
|
|
108
|
+
return (_clientCertificatesPr2 = clientCertificatesProxy) === null || _clientCertificatesPr2 === void 0 ? void 0 : _clientCertificatesPr2.close();
|
|
109
|
+
});
|
|
110
|
+
const browser = await this._innerLaunchWithRetries(progress, launchOptions, options, _helper.helper.debugProtocolLogger(), userDataDir).catch(e => {
|
|
85
111
|
throw this._rewriteStartupLog(e);
|
|
86
112
|
});
|
|
87
113
|
browser._defaultContext._clientCertificatesProxy = clientCertificatesProxy;
|
|
88
114
|
return browser;
|
|
89
|
-
}, _timeoutSettings.TimeoutSettings.launchTimeout(
|
|
115
|
+
}, _timeoutSettings.TimeoutSettings.launchTimeout(launchOptions));
|
|
90
116
|
return browser._defaultContext;
|
|
91
117
|
}
|
|
92
118
|
async _innerLaunchWithRetries(progress, options, persistent, protocolLogger, userDataDir) {
|
|
@@ -164,6 +190,7 @@ class BrowserType extends _instrumentation.SdkObject {
|
|
|
164
190
|
userDataDir = await _fs.default.promises.mkdtemp(_path.default.join(os.tmpdir(), `playwright_${this._name}dev_profile-`));
|
|
165
191
|
tempDirectories.push(userDataDir);
|
|
166
192
|
}
|
|
193
|
+
await this.prepareUserDataDir(options, userDataDir);
|
|
167
194
|
const browserArguments = [];
|
|
168
195
|
if (ignoreAllDefaultArgs) browserArguments.push(...args);else if (ignoreDefaultArgs) browserArguments.push(...this.defaultArgs(options, isPersistent, userDataDir).filter(arg => ignoreDefaultArgs.indexOf(arg) === -1));else browserArguments.push(...this.defaultArgs(options, isPersistent, userDataDir));
|
|
169
196
|
let executable;
|
|
@@ -171,7 +198,7 @@ class BrowserType extends _instrumentation.SdkObject {
|
|
|
171
198
|
if (!(await (0, _fileUtils.existsAsync)(executablePath))) throw new Error(`Failed to launch ${this._name} because executable doesn't exist at ${executablePath}`);
|
|
172
199
|
executable = executablePath;
|
|
173
200
|
} else {
|
|
174
|
-
const registryExecutable = _registry.registry.findExecutable(
|
|
201
|
+
const registryExecutable = _registry.registry.findExecutable(this.getExecutableName(options));
|
|
175
202
|
if (!registryExecutable || registryExecutable.browserName !== this._name) throw new Error(`Unsupported ${this._name} channel "${options.channel}"`);
|
|
176
203
|
executable = registryExecutable.executablePathOrDie(this.attribution.playwright.options.sdkLanguage);
|
|
177
204
|
await _registry.registry.validateHostRequirementsForExecutablesIfNeeded([registryExecutable], this.attribution.playwright.options.sdkLanguage);
|
|
@@ -298,6 +325,10 @@ class BrowserType extends _instrumentation.SdkObject {
|
|
|
298
325
|
readyState(options) {
|
|
299
326
|
return undefined;
|
|
300
327
|
}
|
|
328
|
+
async prepareUserDataDir(options, userDataDir) {}
|
|
329
|
+
getExecutableName(options) {
|
|
330
|
+
return options.channel || this._name;
|
|
331
|
+
}
|
|
301
332
|
}
|
|
302
333
|
exports.BrowserType = BrowserType;
|
|
303
334
|
function copyTestHooks(from, to) {
|
|
@@ -256,8 +256,7 @@ class Chromium extends _browserType.BrowserType {
|
|
|
256
256
|
}
|
|
257
257
|
_innerDefaultArgs(options) {
|
|
258
258
|
const {
|
|
259
|
-
args = []
|
|
260
|
-
proxy
|
|
259
|
+
args = []
|
|
261
260
|
} = options;
|
|
262
261
|
const userDataDirArg = args.find(arg => arg.startsWith('--user-data-dir'));
|
|
263
262
|
if (userDataDirArg) throw this._createUserDataDirArgMisuseError('--user-data-dir');
|
|
@@ -268,14 +267,15 @@ class Chromium extends _browserType.BrowserType {
|
|
|
268
267
|
// See https://github.com/microsoft/playwright/issues/7362
|
|
269
268
|
chromeArguments.push('--enable-use-zoom-for-dsf=false');
|
|
270
269
|
// See https://bugs.chromium.org/p/chromium/issues/detail?id=1407025.
|
|
271
|
-
if (options.headless) chromeArguments.push('--use-angle');
|
|
270
|
+
if (options.headless && (!options.channel || options.channel === 'chromium-headless-shell')) chromeArguments.push('--use-angle');
|
|
272
271
|
}
|
|
273
272
|
if (options.devtools) chromeArguments.push('--auto-open-devtools-for-tabs');
|
|
274
273
|
if (options.headless) {
|
|
275
|
-
|
|
274
|
+
chromeArguments.push('--headless');
|
|
276
275
|
chromeArguments.push('--hide-scrollbars', '--mute-audio', '--blink-settings=primaryHoverType=2,availableHoverTypes=2,primaryPointerType=4,availablePointerTypes=4');
|
|
277
276
|
}
|
|
278
277
|
if (options.chromiumSandbox !== true) chromeArguments.push('--no-sandbox');
|
|
278
|
+
const proxy = options.proxyOverride || options.proxy;
|
|
279
279
|
if (proxy) {
|
|
280
280
|
const proxyURL = new URL(proxy.server);
|
|
281
281
|
const isSocks = proxyURL.protocol === 'socks5:';
|
|
@@ -300,25 +300,17 @@ class Chromium extends _browserType.BrowserType {
|
|
|
300
300
|
if (options.useWebSocket || (_options$args = options.args) !== null && _options$args !== void 0 && _options$args.some(a => a.startsWith('--remote-debugging-port'))) return new ChromiumReadyState();
|
|
301
301
|
return undefined;
|
|
302
302
|
}
|
|
303
|
+
getExecutableName(options) {
|
|
304
|
+
if (options.channel) return options.channel;
|
|
305
|
+
return options.headless ? 'chromium-headless-shell' : 'chromium';
|
|
306
|
+
}
|
|
303
307
|
}
|
|
304
308
|
exports.Chromium = Chromium;
|
|
305
|
-
class ChromiumReadyState {
|
|
306
|
-
constructor() {
|
|
307
|
-
this._wsEndpoint = new _manualPromise.ManualPromise();
|
|
308
|
-
}
|
|
309
|
+
class ChromiumReadyState extends _browserType.BrowserReadyState {
|
|
309
310
|
onBrowserOutput(message) {
|
|
310
311
|
const match = message.match(/DevTools listening on (.*)/);
|
|
311
312
|
if (match) this._wsEndpoint.resolve(match[1]);
|
|
312
313
|
}
|
|
313
|
-
onBrowserExit() {
|
|
314
|
-
this._wsEndpoint.resolve(undefined);
|
|
315
|
-
}
|
|
316
|
-
async waitUntilReady() {
|
|
317
|
-
const wsEndpoint = await this._wsEndpoint;
|
|
318
|
-
return {
|
|
319
|
-
wsEndpoint
|
|
320
|
-
};
|
|
321
|
-
}
|
|
322
314
|
}
|
|
323
315
|
async function urlToWSEndpoint(progress, endpointURL, headers) {
|
|
324
316
|
if (endpointURL.startsWith('ws')) return endpointURL;
|
|
@@ -34,8 +34,10 @@ const chromiumSwitches = exports.chromiumSwitches = ['--disable-field-trial-conf
|
|
|
34
34
|
// Translate - https://github.com/microsoft/playwright/issues/16126
|
|
35
35
|
// HttpsUpgrades - https://github.com/microsoft/playwright/pull/27605
|
|
36
36
|
// PaintHolding - https://github.com/microsoft/playwright/issues/28023
|
|
37
|
+
// ThirdPartyStoragePartitioning - https://github.com/microsoft/playwright/issues/32230
|
|
38
|
+
// LensOverlay - Hides the Lens feature in the URL address bar. Its not working in unofficial builds.
|
|
37
39
|
// PlzDedicatedWorker - https://github.com/microsoft/playwright/issues/31747
|
|
38
|
-
'--disable-features=ImprovedCookieControls,LazyFrameLoading,GlobalMediaControls,DestroyProfileOnBrowserClose,MediaRouter,DialMediaRouteProvider,AcceptCHFrame,AutoExpandDetailsElement,CertificateTransparencyComponentUpdater,AvoidUnnecessaryBeforeUnloadCheckSync,Translate,HttpsUpgrades,PaintHolding,PlzDedicatedWorker', '--allow-pre-commit-input', '--disable-hang-monitor', '--disable-ipc-flooding-protection', '--disable-popup-blocking', '--disable-prompt-on-repost', '--disable-renderer-backgrounding', '--force-color-profile=srgb', '--metrics-recording-only', '--no-first-run', '--enable-automation', '--password-store=basic', '--use-mock-keychain',
|
|
40
|
+
'--disable-features=ImprovedCookieControls,LazyFrameLoading,GlobalMediaControls,DestroyProfileOnBrowserClose,MediaRouter,DialMediaRouteProvider,AcceptCHFrame,AutoExpandDetailsElement,CertificateTransparencyComponentUpdater,AvoidUnnecessaryBeforeUnloadCheckSync,Translate,HttpsUpgrades,PaintHolding,ThirdPartyStoragePartitioning,LensOverlay,PlzDedicatedWorker', '--allow-pre-commit-input', '--disable-hang-monitor', '--disable-ipc-flooding-protection', '--disable-popup-blocking', '--disable-prompt-on-repost', '--disable-renderer-backgrounding', '--force-color-profile=srgb', '--metrics-recording-only', '--no-first-run', '--enable-automation', '--password-store=basic', '--use-mock-keychain',
|
|
39
41
|
// See https://chromium-review.googlesource.com/c/chromium/src/+/2436773
|
|
40
42
|
'--no-service-autorun', '--export-tagged-pdf',
|
|
41
43
|
// https://chromium-review.googlesource.com/c/chromium/src/+/4853540
|
|
@@ -99,15 +99,16 @@ class CRBrowser extends _browser.Browser {
|
|
|
99
99
|
this._session.on('Browser.downloadProgress', this._onDownloadProgress.bind(this));
|
|
100
100
|
}
|
|
101
101
|
async doCreateNewContext(options) {
|
|
102
|
+
const proxy = options.proxyOverride || options.proxy;
|
|
102
103
|
let proxyBypassList = undefined;
|
|
103
|
-
if (
|
|
104
|
-
if (process.env.PLAYWRIGHT_DISABLE_FORCED_CHROMIUM_PROXIED_LOOPBACK) proxyBypassList =
|
|
104
|
+
if (proxy) {
|
|
105
|
+
if (process.env.PLAYWRIGHT_DISABLE_FORCED_CHROMIUM_PROXIED_LOOPBACK) proxyBypassList = proxy.bypass;else proxyBypassList = '<-loopback>' + (proxy.bypass ? `,${proxy.bypass}` : '');
|
|
105
106
|
}
|
|
106
107
|
const {
|
|
107
108
|
browserContextId
|
|
108
109
|
} = await this._session.send('Target.createBrowserContext', {
|
|
109
110
|
disposeOnDetach: true,
|
|
110
|
-
proxyServer:
|
|
111
|
+
proxyServer: proxy ? proxy.server : undefined,
|
|
111
112
|
proxyBypassList
|
|
112
113
|
});
|
|
113
114
|
const context = new CRBrowserContext(this, browserContextId, options);
|
|
@@ -183,6 +183,159 @@ class CRSession extends _events.EventEmitter {
|
|
|
183
183
|
}
|
|
184
184
|
this._callbacks.clear();
|
|
185
185
|
}
|
|
186
|
+
async __re__emitExecutionContext({
|
|
187
|
+
world,
|
|
188
|
+
targetId,
|
|
189
|
+
frame = null
|
|
190
|
+
}) {
|
|
191
|
+
const fixMode = process.env['REBROWSER_PATCHES_RUNTIME_FIX_MODE'] || 'addBinding';
|
|
192
|
+
const utilityWorldName = process.env['REBROWSER_PATCHES_UTILITY_WORLD_NAME'] !== '0' ? process.env['REBROWSER_PATCHES_UTILITY_WORLD_NAME'] || 'util' : '__playwright_utility_world__';
|
|
193
|
+
process.env['REBROWSER_PATCHES_DEBUG'] && console.log(`[rebrowser-patches][crSession] targetId = ${targetId}, world = ${world}, frame = ${frame ? 'Y' : 'N'}, fixMode = ${fixMode}`);
|
|
194
|
+
let getWorldPromise;
|
|
195
|
+
if (fixMode === 'addBinding') {
|
|
196
|
+
if (world === 'utility') {
|
|
197
|
+
getWorldPromise = this.__re__getIsolatedWorld({
|
|
198
|
+
client: this,
|
|
199
|
+
frameId: targetId,
|
|
200
|
+
worldName: utilityWorldName
|
|
201
|
+
}).then(contextId => {
|
|
202
|
+
return {
|
|
203
|
+
id: contextId,
|
|
204
|
+
// use UTILITY_WORLD_NAME value from crPage.ts otherwise _onExecutionContextCreated will ignore it
|
|
205
|
+
name: '__playwright_utility_world__',
|
|
206
|
+
auxData: {
|
|
207
|
+
frameId: targetId,
|
|
208
|
+
isDefault: false
|
|
209
|
+
}
|
|
210
|
+
};
|
|
211
|
+
});
|
|
212
|
+
} else if (world === 'main') {
|
|
213
|
+
getWorldPromise = this.__re__getMainWorld({
|
|
214
|
+
client: this,
|
|
215
|
+
frameId: targetId,
|
|
216
|
+
isWorker: frame === null
|
|
217
|
+
}).then(contextId => {
|
|
218
|
+
return {
|
|
219
|
+
id: contextId,
|
|
220
|
+
name: '',
|
|
221
|
+
auxData: {
|
|
222
|
+
frameId: targetId,
|
|
223
|
+
isDefault: true
|
|
224
|
+
}
|
|
225
|
+
};
|
|
226
|
+
});
|
|
227
|
+
}
|
|
228
|
+
} else if (fixMode === 'alwaysIsolated') {
|
|
229
|
+
// use only utility context
|
|
230
|
+
getWorldPromise = this.__re__getIsolatedWorld({
|
|
231
|
+
client: this,
|
|
232
|
+
frameId: targetId,
|
|
233
|
+
worldName: utilityWorldName
|
|
234
|
+
}).then(contextId => {
|
|
235
|
+
// make it look as main world
|
|
236
|
+
return {
|
|
237
|
+
id: contextId,
|
|
238
|
+
name: '',
|
|
239
|
+
auxData: {
|
|
240
|
+
frameId: targetId,
|
|
241
|
+
isDefault: true
|
|
242
|
+
}
|
|
243
|
+
};
|
|
244
|
+
});
|
|
245
|
+
}
|
|
246
|
+
const contextPayload = await getWorldPromise;
|
|
247
|
+
this.emit('Runtime.executionContextCreated', {
|
|
248
|
+
context: contextPayload
|
|
249
|
+
});
|
|
250
|
+
}
|
|
251
|
+
async __re__getMainWorld({
|
|
252
|
+
client,
|
|
253
|
+
frameId,
|
|
254
|
+
isWorker = false
|
|
255
|
+
}) {
|
|
256
|
+
let contextId;
|
|
257
|
+
|
|
258
|
+
// random name to make it harder to detect for any 3rd party script by watching window object and events
|
|
259
|
+
const randomName = [...Array(Math.floor(Math.random() * (10 + 1)) + 10)].map(() => Math.random().toString(36)[2]).join('');
|
|
260
|
+
process.env['REBROWSER_PATCHES_DEBUG'] && console.log(`[rebrowser-patches][getMainWorld] binding name = ${randomName}`);
|
|
261
|
+
|
|
262
|
+
// add the binding
|
|
263
|
+
await client.send('Runtime.addBinding', {
|
|
264
|
+
name: randomName
|
|
265
|
+
});
|
|
266
|
+
|
|
267
|
+
// listen for 'Runtime.bindingCalled' event
|
|
268
|
+
const bindingCalledHandler = ({
|
|
269
|
+
name,
|
|
270
|
+
payload,
|
|
271
|
+
executionContextId
|
|
272
|
+
}) => {
|
|
273
|
+
process.env['REBROWSER_PATCHES_DEBUG'] && console.log('[rebrowser-patches][bindingCalledHandler]', {
|
|
274
|
+
name,
|
|
275
|
+
payload,
|
|
276
|
+
executionContextId
|
|
277
|
+
});
|
|
278
|
+
if (contextId > 0) {
|
|
279
|
+
// already acquired the id
|
|
280
|
+
return;
|
|
281
|
+
}
|
|
282
|
+
if (name !== randomName) {
|
|
283
|
+
// ignore irrelevant bindings
|
|
284
|
+
return;
|
|
285
|
+
}
|
|
286
|
+
if (payload !== frameId) {
|
|
287
|
+
// ignore irrelevant frames
|
|
288
|
+
return;
|
|
289
|
+
}
|
|
290
|
+
contextId = executionContextId;
|
|
291
|
+
// remove this listener
|
|
292
|
+
client.off('Runtime.bindingCalled', bindingCalledHandler);
|
|
293
|
+
};
|
|
294
|
+
client.on('Runtime.bindingCalled', bindingCalledHandler);
|
|
295
|
+
if (isWorker) {
|
|
296
|
+
// workers don't support `Page.addScriptToEvaluateOnNewDocument` and `Page.createIsolatedWorld`, but there are no iframes inside of them, so it's safe to just use Runtime.evaluate
|
|
297
|
+
await client.send('Runtime.evaluate', {
|
|
298
|
+
expression: `this['${randomName}']('${frameId}')`
|
|
299
|
+
});
|
|
300
|
+
} else {
|
|
301
|
+
// we could call the binding right from `addScriptToEvaluateOnNewDocument`, but this way it will be called in all existing frames and it's hard to distinguish children from the parent
|
|
302
|
+
await client.send('Page.addScriptToEvaluateOnNewDocument', {
|
|
303
|
+
source: `document.addEventListener('${randomName}', (e) => self['${randomName}'](e.detail.frameId))`,
|
|
304
|
+
runImmediately: true
|
|
305
|
+
});
|
|
306
|
+
|
|
307
|
+
// create new isolated world for this frame
|
|
308
|
+
const createIsolatedWorldResult = await client.send('Page.createIsolatedWorld', {
|
|
309
|
+
frameId,
|
|
310
|
+
// use randomName for worldName to distinguish from normal utility world
|
|
311
|
+
worldName: randomName,
|
|
312
|
+
grantUniveralAccess: true
|
|
313
|
+
});
|
|
314
|
+
|
|
315
|
+
// emit event in the specific frame from the isolated world
|
|
316
|
+
await client.send('Runtime.evaluate', {
|
|
317
|
+
expression: `document.dispatchEvent(new CustomEvent('${randomName}', { detail: { frameId: '${frameId}' } }))`,
|
|
318
|
+
contextId: createIsolatedWorldResult.executionContextId
|
|
319
|
+
});
|
|
320
|
+
}
|
|
321
|
+
process.env['REBROWSER_PATCHES_DEBUG'] && console.log(`[rebrowser-patches][getMainWorld] result:`, {
|
|
322
|
+
contextId
|
|
323
|
+
});
|
|
324
|
+
return contextId;
|
|
325
|
+
}
|
|
326
|
+
async __re__getIsolatedWorld({
|
|
327
|
+
client,
|
|
328
|
+
frameId,
|
|
329
|
+
worldName
|
|
330
|
+
}) {
|
|
331
|
+
const createIsolatedWorldResult = await client.send('Page.createIsolatedWorld', {
|
|
332
|
+
frameId,
|
|
333
|
+
worldName,
|
|
334
|
+
grantUniveralAccess: true
|
|
335
|
+
});
|
|
336
|
+
process.env['REBROWSER_PATCHES_DEBUG'] && console.log(`[rebrowser-patches][getIsolatedWorld] result:`, createIsolatedWorldResult);
|
|
337
|
+
return createIsolatedWorldResult.executionContextId;
|
|
338
|
+
}
|
|
186
339
|
}
|
|
187
340
|
exports.CRSession = CRSession;
|
|
188
341
|
class CDPSession extends _events.EventEmitter {
|
|
@@ -58,19 +58,6 @@ class CRExecutionContext {
|
|
|
58
58
|
if (exceptionDetails) throw new js.JavaScriptErrorInEvaluate((0, _crProtocolHelper.getExceptionMessage)(exceptionDetails));
|
|
59
59
|
return remoteObject.objectId;
|
|
60
60
|
}
|
|
61
|
-
rawCallFunctionNoReply(func, ...args) {
|
|
62
|
-
this._client.send('Runtime.callFunctionOn', {
|
|
63
|
-
functionDeclaration: func.toString(),
|
|
64
|
-
arguments: args.map(a => a instanceof js.JSHandle ? {
|
|
65
|
-
objectId: a._objectId
|
|
66
|
-
} : {
|
|
67
|
-
value: a
|
|
68
|
-
}),
|
|
69
|
-
returnByValue: true,
|
|
70
|
-
executionContextId: this._contextId,
|
|
71
|
-
userGesture: true
|
|
72
|
-
}).catch(() => {});
|
|
73
|
-
}
|
|
74
61
|
async evaluateWithArguments(expression, returnByValue, utilityScript, values, objectIds) {
|
|
75
62
|
const {
|
|
76
63
|
exceptionDetails,
|
|
@@ -110,13 +97,6 @@ class CRExecutionContext {
|
|
|
110
97
|
async releaseHandle(objectId) {
|
|
111
98
|
await (0, _crProtocolHelper.releaseObject)(this._client, objectId);
|
|
112
99
|
}
|
|
113
|
-
async objectCount(objectId) {
|
|
114
|
-
const result = await this._client.send('Runtime.queryObjects', {
|
|
115
|
-
prototypeObjectId: objectId
|
|
116
|
-
});
|
|
117
|
-
const match = result.objects.description.match(/Array\((\d+)\)/);
|
|
118
|
-
return +match[1];
|
|
119
|
-
}
|
|
120
100
|
}
|
|
121
101
|
exports.CRExecutionContext = CRExecutionContext;
|
|
122
102
|
function rewriteError(error) {
|
|
@@ -214,6 +214,9 @@ class CRPage {
|
|
|
214
214
|
goForward() {
|
|
215
215
|
return this._go(+1);
|
|
216
216
|
}
|
|
217
|
+
async requestGC() {
|
|
218
|
+
await this._mainFrameSession._client.send('HeapProfiler.collectGarbage');
|
|
219
|
+
}
|
|
217
220
|
async addInitScript(initScript, world = 'main') {
|
|
218
221
|
await this._forAllFrameSessions(frame => frame._evaluateOnNewDocument(initScript, world));
|
|
219
222
|
}
|
|
@@ -315,7 +318,10 @@ class CRPage {
|
|
|
315
318
|
async inputActionEpilogue() {
|
|
316
319
|
await this._mainFrameSession._client.send('Page.enable').catch(e => {});
|
|
317
320
|
}
|
|
318
|
-
async resetForReuse() {
|
|
321
|
+
async resetForReuse() {
|
|
322
|
+
// See https://github.com/microsoft/playwright/issues/22432.
|
|
323
|
+
await this.rawMouse.move(-1, -1, 'none', new Set(), new Set(), true);
|
|
324
|
+
}
|
|
319
325
|
async pdf(options) {
|
|
320
326
|
return this._pdf.generate(options);
|
|
321
327
|
}
|
|
@@ -467,7 +473,7 @@ class FrameSession {
|
|
|
467
473
|
if (options.bypassCSP) promises.push(this._client.send('Page.setBypassCSP', {
|
|
468
474
|
enabled: true
|
|
469
475
|
}));
|
|
470
|
-
if (options.ignoreHTTPSErrors) promises.push(this._client.send('Security.setIgnoreCertificateErrors', {
|
|
476
|
+
if (options.ignoreHTTPSErrors || options.internalIgnoreHTTPSErrors) promises.push(this._client.send('Security.setIgnoreCertificateErrors', {
|
|
471
477
|
ignore: true
|
|
472
478
|
}));
|
|
473
479
|
if (this._isMainFrame()) promises.push(this._updateViewport());
|
|
@@ -587,11 +593,11 @@ class FrameSession {
|
|
|
587
593
|
const frame = contextPayload.auxData ? this._page._frameManager.frame(contextPayload.auxData.frameId) : null;
|
|
588
594
|
if (!frame || this._eventBelongsToStaleFrame(frame._id)) return;
|
|
589
595
|
const delegate = new _crExecutionContext.CRExecutionContext(this._client, contextPayload);
|
|
590
|
-
let worldName;
|
|
591
|
-
if (contextPayload.auxData && !!contextPayload.auxData.isDefault) worldName = 'main';else if (contextPayload.name === UTILITY_WORLD_NAME) worldName = 'utility';
|
|
596
|
+
let worldName = null;
|
|
597
|
+
if (contextPayload.auxData && !!contextPayload.auxData.isDefault) worldName = 'main';else if (contextPayload.name === UTILITY_WORLD_NAME) worldName = 'utility';
|
|
592
598
|
const context = new dom.FrameExecutionContext(delegate, frame, worldName);
|
|
593
599
|
context[contextDelegateSymbol] = delegate;
|
|
594
|
-
frame._contextCreated(worldName, context);
|
|
600
|
+
if (worldName) frame._contextCreated(worldName, context);
|
|
595
601
|
this._contextIdToContext.set(contextPayload.id, context);
|
|
596
602
|
}
|
|
597
603
|
_onExecutionContextDestroyed(executionContextId) {
|
|
@@ -622,7 +628,7 @@ class FrameSession {
|
|
|
622
628
|
return;
|
|
623
629
|
}
|
|
624
630
|
const url = event.targetInfo.url;
|
|
625
|
-
const worker = new _page.Worker(this._page, url);
|
|
631
|
+
const worker = new _page.Worker(this._page, url, event.targetInfo.targetId, session);
|
|
626
632
|
this._page._addWorker(event.sessionId, worker);
|
|
627
633
|
this._workerSessions.set(event.sessionId, session);
|
|
628
634
|
session.once('Runtime.executionContextCreated', async event => {
|
|
@@ -777,7 +783,7 @@ class FrameSession {
|
|
|
777
783
|
const buffer = Buffer.from(payload.data, 'base64');
|
|
778
784
|
this._page.emit(_page.Page.Events.ScreencastFrame, {
|
|
779
785
|
buffer,
|
|
780
|
-
|
|
786
|
+
frameSwapWallTime: payload.metadata.timestamp ? payload.metadata.timestamp * 1000 : undefined,
|
|
781
787
|
width: payload.metadata.deviceWidth,
|
|
782
788
|
height: payload.metadata.deviceHeight
|
|
783
789
|
});
|
|
@@ -50,7 +50,7 @@ class VideoRecorder {
|
|
|
50
50
|
this._ffmpegPath = void 0;
|
|
51
51
|
this._progress = progress;
|
|
52
52
|
this._ffmpegPath = ffmpegPath;
|
|
53
|
-
page.on(_page.Page.Events.ScreencastFrame, frame => this.writeFrame(frame.buffer, frame.
|
|
53
|
+
page.on(_page.Page.Events.ScreencastFrame, frame => this.writeFrame(frame.buffer, frame.frameSwapWallTime / 1000));
|
|
54
54
|
}
|
|
55
55
|
async _launch(options) {
|
|
56
56
|
// How to tune the codec:
|
|
@@ -96,7 +96,7 @@ class CSharpLanguageGenerator {
|
|
|
96
96
|
{
|
|
97
97
|
let method = 'Click';
|
|
98
98
|
if (action.clickCount === 2) method = 'DblClick';
|
|
99
|
-
const options = (0, _language.
|
|
99
|
+
const options = (0, _language.toClickOptionsForSourceCode)(action);
|
|
100
100
|
if (!Object.entries(options).length) return `await ${subject}.${this._asLocator(action.selector)}.${method}Async();`;
|
|
101
101
|
const optionsString = formatObject(options, ' ', 'Locator' + method + 'Options');
|
|
102
102
|
return `await ${subject}.${this._asLocator(action.selector)}.${method}Async(${optionsString});`;
|
|
@@ -130,6 +130,8 @@ class CSharpLanguageGenerator {
|
|
|
130
130
|
const assertion = action.value ? `ToHaveValueAsync(${quote(action.value)})` : `ToBeEmptyAsync()`;
|
|
131
131
|
return `await Expect(${subject}.${this._asLocator(action.selector)}).${assertion};`;
|
|
132
132
|
}
|
|
133
|
+
case 'assertSnapshot':
|
|
134
|
+
return `await Expect(${subject}.${this._asLocator(action.selector)}).ToMatchAriaSnapshotAsync(${quote(action.snapshot)});`;
|
|
133
135
|
}
|
|
134
136
|
}
|
|
135
137
|
_asLocator(selector) {
|
|
@@ -149,6 +151,7 @@ class CSharpLanguageGenerator {
|
|
|
149
151
|
using var playwright = await Playwright.CreateAsync();
|
|
150
152
|
await using var browser = await playwright.${toPascal(options.browserName)}.LaunchAsync(${formatObject(options.launchOptions, ' ', 'BrowserTypeLaunchOptions')});
|
|
151
153
|
var context = await browser.NewContextAsync(${formatContextOptions(options.contextOptions, options.deviceName)});`);
|
|
154
|
+
if (options.contextOptions.recordHar) formatter.add(` await context.RouteFromHARAsync(${quote(options.contextOptions.recordHar.path)});`);
|
|
152
155
|
formatter.newLine();
|
|
153
156
|
return formatter.format();
|
|
154
157
|
}
|
|
@@ -173,6 +176,7 @@ class CSharpLanguageGenerator {
|
|
|
173
176
|
formatter.add(` [${this._mode === 'nunit' ? 'Test' : 'TestMethod'}]
|
|
174
177
|
public async Task MyTest()
|
|
175
178
|
{`);
|
|
179
|
+
if (options.contextOptions.recordHar) formatter.add(` await context.RouteFromHARAsync(${quote(options.contextOptions.recordHar.path)});`);
|
|
176
180
|
return formatter.format();
|
|
177
181
|
}
|
|
178
182
|
generateFooter(saveStorage) {
|
|
@@ -238,29 +242,20 @@ function getPropertyName(key) {
|
|
|
238
242
|
function toPascal(value) {
|
|
239
243
|
return value[0].toUpperCase() + value.slice(1);
|
|
240
244
|
}
|
|
241
|
-
function
|
|
242
|
-
|
|
243
|
-
...
|
|
245
|
+
function formatContextOptions(contextOptions, deviceName) {
|
|
246
|
+
let options = {
|
|
247
|
+
...contextOptions
|
|
244
248
|
};
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
result['recordHarContent'] = options.recordHar.content;
|
|
248
|
-
result['recordHarMode'] = options.recordHar.mode;
|
|
249
|
-
result['recordHarOmitContent'] = options.recordHar.omitContent;
|
|
250
|
-
result['recordHarUrlFilter'] = options.recordHar.urlFilter;
|
|
251
|
-
delete result.recordHar;
|
|
252
|
-
}
|
|
253
|
-
return result;
|
|
254
|
-
}
|
|
255
|
-
function formatContextOptions(options, deviceName) {
|
|
249
|
+
// recordHAR is replaced with routeFromHAR in the generated code.
|
|
250
|
+
delete options.recordHar;
|
|
256
251
|
const device = deviceName && _deviceDescriptors.deviceDescriptors[deviceName];
|
|
257
252
|
if (!device) {
|
|
258
253
|
if (!Object.entries(options).length) return '';
|
|
259
|
-
return formatObject(
|
|
254
|
+
return formatObject(options, ' ', 'BrowserNewContextOptions');
|
|
260
255
|
}
|
|
261
256
|
options = (0, _language.sanitizeDeviceOptions)(device, options);
|
|
262
257
|
if (!Object.entries(options).length) return `playwright.Devices[${quote(deviceName)}]`;
|
|
263
|
-
return formatObject(
|
|
258
|
+
return formatObject(options, ' ', `BrowserNewContextOptions(playwright.Devices[${quote(deviceName)}])`);
|
|
264
259
|
}
|
|
265
260
|
class CSharpFormatter {
|
|
266
261
|
constructor(offset = 0) {
|