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
|
@@ -87,7 +87,7 @@ class JavaLanguageGenerator {
|
|
|
87
87
|
{
|
|
88
88
|
let method = 'click';
|
|
89
89
|
if (action.clickCount === 2) method = 'dblclick';
|
|
90
|
-
const options = (0, _language.
|
|
90
|
+
const options = (0, _language.toClickOptionsForSourceCode)(action);
|
|
91
91
|
const optionsText = formatClickOptions(options);
|
|
92
92
|
return `${subject}.${this._asLocator(action.selector, inFrameLocator)}.${method}(${optionsText});`;
|
|
93
93
|
}
|
|
@@ -108,7 +108,7 @@ class JavaLanguageGenerator {
|
|
|
108
108
|
case 'navigate':
|
|
109
109
|
return `${subject}.navigate(${quote(action.url)});`;
|
|
110
110
|
case 'select':
|
|
111
|
-
return `${subject}.${this._asLocator(action.selector, inFrameLocator)}.selectOption(${formatSelectOption(action.options.length
|
|
111
|
+
return `${subject}.${this._asLocator(action.selector, inFrameLocator)}.selectOption(${formatSelectOption(action.options.length === 1 ? action.options[0] : action.options)});`;
|
|
112
112
|
case 'assertText':
|
|
113
113
|
return `assertThat(${subject}.${this._asLocator(action.selector, inFrameLocator)}).${action.substring ? 'containsText' : 'hasText'}(${quote(action.text)});`;
|
|
114
114
|
case 'assertChecked':
|
|
@@ -120,6 +120,8 @@ class JavaLanguageGenerator {
|
|
|
120
120
|
const assertion = action.value ? `hasValue(${quote(action.value)})` : `isEmpty()`;
|
|
121
121
|
return `assertThat(${subject}.${this._asLocator(action.selector, inFrameLocator)}).${assertion};`;
|
|
122
122
|
}
|
|
123
|
+
case 'assertSnapshot':
|
|
124
|
+
return `assertThat(${subject}.${this._asLocator(action.selector, inFrameLocator)}).matchesAriaSnapshot(${quote(action.snapshot)});`;
|
|
123
125
|
}
|
|
124
126
|
}
|
|
125
127
|
_asLocator(selector, inFrameLocator) {
|
|
@@ -153,6 +155,7 @@ class JavaLanguageGenerator {
|
|
|
153
155
|
try (Playwright playwright = Playwright.create()) {
|
|
154
156
|
Browser browser = playwright.${options.browserName}().launch(${formatLaunchOptions(options.launchOptions)});
|
|
155
157
|
BrowserContext context = browser.newContext(${formatContextOptions(options.contextOptions, options.deviceName)});`);
|
|
158
|
+
if (options.contextOptions.recordHar) formatter.add(` context.routeFromHAR(${quote(options.contextOptions.recordHar.path)});`);
|
|
156
159
|
return formatter.format();
|
|
157
160
|
}
|
|
158
161
|
generateFooter(saveStorage) {
|
|
@@ -190,7 +193,6 @@ function formatLaunchOptions(options) {
|
|
|
190
193
|
return lines.join('\n');
|
|
191
194
|
}
|
|
192
195
|
function formatContextOptions(contextOptions, deviceName) {
|
|
193
|
-
var _options$recordHar, _options$recordHar2, _options$recordHar3, _options$recordHar4, _options$recordHar5, _options$recordHar6, _options$recordHar7;
|
|
194
196
|
const lines = [];
|
|
195
197
|
if (!Object.keys(contextOptions).length && !deviceName) return '';
|
|
196
198
|
const device = deviceName ? _deviceDescriptors.deviceDescriptors[deviceName] : {};
|
|
@@ -208,11 +210,6 @@ function formatContextOptions(contextOptions, deviceName) {
|
|
|
208
210
|
if (options.isMobile) lines.push(` .setIsMobile(${options.isMobile})`);
|
|
209
211
|
if (options.locale) lines.push(` .setLocale(${quote(options.locale)})`);
|
|
210
212
|
if (options.proxy) lines.push(` .setProxy(new Proxy(${quote(options.proxy.server)}))`);
|
|
211
|
-
if ((_options$recordHar = options.recordHar) !== null && _options$recordHar !== void 0 && _options$recordHar.content) lines.push(` .setRecordHarContent(HarContentPolicy.${(_options$recordHar2 = options.recordHar) === null || _options$recordHar2 === void 0 ? void 0 : _options$recordHar2.content.toUpperCase()})`);
|
|
212
|
-
if ((_options$recordHar3 = options.recordHar) !== null && _options$recordHar3 !== void 0 && _options$recordHar3.mode) lines.push(` .setRecordHarMode(HarMode.${(_options$recordHar4 = options.recordHar) === null || _options$recordHar4 === void 0 ? void 0 : _options$recordHar4.mode.toUpperCase()})`);
|
|
213
|
-
if ((_options$recordHar5 = options.recordHar) !== null && _options$recordHar5 !== void 0 && _options$recordHar5.omitContent) lines.push(` .setRecordHarOmitContent(true)`);
|
|
214
|
-
if ((_options$recordHar6 = options.recordHar) !== null && _options$recordHar6 !== void 0 && _options$recordHar6.path) lines.push(` .setRecordHarPath(Paths.get(${quote(options.recordHar.path)}))`);
|
|
215
|
-
if ((_options$recordHar7 = options.recordHar) !== null && _options$recordHar7 !== void 0 && _options$recordHar7.urlFilter) lines.push(` .setRecordHarUrlFilter(${quote(options.recordHar.urlFilter)})`);
|
|
216
213
|
if (options.serviceWorkers) lines.push(` .setServiceWorkers(ServiceWorkerPolicy.${options.serviceWorkers.toUpperCase()})`);
|
|
217
214
|
if (options.storageState) lines.push(` .setStorageStatePath(Paths.get(${quote(options.storageState)}))`);
|
|
218
215
|
if (options.timezoneId) lines.push(` .setTimezoneId(${quote(options.timezoneId)})`);
|
|
@@ -4,6 +4,7 @@ Object.defineProperty(exports, "__esModule", {
|
|
|
4
4
|
value: true
|
|
5
5
|
});
|
|
6
6
|
exports.JavaScriptLanguageGenerator = exports.JavaScriptFormatter = void 0;
|
|
7
|
+
exports.quoteMultiline = quoteMultiline;
|
|
7
8
|
var _language = require("./language");
|
|
8
9
|
var _deviceDescriptors = require("../deviceDescriptors");
|
|
9
10
|
var _utils = require("../../utils");
|
|
@@ -71,7 +72,7 @@ class JavaScriptLanguageGenerator {
|
|
|
71
72
|
{
|
|
72
73
|
let method = 'click';
|
|
73
74
|
if (action.clickCount === 2) method = 'dblclick';
|
|
74
|
-
const options = (0, _language.
|
|
75
|
+
const options = (0, _language.toClickOptionsForSourceCode)(action);
|
|
75
76
|
const optionsString = formatOptions(options, false);
|
|
76
77
|
return `await ${subject}.${this._asLocator(action.selector)}.${method}(${optionsString});`;
|
|
77
78
|
}
|
|
@@ -92,7 +93,7 @@ class JavaScriptLanguageGenerator {
|
|
|
92
93
|
case 'navigate':
|
|
93
94
|
return `await ${subject}.goto(${quote(action.url)});`;
|
|
94
95
|
case 'select':
|
|
95
|
-
return `await ${subject}.${this._asLocator(action.selector)}.selectOption(${formatObject(action.options.length
|
|
96
|
+
return `await ${subject}.${this._asLocator(action.selector)}.selectOption(${formatObject(action.options.length === 1 ? action.options[0] : action.options)});`;
|
|
96
97
|
case 'assertText':
|
|
97
98
|
return `${this._isTest ? '' : '// '}await expect(${subject}.${this._asLocator(action.selector)}).${action.substring ? 'toContainText' : 'toHaveText'}(${quote(action.text)});`;
|
|
98
99
|
case 'assertChecked':
|
|
@@ -104,6 +105,8 @@ class JavaScriptLanguageGenerator {
|
|
|
104
105
|
const assertion = action.value ? `toHaveValue(${quote(action.value)})` : `toBeEmpty()`;
|
|
105
106
|
return `${this._isTest ? '' : '// '}await expect(${subject}.${this._asLocator(action.selector)}).${assertion};`;
|
|
106
107
|
}
|
|
108
|
+
case 'assertSnapshot':
|
|
109
|
+
return `${this._isTest ? '' : '// '}await expect(${subject}.${this._asLocator(action.selector)}).toMatchAriaSnapshot(${quoteMultiline(action.snapshot)});`;
|
|
107
110
|
}
|
|
108
111
|
}
|
|
109
112
|
_asLocator(selector) {
|
|
@@ -119,11 +122,12 @@ class JavaScriptLanguageGenerator {
|
|
|
119
122
|
}
|
|
120
123
|
generateTestHeader(options) {
|
|
121
124
|
const formatter = new JavaScriptFormatter();
|
|
122
|
-
const useText = formatContextOptions(options.contextOptions, options.deviceName);
|
|
125
|
+
const useText = formatContextOptions(options.contextOptions, options.deviceName, this._isTest);
|
|
123
126
|
formatter.add(`
|
|
124
127
|
import { test, expect${options.deviceName ? ', devices' : ''} } from '@playwright/test';
|
|
125
128
|
${useText ? '\ntest.use(' + useText + ');\n' : ''}
|
|
126
129
|
test('test', async ({ page }) => {`);
|
|
130
|
+
if (options.contextOptions.recordHar) formatter.add(` await page.routeFromHAR(${quote(options.contextOptions.recordHar.path)});`);
|
|
127
131
|
return formatter.format();
|
|
128
132
|
}
|
|
129
133
|
generateTestFooter(saveStorage) {
|
|
@@ -136,7 +140,8 @@ ${useText ? '\ntest.use(' + useText + ');\n' : ''}
|
|
|
136
140
|
|
|
137
141
|
(async () => {
|
|
138
142
|
const browser = await ${options.browserName}.launch(${formatObjectOrVoid(options.launchOptions)});
|
|
139
|
-
const context = await browser.newContext(${formatContextOptions(options.contextOptions, options.deviceName)});`);
|
|
143
|
+
const context = await browser.newContext(${formatContextOptions(options.contextOptions, options.deviceName, false)});`);
|
|
144
|
+
if (options.contextOptions.recordHar) formatter.add(` await context.routeFromHAR(${quote(options.contextOptions.recordHar.path)});`);
|
|
140
145
|
return formatter.format();
|
|
141
146
|
}
|
|
142
147
|
generateStandaloneFooter(saveStorage) {
|
|
@@ -169,8 +174,13 @@ function formatObjectOrVoid(value, indent = ' ') {
|
|
|
169
174
|
const result = formatObject(value, indent);
|
|
170
175
|
return result === '{}' ? '' : result;
|
|
171
176
|
}
|
|
172
|
-
function formatContextOptions(options, deviceName) {
|
|
177
|
+
function formatContextOptions(options, deviceName, isTest) {
|
|
173
178
|
const device = deviceName && _deviceDescriptors.deviceDescriptors[deviceName];
|
|
179
|
+
// recordHAR is replaced with routeFromHAR in the generated code.
|
|
180
|
+
options = {
|
|
181
|
+
...options,
|
|
182
|
+
recordHar: undefined
|
|
183
|
+
};
|
|
174
184
|
if (!device) return formatObjectOrVoid(options);
|
|
175
185
|
// Filter out all the properties from the device descriptor.
|
|
176
186
|
let serializedObject = formatObjectOrVoid((0, _language.sanitizeDeviceOptions)(device, options));
|
|
@@ -189,10 +199,12 @@ class JavaScriptFormatter {
|
|
|
189
199
|
this._baseOffset = ' '.repeat(offset);
|
|
190
200
|
}
|
|
191
201
|
prepend(text) {
|
|
192
|
-
|
|
202
|
+
const trim = isMultilineString(text) ? line => line : line => line.trim();
|
|
203
|
+
this._lines = text.trim().split('\n').map(trim).concat(this._lines);
|
|
193
204
|
}
|
|
194
205
|
add(text) {
|
|
195
|
-
|
|
206
|
+
const trim = isMultilineString(text) ? line => line : line => line.trim();
|
|
207
|
+
this._lines.push(...text.trim().split('\n').map(trim));
|
|
196
208
|
}
|
|
197
209
|
newLine() {
|
|
198
210
|
this._lines.push('');
|
|
@@ -220,4 +232,14 @@ function wrapWithStep(description, body) {
|
|
|
220
232
|
return description ? `await test.step(\`${description}\`, async () => {
|
|
221
233
|
${body}
|
|
222
234
|
});` : body;
|
|
235
|
+
}
|
|
236
|
+
function quoteMultiline(text, indent = ' ') {
|
|
237
|
+
const escape = text => text.replace(/\\/g, '\\\\').replace(/`/g, '\\`').replace(/\$\{/g, '\\${');
|
|
238
|
+
const lines = text.split('\n');
|
|
239
|
+
if (lines.length === 1) return '`' + escape(text) + '`';
|
|
240
|
+
return '`\n' + lines.map(line => indent + escape(line).replace(/\${/g, '\\${')).join('\n') + `\n${indent}\``;
|
|
241
|
+
}
|
|
242
|
+
function isMultilineString(text) {
|
|
243
|
+
var _text$match;
|
|
244
|
+
return (_text$match = text.match(/`[\S\s]*`/)) === null || _text$match === void 0 ? void 0 : _text$match[0].includes('\n');
|
|
223
245
|
}
|
|
@@ -3,9 +3,10 @@
|
|
|
3
3
|
Object.defineProperty(exports, "__esModule", {
|
|
4
4
|
value: true
|
|
5
5
|
});
|
|
6
|
+
exports.fromKeyboardModifiers = fromKeyboardModifiers;
|
|
6
7
|
exports.generateCode = generateCode;
|
|
7
8
|
exports.sanitizeDeviceOptions = sanitizeDeviceOptions;
|
|
8
|
-
exports.
|
|
9
|
+
exports.toClickOptionsForSourceCode = toClickOptionsForSourceCode;
|
|
9
10
|
exports.toKeyboardModifiers = toKeyboardModifiers;
|
|
10
11
|
exports.toSignalMap = toSignalMap;
|
|
11
12
|
/**
|
|
@@ -65,11 +66,22 @@ function toKeyboardModifiers(modifiers) {
|
|
|
65
66
|
if (modifiers & 8) result.push('Shift');
|
|
66
67
|
return result;
|
|
67
68
|
}
|
|
68
|
-
function
|
|
69
|
+
function fromKeyboardModifiers(modifiers) {
|
|
70
|
+
let result = 0;
|
|
71
|
+
if (!modifiers) return result;
|
|
72
|
+
if (modifiers.includes('Alt')) result |= 1;
|
|
73
|
+
if (modifiers.includes('Control')) result |= 2;
|
|
74
|
+
if (modifiers.includes('ControlOrMeta')) result |= 2;
|
|
75
|
+
if (modifiers.includes('Meta')) result |= 4;
|
|
76
|
+
if (modifiers.includes('Shift')) result |= 8;
|
|
77
|
+
return result;
|
|
78
|
+
}
|
|
79
|
+
function toClickOptionsForSourceCode(action) {
|
|
69
80
|
const modifiers = toKeyboardModifiers(action.modifiers);
|
|
70
81
|
const options = {};
|
|
71
82
|
if (action.button !== 'left') options.button = action.button;
|
|
72
83
|
if (modifiers.length) options.modifiers = modifiers;
|
|
84
|
+
// Do not render clickCount === 2 for dblclick.
|
|
73
85
|
if (action.clickCount > 2) options.clickCount = action.clickCount;
|
|
74
86
|
if (action.position) options.position = action.position;
|
|
75
87
|
return options;
|
|
@@ -81,7 +81,7 @@ class PythonLanguageGenerator {
|
|
|
81
81
|
{
|
|
82
82
|
let method = 'click';
|
|
83
83
|
if (action.clickCount === 2) method = 'dblclick';
|
|
84
|
-
const options = (0, _language.
|
|
84
|
+
const options = (0, _language.toClickOptionsForSourceCode)(action);
|
|
85
85
|
const optionsString = formatOptions(options, false);
|
|
86
86
|
return `${subject}.${this._asLocator(action.selector)}.${method}(${optionsString})`;
|
|
87
87
|
}
|
|
@@ -114,6 +114,8 @@ class PythonLanguageGenerator {
|
|
|
114
114
|
const assertion = action.value ? `to_have_value(${quote(action.value)})` : `to_be_empty()`;
|
|
115
115
|
return `expect(${subject}.${this._asLocator(action.selector)}).${assertion};`;
|
|
116
116
|
}
|
|
117
|
+
case 'assertSnapshot':
|
|
118
|
+
return `expect(${subject}.${this._asLocator(action.selector)}).to_match_aria_snapshot(${quote(action.snapshot)})`;
|
|
117
119
|
}
|
|
118
120
|
}
|
|
119
121
|
_asLocator(selector) {
|
|
@@ -135,6 +137,7 @@ from playwright.sync_api import Page, expect
|
|
|
135
137
|
${fixture}
|
|
136
138
|
|
|
137
139
|
def test_example(page: Page) -> None {`);
|
|
140
|
+
if (options.contextOptions.recordHar) formatter.add(` page.route_from_har(${quote(options.contextOptions.recordHar.path)})`);
|
|
138
141
|
} else if (this._isAsync) {
|
|
139
142
|
formatter.add(`
|
|
140
143
|
import asyncio
|
|
@@ -145,6 +148,7 @@ from playwright.async_api import Playwright, async_playwright, expect
|
|
|
145
148
|
async def run(playwright: Playwright) -> None {
|
|
146
149
|
browser = await playwright.${options.browserName}.launch(${formatOptions(options.launchOptions, false)})
|
|
147
150
|
context = await browser.new_context(${formatContextOptions(options.contextOptions, options.deviceName)})`);
|
|
151
|
+
if (options.contextOptions.recordHar) formatter.add(` await page.route_from_har(${quote(options.contextOptions.recordHar.path)})`);
|
|
148
152
|
} else {
|
|
149
153
|
formatter.add(`
|
|
150
154
|
import re
|
|
@@ -154,6 +158,7 @@ from playwright.sync_api import Playwright, sync_playwright, expect
|
|
|
154
158
|
def run(playwright: Playwright) -> None {
|
|
155
159
|
browser = playwright.${options.browserName}.launch(${formatOptions(options.launchOptions, false)})
|
|
156
160
|
context = browser.new_context(${formatContextOptions(options.contextOptions, options.deviceName)})`);
|
|
161
|
+
if (options.contextOptions.recordHar) formatter.add(` context.route_from_har(${quote(options.contextOptions.recordHar.path)})`);
|
|
157
162
|
}
|
|
158
163
|
return formatter.format();
|
|
159
164
|
}
|
|
@@ -205,24 +210,15 @@ function formatOptions(value, hasArguments, asDict) {
|
|
|
205
210
|
return `${(0, _utils.toSnakeCase)(key)}=${formatValue(value[key])}`;
|
|
206
211
|
}).join(', ');
|
|
207
212
|
}
|
|
208
|
-
function convertContextOptions(options) {
|
|
209
|
-
const result = {
|
|
210
|
-
...options
|
|
211
|
-
};
|
|
212
|
-
if (options.recordHar) {
|
|
213
|
-
result['record_har_path'] = options.recordHar.path;
|
|
214
|
-
result['record_har_content'] = options.recordHar.content;
|
|
215
|
-
result['record_har_mode'] = options.recordHar.mode;
|
|
216
|
-
result['record_har_omit_content'] = options.recordHar.omitContent;
|
|
217
|
-
result['record_har_url_filter'] = options.recordHar.urlFilter;
|
|
218
|
-
delete result.recordHar;
|
|
219
|
-
}
|
|
220
|
-
return result;
|
|
221
|
-
}
|
|
222
213
|
function formatContextOptions(options, deviceName, asDict) {
|
|
214
|
+
// recordHAR is replaced with routeFromHAR in the generated code.
|
|
215
|
+
options = {
|
|
216
|
+
...options,
|
|
217
|
+
recordHar: undefined
|
|
218
|
+
};
|
|
223
219
|
const device = deviceName && _deviceDescriptors.deviceDescriptors[deviceName];
|
|
224
|
-
if (!device) return formatOptions(
|
|
225
|
-
return `**playwright.devices[${quote(deviceName)}]` + formatOptions(
|
|
220
|
+
if (!device) return formatOptions(options, false, asDict);
|
|
221
|
+
return `**playwright.devices[${quote(deviceName)}]` + formatOptions((0, _language.sanitizeDeviceOptions)(device, options), true, asDict);
|
|
226
222
|
}
|
|
227
223
|
class PythonFormatter {
|
|
228
224
|
constructor(offset = 0) {
|
|
@@ -5,6 +5,8 @@ Object.defineProperty(exports, "__esModule", {
|
|
|
5
5
|
});
|
|
6
6
|
exports.CookieStore = void 0;
|
|
7
7
|
exports.domainMatches = domainMatches;
|
|
8
|
+
exports.parseRawCookie = parseRawCookie;
|
|
9
|
+
var _network = require("./network");
|
|
8
10
|
/**
|
|
9
11
|
* Copyright (c) Microsoft Corporation.
|
|
10
12
|
*
|
|
@@ -97,6 +99,77 @@ class CookieStore {
|
|
|
97
99
|
}
|
|
98
100
|
}
|
|
99
101
|
exports.CookieStore = CookieStore;
|
|
102
|
+
function parseRawCookie(header) {
|
|
103
|
+
const pairs = header.split(';').filter(s => s.trim().length > 0).map(p => {
|
|
104
|
+
let key = '';
|
|
105
|
+
let value = '';
|
|
106
|
+
const separatorPos = p.indexOf('=');
|
|
107
|
+
if (separatorPos === -1) {
|
|
108
|
+
// If only a key is specified, the value is left undefined.
|
|
109
|
+
key = p.trim();
|
|
110
|
+
} else {
|
|
111
|
+
// Otherwise we assume that the key is the element before the first `=`
|
|
112
|
+
key = p.slice(0, separatorPos).trim();
|
|
113
|
+
// And the value is the rest of the string.
|
|
114
|
+
value = p.slice(separatorPos + 1).trim();
|
|
115
|
+
}
|
|
116
|
+
return [key, value];
|
|
117
|
+
});
|
|
118
|
+
if (!pairs.length) return null;
|
|
119
|
+
const [name, value] = pairs[0];
|
|
120
|
+
const cookie = {
|
|
121
|
+
name,
|
|
122
|
+
value
|
|
123
|
+
};
|
|
124
|
+
for (let i = 1; i < pairs.length; i++) {
|
|
125
|
+
const [name, value] = pairs[i];
|
|
126
|
+
switch (name.toLowerCase()) {
|
|
127
|
+
case 'expires':
|
|
128
|
+
const expiresMs = +new Date(value);
|
|
129
|
+
// https://datatracker.ietf.org/doc/html/rfc6265#section-5.2.1
|
|
130
|
+
if (isFinite(expiresMs)) {
|
|
131
|
+
if (expiresMs <= 0) cookie.expires = 0;else cookie.expires = Math.min(expiresMs / 1000, _network.kMaxCookieExpiresDateInSeconds);
|
|
132
|
+
}
|
|
133
|
+
break;
|
|
134
|
+
case 'max-age':
|
|
135
|
+
const maxAgeSec = parseInt(value, 10);
|
|
136
|
+
if (isFinite(maxAgeSec)) {
|
|
137
|
+
// From https://datatracker.ietf.org/doc/html/rfc6265#section-5.2.2
|
|
138
|
+
// If delta-seconds is less than or equal to zero (0), let expiry-time
|
|
139
|
+
// be the earliest representable date and time.
|
|
140
|
+
if (maxAgeSec <= 0) cookie.expires = 0;else cookie.expires = Math.min(Date.now() / 1000 + maxAgeSec, _network.kMaxCookieExpiresDateInSeconds);
|
|
141
|
+
}
|
|
142
|
+
break;
|
|
143
|
+
case 'domain':
|
|
144
|
+
cookie.domain = value.toLocaleLowerCase() || '';
|
|
145
|
+
if (cookie.domain && !cookie.domain.startsWith('.') && cookie.domain.includes('.')) cookie.domain = '.' + cookie.domain;
|
|
146
|
+
break;
|
|
147
|
+
case 'path':
|
|
148
|
+
cookie.path = value || '';
|
|
149
|
+
break;
|
|
150
|
+
case 'secure':
|
|
151
|
+
cookie.secure = true;
|
|
152
|
+
break;
|
|
153
|
+
case 'httponly':
|
|
154
|
+
cookie.httpOnly = true;
|
|
155
|
+
break;
|
|
156
|
+
case 'samesite':
|
|
157
|
+
switch (value.toLowerCase()) {
|
|
158
|
+
case 'none':
|
|
159
|
+
cookie.sameSite = 'None';
|
|
160
|
+
break;
|
|
161
|
+
case 'lax':
|
|
162
|
+
cookie.sameSite = 'Lax';
|
|
163
|
+
break;
|
|
164
|
+
case 'strict':
|
|
165
|
+
cookie.sameSite = 'Strict';
|
|
166
|
+
break;
|
|
167
|
+
}
|
|
168
|
+
break;
|
|
169
|
+
}
|
|
170
|
+
}
|
|
171
|
+
return cookie;
|
|
172
|
+
}
|
|
100
173
|
function domainMatches(value, domain) {
|
|
101
174
|
if (value === domain) return true;
|
|
102
175
|
// Only strict match is allowed if domain doesn't start with '.' (host-only-flag is true in the spec)
|
|
@@ -9,6 +9,7 @@ var _instrumentation = require("./instrumentation");
|
|
|
9
9
|
var _recorder = require("./recorder");
|
|
10
10
|
var _recorderApp = require("./recorder/recorderApp");
|
|
11
11
|
var _utils = require("../utils");
|
|
12
|
+
var _ariaSnapshot = require("./ariaSnapshot");
|
|
12
13
|
/**
|
|
13
14
|
* Copyright (c) Microsoft Corporation.
|
|
14
15
|
*
|
|
@@ -118,8 +119,10 @@ class DebugController extends _instrumentation.SdkObject {
|
|
|
118
119
|
};
|
|
119
120
|
this._autoCloseTimer = setTimeout(heartBeat, 30000);
|
|
120
121
|
}
|
|
121
|
-
async highlight(
|
|
122
|
-
for (const recorder of await this._allRecorders())
|
|
122
|
+
async highlight(params) {
|
|
123
|
+
for (const recorder of await this._allRecorders()) {
|
|
124
|
+
if (params.ariaTemplate) recorder.setHighlightedAriaTemplate((0, _ariaSnapshot.parseYamlForAriaSnapshot)(params.ariaTemplate));else if (params.selector) recorder.setHighlightedSelector(this._sdkLanguage, params.selector);
|
|
125
|
+
}
|
|
123
126
|
}
|
|
124
127
|
async hideHighlight() {
|
|
125
128
|
// Hide all active recorder highlights.
|
|
@@ -165,9 +168,9 @@ class DebugController extends _instrumentation.SdkObject {
|
|
|
165
168
|
async _allRecorders() {
|
|
166
169
|
const contexts = new Set();
|
|
167
170
|
for (const page of this._playwright.allPages()) contexts.add(page.context());
|
|
168
|
-
const result = await Promise.all([...contexts].map(c => _recorder.Recorder.
|
|
171
|
+
const result = await Promise.all([...contexts].map(c => _recorder.Recorder.showInspector(c, {
|
|
169
172
|
omitCallTracking: true
|
|
170
|
-
})));
|
|
173
|
+
}, () => Promise.resolve(new InspectingRecorderApp(this)))));
|
|
171
174
|
return result.filter(Boolean);
|
|
172
175
|
}
|
|
173
176
|
async _closeBrowsersWithoutPages() {
|
|
@@ -197,10 +200,10 @@ class InspectingRecorderApp extends _recorderApp.EmptyRecorderApp {
|
|
|
197
200
|
this._debugController = void 0;
|
|
198
201
|
this._debugController = debugController;
|
|
199
202
|
}
|
|
200
|
-
async
|
|
201
|
-
const locator = (0, _utils.asLocator)(this._debugController._sdkLanguage, selector);
|
|
203
|
+
async elementPicked(elementInfo) {
|
|
204
|
+
const locator = (0, _utils.asLocator)(this._debugController._sdkLanguage, elementInfo.selector);
|
|
202
205
|
this._debugController.emit(DebugController.Events.InspectRequested, {
|
|
203
|
-
selector,
|
|
206
|
+
selector: elementInfo.selector,
|
|
204
207
|
locator
|
|
205
208
|
});
|
|
206
209
|
}
|