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
@@ -5,7 +5,9 @@ Object.defineProperty(exports, "__esModule", {
5
5
  });
6
6
  exports.getComparator = getComparator;
7
7
  var _utilsBundle = require("../utilsBundle");
8
+ var _pixelmatch = _interopRequireDefault(require("../third_party/pixelmatch"));
8
9
  var _compare = require("../image_tools/compare");
10
+ function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
9
11
  /**
10
12
  * Copyright 2017 Google Inc. All rights reserved.
11
13
  * Modifications copyright (c) Microsoft Corporation.
@@ -23,13 +25,8 @@ var _compare = require("../image_tools/compare");
23
25
  * limitations under the License.
24
26
  */
25
27
 
26
- const pixelmatch = require('../third_party/pixelmatch');
27
- const {
28
- diff_match_patch,
29
- DIFF_INSERT,
30
- DIFF_DELETE,
31
- DIFF_EQUAL
32
- } = require('../third_party/diff_match_patch');
28
+ // @ts-ignore
29
+
33
30
  function getComparator(mimeType) {
34
31
  if (mimeType === 'image/png') return compareImages.bind(null, 'image/png');
35
32
  if (mimeType === 'image/jpeg') return compareImages.bind(null, 'image/jpeg');
@@ -83,7 +80,7 @@ function compareImages(mimeType, actualBuffer, expectedBuffer, options = {}) {
83
80
  });
84
81
  } else if (((_options$comparator = options.comparator) !== null && _options$comparator !== void 0 ? _options$comparator : 'pixelmatch') === 'pixelmatch') {
85
82
  var _options$threshold;
86
- count = pixelmatch(expected.data, actual.data, diff.data, size.width, size.height, {
83
+ count = (0, _pixelmatch.default)(expected.data, actual.data, diff.data, size.width, size.height, {
87
84
  threshold: (_options$threshold = options.threshold) !== null && _options$threshold !== void 0 ? _options$threshold : 0.2
88
85
  });
89
86
  } else {
@@ -116,32 +113,17 @@ function compareText(actual, expectedBuffer) {
116
113
  };
117
114
  const expected = expectedBuffer.toString('utf-8');
118
115
  if (expected === actual) return null;
119
- const dmp = new diff_match_patch();
120
- const d = dmp.diff_main(expected, actual);
121
- dmp.diff_cleanupSemantic(d);
116
+ const diffs = _utilsBundle.diff.diffChars(expected, actual);
122
117
  return {
123
- errorMessage: diff_prettyTerminal(d)
118
+ errorMessage: diff_prettyTerminal(diffs)
124
119
  };
125
120
  }
126
121
  function diff_prettyTerminal(diffs) {
127
- const html = [];
128
- for (let x = 0; x < diffs.length; x++) {
129
- const op = diffs[x][0]; // Operation (insert, delete, equal)
130
- const data = diffs[x][1]; // Text of change.
131
- const text = data;
132
- switch (op) {
133
- case DIFF_INSERT:
134
- html[x] = _utilsBundle.colors.green(text);
135
- break;
136
- case DIFF_DELETE:
137
- html[x] = _utilsBundle.colors.reset(_utilsBundle.colors.strikethrough(_utilsBundle.colors.red(text)));
138
- break;
139
- case DIFF_EQUAL:
140
- html[x] = text;
141
- break;
142
- }
143
- }
144
- return html.join('');
122
+ const result = diffs.map(part => {
123
+ const text = part.value;
124
+ if (part.added) return _utilsBundle.colors.green(text);else if (part.removed) return _utilsBundle.colors.reset(_utilsBundle.colors.strikethrough(_utilsBundle.colors.red(text)));else return text;
125
+ });
126
+ return result.join('');
145
127
  }
146
128
  function resizeImage(image, size) {
147
129
  if (image.width === size.width && image.height === size.height) return image;
@@ -45,7 +45,6 @@ function encodeBase128(value) {
45
45
  } while (value > 0);
46
46
  return Buffer.from(bytes.reverse());
47
47
  }
48
- ;
49
48
 
50
49
  // ASN1/DER Speficiation: https://www.itu.int/rec/T-REC-X.680-X.693-202102-I/en
51
50
  class DER {
@@ -60,9 +59,7 @@ class DER {
60
59
  const parts = oid.split('.').map(v => Number(v));
61
60
  // Encode the second part, which could be large, using base-128 encoding if necessary
62
61
  const output = [encodeBase128(40 * parts[0] + parts[1])];
63
- for (let i = 2; i < parts.length; i++) {
64
- output.push(encodeBase128(parts[i]));
65
- }
62
+ for (let i = 2; i < parts.length; i++) output.push(encodeBase128(parts[i]));
66
63
  return this._encode(0x06, Buffer.concat(output));
67
64
  }
68
65
  static encodeNull() {
@@ -7,6 +7,7 @@ exports.createConnectionAsync = createConnectionAsync;
7
7
  exports.createSocket = createSocket;
8
8
  exports.createTLSSocket = createTLSSocket;
9
9
  exports.httpsHappyEyeballsAgent = exports.httpHappyEyeballsAgent = void 0;
10
+ exports.timingForSocket = timingForSocket;
10
11
  var dns = _interopRequireWildcard(require("dns"));
11
12
  var http = _interopRequireWildcard(require("http"));
12
13
  var https = _interopRequireWildcard(require("https"));
@@ -14,6 +15,7 @@ var net = _interopRequireWildcard(require("net"));
14
15
  var tls = _interopRequireWildcard(require("tls"));
15
16
  var _manualPromise = require("./manualPromise");
16
17
  var _debug = require("./debug");
18
+ var _time = require("./time");
17
19
  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); }
18
20
  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; }
19
21
  /**
@@ -37,6 +39,8 @@ function _interopRequireWildcard(e, r) { if (!r && e && e.__esModule) return e;
37
39
 
38
40
  // Same as in Chromium (https://source.chromium.org/chromium/chromium/src/+/5666ff4f5077a7e2f72902f3a95f5d553ea0d88d:net/socket/transport_connect_job.cc;l=102)
39
41
  const connectionAttemptDelayMs = 300;
42
+ const kDNSLookupAt = Symbol('kDNSLookupAt');
43
+ const kTCPConnectionAt = Symbol('kTCPConnectionAt');
40
44
  class HttpHappyEyeballsAgent extends http.Agent {
41
45
  createConnection(options, oncreate) {
42
46
  // There is no ambiguity in case of IP address.
@@ -101,6 +105,7 @@ async function createConnectionAsync(options, oncreate, useTLS) {
101
105
  const lookup = options.__testHookLookup || lookupAddresses;
102
106
  const hostname = clientRequestArgsToHostName(options);
103
107
  const addresses = await lookup(hostname);
108
+ const dnsLookupAt = (0, _time.monotonicTime)();
104
109
  const sockets = new Set();
105
110
  let firstError;
106
111
  let errorCount = 0;
@@ -125,10 +130,12 @@ async function createConnectionAsync(options, oncreate, useTLS) {
125
130
  port: options.port,
126
131
  host: address
127
132
  });
133
+ socket[kDNSLookupAt] = dnsLookupAt;
128
134
 
129
135
  // Each socket may fire only one of 'connect', 'timeout' or 'error' events.
130
136
  // None of these events are fired after socket.destroy() is called.
131
137
  socket.on('connect', () => {
138
+ socket[kTCPConnectionAt] = (0, _time.monotonicTime)();
132
139
  connected.resolve();
133
140
  oncreate === null || oncreate === void 0 || oncreate(null, socket);
134
141
  // TODO: Cache the result?
@@ -178,4 +185,10 @@ function clientRequestArgsToHostName(options) {
178
185
  if (options.hostname) return options.hostname;
179
186
  if (options.host) return options.host;
180
187
  throw new Error('Either options.hostname or options.host must be provided');
188
+ }
189
+ function timingForSocket(socket) {
190
+ return {
191
+ dnsLookupAt: socket[kDNSLookupAt],
192
+ tcpConnectionAt: socket[kTCPConnectionAt]
193
+ };
181
194
  }
@@ -24,6 +24,12 @@ function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { de
24
24
  */
25
25
 
26
26
  function calculatePlatform() {
27
+ if (process.env.PLAYWRIGHT_HOST_PLATFORM_OVERRIDE) {
28
+ return {
29
+ hostPlatform: process.env.PLAYWRIGHT_HOST_PLATFORM_OVERRIDE,
30
+ isOfficiallySupportedPlatform: false
31
+ };
32
+ }
27
33
  const platform = _os.default.platform();
28
34
  if (platform === 'darwin') {
29
35
  const ver = _os.default.release().split('.').map(a => parseInt(a, 10));
@@ -37,9 +43,9 @@ function calculatePlatform() {
37
43
  macVersion = 'mac10.15';
38
44
  } else {
39
45
  // ver[0] >= 20
40
- const LAST_STABLE_MAC_MAJOR_VERSION = 14;
46
+ const LAST_STABLE_MACOS_MAJOR_VERSION = 15;
41
47
  // Best-effort support for MacOS beta versions.
42
- macVersion = 'mac' + Math.min(ver[0] - 9, LAST_STABLE_MAC_MAJOR_VERSION);
48
+ macVersion = 'mac' + Math.min(ver[0] - 9, LAST_STABLE_MACOS_MAJOR_VERSION);
43
49
  // BigSur is the first version that might run on Apple Silicon.
44
50
  if (_os.default.cpus().some(cpu => cpu.model.includes('Apple'))) macVersion += '-arm64';
45
51
  }
@@ -76,6 +76,7 @@ class HttpServer {
76
76
  path: '/' + this._wsGuid
77
77
  });
78
78
  wss.on('connection', ws => {
79
+ transport.onconnect();
79
80
  transport.sendEvent = (method, params) => ws.send(JSON.stringify({
80
81
  method,
81
82
  params
@@ -0,0 +1,267 @@
1
+ "use strict";
2
+
3
+ Object.defineProperty(exports, "__esModule", {
4
+ value: true
5
+ });
6
+ exports.AriaKeyError = void 0;
7
+ exports.parseAriaKey = parseAriaKey;
8
+ exports.parseYamlTemplate = parseYamlTemplate;
9
+ /**
10
+ * Copyright (c) Microsoft Corporation.
11
+ *
12
+ * Licensed under the Apache License, Version 2.0 (the "License");
13
+ * you may not use this file except in compliance with the License.
14
+ * You may obtain a copy of the License at
15
+ *
16
+ * http://www.apache.org/licenses/LICENSE-2.0
17
+ *
18
+ * Unless required by applicable law or agreed to in writing, software
19
+ * distributed under the License is distributed on an "AS IS" BASIS,
20
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
21
+ * See the License for the specific language governing permissions and
22
+ * limitations under the License.
23
+ */
24
+
25
+ // https://www.w3.org/TR/wai-aria-1.2/#role_definitions
26
+
27
+ function parseYamlTemplate(fragment) {
28
+ const result = {
29
+ kind: 'role',
30
+ role: 'fragment'
31
+ };
32
+ populateNode(result, fragment);
33
+ if (result.children && result.children.length === 1) return result.children[0];
34
+ return result;
35
+ }
36
+ function populateNode(node, container) {
37
+ for (const object of container) {
38
+ if (typeof object === 'string') {
39
+ const childNode = KeyParser.parse(object);
40
+ node.children = node.children || [];
41
+ node.children.push(childNode);
42
+ continue;
43
+ }
44
+ for (const key of Object.keys(object)) {
45
+ node.children = node.children || [];
46
+ const value = object[key];
47
+ if (key === 'text') {
48
+ node.children.push({
49
+ kind: 'text',
50
+ text: valueOrRegex(value)
51
+ });
52
+ continue;
53
+ }
54
+ const childNode = KeyParser.parse(key);
55
+ if (childNode.kind === 'text') {
56
+ node.children.push({
57
+ kind: 'text',
58
+ text: valueOrRegex(value)
59
+ });
60
+ continue;
61
+ }
62
+ if (typeof value === 'string') {
63
+ node.children.push({
64
+ ...childNode,
65
+ children: [{
66
+ kind: 'text',
67
+ text: valueOrRegex(value)
68
+ }]
69
+ });
70
+ continue;
71
+ }
72
+ node.children.push(childNode);
73
+ populateNode(childNode, value);
74
+ }
75
+ }
76
+ }
77
+ function normalizeWhitespace(text) {
78
+ return text.replace(/[\r\n\s\t]+/g, ' ').trim();
79
+ }
80
+ function valueOrRegex(value) {
81
+ return value.startsWith('/') && value.endsWith('/') ? new RegExp(value.slice(1, -1)) : normalizeWhitespace(value);
82
+ }
83
+ class KeyParser {
84
+ static parse(input) {
85
+ return new KeyParser(input)._parse();
86
+ }
87
+ constructor(input) {
88
+ this._input = void 0;
89
+ this._pos = void 0;
90
+ this._length = void 0;
91
+ this._input = input;
92
+ this._pos = 0;
93
+ this._length = input.length;
94
+ }
95
+ _peek() {
96
+ return this._input[this._pos] || '';
97
+ }
98
+ _next() {
99
+ if (this._pos < this._length) return this._input[this._pos++];
100
+ return null;
101
+ }
102
+ _eof() {
103
+ return this._pos >= this._length;
104
+ }
105
+ _isWhitespace() {
106
+ return !this._eof() && /\s/.test(this._peek());
107
+ }
108
+ _skipWhitespace() {
109
+ while (this._isWhitespace()) this._pos++;
110
+ }
111
+ _readIdentifier(type) {
112
+ if (this._eof()) this._throwError(`Unexpected end of input when expecting ${type}`);
113
+ const start = this._pos;
114
+ while (!this._eof() && /[a-zA-Z]/.test(this._peek())) this._pos++;
115
+ return this._input.slice(start, this._pos);
116
+ }
117
+ _readString() {
118
+ let result = '';
119
+ let escaped = false;
120
+ while (!this._eof()) {
121
+ const ch = this._next();
122
+ if (escaped) {
123
+ result += ch;
124
+ escaped = false;
125
+ } else if (ch === '\\') {
126
+ escaped = true;
127
+ } else if (ch === '"') {
128
+ return result;
129
+ } else {
130
+ result += ch;
131
+ }
132
+ }
133
+ this._throwError('Unterminated string');
134
+ }
135
+ _throwError(message, pos) {
136
+ throw new AriaKeyError(message, this._input, pos || this._pos);
137
+ }
138
+ _readRegex() {
139
+ let result = '';
140
+ let escaped = false;
141
+ let insideClass = false;
142
+ while (!this._eof()) {
143
+ const ch = this._next();
144
+ if (escaped) {
145
+ result += ch;
146
+ escaped = false;
147
+ } else if (ch === '\\') {
148
+ escaped = true;
149
+ result += ch;
150
+ } else if (ch === '/' && !insideClass) {
151
+ return result;
152
+ } else if (ch === '[') {
153
+ insideClass = true;
154
+ result += ch;
155
+ } else if (ch === ']' && insideClass) {
156
+ result += ch;
157
+ insideClass = false;
158
+ } else {
159
+ result += ch;
160
+ }
161
+ }
162
+ this._throwError('Unterminated regex');
163
+ }
164
+ _readStringOrRegex() {
165
+ const ch = this._peek();
166
+ if (ch === '"') {
167
+ this._next();
168
+ return this._readString();
169
+ }
170
+ if (ch === '/') {
171
+ this._next();
172
+ return new RegExp(this._readRegex());
173
+ }
174
+ return null;
175
+ }
176
+ _readAttributes(result) {
177
+ let errorPos = this._pos;
178
+ while (true) {
179
+ this._skipWhitespace();
180
+ if (this._peek() === '[') {
181
+ this._next();
182
+ this._skipWhitespace();
183
+ errorPos = this._pos;
184
+ const flagName = this._readIdentifier('attribute');
185
+ this._skipWhitespace();
186
+ let flagValue = '';
187
+ if (this._peek() === '=') {
188
+ this._next();
189
+ this._skipWhitespace();
190
+ errorPos = this._pos;
191
+ while (this._peek() !== ']' && !this._isWhitespace() && !this._eof()) flagValue += this._next();
192
+ }
193
+ this._skipWhitespace();
194
+ if (this._peek() !== ']') this._throwError('Expected ]');
195
+ this._next(); // Consume ']'
196
+ this._applyAttribute(result, flagName, flagValue || 'true', errorPos);
197
+ } else {
198
+ break;
199
+ }
200
+ }
201
+ }
202
+ _parse() {
203
+ this._skipWhitespace();
204
+ const role = this._readIdentifier('role');
205
+ this._skipWhitespace();
206
+ const name = this._readStringOrRegex() || '';
207
+ const result = {
208
+ kind: 'role',
209
+ role,
210
+ name
211
+ };
212
+ this._readAttributes(result);
213
+ this._skipWhitespace();
214
+ if (!this._eof()) this._throwError('Unexpected input');
215
+ return result;
216
+ }
217
+ _applyAttribute(node, key, value, errorPos) {
218
+ if (key === 'checked') {
219
+ this._assert(value === 'true' || value === 'false' || value === 'mixed', 'Value of "checked\" attribute must be a boolean or "mixed"', errorPos);
220
+ node.checked = value === 'true' ? true : value === 'false' ? false : 'mixed';
221
+ return;
222
+ }
223
+ if (key === 'disabled') {
224
+ this._assert(value === 'true' || value === 'false', 'Value of "disabled" attribute must be a boolean', errorPos);
225
+ node.disabled = value === 'true';
226
+ return;
227
+ }
228
+ if (key === 'expanded') {
229
+ this._assert(value === 'true' || value === 'false', 'Value of "expanded" attribute must be a boolean', errorPos);
230
+ node.expanded = value === 'true';
231
+ return;
232
+ }
233
+ if (key === 'level') {
234
+ this._assert(!isNaN(Number(value)), 'Value of "level" attribute must be a number', errorPos);
235
+ node.level = Number(value);
236
+ return;
237
+ }
238
+ if (key === 'pressed') {
239
+ this._assert(value === 'true' || value === 'false' || value === 'mixed', 'Value of "pressed" attribute must be a boolean or "mixed"', errorPos);
240
+ node.pressed = value === 'true' ? true : value === 'false' ? false : 'mixed';
241
+ return;
242
+ }
243
+ if (key === 'selected') {
244
+ this._assert(value === 'true' || value === 'false', 'Value of "selected" attribute must be a boolean', errorPos);
245
+ node.selected = value === 'true';
246
+ return;
247
+ }
248
+ this._assert(false, `Unsupported attribute [${key}]`, errorPos);
249
+ }
250
+ _assert(value, message, valuePos) {
251
+ if (!value) this._throwError(message || 'Assertion error', valuePos);
252
+ }
253
+ }
254
+ function parseAriaKey(key) {
255
+ return KeyParser.parse(key);
256
+ }
257
+ class AriaKeyError extends Error {
258
+ constructor(message, input, pos) {
259
+ super(message + ':\n\n' + input + '\n' + ' '.repeat(pos) + '^\n');
260
+ this.shortMessage = void 0;
261
+ this.pos = void 0;
262
+ this.shortMessage = message;
263
+ this.pos = pos;
264
+ this.stack = undefined;
265
+ }
266
+ }
267
+ exports.AriaKeyError = AriaKeyError;
@@ -37,15 +37,6 @@ function asLocators(lang, selector, isFrameLocator = false, maxOutputSize = 20,
37
37
  }
38
38
  function innerAsLocators(factory, parsed, isFrameLocator = false, maxOutputSize = 20) {
39
39
  const parts = [...parsed.parts];
40
- // frameLocator('iframe').first is actually "iframe >> nth=0 >> internal:control=enter-frame"
41
- // To make it easier to parse, we turn it into "iframe >> internal:control=enter-frame >> nth=0"
42
- for (let index = 0; index < parts.length - 1; index++) {
43
- if (parts[index].name === 'nth' && parts[index + 1].name === 'internal:control' && parts[index + 1].body === 'enter-frame') {
44
- // Swap nth and enter-frame.
45
- const [nth] = parts.splice(index, 1);
46
- parts.splice(index + 1, 0, nth);
47
- }
48
- }
49
40
  const tokens = [];
50
41
  let nextBase = isFrameLocator ? 'frame-locator' : 'page';
51
42
  for (let index = 0; index < parts.length; index++) {
@@ -183,18 +174,28 @@ function innerAsLocators(factory, parsed, isFrameLocator = false, maxOutputSize
183
174
  continue;
184
175
  }
185
176
  }
186
- let locatorType = 'default';
187
- const nextPart = parts[index + 1];
188
- if (nextPart && nextPart.name === 'internal:control' && nextPart.body === 'enter-frame') {
189
- locatorType = 'frame';
177
+ if (part.name === 'internal:control' && part.body === 'enter-frame') {
178
+ // transform last tokens from `${selector}` into `${selector}.contentFrame()` and `frameLocator(${selector})`
179
+ const lastTokens = tokens[tokens.length - 1];
180
+ const lastPart = parts[index - 1];
181
+ const transformed = lastTokens.map(token => factory.chainLocators([token, factory.generateLocator(base, 'frame', '')]));
182
+ if (['xpath', 'css'].includes(lastPart.name)) {
183
+ transformed.push(factory.generateLocator(base, 'frame-locator', (0, _selectorParser.stringifySelector)({
184
+ parts: [lastPart]
185
+ })), factory.generateLocator(base, 'frame-locator', (0, _selectorParser.stringifySelector)({
186
+ parts: [lastPart]
187
+ }, true)));
188
+ }
189
+ lastTokens.splice(0, lastTokens.length, ...transformed);
190
190
  nextBase = 'frame-locator';
191
- index++;
191
+ continue;
192
192
  }
193
+ const nextPart = parts[index + 1];
193
194
  const selectorPart = (0, _selectorParser.stringifySelector)({
194
195
  parts: [part]
195
196
  });
196
- const locatorPart = factory.generateLocator(base, locatorType, selectorPart);
197
- if (locatorType === 'default' && nextPart && ['internal:has-text', 'internal:has-not-text'].includes(nextPart.name)) {
197
+ const locatorPart = factory.generateLocator(base, 'default', selectorPart);
198
+ if (nextPart && ['internal:has-text', 'internal:has-not-text'].includes(nextPart.name)) {
198
199
  const {
199
200
  exact,
200
201
  text
@@ -222,7 +223,7 @@ function innerAsLocators(factory, parsed, isFrameLocator = false, maxOutputSize
222
223
  const selectorPart = (0, _selectorParser.stringifySelector)({
223
224
  parts: [part]
224
225
  }, /* forceEngineName */true);
225
- locatorPartWithEngine = factory.generateLocator(base, locatorType, selectorPart);
226
+ locatorPartWithEngine = factory.generateLocator(base, 'default', selectorPart);
226
227
  }
227
228
  tokens.push([locatorPart, locatorPartWithEngine].filter(Boolean));
228
229
  }
@@ -276,8 +277,10 @@ class JavaScriptLocatorFactory {
276
277
  if (options.hasText !== undefined) return `locator(${this.quote(body)}, { hasText: ${this.toHasText(options.hasText)} })`;
277
278
  if (options.hasNotText !== undefined) return `locator(${this.quote(body)}, { hasNotText: ${this.toHasText(options.hasNotText)} })`;
278
279
  return `locator(${this.quote(body)})`;
279
- case 'frame':
280
+ case 'frame-locator':
280
281
  return `frameLocator(${this.quote(body)})`;
282
+ case 'frame':
283
+ return `contentFrame()`;
281
284
  case 'nth':
282
285
  return `nth(${body})`;
283
286
  case 'first':
@@ -359,8 +362,10 @@ class PythonLocatorFactory {
359
362
  if (options.hasText !== undefined) return `locator(${this.quote(body)}, has_text=${this.toHasText(options.hasText)})`;
360
363
  if (options.hasNotText !== undefined) return `locator(${this.quote(body)}, has_not_text=${this.toHasText(options.hasNotText)})`;
361
364
  return `locator(${this.quote(body)})`;
362
- case 'frame':
365
+ case 'frame-locator':
363
366
  return `frame_locator(${this.quote(body)})`;
367
+ case 'frame':
368
+ return `content_frame`;
364
369
  case 'nth':
365
370
  return `nth(${body})`;
366
371
  case 'first':
@@ -459,8 +464,10 @@ class JavaLocatorFactory {
459
464
  if (options.hasText !== undefined) return `locator(${this.quote(body)}, new ${clazz}.LocatorOptions().setHasText(${this.toHasText(options.hasText)}))`;
460
465
  if (options.hasNotText !== undefined) return `locator(${this.quote(body)}, new ${clazz}.LocatorOptions().setHasNotText(${this.toHasText(options.hasNotText)}))`;
461
466
  return `locator(${this.quote(body)})`;
462
- case 'frame':
467
+ case 'frame-locator':
463
468
  return `frameLocator(${this.quote(body)})`;
469
+ case 'frame':
470
+ return `contentFrame()`;
464
471
  case 'nth':
465
472
  return `nth(${body})`;
466
473
  case 'first':
@@ -543,8 +550,10 @@ class CSharpLocatorFactory {
543
550
  if (options.hasText !== undefined) return `Locator(${this.quote(body)}, new() { ${this.toHasText(options.hasText)} })`;
544
551
  if (options.hasNotText !== undefined) return `Locator(${this.quote(body)}, new() { ${this.toHasNotText(options.hasNotText)} })`;
545
552
  return `Locator(${this.quote(body)})`;
546
- case 'frame':
553
+ case 'frame-locator':
547
554
  return `FrameLocator(${this.quote(body)})`;
555
+ case 'frame':
556
+ return `ContentFrame`;
548
557
  case 'nth':
549
558
  return `Nth(${body})`;
550
559
  case 'first':
@@ -63,7 +63,7 @@ function parseLocator(locator, testIdAttributeName) {
63
63
  }
64
64
 
65
65
  // Equalize languages.
66
- template = template.toLowerCase().replace(/get_by_alt_text/g, 'getbyalttext').replace(/get_by_test_id/g, 'getbytestid').replace(/get_by_([\w]+)/g, 'getby$1').replace(/has_not_text/g, 'hasnottext').replace(/has_text/g, 'hastext').replace(/has_not/g, 'hasnot').replace(/frame_locator/g, 'framelocator').replace(/[{}\s]/g, '').replace(/new\(\)/g, '').replace(/new[\w]+\.[\w]+options\(\)/g, '').replace(/\.set/g, ',set').replace(/\.or_\(/g, 'or(') // Python has "or_" instead of "or".
66
+ template = template.toLowerCase().replace(/get_by_alt_text/g, 'getbyalttext').replace(/get_by_test_id/g, 'getbytestid').replace(/get_by_([\w]+)/g, 'getby$1').replace(/has_not_text/g, 'hasnottext').replace(/has_text/g, 'hastext').replace(/has_not/g, 'hasnot').replace(/frame_locator/g, 'framelocator').replace(/content_frame/g, 'contentframe').replace(/[{}\s]/g, '').replace(/new\(\)/g, '').replace(/new[\w]+\.[\w]+options\(\)/g, '').replace(/\.set/g, ',set').replace(/\.or_\(/g, 'or(') // Python has "or_" instead of "or".
67
67
  .replace(/\.and_\(/g, 'and(') // Python has "and_" instead of "and".
68
68
  .replace(/:/g, '=').replace(/,re\.ignorecase/g, 'i').replace(/,pattern.case_insensitive/g, 'i').replace(/,regexoptions.ignorecase/g, 'i').replace(/re.compile\(([^)]+)\)/g, '$1') // Python has regex strings as r"foo"
69
69
  .replace(/pattern.compile\(([^)]+)\)/g, 'r$1').replace(/newregex\(([^)]+)\)/g, 'r$1').replace(/string=/g, '=').replace(/regex=/g, '=').replace(/,,/g, ',');
@@ -123,7 +123,7 @@ function transform(template, params, testIdAttributeName) {
123
123
  }
124
124
 
125
125
  // Transform to selector engines.
126
- template = template.replace(/\,set([\w]+)\(([^)]+)\)/g, (_, group1, group2) => ',' + group1.toLowerCase() + '=' + group2.toLowerCase()).replace(/framelocator\(([^)]+)\)/g, '$1.internal:control=enter-frame').replace(/locator\(([^)]+),hastext=([^),]+)\)/g, 'locator($1).internal:has-text=$2').replace(/locator\(([^)]+),hasnottext=([^),]+)\)/g, 'locator($1).internal:has-not-text=$2').replace(/locator\(([^)]+),hastext=([^),]+)\)/g, 'locator($1).internal:has-text=$2').replace(/locator\(([^)]+)\)/g, '$1').replace(/getbyrole\(([^)]+)\)/g, 'internal:role=$1').replace(/getbytext\(([^)]+)\)/g, 'internal:text=$1').replace(/getbylabel\(([^)]+)\)/g, 'internal:label=$1').replace(/getbytestid\(([^)]+)\)/g, `internal:testid=[${testIdAttributeName}=$1]`).replace(/getby(placeholder|alt|title)(?:text)?\(([^)]+)\)/g, 'internal:attr=[$1=$2]').replace(/first(\(\))?/g, 'nth=0').replace(/last(\(\))?/g, 'nth=-1').replace(/nth\(([^)]+)\)/g, 'nth=$1').replace(/filter\(,?hastext=([^)]+)\)/g, 'internal:has-text=$1').replace(/filter\(,?hasnottext=([^)]+)\)/g, 'internal:has-not-text=$1').replace(/filter\(,?has2=([^)]+)\)/g, 'internal:has=$1').replace(/filter\(,?hasnot2=([^)]+)\)/g, 'internal:has-not=$1').replace(/,exact=false/g, '').replace(/,exact=true/g, 's').replace(/\,/g, '][');
126
+ template = template.replace(/\,set([\w]+)\(([^)]+)\)/g, (_, group1, group2) => ',' + group1.toLowerCase() + '=' + group2.toLowerCase()).replace(/framelocator\(([^)]+)\)/g, '$1.internal:control=enter-frame').replace(/contentframe(\(\))?/g, 'internal:control=enter-frame').replace(/locator\(([^)]+),hastext=([^),]+)\)/g, 'locator($1).internal:has-text=$2').replace(/locator\(([^)]+),hasnottext=([^),]+)\)/g, 'locator($1).internal:has-not-text=$2').replace(/locator\(([^)]+),hastext=([^),]+)\)/g, 'locator($1).internal:has-text=$2').replace(/locator\(([^)]+)\)/g, '$1').replace(/getbyrole\(([^)]+)\)/g, 'internal:role=$1').replace(/getbytext\(([^)]+)\)/g, 'internal:text=$1').replace(/getbylabel\(([^)]+)\)/g, 'internal:label=$1').replace(/getbytestid\(([^)]+)\)/g, `internal:testid=[${testIdAttributeName}=$1]`).replace(/getby(placeholder|alt|title)(?:text)?\(([^)]+)\)/g, 'internal:attr=[$1=$2]').replace(/first(\(\))?/g, 'nth=0').replace(/last(\(\))?/g, 'nth=-1').replace(/nth\(([^)]+)\)/g, 'nth=$1').replace(/filter\(,?hastext=([^)]+)\)/g, 'internal:has-text=$1').replace(/filter\(,?hasnottext=([^)]+)\)/g, 'internal:has-not-text=$1').replace(/filter\(,?has2=([^)]+)\)/g, 'internal:has=$1').replace(/filter\(,?hasnot2=([^)]+)\)/g, 'internal:has-not=$1').replace(/,exact=false/g, '').replace(/,exact=true/g, 's').replace(/\,/g, '][');
127
127
  const parts = template.split('.');
128
128
  // Turn "internal:control=enter-frame >> nth=0" into "nth=0 >> internal:control=enter-frame"
129
129
  // because these are swapped in locators vs selectors.