patchright-core 1.49.2 → 1.50.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/ThirdPartyNotices.txt +380 -12
- package/bin/reinstall_msedge_beta_linux.sh +6 -0
- package/bin/reinstall_msedge_dev_linux.sh +6 -0
- package/bin/reinstall_msedge_stable_linux.sh +6 -0
- package/browsers.json +17 -16
- package/lib/androidServerImpl.js +1 -1
- package/lib/cli/program.js +6 -30
- package/lib/client/channelOwner.js +35 -55
- package/lib/client/clientInstrumentation.js +2 -0
- package/lib/client/connection.js +3 -3
- package/lib/client/network.js +3 -1
- package/lib/client/waiter.js +1 -1
- package/lib/generated/consoleApiSource.js +1 -1
- package/lib/generated/injectedScriptSource.js +1 -1
- package/lib/generated/pollingRecorderSource.js +1 -1
- package/lib/inProcessFactory.js +2 -0
- package/lib/protocol/debug.js +1 -1
- package/lib/protocol/validator.js +2 -2
- package/lib/remote/playwrightConnection.js +4 -3
- package/lib/remote/playwrightServer.js +2 -1
- package/lib/server/bidi/bidiBrowser.js +9 -6
- package/lib/server/bidi/bidiExecutionContext.js +20 -1
- package/lib/server/bidi/bidiInput.js +7 -5
- package/lib/server/bidi/bidiNetworkManager.js +8 -9
- package/lib/server/bidi/bidiPage.js +9 -20
- package/lib/server/bidi/third_party/bidiKeyboard.js +9 -7
- package/lib/server/browserContext.js +24 -16
- package/lib/server/chromium/crBrowser.js +10 -10
- package/lib/server/chromium/crExecutionContext.js +1 -5
- package/lib/server/chromium/crInput.js +15 -4
- package/lib/server/chromium/crPage.js +17 -31
- package/lib/server/codegen/csharp.js +12 -2
- package/lib/server/codegen/java.js +14 -3
- package/lib/server/codegen/javascript.js +10 -2
- package/lib/server/codegen/jsonl.js +1 -1
- package/lib/server/codegen/python.js +5 -4
- package/lib/server/debugController.js +15 -40
- package/lib/server/debugger.js +1 -1
- package/lib/server/deviceDescriptorsSource.json +50 -50
- package/lib/server/dispatchers/browserContextDispatcher.js +2 -13
- package/lib/server/dispatchers/debugControllerDispatcher.js +4 -2
- package/lib/server/dispatchers/frameDispatcher.js +3 -2
- package/lib/server/dispatchers/pageDispatcher.js +1 -1
- package/lib/server/dispatchers/webSocketRouteDispatcher.js +10 -11
- package/lib/server/dom.js +7 -2
- package/lib/server/fetch.js +14 -20
- package/lib/server/firefox/ffBrowser.js +9 -7
- package/lib/server/firefox/ffInput.js +15 -4
- package/lib/server/firefox/ffPage.js +13 -28
- package/lib/server/frames.js +25 -30
- package/lib/server/har/harTracer.js +1 -1
- package/lib/server/input.js +2 -3
- package/lib/server/network.js +2 -2
- package/lib/server/page.js +23 -16
- package/lib/server/recorder/chat.js +177 -0
- package/lib/server/recorder/contextRecorder.js +6 -15
- package/lib/server/recorder/recorderApp.js +1 -1
- package/lib/server/recorder/recorderCollection.js +5 -17
- package/lib/server/recorder/recorderRunner.js +7 -3
- package/lib/server/recorder/recorderUtils.js +5 -29
- package/lib/server/recorder.js +12 -9
- package/lib/server/registry/browserFetcher.js +1 -1
- package/lib/server/registry/dependencies.js +5 -5
- package/lib/server/registry/index.js +118 -5
- package/lib/server/registry/nativeDeps.js +7 -4
- package/lib/server/socksClientCertificatesInterceptor.js +1 -1
- package/lib/server/trace/recorder/snapshotterInjected.js +12 -5
- package/lib/server/trace/viewer/traceViewer.js +6 -1
- package/lib/server/transport.js +1 -0
- package/lib/server/webkit/webkit.js +1 -1
- package/lib/server/webkit/wkBrowser.js +6 -6
- package/lib/server/webkit/wkExecutionContext.js +1 -0
- package/lib/server/webkit/wkInput.js +15 -5
- package/lib/server/webkit/wkPage.js +7 -25
- package/lib/utils/comparators.js +16 -10
- package/lib/utils/debugLogger.js +3 -1
- package/lib/utils/hostPlatform.js +14 -8
- package/lib/utils/httpServer.js +0 -4
- package/lib/utils/isomorphic/ariaSnapshot.js +176 -52
- package/lib/utils/isomorphic/cssParser.js +4 -4
- package/lib/utils/isomorphic/locatorGenerators.js +2 -2
- package/lib/utils/isomorphic/locatorParser.js +18 -12
- package/lib/utils/isomorphic/urlMatch.js +2 -4
- package/lib/utils/network.js +1 -1
- package/lib/utils/processLauncher.js +1 -1
- package/lib/utils/wsServer.js +1 -0
- package/lib/utils/zones.js +18 -20
- package/lib/utilsBundleImpl/index.js +104 -104
- package/lib/vite/htmlReport/index.html +14 -14
- package/lib/vite/{traceViewer/assets/codeMirrorModule-VZNWuWvU.js → recorder/assets/codeMirrorModule-CNAqJrkA.js} +1 -1
- package/lib/vite/recorder/assets/{index-CqeZmzx8.js → index-DGS0JLxS.js} +78 -78
- package/lib/vite/recorder/assets/{index-iA1aAGZg.css → index-eHBmevrY.css} +1 -1
- package/lib/vite/recorder/index.html +2 -2
- package/lib/vite/{recorder/assets/codeMirrorModule-DUzBrnvO.js → traceViewer/assets/codeMirrorModule-D55P_UuL.js} +10 -10
- package/lib/vite/traceViewer/assets/defaultSettingsView-B-uNoFsX.js +243 -0
- package/lib/vite/traceViewer/defaultSettingsView.2xeEXCXv.css +1 -0
- package/lib/vite/traceViewer/index.BfvuujqP.js +2 -0
- package/lib/vite/traceViewer/index.html +4 -7
- package/lib/vite/traceViewer/sw.bundle.js +3 -3
- package/lib/vite/traceViewer/{uiMode.voC1ZiOQ.css → uiMode.BatfzHMG.css} +1 -1
- package/lib/vite/traceViewer/uiMode.CStJu6jo.js +5 -0
- package/lib/vite/traceViewer/uiMode.html +4 -7
- package/package.json +1 -1
- package/types/protocol.d.ts +269 -20
- package/types/types.d.ts +69 -30
- package/bin/PrintDeps.exe +0 -0
- package/bin/README.md +0 -2
- package/lib/server/ariaSnapshot.js +0 -33
- package/lib/server/recorder/recorderInTraceViewer.js +0 -144
- package/lib/utils/isomorphic/recorderUtils.js +0 -227
- package/lib/vite/traceViewer/assets/inspectorTab-BV-Uf3j9.js +0 -68
- package/lib/vite/traceViewer/assets/testServerConnection-DeE2kSzz.js +0 -1
- package/lib/vite/traceViewer/assets/workbench-B4WPcYi9.js +0 -9
- package/lib/vite/traceViewer/embedded.BLPSqdbm.js +0 -2
- package/lib/vite/traceViewer/embedded.html +0 -18
- package/lib/vite/traceViewer/embedded.w7WN2u1R.css +0 -1
- package/lib/vite/traceViewer/index.BGZfFXXF.js +0 -2
- package/lib/vite/traceViewer/inspectorTab.DEOUW62d.css +0 -1
- package/lib/vite/traceViewer/recorder.B_SY1GJM.css +0 -0
- package/lib/vite/traceViewer/recorder.eWs2vuTG.js +0 -2
- package/lib/vite/traceViewer/recorder.html +0 -17
- package/lib/vite/traceViewer/uiMode.CW2d9h0S.js +0 -5
- package/lib/vite/traceViewer/workbench.C-zR9ysA.css +0 -1
- /package/lib/vite/recorder/assets/{codeMirrorModule-ez37Vkbh.css → codeMirrorModule-C3UTv-Ge.css} +0 -0
- /package/lib/vite/traceViewer/assets/{xtermModule-BeNbaIVa.js → xtermModule-c-SNdYZy.js} +0 -0
- /package/lib/vite/traceViewer/{codeMirrorModule.ez37Vkbh.css → codeMirrorModule.C3UTv-Ge.css} +0 -0
- /package/lib/vite/traceViewer/{index.CrbWWHbf.css → index.CFOW-Ezb.css} +0 -0
- /package/lib/vite/traceViewer/{xtermModule.DSXBckUd.css → xtermModule.Beg8tuEN.css} +0 -0
|
@@ -126,30 +126,21 @@ class ChannelOwner extends _eventEmitter.EventEmitter {
|
|
|
126
126
|
if (validator) {
|
|
127
127
|
return async params => {
|
|
128
128
|
return await this._wrapApiCall(async apiZone => {
|
|
129
|
-
const {
|
|
130
|
-
apiName,
|
|
131
|
-
frames,
|
|
132
|
-
csi,
|
|
133
|
-
callCookie,
|
|
134
|
-
stepId
|
|
135
|
-
} = apiZone.reported ? {
|
|
136
|
-
apiName: undefined,
|
|
137
|
-
csi: undefined,
|
|
138
|
-
callCookie: undefined,
|
|
139
|
-
frames: [],
|
|
140
|
-
stepId: undefined
|
|
141
|
-
} : apiZone;
|
|
142
|
-
apiZone.reported = true;
|
|
143
|
-
let currentStepId = stepId;
|
|
144
|
-
if (csi && apiName) {
|
|
145
|
-
const out = {};
|
|
146
|
-
csi.onApiCallBegin(apiName, params, frames, callCookie, out);
|
|
147
|
-
currentStepId = out.stepId;
|
|
148
|
-
}
|
|
149
|
-
return await this._connection.sendMessageToServer(this, prop, validator(params, '', {
|
|
129
|
+
const validatedParams = validator(params, '', {
|
|
150
130
|
tChannelImpl: tChannelImplToWire,
|
|
151
131
|
binary: this._connection.rawBuffers() ? 'buffer' : 'toBase64'
|
|
152
|
-
})
|
|
132
|
+
});
|
|
133
|
+
if (!apiZone.isInternal && !apiZone.reported) {
|
|
134
|
+
// Reporting/tracing/logging this api call for the first time.
|
|
135
|
+
apiZone.params = params;
|
|
136
|
+
apiZone.reported = true;
|
|
137
|
+
this._instrumentation.onApiCallBegin(apiZone);
|
|
138
|
+
logApiCall(this._logger, `=> ${apiZone.apiName} started`);
|
|
139
|
+
return await this._connection.sendMessageToServer(this, prop, validatedParams, apiZone.apiName, apiZone.frames, apiZone.stepId);
|
|
140
|
+
}
|
|
141
|
+
// Since this api call is either internal, or has already been reported/traced once,
|
|
142
|
+
// passing undefined apiName will avoid an extra unneeded tracing entry.
|
|
143
|
+
return await this._connection.sendMessageToServer(this, prop, validatedParams, undefined, [], undefined);
|
|
153
144
|
});
|
|
154
145
|
};
|
|
155
146
|
}
|
|
@@ -162,45 +153,35 @@ class ChannelOwner extends _eventEmitter.EventEmitter {
|
|
|
162
153
|
}
|
|
163
154
|
async _wrapApiCall(func, isInternal) {
|
|
164
155
|
const logger = this._logger;
|
|
165
|
-
const
|
|
166
|
-
if (
|
|
167
|
-
const stackTrace = (0, _stackTrace.captureLibraryStackTrace)();
|
|
168
|
-
let apiName = stackTrace.apiName;
|
|
169
|
-
const frames = stackTrace.frames;
|
|
156
|
+
const existingApiZone = _zones.zones.zoneData('apiZone');
|
|
157
|
+
if (existingApiZone) return await func(existingApiZone);
|
|
170
158
|
if (isInternal === undefined) isInternal = this._isInternalType;
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
const csi = isInternal || expectZone ? undefined : this._instrumentation;
|
|
181
|
-
const callCookie = {};
|
|
159
|
+
const stackTrace = (0, _stackTrace.captureLibraryStackTrace)();
|
|
160
|
+
const apiZone = {
|
|
161
|
+
apiName: stackTrace.apiName,
|
|
162
|
+
frames: stackTrace.frames,
|
|
163
|
+
isInternal,
|
|
164
|
+
reported: false,
|
|
165
|
+
userData: undefined,
|
|
166
|
+
stepId: undefined
|
|
167
|
+
};
|
|
182
168
|
try {
|
|
183
|
-
logApiCall(logger, `=> ${apiName} started`, isInternal);
|
|
184
|
-
const apiZone = {
|
|
185
|
-
apiName,
|
|
186
|
-
frames,
|
|
187
|
-
isInternal,
|
|
188
|
-
reported: false,
|
|
189
|
-
csi,
|
|
190
|
-
callCookie,
|
|
191
|
-
stepId
|
|
192
|
-
};
|
|
193
169
|
const result = await _zones.zones.run('apiZone', apiZone, async () => await func(apiZone));
|
|
194
|
-
|
|
195
|
-
|
|
170
|
+
if (!isInternal) {
|
|
171
|
+
logApiCall(logger, `<= ${apiZone.apiName} succeeded`);
|
|
172
|
+
this._instrumentation.onApiCallEnd(apiZone);
|
|
173
|
+
}
|
|
196
174
|
return result;
|
|
197
175
|
} catch (e) {
|
|
198
176
|
const innerError = (process.env.PWDEBUGIMPL || (0, _utils.isUnderTest)()) && e.stack ? '\n<inner error>\n' + e.stack : '';
|
|
199
|
-
if (apiName && !apiName.includes('<anonymous>')) e.message = apiName + ': ' + e.message;
|
|
177
|
+
if (apiZone.apiName && !apiZone.apiName.includes('<anonymous>')) e.message = apiZone.apiName + ': ' + e.message;
|
|
200
178
|
const stackFrames = '\n' + (0, _stackTrace.stringifyStackFrames)(stackTrace.frames).join('\n') + innerError;
|
|
201
179
|
if (stackFrames.trim()) e.stack = e.message + stackFrames;else e.stack = '';
|
|
202
|
-
|
|
203
|
-
|
|
180
|
+
if (!isInternal) {
|
|
181
|
+
apiZone.error = e;
|
|
182
|
+
logApiCall(logger, `<= ${apiZone.apiName} failed`);
|
|
183
|
+
this._instrumentation.onApiCallEnd(apiZone);
|
|
184
|
+
}
|
|
204
185
|
throw e;
|
|
205
186
|
}
|
|
206
187
|
}
|
|
@@ -220,8 +201,7 @@ class ChannelOwner extends _eventEmitter.EventEmitter {
|
|
|
220
201
|
}
|
|
221
202
|
}
|
|
222
203
|
exports.ChannelOwner = ChannelOwner;
|
|
223
|
-
function logApiCall(logger, message
|
|
224
|
-
if (isNested) return;
|
|
204
|
+
function logApiCall(logger, message) {
|
|
225
205
|
if (logger && logger.isEnabled('api', 'info')) logger.log('api', 'info', message, [], {
|
|
226
206
|
color: 'cyan'
|
|
227
207
|
});
|
|
@@ -20,6 +20,8 @@ exports.createInstrumentation = createInstrumentation;
|
|
|
20
20
|
* limitations under the License.
|
|
21
21
|
*/
|
|
22
22
|
|
|
23
|
+
// Instrumentation can mutate the data, for example change apiName or stepId.
|
|
24
|
+
|
|
23
25
|
function createInstrumentation() {
|
|
24
26
|
const listeners = [];
|
|
25
27
|
return new Proxy({}, {
|
package/lib/client/connection.js
CHANGED
|
@@ -76,9 +76,9 @@ class Connection extends _events.EventEmitter {
|
|
|
76
76
|
this.toImpl = void 0;
|
|
77
77
|
this._tracingCount = 0;
|
|
78
78
|
this._instrumentation = void 0;
|
|
79
|
-
this._rootObject = new Root(this);
|
|
80
|
-
this._localUtils = localUtils;
|
|
81
79
|
this._instrumentation = instrumentation || (0, _clientInstrumentation.createInstrumentation)();
|
|
80
|
+
this._localUtils = localUtils;
|
|
81
|
+
this._rootObject = new Root(this);
|
|
82
82
|
}
|
|
83
83
|
markAsRemote() {
|
|
84
84
|
this._isRemote = true;
|
|
@@ -140,7 +140,7 @@ class Connection extends _events.EventEmitter {
|
|
|
140
140
|
}).catch(() => {});
|
|
141
141
|
// We need to exit zones before calling into the server, otherwise
|
|
142
142
|
// when we receive events from the server, we would be in an API zone.
|
|
143
|
-
_utils.zones.
|
|
143
|
+
_utils.zones.empty().run(() => this.onmessage({
|
|
144
144
|
...message,
|
|
145
145
|
metadata
|
|
146
146
|
}));
|
package/lib/client/network.js
CHANGED
|
@@ -50,6 +50,7 @@ class Request extends _channelOwner.ChannelOwner {
|
|
|
50
50
|
this._actualHeadersPromise = void 0;
|
|
51
51
|
this._timing = void 0;
|
|
52
52
|
this._fallbackOverrides = {};
|
|
53
|
+
this.markAsInternalType();
|
|
53
54
|
this._redirectedFrom = Request.fromNullable(initializer.redirectedFrom);
|
|
54
55
|
if (this._redirectedFrom) this._redirectedFrom._redirectedTo = this;
|
|
55
56
|
this._provisionalHeaders = new RawHeaders(initializer.headers);
|
|
@@ -510,6 +511,7 @@ class Response extends _channelOwner.ChannelOwner {
|
|
|
510
511
|
this._actualHeadersPromise = void 0;
|
|
511
512
|
this._request = void 0;
|
|
512
513
|
this._finishedPromise = new _manualPromise.ManualPromise();
|
|
514
|
+
this.markAsInternalType();
|
|
513
515
|
this._provisionalHeaders = new RawHeaders(initializer.headers);
|
|
514
516
|
this._request = Request.from(this._initializer.request);
|
|
515
517
|
Object.assign(this._request._timing, this._initializer.timing);
|
|
@@ -659,7 +661,7 @@ class RouteHandler {
|
|
|
659
661
|
this._times = times;
|
|
660
662
|
this.url = url;
|
|
661
663
|
this.handler = handler;
|
|
662
|
-
this._svedZone = _utils.zones.
|
|
664
|
+
this._svedZone = _utils.zones.current().without('apiZone');
|
|
663
665
|
}
|
|
664
666
|
static prepareInterceptionPatterns(handlers) {
|
|
665
667
|
const patterns = [];
|
package/lib/client/waiter.js
CHANGED
|
@@ -35,7 +35,7 @@ class Waiter {
|
|
|
35
35
|
this._savedZone = void 0;
|
|
36
36
|
this._waitId = (0, _utils.createGuid)();
|
|
37
37
|
this._channelOwner = channelOwner;
|
|
38
|
-
this._savedZone = _utils.zones.
|
|
38
|
+
this._savedZone = _utils.zones.current().without('apiZone');
|
|
39
39
|
this._channelOwner._channel.waitForEventInfo({
|
|
40
40
|
info: {
|
|
41
41
|
waitId: this._waitId,
|
|
@@ -4,4 +4,4 @@ Object.defineProperty(exports, "__esModule", {
|
|
|
4
4
|
value: true
|
|
5
5
|
});
|
|
6
6
|
exports.source = void 0;
|
|
7
|
-
const source = exports.source = "\nvar __commonJS = obj => {\n let required = false;\n let result;\n return function __require() {\n if (!required) {\n required = true;\n let fn;\n for (const name in obj) { fn = obj[name]; break; }\n const module = { exports: {} };\n fn(module.exports, module);\n result = module.exports;\n }\n return result;\n }\n};\nvar __export = (target, all) => {for (var name in all) target[name] = all[name];};\nvar __toESM = mod => ({ ...mod, 'default': mod });\nvar __toCommonJS = mod => ({ ...mod, __esModule: true });\n\n\n// packages/playwright-core/src/server/injected/consoleApi.ts\nvar consoleApi_exports = {};\n__export(consoleApi_exports, {\n default: () => consoleApi_default\n});\nmodule.exports = __toCommonJS(consoleApi_exports);\n\n// packages/playwright-core/src/utils/isomorphic/stringUtils.ts\nfunction escapeWithQuotes(text, char = \"'\") {\n const stringified = JSON.stringify(text);\n const escapedText = stringified.substring(1, stringified.length - 1).replace(/\\\\\"/g, '\"');\n if (char === \"'\")\n return char + escapedText.replace(/[']/g, \"\\\\'\") + char;\n if (char === '\"')\n return char + escapedText.replace(/[\"]/g, '\\\\\"') + char;\n if (char === \"`\")\n return char + escapedText.replace(/[`]/g, \"`\") + char;\n throw new Error(\"Invalid escape char\");\n}\nfunction toTitleCase(name) {\n return name.charAt(0).toUpperCase() + name.substring(1);\n}\nfunction toSnakeCase(name) {\n return name.replace(/([a-z0-9])([A-Z])/g, \"$1_$2\").replace(/([A-Z])([A-Z][a-z])/g, \"$1_$2\").toLowerCase();\n}\nfunction normalizeEscapedRegexQuotes(source) {\n return source.replace(/(^|[^\\\\])(\\\\\\\\)*\\\\(['\"`])/g, \"$1$2$3\");\n}\nfunction escapeRegexForSelector(re) {\n if (re.unicode || re.unicodeSets)\n return String(re);\n return String(re).replace(/(^|[^\\\\])(\\\\\\\\)*([\"'`])/g, \"$1$2\\\\$3\").replace(/>>/g, \"\\\\>\\\\>\");\n}\nfunction escapeForTextSelector(text, exact) {\n if (typeof text !== \"string\")\n return escapeRegexForSelector(text);\n return `${JSON.stringify(text)}${exact ? \"s\" : \"i\"}`;\n}\nfunction escapeForAttributeSelector(value, exact) {\n if (typeof value !== \"string\")\n return escapeRegexForSelector(value);\n return `\"${value.replace(/\\\\/g, \"\\\\\\\\\").replace(/[\"]/g, '\\\\\"')}\"${exact ? \"s\" : \"i\"}`;\n}\n\n// packages/playwright-core/src/utils/isomorphic/locatorUtils.ts\nfunction getByAttributeTextSelector(attrName, text, options) {\n return `internal:attr=[${attrName}=${escapeForAttributeSelector(text, (options == null ? void 0 : options.exact) || false)}]`;\n}\nfunction getByTestIdSelector(testIdAttributeName, testId) {\n return `internal:testid=[${testIdAttributeName}=${escapeForAttributeSelector(testId, true)}]`;\n}\nfunction getByLabelSelector(text, options) {\n return \"internal:label=\" + escapeForTextSelector(text, !!(options == null ? void 0 : options.exact));\n}\nfunction getByAltTextSelector(text, options) {\n return getByAttributeTextSelector(\"alt\", text, options);\n}\nfunction getByTitleSelector(text, options) {\n return getByAttributeTextSelector(\"title\", text, options);\n}\nfunction getByPlaceholderSelector(text, options) {\n return getByAttributeTextSelector(\"placeholder\", text, options);\n}\nfunction getByTextSelector(text, options) {\n return \"internal:text=\" + escapeForTextSelector(text, !!(options == null ? void 0 : options.exact));\n}\nfunction getByRoleSelector(role, options = {}) {\n const props = [];\n if (options.checked !== void 0)\n props.push([\"checked\", String(options.checked)]);\n if (options.disabled !== void 0)\n props.push([\"disabled\", String(options.disabled)]);\n if (options.selected !== void 0)\n props.push([\"selected\", String(options.selected)]);\n if (options.expanded !== void 0)\n props.push([\"expanded\", String(options.expanded)]);\n if (options.includeHidden !== void 0)\n props.push([\"include-hidden\", String(options.includeHidden)]);\n if (options.level !== void 0)\n props.push([\"level\", String(options.level)]);\n if (options.name !== void 0)\n props.push([\"name\", escapeForAttributeSelector(options.name, !!options.exact)]);\n if (options.pressed !== void 0)\n props.push([\"pressed\", String(options.pressed)]);\n return `internal:role=${role}${props.map(([n, v]) => `[${n}=${v}]`).join(\"\")}`;\n}\n\n// packages/playwright-core/src/utils/isomorphic/cssTokenizer.ts\nvar between = function(num, first, last) {\n return num >= first && num <= last;\n};\nfunction digit(code) {\n return between(code, 48, 57);\n}\nfunction hexdigit(code) {\n return digit(code) || between(code, 65, 70) || between(code, 97, 102);\n}\nfunction uppercaseletter(code) {\n return between(code, 65, 90);\n}\nfunction lowercaseletter(code) {\n return between(code, 97, 122);\n}\nfunction letter(code) {\n return uppercaseletter(code) || lowercaseletter(code);\n}\nfunction nonascii(code) {\n return code >= 128;\n}\nfunction namestartchar(code) {\n return letter(code) || nonascii(code) || code === 95;\n}\nfunction namechar(code) {\n return namestartchar(code) || digit(code) || code === 45;\n}\nfunction nonprintable(code) {\n return between(code, 0, 8) || code === 11 || between(code, 14, 31) || code === 127;\n}\nfunction newline(code) {\n return code === 10;\n}\nfunction whitespace(code) {\n return newline(code) || code === 9 || code === 32;\n}\nvar maximumallowedcodepoint = 1114111;\nvar InvalidCharacterError = class extends Error {\n constructor(message) {\n super(message);\n this.name = \"InvalidCharacterError\";\n }\n};\nfunction preprocess(str) {\n const codepoints = [];\n for (let i = 0; i < str.length; i++) {\n let code = str.charCodeAt(i);\n if (code === 13 && str.charCodeAt(i + 1) === 10) {\n code = 10;\n i++;\n }\n if (code === 13 || code === 12)\n code = 10;\n if (code === 0)\n code = 65533;\n if (between(code, 55296, 56319) && between(str.charCodeAt(i + 1), 56320, 57343)) {\n const lead = code - 55296;\n const trail = str.charCodeAt(i + 1) - 56320;\n code = Math.pow(2, 16) + lead * Math.pow(2, 10) + trail;\n i++;\n }\n codepoints.push(code);\n }\n return codepoints;\n}\nfunction stringFromCode(code) {\n if (code <= 65535)\n return String.fromCharCode(code);\n code -= Math.pow(2, 16);\n const lead = Math.floor(code / Math.pow(2, 10)) + 55296;\n const trail = code % Math.pow(2, 10) + 56320;\n return String.fromCharCode(lead) + String.fromCharCode(trail);\n}\nfunction tokenize(str1) {\n const str = preprocess(str1);\n let i = -1;\n const tokens = [];\n let code;\n let line = 0;\n let column = 0;\n let lastLineLength = 0;\n const incrLineno = function() {\n line += 1;\n lastLineLength = column;\n column = 0;\n };\n const locStart = { line, column };\n const codepoint = function(i2) {\n if (i2 >= str.length)\n return -1;\n return str[i2];\n };\n const next = function(num) {\n if (num === void 0)\n num = 1;\n if (num > 3)\n throw \"Spec Error: no more than three codepoints of lookahead.\";\n return codepoint(i + num);\n };\n const consume = function(num) {\n if (num === void 0)\n num = 1;\n i += num;\n code = codepoint(i);\n if (newline(code))\n incrLineno();\n else\n column += num;\n return true;\n };\n const reconsume = function() {\n i -= 1;\n if (newline(code)) {\n line -= 1;\n column = lastLineLength;\n } else {\n column -= 1;\n }\n locStart.line = line;\n locStart.column = column;\n return true;\n };\n const eof = function(codepoint2) {\n if (codepoint2 === void 0)\n codepoint2 = code;\n return codepoint2 === -1;\n };\n const donothing = function() {\n };\n const parseerror = function() {\n };\n const consumeAToken = function() {\n consumeComments();\n consume();\n if (whitespace(code)) {\n while (whitespace(next()))\n consume();\n return new WhitespaceToken();\n } else if (code === 34) {\n return consumeAStringToken();\n } else if (code === 35) {\n if (namechar(next()) || areAValidEscape(next(1), next(2))) {\n const token = new HashToken(\"\");\n if (wouldStartAnIdentifier(next(1), next(2), next(3)))\n token.type = \"id\";\n token.value = consumeAName();\n return token;\n } else {\n return new DelimToken(code);\n }\n } else if (code === 36) {\n if (next() === 61) {\n consume();\n return new SuffixMatchToken();\n } else {\n return new DelimToken(code);\n }\n } else if (code === 39) {\n return consumeAStringToken();\n } else if (code === 40) {\n return new OpenParenToken();\n } else if (code === 41) {\n return new CloseParenToken();\n } else if (code === 42) {\n if (next() === 61) {\n consume();\n return new SubstringMatchToken();\n } else {\n return new DelimToken(code);\n }\n } else if (code === 43) {\n if (startsWithANumber()) {\n reconsume();\n return consumeANumericToken();\n } else {\n return new DelimToken(code);\n }\n } else if (code === 44) {\n return new CommaToken();\n } else if (code === 45) {\n if (startsWithANumber()) {\n reconsume();\n return consumeANumericToken();\n } else if (next(1) === 45 && next(2) === 62) {\n consume(2);\n return new CDCToken();\n } else if (startsWithAnIdentifier()) {\n reconsume();\n return consumeAnIdentlikeToken();\n } else {\n return new DelimToken(code);\n }\n } else if (code === 46) {\n if (startsWithANumber()) {\n reconsume();\n return consumeANumericToken();\n } else {\n return new DelimToken(code);\n }\n } else if (code === 58) {\n return new ColonToken();\n } else if (code === 59) {\n return new SemicolonToken();\n } else if (code === 60) {\n if (next(1) === 33 && next(2) === 45 && next(3) === 45) {\n consume(3);\n return new CDOToken();\n } else {\n return new DelimToken(code);\n }\n } else if (code === 64) {\n if (wouldStartAnIdentifier(next(1), next(2), next(3)))\n return new AtKeywordToken(consumeAName());\n else\n return new DelimToken(code);\n } else if (code === 91) {\n return new OpenSquareToken();\n } else if (code === 92) {\n if (startsWithAValidEscape()) {\n reconsume();\n return consumeAnIdentlikeToken();\n } else {\n parseerror();\n return new DelimToken(code);\n }\n } else if (code === 93) {\n return new CloseSquareToken();\n } else if (code === 94) {\n if (next() === 61) {\n consume();\n return new PrefixMatchToken();\n } else {\n return new DelimToken(code);\n }\n } else if (code === 123) {\n return new OpenCurlyToken();\n } else if (code === 124) {\n if (next() === 61) {\n consume();\n return new DashMatchToken();\n } else if (next() === 124) {\n consume();\n return new ColumnToken();\n } else {\n return new DelimToken(code);\n }\n } else if (code === 125) {\n return new CloseCurlyToken();\n } else if (code === 126) {\n if (next() === 61) {\n consume();\n return new IncludeMatchToken();\n } else {\n return new DelimToken(code);\n }\n } else if (digit(code)) {\n reconsume();\n return consumeANumericToken();\n } else if (namestartchar(code)) {\n reconsume();\n return consumeAnIdentlikeToken();\n } else if (eof()) {\n return new EOFToken();\n } else {\n return new DelimToken(code);\n }\n };\n const consumeComments = function() {\n while (next(1) === 47 && next(2) === 42) {\n consume(2);\n while (true) {\n consume();\n if (code === 42 && next() === 47) {\n consume();\n break;\n } else if (eof()) {\n parseerror();\n return;\n }\n }\n }\n };\n const consumeANumericToken = function() {\n const num = consumeANumber();\n if (wouldStartAnIdentifier(next(1), next(2), next(3))) {\n const token = new DimensionToken();\n token.value = num.value;\n token.repr = num.repr;\n token.type = num.type;\n token.unit = consumeAName();\n return token;\n } else if (next() === 37) {\n consume();\n const token = new PercentageToken();\n token.value = num.value;\n token.repr = num.repr;\n return token;\n } else {\n const token = new NumberToken();\n token.value = num.value;\n token.repr = num.repr;\n token.type = num.type;\n return token;\n }\n };\n const consumeAnIdentlikeToken = function() {\n const str2 = consumeAName();\n if (str2.toLowerCase() === \"url\" && next() === 40) {\n consume();\n while (whitespace(next(1)) && whitespace(next(2)))\n consume();\n if (next() === 34 || next() === 39)\n return new FunctionToken(str2);\n else if (whitespace(next()) && (next(2) === 34 || next(2) === 39))\n return new FunctionToken(str2);\n else\n return consumeAURLToken();\n } else if (next() === 40) {\n consume();\n return new FunctionToken(str2);\n } else {\n return new IdentToken(str2);\n }\n };\n const consumeAStringToken = function(endingCodePoint) {\n if (endingCodePoint === void 0)\n endingCodePoint = code;\n let string = \"\";\n while (consume()) {\n if (code === endingCodePoint || eof()) {\n return new StringToken(string);\n } else if (newline(code)) {\n parseerror();\n reconsume();\n return new BadStringToken();\n } else if (code === 92) {\n if (eof(next()))\n donothing();\n else if (newline(next()))\n consume();\n else\n string += stringFromCode(consumeEscape());\n } else {\n string += stringFromCode(code);\n }\n }\n throw new Error(\"Internal error\");\n };\n const consumeAURLToken = function() {\n const token = new URLToken(\"\");\n while (whitespace(next()))\n consume();\n if (eof(next()))\n return token;\n while (consume()) {\n if (code === 41 || eof()) {\n return token;\n } else if (whitespace(code)) {\n while (whitespace(next()))\n consume();\n if (next() === 41 || eof(next())) {\n consume();\n return token;\n } else {\n consumeTheRemnantsOfABadURL();\n return new BadURLToken();\n }\n } else if (code === 34 || code === 39 || code === 40 || nonprintable(code)) {\n parseerror();\n consumeTheRemnantsOfABadURL();\n return new BadURLToken();\n } else if (code === 92) {\n if (startsWithAValidEscape()) {\n token.value += stringFromCode(consumeEscape());\n } else {\n parseerror();\n consumeTheRemnantsOfABadURL();\n return new BadURLToken();\n }\n } else {\n token.value += stringFromCode(code);\n }\n }\n throw new Error(\"Internal error\");\n };\n const consumeEscape = function() {\n consume();\n if (hexdigit(code)) {\n const digits = [code];\n for (let total = 0; total < 5; total++) {\n if (hexdigit(next())) {\n consume();\n digits.push(code);\n } else {\n break;\n }\n }\n if (whitespace(next()))\n consume();\n let value = parseInt(digits.map(function(x) {\n return String.fromCharCode(x);\n }).join(\"\"), 16);\n if (value > maximumallowedcodepoint)\n value = 65533;\n return value;\n } else if (eof()) {\n return 65533;\n } else {\n return code;\n }\n };\n const areAValidEscape = function(c1, c2) {\n if (c1 !== 92)\n return false;\n if (newline(c2))\n return false;\n return true;\n };\n const startsWithAValidEscape = function() {\n return areAValidEscape(code, next());\n };\n const wouldStartAnIdentifier = function(c1, c2, c3) {\n if (c1 === 45)\n return namestartchar(c2) || c2 === 45 || areAValidEscape(c2, c3);\n else if (namestartchar(c1))\n return true;\n else if (c1 === 92)\n return areAValidEscape(c1, c2);\n else\n return false;\n };\n const startsWithAnIdentifier = function() {\n return wouldStartAnIdentifier(code, next(1), next(2));\n };\n const wouldStartANumber = function(c1, c2, c3) {\n if (c1 === 43 || c1 === 45) {\n if (digit(c2))\n return true;\n if (c2 === 46 && digit(c3))\n return true;\n return false;\n } else if (c1 === 46) {\n if (digit(c2))\n return true;\n return false;\n } else if (digit(c1)) {\n return true;\n } else {\n return false;\n }\n };\n const startsWithANumber = function() {\n return wouldStartANumber(code, next(1), next(2));\n };\n const consumeAName = function() {\n let result = \"\";\n while (consume()) {\n if (namechar(code)) {\n result += stringFromCode(code);\n } else if (startsWithAValidEscape()) {\n result += stringFromCode(consumeEscape());\n } else {\n reconsume();\n return result;\n }\n }\n throw new Error(\"Internal parse error\");\n };\n const consumeANumber = function() {\n let repr = \"\";\n let type = \"integer\";\n if (next() === 43 || next() === 45) {\n consume();\n repr += stringFromCode(code);\n }\n while (digit(next())) {\n consume();\n repr += stringFromCode(code);\n }\n if (next(1) === 46 && digit(next(2))) {\n consume();\n repr += stringFromCode(code);\n consume();\n repr += stringFromCode(code);\n type = \"number\";\n while (digit(next())) {\n consume();\n repr += stringFromCode(code);\n }\n }\n const c1 = next(1), c2 = next(2), c3 = next(3);\n if ((c1 === 69 || c1 === 101) && digit(c2)) {\n consume();\n repr += stringFromCode(code);\n consume();\n repr += stringFromCode(code);\n type = \"number\";\n while (digit(next())) {\n consume();\n repr += stringFromCode(code);\n }\n } else if ((c1 === 69 || c1 === 101) && (c2 === 43 || c2 === 45) && digit(c3)) {\n consume();\n repr += stringFromCode(code);\n consume();\n repr += stringFromCode(code);\n consume();\n repr += stringFromCode(code);\n type = \"number\";\n while (digit(next())) {\n consume();\n repr += stringFromCode(code);\n }\n }\n const value = convertAStringToANumber(repr);\n return { type, value, repr };\n };\n const convertAStringToANumber = function(string) {\n return +string;\n };\n const consumeTheRemnantsOfABadURL = function() {\n while (consume()) {\n if (code === 41 || eof()) {\n return;\n } else if (startsWithAValidEscape()) {\n consumeEscape();\n donothing();\n } else {\n donothing();\n }\n }\n };\n let iterationCount = 0;\n while (!eof(next())) {\n tokens.push(consumeAToken());\n iterationCount++;\n if (iterationCount > str.length * 2)\n throw new Error(\"I'm infinite-looping!\");\n }\n return tokens;\n}\nvar CSSParserToken = class {\n constructor() {\n this.tokenType = \"\";\n }\n toJSON() {\n return { token: this.tokenType };\n }\n toString() {\n return this.tokenType;\n }\n toSource() {\n return \"\" + this;\n }\n};\nvar BadStringToken = class extends CSSParserToken {\n constructor() {\n super(...arguments);\n this.tokenType = \"BADSTRING\";\n }\n};\nvar BadURLToken = class extends CSSParserToken {\n constructor() {\n super(...arguments);\n this.tokenType = \"BADURL\";\n }\n};\nvar WhitespaceToken = class extends CSSParserToken {\n constructor() {\n super(...arguments);\n this.tokenType = \"WHITESPACE\";\n }\n toString() {\n return \"WS\";\n }\n toSource() {\n return \" \";\n }\n};\nvar CDOToken = class extends CSSParserToken {\n constructor() {\n super(...arguments);\n this.tokenType = \"CDO\";\n }\n toSource() {\n return \"<!--\";\n }\n};\nvar CDCToken = class extends CSSParserToken {\n constructor() {\n super(...arguments);\n this.tokenType = \"CDC\";\n }\n toSource() {\n return \"-->\";\n }\n};\nvar ColonToken = class extends CSSParserToken {\n constructor() {\n super(...arguments);\n this.tokenType = \":\";\n }\n};\nvar SemicolonToken = class extends CSSParserToken {\n constructor() {\n super(...arguments);\n this.tokenType = \";\";\n }\n};\nvar CommaToken = class extends CSSParserToken {\n constructor() {\n super(...arguments);\n this.tokenType = \",\";\n }\n};\nvar GroupingToken = class extends CSSParserToken {\n constructor() {\n super(...arguments);\n this.value = \"\";\n this.mirror = \"\";\n }\n};\nvar OpenCurlyToken = class extends GroupingToken {\n constructor() {\n super();\n this.tokenType = \"{\";\n this.value = \"{\";\n this.mirror = \"}\";\n }\n};\nvar CloseCurlyToken = class extends GroupingToken {\n constructor() {\n super();\n this.tokenType = \"}\";\n this.value = \"}\";\n this.mirror = \"{\";\n }\n};\nvar OpenSquareToken = class extends GroupingToken {\n constructor() {\n super();\n this.tokenType = \"[\";\n this.value = \"[\";\n this.mirror = \"]\";\n }\n};\nvar CloseSquareToken = class extends GroupingToken {\n constructor() {\n super();\n this.tokenType = \"]\";\n this.value = \"]\";\n this.mirror = \"[\";\n }\n};\nvar OpenParenToken = class extends GroupingToken {\n constructor() {\n super();\n this.tokenType = \"(\";\n this.value = \"(\";\n this.mirror = \")\";\n }\n};\nvar CloseParenToken = class extends GroupingToken {\n constructor() {\n super();\n this.tokenType = \")\";\n this.value = \")\";\n this.mirror = \"(\";\n }\n};\nvar IncludeMatchToken = class extends CSSParserToken {\n constructor() {\n super(...arguments);\n this.tokenType = \"~=\";\n }\n};\nvar DashMatchToken = class extends CSSParserToken {\n constructor() {\n super(...arguments);\n this.tokenType = \"|=\";\n }\n};\nvar PrefixMatchToken = class extends CSSParserToken {\n constructor() {\n super(...arguments);\n this.tokenType = \"^=\";\n }\n};\nvar SuffixMatchToken = class extends CSSParserToken {\n constructor() {\n super(...arguments);\n this.tokenType = \"$=\";\n }\n};\nvar SubstringMatchToken = class extends CSSParserToken {\n constructor() {\n super(...arguments);\n this.tokenType = \"*=\";\n }\n};\nvar ColumnToken = class extends CSSParserToken {\n constructor() {\n super(...arguments);\n this.tokenType = \"||\";\n }\n};\nvar EOFToken = class extends CSSParserToken {\n constructor() {\n super(...arguments);\n this.tokenType = \"EOF\";\n }\n toSource() {\n return \"\";\n }\n};\nvar DelimToken = class extends CSSParserToken {\n constructor(code) {\n super();\n this.tokenType = \"DELIM\";\n this.value = \"\";\n this.value = stringFromCode(code);\n }\n toString() {\n return \"DELIM(\" + this.value + \")\";\n }\n toJSON() {\n const json = this.constructor.prototype.constructor.prototype.toJSON.call(this);\n json.value = this.value;\n return json;\n }\n toSource() {\n if (this.value === \"\\\\\")\n return \"\\\\\\n\";\n else\n return this.value;\n }\n};\nvar StringValuedToken = class extends CSSParserToken {\n constructor() {\n super(...arguments);\n this.value = \"\";\n }\n ASCIIMatch(str) {\n return this.value.toLowerCase() === str.toLowerCase();\n }\n toJSON() {\n const json = this.constructor.prototype.constructor.prototype.toJSON.call(this);\n json.value = this.value;\n return json;\n }\n};\nvar IdentToken = class extends StringValuedToken {\n constructor(val) {\n super();\n this.tokenType = \"IDENT\";\n this.value = val;\n }\n toString() {\n return \"IDENT(\" + this.value + \")\";\n }\n toSource() {\n return escapeIdent(this.value);\n }\n};\nvar FunctionToken = class extends StringValuedToken {\n constructor(val) {\n super();\n this.tokenType = \"FUNCTION\";\n this.value = val;\n this.mirror = \")\";\n }\n toString() {\n return \"FUNCTION(\" + this.value + \")\";\n }\n toSource() {\n return escapeIdent(this.value) + \"(\";\n }\n};\nvar AtKeywordToken = class extends StringValuedToken {\n constructor(val) {\n super();\n this.tokenType = \"AT-KEYWORD\";\n this.value = val;\n }\n toString() {\n return \"AT(\" + this.value + \")\";\n }\n toSource() {\n return \"@\" + escapeIdent(this.value);\n }\n};\nvar HashToken = class extends StringValuedToken {\n constructor(val) {\n super();\n this.tokenType = \"HASH\";\n this.value = val;\n this.type = \"unrestricted\";\n }\n toString() {\n return \"HASH(\" + this.value + \")\";\n }\n toJSON() {\n const json = this.constructor.prototype.constructor.prototype.toJSON.call(this);\n json.value = this.value;\n json.type = this.type;\n return json;\n }\n toSource() {\n if (this.type === \"id\")\n return \"#\" + escapeIdent(this.value);\n else\n return \"#\" + escapeHash(this.value);\n }\n};\nvar StringToken = class extends StringValuedToken {\n constructor(val) {\n super();\n this.tokenType = \"STRING\";\n this.value = val;\n }\n toString() {\n return '\"' + escapeString(this.value) + '\"';\n }\n};\nvar URLToken = class extends StringValuedToken {\n constructor(val) {\n super();\n this.tokenType = \"URL\";\n this.value = val;\n }\n toString() {\n return \"URL(\" + this.value + \")\";\n }\n toSource() {\n return 'url(\"' + escapeString(this.value) + '\")';\n }\n};\nvar NumberToken = class extends CSSParserToken {\n constructor() {\n super();\n this.tokenType = \"NUMBER\";\n this.type = \"integer\";\n this.repr = \"\";\n }\n toString() {\n if (this.type === \"integer\")\n return \"INT(\" + this.value + \")\";\n return \"NUMBER(\" + this.value + \")\";\n }\n toJSON() {\n const json = super.toJSON();\n json.value = this.value;\n json.type = this.type;\n json.repr = this.repr;\n return json;\n }\n toSource() {\n return this.repr;\n }\n};\nvar PercentageToken = class extends CSSParserToken {\n constructor() {\n super();\n this.tokenType = \"PERCENTAGE\";\n this.repr = \"\";\n }\n toString() {\n return \"PERCENTAGE(\" + this.value + \")\";\n }\n toJSON() {\n const json = this.constructor.prototype.constructor.prototype.toJSON.call(this);\n json.value = this.value;\n json.repr = this.repr;\n return json;\n }\n toSource() {\n return this.repr + \"%\";\n }\n};\nvar DimensionToken = class extends CSSParserToken {\n constructor() {\n super();\n this.tokenType = \"DIMENSION\";\n this.type = \"integer\";\n this.repr = \"\";\n this.unit = \"\";\n }\n toString() {\n return \"DIM(\" + this.value + \",\" + this.unit + \")\";\n }\n toJSON() {\n const json = this.constructor.prototype.constructor.prototype.toJSON.call(this);\n json.value = this.value;\n json.type = this.type;\n json.repr = this.repr;\n json.unit = this.unit;\n return json;\n }\n toSource() {\n const source = this.repr;\n let unit = escapeIdent(this.unit);\n if (unit[0].toLowerCase() === \"e\" && (unit[1] === \"-\" || between(unit.charCodeAt(1), 48, 57))) {\n unit = \"\\\\65 \" + unit.slice(1, unit.length);\n }\n return source + unit;\n }\n};\nfunction escapeIdent(string) {\n string = \"\" + string;\n let result = \"\";\n const firstcode = string.charCodeAt(0);\n for (let i = 0; i < string.length; i++) {\n const code = string.charCodeAt(i);\n if (code === 0)\n throw new InvalidCharacterError(\"Invalid character: the input contains U+0000.\");\n if (between(code, 1, 31) || code === 127 || i === 0 && between(code, 48, 57) || i === 1 && between(code, 48, 57) && firstcode === 45)\n result += \"\\\\\" + code.toString(16) + \" \";\n else if (code >= 128 || code === 45 || code === 95 || between(code, 48, 57) || between(code, 65, 90) || between(code, 97, 122))\n result += string[i];\n else\n result += \"\\\\\" + string[i];\n }\n return result;\n}\nfunction escapeHash(string) {\n string = \"\" + string;\n let result = \"\";\n for (let i = 0; i < string.length; i++) {\n const code = string.charCodeAt(i);\n if (code === 0)\n throw new InvalidCharacterError(\"Invalid character: the input contains U+0000.\");\n if (code >= 128 || code === 45 || code === 95 || between(code, 48, 57) || between(code, 65, 90) || between(code, 97, 122))\n result += string[i];\n else\n result += \"\\\\\" + code.toString(16) + \" \";\n }\n return result;\n}\nfunction escapeString(string) {\n string = \"\" + string;\n let result = \"\";\n for (let i = 0; i < string.length; i++) {\n const code = string.charCodeAt(i);\n if (code === 0)\n throw new InvalidCharacterError(\"Invalid character: the input contains U+0000.\");\n if (between(code, 1, 31) || code === 127)\n result += \"\\\\\" + code.toString(16) + \" \";\n else if (code === 34 || code === 92)\n result += \"\\\\\" + string[i];\n else\n result += string[i];\n }\n return result;\n}\n\n// packages/playwright-core/src/utils/isomorphic/cssParser.ts\nvar InvalidSelectorError = class extends Error {\n};\nfunction parseCSS(selector, customNames) {\n let tokens;\n try {\n tokens = tokenize(selector);\n if (!(tokens[tokens.length - 1] instanceof EOFToken))\n tokens.push(new EOFToken());\n } catch (e) {\n const newMessage = e.message + ` while parsing selector \"${selector}\"`;\n const index = (e.stack || \"\").indexOf(e.message);\n if (index !== -1)\n e.stack = e.stack.substring(0, index) + newMessage + e.stack.substring(index + e.message.length);\n e.message = newMessage;\n throw e;\n }\n const unsupportedToken = tokens.find((token) => {\n return token instanceof AtKeywordToken || token instanceof BadStringToken || token instanceof BadURLToken || token instanceof ColumnToken || token instanceof CDOToken || token instanceof CDCToken || token instanceof SemicolonToken || // TODO: Consider using these for something, e.g. to escape complex strings.\n // For example :xpath{ (//div/bar[@attr=\"foo\"])[2]/baz }\n // Or this way :xpath( {complex-xpath-goes-here(\"hello\")} )\n token instanceof OpenCurlyToken || token instanceof CloseCurlyToken || // TODO: Consider treating these as strings?\n token instanceof URLToken || token instanceof PercentageToken;\n });\n if (unsupportedToken)\n throw new InvalidSelectorError(`Unsupported token \"${unsupportedToken.toSource()}\" while parsing selector \"${selector}\"`);\n let pos = 0;\n const names = /* @__PURE__ */ new Set();\n function unexpected() {\n return new InvalidSelectorError(`Unexpected token \"${tokens[pos].toSource()}\" while parsing selector \"${selector}\"`);\n }\n function skipWhitespace() {\n while (tokens[pos] instanceof WhitespaceToken)\n pos++;\n }\n function isIdent(p = pos) {\n return tokens[p] instanceof IdentToken;\n }\n function isString(p = pos) {\n return tokens[p] instanceof StringToken;\n }\n function isNumber(p = pos) {\n return tokens[p] instanceof NumberToken;\n }\n function isComma(p = pos) {\n return tokens[p] instanceof CommaToken;\n }\n function isOpenParen(p = pos) {\n return tokens[p] instanceof OpenParenToken;\n }\n function isCloseParen(p = pos) {\n return tokens[p] instanceof CloseParenToken;\n }\n function isFunction(p = pos) {\n return tokens[p] instanceof FunctionToken;\n }\n function isStar(p = pos) {\n return tokens[p] instanceof DelimToken && tokens[p].value === \"*\";\n }\n function isEOF(p = pos) {\n return tokens[p] instanceof EOFToken;\n }\n function isClauseCombinator(p = pos) {\n return tokens[p] instanceof DelimToken && [\">\", \"+\", \"~\"].includes(tokens[p].value);\n }\n function isSelectorClauseEnd(p = pos) {\n return isComma(p) || isCloseParen(p) || isEOF(p) || isClauseCombinator(p) || tokens[p] instanceof WhitespaceToken;\n }\n function consumeFunctionArguments() {\n const result2 = [consumeArgument()];\n while (true) {\n skipWhitespace();\n if (!isComma())\n break;\n pos++;\n result2.push(consumeArgument());\n }\n return result2;\n }\n function consumeArgument() {\n skipWhitespace();\n if (isNumber())\n return tokens[pos++].value;\n if (isString())\n return tokens[pos++].value;\n return consumeComplexSelector();\n }\n function consumeComplexSelector() {\n const result2 = { simples: [] };\n skipWhitespace();\n if (isClauseCombinator()) {\n result2.simples.push({ selector: { functions: [{ name: \"scope\", args: [] }] }, combinator: \"\" });\n } else {\n result2.simples.push({ selector: consumeSimpleSelector(), combinator: \"\" });\n }\n while (true) {\n skipWhitespace();\n if (isClauseCombinator()) {\n result2.simples[result2.simples.length - 1].combinator = tokens[pos++].value;\n skipWhitespace();\n } else if (isSelectorClauseEnd()) {\n break;\n }\n result2.simples.push({ combinator: \"\", selector: consumeSimpleSelector() });\n }\n return result2;\n }\n function consumeSimpleSelector() {\n let rawCSSString = \"\";\n const functions = [];\n while (!isSelectorClauseEnd()) {\n if (isIdent() || isStar()) {\n rawCSSString += tokens[pos++].toSource();\n } else if (tokens[pos] instanceof HashToken) {\n rawCSSString += tokens[pos++].toSource();\n } else if (tokens[pos] instanceof DelimToken && tokens[pos].value === \".\") {\n pos++;\n if (isIdent())\n rawCSSString += \".\" + tokens[pos++].toSource();\n else\n throw unexpected();\n } else if (tokens[pos] instanceof ColonToken) {\n pos++;\n if (isIdent()) {\n if (!customNames.has(tokens[pos].value.toLowerCase())) {\n rawCSSString += \":\" + tokens[pos++].toSource();\n } else {\n const name = tokens[pos++].value.toLowerCase();\n functions.push({ name, args: [] });\n names.add(name);\n }\n } else if (isFunction()) {\n const name = tokens[pos++].value.toLowerCase();\n if (!customNames.has(name)) {\n rawCSSString += `:${name}(${consumeBuiltinFunctionArguments()})`;\n } else {\n functions.push({ name, args: consumeFunctionArguments() });\n names.add(name);\n }\n skipWhitespace();\n if (!isCloseParen())\n throw unexpected();\n pos++;\n } else {\n throw unexpected();\n }\n } else if (tokens[pos] instanceof OpenSquareToken) {\n rawCSSString += \"[\";\n pos++;\n while (!(tokens[pos] instanceof CloseSquareToken) && !isEOF())\n rawCSSString += tokens[pos++].toSource();\n if (!(tokens[pos] instanceof CloseSquareToken))\n throw unexpected();\n rawCSSString += \"]\";\n pos++;\n } else {\n throw unexpected();\n }\n }\n if (!rawCSSString && !functions.length)\n throw unexpected();\n return { css: rawCSSString || void 0, functions };\n }\n function consumeBuiltinFunctionArguments() {\n let s = \"\";\n let balance = 1;\n while (!isEOF()) {\n if (isOpenParen() || isFunction())\n balance++;\n if (isCloseParen())\n balance--;\n if (!balance)\n break;\n s += tokens[pos++].toSource();\n }\n return s;\n }\n const result = consumeFunctionArguments();\n if (!isEOF())\n throw unexpected();\n if (result.some((arg) => typeof arg !== \"object\" || !(\"simples\" in arg)))\n throw new InvalidSelectorError(`Error while parsing selector \"${selector}\"`);\n return { selector: result, names: Array.from(names) };\n}\n\n// packages/playwright-core/src/utils/isomorphic/selectorParser.ts\nvar kNestedSelectorNames = /* @__PURE__ */ new Set([\"internal:has\", \"internal:has-not\", \"internal:and\", \"internal:or\", \"internal:chain\", \"left-of\", \"right-of\", \"above\", \"below\", \"near\"]);\nvar kNestedSelectorNamesWithDistance = /* @__PURE__ */ new Set([\"left-of\", \"right-of\", \"above\", \"below\", \"near\"]);\nvar customCSSNames = /* @__PURE__ */ new Set([\"not\", \"is\", \"where\", \"has\", \"scope\", \"light\", \"visible\", \"text\", \"text-matches\", \"text-is\", \"has-text\", \"above\", \"below\", \"right-of\", \"left-of\", \"near\", \"nth-match\"]);\nfunction parseSelector(selector) {\n const parsedStrings = parseSelectorString(selector);\n const parts = [];\n for (const part of parsedStrings.parts) {\n if (part.name === \"css\" || part.name === \"css:light\") {\n if (part.name === \"css:light\")\n part.body = \":light(\" + part.body + \")\";\n const parsedCSS = parseCSS(part.body, customCSSNames);\n parts.push({\n name: \"css\",\n body: parsedCSS.selector,\n source: part.body\n });\n continue;\n }\n if (kNestedSelectorNames.has(part.name)) {\n let innerSelector;\n let distance;\n try {\n const unescaped = JSON.parse(\"[\" + part.body + \"]\");\n if (!Array.isArray(unescaped) || unescaped.length < 1 || unescaped.length > 2 || typeof unescaped[0] !== \"string\")\n throw new InvalidSelectorError(`Malformed selector: ${part.name}=` + part.body);\n innerSelector = unescaped[0];\n if (unescaped.length === 2) {\n if (typeof unescaped[1] !== \"number\" || !kNestedSelectorNamesWithDistance.has(part.name))\n throw new InvalidSelectorError(`Malformed selector: ${part.name}=` + part.body);\n distance = unescaped[1];\n }\n } catch (e) {\n throw new InvalidSelectorError(`Malformed selector: ${part.name}=` + part.body);\n }\n const nested = { name: part.name, source: part.body, body: { parsed: parseSelector(innerSelector), distance } };\n const lastFrame = [...nested.body.parsed.parts].reverse().find((part2) => part2.name === \"internal:control\" && part2.body === \"enter-frame\");\n const lastFrameIndex = lastFrame ? nested.body.parsed.parts.indexOf(lastFrame) : -1;\n if (lastFrameIndex !== -1 && selectorPartsEqual(nested.body.parsed.parts.slice(0, lastFrameIndex + 1), parts.slice(0, lastFrameIndex + 1)))\n nested.body.parsed.parts.splice(0, lastFrameIndex + 1);\n parts.push(nested);\n continue;\n }\n parts.push({ ...part, source: part.body });\n }\n if (kNestedSelectorNames.has(parts[0].name))\n throw new InvalidSelectorError(`\"${parts[0].name}\" selector cannot be first`);\n return {\n capture: parsedStrings.capture,\n parts\n };\n}\nfunction selectorPartsEqual(list1, list2) {\n return stringifySelector({ parts: list1 }) === stringifySelector({ parts: list2 });\n}\nfunction stringifySelector(selector, forceEngineName) {\n if (typeof selector === \"string\")\n return selector;\n return selector.parts.map((p, i) => {\n let includeEngine = true;\n if (!forceEngineName && i !== selector.capture) {\n if (p.name === \"css\")\n includeEngine = false;\n else if (p.name === \"xpath\" && p.source.startsWith(\"//\") || p.source.startsWith(\"..\"))\n includeEngine = false;\n }\n const prefix = includeEngine ? p.name + \"=\" : \"\";\n return `${i === selector.capture ? \"*\" : \"\"}${prefix}${p.source}`;\n }).join(\" >> \");\n}\nfunction parseSelectorString(selector) {\n let index = 0;\n let quote;\n let start = 0;\n const result = { parts: [] };\n const append = () => {\n const part = selector.substring(start, index).trim();\n const eqIndex = part.indexOf(\"=\");\n let name;\n let body;\n if (eqIndex !== -1 && part.substring(0, eqIndex).trim().match(/^[a-zA-Z_0-9-+:*]+$/)) {\n name = part.substring(0, eqIndex).trim();\n body = part.substring(eqIndex + 1);\n } else if (part.length > 1 && part[0] === '\"' && part[part.length - 1] === '\"') {\n name = \"text\";\n body = part;\n } else if (part.length > 1 && part[0] === \"'\" && part[part.length - 1] === \"'\") {\n name = \"text\";\n body = part;\n } else if (/^\\(*\\/\\//.test(part) || part.startsWith(\"..\")) {\n name = \"xpath\";\n body = part;\n } else {\n name = \"css\";\n body = part;\n }\n let capture = false;\n if (name[0] === \"*\") {\n capture = true;\n name = name.substring(1);\n }\n result.parts.push({ name, body });\n if (capture) {\n if (result.capture !== void 0)\n throw new InvalidSelectorError(`Only one of the selectors can capture using * modifier`);\n result.capture = result.parts.length - 1;\n }\n };\n if (!selector.includes(\">>\")) {\n index = selector.length;\n append();\n return result;\n }\n const shouldIgnoreTextSelectorQuote = () => {\n const prefix = selector.substring(start, index);\n const match = prefix.match(/^\\s*text\\s*=(.*)$/);\n return !!match && !!match[1];\n };\n while (index < selector.length) {\n const c = selector[index];\n if (c === \"\\\\\" && index + 1 < selector.length) {\n index += 2;\n } else if (c === quote) {\n quote = void 0;\n index++;\n } else if (!quote && (c === '\"' || c === \"'\" || c === \"`\") && !shouldIgnoreTextSelectorQuote()) {\n quote = c;\n index++;\n } else if (!quote && c === \">\" && selector[index + 1] === \">\") {\n append();\n index += 2;\n start = index;\n } else {\n index++;\n }\n }\n append();\n return result;\n}\nfunction parseAttributeSelector(selector, allowUnquotedStrings) {\n let wp = 0;\n let EOL = selector.length === 0;\n const next = () => selector[wp] || \"\";\n const eat1 = () => {\n const result2 = next();\n ++wp;\n EOL = wp >= selector.length;\n return result2;\n };\n const syntaxError = (stage) => {\n if (EOL)\n throw new InvalidSelectorError(`Unexpected end of selector while parsing selector \\`${selector}\\``);\n throw new InvalidSelectorError(`Error while parsing selector \\`${selector}\\` - unexpected symbol \"${next()}\" at position ${wp}` + (stage ? \" during \" + stage : \"\"));\n };\n function skipSpaces() {\n while (!EOL && /\\s/.test(next()))\n eat1();\n }\n function isCSSNameChar(char) {\n return char >= \"\\x80\" || char >= \"0\" && char <= \"9\" || char >= \"A\" && char <= \"Z\" || char >= \"a\" && char <= \"z\" || char >= \"0\" && char <= \"9\" || char === \"_\" || char === \"-\";\n }\n function readIdentifier() {\n let result2 = \"\";\n skipSpaces();\n while (!EOL && isCSSNameChar(next()))\n result2 += eat1();\n return result2;\n }\n function readQuotedString(quote) {\n let result2 = eat1();\n if (result2 !== quote)\n syntaxError(\"parsing quoted string\");\n while (!EOL && next() !== quote) {\n if (next() === \"\\\\\")\n eat1();\n result2 += eat1();\n }\n if (next() !== quote)\n syntaxError(\"parsing quoted string\");\n result2 += eat1();\n return result2;\n }\n function readRegularExpression() {\n if (eat1() !== \"/\")\n syntaxError(\"parsing regular expression\");\n let source = \"\";\n let inClass = false;\n while (!EOL) {\n if (next() === \"\\\\\") {\n source += eat1();\n if (EOL)\n syntaxError(\"parsing regular expression\");\n } else if (inClass && next() === \"]\") {\n inClass = false;\n } else if (!inClass && next() === \"[\") {\n inClass = true;\n } else if (!inClass && next() === \"/\") {\n break;\n }\n source += eat1();\n }\n if (eat1() !== \"/\")\n syntaxError(\"parsing regular expression\");\n let flags = \"\";\n while (!EOL && next().match(/[dgimsuy]/))\n flags += eat1();\n try {\n return new RegExp(source, flags);\n } catch (e) {\n throw new InvalidSelectorError(`Error while parsing selector \\`${selector}\\`: ${e.message}`);\n }\n }\n function readAttributeToken() {\n let token = \"\";\n skipSpaces();\n if (next() === `'` || next() === `\"`)\n token = readQuotedString(next()).slice(1, -1);\n else\n token = readIdentifier();\n if (!token)\n syntaxError(\"parsing property path\");\n return token;\n }\n function readOperator() {\n skipSpaces();\n let op = \"\";\n if (!EOL)\n op += eat1();\n if (!EOL && op !== \"=\")\n op += eat1();\n if (![\"=\", \"*=\", \"^=\", \"$=\", \"|=\", \"~=\"].includes(op))\n syntaxError(\"parsing operator\");\n return op;\n }\n function readAttribute() {\n eat1();\n const jsonPath = [];\n jsonPath.push(readAttributeToken());\n skipSpaces();\n while (next() === \".\") {\n eat1();\n jsonPath.push(readAttributeToken());\n skipSpaces();\n }\n if (next() === \"]\") {\n eat1();\n return { name: jsonPath.join(\".\"), jsonPath, op: \"<truthy>\", value: null, caseSensitive: false };\n }\n const operator = readOperator();\n let value = void 0;\n let caseSensitive = true;\n skipSpaces();\n if (next() === \"/\") {\n if (operator !== \"=\")\n throw new InvalidSelectorError(`Error while parsing selector \\`${selector}\\` - cannot use ${operator} in attribute with regular expression`);\n value = readRegularExpression();\n } else if (next() === `'` || next() === `\"`) {\n value = readQuotedString(next()).slice(1, -1);\n skipSpaces();\n if (next() === \"i\" || next() === \"I\") {\n caseSensitive = false;\n eat1();\n } else if (next() === \"s\" || next() === \"S\") {\n caseSensitive = true;\n eat1();\n }\n } else {\n value = \"\";\n while (!EOL && (isCSSNameChar(next()) || next() === \"+\" || next() === \".\"))\n value += eat1();\n if (value === \"true\") {\n value = true;\n } else if (value === \"false\") {\n value = false;\n } else {\n if (!allowUnquotedStrings) {\n value = +value;\n if (Number.isNaN(value))\n syntaxError(\"parsing attribute value\");\n }\n }\n }\n skipSpaces();\n if (next() !== \"]\")\n syntaxError(\"parsing attribute value\");\n eat1();\n if (operator !== \"=\" && typeof value !== \"string\")\n throw new InvalidSelectorError(`Error while parsing selector \\`${selector}\\` - cannot use ${operator} in attribute with non-string matching value - ${value}`);\n return { name: jsonPath.join(\".\"), jsonPath, op: operator, value, caseSensitive };\n }\n const result = {\n name: \"\",\n attributes: []\n };\n result.name = readIdentifier();\n skipSpaces();\n while (next() === \"[\") {\n result.attributes.push(readAttribute());\n skipSpaces();\n }\n if (!EOL)\n syntaxError(void 0);\n if (!result.name && !result.attributes.length)\n throw new InvalidSelectorError(`Error while parsing selector \\`${selector}\\` - selector cannot be empty`);\n return result;\n}\n\n// packages/playwright-core/src/utils/isomorphic/locatorGenerators.ts\nfunction asLocator(lang, selector, isFrameLocator = false) {\n return asLocators(lang, selector, isFrameLocator)[0];\n}\nfunction asLocators(lang, selector, isFrameLocator = false, maxOutputSize = 20, preferredQuote) {\n try {\n return innerAsLocators(new generators[lang](preferredQuote), parseSelector(selector), isFrameLocator, maxOutputSize);\n } catch (e) {\n return [selector];\n }\n}\nfunction innerAsLocators(factory, parsed, isFrameLocator = false, maxOutputSize = 20) {\n const parts = [...parsed.parts];\n const tokens = [];\n let nextBase = isFrameLocator ? \"frame-locator\" : \"page\";\n for (let index = 0; index < parts.length; index++) {\n const part = parts[index];\n const base = nextBase;\n nextBase = \"locator\";\n if (part.name === \"nth\") {\n if (part.body === \"0\")\n tokens.push([factory.generateLocator(base, \"first\", \"\"), factory.generateLocator(base, \"nth\", \"0\")]);\n else if (part.body === \"-1\")\n tokens.push([factory.generateLocator(base, \"last\", \"\"), factory.generateLocator(base, \"nth\", \"-1\")]);\n else\n tokens.push([factory.generateLocator(base, \"nth\", part.body)]);\n continue;\n }\n if (part.name === \"internal:text\") {\n const { exact, text } = detectExact(part.body);\n tokens.push([factory.generateLocator(base, \"text\", text, { exact })]);\n continue;\n }\n if (part.name === \"internal:has-text\") {\n const { exact, text } = detectExact(part.body);\n if (!exact) {\n tokens.push([factory.generateLocator(base, \"has-text\", text, { exact })]);\n continue;\n }\n }\n if (part.name === \"internal:has-not-text\") {\n const { exact, text } = detectExact(part.body);\n if (!exact) {\n tokens.push([factory.generateLocator(base, \"has-not-text\", text, { exact })]);\n continue;\n }\n }\n if (part.name === \"internal:has\") {\n const inners = innerAsLocators(factory, part.body.parsed, false, maxOutputSize);\n tokens.push(inners.map((inner) => factory.generateLocator(base, \"has\", inner)));\n continue;\n }\n if (part.name === \"internal:has-not\") {\n const inners = innerAsLocators(factory, part.body.parsed, false, maxOutputSize);\n tokens.push(inners.map((inner) => factory.generateLocator(base, \"hasNot\", inner)));\n continue;\n }\n if (part.name === \"internal:and\") {\n const inners = innerAsLocators(factory, part.body.parsed, false, maxOutputSize);\n tokens.push(inners.map((inner) => factory.generateLocator(base, \"and\", inner)));\n continue;\n }\n if (part.name === \"internal:or\") {\n const inners = innerAsLocators(factory, part.body.parsed, false, maxOutputSize);\n tokens.push(inners.map((inner) => factory.generateLocator(base, \"or\", inner)));\n continue;\n }\n if (part.name === \"internal:chain\") {\n const inners = innerAsLocators(factory, part.body.parsed, false, maxOutputSize);\n tokens.push(inners.map((inner) => factory.generateLocator(base, \"chain\", inner)));\n continue;\n }\n if (part.name === \"internal:label\") {\n const { exact, text } = detectExact(part.body);\n tokens.push([factory.generateLocator(base, \"label\", text, { exact })]);\n continue;\n }\n if (part.name === \"internal:role\") {\n const attrSelector = parseAttributeSelector(part.body, true);\n const options = { attrs: [] };\n for (const attr of attrSelector.attributes) {\n if (attr.name === \"name\") {\n options.exact = attr.caseSensitive;\n options.name = attr.value;\n } else {\n if (attr.name === \"level\" && typeof attr.value === \"string\")\n attr.value = +attr.value;\n options.attrs.push({ name: attr.name === \"include-hidden\" ? \"includeHidden\" : attr.name, value: attr.value });\n }\n }\n tokens.push([factory.generateLocator(base, \"role\", attrSelector.name, options)]);\n continue;\n }\n if (part.name === \"internal:testid\") {\n const attrSelector = parseAttributeSelector(part.body, true);\n const { value } = attrSelector.attributes[0];\n tokens.push([factory.generateLocator(base, \"test-id\", value)]);\n continue;\n }\n if (part.name === \"internal:attr\") {\n const attrSelector = parseAttributeSelector(part.body, true);\n const { name, value, caseSensitive } = attrSelector.attributes[0];\n const text = value;\n const exact = !!caseSensitive;\n if (name === \"placeholder\") {\n tokens.push([factory.generateLocator(base, \"placeholder\", text, { exact })]);\n continue;\n }\n if (name === \"alt\") {\n tokens.push([factory.generateLocator(base, \"alt\", text, { exact })]);\n continue;\n }\n if (name === \"title\") {\n tokens.push([factory.generateLocator(base, \"title\", text, { exact })]);\n continue;\n }\n }\n if (part.name === \"internal:control\" && part.body === \"enter-frame\") {\n const lastTokens = tokens[tokens.length - 1];\n const lastPart = parts[index - 1];\n const transformed = lastTokens.map((token) => factory.chainLocators([token, factory.generateLocator(base, \"frame\", \"\")]));\n if ([\"xpath\", \"css\"].includes(lastPart.name)) {\n transformed.push(\n factory.generateLocator(base, \"frame-locator\", stringifySelector({ parts: [lastPart] })),\n factory.generateLocator(base, \"frame-locator\", stringifySelector({ parts: [lastPart] }, true))\n );\n }\n lastTokens.splice(0, lastTokens.length, ...transformed);\n nextBase = \"frame-locator\";\n continue;\n }\n const nextPart = parts[index + 1];\n const selectorPart = stringifySelector({ parts: [part] });\n const locatorPart = factory.generateLocator(base, \"default\", selectorPart);\n if (nextPart && [\"internal:has-text\", \"internal:has-not-text\"].includes(nextPart.name)) {\n const { exact, text } = detectExact(nextPart.body);\n if (!exact) {\n const nextLocatorPart = factory.generateLocator(\"locator\", nextPart.name === \"internal:has-text\" ? \"has-text\" : \"has-not-text\", text, { exact });\n const options = {};\n if (nextPart.name === \"internal:has-text\")\n options.hasText = text;\n else\n options.hasNotText = text;\n const combinedPart = factory.generateLocator(base, \"default\", selectorPart, options);\n tokens.push([factory.chainLocators([locatorPart, nextLocatorPart]), combinedPart]);\n index++;\n continue;\n }\n }\n let locatorPartWithEngine;\n if ([\"xpath\", \"css\"].includes(part.name)) {\n const selectorPart2 = stringifySelector(\n { parts: [part] },\n /* forceEngineName */\n true\n );\n locatorPartWithEngine = factory.generateLocator(base, \"default\", selectorPart2);\n }\n tokens.push([locatorPart, locatorPartWithEngine].filter(Boolean));\n }\n return combineTokens(factory, tokens, maxOutputSize);\n}\nfunction combineTokens(factory, tokens, maxOutputSize) {\n const currentTokens = tokens.map(() => \"\");\n const result = [];\n const visit = (index) => {\n if (index === tokens.length) {\n result.push(factory.chainLocators(currentTokens));\n return currentTokens.length < maxOutputSize;\n }\n for (const taken of tokens[index]) {\n currentTokens[index] = taken;\n if (!visit(index + 1))\n return false;\n }\n return true;\n };\n visit(0);\n return result;\n}\nfunction detectExact(text) {\n let exact = false;\n const match = text.match(/^\\/(.*)\\/([igm]*)$/);\n if (match)\n return { text: new RegExp(match[1], match[2]) };\n if (text.endsWith('\"')) {\n text = JSON.parse(text);\n exact = true;\n } else if (text.endsWith('\"s')) {\n text = JSON.parse(text.substring(0, text.length - 1));\n exact = true;\n } else if (text.endsWith('\"i')) {\n text = JSON.parse(text.substring(0, text.length - 1));\n exact = false;\n }\n return { exact, text };\n}\nvar JavaScriptLocatorFactory = class {\n constructor(preferredQuote) {\n this.preferredQuote = preferredQuote;\n }\n generateLocator(base, kind, body, options = {}) {\n switch (kind) {\n case \"default\":\n if (options.hasText !== void 0)\n return `locator(${this.quote(body)}, { hasText: ${this.toHasText(options.hasText)} })`;\n if (options.hasNotText !== void 0)\n return `locator(${this.quote(body)}, { hasNotText: ${this.toHasText(options.hasNotText)} })`;\n return `locator(${this.quote(body)})`;\n case \"frame-locator\":\n return `frameLocator(${this.quote(body)})`;\n case \"frame\":\n return `contentFrame()`;\n case \"nth\":\n return `nth(${body})`;\n case \"first\":\n return `first()`;\n case \"last\":\n return `last()`;\n case \"role\":\n const attrs = [];\n if (isRegExp(options.name)) {\n attrs.push(`name: ${this.regexToSourceString(options.name)}`);\n } else if (typeof options.name === \"string\") {\n attrs.push(`name: ${this.quote(options.name)}`);\n if (options.exact)\n attrs.push(`exact: true`);\n }\n for (const { name, value } of options.attrs)\n attrs.push(`${name}: ${typeof value === \"string\" ? this.quote(value) : value}`);\n const attrString = attrs.length ? `, { ${attrs.join(\", \")} }` : \"\";\n return `getByRole(${this.quote(body)}${attrString})`;\n case \"has-text\":\n return `filter({ hasText: ${this.toHasText(body)} })`;\n case \"has-not-text\":\n return `filter({ hasNotText: ${this.toHasText(body)} })`;\n case \"has\":\n return `filter({ has: ${body} })`;\n case \"hasNot\":\n return `filter({ hasNot: ${body} })`;\n case \"and\":\n return `and(${body})`;\n case \"or\":\n return `or(${body})`;\n case \"chain\":\n return `locator(${body})`;\n case \"test-id\":\n return `getByTestId(${this.toTestIdValue(body)})`;\n case \"text\":\n return this.toCallWithExact(\"getByText\", body, !!options.exact);\n case \"alt\":\n return this.toCallWithExact(\"getByAltText\", body, !!options.exact);\n case \"placeholder\":\n return this.toCallWithExact(\"getByPlaceholder\", body, !!options.exact);\n case \"label\":\n return this.toCallWithExact(\"getByLabel\", body, !!options.exact);\n case \"title\":\n return this.toCallWithExact(\"getByTitle\", body, !!options.exact);\n default:\n throw new Error(\"Unknown selector kind \" + kind);\n }\n }\n chainLocators(locators) {\n return locators.join(\".\");\n }\n regexToSourceString(re) {\n return normalizeEscapedRegexQuotes(String(re));\n }\n toCallWithExact(method, body, exact) {\n if (isRegExp(body))\n return `${method}(${this.regexToSourceString(body)})`;\n return exact ? `${method}(${this.quote(body)}, { exact: true })` : `${method}(${this.quote(body)})`;\n }\n toHasText(body) {\n if (isRegExp(body))\n return this.regexToSourceString(body);\n return this.quote(body);\n }\n toTestIdValue(value) {\n if (isRegExp(value))\n return this.regexToSourceString(value);\n return this.quote(value);\n }\n quote(text) {\n var _a;\n return escapeWithQuotes(text, (_a = this.preferredQuote) != null ? _a : \"'\");\n }\n};\nvar PythonLocatorFactory = class {\n generateLocator(base, kind, body, options = {}) {\n switch (kind) {\n case \"default\":\n if (options.hasText !== void 0)\n return `locator(${this.quote(body)}, has_text=${this.toHasText(options.hasText)})`;\n if (options.hasNotText !== void 0)\n return `locator(${this.quote(body)}, has_not_text=${this.toHasText(options.hasNotText)})`;\n return `locator(${this.quote(body)})`;\n case \"frame-locator\":\n return `frame_locator(${this.quote(body)})`;\n case \"frame\":\n return `content_frame`;\n case \"nth\":\n return `nth(${body})`;\n case \"first\":\n return `first`;\n case \"last\":\n return `last`;\n case \"role\":\n const attrs = [];\n if (isRegExp(options.name)) {\n attrs.push(`name=${this.regexToString(options.name)}`);\n } else if (typeof options.name === \"string\") {\n attrs.push(`name=${this.quote(options.name)}`);\n if (options.exact)\n attrs.push(`exact=True`);\n }\n for (const { name, value } of options.attrs) {\n let valueString = typeof value === \"string\" ? this.quote(value) : value;\n if (typeof value === \"boolean\")\n valueString = value ? \"True\" : \"False\";\n attrs.push(`${toSnakeCase(name)}=${valueString}`);\n }\n const attrString = attrs.length ? `, ${attrs.join(\", \")}` : \"\";\n return `get_by_role(${this.quote(body)}${attrString})`;\n case \"has-text\":\n return `filter(has_text=${this.toHasText(body)})`;\n case \"has-not-text\":\n return `filter(has_not_text=${this.toHasText(body)})`;\n case \"has\":\n return `filter(has=${body})`;\n case \"hasNot\":\n return `filter(has_not=${body})`;\n case \"and\":\n return `and_(${body})`;\n case \"or\":\n return `or_(${body})`;\n case \"chain\":\n return `locator(${body})`;\n case \"test-id\":\n return `get_by_test_id(${this.toTestIdValue(body)})`;\n case \"text\":\n return this.toCallWithExact(\"get_by_text\", body, !!options.exact);\n case \"alt\":\n return this.toCallWithExact(\"get_by_alt_text\", body, !!options.exact);\n case \"placeholder\":\n return this.toCallWithExact(\"get_by_placeholder\", body, !!options.exact);\n case \"label\":\n return this.toCallWithExact(\"get_by_label\", body, !!options.exact);\n case \"title\":\n return this.toCallWithExact(\"get_by_title\", body, !!options.exact);\n default:\n throw new Error(\"Unknown selector kind \" + kind);\n }\n }\n chainLocators(locators) {\n return locators.join(\".\");\n }\n regexToString(body) {\n const suffix = body.flags.includes(\"i\") ? \", re.IGNORECASE\" : \"\";\n return `re.compile(r\"${normalizeEscapedRegexQuotes(body.source).replace(/\\\\\\//, \"/\").replace(/\"/g, '\\\\\"')}\"${suffix})`;\n }\n toCallWithExact(method, body, exact) {\n if (isRegExp(body))\n return `${method}(${this.regexToString(body)})`;\n if (exact)\n return `${method}(${this.quote(body)}, exact=True)`;\n return `${method}(${this.quote(body)})`;\n }\n toHasText(body) {\n if (isRegExp(body))\n return this.regexToString(body);\n return `${this.quote(body)}`;\n }\n toTestIdValue(value) {\n if (isRegExp(value))\n return this.regexToString(value);\n return this.quote(value);\n }\n quote(text) {\n return escapeWithQuotes(text, '\"');\n }\n};\nvar JavaLocatorFactory = class {\n generateLocator(base, kind, body, options = {}) {\n let clazz;\n switch (base) {\n case \"page\":\n clazz = \"Page\";\n break;\n case \"frame-locator\":\n clazz = \"FrameLocator\";\n break;\n case \"locator\":\n clazz = \"Locator\";\n break;\n }\n switch (kind) {\n case \"default\":\n if (options.hasText !== void 0)\n return `locator(${this.quote(body)}, new ${clazz}.LocatorOptions().setHasText(${this.toHasText(options.hasText)}))`;\n if (options.hasNotText !== void 0)\n return `locator(${this.quote(body)}, new ${clazz}.LocatorOptions().setHasNotText(${this.toHasText(options.hasNotText)}))`;\n return `locator(${this.quote(body)})`;\n case \"frame-locator\":\n return `frameLocator(${this.quote(body)})`;\n case \"frame\":\n return `contentFrame()`;\n case \"nth\":\n return `nth(${body})`;\n case \"first\":\n return `first()`;\n case \"last\":\n return `last()`;\n case \"role\":\n const attrs = [];\n if (isRegExp(options.name)) {\n attrs.push(`.setName(${this.regexToString(options.name)})`);\n } else if (typeof options.name === \"string\") {\n attrs.push(`.setName(${this.quote(options.name)})`);\n if (options.exact)\n attrs.push(`.setExact(true)`);\n }\n for (const { name, value } of options.attrs)\n attrs.push(`.set${toTitleCase(name)}(${typeof value === \"string\" ? this.quote(value) : value})`);\n const attrString = attrs.length ? `, new ${clazz}.GetByRoleOptions()${attrs.join(\"\")}` : \"\";\n return `getByRole(AriaRole.${toSnakeCase(body).toUpperCase()}${attrString})`;\n case \"has-text\":\n return `filter(new ${clazz}.FilterOptions().setHasText(${this.toHasText(body)}))`;\n case \"has-not-text\":\n return `filter(new ${clazz}.FilterOptions().setHasNotText(${this.toHasText(body)}))`;\n case \"has\":\n return `filter(new ${clazz}.FilterOptions().setHas(${body}))`;\n case \"hasNot\":\n return `filter(new ${clazz}.FilterOptions().setHasNot(${body}))`;\n case \"and\":\n return `and(${body})`;\n case \"or\":\n return `or(${body})`;\n case \"chain\":\n return `locator(${body})`;\n case \"test-id\":\n return `getByTestId(${this.toTestIdValue(body)})`;\n case \"text\":\n return this.toCallWithExact(clazz, \"getByText\", body, !!options.exact);\n case \"alt\":\n return this.toCallWithExact(clazz, \"getByAltText\", body, !!options.exact);\n case \"placeholder\":\n return this.toCallWithExact(clazz, \"getByPlaceholder\", body, !!options.exact);\n case \"label\":\n return this.toCallWithExact(clazz, \"getByLabel\", body, !!options.exact);\n case \"title\":\n return this.toCallWithExact(clazz, \"getByTitle\", body, !!options.exact);\n default:\n throw new Error(\"Unknown selector kind \" + kind);\n }\n }\n chainLocators(locators) {\n return locators.join(\".\");\n }\n regexToString(body) {\n const suffix = body.flags.includes(\"i\") ? \", Pattern.CASE_INSENSITIVE\" : \"\";\n return `Pattern.compile(${this.quote(normalizeEscapedRegexQuotes(body.source))}${suffix})`;\n }\n toCallWithExact(clazz, method, body, exact) {\n if (isRegExp(body))\n return `${method}(${this.regexToString(body)})`;\n if (exact)\n return `${method}(${this.quote(body)}, new ${clazz}.${toTitleCase(method)}Options().setExact(true))`;\n return `${method}(${this.quote(body)})`;\n }\n toHasText(body) {\n if (isRegExp(body))\n return this.regexToString(body);\n return this.quote(body);\n }\n toTestIdValue(value) {\n if (isRegExp(value))\n return this.regexToString(value);\n return this.quote(value);\n }\n quote(text) {\n return escapeWithQuotes(text, '\"');\n }\n};\nvar CSharpLocatorFactory = class {\n generateLocator(base, kind, body, options = {}) {\n switch (kind) {\n case \"default\":\n if (options.hasText !== void 0)\n return `Locator(${this.quote(body)}, new() { ${this.toHasText(options.hasText)} })`;\n if (options.hasNotText !== void 0)\n return `Locator(${this.quote(body)}, new() { ${this.toHasNotText(options.hasNotText)} })`;\n return `Locator(${this.quote(body)})`;\n case \"frame-locator\":\n return `FrameLocator(${this.quote(body)})`;\n case \"frame\":\n return `ContentFrame`;\n case \"nth\":\n return `Nth(${body})`;\n case \"first\":\n return `First`;\n case \"last\":\n return `Last`;\n case \"role\":\n const attrs = [];\n if (isRegExp(options.name)) {\n attrs.push(`NameRegex = ${this.regexToString(options.name)}`);\n } else if (typeof options.name === \"string\") {\n attrs.push(`Name = ${this.quote(options.name)}`);\n if (options.exact)\n attrs.push(`Exact = true`);\n }\n for (const { name, value } of options.attrs)\n attrs.push(`${toTitleCase(name)} = ${typeof value === \"string\" ? this.quote(value) : value}`);\n const attrString = attrs.length ? `, new() { ${attrs.join(\", \")} }` : \"\";\n return `GetByRole(AriaRole.${toTitleCase(body)}${attrString})`;\n case \"has-text\":\n return `Filter(new() { ${this.toHasText(body)} })`;\n case \"has-not-text\":\n return `Filter(new() { ${this.toHasNotText(body)} })`;\n case \"has\":\n return `Filter(new() { Has = ${body} })`;\n case \"hasNot\":\n return `Filter(new() { HasNot = ${body} })`;\n case \"and\":\n return `And(${body})`;\n case \"or\":\n return `Or(${body})`;\n case \"chain\":\n return `Locator(${body})`;\n case \"test-id\":\n return `GetByTestId(${this.toTestIdValue(body)})`;\n case \"text\":\n return this.toCallWithExact(\"GetByText\", body, !!options.exact);\n case \"alt\":\n return this.toCallWithExact(\"GetByAltText\", body, !!options.exact);\n case \"placeholder\":\n return this.toCallWithExact(\"GetByPlaceholder\", body, !!options.exact);\n case \"label\":\n return this.toCallWithExact(\"GetByLabel\", body, !!options.exact);\n case \"title\":\n return this.toCallWithExact(\"GetByTitle\", body, !!options.exact);\n default:\n throw new Error(\"Unknown selector kind \" + kind);\n }\n }\n chainLocators(locators) {\n return locators.join(\".\");\n }\n regexToString(body) {\n const suffix = body.flags.includes(\"i\") ? \", RegexOptions.IgnoreCase\" : \"\";\n return `new Regex(${this.quote(normalizeEscapedRegexQuotes(body.source))}${suffix})`;\n }\n toCallWithExact(method, body, exact) {\n if (isRegExp(body))\n return `${method}(${this.regexToString(body)})`;\n if (exact)\n return `${method}(${this.quote(body)}, new() { Exact = true })`;\n return `${method}(${this.quote(body)})`;\n }\n toHasText(body) {\n if (isRegExp(body))\n return `HasTextRegex = ${this.regexToString(body)}`;\n return `HasText = ${this.quote(body)}`;\n }\n toTestIdValue(value) {\n if (isRegExp(value))\n return this.regexToString(value);\n return this.quote(value);\n }\n toHasNotText(body) {\n if (isRegExp(body))\n return `HasNotTextRegex = ${this.regexToString(body)}`;\n return `HasNotText = ${this.quote(body)}`;\n }\n quote(text) {\n return escapeWithQuotes(text, '\"');\n }\n};\nvar JsonlLocatorFactory = class {\n generateLocator(base, kind, body, options = {}) {\n return JSON.stringify({\n kind,\n body,\n options\n });\n }\n chainLocators(locators) {\n const objects = locators.map((l) => JSON.parse(l));\n for (let i = 0; i < objects.length - 1; ++i)\n objects[i].next = objects[i + 1];\n return JSON.stringify(objects[0]);\n }\n};\nvar generators = {\n javascript: JavaScriptLocatorFactory,\n python: PythonLocatorFactory,\n java: JavaLocatorFactory,\n csharp: CSharpLocatorFactory,\n jsonl: JsonlLocatorFactory\n};\nfunction isRegExp(obj) {\n return obj instanceof RegExp;\n}\n\n// packages/playwright-core/src/server/injected/consoleApi.ts\nvar selectorSymbol = Symbol(\"selector\");\nvar _Locator = class _Locator {\n constructor(injectedScript, selector, options) {\n if (options == null ? void 0 : options.hasText)\n selector += ` >> internal:has-text=${escapeForTextSelector(options.hasText, false)}`;\n if (options == null ? void 0 : options.hasNotText)\n selector += ` >> internal:has-not-text=${escapeForTextSelector(options.hasNotText, false)}`;\n if (options == null ? void 0 : options.has)\n selector += ` >> internal:has=` + JSON.stringify(options.has[selectorSymbol]);\n if (options == null ? void 0 : options.hasNot)\n selector += ` >> internal:has-not=` + JSON.stringify(options.hasNot[selectorSymbol]);\n this[selectorSymbol] = selector;\n if (selector) {\n const parsed = injectedScript.parseSelector(selector);\n this.element = injectedScript.querySelector(parsed, injectedScript.document, false);\n this.elements = injectedScript.querySelectorAll(parsed, injectedScript.document);\n }\n const selectorBase = selector;\n const self = this;\n self.locator = (selector2, options2) => {\n return new _Locator(injectedScript, selectorBase ? selectorBase + \" >> \" + selector2 : selector2, options2);\n };\n self.getByTestId = (testId) => self.locator(getByTestIdSelector(injectedScript.testIdAttributeNameForStrictErrorAndConsoleCodegen(), testId));\n self.getByAltText = (text, options2) => self.locator(getByAltTextSelector(text, options2));\n self.getByLabel = (text, options2) => self.locator(getByLabelSelector(text, options2));\n self.getByPlaceholder = (text, options2) => self.locator(getByPlaceholderSelector(text, options2));\n self.getByText = (text, options2) => self.locator(getByTextSelector(text, options2));\n self.getByTitle = (text, options2) => self.locator(getByTitleSelector(text, options2));\n self.getByRole = (role, options2 = {}) => self.locator(getByRoleSelector(role, options2));\n self.filter = (options2) => new _Locator(injectedScript, selector, options2);\n self.first = () => self.locator(\"nth=0\");\n self.last = () => self.locator(\"nth=-1\");\n self.nth = (index) => self.locator(`nth=${index}`);\n self.and = (locator) => new _Locator(injectedScript, selectorBase + ` >> internal:and=` + JSON.stringify(locator[selectorSymbol]));\n self.or = (locator) => new _Locator(injectedScript, selectorBase + ` >> internal:or=` + JSON.stringify(locator[selectorSymbol]));\n }\n};\nselectorSymbol;\nvar Locator = _Locator;\nvar ConsoleAPI = class {\n constructor(injectedScript) {\n this._injectedScript = injectedScript;\n if (this._injectedScript.window.playwright)\n return;\n this._injectedScript.window.playwright = {\n $: (selector, strict) => this._querySelector(selector, !!strict),\n $$: (selector) => this._querySelectorAll(selector),\n inspect: (selector) => this._inspect(selector),\n selector: (element) => this._selector(element),\n generateLocator: (element, language) => this._generateLocator(element, language),\n ariaSnapshot: (element) => {\n const snapshot = this._injectedScript.ariaSnapshot(element || this._injectedScript.document.body);\n console.log(snapshot);\n },\n resume: () => this._resume(),\n ...new Locator(injectedScript, \"\")\n };\n delete this._injectedScript.window.playwright.filter;\n delete this._injectedScript.window.playwright.first;\n delete this._injectedScript.window.playwright.last;\n delete this._injectedScript.window.playwright.nth;\n delete this._injectedScript.window.playwright.and;\n delete this._injectedScript.window.playwright.or;\n }\n _querySelector(selector, strict) {\n if (typeof selector !== \"string\")\n throw new Error(`Usage: playwright.query('Playwright >> selector').`);\n const parsed = this._injectedScript.parseSelector(selector);\n return this._injectedScript.querySelector(parsed, this._injectedScript.document, strict);\n }\n _querySelectorAll(selector) {\n if (typeof selector !== \"string\")\n throw new Error(`Usage: playwright.$$('Playwright >> selector').`);\n const parsed = this._injectedScript.parseSelector(selector);\n return this._injectedScript.querySelectorAll(parsed, this._injectedScript.document);\n }\n _inspect(selector) {\n if (typeof selector !== \"string\")\n throw new Error(`Usage: playwright.inspect('Playwright >> selector').`);\n this._injectedScript.window.inspect(this._querySelector(selector, false));\n }\n _selector(element) {\n if (!(element instanceof Element))\n throw new Error(`Usage: playwright.selector(element).`);\n return this._injectedScript.generateSelectorSimple(element);\n }\n _generateLocator(element, language) {\n if (!(element instanceof Element))\n throw new Error(`Usage: playwright.locator(element).`);\n const selector = this._injectedScript.generateSelectorSimple(element);\n return asLocator(language || \"javascript\", selector);\n }\n _resume() {\n this._injectedScript.window.__pw_resume().catch(() => {\n });\n }\n};\nvar consoleApi_default = ConsoleAPI;\n";
|
|
7
|
+
const source = exports.source = "\nvar __commonJS = obj => {\n let required = false;\n let result;\n return function __require() {\n if (!required) {\n required = true;\n let fn;\n for (const name in obj) { fn = obj[name]; break; }\n const module = { exports: {} };\n fn(module.exports, module);\n result = module.exports;\n }\n return result;\n }\n};\nvar __export = (target, all) => {for (var name in all) target[name] = all[name];};\nvar __toESM = mod => ({ ...mod, 'default': mod });\nvar __toCommonJS = mod => ({ ...mod, __esModule: true });\n\n\n// packages/playwright-core/src/server/injected/consoleApi.ts\nvar consoleApi_exports = {};\n__export(consoleApi_exports, {\n default: () => consoleApi_default\n});\nmodule.exports = __toCommonJS(consoleApi_exports);\n\n// packages/playwright-core/src/utils/isomorphic/stringUtils.ts\nfunction escapeWithQuotes(text, char = \"'\") {\n const stringified = JSON.stringify(text);\n const escapedText = stringified.substring(1, stringified.length - 1).replace(/\\\\\"/g, '\"');\n if (char === \"'\")\n return char + escapedText.replace(/[']/g, \"\\\\'\") + char;\n if (char === '\"')\n return char + escapedText.replace(/[\"]/g, '\\\\\"') + char;\n if (char === \"`\")\n return char + escapedText.replace(/[`]/g, \"`\") + char;\n throw new Error(\"Invalid escape char\");\n}\nfunction toTitleCase(name) {\n return name.charAt(0).toUpperCase() + name.substring(1);\n}\nfunction toSnakeCase(name) {\n return name.replace(/([a-z0-9])([A-Z])/g, \"$1_$2\").replace(/([A-Z])([A-Z][a-z])/g, \"$1_$2\").toLowerCase();\n}\nfunction normalizeEscapedRegexQuotes(source) {\n return source.replace(/(^|[^\\\\])(\\\\\\\\)*\\\\(['\"`])/g, \"$1$2$3\");\n}\nfunction escapeRegexForSelector(re) {\n if (re.unicode || re.unicodeSets)\n return String(re);\n return String(re).replace(/(^|[^\\\\])(\\\\\\\\)*([\"'`])/g, \"$1$2\\\\$3\").replace(/>>/g, \"\\\\>\\\\>\");\n}\nfunction escapeForTextSelector(text, exact) {\n if (typeof text !== \"string\")\n return escapeRegexForSelector(text);\n return `${JSON.stringify(text)}${exact ? \"s\" : \"i\"}`;\n}\nfunction escapeForAttributeSelector(value, exact) {\n if (typeof value !== \"string\")\n return escapeRegexForSelector(value);\n return `\"${value.replace(/\\\\/g, \"\\\\\\\\\").replace(/[\"]/g, '\\\\\"')}\"${exact ? \"s\" : \"i\"}`;\n}\n\n// packages/playwright-core/src/utils/isomorphic/locatorUtils.ts\nfunction getByAttributeTextSelector(attrName, text, options) {\n return `internal:attr=[${attrName}=${escapeForAttributeSelector(text, (options == null ? void 0 : options.exact) || false)}]`;\n}\nfunction getByTestIdSelector(testIdAttributeName, testId) {\n return `internal:testid=[${testIdAttributeName}=${escapeForAttributeSelector(testId, true)}]`;\n}\nfunction getByLabelSelector(text, options) {\n return \"internal:label=\" + escapeForTextSelector(text, !!(options == null ? void 0 : options.exact));\n}\nfunction getByAltTextSelector(text, options) {\n return getByAttributeTextSelector(\"alt\", text, options);\n}\nfunction getByTitleSelector(text, options) {\n return getByAttributeTextSelector(\"title\", text, options);\n}\nfunction getByPlaceholderSelector(text, options) {\n return getByAttributeTextSelector(\"placeholder\", text, options);\n}\nfunction getByTextSelector(text, options) {\n return \"internal:text=\" + escapeForTextSelector(text, !!(options == null ? void 0 : options.exact));\n}\nfunction getByRoleSelector(role, options = {}) {\n const props = [];\n if (options.checked !== void 0)\n props.push([\"checked\", String(options.checked)]);\n if (options.disabled !== void 0)\n props.push([\"disabled\", String(options.disabled)]);\n if (options.selected !== void 0)\n props.push([\"selected\", String(options.selected)]);\n if (options.expanded !== void 0)\n props.push([\"expanded\", String(options.expanded)]);\n if (options.includeHidden !== void 0)\n props.push([\"include-hidden\", String(options.includeHidden)]);\n if (options.level !== void 0)\n props.push([\"level\", String(options.level)]);\n if (options.name !== void 0)\n props.push([\"name\", escapeForAttributeSelector(options.name, !!options.exact)]);\n if (options.pressed !== void 0)\n props.push([\"pressed\", String(options.pressed)]);\n return `internal:role=${role}${props.map(([n, v]) => `[${n}=${v}]`).join(\"\")}`;\n}\n\n// packages/playwright-core/src/utils/isomorphic/cssTokenizer.ts\nvar between = function(num, first, last) {\n return num >= first && num <= last;\n};\nfunction digit(code) {\n return between(code, 48, 57);\n}\nfunction hexdigit(code) {\n return digit(code) || between(code, 65, 70) || between(code, 97, 102);\n}\nfunction uppercaseletter(code) {\n return between(code, 65, 90);\n}\nfunction lowercaseletter(code) {\n return between(code, 97, 122);\n}\nfunction letter(code) {\n return uppercaseletter(code) || lowercaseletter(code);\n}\nfunction nonascii(code) {\n return code >= 128;\n}\nfunction namestartchar(code) {\n return letter(code) || nonascii(code) || code === 95;\n}\nfunction namechar(code) {\n return namestartchar(code) || digit(code) || code === 45;\n}\nfunction nonprintable(code) {\n return between(code, 0, 8) || code === 11 || between(code, 14, 31) || code === 127;\n}\nfunction newline(code) {\n return code === 10;\n}\nfunction whitespace(code) {\n return newline(code) || code === 9 || code === 32;\n}\nvar maximumallowedcodepoint = 1114111;\nvar InvalidCharacterError = class extends Error {\n constructor(message) {\n super(message);\n this.name = \"InvalidCharacterError\";\n }\n};\nfunction preprocess(str) {\n const codepoints = [];\n for (let i = 0; i < str.length; i++) {\n let code = str.charCodeAt(i);\n if (code === 13 && str.charCodeAt(i + 1) === 10) {\n code = 10;\n i++;\n }\n if (code === 13 || code === 12)\n code = 10;\n if (code === 0)\n code = 65533;\n if (between(code, 55296, 56319) && between(str.charCodeAt(i + 1), 56320, 57343)) {\n const lead = code - 55296;\n const trail = str.charCodeAt(i + 1) - 56320;\n code = Math.pow(2, 16) + lead * Math.pow(2, 10) + trail;\n i++;\n }\n codepoints.push(code);\n }\n return codepoints;\n}\nfunction stringFromCode(code) {\n if (code <= 65535)\n return String.fromCharCode(code);\n code -= Math.pow(2, 16);\n const lead = Math.floor(code / Math.pow(2, 10)) + 55296;\n const trail = code % Math.pow(2, 10) + 56320;\n return String.fromCharCode(lead) + String.fromCharCode(trail);\n}\nfunction tokenize(str1) {\n const str = preprocess(str1);\n let i = -1;\n const tokens = [];\n let code;\n let line = 0;\n let column = 0;\n let lastLineLength = 0;\n const incrLineno = function() {\n line += 1;\n lastLineLength = column;\n column = 0;\n };\n const locStart = { line, column };\n const codepoint = function(i2) {\n if (i2 >= str.length)\n return -1;\n return str[i2];\n };\n const next = function(num) {\n if (num === void 0)\n num = 1;\n if (num > 3)\n throw \"Spec Error: no more than three codepoints of lookahead.\";\n return codepoint(i + num);\n };\n const consume = function(num) {\n if (num === void 0)\n num = 1;\n i += num;\n code = codepoint(i);\n if (newline(code))\n incrLineno();\n else\n column += num;\n return true;\n };\n const reconsume = function() {\n i -= 1;\n if (newline(code)) {\n line -= 1;\n column = lastLineLength;\n } else {\n column -= 1;\n }\n locStart.line = line;\n locStart.column = column;\n return true;\n };\n const eof = function(codepoint2) {\n if (codepoint2 === void 0)\n codepoint2 = code;\n return codepoint2 === -1;\n };\n const donothing = function() {\n };\n const parseerror = function() {\n };\n const consumeAToken = function() {\n consumeComments();\n consume();\n if (whitespace(code)) {\n while (whitespace(next()))\n consume();\n return new WhitespaceToken();\n } else if (code === 34) {\n return consumeAStringToken();\n } else if (code === 35) {\n if (namechar(next()) || areAValidEscape(next(1), next(2))) {\n const token = new HashToken(\"\");\n if (wouldStartAnIdentifier(next(1), next(2), next(3)))\n token.type = \"id\";\n token.value = consumeAName();\n return token;\n } else {\n return new DelimToken(code);\n }\n } else if (code === 36) {\n if (next() === 61) {\n consume();\n return new SuffixMatchToken();\n } else {\n return new DelimToken(code);\n }\n } else if (code === 39) {\n return consumeAStringToken();\n } else if (code === 40) {\n return new OpenParenToken();\n } else if (code === 41) {\n return new CloseParenToken();\n } else if (code === 42) {\n if (next() === 61) {\n consume();\n return new SubstringMatchToken();\n } else {\n return new DelimToken(code);\n }\n } else if (code === 43) {\n if (startsWithANumber()) {\n reconsume();\n return consumeANumericToken();\n } else {\n return new DelimToken(code);\n }\n } else if (code === 44) {\n return new CommaToken();\n } else if (code === 45) {\n if (startsWithANumber()) {\n reconsume();\n return consumeANumericToken();\n } else if (next(1) === 45 && next(2) === 62) {\n consume(2);\n return new CDCToken();\n } else if (startsWithAnIdentifier()) {\n reconsume();\n return consumeAnIdentlikeToken();\n } else {\n return new DelimToken(code);\n }\n } else if (code === 46) {\n if (startsWithANumber()) {\n reconsume();\n return consumeANumericToken();\n } else {\n return new DelimToken(code);\n }\n } else if (code === 58) {\n return new ColonToken();\n } else if (code === 59) {\n return new SemicolonToken();\n } else if (code === 60) {\n if (next(1) === 33 && next(2) === 45 && next(3) === 45) {\n consume(3);\n return new CDOToken();\n } else {\n return new DelimToken(code);\n }\n } else if (code === 64) {\n if (wouldStartAnIdentifier(next(1), next(2), next(3)))\n return new AtKeywordToken(consumeAName());\n else\n return new DelimToken(code);\n } else if (code === 91) {\n return new OpenSquareToken();\n } else if (code === 92) {\n if (startsWithAValidEscape()) {\n reconsume();\n return consumeAnIdentlikeToken();\n } else {\n parseerror();\n return new DelimToken(code);\n }\n } else if (code === 93) {\n return new CloseSquareToken();\n } else if (code === 94) {\n if (next() === 61) {\n consume();\n return new PrefixMatchToken();\n } else {\n return new DelimToken(code);\n }\n } else if (code === 123) {\n return new OpenCurlyToken();\n } else if (code === 124) {\n if (next() === 61) {\n consume();\n return new DashMatchToken();\n } else if (next() === 124) {\n consume();\n return new ColumnToken();\n } else {\n return new DelimToken(code);\n }\n } else if (code === 125) {\n return new CloseCurlyToken();\n } else if (code === 126) {\n if (next() === 61) {\n consume();\n return new IncludeMatchToken();\n } else {\n return new DelimToken(code);\n }\n } else if (digit(code)) {\n reconsume();\n return consumeANumericToken();\n } else if (namestartchar(code)) {\n reconsume();\n return consumeAnIdentlikeToken();\n } else if (eof()) {\n return new EOFToken();\n } else {\n return new DelimToken(code);\n }\n };\n const consumeComments = function() {\n while (next(1) === 47 && next(2) === 42) {\n consume(2);\n while (true) {\n consume();\n if (code === 42 && next() === 47) {\n consume();\n break;\n } else if (eof()) {\n parseerror();\n return;\n }\n }\n }\n };\n const consumeANumericToken = function() {\n const num = consumeANumber();\n if (wouldStartAnIdentifier(next(1), next(2), next(3))) {\n const token = new DimensionToken();\n token.value = num.value;\n token.repr = num.repr;\n token.type = num.type;\n token.unit = consumeAName();\n return token;\n } else if (next() === 37) {\n consume();\n const token = new PercentageToken();\n token.value = num.value;\n token.repr = num.repr;\n return token;\n } else {\n const token = new NumberToken();\n token.value = num.value;\n token.repr = num.repr;\n token.type = num.type;\n return token;\n }\n };\n const consumeAnIdentlikeToken = function() {\n const str2 = consumeAName();\n if (str2.toLowerCase() === \"url\" && next() === 40) {\n consume();\n while (whitespace(next(1)) && whitespace(next(2)))\n consume();\n if (next() === 34 || next() === 39)\n return new FunctionToken(str2);\n else if (whitespace(next()) && (next(2) === 34 || next(2) === 39))\n return new FunctionToken(str2);\n else\n return consumeAURLToken();\n } else if (next() === 40) {\n consume();\n return new FunctionToken(str2);\n } else {\n return new IdentToken(str2);\n }\n };\n const consumeAStringToken = function(endingCodePoint) {\n if (endingCodePoint === void 0)\n endingCodePoint = code;\n let string = \"\";\n while (consume()) {\n if (code === endingCodePoint || eof()) {\n return new StringToken(string);\n } else if (newline(code)) {\n parseerror();\n reconsume();\n return new BadStringToken();\n } else if (code === 92) {\n if (eof(next()))\n donothing();\n else if (newline(next()))\n consume();\n else\n string += stringFromCode(consumeEscape());\n } else {\n string += stringFromCode(code);\n }\n }\n throw new Error(\"Internal error\");\n };\n const consumeAURLToken = function() {\n const token = new URLToken(\"\");\n while (whitespace(next()))\n consume();\n if (eof(next()))\n return token;\n while (consume()) {\n if (code === 41 || eof()) {\n return token;\n } else if (whitespace(code)) {\n while (whitespace(next()))\n consume();\n if (next() === 41 || eof(next())) {\n consume();\n return token;\n } else {\n consumeTheRemnantsOfABadURL();\n return new BadURLToken();\n }\n } else if (code === 34 || code === 39 || code === 40 || nonprintable(code)) {\n parseerror();\n consumeTheRemnantsOfABadURL();\n return new BadURLToken();\n } else if (code === 92) {\n if (startsWithAValidEscape()) {\n token.value += stringFromCode(consumeEscape());\n } else {\n parseerror();\n consumeTheRemnantsOfABadURL();\n return new BadURLToken();\n }\n } else {\n token.value += stringFromCode(code);\n }\n }\n throw new Error(\"Internal error\");\n };\n const consumeEscape = function() {\n consume();\n if (hexdigit(code)) {\n const digits = [code];\n for (let total = 0; total < 5; total++) {\n if (hexdigit(next())) {\n consume();\n digits.push(code);\n } else {\n break;\n }\n }\n if (whitespace(next()))\n consume();\n let value = parseInt(digits.map(function(x) {\n return String.fromCharCode(x);\n }).join(\"\"), 16);\n if (value > maximumallowedcodepoint)\n value = 65533;\n return value;\n } else if (eof()) {\n return 65533;\n } else {\n return code;\n }\n };\n const areAValidEscape = function(c1, c2) {\n if (c1 !== 92)\n return false;\n if (newline(c2))\n return false;\n return true;\n };\n const startsWithAValidEscape = function() {\n return areAValidEscape(code, next());\n };\n const wouldStartAnIdentifier = function(c1, c2, c3) {\n if (c1 === 45)\n return namestartchar(c2) || c2 === 45 || areAValidEscape(c2, c3);\n else if (namestartchar(c1))\n return true;\n else if (c1 === 92)\n return areAValidEscape(c1, c2);\n else\n return false;\n };\n const startsWithAnIdentifier = function() {\n return wouldStartAnIdentifier(code, next(1), next(2));\n };\n const wouldStartANumber = function(c1, c2, c3) {\n if (c1 === 43 || c1 === 45) {\n if (digit(c2))\n return true;\n if (c2 === 46 && digit(c3))\n return true;\n return false;\n } else if (c1 === 46) {\n if (digit(c2))\n return true;\n return false;\n } else if (digit(c1)) {\n return true;\n } else {\n return false;\n }\n };\n const startsWithANumber = function() {\n return wouldStartANumber(code, next(1), next(2));\n };\n const consumeAName = function() {\n let result = \"\";\n while (consume()) {\n if (namechar(code)) {\n result += stringFromCode(code);\n } else if (startsWithAValidEscape()) {\n result += stringFromCode(consumeEscape());\n } else {\n reconsume();\n return result;\n }\n }\n throw new Error(\"Internal parse error\");\n };\n const consumeANumber = function() {\n let repr = \"\";\n let type = \"integer\";\n if (next() === 43 || next() === 45) {\n consume();\n repr += stringFromCode(code);\n }\n while (digit(next())) {\n consume();\n repr += stringFromCode(code);\n }\n if (next(1) === 46 && digit(next(2))) {\n consume();\n repr += stringFromCode(code);\n consume();\n repr += stringFromCode(code);\n type = \"number\";\n while (digit(next())) {\n consume();\n repr += stringFromCode(code);\n }\n }\n const c1 = next(1), c2 = next(2), c3 = next(3);\n if ((c1 === 69 || c1 === 101) && digit(c2)) {\n consume();\n repr += stringFromCode(code);\n consume();\n repr += stringFromCode(code);\n type = \"number\";\n while (digit(next())) {\n consume();\n repr += stringFromCode(code);\n }\n } else if ((c1 === 69 || c1 === 101) && (c2 === 43 || c2 === 45) && digit(c3)) {\n consume();\n repr += stringFromCode(code);\n consume();\n repr += stringFromCode(code);\n consume();\n repr += stringFromCode(code);\n type = \"number\";\n while (digit(next())) {\n consume();\n repr += stringFromCode(code);\n }\n }\n const value = convertAStringToANumber(repr);\n return { type, value, repr };\n };\n const convertAStringToANumber = function(string) {\n return +string;\n };\n const consumeTheRemnantsOfABadURL = function() {\n while (consume()) {\n if (code === 41 || eof()) {\n return;\n } else if (startsWithAValidEscape()) {\n consumeEscape();\n donothing();\n } else {\n donothing();\n }\n }\n };\n let iterationCount = 0;\n while (!eof(next())) {\n tokens.push(consumeAToken());\n iterationCount++;\n if (iterationCount > str.length * 2)\n throw new Error(\"I'm infinite-looping!\");\n }\n return tokens;\n}\nvar CSSParserToken = class {\n constructor() {\n this.tokenType = \"\";\n }\n toJSON() {\n return { token: this.tokenType };\n }\n toString() {\n return this.tokenType;\n }\n toSource() {\n return \"\" + this;\n }\n};\nvar BadStringToken = class extends CSSParserToken {\n constructor() {\n super(...arguments);\n this.tokenType = \"BADSTRING\";\n }\n};\nvar BadURLToken = class extends CSSParserToken {\n constructor() {\n super(...arguments);\n this.tokenType = \"BADURL\";\n }\n};\nvar WhitespaceToken = class extends CSSParserToken {\n constructor() {\n super(...arguments);\n this.tokenType = \"WHITESPACE\";\n }\n toString() {\n return \"WS\";\n }\n toSource() {\n return \" \";\n }\n};\nvar CDOToken = class extends CSSParserToken {\n constructor() {\n super(...arguments);\n this.tokenType = \"CDO\";\n }\n toSource() {\n return \"<!--\";\n }\n};\nvar CDCToken = class extends CSSParserToken {\n constructor() {\n super(...arguments);\n this.tokenType = \"CDC\";\n }\n toSource() {\n return \"-->\";\n }\n};\nvar ColonToken = class extends CSSParserToken {\n constructor() {\n super(...arguments);\n this.tokenType = \":\";\n }\n};\nvar SemicolonToken = class extends CSSParserToken {\n constructor() {\n super(...arguments);\n this.tokenType = \";\";\n }\n};\nvar CommaToken = class extends CSSParserToken {\n constructor() {\n super(...arguments);\n this.tokenType = \",\";\n }\n};\nvar GroupingToken = class extends CSSParserToken {\n constructor() {\n super(...arguments);\n this.value = \"\";\n this.mirror = \"\";\n }\n};\nvar OpenCurlyToken = class extends GroupingToken {\n constructor() {\n super();\n this.tokenType = \"{\";\n this.value = \"{\";\n this.mirror = \"}\";\n }\n};\nvar CloseCurlyToken = class extends GroupingToken {\n constructor() {\n super();\n this.tokenType = \"}\";\n this.value = \"}\";\n this.mirror = \"{\";\n }\n};\nvar OpenSquareToken = class extends GroupingToken {\n constructor() {\n super();\n this.tokenType = \"[\";\n this.value = \"[\";\n this.mirror = \"]\";\n }\n};\nvar CloseSquareToken = class extends GroupingToken {\n constructor() {\n super();\n this.tokenType = \"]\";\n this.value = \"]\";\n this.mirror = \"[\";\n }\n};\nvar OpenParenToken = class extends GroupingToken {\n constructor() {\n super();\n this.tokenType = \"(\";\n this.value = \"(\";\n this.mirror = \")\";\n }\n};\nvar CloseParenToken = class extends GroupingToken {\n constructor() {\n super();\n this.tokenType = \")\";\n this.value = \")\";\n this.mirror = \"(\";\n }\n};\nvar IncludeMatchToken = class extends CSSParserToken {\n constructor() {\n super(...arguments);\n this.tokenType = \"~=\";\n }\n};\nvar DashMatchToken = class extends CSSParserToken {\n constructor() {\n super(...arguments);\n this.tokenType = \"|=\";\n }\n};\nvar PrefixMatchToken = class extends CSSParserToken {\n constructor() {\n super(...arguments);\n this.tokenType = \"^=\";\n }\n};\nvar SuffixMatchToken = class extends CSSParserToken {\n constructor() {\n super(...arguments);\n this.tokenType = \"$=\";\n }\n};\nvar SubstringMatchToken = class extends CSSParserToken {\n constructor() {\n super(...arguments);\n this.tokenType = \"*=\";\n }\n};\nvar ColumnToken = class extends CSSParserToken {\n constructor() {\n super(...arguments);\n this.tokenType = \"||\";\n }\n};\nvar EOFToken = class extends CSSParserToken {\n constructor() {\n super(...arguments);\n this.tokenType = \"EOF\";\n }\n toSource() {\n return \"\";\n }\n};\nvar DelimToken = class extends CSSParserToken {\n constructor(code) {\n super();\n this.tokenType = \"DELIM\";\n this.value = \"\";\n this.value = stringFromCode(code);\n }\n toString() {\n return \"DELIM(\" + this.value + \")\";\n }\n toJSON() {\n const json = this.constructor.prototype.constructor.prototype.toJSON.call(this);\n json.value = this.value;\n return json;\n }\n toSource() {\n if (this.value === \"\\\\\")\n return \"\\\\\\n\";\n else\n return this.value;\n }\n};\nvar StringValuedToken = class extends CSSParserToken {\n constructor() {\n super(...arguments);\n this.value = \"\";\n }\n ASCIIMatch(str) {\n return this.value.toLowerCase() === str.toLowerCase();\n }\n toJSON() {\n const json = this.constructor.prototype.constructor.prototype.toJSON.call(this);\n json.value = this.value;\n return json;\n }\n};\nvar IdentToken = class extends StringValuedToken {\n constructor(val) {\n super();\n this.tokenType = \"IDENT\";\n this.value = val;\n }\n toString() {\n return \"IDENT(\" + this.value + \")\";\n }\n toSource() {\n return escapeIdent(this.value);\n }\n};\nvar FunctionToken = class extends StringValuedToken {\n constructor(val) {\n super();\n this.tokenType = \"FUNCTION\";\n this.value = val;\n this.mirror = \")\";\n }\n toString() {\n return \"FUNCTION(\" + this.value + \")\";\n }\n toSource() {\n return escapeIdent(this.value) + \"(\";\n }\n};\nvar AtKeywordToken = class extends StringValuedToken {\n constructor(val) {\n super();\n this.tokenType = \"AT-KEYWORD\";\n this.value = val;\n }\n toString() {\n return \"AT(\" + this.value + \")\";\n }\n toSource() {\n return \"@\" + escapeIdent(this.value);\n }\n};\nvar HashToken = class extends StringValuedToken {\n constructor(val) {\n super();\n this.tokenType = \"HASH\";\n this.value = val;\n this.type = \"unrestricted\";\n }\n toString() {\n return \"HASH(\" + this.value + \")\";\n }\n toJSON() {\n const json = this.constructor.prototype.constructor.prototype.toJSON.call(this);\n json.value = this.value;\n json.type = this.type;\n return json;\n }\n toSource() {\n if (this.type === \"id\")\n return \"#\" + escapeIdent(this.value);\n else\n return \"#\" + escapeHash(this.value);\n }\n};\nvar StringToken = class extends StringValuedToken {\n constructor(val) {\n super();\n this.tokenType = \"STRING\";\n this.value = val;\n }\n toString() {\n return '\"' + escapeString(this.value) + '\"';\n }\n};\nvar URLToken = class extends StringValuedToken {\n constructor(val) {\n super();\n this.tokenType = \"URL\";\n this.value = val;\n }\n toString() {\n return \"URL(\" + this.value + \")\";\n }\n toSource() {\n return 'url(\"' + escapeString(this.value) + '\")';\n }\n};\nvar NumberToken = class extends CSSParserToken {\n constructor() {\n super();\n this.tokenType = \"NUMBER\";\n this.type = \"integer\";\n this.repr = \"\";\n }\n toString() {\n if (this.type === \"integer\")\n return \"INT(\" + this.value + \")\";\n return \"NUMBER(\" + this.value + \")\";\n }\n toJSON() {\n const json = super.toJSON();\n json.value = this.value;\n json.type = this.type;\n json.repr = this.repr;\n return json;\n }\n toSource() {\n return this.repr;\n }\n};\nvar PercentageToken = class extends CSSParserToken {\n constructor() {\n super();\n this.tokenType = \"PERCENTAGE\";\n this.repr = \"\";\n }\n toString() {\n return \"PERCENTAGE(\" + this.value + \")\";\n }\n toJSON() {\n const json = this.constructor.prototype.constructor.prototype.toJSON.call(this);\n json.value = this.value;\n json.repr = this.repr;\n return json;\n }\n toSource() {\n return this.repr + \"%\";\n }\n};\nvar DimensionToken = class extends CSSParserToken {\n constructor() {\n super();\n this.tokenType = \"DIMENSION\";\n this.type = \"integer\";\n this.repr = \"\";\n this.unit = \"\";\n }\n toString() {\n return \"DIM(\" + this.value + \",\" + this.unit + \")\";\n }\n toJSON() {\n const json = this.constructor.prototype.constructor.prototype.toJSON.call(this);\n json.value = this.value;\n json.type = this.type;\n json.repr = this.repr;\n json.unit = this.unit;\n return json;\n }\n toSource() {\n const source = this.repr;\n let unit = escapeIdent(this.unit);\n if (unit[0].toLowerCase() === \"e\" && (unit[1] === \"-\" || between(unit.charCodeAt(1), 48, 57))) {\n unit = \"\\\\65 \" + unit.slice(1, unit.length);\n }\n return source + unit;\n }\n};\nfunction escapeIdent(string) {\n string = \"\" + string;\n let result = \"\";\n const firstcode = string.charCodeAt(0);\n for (let i = 0; i < string.length; i++) {\n const code = string.charCodeAt(i);\n if (code === 0)\n throw new InvalidCharacterError(\"Invalid character: the input contains U+0000.\");\n if (between(code, 1, 31) || code === 127 || i === 0 && between(code, 48, 57) || i === 1 && between(code, 48, 57) && firstcode === 45)\n result += \"\\\\\" + code.toString(16) + \" \";\n else if (code >= 128 || code === 45 || code === 95 || between(code, 48, 57) || between(code, 65, 90) || between(code, 97, 122))\n result += string[i];\n else\n result += \"\\\\\" + string[i];\n }\n return result;\n}\nfunction escapeHash(string) {\n string = \"\" + string;\n let result = \"\";\n for (let i = 0; i < string.length; i++) {\n const code = string.charCodeAt(i);\n if (code === 0)\n throw new InvalidCharacterError(\"Invalid character: the input contains U+0000.\");\n if (code >= 128 || code === 45 || code === 95 || between(code, 48, 57) || between(code, 65, 90) || between(code, 97, 122))\n result += string[i];\n else\n result += \"\\\\\" + code.toString(16) + \" \";\n }\n return result;\n}\nfunction escapeString(string) {\n string = \"\" + string;\n let result = \"\";\n for (let i = 0; i < string.length; i++) {\n const code = string.charCodeAt(i);\n if (code === 0)\n throw new InvalidCharacterError(\"Invalid character: the input contains U+0000.\");\n if (between(code, 1, 31) || code === 127)\n result += \"\\\\\" + code.toString(16) + \" \";\n else if (code === 34 || code === 92)\n result += \"\\\\\" + string[i];\n else\n result += string[i];\n }\n return result;\n}\n\n// packages/playwright-core/src/utils/isomorphic/cssParser.ts\nvar InvalidSelectorError = class extends Error {\n};\nfunction parseCSS(selector, customNames) {\n let tokens;\n try {\n tokens = tokenize(selector);\n if (!(tokens[tokens.length - 1] instanceof EOFToken))\n tokens.push(new EOFToken());\n } catch (e) {\n const newMessage = e.message + ` while parsing css selector \"${selector}\". Did you mean to CSS.escape it?`;\n const index = (e.stack || \"\").indexOf(e.message);\n if (index !== -1)\n e.stack = e.stack.substring(0, index) + newMessage + e.stack.substring(index + e.message.length);\n e.message = newMessage;\n throw e;\n }\n const unsupportedToken = tokens.find((token) => {\n return token instanceof AtKeywordToken || token instanceof BadStringToken || token instanceof BadURLToken || token instanceof ColumnToken || token instanceof CDOToken || token instanceof CDCToken || token instanceof SemicolonToken || // TODO: Consider using these for something, e.g. to escape complex strings.\n // For example :xpath{ (//div/bar[@attr=\"foo\"])[2]/baz }\n // Or this way :xpath( {complex-xpath-goes-here(\"hello\")} )\n token instanceof OpenCurlyToken || token instanceof CloseCurlyToken || // TODO: Consider treating these as strings?\n token instanceof URLToken || token instanceof PercentageToken;\n });\n if (unsupportedToken)\n throw new InvalidSelectorError(`Unsupported token \"${unsupportedToken.toSource()}\" while parsing css selector \"${selector}\". Did you mean to CSS.escape it?`);\n let pos = 0;\n const names = /* @__PURE__ */ new Set();\n function unexpected() {\n return new InvalidSelectorError(`Unexpected token \"${tokens[pos].toSource()}\" while parsing css selector \"${selector}\". Did you mean to CSS.escape it?`);\n }\n function skipWhitespace() {\n while (tokens[pos] instanceof WhitespaceToken)\n pos++;\n }\n function isIdent(p = pos) {\n return tokens[p] instanceof IdentToken;\n }\n function isString(p = pos) {\n return tokens[p] instanceof StringToken;\n }\n function isNumber(p = pos) {\n return tokens[p] instanceof NumberToken;\n }\n function isComma(p = pos) {\n return tokens[p] instanceof CommaToken;\n }\n function isOpenParen(p = pos) {\n return tokens[p] instanceof OpenParenToken;\n }\n function isCloseParen(p = pos) {\n return tokens[p] instanceof CloseParenToken;\n }\n function isFunction(p = pos) {\n return tokens[p] instanceof FunctionToken;\n }\n function isStar(p = pos) {\n return tokens[p] instanceof DelimToken && tokens[p].value === \"*\";\n }\n function isEOF(p = pos) {\n return tokens[p] instanceof EOFToken;\n }\n function isClauseCombinator(p = pos) {\n return tokens[p] instanceof DelimToken && [\">\", \"+\", \"~\"].includes(tokens[p].value);\n }\n function isSelectorClauseEnd(p = pos) {\n return isComma(p) || isCloseParen(p) || isEOF(p) || isClauseCombinator(p) || tokens[p] instanceof WhitespaceToken;\n }\n function consumeFunctionArguments() {\n const result2 = [consumeArgument()];\n while (true) {\n skipWhitespace();\n if (!isComma())\n break;\n pos++;\n result2.push(consumeArgument());\n }\n return result2;\n }\n function consumeArgument() {\n skipWhitespace();\n if (isNumber())\n return tokens[pos++].value;\n if (isString())\n return tokens[pos++].value;\n return consumeComplexSelector();\n }\n function consumeComplexSelector() {\n const result2 = { simples: [] };\n skipWhitespace();\n if (isClauseCombinator()) {\n result2.simples.push({ selector: { functions: [{ name: \"scope\", args: [] }] }, combinator: \"\" });\n } else {\n result2.simples.push({ selector: consumeSimpleSelector(), combinator: \"\" });\n }\n while (true) {\n skipWhitespace();\n if (isClauseCombinator()) {\n result2.simples[result2.simples.length - 1].combinator = tokens[pos++].value;\n skipWhitespace();\n } else if (isSelectorClauseEnd()) {\n break;\n }\n result2.simples.push({ combinator: \"\", selector: consumeSimpleSelector() });\n }\n return result2;\n }\n function consumeSimpleSelector() {\n let rawCSSString = \"\";\n const functions = [];\n while (!isSelectorClauseEnd()) {\n if (isIdent() || isStar()) {\n rawCSSString += tokens[pos++].toSource();\n } else if (tokens[pos] instanceof HashToken) {\n rawCSSString += tokens[pos++].toSource();\n } else if (tokens[pos] instanceof DelimToken && tokens[pos].value === \".\") {\n pos++;\n if (isIdent())\n rawCSSString += \".\" + tokens[pos++].toSource();\n else\n throw unexpected();\n } else if (tokens[pos] instanceof ColonToken) {\n pos++;\n if (isIdent()) {\n if (!customNames.has(tokens[pos].value.toLowerCase())) {\n rawCSSString += \":\" + tokens[pos++].toSource();\n } else {\n const name = tokens[pos++].value.toLowerCase();\n functions.push({ name, args: [] });\n names.add(name);\n }\n } else if (isFunction()) {\n const name = tokens[pos++].value.toLowerCase();\n if (!customNames.has(name)) {\n rawCSSString += `:${name}(${consumeBuiltinFunctionArguments()})`;\n } else {\n functions.push({ name, args: consumeFunctionArguments() });\n names.add(name);\n }\n skipWhitespace();\n if (!isCloseParen())\n throw unexpected();\n pos++;\n } else {\n throw unexpected();\n }\n } else if (tokens[pos] instanceof OpenSquareToken) {\n rawCSSString += \"[\";\n pos++;\n while (!(tokens[pos] instanceof CloseSquareToken) && !isEOF())\n rawCSSString += tokens[pos++].toSource();\n if (!(tokens[pos] instanceof CloseSquareToken))\n throw unexpected();\n rawCSSString += \"]\";\n pos++;\n } else {\n throw unexpected();\n }\n }\n if (!rawCSSString && !functions.length)\n throw unexpected();\n return { css: rawCSSString || void 0, functions };\n }\n function consumeBuiltinFunctionArguments() {\n let s = \"\";\n let balance = 1;\n while (!isEOF()) {\n if (isOpenParen() || isFunction())\n balance++;\n if (isCloseParen())\n balance--;\n if (!balance)\n break;\n s += tokens[pos++].toSource();\n }\n return s;\n }\n const result = consumeFunctionArguments();\n if (!isEOF())\n throw unexpected();\n if (result.some((arg) => typeof arg !== \"object\" || !(\"simples\" in arg)))\n throw new InvalidSelectorError(`Error while parsing css selector \"${selector}\". Did you mean to CSS.escape it?`);\n return { selector: result, names: Array.from(names) };\n}\n\n// packages/playwright-core/src/utils/isomorphic/selectorParser.ts\nvar kNestedSelectorNames = /* @__PURE__ */ new Set([\"internal:has\", \"internal:has-not\", \"internal:and\", \"internal:or\", \"internal:chain\", \"left-of\", \"right-of\", \"above\", \"below\", \"near\"]);\nvar kNestedSelectorNamesWithDistance = /* @__PURE__ */ new Set([\"left-of\", \"right-of\", \"above\", \"below\", \"near\"]);\nvar customCSSNames = /* @__PURE__ */ new Set([\"not\", \"is\", \"where\", \"has\", \"scope\", \"light\", \"visible\", \"text\", \"text-matches\", \"text-is\", \"has-text\", \"above\", \"below\", \"right-of\", \"left-of\", \"near\", \"nth-match\"]);\nfunction parseSelector(selector) {\n const parsedStrings = parseSelectorString(selector);\n const parts = [];\n for (const part of parsedStrings.parts) {\n if (part.name === \"css\" || part.name === \"css:light\") {\n if (part.name === \"css:light\")\n part.body = \":light(\" + part.body + \")\";\n const parsedCSS = parseCSS(part.body, customCSSNames);\n parts.push({\n name: \"css\",\n body: parsedCSS.selector,\n source: part.body\n });\n continue;\n }\n if (kNestedSelectorNames.has(part.name)) {\n let innerSelector;\n let distance;\n try {\n const unescaped = JSON.parse(\"[\" + part.body + \"]\");\n if (!Array.isArray(unescaped) || unescaped.length < 1 || unescaped.length > 2 || typeof unescaped[0] !== \"string\")\n throw new InvalidSelectorError(`Malformed selector: ${part.name}=` + part.body);\n innerSelector = unescaped[0];\n if (unescaped.length === 2) {\n if (typeof unescaped[1] !== \"number\" || !kNestedSelectorNamesWithDistance.has(part.name))\n throw new InvalidSelectorError(`Malformed selector: ${part.name}=` + part.body);\n distance = unescaped[1];\n }\n } catch (e) {\n throw new InvalidSelectorError(`Malformed selector: ${part.name}=` + part.body);\n }\n const nested = { name: part.name, source: part.body, body: { parsed: parseSelector(innerSelector), distance } };\n const lastFrame = [...nested.body.parsed.parts].reverse().find((part2) => part2.name === \"internal:control\" && part2.body === \"enter-frame\");\n const lastFrameIndex = lastFrame ? nested.body.parsed.parts.indexOf(lastFrame) : -1;\n if (lastFrameIndex !== -1 && selectorPartsEqual(nested.body.parsed.parts.slice(0, lastFrameIndex + 1), parts.slice(0, lastFrameIndex + 1)))\n nested.body.parsed.parts.splice(0, lastFrameIndex + 1);\n parts.push(nested);\n continue;\n }\n parts.push({ ...part, source: part.body });\n }\n if (kNestedSelectorNames.has(parts[0].name))\n throw new InvalidSelectorError(`\"${parts[0].name}\" selector cannot be first`);\n return {\n capture: parsedStrings.capture,\n parts\n };\n}\nfunction selectorPartsEqual(list1, list2) {\n return stringifySelector({ parts: list1 }) === stringifySelector({ parts: list2 });\n}\nfunction stringifySelector(selector, forceEngineName) {\n if (typeof selector === \"string\")\n return selector;\n return selector.parts.map((p, i) => {\n let includeEngine = true;\n if (!forceEngineName && i !== selector.capture) {\n if (p.name === \"css\")\n includeEngine = false;\n else if (p.name === \"xpath\" && p.source.startsWith(\"//\") || p.source.startsWith(\"..\"))\n includeEngine = false;\n }\n const prefix = includeEngine ? p.name + \"=\" : \"\";\n return `${i === selector.capture ? \"*\" : \"\"}${prefix}${p.source}`;\n }).join(\" >> \");\n}\nfunction parseSelectorString(selector) {\n let index = 0;\n let quote;\n let start = 0;\n const result = { parts: [] };\n const append = () => {\n const part = selector.substring(start, index).trim();\n const eqIndex = part.indexOf(\"=\");\n let name;\n let body;\n if (eqIndex !== -1 && part.substring(0, eqIndex).trim().match(/^[a-zA-Z_0-9-+:*]+$/)) {\n name = part.substring(0, eqIndex).trim();\n body = part.substring(eqIndex + 1);\n } else if (part.length > 1 && part[0] === '\"' && part[part.length - 1] === '\"') {\n name = \"text\";\n body = part;\n } else if (part.length > 1 && part[0] === \"'\" && part[part.length - 1] === \"'\") {\n name = \"text\";\n body = part;\n } else if (/^\\(*\\/\\//.test(part) || part.startsWith(\"..\")) {\n name = \"xpath\";\n body = part;\n } else {\n name = \"css\";\n body = part;\n }\n let capture = false;\n if (name[0] === \"*\") {\n capture = true;\n name = name.substring(1);\n }\n result.parts.push({ name, body });\n if (capture) {\n if (result.capture !== void 0)\n throw new InvalidSelectorError(`Only one of the selectors can capture using * modifier`);\n result.capture = result.parts.length - 1;\n }\n };\n if (!selector.includes(\">>\")) {\n index = selector.length;\n append();\n return result;\n }\n const shouldIgnoreTextSelectorQuote = () => {\n const prefix = selector.substring(start, index);\n const match = prefix.match(/^\\s*text\\s*=(.*)$/);\n return !!match && !!match[1];\n };\n while (index < selector.length) {\n const c = selector[index];\n if (c === \"\\\\\" && index + 1 < selector.length) {\n index += 2;\n } else if (c === quote) {\n quote = void 0;\n index++;\n } else if (!quote && (c === '\"' || c === \"'\" || c === \"`\") && !shouldIgnoreTextSelectorQuote()) {\n quote = c;\n index++;\n } else if (!quote && c === \">\" && selector[index + 1] === \">\") {\n append();\n index += 2;\n start = index;\n } else {\n index++;\n }\n }\n append();\n return result;\n}\nfunction parseAttributeSelector(selector, allowUnquotedStrings) {\n let wp = 0;\n let EOL = selector.length === 0;\n const next = () => selector[wp] || \"\";\n const eat1 = () => {\n const result2 = next();\n ++wp;\n EOL = wp >= selector.length;\n return result2;\n };\n const syntaxError = (stage) => {\n if (EOL)\n throw new InvalidSelectorError(`Unexpected end of selector while parsing selector \\`${selector}\\``);\n throw new InvalidSelectorError(`Error while parsing selector \\`${selector}\\` - unexpected symbol \"${next()}\" at position ${wp}` + (stage ? \" during \" + stage : \"\"));\n };\n function skipSpaces() {\n while (!EOL && /\\s/.test(next()))\n eat1();\n }\n function isCSSNameChar(char) {\n return char >= \"\\x80\" || char >= \"0\" && char <= \"9\" || char >= \"A\" && char <= \"Z\" || char >= \"a\" && char <= \"z\" || char >= \"0\" && char <= \"9\" || char === \"_\" || char === \"-\";\n }\n function readIdentifier() {\n let result2 = \"\";\n skipSpaces();\n while (!EOL && isCSSNameChar(next()))\n result2 += eat1();\n return result2;\n }\n function readQuotedString(quote) {\n let result2 = eat1();\n if (result2 !== quote)\n syntaxError(\"parsing quoted string\");\n while (!EOL && next() !== quote) {\n if (next() === \"\\\\\")\n eat1();\n result2 += eat1();\n }\n if (next() !== quote)\n syntaxError(\"parsing quoted string\");\n result2 += eat1();\n return result2;\n }\n function readRegularExpression() {\n if (eat1() !== \"/\")\n syntaxError(\"parsing regular expression\");\n let source = \"\";\n let inClass = false;\n while (!EOL) {\n if (next() === \"\\\\\") {\n source += eat1();\n if (EOL)\n syntaxError(\"parsing regular expression\");\n } else if (inClass && next() === \"]\") {\n inClass = false;\n } else if (!inClass && next() === \"[\") {\n inClass = true;\n } else if (!inClass && next() === \"/\") {\n break;\n }\n source += eat1();\n }\n if (eat1() !== \"/\")\n syntaxError(\"parsing regular expression\");\n let flags = \"\";\n while (!EOL && next().match(/[dgimsuy]/))\n flags += eat1();\n try {\n return new RegExp(source, flags);\n } catch (e) {\n throw new InvalidSelectorError(`Error while parsing selector \\`${selector}\\`: ${e.message}`);\n }\n }\n function readAttributeToken() {\n let token = \"\";\n skipSpaces();\n if (next() === `'` || next() === `\"`)\n token = readQuotedString(next()).slice(1, -1);\n else\n token = readIdentifier();\n if (!token)\n syntaxError(\"parsing property path\");\n return token;\n }\n function readOperator() {\n skipSpaces();\n let op = \"\";\n if (!EOL)\n op += eat1();\n if (!EOL && op !== \"=\")\n op += eat1();\n if (![\"=\", \"*=\", \"^=\", \"$=\", \"|=\", \"~=\"].includes(op))\n syntaxError(\"parsing operator\");\n return op;\n }\n function readAttribute() {\n eat1();\n const jsonPath = [];\n jsonPath.push(readAttributeToken());\n skipSpaces();\n while (next() === \".\") {\n eat1();\n jsonPath.push(readAttributeToken());\n skipSpaces();\n }\n if (next() === \"]\") {\n eat1();\n return { name: jsonPath.join(\".\"), jsonPath, op: \"<truthy>\", value: null, caseSensitive: false };\n }\n const operator = readOperator();\n let value = void 0;\n let caseSensitive = true;\n skipSpaces();\n if (next() === \"/\") {\n if (operator !== \"=\")\n throw new InvalidSelectorError(`Error while parsing selector \\`${selector}\\` - cannot use ${operator} in attribute with regular expression`);\n value = readRegularExpression();\n } else if (next() === `'` || next() === `\"`) {\n value = readQuotedString(next()).slice(1, -1);\n skipSpaces();\n if (next() === \"i\" || next() === \"I\") {\n caseSensitive = false;\n eat1();\n } else if (next() === \"s\" || next() === \"S\") {\n caseSensitive = true;\n eat1();\n }\n } else {\n value = \"\";\n while (!EOL && (isCSSNameChar(next()) || next() === \"+\" || next() === \".\"))\n value += eat1();\n if (value === \"true\") {\n value = true;\n } else if (value === \"false\") {\n value = false;\n } else {\n if (!allowUnquotedStrings) {\n value = +value;\n if (Number.isNaN(value))\n syntaxError(\"parsing attribute value\");\n }\n }\n }\n skipSpaces();\n if (next() !== \"]\")\n syntaxError(\"parsing attribute value\");\n eat1();\n if (operator !== \"=\" && typeof value !== \"string\")\n throw new InvalidSelectorError(`Error while parsing selector \\`${selector}\\` - cannot use ${operator} in attribute with non-string matching value - ${value}`);\n return { name: jsonPath.join(\".\"), jsonPath, op: operator, value, caseSensitive };\n }\n const result = {\n name: \"\",\n attributes: []\n };\n result.name = readIdentifier();\n skipSpaces();\n while (next() === \"[\") {\n result.attributes.push(readAttribute());\n skipSpaces();\n }\n if (!EOL)\n syntaxError(void 0);\n if (!result.name && !result.attributes.length)\n throw new InvalidSelectorError(`Error while parsing selector \\`${selector}\\` - selector cannot be empty`);\n return result;\n}\n\n// packages/playwright-core/src/utils/isomorphic/locatorGenerators.ts\nfunction asLocator(lang, selector, isFrameLocator = false) {\n return asLocators(lang, selector, isFrameLocator, 1)[0];\n}\nfunction asLocators(lang, selector, isFrameLocator = false, maxOutputSize = 20, preferredQuote) {\n try {\n return innerAsLocators(new generators[lang](preferredQuote), parseSelector(selector), isFrameLocator, maxOutputSize);\n } catch (e) {\n return [selector];\n }\n}\nfunction innerAsLocators(factory, parsed, isFrameLocator = false, maxOutputSize = 20) {\n const parts = [...parsed.parts];\n const tokens = [];\n let nextBase = isFrameLocator ? \"frame-locator\" : \"page\";\n for (let index = 0; index < parts.length; index++) {\n const part = parts[index];\n const base = nextBase;\n nextBase = \"locator\";\n if (part.name === \"nth\") {\n if (part.body === \"0\")\n tokens.push([factory.generateLocator(base, \"first\", \"\"), factory.generateLocator(base, \"nth\", \"0\")]);\n else if (part.body === \"-1\")\n tokens.push([factory.generateLocator(base, \"last\", \"\"), factory.generateLocator(base, \"nth\", \"-1\")]);\n else\n tokens.push([factory.generateLocator(base, \"nth\", part.body)]);\n continue;\n }\n if (part.name === \"internal:text\") {\n const { exact, text } = detectExact(part.body);\n tokens.push([factory.generateLocator(base, \"text\", text, { exact })]);\n continue;\n }\n if (part.name === \"internal:has-text\") {\n const { exact, text } = detectExact(part.body);\n if (!exact) {\n tokens.push([factory.generateLocator(base, \"has-text\", text, { exact })]);\n continue;\n }\n }\n if (part.name === \"internal:has-not-text\") {\n const { exact, text } = detectExact(part.body);\n if (!exact) {\n tokens.push([factory.generateLocator(base, \"has-not-text\", text, { exact })]);\n continue;\n }\n }\n if (part.name === \"internal:has\") {\n const inners = innerAsLocators(factory, part.body.parsed, false, maxOutputSize);\n tokens.push(inners.map((inner) => factory.generateLocator(base, \"has\", inner)));\n continue;\n }\n if (part.name === \"internal:has-not\") {\n const inners = innerAsLocators(factory, part.body.parsed, false, maxOutputSize);\n tokens.push(inners.map((inner) => factory.generateLocator(base, \"hasNot\", inner)));\n continue;\n }\n if (part.name === \"internal:and\") {\n const inners = innerAsLocators(factory, part.body.parsed, false, maxOutputSize);\n tokens.push(inners.map((inner) => factory.generateLocator(base, \"and\", inner)));\n continue;\n }\n if (part.name === \"internal:or\") {\n const inners = innerAsLocators(factory, part.body.parsed, false, maxOutputSize);\n tokens.push(inners.map((inner) => factory.generateLocator(base, \"or\", inner)));\n continue;\n }\n if (part.name === \"internal:chain\") {\n const inners = innerAsLocators(factory, part.body.parsed, false, maxOutputSize);\n tokens.push(inners.map((inner) => factory.generateLocator(base, \"chain\", inner)));\n continue;\n }\n if (part.name === \"internal:label\") {\n const { exact, text } = detectExact(part.body);\n tokens.push([factory.generateLocator(base, \"label\", text, { exact })]);\n continue;\n }\n if (part.name === \"internal:role\") {\n const attrSelector = parseAttributeSelector(part.body, true);\n const options = { attrs: [] };\n for (const attr of attrSelector.attributes) {\n if (attr.name === \"name\") {\n options.exact = attr.caseSensitive;\n options.name = attr.value;\n } else {\n if (attr.name === \"level\" && typeof attr.value === \"string\")\n attr.value = +attr.value;\n options.attrs.push({ name: attr.name === \"include-hidden\" ? \"includeHidden\" : attr.name, value: attr.value });\n }\n }\n tokens.push([factory.generateLocator(base, \"role\", attrSelector.name, options)]);\n continue;\n }\n if (part.name === \"internal:testid\") {\n const attrSelector = parseAttributeSelector(part.body, true);\n const { value } = attrSelector.attributes[0];\n tokens.push([factory.generateLocator(base, \"test-id\", value)]);\n continue;\n }\n if (part.name === \"internal:attr\") {\n const attrSelector = parseAttributeSelector(part.body, true);\n const { name, value, caseSensitive } = attrSelector.attributes[0];\n const text = value;\n const exact = !!caseSensitive;\n if (name === \"placeholder\") {\n tokens.push([factory.generateLocator(base, \"placeholder\", text, { exact })]);\n continue;\n }\n if (name === \"alt\") {\n tokens.push([factory.generateLocator(base, \"alt\", text, { exact })]);\n continue;\n }\n if (name === \"title\") {\n tokens.push([factory.generateLocator(base, \"title\", text, { exact })]);\n continue;\n }\n }\n if (part.name === \"internal:control\" && part.body === \"enter-frame\") {\n const lastTokens = tokens[tokens.length - 1];\n const lastPart = parts[index - 1];\n const transformed = lastTokens.map((token) => factory.chainLocators([token, factory.generateLocator(base, \"frame\", \"\")]));\n if ([\"xpath\", \"css\"].includes(lastPart.name)) {\n transformed.push(\n factory.generateLocator(base, \"frame-locator\", stringifySelector({ parts: [lastPart] })),\n factory.generateLocator(base, \"frame-locator\", stringifySelector({ parts: [lastPart] }, true))\n );\n }\n lastTokens.splice(0, lastTokens.length, ...transformed);\n nextBase = \"frame-locator\";\n continue;\n }\n const nextPart = parts[index + 1];\n const selectorPart = stringifySelector({ parts: [part] });\n const locatorPart = factory.generateLocator(base, \"default\", selectorPart);\n if (nextPart && [\"internal:has-text\", \"internal:has-not-text\"].includes(nextPart.name)) {\n const { exact, text } = detectExact(nextPart.body);\n if (!exact) {\n const nextLocatorPart = factory.generateLocator(\"locator\", nextPart.name === \"internal:has-text\" ? \"has-text\" : \"has-not-text\", text, { exact });\n const options = {};\n if (nextPart.name === \"internal:has-text\")\n options.hasText = text;\n else\n options.hasNotText = text;\n const combinedPart = factory.generateLocator(base, \"default\", selectorPart, options);\n tokens.push([factory.chainLocators([locatorPart, nextLocatorPart]), combinedPart]);\n index++;\n continue;\n }\n }\n let locatorPartWithEngine;\n if ([\"xpath\", \"css\"].includes(part.name)) {\n const selectorPart2 = stringifySelector(\n { parts: [part] },\n /* forceEngineName */\n true\n );\n locatorPartWithEngine = factory.generateLocator(base, \"default\", selectorPart2);\n }\n tokens.push([locatorPart, locatorPartWithEngine].filter(Boolean));\n }\n return combineTokens(factory, tokens, maxOutputSize);\n}\nfunction combineTokens(factory, tokens, maxOutputSize) {\n const currentTokens = tokens.map(() => \"\");\n const result = [];\n const visit = (index) => {\n if (index === tokens.length) {\n result.push(factory.chainLocators(currentTokens));\n return result.length < maxOutputSize;\n }\n for (const taken of tokens[index]) {\n currentTokens[index] = taken;\n if (!visit(index + 1))\n return false;\n }\n return true;\n };\n visit(0);\n return result;\n}\nfunction detectExact(text) {\n let exact = false;\n const match = text.match(/^\\/(.*)\\/([igm]*)$/);\n if (match)\n return { text: new RegExp(match[1], match[2]) };\n if (text.endsWith('\"')) {\n text = JSON.parse(text);\n exact = true;\n } else if (text.endsWith('\"s')) {\n text = JSON.parse(text.substring(0, text.length - 1));\n exact = true;\n } else if (text.endsWith('\"i')) {\n text = JSON.parse(text.substring(0, text.length - 1));\n exact = false;\n }\n return { exact, text };\n}\nvar JavaScriptLocatorFactory = class {\n constructor(preferredQuote) {\n this.preferredQuote = preferredQuote;\n }\n generateLocator(base, kind, body, options = {}) {\n switch (kind) {\n case \"default\":\n if (options.hasText !== void 0)\n return `locator(${this.quote(body)}, { hasText: ${this.toHasText(options.hasText)} })`;\n if (options.hasNotText !== void 0)\n return `locator(${this.quote(body)}, { hasNotText: ${this.toHasText(options.hasNotText)} })`;\n return `locator(${this.quote(body)})`;\n case \"frame-locator\":\n return `frameLocator(${this.quote(body)})`;\n case \"frame\":\n return `contentFrame()`;\n case \"nth\":\n return `nth(${body})`;\n case \"first\":\n return `first()`;\n case \"last\":\n return `last()`;\n case \"role\":\n const attrs = [];\n if (isRegExp(options.name)) {\n attrs.push(`name: ${this.regexToSourceString(options.name)}`);\n } else if (typeof options.name === \"string\") {\n attrs.push(`name: ${this.quote(options.name)}`);\n if (options.exact)\n attrs.push(`exact: true`);\n }\n for (const { name, value } of options.attrs)\n attrs.push(`${name}: ${typeof value === \"string\" ? this.quote(value) : value}`);\n const attrString = attrs.length ? `, { ${attrs.join(\", \")} }` : \"\";\n return `getByRole(${this.quote(body)}${attrString})`;\n case \"has-text\":\n return `filter({ hasText: ${this.toHasText(body)} })`;\n case \"has-not-text\":\n return `filter({ hasNotText: ${this.toHasText(body)} })`;\n case \"has\":\n return `filter({ has: ${body} })`;\n case \"hasNot\":\n return `filter({ hasNot: ${body} })`;\n case \"and\":\n return `and(${body})`;\n case \"or\":\n return `or(${body})`;\n case \"chain\":\n return `locator(${body})`;\n case \"test-id\":\n return `getByTestId(${this.toTestIdValue(body)})`;\n case \"text\":\n return this.toCallWithExact(\"getByText\", body, !!options.exact);\n case \"alt\":\n return this.toCallWithExact(\"getByAltText\", body, !!options.exact);\n case \"placeholder\":\n return this.toCallWithExact(\"getByPlaceholder\", body, !!options.exact);\n case \"label\":\n return this.toCallWithExact(\"getByLabel\", body, !!options.exact);\n case \"title\":\n return this.toCallWithExact(\"getByTitle\", body, !!options.exact);\n default:\n throw new Error(\"Unknown selector kind \" + kind);\n }\n }\n chainLocators(locators) {\n return locators.join(\".\");\n }\n regexToSourceString(re) {\n return normalizeEscapedRegexQuotes(String(re));\n }\n toCallWithExact(method, body, exact) {\n if (isRegExp(body))\n return `${method}(${this.regexToSourceString(body)})`;\n return exact ? `${method}(${this.quote(body)}, { exact: true })` : `${method}(${this.quote(body)})`;\n }\n toHasText(body) {\n if (isRegExp(body))\n return this.regexToSourceString(body);\n return this.quote(body);\n }\n toTestIdValue(value) {\n if (isRegExp(value))\n return this.regexToSourceString(value);\n return this.quote(value);\n }\n quote(text) {\n var _a;\n return escapeWithQuotes(text, (_a = this.preferredQuote) != null ? _a : \"'\");\n }\n};\nvar PythonLocatorFactory = class {\n generateLocator(base, kind, body, options = {}) {\n switch (kind) {\n case \"default\":\n if (options.hasText !== void 0)\n return `locator(${this.quote(body)}, has_text=${this.toHasText(options.hasText)})`;\n if (options.hasNotText !== void 0)\n return `locator(${this.quote(body)}, has_not_text=${this.toHasText(options.hasNotText)})`;\n return `locator(${this.quote(body)})`;\n case \"frame-locator\":\n return `frame_locator(${this.quote(body)})`;\n case \"frame\":\n return `content_frame`;\n case \"nth\":\n return `nth(${body})`;\n case \"first\":\n return `first`;\n case \"last\":\n return `last`;\n case \"role\":\n const attrs = [];\n if (isRegExp(options.name)) {\n attrs.push(`name=${this.regexToString(options.name)}`);\n } else if (typeof options.name === \"string\") {\n attrs.push(`name=${this.quote(options.name)}`);\n if (options.exact)\n attrs.push(`exact=True`);\n }\n for (const { name, value } of options.attrs) {\n let valueString = typeof value === \"string\" ? this.quote(value) : value;\n if (typeof value === \"boolean\")\n valueString = value ? \"True\" : \"False\";\n attrs.push(`${toSnakeCase(name)}=${valueString}`);\n }\n const attrString = attrs.length ? `, ${attrs.join(\", \")}` : \"\";\n return `get_by_role(${this.quote(body)}${attrString})`;\n case \"has-text\":\n return `filter(has_text=${this.toHasText(body)})`;\n case \"has-not-text\":\n return `filter(has_not_text=${this.toHasText(body)})`;\n case \"has\":\n return `filter(has=${body})`;\n case \"hasNot\":\n return `filter(has_not=${body})`;\n case \"and\":\n return `and_(${body})`;\n case \"or\":\n return `or_(${body})`;\n case \"chain\":\n return `locator(${body})`;\n case \"test-id\":\n return `get_by_test_id(${this.toTestIdValue(body)})`;\n case \"text\":\n return this.toCallWithExact(\"get_by_text\", body, !!options.exact);\n case \"alt\":\n return this.toCallWithExact(\"get_by_alt_text\", body, !!options.exact);\n case \"placeholder\":\n return this.toCallWithExact(\"get_by_placeholder\", body, !!options.exact);\n case \"label\":\n return this.toCallWithExact(\"get_by_label\", body, !!options.exact);\n case \"title\":\n return this.toCallWithExact(\"get_by_title\", body, !!options.exact);\n default:\n throw new Error(\"Unknown selector kind \" + kind);\n }\n }\n chainLocators(locators) {\n return locators.join(\".\");\n }\n regexToString(body) {\n const suffix = body.flags.includes(\"i\") ? \", re.IGNORECASE\" : \"\";\n return `re.compile(r\"${normalizeEscapedRegexQuotes(body.source).replace(/\\\\\\//, \"/\").replace(/\"/g, '\\\\\"')}\"${suffix})`;\n }\n toCallWithExact(method, body, exact) {\n if (isRegExp(body))\n return `${method}(${this.regexToString(body)})`;\n if (exact)\n return `${method}(${this.quote(body)}, exact=True)`;\n return `${method}(${this.quote(body)})`;\n }\n toHasText(body) {\n if (isRegExp(body))\n return this.regexToString(body);\n return `${this.quote(body)}`;\n }\n toTestIdValue(value) {\n if (isRegExp(value))\n return this.regexToString(value);\n return this.quote(value);\n }\n quote(text) {\n return escapeWithQuotes(text, '\"');\n }\n};\nvar JavaLocatorFactory = class {\n generateLocator(base, kind, body, options = {}) {\n let clazz;\n switch (base) {\n case \"page\":\n clazz = \"Page\";\n break;\n case \"frame-locator\":\n clazz = \"FrameLocator\";\n break;\n case \"locator\":\n clazz = \"Locator\";\n break;\n }\n switch (kind) {\n case \"default\":\n if (options.hasText !== void 0)\n return `locator(${this.quote(body)}, new ${clazz}.LocatorOptions().setHasText(${this.toHasText(options.hasText)}))`;\n if (options.hasNotText !== void 0)\n return `locator(${this.quote(body)}, new ${clazz}.LocatorOptions().setHasNotText(${this.toHasText(options.hasNotText)}))`;\n return `locator(${this.quote(body)})`;\n case \"frame-locator\":\n return `frameLocator(${this.quote(body)})`;\n case \"frame\":\n return `contentFrame()`;\n case \"nth\":\n return `nth(${body})`;\n case \"first\":\n return `first()`;\n case \"last\":\n return `last()`;\n case \"role\":\n const attrs = [];\n if (isRegExp(options.name)) {\n attrs.push(`.setName(${this.regexToString(options.name)})`);\n } else if (typeof options.name === \"string\") {\n attrs.push(`.setName(${this.quote(options.name)})`);\n if (options.exact)\n attrs.push(`.setExact(true)`);\n }\n for (const { name, value } of options.attrs)\n attrs.push(`.set${toTitleCase(name)}(${typeof value === \"string\" ? this.quote(value) : value})`);\n const attrString = attrs.length ? `, new ${clazz}.GetByRoleOptions()${attrs.join(\"\")}` : \"\";\n return `getByRole(AriaRole.${toSnakeCase(body).toUpperCase()}${attrString})`;\n case \"has-text\":\n return `filter(new ${clazz}.FilterOptions().setHasText(${this.toHasText(body)}))`;\n case \"has-not-text\":\n return `filter(new ${clazz}.FilterOptions().setHasNotText(${this.toHasText(body)}))`;\n case \"has\":\n return `filter(new ${clazz}.FilterOptions().setHas(${body}))`;\n case \"hasNot\":\n return `filter(new ${clazz}.FilterOptions().setHasNot(${body}))`;\n case \"and\":\n return `and(${body})`;\n case \"or\":\n return `or(${body})`;\n case \"chain\":\n return `locator(${body})`;\n case \"test-id\":\n return `getByTestId(${this.toTestIdValue(body)})`;\n case \"text\":\n return this.toCallWithExact(clazz, \"getByText\", body, !!options.exact);\n case \"alt\":\n return this.toCallWithExact(clazz, \"getByAltText\", body, !!options.exact);\n case \"placeholder\":\n return this.toCallWithExact(clazz, \"getByPlaceholder\", body, !!options.exact);\n case \"label\":\n return this.toCallWithExact(clazz, \"getByLabel\", body, !!options.exact);\n case \"title\":\n return this.toCallWithExact(clazz, \"getByTitle\", body, !!options.exact);\n default:\n throw new Error(\"Unknown selector kind \" + kind);\n }\n }\n chainLocators(locators) {\n return locators.join(\".\");\n }\n regexToString(body) {\n const suffix = body.flags.includes(\"i\") ? \", Pattern.CASE_INSENSITIVE\" : \"\";\n return `Pattern.compile(${this.quote(normalizeEscapedRegexQuotes(body.source))}${suffix})`;\n }\n toCallWithExact(clazz, method, body, exact) {\n if (isRegExp(body))\n return `${method}(${this.regexToString(body)})`;\n if (exact)\n return `${method}(${this.quote(body)}, new ${clazz}.${toTitleCase(method)}Options().setExact(true))`;\n return `${method}(${this.quote(body)})`;\n }\n toHasText(body) {\n if (isRegExp(body))\n return this.regexToString(body);\n return this.quote(body);\n }\n toTestIdValue(value) {\n if (isRegExp(value))\n return this.regexToString(value);\n return this.quote(value);\n }\n quote(text) {\n return escapeWithQuotes(text, '\"');\n }\n};\nvar CSharpLocatorFactory = class {\n generateLocator(base, kind, body, options = {}) {\n switch (kind) {\n case \"default\":\n if (options.hasText !== void 0)\n return `Locator(${this.quote(body)}, new() { ${this.toHasText(options.hasText)} })`;\n if (options.hasNotText !== void 0)\n return `Locator(${this.quote(body)}, new() { ${this.toHasNotText(options.hasNotText)} })`;\n return `Locator(${this.quote(body)})`;\n case \"frame-locator\":\n return `FrameLocator(${this.quote(body)})`;\n case \"frame\":\n return `ContentFrame`;\n case \"nth\":\n return `Nth(${body})`;\n case \"first\":\n return `First`;\n case \"last\":\n return `Last`;\n case \"role\":\n const attrs = [];\n if (isRegExp(options.name)) {\n attrs.push(`NameRegex = ${this.regexToString(options.name)}`);\n } else if (typeof options.name === \"string\") {\n attrs.push(`Name = ${this.quote(options.name)}`);\n if (options.exact)\n attrs.push(`Exact = true`);\n }\n for (const { name, value } of options.attrs)\n attrs.push(`${toTitleCase(name)} = ${typeof value === \"string\" ? this.quote(value) : value}`);\n const attrString = attrs.length ? `, new() { ${attrs.join(\", \")} }` : \"\";\n return `GetByRole(AriaRole.${toTitleCase(body)}${attrString})`;\n case \"has-text\":\n return `Filter(new() { ${this.toHasText(body)} })`;\n case \"has-not-text\":\n return `Filter(new() { ${this.toHasNotText(body)} })`;\n case \"has\":\n return `Filter(new() { Has = ${body} })`;\n case \"hasNot\":\n return `Filter(new() { HasNot = ${body} })`;\n case \"and\":\n return `And(${body})`;\n case \"or\":\n return `Or(${body})`;\n case \"chain\":\n return `Locator(${body})`;\n case \"test-id\":\n return `GetByTestId(${this.toTestIdValue(body)})`;\n case \"text\":\n return this.toCallWithExact(\"GetByText\", body, !!options.exact);\n case \"alt\":\n return this.toCallWithExact(\"GetByAltText\", body, !!options.exact);\n case \"placeholder\":\n return this.toCallWithExact(\"GetByPlaceholder\", body, !!options.exact);\n case \"label\":\n return this.toCallWithExact(\"GetByLabel\", body, !!options.exact);\n case \"title\":\n return this.toCallWithExact(\"GetByTitle\", body, !!options.exact);\n default:\n throw new Error(\"Unknown selector kind \" + kind);\n }\n }\n chainLocators(locators) {\n return locators.join(\".\");\n }\n regexToString(body) {\n const suffix = body.flags.includes(\"i\") ? \", RegexOptions.IgnoreCase\" : \"\";\n return `new Regex(${this.quote(normalizeEscapedRegexQuotes(body.source))}${suffix})`;\n }\n toCallWithExact(method, body, exact) {\n if (isRegExp(body))\n return `${method}(${this.regexToString(body)})`;\n if (exact)\n return `${method}(${this.quote(body)}, new() { Exact = true })`;\n return `${method}(${this.quote(body)})`;\n }\n toHasText(body) {\n if (isRegExp(body))\n return `HasTextRegex = ${this.regexToString(body)}`;\n return `HasText = ${this.quote(body)}`;\n }\n toTestIdValue(value) {\n if (isRegExp(value))\n return this.regexToString(value);\n return this.quote(value);\n }\n toHasNotText(body) {\n if (isRegExp(body))\n return `HasNotTextRegex = ${this.regexToString(body)}`;\n return `HasNotText = ${this.quote(body)}`;\n }\n quote(text) {\n return escapeWithQuotes(text, '\"');\n }\n};\nvar JsonlLocatorFactory = class {\n generateLocator(base, kind, body, options = {}) {\n return JSON.stringify({\n kind,\n body,\n options\n });\n }\n chainLocators(locators) {\n const objects = locators.map((l) => JSON.parse(l));\n for (let i = 0; i < objects.length - 1; ++i)\n objects[i].next = objects[i + 1];\n return JSON.stringify(objects[0]);\n }\n};\nvar generators = {\n javascript: JavaScriptLocatorFactory,\n python: PythonLocatorFactory,\n java: JavaLocatorFactory,\n csharp: CSharpLocatorFactory,\n jsonl: JsonlLocatorFactory\n};\nfunction isRegExp(obj) {\n return obj instanceof RegExp;\n}\n\n// packages/playwright-core/src/server/injected/consoleApi.ts\nvar selectorSymbol = Symbol(\"selector\");\nvar _Locator = class _Locator {\n constructor(injectedScript, selector, options) {\n if (options == null ? void 0 : options.hasText)\n selector += ` >> internal:has-text=${escapeForTextSelector(options.hasText, false)}`;\n if (options == null ? void 0 : options.hasNotText)\n selector += ` >> internal:has-not-text=${escapeForTextSelector(options.hasNotText, false)}`;\n if (options == null ? void 0 : options.has)\n selector += ` >> internal:has=` + JSON.stringify(options.has[selectorSymbol]);\n if (options == null ? void 0 : options.hasNot)\n selector += ` >> internal:has-not=` + JSON.stringify(options.hasNot[selectorSymbol]);\n this[selectorSymbol] = selector;\n if (selector) {\n const parsed = injectedScript.parseSelector(selector);\n this.element = injectedScript.querySelector(parsed, injectedScript.document, false);\n this.elements = injectedScript.querySelectorAll(parsed, injectedScript.document);\n }\n const selectorBase = selector;\n const self = this;\n self.locator = (selector2, options2) => {\n return new _Locator(injectedScript, selectorBase ? selectorBase + \" >> \" + selector2 : selector2, options2);\n };\n self.getByTestId = (testId) => self.locator(getByTestIdSelector(injectedScript.testIdAttributeNameForStrictErrorAndConsoleCodegen(), testId));\n self.getByAltText = (text, options2) => self.locator(getByAltTextSelector(text, options2));\n self.getByLabel = (text, options2) => self.locator(getByLabelSelector(text, options2));\n self.getByPlaceholder = (text, options2) => self.locator(getByPlaceholderSelector(text, options2));\n self.getByText = (text, options2) => self.locator(getByTextSelector(text, options2));\n self.getByTitle = (text, options2) => self.locator(getByTitleSelector(text, options2));\n self.getByRole = (role, options2 = {}) => self.locator(getByRoleSelector(role, options2));\n self.filter = (options2) => new _Locator(injectedScript, selector, options2);\n self.first = () => self.locator(\"nth=0\");\n self.last = () => self.locator(\"nth=-1\");\n self.nth = (index) => self.locator(`nth=${index}`);\n self.and = (locator) => new _Locator(injectedScript, selectorBase + ` >> internal:and=` + JSON.stringify(locator[selectorSymbol]));\n self.or = (locator) => new _Locator(injectedScript, selectorBase + ` >> internal:or=` + JSON.stringify(locator[selectorSymbol]));\n }\n};\nselectorSymbol;\nvar Locator = _Locator;\nvar ConsoleAPI = class {\n constructor(injectedScript) {\n this._injectedScript = injectedScript;\n if (this._injectedScript.window.playwright)\n return;\n this._injectedScript.window.playwright = {\n $: (selector, strict) => this._querySelector(selector, !!strict),\n $$: (selector) => this._querySelectorAll(selector),\n inspect: (selector) => this._inspect(selector),\n selector: (element) => this._selector(element),\n generateLocator: (element, language) => this._generateLocator(element, language),\n ariaSnapshot: (element) => {\n const snapshot = this._injectedScript.ariaSnapshot(element || this._injectedScript.document.body);\n console.log(snapshot);\n },\n resume: () => this._resume(),\n ...new Locator(injectedScript, \"\")\n };\n delete this._injectedScript.window.playwright.filter;\n delete this._injectedScript.window.playwright.first;\n delete this._injectedScript.window.playwright.last;\n delete this._injectedScript.window.playwright.nth;\n delete this._injectedScript.window.playwright.and;\n delete this._injectedScript.window.playwright.or;\n }\n _querySelector(selector, strict) {\n if (typeof selector !== \"string\")\n throw new Error(`Usage: playwright.query('Playwright >> selector').`);\n const parsed = this._injectedScript.parseSelector(selector);\n return this._injectedScript.querySelector(parsed, this._injectedScript.document, strict);\n }\n _querySelectorAll(selector) {\n if (typeof selector !== \"string\")\n throw new Error(`Usage: playwright.$$('Playwright >> selector').`);\n const parsed = this._injectedScript.parseSelector(selector);\n return this._injectedScript.querySelectorAll(parsed, this._injectedScript.document);\n }\n _inspect(selector) {\n if (typeof selector !== \"string\")\n throw new Error(`Usage: playwright.inspect('Playwright >> selector').`);\n this._injectedScript.window.inspect(this._querySelector(selector, false));\n }\n _selector(element) {\n if (!(element instanceof Element))\n throw new Error(`Usage: playwright.selector(element).`);\n return this._injectedScript.generateSelectorSimple(element);\n }\n _generateLocator(element, language) {\n if (!(element instanceof Element))\n throw new Error(`Usage: playwright.locator(element).`);\n const selector = this._injectedScript.generateSelectorSimple(element);\n return asLocator(language || \"javascript\", selector);\n }\n _resume() {\n this._injectedScript.window.__pw_resume().catch(() => {\n });\n }\n};\nvar consoleApi_default = ConsoleAPI;\n";
|