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.
Files changed (156) hide show
  1. package/README.md +1 -1
  2. package/ThirdPartyNotices.txt +70 -384
  3. package/bin/reinstall_chrome_beta_linux.sh +10 -8
  4. package/bin/reinstall_chrome_stable_linux.sh +10 -8
  5. package/bin/reinstall_msedge_beta_linux.sh +11 -9
  6. package/bin/reinstall_msedge_dev_linux.sh +11 -9
  7. package/bin/reinstall_msedge_stable_linux.sh +11 -9
  8. package/browsers.json +24 -12
  9. package/lib/cli/program.js +49 -19
  10. package/lib/client/api.js +6 -0
  11. package/lib/client/browserContext.js +20 -2
  12. package/lib/client/channelOwner.js +6 -2
  13. package/lib/client/connection.js +3 -0
  14. package/lib/client/fetch.js +16 -21
  15. package/lib/client/jsHandle.js +0 -8
  16. package/lib/client/localUtils.js +1 -0
  17. package/lib/client/locator.js +7 -0
  18. package/lib/client/network.js +180 -17
  19. package/lib/client/page.js +25 -2
  20. package/lib/client/playwright.js +6 -3
  21. package/lib/client/tracing.js +29 -20
  22. package/lib/client/waiter.js +15 -11
  23. package/lib/generated/consoleApiSource.js +1 -1
  24. package/lib/generated/injectedScriptSource.js +1 -1
  25. package/lib/generated/pollingRecorderSource.js +7 -0
  26. package/lib/generated/webSocketMockSource.js +7 -0
  27. package/lib/protocol/validator.js +103 -17
  28. package/lib/server/ariaSnapshot.js +33 -0
  29. package/lib/server/bidi/bidiBrowser.js +23 -8
  30. package/lib/server/bidi/bidiChromium.js +124 -0
  31. package/lib/server/bidi/bidiConnection.js +1 -3
  32. package/lib/server/bidi/bidiExecutionContext.js +0 -6
  33. package/lib/server/bidi/bidiFirefox.js +15 -21
  34. package/lib/server/bidi/bidiInput.js +16 -32
  35. package/lib/server/bidi/bidiNetworkManager.js +39 -5
  36. package/lib/server/bidi/bidiOverCdp.js +103 -0
  37. package/lib/server/bidi/bidiPage.js +98 -25
  38. package/lib/server/bidi/bidiPdf.js +140 -0
  39. package/lib/server/bidi/third_party/firefoxPrefs.js +221 -0
  40. package/lib/server/browser.js +13 -2
  41. package/lib/server/browserContext.js +6 -23
  42. package/lib/server/browserType.js +43 -12
  43. package/lib/server/chromium/chromium.js +9 -17
  44. package/lib/server/chromium/chromiumSwitches.js +3 -1
  45. package/lib/server/chromium/crBrowser.js +4 -3
  46. package/lib/server/chromium/crConnection.js +153 -0
  47. package/lib/server/chromium/crExecutionContext.js +0 -20
  48. package/lib/server/chromium/crPage.js +13 -7
  49. package/lib/server/chromium/videoRecorder.js +1 -1
  50. package/lib/server/codegen/csharp.js +12 -17
  51. package/lib/server/codegen/java.js +5 -8
  52. package/lib/server/codegen/javascript.js +29 -7
  53. package/lib/server/codegen/language.js +14 -2
  54. package/lib/server/codegen/python.js +13 -17
  55. package/lib/server/cookieStore.js +73 -0
  56. package/lib/server/debugController.js +10 -7
  57. package/lib/server/deviceDescriptorsSource.json +127 -127
  58. package/lib/server/dialog.js +1 -0
  59. package/lib/server/dispatchers/androidDispatcher.js +13 -2
  60. package/lib/server/dispatchers/browserContextDispatcher.js +19 -2
  61. package/lib/server/dispatchers/debugControllerDispatcher.js +1 -1
  62. package/lib/server/dispatchers/dispatcher.js +1 -1
  63. package/lib/server/dispatchers/frameDispatcher.js +8 -1
  64. package/lib/server/dispatchers/jsHandleDispatcher.js +0 -5
  65. package/lib/server/dispatchers/pageDispatcher.js +9 -0
  66. package/lib/server/dispatchers/playwrightDispatcher.js +2 -1
  67. package/lib/server/dispatchers/tracingDispatcher.js +10 -0
  68. package/lib/server/dispatchers/webSocketRouteDispatcher.js +189 -0
  69. package/lib/server/dom.js +79 -36
  70. package/lib/server/download.js +9 -2
  71. package/lib/server/fetch.js +105 -96
  72. package/lib/server/firefox/ffBrowser.js +6 -4
  73. package/lib/server/firefox/ffExecutionContext.js +0 -15
  74. package/lib/server/firefox/ffNetworkManager.js +1 -1
  75. package/lib/server/firefox/ffPage.js +6 -3
  76. package/lib/server/firefox/firefox.js +2 -13
  77. package/lib/server/frameSelectors.js +1 -1
  78. package/lib/server/frames.js +68 -67
  79. package/lib/server/har/harTracer.js +11 -0
  80. package/lib/server/index.js +0 -6
  81. package/lib/server/input.js +0 -1
  82. package/lib/server/javascript.js +0 -13
  83. package/lib/server/page.js +53 -9
  84. package/lib/server/playwright.js +5 -2
  85. package/lib/server/progress.js +0 -3
  86. package/lib/server/recorder/contextRecorder.js +33 -50
  87. package/lib/server/recorder/recorderApp.js +22 -25
  88. package/lib/server/recorder/recorderCollection.js +68 -79
  89. package/lib/server/recorder/recorderInTraceViewer.js +144 -0
  90. package/lib/server/recorder/recorderRunner.js +75 -97
  91. package/lib/server/recorder/recorderUtils.js +48 -6
  92. package/lib/server/recorder.js +87 -47
  93. package/lib/server/registry/index.js +154 -26
  94. package/lib/server/registry/nativeDeps.js +6 -3
  95. package/lib/server/socksClientCertificatesInterceptor.js +15 -3
  96. package/lib/server/trace/recorder/snapshotter.js +2 -12
  97. package/lib/server/trace/recorder/snapshotterInjected.js +21 -3
  98. package/lib/server/trace/recorder/tracing.js +127 -17
  99. package/lib/server/trace/test/inMemorySnapshotter.js +4 -4
  100. package/lib/server/trace/viewer/traceViewer.js +4 -6
  101. package/lib/server/webkit/webkit.js +1 -1
  102. package/lib/server/webkit/wkBrowser.js +8 -7
  103. package/lib/server/webkit/wkExecutionContext.js +0 -16
  104. package/lib/server/webkit/wkPage.js +11 -4
  105. package/lib/utils/comparators.js +12 -30
  106. package/lib/utils/crypto.js +1 -4
  107. package/lib/utils/happy-eyeballs.js +13 -0
  108. package/lib/utils/hostPlatform.js +8 -2
  109. package/lib/utils/httpServer.js +1 -0
  110. package/lib/utils/isomorphic/ariaSnapshot.js +267 -0
  111. package/lib/utils/isomorphic/locatorGenerators.js +30 -21
  112. package/lib/utils/isomorphic/locatorParser.js +2 -2
  113. package/lib/utils/isomorphic/recorderUtils.js +227 -0
  114. package/lib/utils/isomorphic/stringUtils.js +30 -0
  115. package/lib/utils/isomorphic/urlMatch.js +5 -1
  116. package/lib/utils/network.js +1 -1
  117. package/lib/utils/sequence.js +64 -0
  118. package/lib/utils/stackTrace.js +16 -3
  119. package/lib/utils/zones.js +32 -23
  120. package/lib/utilsBundle.js +4 -5
  121. package/lib/utilsBundleImpl/index.js +190 -33
  122. package/lib/vite/htmlReport/index.html +19 -16
  123. package/lib/vite/recorder/assets/codeMirrorModule-AFvV6hAs.js +24 -0
  124. package/lib/vite/recorder/assets/index-_cTWgVuJ.js +184 -0
  125. package/lib/vite/recorder/assets/{index-B-MT5gKo.css → index-iA1aAGZg.css} +1 -1
  126. package/lib/vite/recorder/index.html +2 -2
  127. package/lib/vite/traceViewer/assets/codeMirrorModule-KatbITLF.js +24 -0
  128. package/lib/vite/traceViewer/assets/inspectorTab-DdpLd2bb.js +68 -0
  129. package/lib/vite/traceViewer/assets/workbench-CdYbzWFD.js +9 -0
  130. package/lib/vite/traceViewer/embedded.6m3UZh7r.js +2 -0
  131. package/lib/vite/traceViewer/embedded.html +5 -3
  132. package/lib/vite/traceViewer/index.WUV-8boJ.js +2 -0
  133. package/lib/vite/traceViewer/index.html +24 -5
  134. package/lib/vite/traceViewer/inspectorTab.DEOUW62d.css +1 -0
  135. package/lib/vite/traceViewer/recorder.B_SY1GJM.css +0 -0
  136. package/lib/vite/traceViewer/recorder.OT2tVHgn.js +2 -0
  137. package/lib/vite/traceViewer/recorder.html +17 -0
  138. package/lib/vite/traceViewer/sw.bundle.js +3 -3
  139. package/lib/vite/traceViewer/uiMode.BZBTyvGn.js +5 -0
  140. package/lib/vite/traceViewer/uiMode.html +6 -4
  141. package/lib/vite/traceViewer/{uiMode.D3cNFP6u.css → uiMode.voC1ZiOQ.css} +1 -1
  142. package/lib/vite/traceViewer/workbench.C-zR9ysA.css +1 -0
  143. package/package.json +1 -1
  144. package/types/protocol.d.ts +135 -16
  145. package/types/types.d.ts +2149 -965
  146. package/lib/generated/recorderSource.js +0 -7
  147. package/lib/third_party/diff_match_patch.js +0 -2222
  148. package/lib/vite/recorder/assets/codeMirrorModule-aW6Vz3gf.js +0 -24
  149. package/lib/vite/recorder/assets/index-DCpXOVIh.js +0 -47
  150. package/lib/vite/traceViewer/assets/codeMirrorModule-eFKCUIu2.js +0 -24
  151. package/lib/vite/traceViewer/assets/workbench-D0cirtfA.js +0 -72
  152. package/lib/vite/traceViewer/embedded.BkuvsLJp.js +0 -2
  153. package/lib/vite/traceViewer/index.C968kAiC.js +0 -2
  154. package/lib/vite/traceViewer/uiMode.BkFnr4V0.js +0 -10
  155. package/lib/vite/traceViewer/workbench.DjbIuxix.css +0 -1
  156. /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.toClickOptions)(action);
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 > 1 ? action.options : action.options[0])});`;
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.toClickOptions)(action);
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 > 1 ? action.options : action.options[0])});`;
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
- this._lines = text.trim().split('\n').map(line => line.trim()).concat(this._lines);
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
- this._lines.push(...text.trim().split('\n').map(line => line.trim()));
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.toClickOptions = toClickOptions;
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 toClickOptions(action) {
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.toClickOptions)(action);
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(convertContextOptions(options), false, asDict);
225
- return `**playwright.devices[${quote(deviceName)}]` + formatOptions(convertContextOptions((0, _language.sanitizeDeviceOptions)(device, options)), true, asDict);
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(selector) {
122
- for (const recorder of await this._allRecorders()) recorder.setHighlightedSelector(this._sdkLanguage, selector);
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.show(c, () => Promise.resolve(new InspectingRecorderApp(this)), {
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 setSelector(selector) {
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
  }