@vitest/browser 4.1.5 → 5.0.0-beta.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (31) hide show
  1. package/context.d.ts +50 -1
  2. package/dist/client/.vite/manifest.json +15 -6
  3. package/dist/client/__vitest__/assets/index-Cd6On2Pm.js +136 -0
  4. package/dist/client/__vitest__/assets/index-Dw9P28Qt.css +1 -0
  5. package/dist/client/__vitest__/index.html +2 -2
  6. package/dist/client/__vitest_browser__/{orchestrator-DM4mHHP0.js → orchestrator-BfoS0x4w.js} +55 -31
  7. package/dist/client/__vitest_browser__/rrweb-snapshot-xhvrgOHx.js +5476 -0
  8. package/dist/client/__vitest_browser__/{utils-DmkAiRYk.js → tester-BJtW9QqZ.js} +2674 -930
  9. package/dist/client/__vitest_browser__/utils-Nd8hqrhP.js +189 -0
  10. package/dist/client/orchestrator.html +2 -2
  11. package/dist/client/tester/locators.d.ts +69 -0
  12. package/dist/client/tester/tester.html +2 -2
  13. package/dist/client/tester/trace.d.ts +54 -0
  14. package/dist/client.js +1 -0
  15. package/dist/context.js +155 -15
  16. package/dist/expect-element.js +30 -30
  17. package/dist/index.d.ts +6 -22
  18. package/dist/index.js +29 -4551
  19. package/dist/locators-CesZ2RSY.js +5 -0
  20. package/dist/locators.d.ts +9 -1
  21. package/dist/locators.js +1 -1
  22. package/dist/shared/screenshotMatcher/types.d.ts +3 -3
  23. package/dist/state.js +1 -0
  24. package/dist/vendor-types.d.ts +131 -0
  25. package/dist/vendor-types.ts +1 -0
  26. package/package.json +15 -7
  27. package/dist/client/__vitest__/assets/index-BPQdrqGZ.js +0 -94
  28. package/dist/client/__vitest__/assets/index-Da0hb3oU.css +0 -1
  29. package/dist/client/__vitest__/bg.png +0 -0
  30. package/dist/client/__vitest_browser__/tester-Bf18VQr2.js +0 -2288
  31. package/dist/index-D8jtZoIM.js +0 -5
package/dist/index.js CHANGED
@@ -18,7 +18,7 @@ import { PNG } from 'pngjs';
18
18
  import { diff } from '@blazediff/core';
19
19
  import { WebSocketServer } from 'ws';
20
20
 
21
- var version = "4.1.5";
21
+ var version = "5.0.0-beta.2";
22
22
 
23
23
  const _DRIVE_LETTER_START_RE = /^[A-Za-z]:\//;
24
24
  function normalizeWindowsPath(input = "") {
@@ -1056,8 +1056,6 @@ var BrowserPlugin = (parentServer, base = "/") => {
1056
1056
  "@vitest/runner",
1057
1057
  "@vitest/spy",
1058
1058
  "@vitest/utils/error",
1059
- "@vitest/snapshot",
1060
- "@vitest/expect",
1061
1059
  "std-env",
1062
1060
  "tinybench",
1063
1061
  "tinyspy",
@@ -1090,8 +1088,8 @@ var BrowserPlugin = (parentServer, base = "/") => {
1090
1088
  }
1091
1089
  const include = [
1092
1090
  "vitest > expect-type",
1093
- "vitest > @vitest/snapshot > magic-string",
1094
- "vitest > @vitest/expect > chai"
1091
+ "vitest > magic-string",
1092
+ "vitest > chai"
1095
1093
  ];
1096
1094
  const provider = parentServer.config.browser.provider || [...parentServer.children][0]?.provider;
1097
1095
  if (provider?.name === "preview") {
@@ -1116,6 +1114,8 @@ var BrowserPlugin = (parentServer, base = "/") => {
1116
1114
  if (otelConfig?.enabled && otelConfig.browserSdkPath) {
1117
1115
  entries.push(otelConfig.browserSdkPath);
1118
1116
  include.push("@opentelemetry/api");
1117
+ } else {
1118
+ exclude.push("@opentelemetry/api");
1119
1119
  }
1120
1120
  return {
1121
1121
  define,
@@ -2090,7 +2090,8 @@ function resolveOptions({ context, name, options, testName }) {
2090
2090
  testFileDirectory: relative(root, dirname(context.testPath)),
2091
2091
  testFileName: basename(context.testPath),
2092
2092
  testName: sanitize(testName, false),
2093
- browserName: context.project.config.browser.name
2093
+ browserName: context.project.config.browser.name,
2094
+ project: context.project
2094
2095
  };
2095
2096
  return {
2096
2097
  codec: getCodec(extension),
@@ -2489,6 +2490,26 @@ const _groupTraceEnd = async (context) => {
2489
2490
  await context.triggerCommand("__vitest_groupTraceEnd");
2490
2491
  }
2491
2492
  };
2493
+ const _recordBrowserTrace = async ({ project }, { testId, data }) => {
2494
+ // resolve stack strings → source locations server-side (requires source maps)
2495
+ const entries = data.entries.map((entry) => {
2496
+ if (!entry.stack || entry.location) {
2497
+ return entry;
2498
+ }
2499
+ const stacks = project.browser.parseStacktrace(entry.stack);
2500
+ if (stacks[0]) {
2501
+ entry.location = stacks[0];
2502
+ }
2503
+ return entry;
2504
+ });
2505
+ await project.vitest._testRun.recordArtifact(testId, {
2506
+ type: "internal:browserTrace",
2507
+ data: {
2508
+ ...data,
2509
+ entries
2510
+ }
2511
+ });
2512
+ };
2492
2513
 
2493
2514
  var builtinCommands = {
2494
2515
  readFile,
@@ -2497,6 +2518,7 @@ var builtinCommands = {
2497
2518
  __vitest_markTrace: _markTrace,
2498
2519
  __vitest_groupTraceStart: _groupTraceStart,
2499
2520
  __vitest_groupTraceEnd: _groupTraceEnd,
2521
+ __vitest_recordBrowserTrace: _recordBrowserTrace,
2500
2522
  __vitest_fileInfo: _fileInfo,
2501
2523
  __vitest_screenshot: screenshot,
2502
2524
  __vitest_screenshotMatcher: screenshotMatcher
@@ -3303,4550 +3325,6 @@ function stringifyReplace(key, value) {
3303
3325
  }
3304
3326
  }
3305
3327
 
3306
- //#region \0@oxc-project+runtime@0.115.0/helpers/typeof.js
3307
- function _typeof(o) {
3308
- "@babel/helpers - typeof";
3309
- return _typeof = "function" == typeof Symbol && "symbol" == typeof Symbol.iterator ? function(o) {
3310
- return typeof o;
3311
- } : function(o) {
3312
- return o && "function" == typeof Symbol && o.constructor === Symbol && o !== Symbol.prototype ? "symbol" : typeof o;
3313
- }, _typeof(o);
3314
- }
3315
-
3316
- //#endregion
3317
- //#region \0@oxc-project+runtime@0.115.0/helpers/toPrimitive.js
3318
- function toPrimitive(t, r) {
3319
- if ("object" != _typeof(t) || !t) return t;
3320
- var e = t[Symbol.toPrimitive];
3321
- if (void 0 !== e) {
3322
- var i = e.call(t, r);
3323
- if ("object" != _typeof(i)) return i;
3324
- throw new TypeError("@@toPrimitive must return a primitive value.");
3325
- }
3326
- return ("string" === r ? String : Number)(t);
3327
- }
3328
-
3329
- //#endregion
3330
- //#region \0@oxc-project+runtime@0.115.0/helpers/toPropertyKey.js
3331
- function toPropertyKey(t) {
3332
- var i = toPrimitive(t, "string");
3333
- return "symbol" == _typeof(i) ? i : i + "";
3334
- }
3335
-
3336
- //#endregion
3337
- //#region \0@oxc-project+runtime@0.115.0/helpers/defineProperty.js
3338
- function _defineProperty(e, r, t) {
3339
- return (r = toPropertyKey(r)) in e ? Object.defineProperty(e, r, {
3340
- value: t,
3341
- enumerable: true,
3342
- configurable: true,
3343
- writable: true
3344
- }) : e[r] = t, e;
3345
- }
3346
-
3347
- //#endregion
3348
- //#region src/cssTokenizer.ts
3349
- const between = function(num, first, last) {
3350
- return num >= first && num <= last;
3351
- };
3352
- function digit(code) {
3353
- return between(code, 48, 57);
3354
- }
3355
- function hexdigit(code) {
3356
- return digit(code) || between(code, 65, 70) || between(code, 97, 102);
3357
- }
3358
- function uppercaseletter(code) {
3359
- return between(code, 65, 90);
3360
- }
3361
- function lowercaseletter(code) {
3362
- return between(code, 97, 122);
3363
- }
3364
- function letter(code) {
3365
- return uppercaseletter(code) || lowercaseletter(code);
3366
- }
3367
- function nonascii(code) {
3368
- return code >= 128;
3369
- }
3370
- function namestartchar(code) {
3371
- return letter(code) || nonascii(code) || code === 95;
3372
- }
3373
- function namechar(code) {
3374
- return namestartchar(code) || digit(code) || code === 45;
3375
- }
3376
- function nonprintable(code) {
3377
- return between(code, 0, 8) || code === 11 || between(code, 14, 31) || code === 127;
3378
- }
3379
- function newline(code) {
3380
- return code === 10;
3381
- }
3382
- function whitespace(code) {
3383
- return newline(code) || code === 9 || code === 32;
3384
- }
3385
- const maximumallowedcodepoint = 1114111;
3386
- var InvalidCharacterError = class extends Error {
3387
- constructor(message) {
3388
- super(message);
3389
- this.name = "InvalidCharacterError";
3390
- }
3391
- };
3392
- function preprocess(str) {
3393
- const codepoints = [];
3394
- for (let i = 0; i < str.length; i++) {
3395
- let code = str.charCodeAt(i);
3396
- if (code === 13 && str.charCodeAt(i + 1) === 10) {
3397
- code = 10;
3398
- i++;
3399
- }
3400
- if (code === 13 || code === 12) code = 10;
3401
- if (code === 0) code = 65533;
3402
- if (between(code, 55296, 56319) && between(str.charCodeAt(i + 1), 56320, 57343)) {
3403
- const lead = code - 55296;
3404
- const trail = str.charCodeAt(i + 1) - 56320;
3405
- code = 2 ** 16 + lead * 2 ** 10 + trail;
3406
- i++;
3407
- }
3408
- codepoints.push(code);
3409
- }
3410
- return codepoints;
3411
- }
3412
- function stringFromCode(code) {
3413
- if (code <= 65535) return String.fromCharCode(code);
3414
- code -= 2 ** 16;
3415
- const lead = Math.floor(code / 2 ** 10) + 55296;
3416
- const trail = code % 2 ** 10 + 56320;
3417
- return String.fromCharCode(lead) + String.fromCharCode(trail);
3418
- }
3419
- function tokenize(str1) {
3420
- const str = preprocess(str1);
3421
- let i = -1;
3422
- const tokens = [];
3423
- let code;
3424
- const codepoint = function(i) {
3425
- if (i >= str.length) return -1;
3426
- return str[i];
3427
- };
3428
- const next = function(num) {
3429
- if (num === void 0) num = 1;
3430
- if (num > 3) throw "Spec Error: no more than three codepoints of lookahead.";
3431
- return codepoint(i + num);
3432
- };
3433
- const consume = function(num) {
3434
- if (num === void 0) num = 1;
3435
- i += num;
3436
- code = codepoint(i);
3437
- return true;
3438
- };
3439
- const reconsume = function() {
3440
- i -= 1;
3441
- return true;
3442
- };
3443
- const eof = function(codepoint) {
3444
- if (codepoint === void 0) codepoint = code;
3445
- return codepoint === -1;
3446
- };
3447
- const consumeAToken = function() {
3448
- consumeComments();
3449
- consume();
3450
- if (whitespace(code)) {
3451
- while (whitespace(next())) consume();
3452
- return new WhitespaceToken();
3453
- } else if (code === 34) return consumeAStringToken();
3454
- else if (code === 35) if (namechar(next()) || areAValidEscape(next(1), next(2))) {
3455
- const token = new HashToken("");
3456
- if (wouldStartAnIdentifier(next(1), next(2), next(3))) token.type = "id";
3457
- token.value = consumeAName();
3458
- return token;
3459
- } else return new DelimToken(code);
3460
- else if (code === 36) if (next() === 61) {
3461
- consume();
3462
- return new SuffixMatchToken();
3463
- } else return new DelimToken(code);
3464
- else if (code === 39) return consumeAStringToken();
3465
- else if (code === 40) return new OpenParenToken();
3466
- else if (code === 41) return new CloseParenToken();
3467
- else if (code === 42) if (next() === 61) {
3468
- consume();
3469
- return new SubstringMatchToken();
3470
- } else return new DelimToken(code);
3471
- else if (code === 43) if (startsWithANumber()) {
3472
- reconsume();
3473
- return consumeANumericToken();
3474
- } else return new DelimToken(code);
3475
- else if (code === 44) return new CommaToken();
3476
- else if (code === 45) if (startsWithANumber()) {
3477
- reconsume();
3478
- return consumeANumericToken();
3479
- } else if (next(1) === 45 && next(2) === 62) {
3480
- consume(2);
3481
- return new CDCToken();
3482
- } else if (startsWithAnIdentifier()) {
3483
- reconsume();
3484
- return consumeAnIdentlikeToken();
3485
- } else return new DelimToken(code);
3486
- else if (code === 46) if (startsWithANumber()) {
3487
- reconsume();
3488
- return consumeANumericToken();
3489
- } else return new DelimToken(code);
3490
- else if (code === 58) return new ColonToken();
3491
- else if (code === 59) return new SemicolonToken();
3492
- else if (code === 60) if (next(1) === 33 && next(2) === 45 && next(3) === 45) {
3493
- consume(3);
3494
- return new CDOToken();
3495
- } else return new DelimToken(code);
3496
- else if (code === 64) if (wouldStartAnIdentifier(next(1), next(2), next(3))) return new AtKeywordToken(consumeAName());
3497
- else return new DelimToken(code);
3498
- else if (code === 91) return new OpenSquareToken();
3499
- else if (code === 92) if (startsWithAValidEscape()) {
3500
- reconsume();
3501
- return consumeAnIdentlikeToken();
3502
- } else {
3503
- return new DelimToken(code);
3504
- }
3505
- else if (code === 93) return new CloseSquareToken();
3506
- else if (code === 94) if (next() === 61) {
3507
- consume();
3508
- return new PrefixMatchToken();
3509
- } else return new DelimToken(code);
3510
- else if (code === 123) return new OpenCurlyToken();
3511
- else if (code === 124) if (next() === 61) {
3512
- consume();
3513
- return new DashMatchToken();
3514
- } else if (next() === 124) {
3515
- consume();
3516
- return new ColumnToken();
3517
- } else return new DelimToken(code);
3518
- else if (code === 125) return new CloseCurlyToken();
3519
- else if (code === 126) if (next() === 61) {
3520
- consume();
3521
- return new IncludeMatchToken();
3522
- } else return new DelimToken(code);
3523
- else if (digit(code)) {
3524
- reconsume();
3525
- return consumeANumericToken();
3526
- } else if (namestartchar(code)) {
3527
- reconsume();
3528
- return consumeAnIdentlikeToken();
3529
- } else if (eof()) return new EOFToken();
3530
- else return new DelimToken(code);
3531
- };
3532
- const consumeComments = function() {
3533
- while (next(1) === 47 && next(2) === 42) {
3534
- consume(2);
3535
- while (true) {
3536
- consume();
3537
- if (code === 42 && next() === 47) {
3538
- consume();
3539
- break;
3540
- } else if (eof()) {
3541
- return;
3542
- }
3543
- }
3544
- }
3545
- };
3546
- const consumeANumericToken = function() {
3547
- const num = consumeANumber();
3548
- if (wouldStartAnIdentifier(next(1), next(2), next(3))) {
3549
- const token = new DimensionToken();
3550
- token.value = num.value;
3551
- token.repr = num.repr;
3552
- token.type = num.type;
3553
- token.unit = consumeAName();
3554
- return token;
3555
- } else if (next() === 37) {
3556
- consume();
3557
- const token = new PercentageToken();
3558
- token.value = num.value;
3559
- token.repr = num.repr;
3560
- return token;
3561
- } else {
3562
- const token = new NumberToken();
3563
- token.value = num.value;
3564
- token.repr = num.repr;
3565
- token.type = num.type;
3566
- return token;
3567
- }
3568
- };
3569
- const consumeAnIdentlikeToken = function() {
3570
- const str = consumeAName();
3571
- if (str.toLowerCase() === "url" && next() === 40) {
3572
- consume();
3573
- while (whitespace(next(1)) && whitespace(next(2))) consume();
3574
- if (next() === 34 || next() === 39) return new FunctionToken(str);
3575
- else if (whitespace(next()) && (next(2) === 34 || next(2) === 39)) return new FunctionToken(str);
3576
- else return consumeAURLToken();
3577
- } else if (next() === 40) {
3578
- consume();
3579
- return new FunctionToken(str);
3580
- } else return new IdentToken(str);
3581
- };
3582
- const consumeAStringToken = function(endingCodePoint) {
3583
- if (endingCodePoint === void 0) endingCodePoint = code;
3584
- let string = "";
3585
- while (consume()) if (code === endingCodePoint || eof()) return new StringToken(string);
3586
- else if (newline(code)) {
3587
- reconsume();
3588
- return new BadStringToken();
3589
- } else if (code === 92) if (eof(next())) ;
3590
- else if (newline(next())) consume();
3591
- else string += stringFromCode(consumeEscape());
3592
- else string += stringFromCode(code);
3593
- throw new Error("Internal error");
3594
- };
3595
- const consumeAURLToken = function() {
3596
- const token = new URLToken("");
3597
- while (whitespace(next())) consume();
3598
- if (eof(next())) return token;
3599
- while (consume()) if (code === 41 || eof()) return token;
3600
- else if (whitespace(code)) {
3601
- while (whitespace(next())) consume();
3602
- if (next() === 41 || eof(next())) {
3603
- consume();
3604
- return token;
3605
- } else {
3606
- consumeTheRemnantsOfABadURL();
3607
- return new BadURLToken();
3608
- }
3609
- } else if (code === 34 || code === 39 || code === 40 || nonprintable(code)) {
3610
- consumeTheRemnantsOfABadURL();
3611
- return new BadURLToken();
3612
- } else if (code === 92) if (startsWithAValidEscape()) token.value += stringFromCode(consumeEscape());
3613
- else {
3614
- consumeTheRemnantsOfABadURL();
3615
- return new BadURLToken();
3616
- }
3617
- else token.value += stringFromCode(code);
3618
- throw new Error("Internal error");
3619
- };
3620
- const consumeEscape = function() {
3621
- consume();
3622
- if (hexdigit(code)) {
3623
- const digits = [code];
3624
- for (let total = 0; total < 5; total++) if (hexdigit(next())) {
3625
- consume();
3626
- digits.push(code);
3627
- } else break;
3628
- if (whitespace(next())) consume();
3629
- let value = Number.parseInt(digits.map((x) => {
3630
- return String.fromCharCode(x);
3631
- }).join(""), 16);
3632
- if (value > maximumallowedcodepoint) value = 65533;
3633
- return value;
3634
- } else if (eof()) return 65533;
3635
- else return code;
3636
- };
3637
- const areAValidEscape = function(c1, c2) {
3638
- if (c1 !== 92) return false;
3639
- if (newline(c2)) return false;
3640
- return true;
3641
- };
3642
- const startsWithAValidEscape = function() {
3643
- return areAValidEscape(code, next());
3644
- };
3645
- const wouldStartAnIdentifier = function(c1, c2, c3) {
3646
- if (c1 === 45) return namestartchar(c2) || c2 === 45 || areAValidEscape(c2, c3);
3647
- else if (namestartchar(c1)) return true;
3648
- else if (c1 === 92) return areAValidEscape(c1, c2);
3649
- else return false;
3650
- };
3651
- const startsWithAnIdentifier = function() {
3652
- return wouldStartAnIdentifier(code, next(1), next(2));
3653
- };
3654
- const wouldStartANumber = function(c1, c2, c3) {
3655
- if (c1 === 43 || c1 === 45) {
3656
- if (digit(c2)) return true;
3657
- if (c2 === 46 && digit(c3)) return true;
3658
- return false;
3659
- } else if (c1 === 46) {
3660
- if (digit(c2)) return true;
3661
- return false;
3662
- } else if (digit(c1)) return true;
3663
- else return false;
3664
- };
3665
- const startsWithANumber = function() {
3666
- return wouldStartANumber(code, next(1), next(2));
3667
- };
3668
- const consumeAName = function() {
3669
- let result = "";
3670
- while (consume()) if (namechar(code)) result += stringFromCode(code);
3671
- else if (startsWithAValidEscape()) result += stringFromCode(consumeEscape());
3672
- else {
3673
- reconsume();
3674
- return result;
3675
- }
3676
- throw new Error("Internal parse error");
3677
- };
3678
- const consumeANumber = function() {
3679
- let repr = "";
3680
- let type = "integer";
3681
- if (next() === 43 || next() === 45) {
3682
- consume();
3683
- repr += stringFromCode(code);
3684
- }
3685
- while (digit(next())) {
3686
- consume();
3687
- repr += stringFromCode(code);
3688
- }
3689
- if (next(1) === 46 && digit(next(2))) {
3690
- consume();
3691
- repr += stringFromCode(code);
3692
- consume();
3693
- repr += stringFromCode(code);
3694
- type = "number";
3695
- while (digit(next())) {
3696
- consume();
3697
- repr += stringFromCode(code);
3698
- }
3699
- }
3700
- const c1 = next(1);
3701
- const c2 = next(2);
3702
- const c3 = next(3);
3703
- if ((c1 === 69 || c1 === 101) && digit(c2)) {
3704
- consume();
3705
- repr += stringFromCode(code);
3706
- consume();
3707
- repr += stringFromCode(code);
3708
- type = "number";
3709
- while (digit(next())) {
3710
- consume();
3711
- repr += stringFromCode(code);
3712
- }
3713
- } else if ((c1 === 69 || c1 === 101) && (c2 === 43 || c2 === 45) && digit(c3)) {
3714
- consume();
3715
- repr += stringFromCode(code);
3716
- consume();
3717
- repr += stringFromCode(code);
3718
- consume();
3719
- repr += stringFromCode(code);
3720
- type = "number";
3721
- while (digit(next())) {
3722
- consume();
3723
- repr += stringFromCode(code);
3724
- }
3725
- }
3726
- const value = convertAStringToANumber(repr);
3727
- return {
3728
- type,
3729
- value,
3730
- repr
3731
- };
3732
- };
3733
- const convertAStringToANumber = function(string) {
3734
- return +string;
3735
- };
3736
- const consumeTheRemnantsOfABadURL = function() {
3737
- while (consume()) if (code === 41 || eof()) return;
3738
- else if (startsWithAValidEscape()) {
3739
- consumeEscape();
3740
- } else ;
3741
- };
3742
- let iterationCount = 0;
3743
- while (!eof(next())) {
3744
- tokens.push(consumeAToken());
3745
- iterationCount++;
3746
- if (iterationCount > str.length * 2) throw new Error("I'm infinite-looping!");
3747
- }
3748
- return tokens;
3749
- }
3750
- var CSSParserToken = class {
3751
- constructor() {
3752
- _defineProperty(this, "tokenType", "");
3753
- _defineProperty(this, "value", void 0);
3754
- }
3755
- toJSON() {
3756
- return { token: this.tokenType };
3757
- }
3758
- toString() {
3759
- return this.tokenType;
3760
- }
3761
- toSource() {
3762
- return `${this}`;
3763
- }
3764
- };
3765
- var BadStringToken = class extends CSSParserToken {
3766
- constructor(..._args) {
3767
- super(..._args);
3768
- _defineProperty(this, "tokenType", "BADSTRING");
3769
- }
3770
- };
3771
- var BadURLToken = class extends CSSParserToken {
3772
- constructor(..._args2) {
3773
- super(..._args2);
3774
- _defineProperty(this, "tokenType", "BADURL");
3775
- }
3776
- };
3777
- var WhitespaceToken = class extends CSSParserToken {
3778
- constructor(..._args3) {
3779
- super(..._args3);
3780
- _defineProperty(this, "tokenType", "WHITESPACE");
3781
- }
3782
- toString() {
3783
- return "WS";
3784
- }
3785
- toSource() {
3786
- return " ";
3787
- }
3788
- };
3789
- var CDOToken = class extends CSSParserToken {
3790
- constructor(..._args4) {
3791
- super(..._args4);
3792
- _defineProperty(this, "tokenType", "CDO");
3793
- }
3794
- toSource() {
3795
- return "<!--";
3796
- }
3797
- };
3798
- var CDCToken = class extends CSSParserToken {
3799
- constructor(..._args5) {
3800
- super(..._args5);
3801
- _defineProperty(this, "tokenType", "CDC");
3802
- }
3803
- toSource() {
3804
- return "-->";
3805
- }
3806
- };
3807
- var ColonToken = class extends CSSParserToken {
3808
- constructor(..._args6) {
3809
- super(..._args6);
3810
- _defineProperty(this, "tokenType", ":");
3811
- }
3812
- };
3813
- var SemicolonToken = class extends CSSParserToken {
3814
- constructor(..._args7) {
3815
- super(..._args7);
3816
- _defineProperty(this, "tokenType", ";");
3817
- }
3818
- };
3819
- var CommaToken = class extends CSSParserToken {
3820
- constructor(..._args8) {
3821
- super(..._args8);
3822
- _defineProperty(this, "tokenType", ",");
3823
- }
3824
- };
3825
- var GroupingToken = class extends CSSParserToken {
3826
- constructor(..._args9) {
3827
- super(..._args9);
3828
- _defineProperty(this, "value", "");
3829
- _defineProperty(this, "mirror", "");
3830
- }
3831
- };
3832
- var OpenCurlyToken = class extends GroupingToken {
3833
- constructor() {
3834
- super();
3835
- _defineProperty(this, "tokenType", "{");
3836
- this.value = "{";
3837
- this.mirror = "}";
3838
- }
3839
- };
3840
- var CloseCurlyToken = class extends GroupingToken {
3841
- constructor() {
3842
- super();
3843
- _defineProperty(this, "tokenType", "}");
3844
- this.value = "}";
3845
- this.mirror = "{";
3846
- }
3847
- };
3848
- var OpenSquareToken = class extends GroupingToken {
3849
- constructor() {
3850
- super();
3851
- _defineProperty(this, "tokenType", "[");
3852
- this.value = "[";
3853
- this.mirror = "]";
3854
- }
3855
- };
3856
- var CloseSquareToken = class extends GroupingToken {
3857
- constructor() {
3858
- super();
3859
- _defineProperty(this, "tokenType", "]");
3860
- this.value = "]";
3861
- this.mirror = "[";
3862
- }
3863
- };
3864
- var OpenParenToken = class extends GroupingToken {
3865
- constructor() {
3866
- super();
3867
- _defineProperty(this, "tokenType", "(");
3868
- this.value = "(";
3869
- this.mirror = ")";
3870
- }
3871
- };
3872
- var CloseParenToken = class extends GroupingToken {
3873
- constructor() {
3874
- super();
3875
- _defineProperty(this, "tokenType", ")");
3876
- this.value = ")";
3877
- this.mirror = "(";
3878
- }
3879
- };
3880
- var IncludeMatchToken = class extends CSSParserToken {
3881
- constructor(..._args10) {
3882
- super(..._args10);
3883
- _defineProperty(this, "tokenType", "~=");
3884
- }
3885
- };
3886
- var DashMatchToken = class extends CSSParserToken {
3887
- constructor(..._args11) {
3888
- super(..._args11);
3889
- _defineProperty(this, "tokenType", "|=");
3890
- }
3891
- };
3892
- var PrefixMatchToken = class extends CSSParserToken {
3893
- constructor(..._args12) {
3894
- super(..._args12);
3895
- _defineProperty(this, "tokenType", "^=");
3896
- }
3897
- };
3898
- var SuffixMatchToken = class extends CSSParserToken {
3899
- constructor(..._args13) {
3900
- super(..._args13);
3901
- _defineProperty(this, "tokenType", "$=");
3902
- }
3903
- };
3904
- var SubstringMatchToken = class extends CSSParserToken {
3905
- constructor(..._args14) {
3906
- super(..._args14);
3907
- _defineProperty(this, "tokenType", "*=");
3908
- }
3909
- };
3910
- var ColumnToken = class extends CSSParserToken {
3911
- constructor(..._args15) {
3912
- super(..._args15);
3913
- _defineProperty(this, "tokenType", "||");
3914
- }
3915
- };
3916
- var EOFToken = class extends CSSParserToken {
3917
- constructor(..._args16) {
3918
- super(..._args16);
3919
- _defineProperty(this, "tokenType", "EOF");
3920
- }
3921
- toSource() {
3922
- return "";
3923
- }
3924
- };
3925
- var DelimToken = class extends CSSParserToken {
3926
- constructor(code) {
3927
- super();
3928
- _defineProperty(this, "tokenType", "DELIM");
3929
- _defineProperty(this, "value", "");
3930
- this.value = stringFromCode(code);
3931
- }
3932
- toString() {
3933
- return `DELIM(${this.value})`;
3934
- }
3935
- toJSON() {
3936
- const json = this.constructor.prototype.constructor.prototype.toJSON.call(this);
3937
- json.value = this.value;
3938
- return json;
3939
- }
3940
- toSource() {
3941
- if (this.value === "\\") return "\\\n";
3942
- else return this.value;
3943
- }
3944
- };
3945
- var StringValuedToken = class extends CSSParserToken {
3946
- constructor(..._args17) {
3947
- super(..._args17);
3948
- _defineProperty(this, "value", "");
3949
- }
3950
- ASCIIMatch(str) {
3951
- return this.value.toLowerCase() === str.toLowerCase();
3952
- }
3953
- toJSON() {
3954
- const json = this.constructor.prototype.constructor.prototype.toJSON.call(this);
3955
- json.value = this.value;
3956
- return json;
3957
- }
3958
- };
3959
- var IdentToken = class extends StringValuedToken {
3960
- constructor(val) {
3961
- super();
3962
- _defineProperty(this, "tokenType", "IDENT");
3963
- this.value = val;
3964
- }
3965
- toString() {
3966
- return `IDENT(${this.value})`;
3967
- }
3968
- toSource() {
3969
- return escapeIdent(this.value);
3970
- }
3971
- };
3972
- var FunctionToken = class extends StringValuedToken {
3973
- constructor(val) {
3974
- super();
3975
- _defineProperty(this, "tokenType", "FUNCTION");
3976
- _defineProperty(this, "mirror", void 0);
3977
- this.value = val;
3978
- this.mirror = ")";
3979
- }
3980
- toString() {
3981
- return `FUNCTION(${this.value})`;
3982
- }
3983
- toSource() {
3984
- return `${escapeIdent(this.value)}(`;
3985
- }
3986
- };
3987
- var AtKeywordToken = class extends StringValuedToken {
3988
- constructor(val) {
3989
- super();
3990
- _defineProperty(this, "tokenType", "AT-KEYWORD");
3991
- this.value = val;
3992
- }
3993
- toString() {
3994
- return `AT(${this.value})`;
3995
- }
3996
- toSource() {
3997
- return `@${escapeIdent(this.value)}`;
3998
- }
3999
- };
4000
- var HashToken = class extends StringValuedToken {
4001
- constructor(val) {
4002
- super();
4003
- _defineProperty(this, "tokenType", "HASH");
4004
- _defineProperty(this, "type", void 0);
4005
- this.value = val;
4006
- this.type = "unrestricted";
4007
- }
4008
- toString() {
4009
- return `HASH(${this.value})`;
4010
- }
4011
- toJSON() {
4012
- const json = this.constructor.prototype.constructor.prototype.toJSON.call(this);
4013
- json.value = this.value;
4014
- json.type = this.type;
4015
- return json;
4016
- }
4017
- toSource() {
4018
- if (this.type === "id") return `#${escapeIdent(this.value)}`;
4019
- else return `#${escapeHash(this.value)}`;
4020
- }
4021
- };
4022
- var StringToken = class extends StringValuedToken {
4023
- constructor(val) {
4024
- super();
4025
- _defineProperty(this, "tokenType", "STRING");
4026
- this.value = val;
4027
- }
4028
- toString() {
4029
- return `"${escapeString(this.value)}"`;
4030
- }
4031
- };
4032
- var URLToken = class extends StringValuedToken {
4033
- constructor(val) {
4034
- super();
4035
- _defineProperty(this, "tokenType", "URL");
4036
- this.value = val;
4037
- }
4038
- toString() {
4039
- return `URL(${this.value})`;
4040
- }
4041
- toSource() {
4042
- return `url("${escapeString(this.value)}")`;
4043
- }
4044
- };
4045
- var NumberToken = class extends CSSParserToken {
4046
- constructor() {
4047
- super();
4048
- _defineProperty(this, "tokenType", "NUMBER");
4049
- _defineProperty(this, "type", void 0);
4050
- _defineProperty(this, "repr", void 0);
4051
- this.type = "integer";
4052
- this.repr = "";
4053
- }
4054
- toString() {
4055
- if (this.type === "integer") return `INT(${this.value})`;
4056
- return `NUMBER(${this.value})`;
4057
- }
4058
- toJSON() {
4059
- const json = super.toJSON();
4060
- json.value = this.value;
4061
- json.type = this.type;
4062
- json.repr = this.repr;
4063
- return json;
4064
- }
4065
- toSource() {
4066
- return this.repr;
4067
- }
4068
- };
4069
- var PercentageToken = class extends CSSParserToken {
4070
- constructor() {
4071
- super();
4072
- _defineProperty(this, "tokenType", "PERCENTAGE");
4073
- _defineProperty(this, "repr", void 0);
4074
- this.repr = "";
4075
- }
4076
- toString() {
4077
- return `PERCENTAGE(${this.value})`;
4078
- }
4079
- toJSON() {
4080
- const json = this.constructor.prototype.constructor.prototype.toJSON.call(this);
4081
- json.value = this.value;
4082
- json.repr = this.repr;
4083
- return json;
4084
- }
4085
- toSource() {
4086
- return `${this.repr}%`;
4087
- }
4088
- };
4089
- var DimensionToken = class extends CSSParserToken {
4090
- constructor() {
4091
- super();
4092
- _defineProperty(this, "tokenType", "DIMENSION");
4093
- _defineProperty(this, "type", void 0);
4094
- _defineProperty(this, "repr", void 0);
4095
- _defineProperty(this, "unit", void 0);
4096
- this.type = "integer";
4097
- this.repr = "";
4098
- this.unit = "";
4099
- }
4100
- toString() {
4101
- return `DIM(${this.value},${this.unit})`;
4102
- }
4103
- toJSON() {
4104
- const json = this.constructor.prototype.constructor.prototype.toJSON.call(this);
4105
- json.value = this.value;
4106
- json.type = this.type;
4107
- json.repr = this.repr;
4108
- json.unit = this.unit;
4109
- return json;
4110
- }
4111
- toSource() {
4112
- const source = this.repr;
4113
- let unit = escapeIdent(this.unit);
4114
- if (unit[0].toLowerCase() === "e" && (unit[1] === "-" || between(unit.charCodeAt(1), 48, 57))) unit = `\\65 ${unit.slice(1, unit.length)}`;
4115
- return source + unit;
4116
- }
4117
- };
4118
- function escapeIdent(string) {
4119
- string = `${string}`;
4120
- let result = "";
4121
- const firstcode = string.charCodeAt(0);
4122
- for (let i = 0; i < string.length; i++) {
4123
- const code = string.charCodeAt(i);
4124
- if (code === 0) throw new InvalidCharacterError("Invalid character: the input contains U+0000.");
4125
- if (between(code, 1, 31) || code === 127 || i === 0 && between(code, 48, 57) || i === 1 && between(code, 48, 57) && firstcode === 45) result += `\\${code.toString(16)} `;
4126
- else if (code >= 128 || code === 45 || code === 95 || between(code, 48, 57) || between(code, 65, 90) || between(code, 97, 122)) result += string[i];
4127
- else result += `\\${string[i]}`;
4128
- }
4129
- return result;
4130
- }
4131
- function escapeHash(string) {
4132
- string = `${string}`;
4133
- let result = "";
4134
- for (let i = 0; i < string.length; i++) {
4135
- const code = string.charCodeAt(i);
4136
- if (code === 0) throw new InvalidCharacterError("Invalid character: the input contains U+0000.");
4137
- if (code >= 128 || code === 45 || code === 95 || between(code, 48, 57) || between(code, 65, 90) || between(code, 97, 122)) result += string[i];
4138
- else result += `\\${code.toString(16)} `;
4139
- }
4140
- return result;
4141
- }
4142
- function escapeString(string) {
4143
- string = `${string}`;
4144
- let result = "";
4145
- for (let i = 0; i < string.length; i++) {
4146
- const code = string.charCodeAt(i);
4147
- if (code === 0) throw new InvalidCharacterError("Invalid character: the input contains U+0000.");
4148
- if (between(code, 1, 31) || code === 127) result += `\\${code.toString(16)} `;
4149
- else if (code === 34 || code === 92) result += `\\${string[i]}`;
4150
- else result += string[i];
4151
- }
4152
- return result;
4153
- }
4154
-
4155
- //#endregion
4156
- //#region src/cssParser.ts
4157
- /**
4158
- * Copyright (c) Microsoft Corporation.
4159
- *
4160
- * Licensed under the Apache License, Version 2.0 (the "License");
4161
- * you may not use this file except in compliance with the License.
4162
- * You may obtain a copy of the License at
4163
- *
4164
- * http://www.apache.org/licenses/LICENSE-2.0
4165
- *
4166
- * Unless required by applicable law or agreed to in writing, software
4167
- * distributed under the License is distributed on an "AS IS" BASIS,
4168
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
4169
- * See the License for the specific language governing permissions and
4170
- * limitations under the License.
4171
- */
4172
- var InvalidSelectorError = class extends Error {};
4173
- function parseCSS(selector, customNames) {
4174
- let tokens;
4175
- try {
4176
- tokens = tokenize(selector);
4177
- if (!(tokens[tokens.length - 1] instanceof EOFToken)) tokens.push(new EOFToken());
4178
- } catch (e) {
4179
- const newMessage = `${e.message} while parsing selector "${selector}"`;
4180
- const index = (e.stack || "").indexOf(e.message);
4181
- if (index !== -1) e.stack = e.stack.substring(0, index) + newMessage + e.stack.substring(index + e.message.length);
4182
- e.message = newMessage;
4183
- throw e;
4184
- }
4185
- const unsupportedToken = tokens.find((token) => {
4186
- return token instanceof AtKeywordToken || token instanceof BadStringToken || token instanceof BadURLToken || token instanceof ColumnToken || token instanceof CDOToken || token instanceof CDCToken || token instanceof SemicolonToken || token instanceof OpenCurlyToken || token instanceof CloseCurlyToken || token instanceof URLToken || token instanceof PercentageToken;
4187
- });
4188
- if (unsupportedToken) throw new InvalidSelectorError(`Unsupported token "${unsupportedToken.toSource()}" while parsing selector "${selector}"`);
4189
- let pos = 0;
4190
- const names = /* @__PURE__ */ new Set();
4191
- function unexpected() {
4192
- return new InvalidSelectorError(`Unexpected token "${tokens[pos].toSource()}" while parsing selector "${selector}"`);
4193
- }
4194
- function skipWhitespace() {
4195
- while (tokens[pos] instanceof WhitespaceToken) pos++;
4196
- }
4197
- function isIdent(p = pos) {
4198
- return tokens[p] instanceof IdentToken;
4199
- }
4200
- function isString(p = pos) {
4201
- return tokens[p] instanceof StringToken;
4202
- }
4203
- function isNumber(p = pos) {
4204
- return tokens[p] instanceof NumberToken;
4205
- }
4206
- function isComma(p = pos) {
4207
- return tokens[p] instanceof CommaToken;
4208
- }
4209
- function isOpenParen(p = pos) {
4210
- return tokens[p] instanceof OpenParenToken;
4211
- }
4212
- function isCloseParen(p = pos) {
4213
- return tokens[p] instanceof CloseParenToken;
4214
- }
4215
- function isFunction(p = pos) {
4216
- return tokens[p] instanceof FunctionToken;
4217
- }
4218
- function isStar(p = pos) {
4219
- return tokens[p] instanceof DelimToken && tokens[p].value === "*";
4220
- }
4221
- function isEOF(p = pos) {
4222
- return tokens[p] instanceof EOFToken;
4223
- }
4224
- function isClauseCombinator(p = pos) {
4225
- return tokens[p] instanceof DelimToken && [
4226
- ">",
4227
- "+",
4228
- "~"
4229
- ].includes(tokens[p].value);
4230
- }
4231
- function isSelectorClauseEnd(p = pos) {
4232
- return isComma(p) || isCloseParen(p) || isEOF(p) || isClauseCombinator(p) || tokens[p] instanceof WhitespaceToken;
4233
- }
4234
- function consumeFunctionArguments() {
4235
- const result = [consumeArgument()];
4236
- while (true) {
4237
- skipWhitespace();
4238
- if (!isComma()) break;
4239
- pos++;
4240
- result.push(consumeArgument());
4241
- }
4242
- return result;
4243
- }
4244
- function consumeArgument() {
4245
- skipWhitespace();
4246
- if (isNumber()) return tokens[pos++].value;
4247
- if (isString()) return tokens[pos++].value;
4248
- return consumeComplexSelector();
4249
- }
4250
- function consumeComplexSelector() {
4251
- const result = { simples: [] };
4252
- skipWhitespace();
4253
- if (isClauseCombinator()) result.simples.push({
4254
- selector: { functions: [{
4255
- name: "scope",
4256
- args: []
4257
- }] },
4258
- combinator: ""
4259
- });
4260
- else result.simples.push({
4261
- selector: consumeSimpleSelector(),
4262
- combinator: ""
4263
- });
4264
- while (true) {
4265
- skipWhitespace();
4266
- if (isClauseCombinator()) {
4267
- result.simples[result.simples.length - 1].combinator = tokens[pos++].value;
4268
- skipWhitespace();
4269
- } else if (isSelectorClauseEnd()) break;
4270
- result.simples.push({
4271
- combinator: "",
4272
- selector: consumeSimpleSelector()
4273
- });
4274
- }
4275
- return result;
4276
- }
4277
- function consumeSimpleSelector() {
4278
- let rawCSSString = "";
4279
- const functions = [];
4280
- while (!isSelectorClauseEnd()) if (isIdent() || isStar()) rawCSSString += tokens[pos++].toSource();
4281
- else if (tokens[pos] instanceof HashToken) rawCSSString += tokens[pos++].toSource();
4282
- else if (tokens[pos] instanceof DelimToken && tokens[pos].value === ".") {
4283
- pos++;
4284
- if (isIdent()) rawCSSString += `.${tokens[pos++].toSource()}`;
4285
- else throw unexpected();
4286
- } else if (tokens[pos] instanceof ColonToken) {
4287
- pos++;
4288
- if (isIdent()) if (!customNames.has(tokens[pos].value.toLowerCase())) rawCSSString += `:${tokens[pos++].toSource()}`;
4289
- else {
4290
- const name = tokens[pos++].value.toLowerCase();
4291
- functions.push({
4292
- name,
4293
- args: []
4294
- });
4295
- names.add(name);
4296
- }
4297
- else if (isFunction()) {
4298
- const name = tokens[pos++].value.toLowerCase();
4299
- if (!customNames.has(name)) rawCSSString += `:${name}(${consumeBuiltinFunctionArguments()})`;
4300
- else {
4301
- functions.push({
4302
- name,
4303
- args: consumeFunctionArguments()
4304
- });
4305
- names.add(name);
4306
- }
4307
- skipWhitespace();
4308
- if (!isCloseParen()) throw unexpected();
4309
- pos++;
4310
- } else throw unexpected();
4311
- } else if (tokens[pos] instanceof OpenSquareToken) {
4312
- rawCSSString += "[";
4313
- pos++;
4314
- while (!(tokens[pos] instanceof CloseSquareToken) && !isEOF()) rawCSSString += tokens[pos++].toSource();
4315
- if (!(tokens[pos] instanceof CloseSquareToken)) throw unexpected();
4316
- rawCSSString += "]";
4317
- pos++;
4318
- } else throw unexpected();
4319
- if (!rawCSSString && !functions.length) throw unexpected();
4320
- return {
4321
- css: rawCSSString || void 0,
4322
- functions
4323
- };
4324
- }
4325
- function consumeBuiltinFunctionArguments() {
4326
- let s = "";
4327
- let balance = 1;
4328
- while (!isEOF()) {
4329
- if (isOpenParen() || isFunction()) balance++;
4330
- if (isCloseParen()) balance--;
4331
- if (!balance) break;
4332
- s += tokens[pos++].toSource();
4333
- }
4334
- return s;
4335
- }
4336
- const result = consumeFunctionArguments();
4337
- if (!isEOF()) throw unexpected();
4338
- if (result.some((arg) => typeof arg !== "object" || !("simples" in arg))) throw new InvalidSelectorError(`Error while parsing selector "${selector}"`);
4339
- return {
4340
- selector: result,
4341
- names: Array.from(names)
4342
- };
4343
- }
4344
-
4345
- //#endregion
4346
- //#region src/selectorParser.ts
4347
- const kNestedSelectorNames = new Set([
4348
- "internal:has",
4349
- "internal:has-not",
4350
- "internal:and",
4351
- "internal:or",
4352
- "internal:chain",
4353
- "left-of",
4354
- "right-of",
4355
- "above",
4356
- "below",
4357
- "near"
4358
- ]);
4359
- const kNestedSelectorNamesWithDistance = new Set([
4360
- "left-of",
4361
- "right-of",
4362
- "above",
4363
- "below",
4364
- "near"
4365
- ]);
4366
- const customCSSNames = new Set([
4367
- "not",
4368
- "is",
4369
- "where",
4370
- "has",
4371
- "scope",
4372
- "light",
4373
- "visible",
4374
- "text",
4375
- "text-matches",
4376
- "text-is",
4377
- "has-text",
4378
- "above",
4379
- "below",
4380
- "right-of",
4381
- "left-of",
4382
- "near",
4383
- "nth-match"
4384
- ]);
4385
- function parseSelector(selector) {
4386
- const parsedStrings = parseSelectorString(selector);
4387
- const parts = [];
4388
- for (const part of parsedStrings.parts) {
4389
- if (part.name === "css" || part.name === "css:light") {
4390
- if (part.name === "css:light") part.body = `:light(${part.body})`;
4391
- const parsedCSS = parseCSS(part.body, customCSSNames);
4392
- parts.push({
4393
- name: "css",
4394
- body: parsedCSS.selector,
4395
- source: part.body
4396
- });
4397
- continue;
4398
- }
4399
- if (kNestedSelectorNames.has(part.name)) {
4400
- let innerSelector;
4401
- let distance;
4402
- try {
4403
- const unescaped = JSON.parse(`[${part.body}]`);
4404
- if (!Array.isArray(unescaped) || unescaped.length < 1 || unescaped.length > 2 || typeof unescaped[0] !== "string") throw new InvalidSelectorError(`Malformed selector: ${part.name}=${part.body}`);
4405
- innerSelector = unescaped[0];
4406
- if (unescaped.length === 2) {
4407
- if (typeof unescaped[1] !== "number" || !kNestedSelectorNamesWithDistance.has(part.name)) throw new InvalidSelectorError(`Malformed selector: ${part.name}=${part.body}`);
4408
- distance = unescaped[1];
4409
- }
4410
- } catch (e) {
4411
- throw new InvalidSelectorError(`Malformed selector: ${part.name}=${part.body}`);
4412
- }
4413
- const nested = {
4414
- name: part.name,
4415
- source: part.body,
4416
- body: {
4417
- parsed: parseSelector(innerSelector),
4418
- distance
4419
- }
4420
- };
4421
- const lastFrame = [...nested.body.parsed.parts].reverse().find((part) => part.name === "internal:control" && part.body === "enter-frame");
4422
- const lastFrameIndex = lastFrame ? nested.body.parsed.parts.indexOf(lastFrame) : -1;
4423
- if (lastFrameIndex !== -1 && selectorPartsEqual(nested.body.parsed.parts.slice(0, lastFrameIndex + 1), parts.slice(0, lastFrameIndex + 1))) nested.body.parsed.parts.splice(0, lastFrameIndex + 1);
4424
- parts.push(nested);
4425
- continue;
4426
- }
4427
- parts.push({
4428
- ...part,
4429
- source: part.body
4430
- });
4431
- }
4432
- if (kNestedSelectorNames.has(parts[0].name)) throw new InvalidSelectorError(`"${parts[0].name}" selector cannot be first`);
4433
- return {
4434
- capture: parsedStrings.capture,
4435
- parts
4436
- };
4437
- }
4438
- function selectorPartsEqual(list1, list2) {
4439
- return stringifySelector({ parts: list1 }) === stringifySelector({ parts: list2 });
4440
- }
4441
- function stringifySelector(selector, forceEngineName) {
4442
- if (typeof selector === "string") return selector;
4443
- return selector.parts.map((p, i) => {
4444
- let includeEngine = true;
4445
- if (!forceEngineName && i !== selector.capture) {
4446
- if (p.name === "css") includeEngine = false;
4447
- else if (p.name === "xpath" && p.source.startsWith("//") || p.source.startsWith("..")) includeEngine = false;
4448
- }
4449
- const prefix = includeEngine ? `${p.name}=` : "";
4450
- return `${i === selector.capture ? "*" : ""}${prefix}${p.source}`;
4451
- }).join(" >> ");
4452
- }
4453
- function visitAllSelectorParts(selector, visitor) {
4454
- const visit = (selector, nested) => {
4455
- for (const part of selector.parts) {
4456
- visitor(part, nested);
4457
- if (kNestedSelectorNames.has(part.name)) visit(part.body.parsed, true);
4458
- }
4459
- };
4460
- visit(selector, false);
4461
- }
4462
- function parseSelectorString(selector) {
4463
- let index = 0;
4464
- let quote;
4465
- let start = 0;
4466
- const result = { parts: [] };
4467
- const append = () => {
4468
- const part = selector.substring(start, index).trim();
4469
- const eqIndex = part.indexOf("=");
4470
- let name;
4471
- let body;
4472
- if (eqIndex !== -1 && part.substring(0, eqIndex).trim().match(/^[\w\-+:*]+$/)) {
4473
- name = part.substring(0, eqIndex).trim();
4474
- body = part.substring(eqIndex + 1);
4475
- } else if (part.length > 1 && part[0] === "\"" && part[part.length - 1] === "\"") {
4476
- name = "text";
4477
- body = part;
4478
- } else if (part.length > 1 && part[0] === "'" && part[part.length - 1] === "'") {
4479
- name = "text";
4480
- body = part;
4481
- } else if (/^\(*\/\//.test(part) || part.startsWith("..")) {
4482
- name = "xpath";
4483
- body = part;
4484
- } else {
4485
- name = "css";
4486
- body = part;
4487
- }
4488
- let capture = false;
4489
- if (name[0] === "*") {
4490
- capture = true;
4491
- name = name.substring(1);
4492
- }
4493
- result.parts.push({
4494
- name,
4495
- body
4496
- });
4497
- if (capture) {
4498
- if (result.capture !== void 0) throw new InvalidSelectorError(`Only one of the selectors can capture using * modifier`);
4499
- result.capture = result.parts.length - 1;
4500
- }
4501
- };
4502
- if (!selector.includes(">>")) {
4503
- index = selector.length;
4504
- append();
4505
- return result;
4506
- }
4507
- const shouldIgnoreTextSelectorQuote = () => {
4508
- const match = selector.substring(start, index).match(/^\s*text\s*=(.*)$/);
4509
- return !!match && !!match[1];
4510
- };
4511
- while (index < selector.length) {
4512
- const c = selector[index];
4513
- if (c === "\\" && index + 1 < selector.length) index += 2;
4514
- else if (c === quote) {
4515
- quote = void 0;
4516
- index++;
4517
- } else if (!quote && (c === "\"" || c === "'" || c === "`") && !shouldIgnoreTextSelectorQuote()) {
4518
- quote = c;
4519
- index++;
4520
- } else if (!quote && c === ">" && selector[index + 1] === ">") {
4521
- append();
4522
- index += 2;
4523
- start = index;
4524
- } else index++;
4525
- }
4526
- append();
4527
- return result;
4528
- }
4529
- function parseAttributeSelector(selector, allowUnquotedStrings) {
4530
- let wp = 0;
4531
- let EOL = selector.length === 0;
4532
- const next = () => selector[wp] || "";
4533
- const eat1 = () => {
4534
- const result = next();
4535
- ++wp;
4536
- EOL = wp >= selector.length;
4537
- return result;
4538
- };
4539
- const syntaxError = (stage) => {
4540
- if (EOL) throw new InvalidSelectorError(`Unexpected end of selector while parsing selector \`${selector}\``);
4541
- throw new InvalidSelectorError(`Error while parsing selector \`${selector}\` - unexpected symbol "${next()}" at position ${wp}${stage ? ` during ${stage}` : ""}`);
4542
- };
4543
- function skipSpaces() {
4544
- while (!EOL && /\s/.test(next())) eat1();
4545
- }
4546
- function isCSSNameChar(char) {
4547
- return char >= "€" || char >= "0" && char <= "9" || char >= "A" && char <= "Z" || char >= "a" && char <= "z" || char >= "0" && char <= "9" || char === "_" || char === "-";
4548
- }
4549
- function readIdentifier() {
4550
- let result = "";
4551
- skipSpaces();
4552
- while (!EOL && isCSSNameChar(next())) result += eat1();
4553
- return result;
4554
- }
4555
- function readQuotedString(quote) {
4556
- let result = eat1();
4557
- if (result !== quote) syntaxError("parsing quoted string");
4558
- while (!EOL && next() !== quote) {
4559
- if (next() === "\\") eat1();
4560
- result += eat1();
4561
- }
4562
- if (next() !== quote) syntaxError("parsing quoted string");
4563
- result += eat1();
4564
- return result;
4565
- }
4566
- function readRegularExpression() {
4567
- if (eat1() !== "/") syntaxError("parsing regular expression");
4568
- let source = "";
4569
- let inClass = false;
4570
- while (!EOL) {
4571
- if (next() === "\\") {
4572
- source += eat1();
4573
- if (EOL) syntaxError("parsing regular expression");
4574
- } else if (inClass && next() === "]") inClass = false;
4575
- else if (!inClass && next() === "[") inClass = true;
4576
- else if (!inClass && next() === "/") break;
4577
- source += eat1();
4578
- }
4579
- if (eat1() !== "/") syntaxError("parsing regular expression");
4580
- let flags = "";
4581
- while (!EOL && next().match(/[dgimsuy]/)) flags += eat1();
4582
- try {
4583
- return new RegExp(source, flags);
4584
- } catch (e) {
4585
- throw new InvalidSelectorError(`Error while parsing selector \`${selector}\`: ${e.message}`);
4586
- }
4587
- }
4588
- function readAttributeToken() {
4589
- let token = "";
4590
- skipSpaces();
4591
- if (next() === `'` || next() === `"`) token = readQuotedString(next()).slice(1, -1);
4592
- else token = readIdentifier();
4593
- if (!token) syntaxError("parsing property path");
4594
- return token;
4595
- }
4596
- function readOperator() {
4597
- skipSpaces();
4598
- let op = "";
4599
- if (!EOL) op += eat1();
4600
- if (!EOL && op !== "=") op += eat1();
4601
- if (![
4602
- "=",
4603
- "*=",
4604
- "^=",
4605
- "$=",
4606
- "|=",
4607
- "~="
4608
- ].includes(op)) syntaxError("parsing operator");
4609
- return op;
4610
- }
4611
- function readAttribute() {
4612
- eat1();
4613
- const jsonPath = [];
4614
- jsonPath.push(readAttributeToken());
4615
- skipSpaces();
4616
- while (next() === ".") {
4617
- eat1();
4618
- jsonPath.push(readAttributeToken());
4619
- skipSpaces();
4620
- }
4621
- if (next() === "]") {
4622
- eat1();
4623
- return {
4624
- name: jsonPath.join("."),
4625
- jsonPath,
4626
- op: "<truthy>",
4627
- value: null,
4628
- caseSensitive: false
4629
- };
4630
- }
4631
- const operator = readOperator();
4632
- let value;
4633
- let caseSensitive = true;
4634
- skipSpaces();
4635
- if (next() === "/") {
4636
- if (operator !== "=") throw new InvalidSelectorError(`Error while parsing selector \`${selector}\` - cannot use ${operator} in attribute with regular expression`);
4637
- value = readRegularExpression();
4638
- } else if (next() === `'` || next() === `"`) {
4639
- value = readQuotedString(next()).slice(1, -1);
4640
- skipSpaces();
4641
- if (next() === "i" || next() === "I") {
4642
- caseSensitive = false;
4643
- eat1();
4644
- } else if (next() === "s" || next() === "S") {
4645
- caseSensitive = true;
4646
- eat1();
4647
- }
4648
- } else {
4649
- value = "";
4650
- while (!EOL && (isCSSNameChar(next()) || next() === "+" || next() === ".")) value += eat1();
4651
- if (value === "true") value = true;
4652
- else if (value === "false") value = false;
4653
- else ;
4654
- }
4655
- skipSpaces();
4656
- if (next() !== "]") syntaxError("parsing attribute value");
4657
- eat1();
4658
- if (operator !== "=" && typeof value !== "string") throw new InvalidSelectorError(`Error while parsing selector \`${selector}\` - cannot use ${operator} in attribute with non-string matching value - ${value}`);
4659
- return {
4660
- name: jsonPath.join("."),
4661
- jsonPath,
4662
- op: operator,
4663
- value,
4664
- caseSensitive
4665
- };
4666
- }
4667
- const result = {
4668
- name: "",
4669
- attributes: []
4670
- };
4671
- result.name = readIdentifier();
4672
- skipSpaces();
4673
- while (next() === "[") {
4674
- result.attributes.push(readAttribute());
4675
- skipSpaces();
4676
- }
4677
- if (!EOL) syntaxError(void 0);
4678
- if (!result.name && !result.attributes.length) throw new InvalidSelectorError(`Error while parsing selector \`${selector}\` - selector cannot be empty`);
4679
- return result;
4680
- }
4681
-
4682
- //#endregion
4683
- //#region src/stringUtils.ts
4684
- /**
4685
- * Copyright (c) Microsoft Corporation.
4686
- *
4687
- * Licensed under the Apache License, Version 2.0 (the "License");
4688
- * you may not use this file except in compliance with the License.
4689
- * You may obtain a copy of the License at
4690
- *
4691
- * http://www.apache.org/licenses/LICENSE-2.0
4692
- *
4693
- * Unless required by applicable law or agreed to in writing, software
4694
- * distributed under the License is distributed on an "AS IS" BASIS,
4695
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
4696
- * See the License for the specific language governing permissions and
4697
- * limitations under the License.
4698
- */
4699
- function escapeWithQuotes(text, char = "'") {
4700
- const stringified = JSON.stringify(text);
4701
- const escapedText = stringified.substring(1, stringified.length - 1).replace(/\\"/g, "\"");
4702
- if (char === "'") return char + escapedText.replace(/'/g, "\\'") + char;
4703
- if (char === "\"") return char + escapedText.replace(/"/g, "\\\"") + char;
4704
- if (char === "`") return char + escapedText.replace(/`/g, "`") + char;
4705
- throw new Error("Invalid escape char");
4706
- }
4707
- function cssEscape(s) {
4708
- let result = "";
4709
- for (let i = 0; i < s.length; i++) result += cssEscapeOne(s, i);
4710
- return result;
4711
- }
4712
- function quoteCSSAttributeValue(text) {
4713
- return `"${cssEscape(text).replace(/\\ /g, " ")}"`;
4714
- }
4715
- function cssEscapeOne(s, i) {
4716
- const c = s.charCodeAt(i);
4717
- if (c === 0) return "�";
4718
- if (c >= 1 && c <= 31 || c >= 48 && c <= 57 && (i === 0 || i === 1 && s.charCodeAt(0) === 45)) return `\\${c.toString(16)} `;
4719
- if (i === 0 && c === 45 && s.length === 1) return `\\${s.charAt(i)}`;
4720
- if (c >= 128 || c === 45 || c === 95 || c >= 48 && c <= 57 || c >= 65 && c <= 90 || c >= 97 && c <= 122) return s.charAt(i);
4721
- return `\\${s.charAt(i)}`;
4722
- }
4723
- function normalizeWhiteSpace(text) {
4724
- return text.replace(/[\u200b\u00ad]/g, "").trim().replace(/\s+/g, " ");
4725
- }
4726
- function normalizeEscapedRegexQuotes(source) {
4727
- return source.replace(/(^|[^\\])(\\\\)*\\(['"`])/g, "$1$2$3");
4728
- }
4729
- function escapeRegexForSelector(re) {
4730
- if (re.unicode || re.unicodeSets) return String(re);
4731
- return String(re).replace(/(^|[^\\])(\\\\)*(["'`])/g, "$1$2\\$3").replace(/>>/g, "\\>\\>");
4732
- }
4733
- function escapeForTextSelector(text, exact) {
4734
- if (typeof text !== "string") return escapeRegexForSelector(text);
4735
- return `${JSON.stringify(text)}${exact ? "s" : "i"}`;
4736
- }
4737
- function escapeForAttributeSelector(value, exact) {
4738
- if (typeof value !== "string") return escapeRegexForSelector(value);
4739
- return `"${value.replace(/\\/g, "\\\\").replace(/"/g, "\\\"")}"${exact ? "s" : "i"}`;
4740
- }
4741
- function trimString(input, cap, suffix = "") {
4742
- if (input.length <= cap) return input;
4743
- const chars = [...input];
4744
- if (chars.length > cap) return chars.slice(0, cap - suffix.length).join("") + suffix;
4745
- return chars.join("");
4746
- }
4747
- function trimStringWithEllipsis(input, cap) {
4748
- return trimString(input, cap, "…");
4749
- }
4750
- function escapeRegExp(s) {
4751
- return s.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
4752
- }
4753
-
4754
- //#endregion
4755
- //#region src/domUtils.ts
4756
- /**
4757
- * Copyright (c) Microsoft Corporation.
4758
- *
4759
- * Licensed under the Apache License, Version 2.0 (the "License");
4760
- * you may not use this file except in compliance with the License.
4761
- * You may obtain a copy of the License at
4762
- *
4763
- * http://www.apache.org/licenses/LICENSE-2.0
4764
- *
4765
- * Unless required by applicable law or agreed to in writing, software
4766
- * distributed under the License is distributed on an "AS IS" BASIS,
4767
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
4768
- * See the License for the specific language governing permissions and
4769
- * limitations under the License.
4770
- */
4771
- function parentElementOrShadowHost(element) {
4772
- if (element.parentElement) return element.parentElement;
4773
- if (!element.parentNode) return;
4774
- if (element.parentNode.nodeType === 11 && element.parentNode.host) return element.parentNode.host;
4775
- }
4776
- function enclosingShadowRootOrDocument(element) {
4777
- let node = element;
4778
- while (node.parentNode) node = node.parentNode;
4779
- if (node.nodeType === 11 || node.nodeType === 9) return node;
4780
- }
4781
- function enclosingShadowHost(element) {
4782
- while (element.parentElement) element = element.parentElement;
4783
- return parentElementOrShadowHost(element);
4784
- }
4785
- function closestCrossShadow(element, css) {
4786
- while (element) {
4787
- const closest = element.closest(css);
4788
- if (closest) return closest;
4789
- element = enclosingShadowHost(element);
4790
- }
4791
- }
4792
- function getElementComputedStyle(element, pseudo) {
4793
- return element.ownerDocument && element.ownerDocument.defaultView ? element.ownerDocument.defaultView.getComputedStyle(element, pseudo) : void 0;
4794
- }
4795
- function isElementStyleVisibilityVisible(element, style) {
4796
- var _style;
4797
- style = (_style = style) !== null && _style !== void 0 ? _style : getElementComputedStyle(element);
4798
- if (!style) return true;
4799
- if (Element.prototype.checkVisibility && Ivya.options.browser !== "webkit") {
4800
- if (!element.checkVisibility()) return false;
4801
- } else {
4802
- const detailsOrSummary = element.closest("details,summary");
4803
- if (detailsOrSummary !== element && (detailsOrSummary === null || detailsOrSummary === void 0 ? void 0 : detailsOrSummary.nodeName) === "DETAILS" && !detailsOrSummary.open) return false;
4804
- }
4805
- if (style.visibility !== "visible") return false;
4806
- return true;
4807
- }
4808
- function isElementVisible(element) {
4809
- const style = getElementComputedStyle(element);
4810
- if (!style) return true;
4811
- if (style.display === "contents") {
4812
- for (let child = element.firstChild; child; child = child.nextSibling) {
4813
- if (child.nodeType === 1 && isElementVisible(child)) return true;
4814
- if (child.nodeType === 3 && isVisibleTextNode(child)) return true;
4815
- }
4816
- return false;
4817
- }
4818
- if (!isElementStyleVisibilityVisible(element, style)) return false;
4819
- const rect = element.getBoundingClientRect();
4820
- return rect.width > 0 && rect.height > 0;
4821
- }
4822
- function isVisibleTextNode(node) {
4823
- const range = node.ownerDocument.createRange();
4824
- range.selectNode(node);
4825
- const rect = range.getBoundingClientRect();
4826
- return rect.width > 0 && rect.height > 0;
4827
- }
4828
- function elementSafeTagName(element) {
4829
- if (element instanceof HTMLFormElement) return "FORM";
4830
- return element.tagName.toUpperCase();
4831
- }
4832
-
4833
- //#endregion
4834
- //#region src/roleUtils.ts
4835
- /**
4836
- * Copyright (c) Microsoft Corporation.
4837
- *
4838
- * Licensed under the Apache License, Version 2.0 (the "License");
4839
- * you may not use this file except in compliance with the License.
4840
- * You may obtain a copy of the License at
4841
- *
4842
- * http://www.apache.org/licenses/LICENSE-2.0
4843
- *
4844
- * Unless required by applicable law or agreed to in writing, software
4845
- * distributed under the License is distributed on an "AS IS" BASIS,
4846
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
4847
- * See the License for the specific language governing permissions and
4848
- * limitations under the License.
4849
- */
4850
- function hasExplicitAccessibleName(e) {
4851
- return e.hasAttribute("aria-label") || e.hasAttribute("aria-labelledby");
4852
- }
4853
- const kAncestorPreventingLandmark = "article:not([role]), aside:not([role]), main:not([role]), nav:not([role]), section:not([role]), [role=article], [role=complementary], [role=main], [role=navigation], [role=region]";
4854
- const kGlobalAriaAttributes = new Map([
4855
- ["aria-atomic", void 0],
4856
- ["aria-busy", void 0],
4857
- ["aria-controls", void 0],
4858
- ["aria-current", void 0],
4859
- ["aria-describedby", void 0],
4860
- ["aria-details", void 0],
4861
- ["aria-dropeffect", void 0],
4862
- ["aria-flowto", void 0],
4863
- ["aria-grabbed", void 0],
4864
- ["aria-hidden", void 0],
4865
- ["aria-keyshortcuts", void 0],
4866
- ["aria-label", new Set([
4867
- "caption",
4868
- "code",
4869
- "deletion",
4870
- "emphasis",
4871
- "generic",
4872
- "insertion",
4873
- "paragraph",
4874
- "presentation",
4875
- "strong",
4876
- "subscript",
4877
- "superscript"
4878
- ])],
4879
- ["aria-labelledby", new Set([
4880
- "caption",
4881
- "code",
4882
- "deletion",
4883
- "emphasis",
4884
- "generic",
4885
- "insertion",
4886
- "paragraph",
4887
- "presentation",
4888
- "strong",
4889
- "subscript",
4890
- "superscript"
4891
- ])],
4892
- ["aria-live", void 0],
4893
- ["aria-owns", void 0],
4894
- ["aria-relevant", void 0],
4895
- ["aria-roledescription", new Set(["generic"])]
4896
- ]);
4897
- function hasGlobalAriaAttribute(element, forRole) {
4898
- return [...kGlobalAriaAttributes].some(([attr, prohibited]) => {
4899
- return !(prohibited === null || prohibited === void 0 ? void 0 : prohibited.has(forRole || "")) && element.hasAttribute(attr);
4900
- });
4901
- }
4902
- function hasTabIndex(element) {
4903
- return !Number.isNaN(Number(String(element.getAttribute("tabindex"))));
4904
- }
4905
- function isFocusable(element) {
4906
- return !isNativelyDisabled(element) && (isNativelyFocusable(element) || hasTabIndex(element));
4907
- }
4908
- function isNativelyFocusable(element) {
4909
- const tagName = elementSafeTagName(element);
4910
- if ([
4911
- "BUTTON",
4912
- "DETAILS",
4913
- "SELECT",
4914
- "TEXTAREA"
4915
- ].includes(tagName)) return true;
4916
- if (tagName === "A" || tagName === "AREA") return element.hasAttribute("href");
4917
- if (tagName === "INPUT") return !element.hidden;
4918
- return false;
4919
- }
4920
- const kImplicitRoleByTagName = {
4921
- A: (e) => {
4922
- return e.hasAttribute("href") ? "link" : null;
4923
- },
4924
- AREA: (e) => {
4925
- return e.hasAttribute("href") ? "link" : null;
4926
- },
4927
- ARTICLE: () => "article",
4928
- ASIDE: () => "complementary",
4929
- BLOCKQUOTE: () => "blockquote",
4930
- BUTTON: () => "button",
4931
- CAPTION: () => "caption",
4932
- CODE: () => "code",
4933
- DATALIST: () => "listbox",
4934
- DD: () => "definition",
4935
- DEL: () => "deletion",
4936
- DETAILS: () => "group",
4937
- DFN: () => "term",
4938
- DIALOG: () => "dialog",
4939
- DT: () => "term",
4940
- EM: () => "emphasis",
4941
- FIELDSET: () => "group",
4942
- FIGURE: () => "figure",
4943
- FOOTER: (e) => closestCrossShadow(e, kAncestorPreventingLandmark) ? null : "contentinfo",
4944
- FORM: (e) => hasExplicitAccessibleName(e) ? "form" : null,
4945
- H1: () => "heading",
4946
- H2: () => "heading",
4947
- H3: () => "heading",
4948
- H4: () => "heading",
4949
- H5: () => "heading",
4950
- H6: () => "heading",
4951
- HEADER: (e) => closestCrossShadow(e, kAncestorPreventingLandmark) ? null : "banner",
4952
- HR: () => "separator",
4953
- HTML: () => "document",
4954
- IMG: (e) => e.getAttribute("alt") === "" && !e.getAttribute("title") && !hasGlobalAriaAttribute(e) && !hasTabIndex(e) ? "presentation" : "img",
4955
- INPUT: (e) => {
4956
- const type = e.type.toLowerCase();
4957
- if (type === "search") return e.hasAttribute("list") ? "combobox" : "searchbox";
4958
- if ([
4959
- "email",
4960
- "tel",
4961
- "text",
4962
- "url",
4963
- ""
4964
- ].includes(type)) {
4965
- const list = getIdRefs(e, e.getAttribute("list"))[0];
4966
- return list && elementSafeTagName(list) === "DATALIST" ? "combobox" : "textbox";
4967
- }
4968
- if (type === "hidden") return "";
4969
- if (type === "file") return "button";
4970
- return {
4971
- button: "button",
4972
- checkbox: "checkbox",
4973
- image: "button",
4974
- number: "spinbutton",
4975
- radio: "radio",
4976
- range: "slider",
4977
- reset: "button",
4978
- submit: "button"
4979
- }[type] || "textbox";
4980
- },
4981
- INS: () => "insertion",
4982
- LI: () => "listitem",
4983
- MAIN: () => "main",
4984
- MARK: () => "mark",
4985
- MATH: () => "math",
4986
- MENU: () => "list",
4987
- METER: () => "meter",
4988
- NAV: () => "navigation",
4989
- OL: () => "list",
4990
- OPTGROUP: () => "group",
4991
- OPTION: () => "option",
4992
- OUTPUT: () => "status",
4993
- P: () => "paragraph",
4994
- PROGRESS: () => "progressbar",
4995
- SECTION: (e) => hasExplicitAccessibleName(e) ? "region" : null,
4996
- SELECT: (e) => e.hasAttribute("multiple") || e.size > 1 ? "listbox" : "combobox",
4997
- STRONG: () => "strong",
4998
- SUB: () => "subscript",
4999
- SUP: () => "superscript",
5000
- SVG: () => "img",
5001
- TABLE: () => "table",
5002
- TBODY: () => "rowgroup",
5003
- TD: (e) => {
5004
- const table = closestCrossShadow(e, "table");
5005
- const role = table ? getExplicitAriaRole(table) : "";
5006
- return role === "grid" || role === "treegrid" ? "gridcell" : "cell";
5007
- },
5008
- TEXTAREA: () => "textbox",
5009
- TFOOT: () => "rowgroup",
5010
- TH: (e) => {
5011
- if (e.getAttribute("scope") === "col") return "columnheader";
5012
- if (e.getAttribute("scope") === "row") return "rowheader";
5013
- const table = closestCrossShadow(e, "table");
5014
- const role = table ? getExplicitAriaRole(table) : "";
5015
- return role === "grid" || role === "treegrid" ? "gridcell" : "cell";
5016
- },
5017
- THEAD: () => "rowgroup",
5018
- TIME: () => "time",
5019
- TR: () => "row",
5020
- UL: () => "list"
5021
- };
5022
- const kPresentationInheritanceParents = {
5023
- DD: ["DL", "DIV"],
5024
- DIV: ["DL"],
5025
- DT: ["DL", "DIV"],
5026
- LI: ["OL", "UL"],
5027
- TBODY: ["TABLE"],
5028
- TD: ["TR"],
5029
- TFOOT: ["TABLE"],
5030
- TH: ["TR"],
5031
- THEAD: ["TABLE"],
5032
- TR: [
5033
- "THEAD",
5034
- "TBODY",
5035
- "TFOOT",
5036
- "TABLE"
5037
- ]
5038
- };
5039
- function getImplicitAriaRole(element) {
5040
- var _kImplicitRoleByTagNa;
5041
- const implicitRole = ((_kImplicitRoleByTagNa = kImplicitRoleByTagName[elementSafeTagName(element)]) === null || _kImplicitRoleByTagNa === void 0 ? void 0 : _kImplicitRoleByTagNa.call(kImplicitRoleByTagName, element)) || "";
5042
- if (!implicitRole) return null;
5043
- let ancestor = element;
5044
- while (ancestor) {
5045
- const parent = parentElementOrShadowHost(ancestor);
5046
- const parents = kPresentationInheritanceParents[elementSafeTagName(ancestor)];
5047
- if (!parents || !parent || !parents.includes(elementSafeTagName(parent))) break;
5048
- const parentExplicitRole = getExplicitAriaRole(parent);
5049
- if ((parentExplicitRole === "none" || parentExplicitRole === "presentation") && !hasPresentationConflictResolution(parent, parentExplicitRole)) return parentExplicitRole;
5050
- ancestor = parent;
5051
- }
5052
- return implicitRole;
5053
- }
5054
- const allRoles = [
5055
- "alert",
5056
- "alertdialog",
5057
- "application",
5058
- "article",
5059
- "banner",
5060
- "blockquote",
5061
- "button",
5062
- "caption",
5063
- "cell",
5064
- "checkbox",
5065
- "code",
5066
- "columnheader",
5067
- "combobox",
5068
- "command",
5069
- "complementary",
5070
- "composite",
5071
- "contentinfo",
5072
- "definition",
5073
- "deletion",
5074
- "dialog",
5075
- "directory",
5076
- "document",
5077
- "emphasis",
5078
- "feed",
5079
- "figure",
5080
- "form",
5081
- "generic",
5082
- "grid",
5083
- "gridcell",
5084
- "group",
5085
- "heading",
5086
- "img",
5087
- "input",
5088
- "insertion",
5089
- "landmark",
5090
- "link",
5091
- "list",
5092
- "listbox",
5093
- "listitem",
5094
- "log",
5095
- "main",
5096
- "marquee",
5097
- "math",
5098
- "meter",
5099
- "menu",
5100
- "menubar",
5101
- "menuitem",
5102
- "menuitemcheckbox",
5103
- "menuitemradio",
5104
- "navigation",
5105
- "none",
5106
- "note",
5107
- "option",
5108
- "paragraph",
5109
- "presentation",
5110
- "progressbar",
5111
- "radio",
5112
- "radiogroup",
5113
- "range",
5114
- "region",
5115
- "roletype",
5116
- "row",
5117
- "rowgroup",
5118
- "rowheader",
5119
- "scrollbar",
5120
- "search",
5121
- "searchbox",
5122
- "section",
5123
- "sectionhead",
5124
- "select",
5125
- "separator",
5126
- "slider",
5127
- "spinbutton",
5128
- "status",
5129
- "strong",
5130
- "structure",
5131
- "subscript",
5132
- "superscript",
5133
- "switch",
5134
- "tab",
5135
- "table",
5136
- "tablist",
5137
- "tabpanel",
5138
- "term",
5139
- "textbox",
5140
- "time",
5141
- "timer",
5142
- "toolbar",
5143
- "tooltip",
5144
- "tree",
5145
- "treegrid",
5146
- "treeitem",
5147
- "widget",
5148
- "window"
5149
- ];
5150
- const abstractRoles = [
5151
- "command",
5152
- "composite",
5153
- "input",
5154
- "landmark",
5155
- "range",
5156
- "roletype",
5157
- "section",
5158
- "sectionhead",
5159
- "select",
5160
- "structure",
5161
- "widget",
5162
- "window"
5163
- ];
5164
- const validRoles = allRoles.filter((role) => !abstractRoles.includes(role));
5165
- function getExplicitAriaRole(element) {
5166
- return (element.getAttribute("role") || "").split(" ").map((role) => role.trim()).find((role) => validRoles.includes(role)) || null;
5167
- }
5168
- function hasPresentationConflictResolution(element, role) {
5169
- return hasGlobalAriaAttribute(element, role) || isFocusable(element);
5170
- }
5171
- function getAriaRole(element) {
5172
- const explicitRole = getExplicitAriaRole(element);
5173
- if (!explicitRole) return getImplicitAriaRole(element);
5174
- if (explicitRole === "none" || explicitRole === "presentation") {
5175
- const implicitRole = getImplicitAriaRole(element);
5176
- if (hasPresentationConflictResolution(element, implicitRole)) return implicitRole;
5177
- }
5178
- return explicitRole;
5179
- }
5180
- function getAriaBoolean(attr) {
5181
- return attr === null ? void 0 : attr.toLowerCase() === "true";
5182
- }
5183
- function isElementIgnoredForAria(element) {
5184
- return [
5185
- "STYLE",
5186
- "SCRIPT",
5187
- "NOSCRIPT",
5188
- "TEMPLATE"
5189
- ].includes(elementSafeTagName(element));
5190
- }
5191
- function isElementHiddenForAria(element) {
5192
- if (isElementIgnoredForAria(element)) return true;
5193
- const style = getElementComputedStyle(element);
5194
- const isSlot = element.nodeName === "SLOT";
5195
- if ((style === null || style === void 0 ? void 0 : style.display) === "contents" && !isSlot) {
5196
- for (let child = element.firstChild; child; child = child.nextSibling) {
5197
- if (child.nodeType === 1 && !isElementHiddenForAria(child)) return false;
5198
- if (child.nodeType === 3 && isVisibleTextNode(child)) return false;
5199
- }
5200
- return true;
5201
- }
5202
- if (!(element.nodeName === "OPTION" && !!element.closest("select")) && !isSlot && !isElementStyleVisibilityVisible(element, style)) return true;
5203
- return belongsToDisplayNoneOrAriaHiddenOrNonSlotted(element);
5204
- }
5205
- function belongsToDisplayNoneOrAriaHiddenOrNonSlotted(element) {
5206
- let hidden = cacheIsHidden === null || cacheIsHidden === void 0 ? void 0 : cacheIsHidden.get(element);
5207
- if (hidden === void 0) {
5208
- hidden = false;
5209
- if (element.parentElement && element.parentElement.shadowRoot && !element.assignedSlot) hidden = true;
5210
- if (!hidden) {
5211
- const style = getElementComputedStyle(element);
5212
- hidden = !style || style.display === "none" || getAriaBoolean(element.getAttribute("aria-hidden")) === true;
5213
- }
5214
- if (!hidden) {
5215
- const parent = parentElementOrShadowHost(element);
5216
- if (parent) hidden = belongsToDisplayNoneOrAriaHiddenOrNonSlotted(parent);
5217
- }
5218
- cacheIsHidden === null || cacheIsHidden === void 0 || cacheIsHidden.set(element, hidden);
5219
- }
5220
- return hidden;
5221
- }
5222
- function getIdRefs(element, ref) {
5223
- if (!ref) return [];
5224
- const root = enclosingShadowRootOrDocument(element);
5225
- if (!root) return [];
5226
- try {
5227
- const ids = ref.split(" ").filter((id) => !!id);
5228
- const set = /* @__PURE__ */ new Set();
5229
- for (const id of ids) {
5230
- const firstElement = root.querySelector(`#${CSS.escape(id)}`);
5231
- if (firstElement) set.add(firstElement);
5232
- }
5233
- return [...set];
5234
- } catch (e) {
5235
- return [];
5236
- }
5237
- }
5238
- function trimFlatString(s) {
5239
- return s.trim();
5240
- }
5241
- function asFlatString(s) {
5242
- return s.split("\xA0").map((chunk) => chunk.replace(/\r\n/g, "\n").replace(/[\u200b\u00ad]/g, "").replace(/\s\s*/g, " ")).join("\xA0").trim();
5243
- }
5244
- function queryInAriaOwned(element, selector) {
5245
- const result = [...element.querySelectorAll(selector)];
5246
- for (const owned of getIdRefs(element, element.getAttribute("aria-owns"))) {
5247
- if (owned.matches(selector)) result.push(owned);
5248
- result.push(...owned.querySelectorAll(selector));
5249
- }
5250
- return result;
5251
- }
5252
- function getPseudoContent(element, pseudo) {
5253
- const cache = pseudo === "::before" ? cachePseudoContentBefore : cachePseudoContentAfter;
5254
- if (cache === null || cache === void 0 ? void 0 : cache.has(element)) return (cache === null || cache === void 0 ? void 0 : cache.get(element)) || "";
5255
- const content = getPseudoContentImpl(getElementComputedStyle(element, pseudo));
5256
- if (cache) cache.set(element, content);
5257
- return content;
5258
- }
5259
- function getPseudoContentImpl(pseudoStyle) {
5260
- if (!pseudoStyle) return "";
5261
- const content = pseudoStyle.content;
5262
- if (content[0] === "'" && content[content.length - 1] === "'" || content[0] === "\"" && content[content.length - 1] === "\"") {
5263
- const unquoted = content.substring(1, content.length - 1);
5264
- if ((pseudoStyle.display || "inline") !== "inline") return ` ${unquoted} `;
5265
- return unquoted;
5266
- }
5267
- return "";
5268
- }
5269
- function getAriaLabelledByElements(element) {
5270
- const ref = element.getAttribute("aria-labelledby");
5271
- if (ref === null) return null;
5272
- return getIdRefs(element, ref);
5273
- }
5274
- function allowsNameFromContent(role, targetDescendant) {
5275
- const alwaysAllowsNameFromContent = [
5276
- "button",
5277
- "cell",
5278
- "checkbox",
5279
- "columnheader",
5280
- "gridcell",
5281
- "heading",
5282
- "link",
5283
- "menuitem",
5284
- "menuitemcheckbox",
5285
- "menuitemradio",
5286
- "option",
5287
- "radio",
5288
- "row",
5289
- "rowheader",
5290
- "switch",
5291
- "tab",
5292
- "tooltip",
5293
- "treeitem"
5294
- ].includes(role);
5295
- const descendantAllowsNameFromContent = targetDescendant && [
5296
- "",
5297
- "caption",
5298
- "code",
5299
- "contentinfo",
5300
- "definition",
5301
- "deletion",
5302
- "emphasis",
5303
- "insertion",
5304
- "list",
5305
- "listitem",
5306
- "mark",
5307
- "none",
5308
- "paragraph",
5309
- "presentation",
5310
- "region",
5311
- "row",
5312
- "rowgroup",
5313
- "section",
5314
- "strong",
5315
- "subscript",
5316
- "superscript",
5317
- "table",
5318
- "term",
5319
- "time"
5320
- ].includes(role);
5321
- return alwaysAllowsNameFromContent || descendantAllowsNameFromContent;
5322
- }
5323
- function getElementAccessibleName(element, includeHidden) {
5324
- const cache = includeHidden ? cacheAccessibleNameHidden : cacheAccessibleName;
5325
- let accessibleName = cache === null || cache === void 0 ? void 0 : cache.get(element);
5326
- if (accessibleName === void 0) {
5327
- accessibleName = "";
5328
- if (![
5329
- "caption",
5330
- "code",
5331
- "definition",
5332
- "deletion",
5333
- "emphasis",
5334
- "generic",
5335
- "insertion",
5336
- "mark",
5337
- "paragraph",
5338
- "presentation",
5339
- "strong",
5340
- "subscript",
5341
- "suggestion",
5342
- "superscript",
5343
- "term",
5344
- "time"
5345
- ].includes(getAriaRole(element) || "")) accessibleName = asFlatString(getTextAlternativeInternal(element, {
5346
- includeHidden,
5347
- visitedElements: /* @__PURE__ */ new Set(),
5348
- embeddedInDescribedBy: void 0,
5349
- embeddedInLabelledBy: void 0,
5350
- embeddedInLabel: void 0,
5351
- embeddedInNativeTextAlternative: void 0,
5352
- embeddedInTargetElement: "self"
5353
- }));
5354
- cache === null || cache === void 0 || cache.set(element, accessibleName);
5355
- }
5356
- return accessibleName;
5357
- }
5358
- function getTextAlternativeInternal(element, options) {
5359
- if (options.visitedElements.has(element)) return "";
5360
- const childOptions = {
5361
- ...options,
5362
- embeddedInTargetElement: options.embeddedInTargetElement === "self" ? "descendant" : options.embeddedInTargetElement
5363
- };
5364
- if (!options.includeHidden) {
5365
- var _options$embeddedInLa, _options$embeddedInDe, _options$embeddedInNa, _options$embeddedInLa2;
5366
- const isEmbeddedInHiddenReferenceTraversal = !!((_options$embeddedInLa = options.embeddedInLabelledBy) === null || _options$embeddedInLa === void 0 ? void 0 : _options$embeddedInLa.hidden) || !!((_options$embeddedInDe = options.embeddedInDescribedBy) === null || _options$embeddedInDe === void 0 ? void 0 : _options$embeddedInDe.hidden) || !!((_options$embeddedInNa = options.embeddedInNativeTextAlternative) === null || _options$embeddedInNa === void 0 ? void 0 : _options$embeddedInNa.hidden) || !!((_options$embeddedInLa2 = options.embeddedInLabel) === null || _options$embeddedInLa2 === void 0 ? void 0 : _options$embeddedInLa2.hidden);
5367
- if (isElementIgnoredForAria(element) || !isEmbeddedInHiddenReferenceTraversal && isElementHiddenForAria(element)) {
5368
- options.visitedElements.add(element);
5369
- return "";
5370
- }
5371
- }
5372
- const labelledBy = getAriaLabelledByElements(element);
5373
- if (!options.embeddedInLabelledBy) {
5374
- const accessibleName = (labelledBy || []).map((ref) => getTextAlternativeInternal(ref, {
5375
- ...options,
5376
- embeddedInLabelledBy: {
5377
- element: ref,
5378
- hidden: isElementHiddenForAria(ref)
5379
- },
5380
- embeddedInDescribedBy: void 0,
5381
- embeddedInTargetElement: void 0,
5382
- embeddedInLabel: void 0,
5383
- embeddedInNativeTextAlternative: void 0
5384
- })).join(" ");
5385
- if (accessibleName) return accessibleName;
5386
- }
5387
- const role = getAriaRole(element) || "";
5388
- const tagName = elementSafeTagName(element);
5389
- if (!!options.embeddedInLabel || !!options.embeddedInLabelledBy || options.embeddedInTargetElement === "descendant") {
5390
- const isOwnLabel = [...element.labels || []].includes(element);
5391
- const isOwnLabelledBy = (labelledBy || []).includes(element);
5392
- if (!isOwnLabel && !isOwnLabelledBy) {
5393
- if (role === "textbox") {
5394
- options.visitedElements.add(element);
5395
- if (tagName === "INPUT" || tagName === "TEXTAREA") return element.value;
5396
- return element.textContent || "";
5397
- }
5398
- if (["combobox", "listbox"].includes(role)) {
5399
- options.visitedElements.add(element);
5400
- let selectedOptions;
5401
- if (tagName === "SELECT") {
5402
- selectedOptions = [...element.selectedOptions];
5403
- if (!selectedOptions.length && element.options.length) selectedOptions.push(element.options[0]);
5404
- } else {
5405
- const listbox = role === "combobox" ? queryInAriaOwned(element, "*").find((e) => getAriaRole(e) === "listbox") : element;
5406
- selectedOptions = listbox ? queryInAriaOwned(listbox, "[aria-selected=\"true\"]").filter((e) => getAriaRole(e) === "option") : [];
5407
- }
5408
- if (!selectedOptions.length && tagName === "INPUT") return element.value;
5409
- return selectedOptions.map((option) => getTextAlternativeInternal(option, childOptions)).join(" ");
5410
- }
5411
- if ([
5412
- "progressbar",
5413
- "scrollbar",
5414
- "slider",
5415
- "spinbutton",
5416
- "meter"
5417
- ].includes(role)) {
5418
- options.visitedElements.add(element);
5419
- if (element.hasAttribute("aria-valuetext")) return element.getAttribute("aria-valuetext") || "";
5420
- if (element.hasAttribute("aria-valuenow")) return element.getAttribute("aria-valuenow") || "";
5421
- return element.getAttribute("value") || "";
5422
- }
5423
- if (["menu"].includes(role)) {
5424
- options.visitedElements.add(element);
5425
- return "";
5426
- }
5427
- }
5428
- }
5429
- const ariaLabel = element.getAttribute("aria-label") || "";
5430
- if (trimFlatString(ariaLabel)) {
5431
- options.visitedElements.add(element);
5432
- return ariaLabel;
5433
- }
5434
- if (!["presentation", "none"].includes(role)) {
5435
- if (tagName === "INPUT" && [
5436
- "button",
5437
- "submit",
5438
- "reset"
5439
- ].includes(element.type)) {
5440
- options.visitedElements.add(element);
5441
- const value = element.value || "";
5442
- if (trimFlatString(value)) return value;
5443
- if (element.type === "submit") return "Submit";
5444
- if (element.type === "reset") return "Reset";
5445
- return element.getAttribute("title") || "";
5446
- }
5447
- if (tagName === "INPUT" && element.type === "image") {
5448
- options.visitedElements.add(element);
5449
- const labels = element.labels || [];
5450
- if (labels.length && !options.embeddedInLabelledBy) return getAccessibleNameFromAssociatedLabels(labels, options);
5451
- const alt = element.getAttribute("alt") || "";
5452
- if (trimFlatString(alt)) return alt;
5453
- const title = element.getAttribute("title") || "";
5454
- if (trimFlatString(title)) return title;
5455
- return "Submit";
5456
- }
5457
- if (!labelledBy && tagName === "BUTTON") {
5458
- options.visitedElements.add(element);
5459
- const labels = element.labels || [];
5460
- if (labels.length) return getAccessibleNameFromAssociatedLabels(labels, options);
5461
- }
5462
- if (!labelledBy && tagName === "OUTPUT") {
5463
- options.visitedElements.add(element);
5464
- const labels = element.labels || [];
5465
- if (labels.length) return getAccessibleNameFromAssociatedLabels(labels, options);
5466
- return element.getAttribute("title") || "";
5467
- }
5468
- if (!labelledBy && (tagName === "TEXTAREA" || tagName === "SELECT" || tagName === "INPUT")) {
5469
- options.visitedElements.add(element);
5470
- const labels = element.labels || [];
5471
- if (labels.length) return getAccessibleNameFromAssociatedLabels(labels, options);
5472
- const usePlaceholder = tagName === "INPUT" && [
5473
- "text",
5474
- "password",
5475
- "search",
5476
- "tel",
5477
- "email",
5478
- "url"
5479
- ].includes(element.type) || tagName === "TEXTAREA";
5480
- const placeholder = element.getAttribute("placeholder") || "";
5481
- const title = element.getAttribute("title") || "";
5482
- if (!usePlaceholder || title) return title;
5483
- return placeholder;
5484
- }
5485
- if (!labelledBy && tagName === "FIELDSET") {
5486
- options.visitedElements.add(element);
5487
- for (let child = element.firstElementChild; child; child = child.nextElementSibling) if (elementSafeTagName(child) === "LEGEND") return getTextAlternativeInternal(child, {
5488
- ...childOptions,
5489
- embeddedInNativeTextAlternative: {
5490
- element: child,
5491
- hidden: isElementHiddenForAria(child)
5492
- }
5493
- });
5494
- return element.getAttribute("title") || "";
5495
- }
5496
- if (!labelledBy && tagName === "FIGURE") {
5497
- options.visitedElements.add(element);
5498
- for (let child = element.firstElementChild; child; child = child.nextElementSibling) if (elementSafeTagName(child) === "FIGCAPTION") return getTextAlternativeInternal(child, {
5499
- ...childOptions,
5500
- embeddedInNativeTextAlternative: {
5501
- element: child,
5502
- hidden: isElementHiddenForAria(child)
5503
- }
5504
- });
5505
- return element.getAttribute("title") || "";
5506
- }
5507
- if (tagName === "IMG") {
5508
- options.visitedElements.add(element);
5509
- const alt = element.getAttribute("alt") || "";
5510
- if (trimFlatString(alt)) return alt;
5511
- return element.getAttribute("title") || "";
5512
- }
5513
- if (tagName === "TABLE") {
5514
- options.visitedElements.add(element);
5515
- for (let child = element.firstElementChild; child; child = child.nextElementSibling) if (elementSafeTagName(child) === "CAPTION") return getTextAlternativeInternal(child, {
5516
- ...childOptions,
5517
- embeddedInNativeTextAlternative: {
5518
- element: child,
5519
- hidden: isElementHiddenForAria(child)
5520
- }
5521
- });
5522
- const summary = element.getAttribute("summary") || "";
5523
- if (summary) return summary;
5524
- }
5525
- if (tagName === "AREA") {
5526
- options.visitedElements.add(element);
5527
- const alt = element.getAttribute("alt") || "";
5528
- if (trimFlatString(alt)) return alt;
5529
- return element.getAttribute("title") || "";
5530
- }
5531
- if (tagName === "SVG" || element.ownerSVGElement) {
5532
- options.visitedElements.add(element);
5533
- for (let child = element.firstElementChild; child; child = child.nextElementSibling) if (elementSafeTagName(child) === "TITLE" && child.ownerSVGElement) return getTextAlternativeInternal(child, {
5534
- ...childOptions,
5535
- embeddedInLabelledBy: {
5536
- element: child,
5537
- hidden: isElementHiddenForAria(child)
5538
- }
5539
- });
5540
- }
5541
- if (element.ownerSVGElement && tagName === "A") {
5542
- const title = element.getAttribute("xlink:title") || "";
5543
- if (trimFlatString(title)) {
5544
- options.visitedElements.add(element);
5545
- return title;
5546
- }
5547
- }
5548
- }
5549
- const shouldNameFromContentForSummary = tagName === "SUMMARY" && !["presentation", "none"].includes(role);
5550
- if (allowsNameFromContent(role, options.embeddedInTargetElement === "descendant") || shouldNameFromContentForSummary || !!options.embeddedInLabelledBy || !!options.embeddedInDescribedBy || !!options.embeddedInLabel || !!options.embeddedInNativeTextAlternative) {
5551
- options.visitedElements.add(element);
5552
- const accessibleName = innerAccumulatedElementText(element, childOptions);
5553
- if (options.embeddedInTargetElement === "self" ? trimFlatString(accessibleName) : accessibleName) return accessibleName;
5554
- }
5555
- if (!["presentation", "none"].includes(role) || tagName === "IFRAME") {
5556
- options.visitedElements.add(element);
5557
- const title = element.getAttribute("title") || "";
5558
- if (trimFlatString(title)) return title;
5559
- }
5560
- options.visitedElements.add(element);
5561
- return "";
5562
- }
5563
- function innerAccumulatedElementText(element, options) {
5564
- const tokens = [];
5565
- const visit = (node, skipSlotted) => {
5566
- if (skipSlotted && node.assignedSlot) return;
5567
- if (node.nodeType === 1) {
5568
- var _getElementComputedSt;
5569
- const display = ((_getElementComputedSt = getElementComputedStyle(node)) === null || _getElementComputedSt === void 0 ? void 0 : _getElementComputedSt.display) || "inline";
5570
- let token = getTextAlternativeInternal(node, options);
5571
- if (display !== "inline" || node.nodeName === "BR") token = " " + token + " ";
5572
- tokens.push(token);
5573
- } else if (node.nodeType === 3) tokens.push(node.textContent || "");
5574
- };
5575
- tokens.push(getPseudoContent(element, "::before"));
5576
- const assignedNodes = element.nodeName === "SLOT" ? element.assignedNodes() : [];
5577
- if (assignedNodes.length) for (const child of assignedNodes) visit(child, false);
5578
- else {
5579
- for (let child = element.firstChild; child; child = child.nextSibling) visit(child, true);
5580
- if (element.shadowRoot) for (let child = element.shadowRoot.firstChild; child; child = child.nextSibling) visit(child, true);
5581
- for (const owned of getIdRefs(element, element.getAttribute("aria-owns"))) visit(owned, true);
5582
- }
5583
- tokens.push(getPseudoContent(element, "::after"));
5584
- return tokens.join("");
5585
- }
5586
- const kAriaSelectedRoles = [
5587
- "gridcell",
5588
- "option",
5589
- "row",
5590
- "tab",
5591
- "rowheader",
5592
- "columnheader",
5593
- "treeitem"
5594
- ];
5595
- function getAriaSelected(element) {
5596
- if (elementSafeTagName(element) === "OPTION") return element.selected;
5597
- if (kAriaSelectedRoles.includes(getAriaRole(element) || "")) return getAriaBoolean(element.getAttribute("aria-selected")) === true;
5598
- return false;
5599
- }
5600
- const kAriaCheckedRoles = [
5601
- "checkbox",
5602
- "menuitemcheckbox",
5603
- "option",
5604
- "radio",
5605
- "switch",
5606
- "menuitemradio",
5607
- "treeitem"
5608
- ];
5609
- function getAriaChecked(element) {
5610
- const result = getChecked(element);
5611
- return result === "error" ? false : result;
5612
- }
5613
- function getChecked(element, allowMixed) {
5614
- const tagName = elementSafeTagName(element);
5615
- if (tagName === "INPUT" && element.indeterminate) return "mixed";
5616
- if (tagName === "INPUT" && ["checkbox", "radio"].includes(element.type)) return element.checked;
5617
- if (kAriaCheckedRoles.includes(getAriaRole(element) || "")) {
5618
- const checked = element.getAttribute("aria-checked");
5619
- if (checked === "true") return true;
5620
- if (checked === "mixed") return "mixed";
5621
- return false;
5622
- }
5623
- return "error";
5624
- }
5625
- const kAriaPressedRoles = ["button"];
5626
- function getAriaPressed(element) {
5627
- if (kAriaPressedRoles.includes(getAriaRole(element) || "")) {
5628
- const pressed = element.getAttribute("aria-pressed");
5629
- if (pressed === "true") return true;
5630
- if (pressed === "mixed") return "mixed";
5631
- }
5632
- return false;
5633
- }
5634
- const kAriaExpandedRoles = [
5635
- "application",
5636
- "button",
5637
- "checkbox",
5638
- "combobox",
5639
- "gridcell",
5640
- "link",
5641
- "listbox",
5642
- "menuitem",
5643
- "row",
5644
- "rowheader",
5645
- "tab",
5646
- "treeitem",
5647
- "columnheader",
5648
- "menuitemcheckbox",
5649
- "menuitemradio",
5650
- "rowheader",
5651
- "switch"
5652
- ];
5653
- function getAriaExpanded(element) {
5654
- if (elementSafeTagName(element) === "DETAILS") return element.open;
5655
- if (kAriaExpandedRoles.includes(getAriaRole(element) || "")) {
5656
- const expanded = element.getAttribute("aria-expanded");
5657
- if (expanded === null) return "none";
5658
- if (expanded === "true") return true;
5659
- return false;
5660
- }
5661
- return "none";
5662
- }
5663
- const kAriaLevelRoles = [
5664
- "heading",
5665
- "listitem",
5666
- "row",
5667
- "treeitem"
5668
- ];
5669
- function getAriaLevel(element) {
5670
- const native = {
5671
- H1: 1,
5672
- H2: 2,
5673
- H3: 3,
5674
- H4: 4,
5675
- H5: 5,
5676
- H6: 6
5677
- }[elementSafeTagName(element)];
5678
- if (native) return native;
5679
- if (kAriaLevelRoles.includes(getAriaRole(element) || "")) {
5680
- const attr = element.getAttribute("aria-level");
5681
- const value = attr === null ? NaN : Number(attr);
5682
- if (Number.isInteger(value) && value >= 1) return value;
5683
- }
5684
- return 0;
5685
- }
5686
- const kAriaDisabledRoles = [
5687
- "application",
5688
- "button",
5689
- "composite",
5690
- "gridcell",
5691
- "group",
5692
- "input",
5693
- "link",
5694
- "menuitem",
5695
- "scrollbar",
5696
- "separator",
5697
- "tab",
5698
- "checkbox",
5699
- "columnheader",
5700
- "combobox",
5701
- "grid",
5702
- "listbox",
5703
- "menu",
5704
- "menubar",
5705
- "menuitemcheckbox",
5706
- "menuitemradio",
5707
- "option",
5708
- "radio",
5709
- "radiogroup",
5710
- "row",
5711
- "rowheader",
5712
- "searchbox",
5713
- "select",
5714
- "slider",
5715
- "spinbutton",
5716
- "switch",
5717
- "tablist",
5718
- "textbox",
5719
- "toolbar",
5720
- "tree",
5721
- "treegrid",
5722
- "treeitem"
5723
- ];
5724
- function getAriaDisabled(element) {
5725
- return isNativelyDisabled(element) || hasExplicitAriaDisabled(element);
5726
- }
5727
- function isNativelyDisabled(element) {
5728
- return [
5729
- "BUTTON",
5730
- "INPUT",
5731
- "SELECT",
5732
- "TEXTAREA",
5733
- "OPTION",
5734
- "OPTGROUP"
5735
- ].includes(element.tagName) && (element.hasAttribute("disabled") || belongsToDisabledFieldSet(element));
5736
- }
5737
- function belongsToDisabledFieldSet(element) {
5738
- const fieldSetElement = element === null || element === void 0 ? void 0 : element.closest("FIELDSET[DISABLED]");
5739
- if (!fieldSetElement) return false;
5740
- const legendElement = fieldSetElement.querySelector(":scope > LEGEND");
5741
- return !legendElement || !legendElement.contains(element);
5742
- }
5743
- function hasExplicitAriaDisabled(element) {
5744
- if (!element) return false;
5745
- if (kAriaDisabledRoles.includes(getAriaRole(element) || "")) {
5746
- const attribute = (element.getAttribute("aria-disabled") || "").toLowerCase();
5747
- if (attribute === "true") return true;
5748
- if (attribute === "false") return false;
5749
- }
5750
- return hasExplicitAriaDisabled(parentElementOrShadowHost(element));
5751
- }
5752
- function getAccessibleNameFromAssociatedLabels(labels, options) {
5753
- return [...labels].map((label) => getTextAlternativeInternal(label, {
5754
- ...options,
5755
- embeddedInLabel: {
5756
- element: label,
5757
- hidden: isElementHiddenForAria(label)
5758
- },
5759
- embeddedInNativeTextAlternative: void 0,
5760
- embeddedInLabelledBy: void 0,
5761
- embeddedInDescribedBy: void 0,
5762
- embeddedInTargetElement: void 0
5763
- })).filter((accessibleName) => !!accessibleName).join(" ");
5764
- }
5765
- let cacheAccessibleName;
5766
- let cacheAccessibleNameHidden;
5767
- let cacheIsHidden;
5768
- let cachePseudoContentBefore;
5769
- let cachePseudoContentAfter;
5770
- let cachesCounter = 0;
5771
- function beginAriaCaches() {
5772
- var _cacheAccessibleName, _cacheAccessibleNameH, _cacheIsHidden, _cachePseudoContentBe, _cachePseudoContentAf;
5773
- ++cachesCounter;
5774
- (_cacheAccessibleName = cacheAccessibleName) !== null && _cacheAccessibleName !== void 0 || (cacheAccessibleName = /* @__PURE__ */ new Map());
5775
- (_cacheAccessibleNameH = cacheAccessibleNameHidden) !== null && _cacheAccessibleNameH !== void 0 || (cacheAccessibleNameHidden = /* @__PURE__ */ new Map());
5776
- (_cacheIsHidden = cacheIsHidden) !== null && _cacheIsHidden !== void 0 || (cacheIsHidden = /* @__PURE__ */ new Map());
5777
- (_cachePseudoContentBe = cachePseudoContentBefore) !== null && _cachePseudoContentBe !== void 0 || (cachePseudoContentBefore = /* @__PURE__ */ new Map());
5778
- (_cachePseudoContentAf = cachePseudoContentAfter) !== null && _cachePseudoContentAf !== void 0 || (cachePseudoContentAfter = /* @__PURE__ */ new Map());
5779
- }
5780
- function endAriaCaches() {
5781
- if (!--cachesCounter) {
5782
- cacheAccessibleName = void 0;
5783
- cacheAccessibleNameHidden = void 0;
5784
- cacheIsHidden = void 0;
5785
- cachePseudoContentBefore = void 0;
5786
- cachePseudoContentAfter = void 0;
5787
- }
5788
- }
5789
-
5790
- //#endregion
5791
- //#region src/selectorUtils.ts
5792
- function matchesAttributePart(value, attr) {
5793
- const objValue = typeof value === "string" && !attr.caseSensitive ? value.toUpperCase() : value;
5794
- const attrValue = typeof attr.value === "string" && !attr.caseSensitive ? attr.value.toUpperCase() : attr.value;
5795
- if (attr.op === "<truthy>") return !!objValue;
5796
- if (attr.op === "=") {
5797
- if (attrValue instanceof RegExp) return typeof objValue === "string" && !!objValue.match(attrValue);
5798
- return objValue === attrValue;
5799
- }
5800
- if (typeof objValue !== "string" || typeof attrValue !== "string") return false;
5801
- if (attr.op === "*=") return objValue.includes(attrValue);
5802
- if (attr.op === "^=") return objValue.startsWith(attrValue);
5803
- if (attr.op === "$=") return objValue.endsWith(attrValue);
5804
- if (attr.op === "|=") return objValue === attrValue || objValue.startsWith(`${attrValue}-`);
5805
- if (attr.op === "~=") return objValue.split(" ").includes(attrValue);
5806
- return false;
5807
- }
5808
- function shouldSkipForTextMatching(element) {
5809
- const document = element.ownerDocument;
5810
- return element.nodeName === "SCRIPT" || element.nodeName === "NOSCRIPT" || element.nodeName === "STYLE" || document.head && document.head.contains(element);
5811
- }
5812
- function elementText(cache, root) {
5813
- let value = cache.get(root);
5814
- if (value === void 0) {
5815
- value = {
5816
- full: "",
5817
- normalized: "",
5818
- immediate: []
5819
- };
5820
- if (!shouldSkipForTextMatching(root)) {
5821
- let currentImmediate = "";
5822
- if (root instanceof HTMLInputElement && (root.type === "submit" || root.type === "button")) value = {
5823
- full: root.value,
5824
- normalized: normalizeWhiteSpace(root.value),
5825
- immediate: [root.value]
5826
- };
5827
- else {
5828
- for (let child = root.firstChild; child; child = child.nextSibling) if (child.nodeType === Node.TEXT_NODE) {
5829
- value.full += child.nodeValue || "";
5830
- currentImmediate += child.nodeValue || "";
5831
- } else {
5832
- if (currentImmediate) value.immediate.push(currentImmediate);
5833
- currentImmediate = "";
5834
- if (child.nodeType === Node.ELEMENT_NODE) value.full += elementText(cache, child).full;
5835
- }
5836
- if (currentImmediate) value.immediate.push(currentImmediate);
5837
- if (root.shadowRoot) value.full += elementText(cache, root.shadowRoot).full;
5838
- if (value.full) value.normalized = normalizeWhiteSpace(value.full);
5839
- }
5840
- }
5841
- cache.set(root, value);
5842
- }
5843
- return value;
5844
- }
5845
- function elementMatchesText(cache, element, matcher) {
5846
- if (shouldSkipForTextMatching(element)) return "none";
5847
- if (!matcher(elementText(cache, element))) return "none";
5848
- for (let child = element.firstChild; child; child = child.nextSibling) if (child.nodeType === Node.ELEMENT_NODE && matcher(elementText(cache, child))) return "selfAndChildren";
5849
- if (element.shadowRoot && matcher(elementText(cache, element.shadowRoot))) return "selfAndChildren";
5850
- return "self";
5851
- }
5852
- function getElementLabels(textCache, element) {
5853
- const labels = getAriaLabelledByElements(element);
5854
- if (labels) return labels.map((label) => elementText(textCache, label));
5855
- const ariaLabel = element.getAttribute("aria-label");
5856
- if (ariaLabel !== null && !!ariaLabel.trim()) return [{
5857
- full: ariaLabel,
5858
- normalized: normalizeWhiteSpace(ariaLabel),
5859
- immediate: [ariaLabel]
5860
- }];
5861
- const isNonHiddenInput = element.nodeName === "INPUT" && element.type !== "hidden";
5862
- if ([
5863
- "BUTTON",
5864
- "METER",
5865
- "OUTPUT",
5866
- "PROGRESS",
5867
- "SELECT",
5868
- "TEXTAREA"
5869
- ].includes(element.nodeName) || isNonHiddenInput) {
5870
- const labels = element.labels;
5871
- if (labels) return [...labels].map((label) => elementText(textCache, label));
5872
- }
5873
- return [];
5874
- }
5875
-
5876
- //#endregion
5877
- //#region src/roleSelectorEngine.ts
5878
- /**
5879
- * Copyright (c) Microsoft Corporation.
5880
- *
5881
- * Licensed under the Apache License, Version 2.0 (the "License");
5882
- * you may not use this file except in compliance with the License.
5883
- * You may obtain a copy of the License at
5884
- *
5885
- * http://www.apache.org/licenses/LICENSE-2.0
5886
- *
5887
- * Unless required by applicable law or agreed to in writing, software
5888
- * distributed under the License is distributed on an "AS IS" BASIS,
5889
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
5890
- * See the License for the specific language governing permissions and
5891
- * limitations under the License.
5892
- */
5893
- const kSupportedAttributes = [
5894
- "selected",
5895
- "checked",
5896
- "pressed",
5897
- "expanded",
5898
- "level",
5899
- "disabled",
5900
- "name",
5901
- "include-hidden"
5902
- ];
5903
- kSupportedAttributes.sort();
5904
- function validateSupportedRole(attr, roles, role) {
5905
- if (!roles.includes(role)) throw new Error(`"${attr}" attribute is only supported for roles: ${roles.slice().sort().map((role) => `"${role}"`).join(", ")}`);
5906
- }
5907
- function validateSupportedValues(attr, values) {
5908
- if (attr.op !== "<truthy>" && !values.includes(attr.value)) throw new Error(`"${attr.name}" must be one of ${values.map((v) => JSON.stringify(v)).join(", ")}`);
5909
- }
5910
- function validateSupportedOp(attr, ops) {
5911
- if (!ops.includes(attr.op)) throw new Error(`"${attr.name}" does not support "${attr.op}" matcher`);
5912
- }
5913
- function validateAttributes(attrs, role) {
5914
- const options = { role };
5915
- for (const attr of attrs) switch (attr.name) {
5916
- case "checked":
5917
- validateSupportedRole(attr.name, kAriaCheckedRoles, role);
5918
- validateSupportedValues(attr, [
5919
- true,
5920
- false,
5921
- "mixed"
5922
- ]);
5923
- validateSupportedOp(attr, ["<truthy>", "="]);
5924
- options.checked = attr.op === "<truthy>" ? true : attr.value;
5925
- break;
5926
- case "pressed":
5927
- validateSupportedRole(attr.name, kAriaPressedRoles, role);
5928
- validateSupportedValues(attr, [
5929
- true,
5930
- false,
5931
- "mixed"
5932
- ]);
5933
- validateSupportedOp(attr, ["<truthy>", "="]);
5934
- options.pressed = attr.op === "<truthy>" ? true : attr.value;
5935
- break;
5936
- case "selected":
5937
- validateSupportedRole(attr.name, kAriaSelectedRoles, role);
5938
- validateSupportedValues(attr, [true, false]);
5939
- validateSupportedOp(attr, ["<truthy>", "="]);
5940
- options.selected = attr.op === "<truthy>" ? true : attr.value;
5941
- break;
5942
- case "expanded":
5943
- validateSupportedRole(attr.name, kAriaExpandedRoles, role);
5944
- validateSupportedValues(attr, [true, false]);
5945
- validateSupportedOp(attr, ["<truthy>", "="]);
5946
- options.expanded = attr.op === "<truthy>" ? true : attr.value;
5947
- break;
5948
- case "level":
5949
- validateSupportedRole(attr.name, kAriaLevelRoles, role);
5950
- if (typeof attr.value === "string") attr.value = +attr.value;
5951
- if (attr.op !== "=" || typeof attr.value !== "number" || Number.isNaN(attr.value)) throw new Error(`"level" attribute must be compared to a number`);
5952
- options.level = attr.value;
5953
- break;
5954
- case "disabled":
5955
- validateSupportedValues(attr, [true, false]);
5956
- validateSupportedOp(attr, ["<truthy>", "="]);
5957
- options.disabled = attr.op === "<truthy>" ? true : attr.value;
5958
- break;
5959
- case "name":
5960
- if (attr.op === "<truthy>") throw new Error(`"name" attribute must have a value`);
5961
- if (typeof attr.value !== "string" && !(attr.value instanceof RegExp)) throw new TypeError(`"name" attribute must be a string or a regular expression`);
5962
- options.name = attr.value;
5963
- options.nameOp = attr.op;
5964
- options.exact = attr.caseSensitive;
5965
- break;
5966
- case "include-hidden":
5967
- validateSupportedValues(attr, [true, false]);
5968
- validateSupportedOp(attr, ["<truthy>", "="]);
5969
- options.includeHidden = attr.op === "<truthy>" ? true : attr.value;
5970
- break;
5971
- default: throw new Error(`Unknown attribute "${attr.name}", must be one of ${kSupportedAttributes.map((a) => `"${a}"`).join(", ")}.`);
5972
- }
5973
- return options;
5974
- }
5975
- function queryRole(scope, options, internal) {
5976
- const result = [];
5977
- const match = (element) => {
5978
- if (getAriaRole(element) !== options.role) return;
5979
- if (options.selected !== void 0 && getAriaSelected(element) !== options.selected) return;
5980
- if (options.checked !== void 0 && getAriaChecked(element) !== options.checked) return;
5981
- if (options.pressed !== void 0 && getAriaPressed(element) !== options.pressed) return;
5982
- if (options.expanded !== void 0 && getAriaExpanded(element) !== options.expanded) return;
5983
- if (options.level !== void 0 && getAriaLevel(element) !== options.level) return;
5984
- if (options.disabled !== void 0 && getAriaDisabled(element) !== options.disabled) return;
5985
- if (!options.includeHidden) {
5986
- if (isElementHiddenForAria(element)) return;
5987
- }
5988
- if (options.name !== void 0) {
5989
- const accessibleName = normalizeWhiteSpace(getElementAccessibleName(element, !!options.includeHidden));
5990
- if (typeof options.name === "string") options.name = normalizeWhiteSpace(options.name);
5991
- if (internal && !options.exact && options.nameOp === "=") options.nameOp = "*=";
5992
- if (!matchesAttributePart(accessibleName, {
5993
- op: options.nameOp || "=",
5994
- value: options.name,
5995
- caseSensitive: !!options.exact
5996
- })) return;
5997
- }
5998
- result.push(element);
5999
- };
6000
- const query = (root) => {
6001
- const shadows = [];
6002
- if (root.shadowRoot) shadows.push(root.shadowRoot);
6003
- for (const element of root.querySelectorAll("*")) {
6004
- match(element);
6005
- if (element.shadowRoot) shadows.push(element.shadowRoot);
6006
- }
6007
- shadows.forEach(query);
6008
- };
6009
- query(scope);
6010
- return result;
6011
- }
6012
- function createRoleEngine(internal) {
6013
- return { queryAll: (scope, selector) => {
6014
- const parsed = parseAttributeSelector(selector);
6015
- const role = parsed.name.toLowerCase();
6016
- if (!role) throw new Error(`Role must not be empty`);
6017
- const options = validateAttributes(parsed.attributes, role);
6018
- beginAriaCaches();
6019
- try {
6020
- return queryRole(scope, options, internal);
6021
- } finally {
6022
- endAriaCaches();
6023
- }
6024
- } };
6025
- }
6026
-
6027
- //#endregion
6028
- //#region src/locatorGenerators.ts
6029
- /**
6030
- * Copyright (c) Microsoft Corporation.
6031
- *
6032
- * Licensed under the Apache License, Version 2.0 (the "License");
6033
- * you may not use this file except in compliance with the License.
6034
- * You may obtain a copy of the License at
6035
- *
6036
- * http://www.apache.org/licenses/LICENSE-2.0
6037
- *
6038
- * Unless required by applicable law or agreed to in writing, software
6039
- * distributed under the License is distributed on an "AS IS" BASIS,
6040
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
6041
- * See the License for the specific language governing permissions and
6042
- * limitations under the License.
6043
- */
6044
- function asLocator(lang, selector, isFrameLocator = false) {
6045
- return asLocators(lang, selector, isFrameLocator)[0];
6046
- }
6047
- function asLocators(lang, selector, isFrameLocator = false, maxOutputSize = 20, preferredQuote) {
6048
- try {
6049
- return innerAsLocators(new generators[lang](preferredQuote), parseSelector(selector), isFrameLocator, maxOutputSize);
6050
- } catch (e) {
6051
- return [selector];
6052
- }
6053
- }
6054
- function innerAsLocators(factory, parsed, isFrameLocator = false, maxOutputSize = 20) {
6055
- const parts = [...parsed.parts];
6056
- for (let index = 0; index < parts.length - 1; index++) if (parts[index].name === "nth" && parts[index + 1].name === "internal:control" && parts[index + 1].body === "enter-frame") {
6057
- const [nth] = parts.splice(index, 1);
6058
- parts.splice(index + 1, 0, nth);
6059
- }
6060
- const tokens = [];
6061
- let nextBase = isFrameLocator ? "frame-locator" : "page";
6062
- for (let index = 0; index < parts.length; index++) {
6063
- const part = parts[index];
6064
- const base = nextBase;
6065
- nextBase = "locator";
6066
- if (part.name === "nth") {
6067
- if (part.body === "0") tokens.push([factory.generateLocator(base, "first", ""), factory.generateLocator(base, "nth", "0")]);
6068
- else if (part.body === "-1") tokens.push([factory.generateLocator(base, "last", ""), factory.generateLocator(base, "nth", "-1")]);
6069
- else tokens.push([factory.generateLocator(base, "nth", part.body)]);
6070
- continue;
6071
- }
6072
- if (part.name === "internal:text") {
6073
- const { exact, text } = detectExact(part.body);
6074
- tokens.push([factory.generateLocator(base, "text", text, { exact })]);
6075
- continue;
6076
- }
6077
- if (part.name === "internal:has-text") {
6078
- const { exact, text } = detectExact(part.body);
6079
- if (!exact) {
6080
- tokens.push([factory.generateLocator(base, "has-text", text, { exact })]);
6081
- continue;
6082
- }
6083
- }
6084
- if (part.name === "internal:has-not-text") {
6085
- const { exact, text } = detectExact(part.body);
6086
- if (!exact) {
6087
- tokens.push([factory.generateLocator(base, "has-not-text", text, { exact })]);
6088
- continue;
6089
- }
6090
- }
6091
- if (part.name === "internal:has") {
6092
- const inners = innerAsLocators(factory, part.body.parsed, false, maxOutputSize);
6093
- tokens.push(inners.map((inner) => factory.generateLocator(base, "has", inner)));
6094
- continue;
6095
- }
6096
- if (part.name === "internal:has-not") {
6097
- const inners = innerAsLocators(factory, part.body.parsed, false, maxOutputSize);
6098
- tokens.push(inners.map((inner) => factory.generateLocator(base, "hasNot", inner)));
6099
- continue;
6100
- }
6101
- if (part.name === "internal:and") {
6102
- const inners = innerAsLocators(factory, part.body.parsed, false, maxOutputSize);
6103
- tokens.push(inners.map((inner) => factory.generateLocator(base, "and", inner)));
6104
- continue;
6105
- }
6106
- if (part.name === "internal:or") {
6107
- const inners = innerAsLocators(factory, part.body.parsed, false, maxOutputSize);
6108
- tokens.push(inners.map((inner) => factory.generateLocator(base, "or", inner)));
6109
- continue;
6110
- }
6111
- if (part.name === "internal:chain") {
6112
- const inners = innerAsLocators(factory, part.body.parsed, false, maxOutputSize);
6113
- tokens.push(inners.map((inner) => factory.generateLocator(base, "chain", inner)));
6114
- continue;
6115
- }
6116
- if (part.name === "internal:label") {
6117
- const { exact, text } = detectExact(part.body);
6118
- tokens.push([factory.generateLocator(base, "label", text, { exact })]);
6119
- continue;
6120
- }
6121
- if (part.name === "internal:role") {
6122
- const attrSelector = parseAttributeSelector(part.body);
6123
- const options = { attrs: [] };
6124
- for (const attr of attrSelector.attributes) if (attr.name === "name") {
6125
- options.exact = attr.caseSensitive;
6126
- options.name = attr.value;
6127
- } else {
6128
- if (attr.name === "level" && typeof attr.value === "string") attr.value = +attr.value;
6129
- options.attrs.push({
6130
- name: attr.name === "include-hidden" ? "includeHidden" : attr.name,
6131
- value: attr.value
6132
- });
6133
- }
6134
- tokens.push([factory.generateLocator(base, "role", attrSelector.name, options)]);
6135
- continue;
6136
- }
6137
- if (part.name === "internal:testid") {
6138
- const { value } = parseAttributeSelector(part.body).attributes[0];
6139
- tokens.push([factory.generateLocator(base, "test-id", value)]);
6140
- continue;
6141
- }
6142
- if (part.name === "internal:attr") {
6143
- const { name, value, caseSensitive } = parseAttributeSelector(part.body).attributes[0];
6144
- const text = value;
6145
- const exact = !!caseSensitive;
6146
- if (name === "placeholder") {
6147
- tokens.push([factory.generateLocator(base, "placeholder", text, { exact })]);
6148
- continue;
6149
- }
6150
- if (name === "alt") {
6151
- tokens.push([factory.generateLocator(base, "alt", text, { exact })]);
6152
- continue;
6153
- }
6154
- if (name === "title") {
6155
- tokens.push([factory.generateLocator(base, "title", text, { exact })]);
6156
- continue;
6157
- }
6158
- }
6159
- let locatorType = "default";
6160
- const nextPart = parts[index + 1];
6161
- if (nextPart && nextPart.name === "internal:control" && nextPart.body === "enter-frame") {
6162
- locatorType = "frame";
6163
- nextBase = "frame-locator";
6164
- index++;
6165
- }
6166
- const selectorPart = stringifySelector({ parts: [part] });
6167
- const locatorPart = factory.generateLocator(base, locatorType, selectorPart);
6168
- if (locatorType === "default" && nextPart && ["internal:has-text", "internal:has-not-text"].includes(nextPart.name)) {
6169
- const { exact, text } = detectExact(nextPart.body);
6170
- if (!exact) {
6171
- const nextLocatorPart = factory.generateLocator("locator", nextPart.name === "internal:has-text" ? "has-text" : "has-not-text", text, { exact });
6172
- const options = {};
6173
- if (nextPart.name === "internal:has-text") options.hasText = text;
6174
- else options.hasNotText = text;
6175
- const combinedPart = factory.generateLocator(base, "default", selectorPart, options);
6176
- tokens.push([factory.chainLocators([locatorPart, nextLocatorPart]), combinedPart]);
6177
- index++;
6178
- continue;
6179
- }
6180
- }
6181
- let locatorPartWithEngine;
6182
- if (["xpath", "css"].includes(part.name)) {
6183
- const selectorPart = stringifySelector({ parts: [part] }, true);
6184
- locatorPartWithEngine = factory.generateLocator(base, locatorType, selectorPart);
6185
- }
6186
- tokens.push([locatorPart, locatorPartWithEngine].filter(Boolean));
6187
- }
6188
- return combineTokens(factory, tokens, maxOutputSize);
6189
- }
6190
- function combineTokens(factory, tokens, maxOutputSize) {
6191
- const currentTokens = tokens.map(() => "");
6192
- const result = [];
6193
- const visit = (index) => {
6194
- if (index === tokens.length) {
6195
- result.push(factory.chainLocators(currentTokens));
6196
- return currentTokens.length < maxOutputSize;
6197
- }
6198
- for (const taken of tokens[index]) {
6199
- currentTokens[index] = taken;
6200
- if (!visit(index + 1)) return false;
6201
- }
6202
- return true;
6203
- };
6204
- visit(0);
6205
- return result;
6206
- }
6207
- function detectExact(text) {
6208
- let exact = false;
6209
- const match = text.match(/^\/(.*)\/([igm]*)$/);
6210
- if (match) return { text: new RegExp(match[1], match[2]) };
6211
- if (text.endsWith("\"")) {
6212
- text = JSON.parse(text);
6213
- exact = true;
6214
- } else if (text.endsWith("\"s")) {
6215
- text = JSON.parse(text.substring(0, text.length - 1));
6216
- exact = true;
6217
- } else if (text.endsWith("\"i")) {
6218
- text = JSON.parse(text.substring(0, text.length - 1));
6219
- exact = false;
6220
- }
6221
- return {
6222
- exact,
6223
- text
6224
- };
6225
- }
6226
- var JavaScriptLocatorFactory = class {
6227
- constructor(preferredQuote) {
6228
- this.preferredQuote = preferredQuote;
6229
- }
6230
- generateLocator(base, kind, body, options = {}) {
6231
- switch (kind) {
6232
- case "default":
6233
- if (options.hasText !== void 0) return `locator(${this.quote(body)}, { hasText: ${this.toHasText(options.hasText)} })`;
6234
- if (options.hasNotText !== void 0) return `locator(${this.quote(body)}, { hasNotText: ${this.toHasText(options.hasNotText)} })`;
6235
- return `locator(${this.quote(body)})`;
6236
- case "frame": return `frameLocator(${this.quote(body)})`;
6237
- case "nth": return `nth(${body})`;
6238
- case "first": return `first()`;
6239
- case "last": return `last()`;
6240
- case "role": {
6241
- const attrs = [];
6242
- if (isRegExp(options.name)) attrs.push(`name: ${this.regexToSourceString(options.name)}`);
6243
- else if (typeof options.name === "string") {
6244
- attrs.push(`name: ${this.quote(options.name)}`);
6245
- if (options.exact) attrs.push(`exact: true`);
6246
- }
6247
- for (const { name, value } of options.attrs) attrs.push(`${name}: ${typeof value === "string" ? this.quote(value) : value}`);
6248
- const attrString = attrs.length ? `, { ${attrs.join(", ")} }` : "";
6249
- return `getByRole(${this.quote(body)}${attrString})`;
6250
- }
6251
- case "has-text": return `filter({ hasText: ${this.toHasText(body)} })`;
6252
- case "has-not-text": return `filter({ hasNotText: ${this.toHasText(body)} })`;
6253
- case "has": return `filter({ has: ${body} })`;
6254
- case "hasNot": return `filter({ hasNot: ${body} })`;
6255
- case "and": return `and(${body})`;
6256
- case "or": return `or(${body})`;
6257
- case "chain": return `locator(${body})`;
6258
- case "test-id": {
6259
- const value = this.toTestIdValue(body);
6260
- if (value.startsWith("'__vitest_") && value.endsWith("__'")) return "page";
6261
- return `getByTestId(${value})`;
6262
- }
6263
- case "text": return this.toCallWithExact("getByText", body, !!options.exact);
6264
- case "alt": return this.toCallWithExact("getByAltText", body, !!options.exact);
6265
- case "placeholder": return this.toCallWithExact("getByPlaceholder", body, !!options.exact);
6266
- case "label": return this.toCallWithExact("getByLabel", body, !!options.exact);
6267
- case "title": return this.toCallWithExact("getByTitle", body, !!options.exact);
6268
- default: throw new Error(`Unknown selector kind ${kind}`);
6269
- }
6270
- }
6271
- chainLocators(locators) {
6272
- return locators.join(".");
6273
- }
6274
- regexToSourceString(re) {
6275
- return normalizeEscapedRegexQuotes(String(re));
6276
- }
6277
- toCallWithExact(method, body, exact) {
6278
- if (isRegExp(body)) return `${method}(${this.regexToSourceString(body)})`;
6279
- return exact ? `${method}(${this.quote(body)}, { exact: true })` : `${method}(${this.quote(body)})`;
6280
- }
6281
- toHasText(body) {
6282
- if (isRegExp(body)) return this.regexToSourceString(body);
6283
- return this.quote(body);
6284
- }
6285
- toTestIdValue(value) {
6286
- if (isRegExp(value)) return this.regexToSourceString(value);
6287
- return this.quote(value);
6288
- }
6289
- quote(text) {
6290
- var _this$preferredQuote;
6291
- return escapeWithQuotes(text, (_this$preferredQuote = this.preferredQuote) !== null && _this$preferredQuote !== void 0 ? _this$preferredQuote : "'");
6292
- }
6293
- };
6294
- const generators = { javascript: JavaScriptLocatorFactory };
6295
- function isRegExp(obj) {
6296
- return obj instanceof RegExp;
6297
- }
6298
-
6299
- //#endregion
6300
- //#region src/selectorGenerator.ts
6301
- /**
6302
- * Copyright (c) Microsoft Corporation.
6303
- *
6304
- * Licensed under the Apache License, Version 2.0 (the "License");
6305
- * you may not use this file except in compliance with the License.
6306
- * You may obtain a copy of the License at
6307
- *
6308
- * http://www.apache.org/licenses/LICENSE-2.0
6309
- *
6310
- * Unless required by applicable law or agreed to in writing, software
6311
- * distributed under the License is distributed on an "AS IS" BASIS,
6312
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
6313
- * See the License for the specific language governing permissions and
6314
- * limitations under the License.
6315
- */
6316
- const cacheAllowText = /* @__PURE__ */ new Map();
6317
- const cacheDisallowText = /* @__PURE__ */ new Map();
6318
- const kTextScoreRange = 10;
6319
- const kExactPenalty = kTextScoreRange / 2;
6320
- const kTestIdScore = 1;
6321
- const kOtherTestIdScore = 2;
6322
- const kIframeByAttributeScore = 10;
6323
- const kBeginPenalizedScore = 50;
6324
- const kPlaceholderScore = 100;
6325
- const kLabelScore = 120;
6326
- const kRoleWithNameScore = 140;
6327
- const kAltTextScore = 160;
6328
- const kTextScore = 180;
6329
- const kTitleScore = 200;
6330
- const kTextScoreRegex = 250;
6331
- const kPlaceholderScoreExact = kPlaceholderScore + kExactPenalty;
6332
- const kLabelScoreExact = kLabelScore + kExactPenalty;
6333
- const kRoleWithNameScoreExact = kRoleWithNameScore + kExactPenalty;
6334
- const kAltTextScoreExact = kAltTextScore + kExactPenalty;
6335
- const kTextScoreExact = kTextScore + kExactPenalty;
6336
- const kTitleScoreExact = kTitleScore + kExactPenalty;
6337
- const kEndPenalizedScore = 300;
6338
- const kCSSIdScore = 500;
6339
- const kRoleWithoutNameScore = 510;
6340
- const kCSSInputTypeNameScore = 520;
6341
- const kCSSTagNameScore = 530;
6342
- const kNthScore = 1e4;
6343
- const kCSSFallbackScore = 1e7;
6344
- function generateSelector(injectedScript, targetElement, options) {
6345
- injectedScript._evaluator.begin();
6346
- beginAriaCaches();
6347
- try {
6348
- return joinTokens(generateSelectorFor(injectedScript, targetElement, options) || cssFallback(injectedScript, targetElement, options));
6349
- } finally {
6350
- cacheAllowText.clear();
6351
- cacheDisallowText.clear();
6352
- endAriaCaches();
6353
- injectedScript._evaluator.end();
6354
- }
6355
- }
6356
- function filterRegexTokens(textCandidates) {
6357
- return textCandidates.filter((c) => c[0].selector[0] !== "/");
6358
- }
6359
- function generateSelectorFor(injectedScript, targetElement, options) {
6360
- if (targetElement.ownerDocument.documentElement === targetElement) return [{
6361
- engine: "css",
6362
- selector: "html",
6363
- score: 1
6364
- }];
6365
- const calculate = (element, allowText) => {
6366
- const allowNthMatch = element === targetElement;
6367
- let textCandidates = allowText ? buildTextCandidates(injectedScript, element, element === targetElement) : [];
6368
- if (element !== targetElement) textCandidates = filterRegexTokens(textCandidates);
6369
- const noTextCandidates = buildNoTextCandidates(injectedScript, element, options).map((token) => [token]);
6370
- let result = chooseFirstSelector(injectedScript, targetElement.ownerDocument, element, [...textCandidates, ...noTextCandidates], allowNthMatch);
6371
- textCandidates = filterRegexTokens(textCandidates);
6372
- const checkWithText = (textCandidatesToUse) => {
6373
- const allowParentText = allowText && !textCandidatesToUse.length;
6374
- const candidates = [...textCandidatesToUse, ...noTextCandidates].filter((c) => {
6375
- if (!result) return true;
6376
- return combineScores(c) < combineScores(result);
6377
- });
6378
- let bestPossibleInParent = candidates[0];
6379
- if (!bestPossibleInParent) return;
6380
- for (let parent = parentElementOrShadowHost(element); parent; parent = parentElementOrShadowHost(parent)) {
6381
- const parentTokens = calculateCached(parent, allowParentText);
6382
- if (!parentTokens) continue;
6383
- if (result && combineScores([...parentTokens, ...bestPossibleInParent]) >= combineScores(result)) continue;
6384
- bestPossibleInParent = chooseFirstSelector(injectedScript, parent, element, candidates, allowNthMatch);
6385
- if (!bestPossibleInParent) return;
6386
- const combined = [...parentTokens, ...bestPossibleInParent];
6387
- if (!result || combineScores(combined) < combineScores(result)) result = combined;
6388
- }
6389
- };
6390
- checkWithText(textCandidates);
6391
- if (element === targetElement && textCandidates.length) checkWithText([]);
6392
- return result;
6393
- };
6394
- const calculateCached = (element, allowText) => {
6395
- const cache = allowText ? cacheAllowText : cacheDisallowText;
6396
- let value = cache.get(element);
6397
- if (value === void 0) {
6398
- value = calculate(element, allowText);
6399
- cache.set(element, value);
6400
- }
6401
- return value;
6402
- };
6403
- return calculate(targetElement, !options.noText);
6404
- }
6405
- function buildNoTextCandidates(injectedScript, element, options) {
6406
- const candidates = [];
6407
- for (const attr of [
6408
- "data-testid",
6409
- "data-test-id",
6410
- "data-test"
6411
- ]) if (attr !== options.testIdAttributeName && element.getAttribute(attr)) candidates.push({
6412
- engine: "css",
6413
- selector: `[${attr}=${quoteCSSAttributeValue(element.getAttribute(attr))}]`,
6414
- score: kOtherTestIdScore
6415
- });
6416
- if (!options.noCSSId) {
6417
- const idAttr = element.getAttribute("id");
6418
- if (idAttr && !isGuidLike(idAttr)) candidates.push({
6419
- engine: "css",
6420
- selector: makeSelectorForId(idAttr),
6421
- score: kCSSIdScore
6422
- });
6423
- }
6424
- candidates.push({
6425
- engine: "css",
6426
- selector: cssEscape(element.nodeName.toLowerCase()),
6427
- score: kCSSTagNameScore
6428
- });
6429
- if (element.nodeName === "IFRAME") {
6430
- for (const attribute of ["name", "title"]) if (element.getAttribute(attribute)) candidates.push({
6431
- engine: "css",
6432
- selector: `${cssEscape(element.nodeName.toLowerCase())}[${attribute}=${quoteCSSAttributeValue(element.getAttribute(attribute))}]`,
6433
- score: kIframeByAttributeScore
6434
- });
6435
- if (element.getAttribute(options.testIdAttributeName)) candidates.push({
6436
- engine: "css",
6437
- selector: `[${options.testIdAttributeName}=${quoteCSSAttributeValue(element.getAttribute(options.testIdAttributeName))}]`,
6438
- score: kTestIdScore
6439
- });
6440
- penalizeScoreForLength([candidates]);
6441
- return candidates;
6442
- }
6443
- if (element.getAttribute(options.testIdAttributeName)) candidates.push({
6444
- engine: "internal:testid",
6445
- selector: `[${options.testIdAttributeName}=${escapeForAttributeSelector(element.getAttribute(options.testIdAttributeName), true)}]`,
6446
- score: kTestIdScore
6447
- });
6448
- if (element.nodeName === "INPUT" || element.nodeName === "TEXTAREA") {
6449
- const input = element;
6450
- if (input.placeholder) {
6451
- candidates.push({
6452
- engine: "internal:attr",
6453
- selector: `[placeholder=${escapeForAttributeSelector(input.placeholder, true)}]`,
6454
- score: kPlaceholderScoreExact
6455
- });
6456
- for (const alternative of suitableTextAlternatives(input.placeholder)) candidates.push({
6457
- engine: "internal:attr",
6458
- selector: `[placeholder=${escapeForAttributeSelector(alternative.text, false)}]`,
6459
- score: kPlaceholderScore - alternative.scoreBouns
6460
- });
6461
- }
6462
- }
6463
- const labels = getElementLabels(injectedScript._evaluator._cacheText, element);
6464
- for (const label of labels) {
6465
- const labelText = label.normalized;
6466
- candidates.push({
6467
- engine: "internal:label",
6468
- selector: escapeForTextSelector(labelText, true),
6469
- score: kLabelScoreExact
6470
- });
6471
- for (const alternative of suitableTextAlternatives(labelText)) candidates.push({
6472
- engine: "internal:label",
6473
- selector: escapeForTextSelector(alternative.text, false),
6474
- score: kLabelScore - alternative.scoreBouns
6475
- });
6476
- }
6477
- const ariaRole = getAriaRole(element);
6478
- if (ariaRole && !["none", "presentation"].includes(ariaRole)) candidates.push({
6479
- engine: "internal:role",
6480
- selector: ariaRole,
6481
- score: kRoleWithoutNameScore
6482
- });
6483
- if (element.getAttribute("name") && [
6484
- "BUTTON",
6485
- "FORM",
6486
- "FIELDSET",
6487
- "FRAME",
6488
- "IFRAME",
6489
- "INPUT",
6490
- "KEYGEN",
6491
- "OBJECT",
6492
- "OUTPUT",
6493
- "SELECT",
6494
- "TEXTAREA",
6495
- "MAP",
6496
- "META",
6497
- "PARAM"
6498
- ].includes(element.nodeName)) candidates.push({
6499
- engine: "css",
6500
- selector: `${cssEscape(element.nodeName.toLowerCase())}[name=${quoteCSSAttributeValue(element.getAttribute("name"))}]`,
6501
- score: kCSSInputTypeNameScore
6502
- });
6503
- if (["INPUT", "TEXTAREA"].includes(element.nodeName) && element.getAttribute("type") !== "hidden") {
6504
- if (element.getAttribute("type")) candidates.push({
6505
- engine: "css",
6506
- selector: `${cssEscape(element.nodeName.toLowerCase())}[type=${quoteCSSAttributeValue(element.getAttribute("type"))}]`,
6507
- score: kCSSInputTypeNameScore
6508
- });
6509
- }
6510
- if ([
6511
- "INPUT",
6512
- "TEXTAREA",
6513
- "SELECT"
6514
- ].includes(element.nodeName) && element.getAttribute("type") !== "hidden") candidates.push({
6515
- engine: "css",
6516
- selector: cssEscape(element.nodeName.toLowerCase()),
6517
- score: kCSSInputTypeNameScore + 1
6518
- });
6519
- penalizeScoreForLength([candidates]);
6520
- return candidates;
6521
- }
6522
- function buildTextCandidates(injectedScript, element, isTargetNode) {
6523
- if (element.nodeName === "SELECT") return [];
6524
- const candidates = [];
6525
- const title = element.getAttribute("title");
6526
- if (title) {
6527
- candidates.push([{
6528
- engine: "internal:attr",
6529
- selector: `[title=${escapeForAttributeSelector(title, true)}]`,
6530
- score: kTitleScoreExact
6531
- }]);
6532
- for (const alternative of suitableTextAlternatives(title)) candidates.push([{
6533
- engine: "internal:attr",
6534
- selector: `[title=${escapeForAttributeSelector(alternative.text, false)}]`,
6535
- score: kTitleScore - alternative.scoreBouns
6536
- }]);
6537
- }
6538
- const alt = element.getAttribute("alt");
6539
- if (alt && [
6540
- "APPLET",
6541
- "AREA",
6542
- "IMG",
6543
- "INPUT"
6544
- ].includes(element.nodeName)) {
6545
- candidates.push([{
6546
- engine: "internal:attr",
6547
- selector: `[alt=${escapeForAttributeSelector(alt, true)}]`,
6548
- score: kAltTextScoreExact
6549
- }]);
6550
- for (const alternative of suitableTextAlternatives(alt)) candidates.push([{
6551
- engine: "internal:attr",
6552
- selector: `[alt=${escapeForAttributeSelector(alternative.text, false)}]`,
6553
- score: kAltTextScore - alternative.scoreBouns
6554
- }]);
6555
- }
6556
- const text = elementText(injectedScript._evaluator._cacheText, element).normalized;
6557
- if (text) {
6558
- const alternatives = suitableTextAlternatives(text);
6559
- if (isTargetNode) {
6560
- if (text.length <= 80) candidates.push([{
6561
- engine: "internal:text",
6562
- selector: escapeForTextSelector(text, true),
6563
- score: kTextScoreExact
6564
- }]);
6565
- for (const alternative of alternatives) candidates.push([{
6566
- engine: "internal:text",
6567
- selector: escapeForTextSelector(alternative.text, false),
6568
- score: kTextScore - alternative.scoreBouns
6569
- }]);
6570
- }
6571
- const cssToken = {
6572
- engine: "css",
6573
- selector: cssEscape(element.nodeName.toLowerCase()),
6574
- score: kCSSTagNameScore
6575
- };
6576
- for (const alternative of alternatives) candidates.push([cssToken, {
6577
- engine: "internal:has-text",
6578
- selector: escapeForTextSelector(alternative.text, false),
6579
- score: kTextScore - alternative.scoreBouns
6580
- }]);
6581
- if (text.length <= 80) {
6582
- const re = new RegExp(`^${escapeRegExp(text)}$`);
6583
- candidates.push([cssToken, {
6584
- engine: "internal:has-text",
6585
- selector: escapeForTextSelector(re, false),
6586
- score: kTextScoreRegex
6587
- }]);
6588
- }
6589
- }
6590
- const ariaRole = getAriaRole(element);
6591
- if (ariaRole && !["none", "presentation"].includes(ariaRole)) {
6592
- const ariaName = getElementAccessibleName(element, false);
6593
- if (ariaName) {
6594
- candidates.push([{
6595
- engine: "internal:role",
6596
- selector: `${ariaRole}[name=${escapeForAttributeSelector(ariaName, true)}]`,
6597
- score: kRoleWithNameScoreExact
6598
- }]);
6599
- for (const alternative of suitableTextAlternatives(ariaName)) candidates.push([{
6600
- engine: "internal:role",
6601
- selector: `${ariaRole}[name=${escapeForAttributeSelector(alternative.text, false)}]`,
6602
- score: kRoleWithNameScore - alternative.scoreBouns
6603
- }]);
6604
- }
6605
- }
6606
- penalizeScoreForLength(candidates);
6607
- return candidates;
6608
- }
6609
- function makeSelectorForId(id) {
6610
- return /^[a-z][\w\-]+$/i.test(id) ? `#${id}` : `[id="${cssEscape(id)}"]`;
6611
- }
6612
- function cssFallback(injectedScript, targetElement, options) {
6613
- const root = targetElement.ownerDocument;
6614
- const tokens = [];
6615
- function uniqueCSSSelector(prefix) {
6616
- const path = tokens.slice();
6617
- if (prefix) path.unshift(prefix);
6618
- const selector = path.join(" > ");
6619
- const parsedSelector = injectedScript.parseSelector(selector);
6620
- return injectedScript.querySelector(parsedSelector, root, false) === targetElement ? selector : void 0;
6621
- }
6622
- function makeStrict(selector) {
6623
- const token = {
6624
- engine: "css",
6625
- selector,
6626
- score: kCSSFallbackScore
6627
- };
6628
- const parsedSelector = injectedScript.parseSelector(selector);
6629
- const elements = injectedScript.querySelectorAll(parsedSelector, root);
6630
- if (elements.length === 1) return [token];
6631
- return [token, {
6632
- engine: "nth",
6633
- selector: String(elements.indexOf(targetElement)),
6634
- score: kNthScore
6635
- }];
6636
- }
6637
- for (let element = targetElement; element && element !== root; element = parentElementOrShadowHost(element)) {
6638
- const nodeName = element.nodeName.toLowerCase();
6639
- let bestTokenForLevel = "";
6640
- if (element.id && !options.noCSSId) {
6641
- const token = makeSelectorForId(element.id);
6642
- const selector = uniqueCSSSelector(token);
6643
- if (selector) return makeStrict(selector);
6644
- bestTokenForLevel = token;
6645
- }
6646
- const parent = element.parentNode;
6647
- const classes = [...element.classList];
6648
- for (let i = 0; i < classes.length; ++i) {
6649
- const token = `.${cssEscape(classes.slice(0, i + 1).join("."))}`;
6650
- const selector = uniqueCSSSelector(token);
6651
- if (selector) return makeStrict(selector);
6652
- if (!bestTokenForLevel && parent) {
6653
- if (parent.querySelectorAll(token).length === 1) bestTokenForLevel = token;
6654
- }
6655
- }
6656
- if (parent) {
6657
- const siblings = [...parent.children];
6658
- const token = siblings.filter((sibling) => sibling.nodeName.toLowerCase() === nodeName).indexOf(element) === 0 ? cssEscape(nodeName) : `${cssEscape(nodeName)}:nth-child(${1 + siblings.indexOf(element)})`;
6659
- const selector = uniqueCSSSelector(token);
6660
- if (selector) return makeStrict(selector);
6661
- if (!bestTokenForLevel) bestTokenForLevel = token;
6662
- } else if (!bestTokenForLevel) bestTokenForLevel = cssEscape(nodeName);
6663
- tokens.unshift(bestTokenForLevel);
6664
- }
6665
- return makeStrict(uniqueCSSSelector());
6666
- }
6667
- function penalizeScoreForLength(groups) {
6668
- for (const group of groups) for (const token of group) if (token.score > kBeginPenalizedScore && token.score < kEndPenalizedScore) token.score += Math.min(kTextScoreRange, token.selector.length / 10 | 0);
6669
- }
6670
- function joinTokens(tokens) {
6671
- const parts = [];
6672
- let lastEngine = "";
6673
- for (const { engine, selector } of tokens) {
6674
- if (parts.length && (lastEngine !== "css" || engine !== "css" || selector.startsWith(":nth-match("))) parts.push(">>");
6675
- lastEngine = engine;
6676
- if (engine === "css") parts.push(selector);
6677
- else parts.push(`${engine}=${selector}`);
6678
- }
6679
- return parts.join(" ");
6680
- }
6681
- function combineScores(tokens) {
6682
- let score = 0;
6683
- for (let i = 0; i < tokens.length; i++) score += tokens[i].score * (tokens.length - i);
6684
- return score;
6685
- }
6686
- function chooseFirstSelector(injectedScript, scope, targetElement, selectors, allowNthMatch) {
6687
- const joined = selectors.map((tokens) => ({
6688
- tokens,
6689
- score: combineScores(tokens)
6690
- }));
6691
- joined.sort((a, b) => a.score - b.score);
6692
- let bestWithIndex = null;
6693
- for (const { tokens } of joined) {
6694
- const parsedSelector = injectedScript.parseSelector(joinTokens(tokens));
6695
- const result = injectedScript.querySelectorAll(parsedSelector, scope);
6696
- if (result[0] === targetElement && result.length === 1) return tokens;
6697
- const index = result.indexOf(targetElement);
6698
- if (!allowNthMatch || bestWithIndex || index === -1 || result.length > 5) continue;
6699
- const nth = {
6700
- engine: "nth",
6701
- selector: String(index),
6702
- score: kNthScore
6703
- };
6704
- bestWithIndex = [...tokens, nth];
6705
- }
6706
- return bestWithIndex;
6707
- }
6708
- function isGuidLike(id) {
6709
- let lastCharacterType;
6710
- let transitionCount = 0;
6711
- for (let i = 0; i < id.length; ++i) {
6712
- const c = id[i];
6713
- let characterType;
6714
- if (c === "-" || c === "_") continue;
6715
- if (c >= "a" && c <= "z") characterType = "lower";
6716
- else if (c >= "A" && c <= "Z") characterType = "upper";
6717
- else if (c >= "0" && c <= "9") characterType = "digit";
6718
- else characterType = "other";
6719
- if (characterType === "lower" && lastCharacterType === "upper") {
6720
- lastCharacterType = characterType;
6721
- continue;
6722
- }
6723
- if (lastCharacterType && lastCharacterType !== characterType) ++transitionCount;
6724
- lastCharacterType = characterType;
6725
- }
6726
- return transitionCount >= id.length / 4;
6727
- }
6728
- function trimWordBoundary(text, maxLength) {
6729
- if (text.length <= maxLength) return text;
6730
- text = text.substring(0, maxLength);
6731
- const match = text.match(/^(.*)\b(.+)$/);
6732
- if (!match) return "";
6733
- return match[1].trimEnd();
6734
- }
6735
- function suitableTextAlternatives(text) {
6736
- let result = [];
6737
- {
6738
- const match = text.match(/^([\d.,]+)[^.,\w]/);
6739
- const leadingNumberLength = match ? match[1].length : 0;
6740
- if (leadingNumberLength) {
6741
- const alt = trimWordBoundary(text.substring(leadingNumberLength).trimStart(), 80);
6742
- result.push({
6743
- text: alt,
6744
- scoreBouns: alt.length <= 30 ? 2 : 1
6745
- });
6746
- }
6747
- }
6748
- {
6749
- const match = text.match(/[^.,\w]([\d.,]+)$/);
6750
- const trailingNumberLength = match ? match[1].length : 0;
6751
- if (trailingNumberLength) {
6752
- const alt = trimWordBoundary(text.substring(0, text.length - trailingNumberLength).trimEnd(), 80);
6753
- result.push({
6754
- text: alt,
6755
- scoreBouns: alt.length <= 30 ? 2 : 1
6756
- });
6757
- }
6758
- }
6759
- if (text.length <= 30) result.push({
6760
- text,
6761
- scoreBouns: 0
6762
- });
6763
- else {
6764
- result.push({
6765
- text: trimWordBoundary(text, 80),
6766
- scoreBouns: 0
6767
- });
6768
- result.push({
6769
- text: trimWordBoundary(text, 30),
6770
- scoreBouns: 1
6771
- });
6772
- }
6773
- result = result.filter((r) => r.text);
6774
- if (!result.length) result.push({
6775
- text: text.substring(0, 80),
6776
- scoreBouns: 0
6777
- });
6778
- return result;
6779
- }
6780
-
6781
- //#endregion
6782
- //#region src/layoutSelectorUtils.ts
6783
- /**
6784
- * Copyright (c) Microsoft Corporation.
6785
- *
6786
- * Licensed under the Apache License, Version 2.0 (the "License");
6787
- * you may not use this file except in compliance with the License.
6788
- * You may obtain a copy of the License at
6789
- *
6790
- * http://www.apache.org/licenses/LICENSE-2.0
6791
- *
6792
- * Unless required by applicable law or agreed to in writing, software
6793
- * distributed under the License is distributed on an "AS IS" BASIS,
6794
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
6795
- * See the License for the specific language governing permissions and
6796
- * limitations under the License.
6797
- */
6798
- function boxRightOf(box1, box2, maxDistance) {
6799
- const distance = box1.left - box2.right;
6800
- if (distance < 0 || maxDistance !== void 0 && distance > maxDistance) return;
6801
- return distance + Math.max(box2.bottom - box1.bottom, 0) + Math.max(box1.top - box2.top, 0);
6802
- }
6803
- function boxLeftOf(box1, box2, maxDistance) {
6804
- const distance = box2.left - box1.right;
6805
- if (distance < 0 || maxDistance !== void 0 && distance > maxDistance) return;
6806
- return distance + Math.max(box2.bottom - box1.bottom, 0) + Math.max(box1.top - box2.top, 0);
6807
- }
6808
- function boxAbove(box1, box2, maxDistance) {
6809
- const distance = box2.top - box1.bottom;
6810
- if (distance < 0 || maxDistance !== void 0 && distance > maxDistance) return;
6811
- return distance + Math.max(box1.left - box2.left, 0) + Math.max(box2.right - box1.right, 0);
6812
- }
6813
- function boxBelow(box1, box2, maxDistance) {
6814
- const distance = box1.top - box2.bottom;
6815
- if (distance < 0 || maxDistance !== void 0 && distance > maxDistance) return;
6816
- return distance + Math.max(box1.left - box2.left, 0) + Math.max(box2.right - box1.right, 0);
6817
- }
6818
- function boxNear(box1, box2, maxDistance) {
6819
- const kThreshold = maxDistance === void 0 ? 50 : maxDistance;
6820
- let score = 0;
6821
- if (box1.left - box2.right >= 0) score += box1.left - box2.right;
6822
- if (box2.left - box1.right >= 0) score += box2.left - box1.right;
6823
- if (box2.top - box1.bottom >= 0) score += box2.top - box1.bottom;
6824
- if (box1.top - box2.bottom >= 0) score += box1.top - box2.bottom;
6825
- return score > kThreshold ? void 0 : score;
6826
- }
6827
- const kLayoutSelectorNames = [
6828
- "left-of",
6829
- "right-of",
6830
- "above",
6831
- "below",
6832
- "near"
6833
- ];
6834
- function layoutSelectorScore(name, element, inner, maxDistance) {
6835
- const box = element.getBoundingClientRect();
6836
- const scorer = {
6837
- "left-of": boxLeftOf,
6838
- "right-of": boxRightOf,
6839
- above: boxAbove,
6840
- below: boxBelow,
6841
- near: boxNear
6842
- }[name];
6843
- let bestScore;
6844
- for (const e of inner) {
6845
- if (e === element) continue;
6846
- const score = scorer(box, e.getBoundingClientRect(), maxDistance);
6847
- if (score === void 0) continue;
6848
- if (bestScore === void 0 || score < bestScore) bestScore = score;
6849
- }
6850
- return bestScore;
6851
- }
6852
-
6853
- //#endregion
6854
- //#region src/selectorEvaluator.ts
6855
- var SelectorEvaluatorImpl = class {
6856
- constructor(extraEngines) {
6857
- _defineProperty(this, "_engines", /* @__PURE__ */ new Map());
6858
- _defineProperty(this, "_cacheQueryCSS", /* @__PURE__ */ new Map());
6859
- _defineProperty(this, "_cacheMatches", /* @__PURE__ */ new Map());
6860
- _defineProperty(this, "_cacheQuery", /* @__PURE__ */ new Map());
6861
- _defineProperty(this, "_cacheMatchesSimple", /* @__PURE__ */ new Map());
6862
- _defineProperty(this, "_cacheMatchesParents", /* @__PURE__ */ new Map());
6863
- _defineProperty(this, "_cacheCallMatches", /* @__PURE__ */ new Map());
6864
- _defineProperty(this, "_cacheCallQuery", /* @__PURE__ */ new Map());
6865
- _defineProperty(this, "_cacheQuerySimple", /* @__PURE__ */ new Map());
6866
- _defineProperty(this, "_cacheText", /* @__PURE__ */ new Map());
6867
- _defineProperty(this, "_scoreMap", void 0);
6868
- _defineProperty(this, "_retainCacheCounter", 0);
6869
- for (const [name, engine] of extraEngines) this._engines.set(name, engine);
6870
- this._engines.set("not", notEngine);
6871
- this._engines.set("is", isEngine);
6872
- this._engines.set("where", isEngine);
6873
- this._engines.set("has", hasEngine);
6874
- this._engines.set("scope", scopeEngine);
6875
- this._engines.set("light", lightEngine);
6876
- this._engines.set("visible", visibleEngine);
6877
- this._engines.set("text", textEngine);
6878
- this._engines.set("text-is", textIsEngine);
6879
- this._engines.set("text-matches", textMatchesEngine);
6880
- this._engines.set("has-text", hasTextEngine);
6881
- this._engines.set("right-of", createLayoutEngine("right-of"));
6882
- this._engines.set("left-of", createLayoutEngine("left-of"));
6883
- this._engines.set("above", createLayoutEngine("above"));
6884
- this._engines.set("below", createLayoutEngine("below"));
6885
- this._engines.set("near", createLayoutEngine("near"));
6886
- this._engines.set("nth-match", nthMatchEngine);
6887
- const allNames = [...this._engines.keys()];
6888
- allNames.sort();
6889
- const parserNames = [...customCSSNames];
6890
- parserNames.sort();
6891
- if (allNames.join("|") !== parserNames.join("|")) throw new Error(`Please keep customCSSNames in sync with evaluator engines: ${allNames.join("|")} vs ${parserNames.join("|")}`);
6892
- }
6893
- begin() {
6894
- ++this._retainCacheCounter;
6895
- }
6896
- end() {
6897
- --this._retainCacheCounter;
6898
- if (!this._retainCacheCounter) {
6899
- this._cacheQueryCSS.clear();
6900
- this._cacheMatches.clear();
6901
- this._cacheQuery.clear();
6902
- this._cacheMatchesSimple.clear();
6903
- this._cacheMatchesParents.clear();
6904
- this._cacheCallMatches.clear();
6905
- this._cacheCallQuery.clear();
6906
- this._cacheQuerySimple.clear();
6907
- this._cacheText.clear();
6908
- }
6909
- }
6910
- _cached(cache, main, rest, cb) {
6911
- if (!cache.has(main)) cache.set(main, []);
6912
- const entries = cache.get(main);
6913
- const entry = entries.find((e) => rest.every((value, index) => e.rest[index] === value));
6914
- if (entry) return entry.result;
6915
- const result = cb();
6916
- entries.push({
6917
- rest,
6918
- result
6919
- });
6920
- return result;
6921
- }
6922
- _checkSelector(s) {
6923
- if (!(typeof s === "object" && s && (Array.isArray(s) || "simples" in s && s.simples.length))) throw new Error(`Malformed selector "${s}"`);
6924
- return s;
6925
- }
6926
- matches(element, s, context) {
6927
- const selector = this._checkSelector(s);
6928
- this.begin();
6929
- try {
6930
- return this._cached(this._cacheMatches, element, [
6931
- selector,
6932
- context.scope,
6933
- context.pierceShadow,
6934
- context.originalScope
6935
- ], () => {
6936
- if (Array.isArray(selector)) return this._matchesEngine(isEngine, element, selector, context);
6937
- if (this._hasScopeClause(selector)) context = this._expandContextForScopeMatching(context);
6938
- if (!this._matchesSimple(element, selector.simples[selector.simples.length - 1].selector, context)) return false;
6939
- return this._matchesParents(element, selector, selector.simples.length - 2, context);
6940
- });
6941
- } finally {
6942
- this.end();
6943
- }
6944
- }
6945
- query(context, s) {
6946
- const selector = this._checkSelector(s);
6947
- this.begin();
6948
- try {
6949
- return this._cached(this._cacheQuery, selector, [
6950
- context.scope,
6951
- context.pierceShadow,
6952
- context.originalScope
6953
- ], () => {
6954
- if (Array.isArray(selector)) return this._queryEngine(isEngine, context, selector);
6955
- if (this._hasScopeClause(selector)) context = this._expandContextForScopeMatching(context);
6956
- const previousScoreMap = this._scoreMap;
6957
- this._scoreMap = /* @__PURE__ */ new Map();
6958
- let elements = this._querySimple(context, selector.simples[selector.simples.length - 1].selector);
6959
- elements = elements.filter((element) => this._matchesParents(element, selector, selector.simples.length - 2, context));
6960
- if (this._scoreMap.size) elements.sort((a, b) => {
6961
- const aScore = this._scoreMap.get(a);
6962
- const bScore = this._scoreMap.get(b);
6963
- if (aScore === bScore) return 0;
6964
- if (aScore === void 0) return 1;
6965
- if (bScore === void 0) return -1;
6966
- return aScore - bScore;
6967
- });
6968
- this._scoreMap = previousScoreMap;
6969
- return elements;
6970
- });
6971
- } finally {
6972
- this.end();
6973
- }
6974
- }
6975
- _markScore(element, score) {
6976
- if (this._scoreMap) this._scoreMap.set(element, score);
6977
- }
6978
- _hasScopeClause(selector) {
6979
- return selector.simples.some((simple) => simple.selector.functions.some((f) => f.name === "scope"));
6980
- }
6981
- _expandContextForScopeMatching(context) {
6982
- if (context.scope.nodeType !== 1) return context;
6983
- const scope = parentElementOrShadowHost(context.scope);
6984
- if (!scope) return context;
6985
- return {
6986
- ...context,
6987
- scope,
6988
- originalScope: context.originalScope || context.scope
6989
- };
6990
- }
6991
- _matchesSimple(element, simple, context) {
6992
- return this._cached(this._cacheMatchesSimple, element, [
6993
- simple,
6994
- context.scope,
6995
- context.pierceShadow,
6996
- context.originalScope
6997
- ], () => {
6998
- if (element === context.scope) return false;
6999
- if (simple.css && !this._matchesCSS(element, simple.css)) return false;
7000
- for (const func of simple.functions) if (!this._matchesEngine(this._getEngine(func.name), element, func.args, context)) return false;
7001
- return true;
7002
- });
7003
- }
7004
- _querySimple(context, simple) {
7005
- if (!simple.functions.length) return this._queryCSS(context, simple.css || "*");
7006
- return this._cached(this._cacheQuerySimple, simple, [
7007
- context.scope,
7008
- context.pierceShadow,
7009
- context.originalScope
7010
- ], () => {
7011
- let css = simple.css;
7012
- const funcs = simple.functions;
7013
- if (css === "*" && funcs.length) css = void 0;
7014
- let elements;
7015
- let firstIndex = -1;
7016
- if (css !== void 0) elements = this._queryCSS(context, css);
7017
- else {
7018
- firstIndex = funcs.findIndex((func) => this._getEngine(func.name).query !== void 0);
7019
- if (firstIndex === -1) firstIndex = 0;
7020
- elements = this._queryEngine(this._getEngine(funcs[firstIndex].name), context, funcs[firstIndex].args);
7021
- }
7022
- for (let i = 0; i < funcs.length; i++) {
7023
- if (i === firstIndex) continue;
7024
- const engine = this._getEngine(funcs[i].name);
7025
- if (engine.matches !== void 0) elements = elements.filter((e) => this._matchesEngine(engine, e, funcs[i].args, context));
7026
- }
7027
- for (let i = 0; i < funcs.length; i++) {
7028
- if (i === firstIndex) continue;
7029
- const engine = this._getEngine(funcs[i].name);
7030
- if (engine.matches === void 0) elements = elements.filter((e) => this._matchesEngine(engine, e, funcs[i].args, context));
7031
- }
7032
- return elements;
7033
- });
7034
- }
7035
- _matchesParents(element, complex, index, context) {
7036
- if (index < 0) return true;
7037
- return this._cached(this._cacheMatchesParents, element, [
7038
- complex,
7039
- index,
7040
- context.scope,
7041
- context.pierceShadow,
7042
- context.originalScope
7043
- ], () => {
7044
- const { selector: simple, combinator } = complex.simples[index];
7045
- if (combinator === ">") {
7046
- const parent = parentElementOrShadowHostInContext(element, context);
7047
- if (!parent || !this._matchesSimple(parent, simple, context)) return false;
7048
- return this._matchesParents(parent, complex, index - 1, context);
7049
- }
7050
- if (combinator === "+") {
7051
- const previousSibling = previousSiblingInContext(element, context);
7052
- if (!previousSibling || !this._matchesSimple(previousSibling, simple, context)) return false;
7053
- return this._matchesParents(previousSibling, complex, index - 1, context);
7054
- }
7055
- if (combinator === "") {
7056
- let parent = parentElementOrShadowHostInContext(element, context);
7057
- while (parent) {
7058
- if (this._matchesSimple(parent, simple, context)) {
7059
- if (this._matchesParents(parent, complex, index - 1, context)) return true;
7060
- if (complex.simples[index - 1].combinator === "") break;
7061
- }
7062
- parent = parentElementOrShadowHostInContext(parent, context);
7063
- }
7064
- return false;
7065
- }
7066
- if (combinator === "~") {
7067
- let previousSibling = previousSiblingInContext(element, context);
7068
- while (previousSibling) {
7069
- if (this._matchesSimple(previousSibling, simple, context)) {
7070
- if (this._matchesParents(previousSibling, complex, index - 1, context)) return true;
7071
- if (complex.simples[index - 1].combinator === "~") break;
7072
- }
7073
- previousSibling = previousSiblingInContext(previousSibling, context);
7074
- }
7075
- return false;
7076
- }
7077
- if (combinator === ">=") {
7078
- let parent = element;
7079
- while (parent) {
7080
- if (this._matchesSimple(parent, simple, context)) {
7081
- if (this._matchesParents(parent, complex, index - 1, context)) return true;
7082
- if (complex.simples[index - 1].combinator === "") break;
7083
- }
7084
- parent = parentElementOrShadowHostInContext(parent, context);
7085
- }
7086
- return false;
7087
- }
7088
- throw new Error(`Unsupported combinator "${combinator}"`);
7089
- });
7090
- }
7091
- _matchesEngine(engine, element, args, context) {
7092
- if (engine.matches) return this._callMatches(engine, element, args, context);
7093
- if (engine.query) return this._callQuery(engine, args, context).includes(element);
7094
- throw new Error(`Selector engine should implement "matches" or "query"`);
7095
- }
7096
- _queryEngine(engine, context, args) {
7097
- if (engine.query) return this._callQuery(engine, args, context);
7098
- if (engine.matches) return this._queryCSS(context, "*").filter((element) => this._callMatches(engine, element, args, context));
7099
- throw new Error(`Selector engine should implement "matches" or "query"`);
7100
- }
7101
- _callMatches(engine, element, args, context) {
7102
- return this._cached(this._cacheCallMatches, element, [
7103
- engine,
7104
- context.scope,
7105
- context.pierceShadow,
7106
- context.originalScope,
7107
- ...args
7108
- ], () => {
7109
- return engine.matches(element, args, context, this);
7110
- });
7111
- }
7112
- _callQuery(engine, args, context) {
7113
- return this._cached(this._cacheCallQuery, engine, [
7114
- context.scope,
7115
- context.pierceShadow,
7116
- context.originalScope,
7117
- ...args
7118
- ], () => {
7119
- return engine.query(context, args, this);
7120
- });
7121
- }
7122
- _matchesCSS(element, css) {
7123
- return element.matches(css);
7124
- }
7125
- _queryCSS(context, css) {
7126
- return this._cached(this._cacheQueryCSS, css, [
7127
- context.scope,
7128
- context.pierceShadow,
7129
- context.originalScope
7130
- ], () => {
7131
- let result = [];
7132
- function query(root) {
7133
- result = result.concat([...root.querySelectorAll(css)]);
7134
- if (!context.pierceShadow) return;
7135
- if (root.shadowRoot) query(root.shadowRoot);
7136
- for (const element of root.querySelectorAll("*")) if (element.shadowRoot) query(element.shadowRoot);
7137
- }
7138
- query(context.scope);
7139
- return result;
7140
- });
7141
- }
7142
- _getEngine(name) {
7143
- const engine = this._engines.get(name);
7144
- if (!engine) throw new Error(`Unknown selector engine "${name}"`);
7145
- return engine;
7146
- }
7147
- };
7148
- const isEngine = {
7149
- matches(element, args, context, evaluator) {
7150
- if (args.length === 0) throw new Error(`"is" engine expects non-empty selector list`);
7151
- return args.some((selector) => evaluator.matches(element, selector, context));
7152
- },
7153
- query(context, args, evaluator) {
7154
- if (args.length === 0) throw new Error(`"is" engine expects non-empty selector list`);
7155
- let elements = [];
7156
- for (const arg of args) elements = elements.concat(evaluator.query(context, arg));
7157
- return args.length === 1 ? elements : sortInDOMOrder(elements);
7158
- }
7159
- };
7160
- const hasEngine = { matches(element, args, context, evaluator) {
7161
- if (args.length === 0) throw new Error(`"has" engine expects non-empty selector list`);
7162
- return evaluator.query({
7163
- ...context,
7164
- scope: element
7165
- }, args).length > 0;
7166
- } };
7167
- const scopeEngine = {
7168
- matches(element, args, context, _evaluator) {
7169
- if (args.length !== 0) throw new Error(`"scope" engine expects no arguments`);
7170
- const actualScope = context.originalScope || context.scope;
7171
- if (actualScope.nodeType === 9) return element === actualScope.documentElement;
7172
- return element === actualScope;
7173
- },
7174
- query(context, args, _evaluator) {
7175
- if (args.length !== 0) throw new Error(`"scope" engine expects no arguments`);
7176
- const actualScope = context.originalScope || context.scope;
7177
- if (actualScope.nodeType === 9) {
7178
- const root = actualScope.documentElement;
7179
- return root ? [root] : [];
7180
- }
7181
- if (actualScope.nodeType === 1) return [actualScope];
7182
- return [];
7183
- }
7184
- };
7185
- const notEngine = { matches(element, args, context, evaluator) {
7186
- if (args.length === 0) throw new Error(`"not" engine expects non-empty selector list`);
7187
- return !evaluator.matches(element, args, context);
7188
- } };
7189
- const lightEngine = {
7190
- query(context, args, evaluator) {
7191
- return evaluator.query({
7192
- ...context,
7193
- pierceShadow: false
7194
- }, args);
7195
- },
7196
- matches(element, args, context, evaluator) {
7197
- return evaluator.matches(element, args, {
7198
- ...context,
7199
- pierceShadow: false
7200
- });
7201
- }
7202
- };
7203
- const visibleEngine = { matches(element, args, _context, _evaluator) {
7204
- if (args.length) throw new Error(`"visible" engine expects no arguments`);
7205
- return isElementVisible(element);
7206
- } };
7207
- const textEngine = { matches(element, args, context, evaluator) {
7208
- if (args.length !== 1 || typeof args[0] !== "string") throw new Error(`"text" engine expects a single string`);
7209
- const text = normalizeWhiteSpace(args[0]).toLowerCase();
7210
- const matcher = (elementText) => elementText.normalized.toLowerCase().includes(text);
7211
- return elementMatchesText(evaluator._cacheText, element, matcher) === "self";
7212
- } };
7213
- const textIsEngine = { matches(element, args, context, evaluator) {
7214
- if (args.length !== 1 || typeof args[0] !== "string") throw new Error(`"text-is" engine expects a single string`);
7215
- const text = normalizeWhiteSpace(args[0]);
7216
- const matcher = (elementText) => {
7217
- if (!text && !elementText.immediate.length) return true;
7218
- return elementText.immediate.some((s) => normalizeWhiteSpace(s) === text);
7219
- };
7220
- return elementMatchesText(evaluator._cacheText, element, matcher) !== "none";
7221
- } };
7222
- const textMatchesEngine = { matches(element, args, context, evaluator) {
7223
- if (args.length === 0 || typeof args[0] !== "string" || args.length > 2 || args.length === 2 && typeof args[1] !== "string") throw new Error(`"text-matches" engine expects a regexp body and optional regexp flags`);
7224
- const re = new RegExp(args[0], args.length === 2 ? args[1] : void 0);
7225
- const matcher = (elementText) => re.test(elementText.full);
7226
- return elementMatchesText(evaluator._cacheText, element, matcher) === "self";
7227
- } };
7228
- const hasTextEngine = { matches(element, args, context, evaluator) {
7229
- if (args.length !== 1 || typeof args[0] !== "string") throw new Error(`"has-text" engine expects a single string`);
7230
- if (shouldSkipForTextMatching(element)) return false;
7231
- const text = normalizeWhiteSpace(args[0]).toLowerCase();
7232
- const matcher = (elementText) => elementText.normalized.toLowerCase().includes(text);
7233
- return matcher(elementText(evaluator._cacheText, element));
7234
- } };
7235
- function createLayoutEngine(name) {
7236
- return { matches(element, args, context, evaluator) {
7237
- const maxDistance = args.length && typeof args[args.length - 1] === "number" ? args[args.length - 1] : void 0;
7238
- const queryArgs = maxDistance === void 0 ? args : args.slice(0, args.length - 1);
7239
- if (args.length < 1 + (maxDistance === void 0 ? 0 : 1)) throw new Error(`"${name}" engine expects a selector list and optional maximum distance in pixels`);
7240
- const score = layoutSelectorScore(name, element, evaluator.query(context, queryArgs), maxDistance);
7241
- if (score === void 0) return false;
7242
- evaluator._markScore(element, score);
7243
- return true;
7244
- } };
7245
- }
7246
- const nthMatchEngine = { query(context, args, evaluator) {
7247
- let index = args[args.length - 1];
7248
- if (args.length < 2) throw new Error(`"nth-match" engine expects non-empty selector list and an index argument`);
7249
- if (typeof index !== "number" || index < 1) throw new Error(`"nth-match" engine expects a one-based index as the last argument`);
7250
- const elements = isEngine.query(context, args.slice(0, args.length - 1), evaluator);
7251
- index--;
7252
- return index < elements.length ? [elements[index]] : [];
7253
- } };
7254
- function parentElementOrShadowHostInContext(element, context) {
7255
- if (element === context.scope) return;
7256
- if (!context.pierceShadow) return element.parentElement || void 0;
7257
- return parentElementOrShadowHost(element);
7258
- }
7259
- function previousSiblingInContext(element, context) {
7260
- if (element === context.scope) return;
7261
- return element.previousElementSibling || void 0;
7262
- }
7263
- function sortInDOMOrder(elements) {
7264
- const elementToEntry = /* @__PURE__ */ new Map();
7265
- const roots = [];
7266
- const result = [];
7267
- function append(element) {
7268
- let entry = elementToEntry.get(element);
7269
- if (entry) return entry;
7270
- const parent = parentElementOrShadowHost(element);
7271
- if (parent) append(parent).children.push(element);
7272
- else roots.push(element);
7273
- entry = {
7274
- children: [],
7275
- taken: false
7276
- };
7277
- elementToEntry.set(element, entry);
7278
- return entry;
7279
- }
7280
- for (const e of elements) append(e).taken = true;
7281
- function visit(element) {
7282
- const entry = elementToEntry.get(element);
7283
- if (entry.taken) result.push(element);
7284
- if (entry.children.length > 1) {
7285
- const set = new Set(entry.children);
7286
- entry.children = [];
7287
- let child = element.firstElementChild;
7288
- while (child && entry.children.length < set.size) {
7289
- if (set.has(child)) entry.children.push(child);
7290
- child = child.nextElementSibling;
7291
- }
7292
- child = element.shadowRoot ? element.shadowRoot.firstElementChild : null;
7293
- while (child && entry.children.length < set.size) {
7294
- if (set.has(child)) entry.children.push(child);
7295
- child = child.nextElementSibling;
7296
- }
7297
- }
7298
- entry.children.forEach(visit);
7299
- }
7300
- roots.forEach(visit);
7301
- return result;
7302
- }
7303
-
7304
- //#endregion
7305
- //#region src/locatorUtils.ts
7306
- /**
7307
- * Copyright (c) Microsoft Corporation.
7308
- *
7309
- * Licensed under the Apache License, Version 2.0 (the "License");
7310
- * you may not use this file except in compliance with the License.
7311
- * You may obtain a copy of the License at
7312
- *
7313
- * http://www.apache.org/licenses/LICENSE-2.0
7314
- *
7315
- * Unless required by applicable law or agreed to in writing, software
7316
- * distributed under the License is distributed on an "AS IS" BASIS,
7317
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
7318
- * See the License for the specific language governing permissions and
7319
- * limitations under the License.
7320
- */
7321
- function getByAttributeTextSelector(attrName, text, options) {
7322
- var _options$exact;
7323
- return `internal:attr=[${attrName}=${escapeForAttributeSelector(text, (_options$exact = options === null || options === void 0 ? void 0 : options.exact) !== null && _options$exact !== void 0 ? _options$exact : Ivya.options.exact)}]`;
7324
- }
7325
- function getByTestIdSelector(testIdAttributeName, testId) {
7326
- return `internal:testid=[${testIdAttributeName}=${escapeForAttributeSelector(testId, true)}]`;
7327
- }
7328
- function getByLabelSelector(text, options) {
7329
- var _options$exact2;
7330
- return `internal:label=${escapeForTextSelector(text, (_options$exact2 = options === null || options === void 0 ? void 0 : options.exact) !== null && _options$exact2 !== void 0 ? _options$exact2 : Ivya.options.exact)}`;
7331
- }
7332
- function getByAltTextSelector(text, options) {
7333
- return getByAttributeTextSelector("alt", text, options);
7334
- }
7335
- function getByTitleSelector(text, options) {
7336
- return getByAttributeTextSelector("title", text, options);
7337
- }
7338
- function getByPlaceholderSelector(text, options) {
7339
- return getByAttributeTextSelector("placeholder", text, options);
7340
- }
7341
- function getByTextSelector(text, options) {
7342
- var _options$exact3;
7343
- return `internal:text=${escapeForTextSelector(text, (_options$exact3 = options === null || options === void 0 ? void 0 : options.exact) !== null && _options$exact3 !== void 0 ? _options$exact3 : Ivya.options.exact)}`;
7344
- }
7345
- function getByRoleSelector(role, options = {}) {
7346
- const props = [];
7347
- if (options.checked !== void 0) props.push(["checked", String(options.checked)]);
7348
- if (options.disabled !== void 0) props.push(["disabled", String(options.disabled)]);
7349
- if (options.selected !== void 0) props.push(["selected", String(options.selected)]);
7350
- if (options.expanded !== void 0) props.push(["expanded", String(options.expanded)]);
7351
- if (options.includeHidden !== void 0) props.push(["include-hidden", String(options.includeHidden)]);
7352
- if (options.level !== void 0) props.push(["level", String(options.level)]);
7353
- if (options.name !== void 0) {
7354
- var _options$exact4;
7355
- props.push(["name", escapeForAttributeSelector(options.name, (_options$exact4 = options.exact) !== null && _options$exact4 !== void 0 ? _options$exact4 : Ivya.options.exact)]);
7356
- }
7357
- if (options.pressed !== void 0) props.push(["pressed", String(options.pressed)]);
7358
- return `internal:role=${role}${props.map(([n, v]) => `[${n}=${v}]`).join("")}`;
7359
- }
7360
-
7361
- //#endregion
7362
- //#region src/ivya.ts
7363
- /**
7364
- * Copyright (c) Microsoft Corporation.
7365
- *
7366
- * Licensed under the Apache License, Version 2.0 (the "License");
7367
- * you may not use this file except in compliance with the License.
7368
- * You may obtain a copy of the License at
7369
- *
7370
- * http://www.apache.org/licenses/LICENSE-2.0
7371
- *
7372
- * Unless required by applicable law or agreed to in writing, software
7373
- * distributed under the License is distributed on an "AS IS" BASIS,
7374
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
7375
- * See the License for the specific language governing permissions and
7376
- * limitations under the License.
7377
- */
7378
- var Ivya = class Ivya {
7379
- static create(options) {
7380
- Object.assign(Ivya.options, options);
7381
- return Ivya.singleton || (Ivya.singleton = new Ivya());
7382
- }
7383
- constructor() {
7384
- _defineProperty(
7385
- this,
7386
- /** @internal */
7387
- "_engines",
7388
- void 0
7389
- );
7390
- _defineProperty(
7391
- this,
7392
- /** @internal */
7393
- "_evaluator",
7394
- void 0
7395
- );
7396
- this._evaluator = new SelectorEvaluatorImpl(/* @__PURE__ */ new Map());
7397
- this._engines = /* @__PURE__ */ new Map();
7398
- this._engines.set("xpath", XPathEngine);
7399
- this._engines.set("xpath:light", XPathEngine);
7400
- this._engines.set("role", createRoleEngine(false));
7401
- this._engines.set("text", this._createTextEngine(true, false));
7402
- this._engines.set("text:light", this._createTextEngine(false, false));
7403
- this._engines.set("id", this._createAttributeEngine("id", true));
7404
- this._engines.set("id:light", this._createAttributeEngine("id", false));
7405
- this._engines.set("data-testid", this._createAttributeEngine("data-testid", true));
7406
- this._engines.set("data-testid:light", this._createAttributeEngine("data-testid", false));
7407
- this._engines.set("data-test-id", this._createAttributeEngine("data-test-id", true));
7408
- this._engines.set("data-test-id:light", this._createAttributeEngine("data-test-id", false));
7409
- this._engines.set("data-test", this._createAttributeEngine("data-test", true));
7410
- this._engines.set("data-test:light", this._createAttributeEngine("data-test", false));
7411
- this._engines.set("css", this._createCSSEngine());
7412
- this._engines.set("nth", { queryAll: () => [] });
7413
- this._engines.set("visible", this._createVisibleEngine());
7414
- this._engines.set("internal:control", this._createControlEngine());
7415
- this._engines.set("internal:has", this._createHasEngine());
7416
- this._engines.set("internal:has-not", this._createHasNotEngine());
7417
- this._engines.set("internal:and", { queryAll: () => [] });
7418
- this._engines.set("internal:or", { queryAll: () => [] });
7419
- this._engines.set("internal:chain", this._createInternalChainEngine());
7420
- this._engines.set("internal:label", this._createInternalLabelEngine());
7421
- this._engines.set("internal:text", this._createTextEngine(true, true));
7422
- this._engines.set("internal:has-text", this._createInternalHasTextEngine());
7423
- this._engines.set("internal:has-not-text", this._createInternalHasNotTextEngine());
7424
- this._engines.set("internal:attr", this._createNamedAttributeEngine());
7425
- this._engines.set("internal:testid", this._createNamedAttributeEngine());
7426
- this._engines.set("internal:role", createRoleEngine(true));
7427
- }
7428
- queryLocatorSelector(locator, root = document.documentElement, strict = true) {
7429
- return this.querySelector(this.parseSelector(locator), root, strict);
7430
- }
7431
- queryLocatorSelectorAll(locator, root = document.documentElement) {
7432
- return this.querySelectorAll(this.parseSelector(locator), root);
7433
- }
7434
- querySelector(selector, root, strict = true) {
7435
- const result = this.querySelectorAll(selector, root);
7436
- if (strict && result.length > 1) throw this.strictModeViolationError(selector, result);
7437
- return result[0] || null;
7438
- }
7439
- queryAllByRole(text, options, container = document.documentElement) {
7440
- const selector = this.parseSelector(getByRoleSelector(text, options));
7441
- return this.querySelectorAll(selector, container);
7442
- }
7443
- queryAllByLabelText(text, options, container = document.documentElement) {
7444
- const selector = this.parseSelector(getByLabelSelector(text, options));
7445
- return this.querySelectorAll(selector, container);
7446
- }
7447
- queryAllByTestId(text, container = document.documentElement) {
7448
- const selector = this.parseSelector(getByTestIdSelector(Ivya.options.testIdAttribute, text));
7449
- return this.querySelectorAll(selector, container);
7450
- }
7451
- queryAllByText(text, options, container = document.documentElement) {
7452
- const selector = this.parseSelector(getByTextSelector(text, options));
7453
- return this.querySelectorAll(selector, container);
7454
- }
7455
- queryAllByTitle(text, options, container = document.documentElement) {
7456
- const selector = this.parseSelector(getByTitleSelector(text, options));
7457
- return this.querySelectorAll(selector, container);
7458
- }
7459
- queryAllByPlaceholder(text, options, container = document.documentElement) {
7460
- const selector = this.parseSelector(getByPlaceholderSelector(text, options));
7461
- return this.querySelectorAll(selector, container);
7462
- }
7463
- queryAllByAltText(text, options, container = document.documentElement) {
7464
- const selector = this.parseSelector(getByAltTextSelector(text, options));
7465
- return this.querySelectorAll(selector, container);
7466
- }
7467
- queryByRole(text, options, container = document.documentElement) {
7468
- const selector = this.parseSelector(getByRoleSelector(text, options));
7469
- return this.querySelector(selector, container, false);
7470
- }
7471
- queryByLabelText(text, options, container = document.documentElement) {
7472
- const selector = this.parseSelector(getByLabelSelector(text, options));
7473
- return this.querySelector(selector, container, false);
7474
- }
7475
- queryByTestId(text, container = document.documentElement) {
7476
- const selector = this.parseSelector(getByTestIdSelector(Ivya.options.testIdAttribute, text));
7477
- return this.querySelector(selector, container, false);
7478
- }
7479
- queryByText(text, options, container = document.documentElement) {
7480
- const selector = this.parseSelector(getByTextSelector(text, options));
7481
- return this.querySelector(selector, container, false);
7482
- }
7483
- queryByTitle(text, options, container = document.documentElement) {
7484
- const selector = this.parseSelector(getByTitleSelector(text, options));
7485
- return this.querySelector(selector, container, false);
7486
- }
7487
- queryByPlaceholder(text, options, container = document.documentElement) {
7488
- const selector = this.parseSelector(getByPlaceholderSelector(text, options));
7489
- return this.querySelector(selector, container, false);
7490
- }
7491
- queryByAltText(text, options, container = document.documentElement) {
7492
- const selector = this.parseSelector(getByAltTextSelector(text, options));
7493
- return this.querySelector(selector, container, false);
7494
- }
7495
- strictModeViolationError(selector, matches) {
7496
- const infos = matches.slice(0, 10).map((m) => ({
7497
- preview: this.previewNode(m),
7498
- selector: this.generateSelectorSimple(m)
7499
- }));
7500
- const lines = infos.map((info, i) => `\n ${i + 1}) ${info.preview} aka ${asLocator("javascript", info.selector)}`);
7501
- if (infos.length < matches.length) lines.push("\n ...");
7502
- return this.createStacklessError(`strict mode violation: ${asLocator("javascript", stringifySelector(selector))} resolved to ${matches.length} elements:${lines.join("")}\n`);
7503
- }
7504
- generateSelectorSimple(targetElement, options) {
7505
- return generateSelector(this, targetElement, {
7506
- ...options,
7507
- testIdAttributeName: Ivya.options.testIdAttribute
7508
- });
7509
- }
7510
- parseSelector(selector) {
7511
- const result = parseSelector(selector);
7512
- visitAllSelectorParts(result, (part) => {
7513
- if (!this._engines.has(part.name)) throw this.createStacklessError(`Unknown engine "${part.name}" while parsing selector ${selector}`);
7514
- });
7515
- return result;
7516
- }
7517
- previewNode(node) {
7518
- if (node.nodeType === Node.TEXT_NODE) return oneLine(`#text=${node.nodeValue || ""}`);
7519
- if (node.nodeType !== Node.ELEMENT_NODE) return oneLine(`<${node.nodeName.toLowerCase()} />`);
7520
- const element = node;
7521
- const attrs = [];
7522
- for (let i = 0; i < element.attributes.length; i++) {
7523
- const { name, value } = element.attributes[i];
7524
- if (name === "style") continue;
7525
- if (!value && booleanAttributes.has(name)) attrs.push(` ${name}`);
7526
- else attrs.push(` ${name}="${value}"`);
7527
- }
7528
- attrs.sort((a, b) => a.length - b.length);
7529
- const attrText = trimStringWithEllipsis(attrs.join(""), 500);
7530
- if (autoClosingTags.has(element.nodeName)) return oneLine(`<${element.nodeName.toLowerCase()}${attrText}/>`);
7531
- const children = element.childNodes;
7532
- let onlyText = false;
7533
- if (children.length <= 5) {
7534
- onlyText = true;
7535
- for (let i = 0; i < children.length; i++) onlyText = onlyText && children[i].nodeType === Node.TEXT_NODE;
7536
- }
7537
- const text = onlyText ? element.textContent || "" : children.length ? "…" : "";
7538
- return oneLine(`<${element.nodeName.toLowerCase()}${attrText}>${trimStringWithEllipsis(text, 50)}</${element.nodeName.toLowerCase()}>`);
7539
- }
7540
- querySelectorAll(selector, root) {
7541
- if (selector.capture !== void 0) {
7542
- if (selector.parts.some((part) => part.name === "nth")) throw this.createStacklessError(`Can't query n-th element in a request with the capture.`);
7543
- const withHas = { parts: selector.parts.slice(0, selector.capture + 1) };
7544
- if (selector.capture < selector.parts.length - 1) {
7545
- const parsed = { parts: selector.parts.slice(selector.capture + 1) };
7546
- const has = {
7547
- name: "internal:has",
7548
- body: { parsed },
7549
- source: stringifySelector(parsed)
7550
- };
7551
- withHas.parts.push(has);
7552
- }
7553
- return this.querySelectorAll(withHas, root);
7554
- }
7555
- if (!root.querySelectorAll) throw this.createStacklessError("Node is not queryable.");
7556
- if (selector.capture !== void 0) throw this.createStacklessError("Internal error: there should not be a capture in the selector.");
7557
- if (root.nodeType === 11 && selector.parts.length === 1 && selector.parts[0].name === "css" && selector.parts[0].source === ":scope") return [root];
7558
- this._evaluator.begin();
7559
- try {
7560
- let roots = new Set([root]);
7561
- for (const part of selector.parts) if (part.name === "nth") roots = this._queryNth(roots, part);
7562
- else if (part.name === "internal:and") {
7563
- const andElements = this.querySelectorAll(part.body.parsed, root);
7564
- roots = new Set(andElements.filter((e) => roots.has(e)));
7565
- } else if (part.name === "internal:or") {
7566
- const orElements = this.querySelectorAll(part.body.parsed, root);
7567
- roots = new Set(sortInDOMOrder(new Set([...roots, ...orElements])));
7568
- } else if (kLayoutSelectorNames.includes(part.name)) roots = this._queryLayoutSelector(roots, part, root);
7569
- else {
7570
- const next = /* @__PURE__ */ new Set();
7571
- for (const root of roots) {
7572
- const all = this._queryEngineAll(part, root);
7573
- for (const one of all) next.add(one);
7574
- }
7575
- roots = next;
7576
- }
7577
- return [...roots];
7578
- } finally {
7579
- this._evaluator.end();
7580
- }
7581
- }
7582
- _queryEngineAll(part, root) {
7583
- const result = this._engines.get(part.name).queryAll(root, part.body);
7584
- for (const element of result) if (!("nodeName" in element)) throw this.createStacklessError(`Expected a Node but got ${Object.prototype.toString.call(element)}`);
7585
- return result;
7586
- }
7587
- _queryNth(elements, part) {
7588
- const list = [...elements];
7589
- let nth = +part.body;
7590
- if (nth === -1) nth = list.length - 1;
7591
- return new Set(list.slice(nth, nth + 1));
7592
- }
7593
- _queryLayoutSelector(elements, part, originalRoot) {
7594
- const name = part.name;
7595
- const body = part.body;
7596
- const result = [];
7597
- const inner = this.querySelectorAll(body.parsed, originalRoot);
7598
- for (const element of elements) {
7599
- const score = layoutSelectorScore(name, element, inner, body.distance);
7600
- if (score !== void 0) result.push({
7601
- element,
7602
- score
7603
- });
7604
- }
7605
- result.sort((a, b) => a.score - b.score);
7606
- return new Set(result.map((r) => r.element));
7607
- }
7608
- createStacklessError(message) {
7609
- if (Ivya.options.browser === "firefox") {
7610
- const error = /* @__PURE__ */ new Error(`Error: ${message}`);
7611
- error.stack = "";
7612
- return error;
7613
- }
7614
- const error = new Error(message);
7615
- delete error.stack;
7616
- return error;
7617
- }
7618
- _createTextEngine(shadow, internal) {
7619
- const queryAll = (root, selector) => {
7620
- const { matcher, kind } = createTextMatcher(selector, internal);
7621
- const result = [];
7622
- let lastDidNotMatchSelf = null;
7623
- const appendElement = (element) => {
7624
- if (kind === "lax" && lastDidNotMatchSelf && lastDidNotMatchSelf.contains(element)) return false;
7625
- const matches = elementMatchesText(this._evaluator._cacheText, element, matcher);
7626
- if (matches === "none") lastDidNotMatchSelf = element;
7627
- if (matches === "self" || matches === "selfAndChildren" && kind === "strict" && !internal) result.push(element);
7628
- };
7629
- if (root.nodeType === Node.ELEMENT_NODE) appendElement(root);
7630
- const elements = this._evaluator._queryCSS({
7631
- scope: root,
7632
- pierceShadow: shadow
7633
- }, "*");
7634
- for (const element of elements) appendElement(element);
7635
- return result;
7636
- };
7637
- return { queryAll };
7638
- }
7639
- _createAttributeEngine(attribute, shadow) {
7640
- const toCSS = (selector) => {
7641
- return [{ simples: [{
7642
- selector: {
7643
- css: `[${attribute}=${JSON.stringify(selector)}]`,
7644
- functions: []
7645
- },
7646
- combinator: ""
7647
- }] }];
7648
- };
7649
- return { queryAll: (root, selector) => {
7650
- return this._evaluator.query({
7651
- scope: root,
7652
- pierceShadow: shadow
7653
- }, toCSS(selector));
7654
- } };
7655
- }
7656
- _createCSSEngine() {
7657
- return { queryAll: (root, body) => {
7658
- return this._evaluator.query({
7659
- scope: root,
7660
- pierceShadow: true
7661
- }, body);
7662
- } };
7663
- }
7664
- _createNamedAttributeEngine() {
7665
- const queryAll = (root, selector) => {
7666
- const parsed = parseAttributeSelector(selector);
7667
- if (parsed.name || parsed.attributes.length !== 1) throw new Error(`Malformed attribute selector: ${selector}`);
7668
- const { name, value, caseSensitive } = parsed.attributes[0];
7669
- const lowerCaseValue = caseSensitive ? null : value.toLowerCase();
7670
- let matcher;
7671
- if (value instanceof RegExp) matcher = (s) => !!s.match(value);
7672
- else if (caseSensitive) matcher = (s) => s === value;
7673
- else matcher = (s) => s.toLowerCase().includes(lowerCaseValue);
7674
- return this._evaluator._queryCSS({
7675
- scope: root,
7676
- pierceShadow: true
7677
- }, `[${name}]`).filter((e) => matcher(e.getAttribute(name)));
7678
- };
7679
- return { queryAll };
7680
- }
7681
- _createVisibleEngine() {
7682
- const queryAll = (root, body) => {
7683
- if (root.nodeType !== 1) return [];
7684
- return isElementVisible(root) === Boolean(body) ? [root] : [];
7685
- };
7686
- return { queryAll };
7687
- }
7688
- _createControlEngine() {
7689
- const queryAll = (root, body) => {
7690
- if (body === "enter-frame" && root instanceof HTMLIFrameElement) {
7691
- var _root$contentDocument;
7692
- const doc = (_root$contentDocument = root.contentDocument) === null || _root$contentDocument === void 0 ? void 0 : _root$contentDocument.documentElement;
7693
- return doc ? [doc] : [];
7694
- }
7695
- return [];
7696
- };
7697
- return { queryAll };
7698
- }
7699
- _createHasEngine() {
7700
- const queryAll = (root, body) => {
7701
- if (root.nodeType !== 1) return [];
7702
- return !!this.querySelector(body.parsed, root, false) ? [root] : [];
7703
- };
7704
- return { queryAll };
7705
- }
7706
- _createHasNotEngine() {
7707
- const queryAll = (root, body) => {
7708
- if (root.nodeType !== 1) return [];
7709
- return !!this.querySelector(body.parsed, root, false) ? [] : [root];
7710
- };
7711
- return { queryAll };
7712
- }
7713
- _createInternalChainEngine() {
7714
- const queryAll = (root, body) => {
7715
- return this.querySelectorAll(body.parsed, root);
7716
- };
7717
- return { queryAll };
7718
- }
7719
- _createInternalLabelEngine() {
7720
- return { queryAll: (root, selector) => {
7721
- const { matcher } = createTextMatcher(selector, true);
7722
- return this._evaluator._queryCSS({
7723
- scope: root,
7724
- pierceShadow: true
7725
- }, "*").filter((element) => {
7726
- return getElementLabels(this._evaluator._cacheText, element).some((label) => matcher(label));
7727
- });
7728
- } };
7729
- }
7730
- _createInternalHasTextEngine() {
7731
- return { queryAll: (root, selector) => {
7732
- if (root.nodeType !== 1) return [];
7733
- const element = root;
7734
- const text = elementText(this._evaluator._cacheText, element);
7735
- const { matcher } = createTextMatcher(selector, true);
7736
- return matcher(text) ? [element] : [];
7737
- } };
7738
- }
7739
- _createInternalHasNotTextEngine() {
7740
- return { queryAll: (root, selector) => {
7741
- if (root.nodeType !== 1) return [];
7742
- const element = root;
7743
- const text = elementText(this._evaluator._cacheText, element);
7744
- const { matcher } = createTextMatcher(selector, true);
7745
- return matcher(text) ? [] : [element];
7746
- } };
7747
- }
7748
- };
7749
- _defineProperty(Ivya, "options", {
7750
- testIdAttribute: "data-testid",
7751
- browser: "chromium",
7752
- exact: false
7753
- });
7754
- _defineProperty(Ivya, "singleton", null);
7755
- function oneLine(s) {
7756
- return s.replace(/\n/g, "↵").replace(/\t/g, "⇆");
7757
- }
7758
- const booleanAttributes = new Set([
7759
- "checked",
7760
- "selected",
7761
- "disabled",
7762
- "readonly",
7763
- "multiple"
7764
- ]);
7765
- const autoClosingTags = new Set([
7766
- "AREA",
7767
- "BASE",
7768
- "BR",
7769
- "COL",
7770
- "COMMAND",
7771
- "EMBED",
7772
- "HR",
7773
- "IMG",
7774
- "INPUT",
7775
- "KEYGEN",
7776
- "LINK",
7777
- "MENUITEM",
7778
- "META",
7779
- "PARAM",
7780
- "SOURCE",
7781
- "TRACK",
7782
- "WBR"
7783
- ]);
7784
- function cssUnquote(s) {
7785
- s = s.substring(1, s.length - 1);
7786
- if (!s.includes("\\")) return s;
7787
- const r = [];
7788
- let i = 0;
7789
- while (i < s.length) {
7790
- if (s[i] === "\\" && i + 1 < s.length) i++;
7791
- r.push(s[i++]);
7792
- }
7793
- return r.join("");
7794
- }
7795
- function createTextMatcher(selector, internal) {
7796
- if (selector[0] === "/" && selector.lastIndexOf("/") > 0) {
7797
- const lastSlash = selector.lastIndexOf("/");
7798
- const re = new RegExp(selector.substring(1, lastSlash), selector.substring(lastSlash + 1));
7799
- return {
7800
- matcher: (elementText) => re.test(elementText.full),
7801
- kind: "regex"
7802
- };
7803
- }
7804
- const unquote = internal ? JSON.parse.bind(JSON) : cssUnquote;
7805
- let strict = false;
7806
- if (selector.length > 1 && selector[0] === "\"" && selector[selector.length - 1] === "\"") {
7807
- selector = unquote(selector);
7808
- strict = true;
7809
- } else if (internal && selector.length > 1 && selector[0] === "\"" && selector[selector.length - 2] === "\"" && selector[selector.length - 1] === "i") {
7810
- selector = unquote(selector.substring(0, selector.length - 1));
7811
- strict = false;
7812
- } else if (internal && selector.length > 1 && selector[0] === "\"" && selector[selector.length - 2] === "\"" && selector[selector.length - 1] === "s") {
7813
- selector = unquote(selector.substring(0, selector.length - 1));
7814
- strict = true;
7815
- } else if (selector.length > 1 && selector[0] === "'" && selector[selector.length - 1] === "'") {
7816
- selector = unquote(selector);
7817
- strict = true;
7818
- }
7819
- selector = normalizeWhiteSpace(selector);
7820
- if (strict) {
7821
- if (internal) return {
7822
- kind: "strict",
7823
- matcher: (elementText) => elementText.normalized === selector
7824
- };
7825
- const strictTextNodeMatcher = (elementText) => {
7826
- if (!selector && !elementText.immediate.length) return true;
7827
- return elementText.immediate.some((s) => normalizeWhiteSpace(s) === selector);
7828
- };
7829
- return {
7830
- matcher: strictTextNodeMatcher,
7831
- kind: "strict"
7832
- };
7833
- }
7834
- selector = selector.toLowerCase();
7835
- return {
7836
- kind: "lax",
7837
- matcher: (elementText) => elementText.normalized.toLowerCase().includes(selector)
7838
- };
7839
- }
7840
- const XPathEngine = { queryAll(root, selector) {
7841
- if (selector.startsWith("/") && root.nodeType !== Node.DOCUMENT_NODE) selector = `.${selector}`;
7842
- const result = [];
7843
- const document = root.ownerDocument || root;
7844
- if (!document) return result;
7845
- const it = document.evaluate(selector, root, null, XPathResult.ORDERED_NODE_ITERATOR_TYPE);
7846
- for (let node = it.iterateNext(); node; node = it.iterateNext()) if (node.nodeType === Node.ELEMENT_NODE) result.push(node);
7847
- return result;
7848
- } };
7849
-
7850
3328
  function defineBrowserCommand(fn) {
7851
3329
  return fn;
7852
3330
  }
@@ -7918,4 +3396,4 @@ function defineBrowserProvider(options) {
7918
3396
  };
7919
3397
  }
7920
3398
 
7921
- export { asLocator, createBrowserServer, defineBrowserCommand, defineBrowserProvider, parseKeyDef, resolveScreenshotPath };
3399
+ export { createBrowserServer, defineBrowserCommand, defineBrowserProvider, parseKeyDef, resolveScreenshotPath };