@validate.qa/runner 1.0.103 → 1.0.104

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 (32) hide show
  1. package/README.md +7 -2
  2. package/dist/{chunk-YI3ZC2XI.mjs → chunk-6N4QCCEH.mjs} +2 -2
  3. package/dist/{chunk-CMM4DOXC.mjs → chunk-EBWZ7NGU.mjs} +11 -8
  4. package/dist/{chunk-OTKJZIOD.mjs → chunk-EKY6CN4Y.mjs} +659 -122
  5. package/dist/{chunk-CG75I3ZZ.mjs → chunk-MEE4DXRX.mjs} +13437 -1386
  6. package/dist/{chunk-WVLBQDFP.mjs → chunk-OXDU6ARE.mjs} +8 -2
  7. package/dist/{chunk-AAUHXGDS.mjs → chunk-PUMRAFJS.mjs} +37 -13
  8. package/dist/{chunk-BECCGMU4.mjs → chunk-TGVKABFN.mjs} +45 -3
  9. package/dist/{chunk-YIM746WO.mjs → chunk-TS4OUJVT.mjs} +1 -1
  10. package/dist/{chunk-NNJYKR3O.mjs → chunk-UR7KDOTP.mjs} +29 -6
  11. package/dist/{chunk-36AWDKPL.mjs → chunk-UZ67UBXH.mjs} +14 -3
  12. package/dist/{chunk-7SH3COMQ.mjs → chunk-VWE3SQ2Z.mjs} +1 -1
  13. package/dist/{chunk-TK5KEGNW.mjs → chunk-XXZZMG6Q.mjs} +15 -3
  14. package/dist/cli.js +20962 -7343
  15. package/dist/cli.mjs +1159 -245
  16. package/dist/credential-mcp-server.js +671 -127
  17. package/dist/credential-mcp-server.mjs +3 -3
  18. package/dist/discovery-mcp-server.js +710 -129
  19. package/dist/discovery-mcp-server.mjs +4 -4
  20. package/dist/dist-BXHLIG5H.mjs +17 -0
  21. package/dist/inbox-mcp-server.js +664 -123
  22. package/dist/inbox-mcp-server.mjs +3 -3
  23. package/dist/route-seed-worker.js +671 -124
  24. package/dist/route-seed-worker.mjs +3 -3
  25. package/dist/run-write-guard-preload.js +713 -125
  26. package/dist/run-write-guard-preload.mjs +4 -4
  27. package/dist/run-write-guard.js +713 -125
  28. package/dist/run-write-guard.mjs +3 -3
  29. package/dist/tar-extract-child.js +38 -14
  30. package/dist/tar-extract-child.mjs +2 -2
  31. package/package.json +3 -2
  32. package/dist/dist-2G7YTNH5.mjs +0 -17
@@ -391,7 +391,13 @@ function buildSandboxedEnv(processEnv, testVariables) {
391
391
  return env;
392
392
  }
393
393
  function escapeEnvValue(value) {
394
- const escaped = value.replace(/\\/g, "\\\\").replace(/"/g, '\\"').replace(/\n/g, "\\n").replace(/\r/g, "\\r");
394
+ if (!value.includes("\r")) {
395
+ for (const quote of ["'", "`"]) {
396
+ if (!value.includes(quote))
397
+ return `${quote}${value}${quote}`;
398
+ }
399
+ }
400
+ const escaped = value.replace(/"/g, '\\"').replace(/\n/g, "\\n").replace(/\r/g, "\\r");
395
401
  return `"${escaped}"`;
396
402
  }
397
403
  var API_PREFIX, SUPPORT_EMAIL, PLATFORM_ADMIN_EMAIL, NOREPLY_EMAIL, EMAIL_FROM_IDENTITIES, ADMIN_EMAIL_LIMITS, AUDIO_CHUNK_DURATION_MS, ACTION_BATCH_INTERVAL_MS, ACTION_BATCH_MAX_SIZE, MOBILE_SCREENSHOT_INTERVAL_MS, MOBILE_ACTION_BATCH_MAX_SIZE, MOBILE_BRIDGE_PORT, MOBILE_SNAPSHOT_MAX_SIZE_BYTES, MOBILE_APP_ID_MAX_LENGTH, MOBILE_APP_ID_PATTERN, MOBILE_FOCUS_SETTLE_MS, MOBILE_POST_ACTION_SETTLE_MS, MOBILE_COLD_BOOT_SETTLE_MS, MOBILE_DEFAULT_SWIPE_DISTANCE, MOBILE_MAX_SWIPE_DISTANCE, SELECTOR_PRIORITIES, MOBILE_STEP_TYPES, BLOCKED_ENV_VARS, SPAWNED_ENV_ALLOWLIST, MAX_PLAYWRIGHT_CODE_LENGTH;
@@ -3313,6 +3319,65 @@ var init_discovery_har_decision = __esm({
3313
3319
  }
3314
3320
  });
3315
3321
 
3322
+ // ../shared/dist/regex-literal.js
3323
+ function opensRegexLiteral(code, i) {
3324
+ let j = i - 1;
3325
+ while (j >= 0 && (code[j] === " " || code[j] === " "))
3326
+ j--;
3327
+ if (j < 0 || code[j] === "\n" || code[j] === "\r")
3328
+ return true;
3329
+ return regexOpensAfter(code, j);
3330
+ }
3331
+ function regexOpensAfter(code, j) {
3332
+ const prev = code[j];
3333
+ if (REGEX_OPENERS.includes(prev))
3334
+ return !((prev === "+" || prev === "-") && code[j - 1] === prev);
3335
+ if (!/[a-z]/.test(prev))
3336
+ return false;
3337
+ let start = j;
3338
+ while (start > 0 && /[A-Za-z0-9_$]/.test(code[start - 1]))
3339
+ start--;
3340
+ if (start > 0 && code[start - 1] === ".")
3341
+ return false;
3342
+ return REGEX_KEYWORDS.has(code.slice(start, j + 1));
3343
+ }
3344
+ function regexLiteralEnd(code, i) {
3345
+ let k = i + 1;
3346
+ let inClass = false;
3347
+ while (k < code.length) {
3348
+ const ch = code[k];
3349
+ if (ch === "\n" || ch === "\r")
3350
+ return k;
3351
+ if (ch === "\\") {
3352
+ if (code[k + 1] === "\n" || code[k + 1] === "\r")
3353
+ return k + 1;
3354
+ k += 2;
3355
+ continue;
3356
+ }
3357
+ if (inClass) {
3358
+ if (ch === "]")
3359
+ inClass = false;
3360
+ } else if (ch === "[") {
3361
+ inClass = true;
3362
+ } else if (ch === "/") {
3363
+ k++;
3364
+ while (k < code.length && /[a-z]/i.test(code[k]))
3365
+ k++;
3366
+ return k;
3367
+ }
3368
+ k++;
3369
+ }
3370
+ return code.length;
3371
+ }
3372
+ var REGEX_OPENERS, REGEX_KEYWORDS;
3373
+ var init_regex_literal = __esm({
3374
+ "../shared/dist/regex-literal.js"() {
3375
+ "use strict";
3376
+ REGEX_OPENERS = "(,=:[!&|?{};+-*%~^<>";
3377
+ REGEX_KEYWORDS = /* @__PURE__ */ new Set(["return", "typeof", "case", "do", "else", "in", "of", "new", "delete", "void", "throw", "await", "yield", "instanceof"]);
3378
+ }
3379
+ });
3380
+
3316
3381
  // ../shared/dist/assertion-classifier.js
3317
3382
  function isButtonOrLinkLocator(line) {
3318
3383
  return /getByRole\s*\(\s*['"`](?:button|link)['"`]/i.test(line);
@@ -3621,7 +3686,7 @@ function classifyMobileAssertValue(value, context = {}) {
3621
3686
  return { tier: 2, reason: t2[1] };
3622
3687
  return null;
3623
3688
  }
3624
- function regexLiteralEnd(text, start) {
3689
+ function regexLiteralEnd2(text, start) {
3625
3690
  let inClass = false;
3626
3691
  for (let i = start + 1; i < text.length; i++) {
3627
3692
  const ch = text[i];
@@ -3647,7 +3712,7 @@ function parenDepthDelta(text) {
3647
3712
  let depth = 0;
3648
3713
  let quote = null;
3649
3714
  let escaped = false;
3650
- let prev = "";
3715
+ let prevAt = -1;
3651
3716
  let noRegexUntil = -1;
3652
3717
  for (let i = 0; i < text.length; i++) {
3653
3718
  const ch = text[i];
@@ -3662,7 +3727,7 @@ function parenDepthDelta(text) {
3662
3727
  if (quote) {
3663
3728
  if (ch === quote) {
3664
3729
  quote = null;
3665
- prev = ch;
3730
+ prevAt = i;
3666
3731
  }
3667
3732
  continue;
3668
3733
  }
@@ -3681,11 +3746,11 @@ function parenDepthDelta(text) {
3681
3746
  i = close + 1;
3682
3747
  continue;
3683
3748
  }
3684
- if (i > noRegexUntil && (prev === "" || REGEX_PRECEDER.has(prev))) {
3685
- const end = regexLiteralEnd(text, i);
3749
+ if (i > noRegexUntil && (prevAt < 0 || regexOpensAfter(text, prevAt))) {
3750
+ const end = regexLiteralEnd2(text, i);
3686
3751
  if (end > 0) {
3687
3752
  i = end - 1;
3688
- prev = "a";
3753
+ prevAt = end - 1;
3689
3754
  continue;
3690
3755
  }
3691
3756
  const nl = text.indexOf("\n", i);
@@ -3697,14 +3762,15 @@ function parenDepthDelta(text) {
3697
3762
  else if (ch === ")")
3698
3763
  depth--;
3699
3764
  if (ch !== " " && ch !== " " && ch !== "\n")
3700
- prev = ch;
3765
+ prevAt = i;
3701
3766
  }
3702
3767
  return depth;
3703
3768
  }
3704
- var PENDING_STATE_TEXT_REASON, PENDING_STATE_TEXT, TIER1_PATTERNS, TIER2_PATTERNS, TIER4_PATTERNS, SINGLE_QUOTED, DOUBLE_QUOTED, BACKTICK_QUOTED, QUOTED_BODY, LOCATOR_ARG_VALUE, LOCATOR_TEXT_ARG_PATTERNS, EXPECT_TOKEN, REGEX_PRECEDER;
3769
+ var PENDING_STATE_TEXT_REASON, PENDING_STATE_TEXT, TIER1_PATTERNS, TIER2_PATTERNS, TIER4_PATTERNS, SINGLE_QUOTED, DOUBLE_QUOTED, BACKTICK_QUOTED, QUOTED_BODY, LOCATOR_ARG_VALUE, LOCATOR_TEXT_ARG_PATTERNS, EXPECT_TOKEN;
3705
3770
  var init_assertion_classifier = __esm({
3706
3771
  "../shared/dist/assertion-classifier.js"() {
3707
3772
  "use strict";
3773
+ init_regex_literal();
3708
3774
  PENDING_STATE_TEXT_REASON = 'pending-state text ("Sending...") is transient';
3709
3775
  PENDING_STATE_TEXT = /\b(?!(?:Billing|Shipping|Pricing|Listing|Meeting|Morning|Evening|Warning|Building|Training|Marketing|Parking|Housing|Reading|Rating|Heading|Ceiling|Wedding|Clothing|Painting|Setting)\b)[A-Z][a-z]{2,}ing(?:\.{3}|…)/;
3710
3776
  TIER1_PATTERNS = [
@@ -3781,7 +3847,6 @@ var init_assertion_classifier = __esm({
3781
3847
  new RegExp(`\\.filter\\s*\\(\\s*\\{[^}]{0,200}?\\bhasText\\s*:\\s*${LOCATOR_ARG_VALUE}`, "gi")
3782
3848
  ];
3783
3849
  EXPECT_TOKEN = /\bexpect\s*[.(]/;
3784
- REGEX_PRECEDER = /* @__PURE__ */ new Set(["(", ",", "=", ":", "[", "!", "&", "|", "?", "{", "}", ";", "+", "-", "*", "%", "<", ">", "~", "^"]);
3785
3850
  }
3786
3851
  });
3787
3852
 
@@ -3789,14 +3854,80 @@ var init_assertion_classifier = __esm({
3789
3854
  function isExpectCall(code) {
3790
3855
  return EXPECT_CALL_RE.test(code);
3791
3856
  }
3857
+ function specCallsExpect(code) {
3858
+ if (EXPECT_CALL_RE.test(code) || SPACED_EXPECT_CALL_RE.test(code) || CONFIGURE_IN_PLACE_RE.test(code))
3859
+ return true;
3860
+ const names = /* @__PURE__ */ new Set();
3861
+ for (const clause of code.matchAll(IMPORT_CLAUSE_RE)) {
3862
+ for (const alias of clause[1].matchAll(EXPECT_ALIAS_RE))
3863
+ names.add(alias[1]);
3864
+ }
3865
+ for (const decl of code.matchAll(DERIVED_EXPECT_DECL_RE)) {
3866
+ if (names.size >= MAX_DERIVED_EXPECT_NAMES)
3867
+ break;
3868
+ const base2 = decl[2];
3869
+ if (base2 === void 0 || base2 === "expect" || names.has(base2))
3870
+ names.add(decl[1]);
3871
+ }
3872
+ names.delete("expect");
3873
+ if (names.size === 0)
3874
+ return false;
3875
+ const alts = [...names].map((n) => n.replace(/\$/g, "\\$")).join("|");
3876
+ return new RegExp(String.raw`(?<![\w$.])(?:${alts})${EXPECT_MODIFIERS}`).test(code);
3877
+ }
3792
3878
  function asPlainExpect(code) {
3793
3879
  return code.replace(/\bexpect\.soft(\s*\()/g, "expect$1");
3794
3880
  }
3795
- var EXPECT_CALL_RE;
3881
+ function expectAliasNames(code) {
3882
+ if (!MAY_ALIAS.test(code))
3883
+ return [];
3884
+ const imported = [];
3885
+ for (const m of code.matchAll(PW_IMPORT)) {
3886
+ for (const a of m[1].matchAll(IMPORT_AS_EXPECT))
3887
+ imported.push(a[1]);
3888
+ }
3889
+ const known = /* @__PURE__ */ new Set(["expect", ...imported]);
3890
+ const derived = [];
3891
+ for (const m of code.matchAll(DECLARATION)) {
3892
+ const rhs = DERIVED.exec(code.slice(m.index + m[0].length, m.index + m[0].length + 200));
3893
+ if (!rhs)
3894
+ continue;
3895
+ const base2 = rhs[1];
3896
+ const fromKnown = base2 === void 0 || (rhs[2] ? base2 === "test" : known.has(base2));
3897
+ if (!fromKnown || m[1] === "expect")
3898
+ continue;
3899
+ known.add(m[1]);
3900
+ derived.push(m[1]);
3901
+ }
3902
+ const fromImports = LOCAL_EXPECT.test(code) ? [] : imported;
3903
+ return [.../* @__PURE__ */ new Set([...fromImports, ...derived])].filter((n) => n !== "expect");
3904
+ }
3905
+ function withExpectAliasesAsExpect(code) {
3906
+ let out = code;
3907
+ for (const name of expectAliasNames(code)) {
3908
+ const escaped = name.replace(/\$/g, "\\$");
3909
+ out = out.replace(new RegExp(`(?<![\\w$.])${escaped}(?=\\s{0,20}(?:\\.\\s{0,20}(?:soft|poll)\\s{0,20})?\\()`, "g"), "expect");
3910
+ }
3911
+ return out;
3912
+ }
3913
+ var EXPECT_CALL_RE, EXPECT_MODIFIERS, SPACED_EXPECT_CALL_RE, CONFIGURE_IN_PLACE_RE, IMPORT_CLAUSE_RE, EXPECT_ALIAS_RE, DERIVED_EXPECT_DECL_RE, MAX_DERIVED_EXPECT_NAMES, PW_IMPORT, IMPORT_AS_EXPECT, DECLARATION, DERIVED, LOCAL_EXPECT, MAY_ALIAS;
3796
3914
  var init_expect_call = __esm({
3797
3915
  "../shared/dist/expect-call.js"() {
3798
3916
  "use strict";
3799
3917
  EXPECT_CALL_RE = /\bexpect(?:\.(?:soft|poll))?\s*\(/;
3918
+ EXPECT_MODIFIERS = String.raw`\s*(?:\.\s*soft\s*)?(?:\.\s*poll\s*)?\(`;
3919
+ SPACED_EXPECT_CALL_RE = new RegExp(String.raw`\bexpect${EXPECT_MODIFIERS}`);
3920
+ CONFIGURE_IN_PLACE_RE = new RegExp(String.raw`\bexpect\s*\.\s*configure\s*\(\s*(?:\{[^{}()]{0,500}\}\s*)?\)${EXPECT_MODIFIERS}`);
3921
+ IMPORT_CLAUSE_RE = /\bimport\s+(?:type\s+)?\{([^{}]{0,2000})\}/g;
3922
+ EXPECT_ALIAS_RE = /\bexpect\s+as\s+([A-Za-z_$][\w$]*)/g;
3923
+ DERIVED_EXPECT_DECL_RE = /\b(?:const|let|var)\s+([A-Za-z_$][\w$]*)\s*=\s*(?:([A-Za-z_$][\w$]*)\s*\.\s*(?:configure|extend)|mergeExpects)\s*\(/g;
3924
+ MAX_DERIVED_EXPECT_NAMES = 20;
3925
+ PW_IMPORT = /\bimport\s{0,20}\{([^}]{0,4000})\}\s{0,20}from\s{0,20}['"]@playwright\/test['"]/g;
3926
+ IMPORT_AS_EXPECT = /(?:^|,)\s{0,20}expect\s{1,20}as\s{1,20}([A-Za-z_$][\w$]{0,99})\s{0,20}(?=,|$)/g;
3927
+ DECLARATION = /\b(?:const|let|var)\s{1,20}([A-Za-z_$][\w$]{0,99})\s{0,20}(?::[^=;\n]{0,200})?=\s{0,20}/g;
3928
+ DERIVED = /^(?:mergeExpects\s{0,20}\(|([A-Za-z_$][\w$]{0,99})(\s{0,20}\.\s{0,20}expect)?\s{0,20}\.\s{0,20}(?:configure|extend)\s{0,20}\(|test\s{0,20}\.\s{0,20}expect\b(?!\s{0,20}[.(]))/;
3929
+ LOCAL_EXPECT = /\b(?:const|let|var)\s{1,20}expect\b/;
3930
+ MAY_ALIAS = /\bexpect\s{1,20}as\b|\.\s{0,20}(?:configure|extend)\s{0,20}\(|\bmergeExpects\b|\btest\s{0,20}\.\s{0,20}expect\b/;
3800
3931
  }
3801
3932
  });
3802
3933
 
@@ -3826,65 +3957,6 @@ var init_braced_clause = __esm({
3826
3957
  }
3827
3958
  });
3828
3959
 
3829
- // ../shared/dist/regex-literal.js
3830
- function opensRegexLiteral(code, i) {
3831
- let j = i - 1;
3832
- while (j >= 0 && (code[j] === " " || code[j] === " "))
3833
- j--;
3834
- if (j < 0 || code[j] === "\n" || code[j] === "\r")
3835
- return true;
3836
- return regexOpensAfter(code, j);
3837
- }
3838
- function regexOpensAfter(code, j) {
3839
- const prev = code[j];
3840
- if (REGEX_OPENERS.includes(prev))
3841
- return !((prev === "+" || prev === "-") && code[j - 1] === prev);
3842
- if (!/[a-z]/.test(prev))
3843
- return false;
3844
- let start = j;
3845
- while (start > 0 && /[A-Za-z0-9_$]/.test(code[start - 1]))
3846
- start--;
3847
- if (start > 0 && code[start - 1] === ".")
3848
- return false;
3849
- return REGEX_KEYWORDS.has(code.slice(start, j + 1));
3850
- }
3851
- function regexLiteralEnd2(code, i) {
3852
- let k = i + 1;
3853
- let inClass = false;
3854
- while (k < code.length) {
3855
- const ch = code[k];
3856
- if (ch === "\n" || ch === "\r")
3857
- return k;
3858
- if (ch === "\\") {
3859
- if (code[k + 1] === "\n" || code[k + 1] === "\r")
3860
- return k + 1;
3861
- k += 2;
3862
- continue;
3863
- }
3864
- if (inClass) {
3865
- if (ch === "]")
3866
- inClass = false;
3867
- } else if (ch === "[") {
3868
- inClass = true;
3869
- } else if (ch === "/") {
3870
- k++;
3871
- while (k < code.length && /[a-z]/i.test(code[k]))
3872
- k++;
3873
- return k;
3874
- }
3875
- k++;
3876
- }
3877
- return code.length;
3878
- }
3879
- var REGEX_OPENERS, REGEX_KEYWORDS;
3880
- var init_regex_literal = __esm({
3881
- "../shared/dist/regex-literal.js"() {
3882
- "use strict";
3883
- REGEX_OPENERS = "(,=:[!&|?{};+-*%~^<>";
3884
- REGEX_KEYWORDS = /* @__PURE__ */ new Set(["return", "typeof", "case", "do", "else", "in", "of", "new", "delete", "void", "throw", "await", "yield", "instanceof"]);
3885
- }
3886
- });
3887
-
3888
3960
  // ../shared/dist/assertion-proof.js
3889
3961
  function fragmentKindOf(literal, fromText) {
3890
3962
  const text = literal.trim();
@@ -4043,7 +4115,7 @@ function stripCoverageComments(code) {
4043
4115
  let copyFrom = 0;
4044
4116
  let i = 0;
4045
4117
  let quote = null;
4046
- let prev = "";
4118
+ let prevAt = -1;
4047
4119
  let noRegexBefore = 0;
4048
4120
  while (i < code.length) {
4049
4121
  const ch = code[i];
@@ -4054,17 +4126,18 @@ function stripCoverageComments(code) {
4054
4126
  }
4055
4127
  if (ch === quote || ch === "\n" && quote !== "`") {
4056
4128
  quote = null;
4057
- prev = ch === "\n" ? prev : ch;
4129
+ if (ch !== "\n")
4130
+ prevAt = i;
4058
4131
  }
4059
4132
  i++;
4060
4133
  continue;
4061
4134
  }
4062
4135
  const next = code[i + 1];
4063
- if (ch === "/" && next !== "*" && next !== "/" && i >= noRegexBefore && (prev === "" || REGEX_PRECEDER2.includes(prev))) {
4136
+ if (ch === "/" && next !== "*" && next !== "/" && i >= noRegexBefore && (prevAt < 0 || regexOpensAfter(code, prevAt))) {
4064
4137
  const end = regexLiteralEnd3(code, i);
4065
4138
  if (end > 0) {
4066
4139
  i = end;
4067
- prev = "a";
4140
+ prevAt = end - 1;
4068
4141
  continue;
4069
4142
  }
4070
4143
  const eol = code.indexOf("\n", i);
@@ -4089,7 +4162,7 @@ function stripCoverageComments(code) {
4089
4162
  if (ch === '"' || ch === "'" || ch === "`")
4090
4163
  quote = ch;
4091
4164
  if (ch !== " " && ch !== " " && ch !== "\n" && ch !== "\r")
4092
- prev = ch;
4165
+ prevAt = i;
4093
4166
  i++;
4094
4167
  }
4095
4168
  out.push(code.slice(copyFrom));
@@ -4778,11 +4851,12 @@ function assessTitleCoverage(title, code) {
4778
4851
  const repeatedHit = [...repeated].some((term) => hay.includes(term));
4779
4852
  return { covered: hit >= needed || repeatedHit, missingTerms };
4780
4853
  }
4781
- var COVERAGE_STOP_WORDS, MAX_ASSERTION_JOIN_CHARS, MAX_COVERAGE_LINE_CHARS, REGEX_PRECEDER2, BLOCK_SCAFFOLD, cachedAssertionInput, cachedAssertionSource, LOCAL_BINDING, ASSERTED_STATUS_RE, MAX_RESOLVED_NAMES_PER_LINE, WORD_CHAR_RE, NOT_CONTAIN_TEXT_RE, PLACE_PHRASE_RE, PLACE_WORD_MIN_CHARS, PLACE_FUNCTION_WORDS, HIDDEN_STATE_TERMS, HIDDEN_ELEMENT_LINE_RE, HIDDEN_LINE_RE, MAX_SHOWN_EARLIER_LINES, SHOWN_EARLIER_LITERAL_RE, SHARED_PREFIX_MIN_CHARS, MAX_SHOWN_EARLIER_LITERALS, VALUE_SHAPE_RE, VALUE_SHAPE_ALL_RE, TITLE_TERM_MIN;
4854
+ var COVERAGE_STOP_WORDS, MAX_ASSERTION_JOIN_CHARS, MAX_COVERAGE_LINE_CHARS, BLOCK_SCAFFOLD, cachedAssertionInput, cachedAssertionSource, LOCAL_BINDING, ASSERTED_STATUS_RE, MAX_RESOLVED_NAMES_PER_LINE, WORD_CHAR_RE, NOT_CONTAIN_TEXT_RE, PLACE_PHRASE_RE, PLACE_WORD_MIN_CHARS, PLACE_FUNCTION_WORDS, HIDDEN_STATE_TERMS, HIDDEN_ELEMENT_LINE_RE, HIDDEN_LINE_RE, MAX_SHOWN_EARLIER_LINES, SHOWN_EARLIER_LITERAL_RE, SHARED_PREFIX_MIN_CHARS, MAX_SHOWN_EARLIER_LITERALS, VALUE_SHAPE_RE, VALUE_SHAPE_ALL_RE, TITLE_TERM_MIN;
4782
4855
  var init_expected_outcome_coverage = __esm({
4783
4856
  "../shared/dist/expected-outcome-coverage.js"() {
4784
4857
  "use strict";
4785
4858
  init_assertion_classifier();
4859
+ init_regex_literal();
4786
4860
  COVERAGE_STOP_WORDS = /* @__PURE__ */ new Set([
4787
4861
  "after",
4788
4862
  "before",
@@ -4815,7 +4889,6 @@ var init_expected_outcome_coverage = __esm({
4815
4889
  ]);
4816
4890
  MAX_ASSERTION_JOIN_CHARS = 8e3;
4817
4891
  MAX_COVERAGE_LINE_CHARS = 2e3;
4818
- REGEX_PRECEDER2 = "(,=:[!&|?{};+-*%<>~^";
4819
4892
  BLOCK_SCAFFOLD = /=>|\b(?:test|describe|it|beforeEach|afterEach|beforeAll|afterAll|function)\b/;
4820
4893
  cachedAssertionInput = null;
4821
4894
  cachedAssertionSource = { raw: "", normalized: "", bindings: /* @__PURE__ */ new Map(), bindingRefs: /* @__PURE__ */ new Map() };
@@ -4911,7 +4984,7 @@ function replaceBlockComments(code, replacer) {
4911
4984
  continue;
4912
4985
  const next = code[i + 1];
4913
4986
  if (next !== "/" && next !== "*" && opensRegexLiteral(code, i)) {
4914
- i = regexLiteralEnd2(code, i) - 1;
4987
+ i = regexLiteralEnd(code, i) - 1;
4915
4988
  continue;
4916
4989
  }
4917
4990
  if (next === "/" && !(code[i - 1] === ":" && /[A-Za-z]/.test(code[i - 2] ?? ""))) {
@@ -5086,10 +5159,41 @@ var init_auth_handoff_coverage = __esm({
5086
5159
  });
5087
5160
 
5088
5161
  // ../shared/dist/auth-plan.js
5089
- function hasInlineLoginSteps(playwrightCode) {
5162
+ function stripStampedHelperRegions(code) {
5163
+ let out = code;
5164
+ for (const [open, close] of STAMPED_HELPER_REGIONS) {
5165
+ if (out.indexOf(open) === -1)
5166
+ continue;
5167
+ let kept = "";
5168
+ let i = 0;
5169
+ while (i < out.length) {
5170
+ const start = out.indexOf(open, i);
5171
+ if (start === -1) {
5172
+ kept += out.slice(i);
5173
+ break;
5174
+ }
5175
+ const end = out.indexOf(close, start + open.length);
5176
+ if (end === -1) {
5177
+ kept += out.slice(i);
5178
+ break;
5179
+ }
5180
+ const lineEnd = out.indexOf("\n", end);
5181
+ kept += out.slice(i, start);
5182
+ i = lineEnd === -1 ? out.length : lineEnd + 1;
5183
+ }
5184
+ out = kept;
5185
+ }
5186
+ return out;
5187
+ }
5188
+ function signsInInline(playwrightCode) {
5090
5189
  if (!playwrightCode)
5091
5190
  return false;
5092
- return /process\.env\.TEST_USER_EMAIL!?/.test(playwrightCode) && /process\.env\.TEST_USER_PASSWORD!?/.test(playwrightCode);
5191
+ if (!/process\.env\.TEST_USER_EMAIL!?/.test(playwrightCode))
5192
+ return false;
5193
+ if (/process\.env\.TEST_USER_PASSWORD!?/.test(playwrightCode))
5194
+ return true;
5195
+ const body = stripStampedHelperRegions(playwrightCode);
5196
+ return /process\.env\.TEST_USER_EMAIL/.test(body) && /__vqaWaitForEmail\s*\(/.test(body);
5093
5197
  }
5094
5198
  function hasGeneratedAuthGuard(playwrightCode) {
5095
5199
  if (!playwrightCode)
@@ -5108,7 +5212,7 @@ function legacyNeedsCookieState(tags, code, sharedAuthInjectEnabled) {
5108
5212
  if (sharedAuthInjectEnabled && tags.includes("@auth-guarded") && hasGeneratedAuthGuard(code))
5109
5213
  return true;
5110
5214
  const isGeneratedVariant = tags.includes("@edge") || tags.includes("@bug");
5111
- const hasLegacyInlineLogin = tags.includes("@discovered") && !isGeneratedVariant && hasInlineLoginSteps(code);
5215
+ const hasLegacyInlineLogin = tags.includes("@discovered") && !isGeneratedVariant && signsInInline(code);
5112
5216
  return !tags.includes("@inline-login") && !hasLegacyInlineLogin;
5113
5217
  }
5114
5218
  function codeReadsStoredCredentials(code) {
@@ -5229,7 +5333,7 @@ function authTypeTag(tags) {
5229
5333
  const explicit = explicitAuthTypeTags(t);
5230
5334
  return explicit.length === 1 ? explicit[0] : void 0;
5231
5335
  }
5232
- var AUTH_TAG_COOKIE_STATE, AUTH_TAG_LOGIN_COOKIE, AUTH_TAG_LOGIN_BEARER, AUTH_TAG_STANDING, AUTH_TAG_NONE, AUTH_TAG_UNSUPPORTED, AUTH_TYPE_TAGS, AUTH_TAG_ALIASES, TEST_USER_ENV_KEYS, API_AUTH_HEADERS_ENV_KEY;
5336
+ var AUTH_TAG_COOKIE_STATE, AUTH_TAG_LOGIN_COOKIE, AUTH_TAG_LOGIN_BEARER, AUTH_TAG_STANDING, AUTH_TAG_NONE, AUTH_TAG_UNSUPPORTED, AUTH_TYPE_TAGS, AUTH_TAG_ALIASES, TEST_USER_ENV_KEYS, API_AUTH_HEADERS_ENV_KEY, STAMPED_HELPER_REGIONS;
5233
5337
  var init_auth_plan = __esm({
5234
5338
  "../shared/dist/auth-plan.js"() {
5235
5339
  "use strict";
@@ -5256,6 +5360,10 @@ var init_auth_plan = __esm({
5256
5360
  };
5257
5361
  TEST_USER_ENV_KEYS = ["TEST_USER_EMAIL", "TEST_USER_PASSWORD"];
5258
5362
  API_AUTH_HEADERS_ENV_KEY = "API_AUTH_HEADERS";
5363
+ STAMPED_HELPER_REGIONS = [
5364
+ ["// >>> VALIDATE_QA_INBOX", "// <<< VALIDATE_QA_INBOX"],
5365
+ ["// >>> VALIDATE_QA_SMS", "// <<< VALIDATE_QA_SMS"]
5366
+ ];
5259
5367
  }
5260
5368
  });
5261
5369
 
@@ -5264,6 +5372,9 @@ function parseLastActionProofMode(raw) {
5264
5372
  const v = (raw ?? "").trim().toLowerCase();
5265
5373
  return v === "on" || v === "shadow" ? v : "off";
5266
5374
  }
5375
+ function parseAbsenceProofMode(raw) {
5376
+ return parseLastActionProofMode(raw);
5377
+ }
5267
5378
  function isUrlProof(stmt) {
5268
5379
  if (URL_PROOF.test(stmt))
5269
5380
  return true;
@@ -5651,6 +5762,65 @@ function isVacuousAccountAbsence(stmt, shown, account) {
5651
5762
  }
5652
5763
  return true;
5653
5764
  }
5765
+ function isAbsenceCheck(stmt) {
5766
+ if (!claimsAbsence(stmt))
5767
+ return false;
5768
+ const bare = stmt.replace(NOT_BEFORE_MATCHER, ".");
5769
+ return ABSENCE_READ.test(bare) || VALUE_ABSENCE.test(bare) || OPTION_ABSENCE.test(bare);
5770
+ }
5771
+ function comparableSubject(subject) {
5772
+ let s = subject.replace(/^\s*await\s+/, "").replace(/\s+/g, "");
5773
+ for (let prev = ""; prev !== s; ) {
5774
+ prev = s;
5775
+ s = s.replace(/\.(?:count|isVisible|isHidden|first|last)\(\)$/, "").replace(/\.nth\(\d+\)$/, "");
5776
+ }
5777
+ return s;
5778
+ }
5779
+ function subjectsThatExisted(checks, interactionStmts) {
5780
+ const out = [];
5781
+ const bound = locatorBindings(interactionStmts);
5782
+ const resolve = (expr) => {
5783
+ for (let depth = 0; depth < 4; depth++) {
5784
+ const lead = expr.match(/^\s*(?:await\s+)?([A-Za-z_$][\w$]*)(?![\w$(])/);
5785
+ const to = lead ? bound.get(lead[1]) : void 0;
5786
+ if (!lead || to === void 0)
5787
+ break;
5788
+ expr = `${to.replace(/;\s*$/, "")}${expr.slice(lead[0].length)}`;
5789
+ }
5790
+ return expr;
5791
+ };
5792
+ for (const { stmt } of checks) {
5793
+ if (isAbsenceCheck(stmt))
5794
+ continue;
5795
+ const subject = extractExpectSubject(stmt);
5796
+ if (subject)
5797
+ out.push(comparableSubject(subject));
5798
+ }
5799
+ for (const stmt of interactionStmts) {
5800
+ if (EXPECT_CALL_RE.test(stmt))
5801
+ continue;
5802
+ for (const m of stmt.matchAll(LOCATOR_USE)) {
5803
+ if (m[0].includes("waitFor") && /state\s*:\s*['"`](?:hidden|detached)/.test(stmt.slice(m.index)))
5804
+ continue;
5805
+ const before = stmt.slice(0, m.index).replace(/^\s*(?:(?:const|let|var)\s+[\w$]+\s*=|return\b)\s*/, "");
5806
+ if (before.trim())
5807
+ out.push(comparableSubject(resolve(before)));
5808
+ }
5809
+ }
5810
+ return out;
5811
+ }
5812
+ function isUnshownAbsence(stmt, existed, entered) {
5813
+ if (!isAbsenceCheck(stmt))
5814
+ return false;
5815
+ const raw = extractExpectSubject(stmt);
5816
+ if (!raw || !/\b(?:locator|getBy[A-Z]\w*|frameLocator)\s*\(/.test(raw))
5817
+ return false;
5818
+ const subject = comparableSubject(raw);
5819
+ if (existed.some((e) => e === subject || e.startsWith(`${subject}.`)))
5820
+ return false;
5821
+ const named = raw.match(/['"`][^'"`]+['"`]/g) ?? [];
5822
+ return !named.some((q) => entered.values.has(q.slice(1, -1).toLowerCase()));
5823
+ }
5654
5824
  function endOfEnclosingCall(stmt, from) {
5655
5825
  let depth = 0;
5656
5826
  for (let i = from; i < stmt.length; i++) {
@@ -6150,10 +6320,31 @@ function comparesWithItself(subject, tail) {
6150
6320
  const expected = squash2(args);
6151
6321
  return expected === squash2(m[1] === "toHaveCount" ? `${subject}.count()` : subject);
6152
6322
  }
6323
+ function ariaSnapshotTexts(template) {
6324
+ const out = [];
6325
+ for (const raw of template.split(ARIA_ENTRY_SPLIT)) {
6326
+ const m = ARIA_ENTRY.exec(raw.trim());
6327
+ if (!m || !ARIA_CONTENT_ROLES.has(m[1]))
6328
+ continue;
6329
+ for (const text of [m[2], m[3]]) {
6330
+ const t = text?.replace(/\\(.)/g, "$1").replace(/^["']|["']$/g, "").trim();
6331
+ if (!t || t.startsWith("/") || !/[\p{L}\p{N}]/u.test(t) || /["'`\\]/.test(t))
6332
+ continue;
6333
+ out.push(t);
6334
+ }
6335
+ }
6336
+ return out;
6337
+ }
6338
+ function presenceTwin(stmt) {
6339
+ return stmt.replace(NOT_HIDDEN, ".toBeVisible(").replace(NOT_COUNT_ZERO, ".toBeVisible(");
6340
+ }
6153
6341
  function asPlainAssertion(s) {
6154
6342
  return plainAssertion(s, false);
6155
6343
  }
6156
6344
  function plainAssertion(s, keepSelfCompare) {
6345
+ const twin = presenceTwin(s.stmt);
6346
+ if (twin !== s.stmt)
6347
+ return plainAssertion({ ...s, stmt: twin }, keepSelfCompare);
6157
6348
  const { stmt } = s;
6158
6349
  if (COUNT_AT_LEAST_ZERO.test(stmt))
6159
6350
  return [];
@@ -6164,6 +6355,12 @@ function plainAssertion(s, keepSelfCompare) {
6164
6355
  const subjectAt = stmt.indexOf(subject, stmt.search(EXPECT_CALL_RE));
6165
6356
  const tail = (stmt.startsWith("expect.poll", stmt.search(EXPECT_CALL_RE)) ? expectCallTail(stmt) : null) ?? stmt.slice(subjectAt + subject.length).trimStart();
6166
6357
  const as = (rewritten) => [{ ...s, stmt: rewritten }];
6358
+ const aria = ARIA_TEMPLATE_CALL.exec(tail);
6359
+ if (aria && !NEGATED_ASSERT.test(stmt)) {
6360
+ const texts = ariaSnapshotTexts(aria[2]);
6361
+ if (texts.length > 0)
6362
+ return texts.map((t) => ({ ...s, stmt: `await expect(${plain}).toContainText(${JSON.stringify(t)});` }));
6363
+ }
6167
6364
  if (LOCATOR_EXPR.test(plain) && !LOCATOR_VALUE_READ.test(plain) && ALWAYS_TRUE_ON_OBJECT.test(tail))
6168
6365
  return [];
6169
6366
  if (DEFINED_VALUE_READ.test(plain) && ALWAYS_DEFINED.test(tail))
@@ -6470,7 +6667,8 @@ function teardownLines(lines) {
6470
6667
  }
6471
6668
  return out;
6472
6669
  }
6473
- function validateAssertionStrength(code, opts = {}) {
6670
+ function validateAssertionStrength(source, opts = {}) {
6671
+ const code = withExpectAliasesAsExpect(source);
6474
6672
  const body = stripLoginScaffolding(code);
6475
6673
  const lines = body.split("\n");
6476
6674
  const stmts = liveExpectStatements(stripCoverageComments(body)).flatMap(asPlainAssertion);
@@ -6623,8 +6821,23 @@ function validateAssertionStrength(code, opts = {}) {
6623
6821
  const orderedPersistence = firstMutLine !== void 0 ? persistence : void 0;
6624
6822
  const signedIn = signInProofWindow(interactionStmts, interactionStmtsWithLines, lines, blockIds);
6625
6823
  const proof = classifyOutcomeProof(checks, mutated, reloaded, clickedControlNames(interactionStmts), entered, firstMutLine, orderedPersistence, signedIn);
6824
+ const absenceProof = opts.absenceProof ?? "off";
6825
+ let absenceOnly = false;
6826
+ if (absenceProof !== "off" && proof.kind === "durable") {
6827
+ const existed = subjectsThatExisted(checks, interactionStmtsRaw);
6828
+ const kept = checks.filter(({ stmt }) => !isUnshownAbsence(stmt, existed, entered));
6829
+ absenceOnly = kept.length < checks.length && classifyOutcomeProof(kept, mutated, reloaded, clickedControlNames(interactionStmts), entered, firstMutLine, orderedPersistence, signedIn).kind !== "durable";
6830
+ if (absenceOnly && absenceProof === "on") {
6831
+ return {
6832
+ ok: false,
6833
+ severity: mutated ? "block" : "downgrade",
6834
+ reason: "the only outcome checks are absence checks (not visible / hidden / count 0) of something the test never showed, touched or typed: they pass whether or not the action worked \u2014 assert what the action produced (the new text, value, count or page), or assert the element present before it goes",
6835
+ signals: base2({ interacted, mutated, navOnly, hasOutcomeProof: false, terminal, absenceOnly: true })
6836
+ };
6837
+ }
6838
+ }
6626
6839
  if (proof.kind === "durable") {
6627
- const signals2 = base2({ interacted, mutated, navOnly, hasOutcomeProof: true, terminal });
6840
+ const signals2 = base2({ interacted, mutated, navOnly, hasOutcomeProof: true, terminal, ...absenceOnly ? { absenceOnly } : {} });
6628
6841
  const { last, prev } = acts;
6629
6842
  if (teardown !== null && last !== null && acts.count >= 2 && last.kind !== "fill" && (last.kind === "enter" || isCommitClick(interactionStmts[last.i]) && !(prev?.kind === "click" && isDestructiveClick(interactionStmts[prev.i])))) {
6630
6843
  const lastLine = interactionStmtsWithLines[last.i].startLine;
@@ -6728,7 +6941,7 @@ function validateAssertionStrength(code, opts = {}) {
6728
6941
  signals
6729
6942
  };
6730
6943
  }
6731
- var ERROR_BANNER, OUTCOME_ALWAYS, FORM_INPUT, ANY_CLICK, NAV_LOCATOR, URL_PROOF, POLL_OPEN, POLL_PAGE_URL, POLL_PAGE_URL_ONLY, POLL_CALLS_MAX, RELOAD, EXPECT_STMT, BLOCK_SCAFFOLD2, TEST_BLOCK_OPEN, LOCATOR_BINDING, EXPECT_OPEN, STATE_MATCHER, CHROME_LOCATOR, CONTENT_ROLE, GET_BY_TEXT, BARE_CONTAINER_ROLE, HAS_TEXT_FILTER, OUTCOME_TEXT, SUCCESS_NOTICE_TEXT, NOT_A_SUCCESS_NOTICE, ANNOUNCE_ROLE, MESSAGE_SURFACE, TEXT_CONTENT_MATCHER, VISIBILITY_MATCHER, ADDITIVE_AFFORDANCE, EXCLUDE_AFFORDANCE, EMPTY_STATE_TEXT, AUTH_START, AUTH_END, LOGIN_CALL_LINE, INBOX_START, INBOX_END, NEGATED_ASSERT, NEGATED_ASSERT_NOT, CONST_CHAIN_MAX_DEPTH, TYPED_INPUT, TITLE_LINE, FAILURE_STATUS_ASSERT, FAILED_LIST_ASSERT, ENDPOINT_DOWN, NAVIGATION_RESPONSE_VAR, DIRECT_REQUEST_VAR, CATCH_ALL_PATTERN, GUARD_HANDLER, ABSENCE_MATCHER, VALUE_ABSENCE, OPTION_ABSENCE, NOT_BEFORE_MATCHER, ERROR_TEXT_WORDS, TEXT_REGEX_ARG, SCOPING_STEP, NEGATED_TEXT, ASYNC_DESTRUCTURE_OPEN, REQUEST_WORD, ROLE_NAME_RE, INTERACT_VERB, ROLE_SIG_RE, TRANSIENT_CONTROL_ROLES, POLL_CALLBACK, CONSTANT_WORD, CONSTANT_GLUE_CHARS, WORD_CHAR, MATCHER_ARGS_MAX, MATCHER_OPEN, ENTERED_VALUE, PAGE_NAV, ENV_READ, ENTERED_ENV, HAVE_VALUE_ENV, ACCOUNT_KEYS, ACCOUNT_ENV, ACCOUNT_CONST, IN_FLIGHT_TEXT, LOCATOR_VALUE_READ, LOCATOR_EXPR, ALWAYS_TRUE_ON_OBJECT, COUNT_AT_LEAST_ZERO, DEFINED_VALUE_READ, ALWAYS_DEFINED, SELF_COMPARE_MATCHER, IS_VISIBLE_TRUE, PAGE_VALUE_MATCHER, COUNT_ONE, URL_CHECK_ARG, PLAIN_PAGE_PATH, FORM_CONTROL_LOCATOR, CONTROL_VALUE_MATCHER, ACCOUNT_EMAIL_ENV, ENV_PASSWORD_ENTRY, ACCOUNT_CONTROL_ROLE, ASSERT_OR_WAIT, ACTS, actLinesOf, CLOSERS_ONLY, TEST_BODY_OPEN, COPY_CONTROL, COPIED_NOTICE, SEND_CONTROL, SEND_NOTICE, MARK_READ_CONTROL, APP_DATA_FIELD, COMMIT_CONTROL, NOT_COMMIT_CONTROL, DESTRUCTIVE_CONTROL, ENTER_PRESS, CONTROL_LABEL, isCommitClick, isDestructiveClick, TEARDOWN_OPEN;
6944
+ var ERROR_BANNER, OUTCOME_ALWAYS, FORM_INPUT, ANY_CLICK, NAV_LOCATOR, URL_PROOF, POLL_OPEN, POLL_PAGE_URL, POLL_PAGE_URL_ONLY, POLL_CALLS_MAX, RELOAD, EXPECT_STMT, BLOCK_SCAFFOLD2, TEST_BLOCK_OPEN, LOCATOR_BINDING, EXPECT_OPEN, STATE_MATCHER, CHROME_LOCATOR, CONTENT_ROLE, GET_BY_TEXT, BARE_CONTAINER_ROLE, HAS_TEXT_FILTER, OUTCOME_TEXT, SUCCESS_NOTICE_TEXT, NOT_A_SUCCESS_NOTICE, ANNOUNCE_ROLE, MESSAGE_SURFACE, TEXT_CONTENT_MATCHER, VISIBILITY_MATCHER, ADDITIVE_AFFORDANCE, EXCLUDE_AFFORDANCE, EMPTY_STATE_TEXT, AUTH_START, AUTH_END, LOGIN_CALL_LINE, INBOX_START, INBOX_END, NEGATED_ASSERT, NEGATED_ASSERT_NOT, CONST_CHAIN_MAX_DEPTH, TYPED_INPUT, TITLE_LINE, FAILURE_STATUS_ASSERT, FAILED_LIST_ASSERT, ENDPOINT_DOWN, NAVIGATION_RESPONSE_VAR, DIRECT_REQUEST_VAR, CATCH_ALL_PATTERN, GUARD_HANDLER, ABSENCE_MATCHER, VALUE_ABSENCE, OPTION_ABSENCE, NOT_BEFORE_MATCHER, ERROR_TEXT_WORDS, TEXT_REGEX_ARG, ABSENCE_READ, LOCATOR_USE, SCOPING_STEP, NEGATED_TEXT, ASYNC_DESTRUCTURE_OPEN, REQUEST_WORD, ROLE_NAME_RE, INTERACT_VERB, ROLE_SIG_RE, TRANSIENT_CONTROL_ROLES, POLL_CALLBACK, CONSTANT_WORD, CONSTANT_GLUE_CHARS, WORD_CHAR, MATCHER_ARGS_MAX, MATCHER_OPEN, ENTERED_VALUE, PAGE_NAV, ENV_READ, ENTERED_ENV, HAVE_VALUE_ENV, ACCOUNT_KEYS, ACCOUNT_ENV, ACCOUNT_CONST, IN_FLIGHT_TEXT, LOCATOR_VALUE_READ, LOCATOR_EXPR, ALWAYS_TRUE_ON_OBJECT, COUNT_AT_LEAST_ZERO, DEFINED_VALUE_READ, ALWAYS_DEFINED, SELF_COMPARE_MATCHER, IS_VISIBLE_TRUE, PAGE_VALUE_MATCHER, COUNT_ONE, ARIA_TEMPLATE_CALL, ARIA_ENTRY_SPLIT, ARIA_CONTENT_ROLES, ARIA_ENTRY, NOT_HIDDEN, NOT_COUNT_ZERO, URL_CHECK_ARG, PLAIN_PAGE_PATH, FORM_CONTROL_LOCATOR, CONTROL_VALUE_MATCHER, ACCOUNT_EMAIL_ENV, ENV_PASSWORD_ENTRY, ACCOUNT_CONTROL_ROLE, ASSERT_OR_WAIT, ACTS, actLinesOf, CLOSERS_ONLY, TEST_BODY_OPEN, COPY_CONTROL, COPIED_NOTICE, SEND_CONTROL, SEND_NOTICE, MARK_READ_CONTROL, APP_DATA_FIELD, COMMIT_CONTROL, NOT_COMMIT_CONTROL, DESTRUCTIVE_CONTROL, ENTER_PRESS, CONTROL_LABEL, isCommitClick, isDestructiveClick, TEARDOWN_OPEN;
6732
6945
  var init_assertion_strength = __esm({
6733
6946
  "../shared/dist/assertion-strength.js"() {
6734
6947
  "use strict";
@@ -6817,6 +7030,8 @@ var init_assertion_strength = __esm({
6817
7030
  NOT_BEFORE_MATCHER = /\.not\s*\.\s*(?=to[A-Z])/g;
6818
7031
  ERROR_TEXT_WORDS = /error|fail|wrong|try again|unable|could ?n.?t|can.?t|problem|invalid|unavailable/i;
6819
7032
  TEXT_REGEX_ARG = /(?:getByText|hasText|getByRole\([^)]{0,200}name)\s*[:(]\s*(\/(?:\\.|[^/\\\n])+\/[a-z]*)/;
7033
+ ABSENCE_READ = /\.toBeVisible\s*\(|\.toBeHidden\s*\(|\.toHaveCount\s*\(\s*0\s*\)|\.toBeAttached\s*\(|\.toHaveText\s*\(|\.toContainText\s*\(/;
7034
+ LOCATOR_USE = /\.\s*(?:click|dblclick|tap|hover|focus|fill|clear|press|pressSequentially|type|check|uncheck|setChecked|selectOption|setInputFiles|dragTo|selectText|waitFor)\s*\(/g;
6820
7035
  SCOPING_STEP = /(?:\s*\.\s*(?:first|last|nth)\s*\([^()]*\))*\s*\.\s*(?:getBy[A-Z]\w*|locator|filter)\s*\(/y;
6821
7036
  NEGATED_TEXT = /\.not\s*\.\s*(?:toHaveText|toContainText)\s*\(\s*(?:['"`]([^'"`]*)['"`]|\/((?:\\.|[^/\\\n])+)\/)?/;
6822
7037
  ASYNC_DESTRUCTURE_OPEN = /async\s*\(\s*\{/g;
@@ -6858,6 +7073,29 @@ var init_assertion_strength = __esm({
6858
7073
  IS_VISIBLE_TRUE = /^\)*\s*\.\s*(?:toBe\s*\(\s*true\s*\)|toBeTruthy\s*\(\s*\))/;
6859
7074
  PAGE_VALUE_MATCHER = /^\)\s*\.\s*(?:toBe|toEqual|toStrictEqual|toMatch|toContain)\s*\(/;
6860
7075
  COUNT_ONE = /\.\s*toHaveCount\s*\(\s*1\s*\)/;
7076
+ ARIA_TEMPLATE_CALL = /^\)\s*\.\s*toMatchAriaSnapshot\s*\(\s*(['"`])((?:\\[\s\S]|(?!\1)[^\\])*)\1/;
7077
+ ARIA_ENTRY_SPLIT = /(?:^|\s)-\s+(?=\/?[a-z][a-z-]*(?:\s|:|\[|$))/;
7078
+ ARIA_CONTENT_ROLES = /* @__PURE__ */ new Set([
7079
+ "listitem",
7080
+ "row",
7081
+ "cell",
7082
+ "gridcell",
7083
+ "article",
7084
+ "treeitem",
7085
+ "paragraph",
7086
+ "text",
7087
+ "strong",
7088
+ "emphasis",
7089
+ "code",
7090
+ "blockquote",
7091
+ "term",
7092
+ "definition",
7093
+ "alert",
7094
+ "status"
7095
+ ]);
7096
+ ARIA_ENTRY = /^([a-z][a-z-]*)(?:\s+"((?:\\.|[^"\\])*)")?(?:\s*\[[^\]]*\])*\s*(?::\s*(\S.*)?)?$/s;
7097
+ NOT_HIDDEN = /\.not\s*\.\s*toBeHidden\s*\(/g;
7098
+ NOT_COUNT_ZERO = /\.not\s*\.\s*toHaveCount\s*\(\s*0\s*(?:,\s*|(?=\)))/g;
6861
7099
  URL_CHECK_ARG = /\bpage\s*\.\s*waitForURL\s*\(\s*([^,)]+)|\bexpect(?:\s*\.\s*soft)?\s*\(\s*page\s*\)\s*\.\s*toHaveURL\s*\(\s*([^,)]+)/;
6862
7100
  PLAIN_PAGE_PATH = /^\/[\w\-./~%]*(?:[?#][^\s]*)?$/;
6863
7101
  FORM_CONTROL_LOCATOR = /getByRole\s*\(\s*['"`](?:combobox|switch|checkbox|radio|textbox|searchbox|spinbutton|slider)['"`]|getByLabel\s*\(|getByPlaceholder\s*\(/;
@@ -6903,6 +7141,12 @@ function isCancelledReason(error2) {
6903
7141
  function isHeldHealRun(run) {
6904
7142
  return run?.status === "PASSED" && typeof run.error === "string" && run.error.startsWith(HEAL_PATCH_HELD_RUN_ERROR_PREFIX);
6905
7143
  }
7144
+ function mayBeUnverifiedHealPass(run, tags) {
7145
+ return !!run && run.status === "PASSED" && run.mode === "heal" && !isHeldHealRun(run) && (tags ?? []).includes(NEEDS_REVIEW_TAG);
7146
+ }
7147
+ function isUnverifiedHealPass(run, tags, healConfidence) {
7148
+ return mayBeUnverifiedHealPass(run, tags) && typeof healConfidence === "string" && UNVERIFIED_HEAL_CONFIDENCES.has(healConfidence);
7149
+ }
6906
7150
  function isRunInterruptedError(error2) {
6907
7151
  return typeof error2 === "string" && RUN_INTERRUPTED_ERROR_PREFIXES.some((prefix) => error2.startsWith(prefix));
6908
7152
  }
@@ -6989,12 +7233,25 @@ function classifyErrorMessage(error2) {
6989
7233
  return "config_issue";
6990
7234
  if (EMAIL_NOT_RECEIVED_PREFIXES.some((prefix) => normalized.startsWith(prefix)))
6991
7235
  return "email_not_received";
7236
+ if (SMS_NOT_RECEIVED_PREFIXES.some((prefix) => normalized.startsWith(prefix)))
7237
+ return "email_not_received";
6992
7238
  if (isCancelledReason(error2))
6993
7239
  return "cancelled";
6994
7240
  if (isRetriedRunInterruption(error2))
6995
7241
  return "interrupted";
6996
7242
  return null;
6997
7243
  }
7244
+ function isSmsNotReceivedError(error2) {
7245
+ if (typeof error2 !== "string")
7246
+ return false;
7247
+ const normalized = error2.replace(/^\s+/, "").replace(/^Error:\s*/i, "");
7248
+ return SMS_NOT_RECEIVED_PREFIXES.some((prefix) => normalized.startsWith(prefix));
7249
+ }
7250
+ function blockedRunLogLine(error2, durationMs) {
7251
+ const reason = classifyErrorMessage(error2);
7252
+ const tail = reason === "rate_limit" ? "rate-limit detected, not counted as failure" : reason === "config_issue" ? "config issue (needs setup or sign-in), not counted as failure" : reason === "email_not_received" ? "verification email not received, not counted as failure" : reason === "cancelled" ? "cancelled, not counted as failure" : reason === "interrupted" ? "run interrupted, not counted as failure" : "run error";
7253
+ return `BLOCKED (${durationMs}ms) \u2014 ${tail}`;
7254
+ }
6998
7255
  function stripEnvironmentalPrefix(error2) {
6999
7256
  for (const prefix of ENVIRONMENTAL_ERROR_PREFIXES) {
7000
7257
  if (error2.startsWith(prefix)) {
@@ -7057,8 +7314,19 @@ function computePassRate(passed, tested) {
7057
7314
  }
7058
7315
  function summarizeHealth(items) {
7059
7316
  const c = { passed: 0, failed: 0, errored: 0, environmental: 0, never_run: 0 };
7317
+ let rateLimited = 0;
7318
+ let configIssue = 0;
7060
7319
  for (const item of items) {
7061
- c[classifyTestHealth(item.run)] += item.weight ?? 1;
7320
+ const weight = item.weight ?? 1;
7321
+ const bucket = classifyTestHealth(item.run);
7322
+ c[bucket] += weight;
7323
+ if (bucket === "environmental") {
7324
+ const reason = classifyErrorMessage(item.run?.error);
7325
+ if (reason === "rate_limit")
7326
+ rateLimited += weight;
7327
+ else if (isConfigLikeReason(reason))
7328
+ configIssue += weight;
7329
+ }
7062
7330
  }
7063
7331
  const tested = c.passed + c.failed + c.errored;
7064
7332
  return {
@@ -7066,6 +7334,8 @@ function summarizeHealth(items) {
7066
7334
  failed: c.failed,
7067
7335
  errored: c.errored,
7068
7336
  environmental: c.environmental,
7337
+ rateLimited,
7338
+ configIssue,
7069
7339
  neverRun: c.never_run,
7070
7340
  tested,
7071
7341
  total: tested + c.environmental + c.never_run,
@@ -7102,7 +7372,7 @@ function reportedBugWhere() {
7102
7372
  function bugReviewStatus(status) {
7103
7373
  return status === "QUARANTINED" ? "confirmed" : "needs-review";
7104
7374
  }
7105
- var USER_CANCELLED_RUN_ERROR, PLATFORM_SUPPORT_CANCELLED_RUN_ERROR, PLATFORM_ADMIN_CANCELLED_RUN_ERROR, ADMIN_FORCE_CANCELLED_RUN_ERROR, CHAIN_CANCELLED_RUN_ERROR, RETRY_HELD_RUN_ERROR, CANCELLED_REASON_PREFIXES, isUserCancelledError, HEAL_PATCH_HELD_RUN_ERROR_PREFIX, RUNNER_INTERRUPT_ERROR_PREFIX, SERVER_INTERRUPT_ERROR_PREFIX, RUN_LOST_ERROR_PREFIX, RUN_INTERRUPTED_ERROR_PREFIXES, RUN_INTERRUPTION_RETRIED_MARKER, RUN_INTERRUPTION_NOT_RETRIED_MARKERS, BUG_ASSERTION_TAG, UNEXPECTED_PASS_ERROR_PREFIX, BUG_TEST_BROKEN_ERROR_PREFIX, BUG_TEST_BROKEN_REASON, TEST_DID_NOT_RUN_ERROR, EMAIL_NOT_RECEIVED_PREFIXES, ENVIRONMENTAL_ERROR_PREFIXES, ENVIRONMENTAL_ERROR_STORED_PREFIXES, FEATURE_ROW_RUN_WINDOW, BUG_SURFACE_SUPPRESSING_TAGS, QUARANTINE_CLOSE_TAGS, BUG_CANDIDATE_TAG, BUG_TEST_BROKEN_TAG;
7375
+ var USER_CANCELLED_RUN_ERROR, PLATFORM_SUPPORT_CANCELLED_RUN_ERROR, PLATFORM_ADMIN_CANCELLED_RUN_ERROR, ADMIN_FORCE_CANCELLED_RUN_ERROR, CHAIN_CANCELLED_RUN_ERROR, RETRY_HELD_RUN_ERROR, CANCELLED_REASON_PREFIXES, isUserCancelledError, HEAL_PATCH_HELD_RUN_ERROR_PREFIX, UNVERIFIED_HEAL_CONFIDENCES, NEEDS_REVIEW_TAG, RUNNER_INTERRUPT_ERROR_PREFIX, SERVER_INTERRUPT_ERROR_PREFIX, RUN_LOST_ERROR_PREFIX, RUN_INTERRUPTED_ERROR_PREFIXES, RUN_INTERRUPTION_RETRIED_MARKER, RUN_INTERRUPTION_NOT_RETRIED_MARKERS, BUG_ASSERTION_TAG, UNEXPECTED_PASS_ERROR_PREFIX, BUG_TEST_BROKEN_ERROR_PREFIX, BUG_TEST_BROKEN_REASON, TEST_DID_NOT_RUN_ERROR, EMAIL_NOT_RECEIVED_PREFIXES, SMS_NOT_RECEIVED_PREFIXES, ENVIRONMENTAL_ERROR_PREFIXES, ENVIRONMENTAL_ERROR_STORED_PREFIXES, FEATURE_ROW_RUN_WINDOW, BUG_SURFACE_SUPPRESSING_TAGS, QUARANTINE_CLOSE_TAGS, BUG_CANDIDATE_TAG, BUG_TEST_BROKEN_TAG;
7106
7376
  var init_run_classification = __esm({
7107
7377
  "../shared/dist/run-classification.js"() {
7108
7378
  "use strict";
@@ -7122,6 +7392,8 @@ var init_run_classification = __esm({
7122
7392
  ];
7123
7393
  isUserCancelledError = isCancelledReason;
7124
7394
  HEAL_PATCH_HELD_RUN_ERROR_PREFIX = "Heal patch held";
7395
+ UNVERIFIED_HEAL_CONFIDENCES = /* @__PURE__ */ new Set(["low", "medium"]);
7396
+ NEEDS_REVIEW_TAG = "@needs-review";
7125
7397
  RUNNER_INTERRUPT_ERROR_PREFIX = "Runner shutting down";
7126
7398
  SERVER_INTERRUPT_ERROR_PREFIX = "Run interrupted by server restart";
7127
7399
  RUN_LOST_ERROR_PREFIX = "Runner lost this run";
@@ -7142,10 +7414,12 @@ var init_run_classification = __esm({
7142
7414
  "VERIFICATION_EMAIL_HAS_NO_CODE:",
7143
7415
  "VERIFICATION_EMAIL_HAS_NO_LINK:"
7144
7416
  ];
7417
+ SMS_NOT_RECEIVED_PREFIXES = ["VERIFICATION_SMS_NOT_RECEIVED:"];
7145
7418
  ENVIRONMENTAL_ERROR_PREFIXES = [
7146
7419
  "RATE_LIMIT_BLOCKED:",
7147
7420
  "CONFIG_ISSUE:",
7148
- ...EMAIL_NOT_RECEIVED_PREFIXES
7421
+ ...EMAIL_NOT_RECEIVED_PREFIXES,
7422
+ ...SMS_NOT_RECEIVED_PREFIXES
7149
7423
  ];
7150
7424
  ENVIRONMENTAL_ERROR_STORED_PREFIXES = [
7151
7425
  ...ENVIRONMENTAL_ERROR_PREFIXES.flatMap((prefix) => [prefix, `Error: ${prefix}`]),
@@ -9530,6 +9804,19 @@ function googleBrokerUnavailableLine(lines, testError) {
9530
9804
  }
9531
9805
  return last;
9532
9806
  }
9807
+ function googleSignInRefusedLine(lines) {
9808
+ let last = null;
9809
+ for (const raw of lines ?? []) {
9810
+ for (const line of String(raw).split("\n")) {
9811
+ const text = line.trim();
9812
+ if (GOOGLE_SIGN_IN_REFUSED.test(text))
9813
+ last = text;
9814
+ else if (GOOGLE_SIGNED_IN.test(text))
9815
+ last = null;
9816
+ }
9817
+ }
9818
+ return last;
9819
+ }
9533
9820
  function stepConsoleLines(result) {
9534
9821
  const steps = Array.isArray(result) ? result : result && typeof result === "object" && Array.isArray(result.stepResults) ? result.stepResults : [];
9535
9822
  return steps.flatMap((step) => {
@@ -9537,7 +9824,23 @@ function stepConsoleLines(result) {
9537
9824
  return Array.isArray(logs) ? logs.filter((line) => typeof line === "string") : [];
9538
9825
  });
9539
9826
  }
9540
- var GOOGLE_BROKER_UNAVAILABLE, GOOGLE_BROKER_ASKED, GOOGLE_SIGNED_IN, TIMEOUT_ERROR;
9827
+ function googleSignInRefusedReason(lines) {
9828
+ let reason = null;
9829
+ for (const raw of lines ?? []) {
9830
+ for (const line of String(raw).split("\n")) {
9831
+ const text = line.trim();
9832
+ if (GOOGLE_SIGNED_IN.test(text)) {
9833
+ reason = null;
9834
+ continue;
9835
+ }
9836
+ const m = GOOGLE_SIGN_IN_ANSWER.exec(text);
9837
+ if (m)
9838
+ reason = m[1];
9839
+ }
9840
+ }
9841
+ return reason;
9842
+ }
9843
+ var GOOGLE_BROKER_UNAVAILABLE, GOOGLE_BROKER_ASKED, GOOGLE_SIGNED_IN, TIMEOUT_ERROR, GOOGLE_SIGN_IN_REFUSED, GOOGLE_SIGN_IN_ANSWER;
9541
9844
  var init_google_broker_lines = __esm({
9542
9845
  "../shared/dist/google-broker-lines.js"() {
9543
9846
  "use strict";
@@ -9545,6 +9848,8 @@ var init_google_broker_lines = __esm({
9545
9848
  GOOGLE_BROKER_ASKED = /^\[google-sign-in\] the app started Google sign-in; asking the broker/;
9546
9849
  GOOGLE_SIGNED_IN = /^\[google-sign-in\] returned to the app with Google sign-in/;
9547
9850
  TIMEOUT_ERROR = /Test timeout of \d+ms exceeded|TimeoutError|Timeout:? \d+ ?ms/i;
9851
+ GOOGLE_SIGN_IN_REFUSED = /^\[google-sign-in\] (?:not_seeded|not_enabled|expired|needs_reseed|device_approval|needs_verification|policy_rejected|unsupported_flow|unsupported_one_tap): /;
9852
+ GOOGLE_SIGN_IN_ANSWER = /^\[google-sign-in\] ([a-z][a-z0-9_]*): /;
9548
9853
  }
9549
9854
  });
9550
9855
 
@@ -9596,22 +9901,87 @@ function isIntentionalNegativeAuthTest(input) {
9596
9901
  const meta = [input.testName, input.description].filter(Boolean).join(" ");
9597
9902
  return matchesAny(meta, INTENTIONAL_NEGATIVE_AUTH);
9598
9903
  }
9904
+ function signInStepPassedBeforeFailure(steps) {
9905
+ const list = steps ?? [];
9906
+ const failedAt = list.findIndex((step) => typeof step.status === "string" && step.status !== "passed" && step.status !== "skipped");
9907
+ if (failedAt <= 0)
9908
+ return false;
9909
+ if (SIGN_IN_STEP_TITLE.test(list[failedAt].title ?? ""))
9910
+ return false;
9911
+ return list.slice(0, failedAt).some((step) => step.status === "passed" && SIGN_IN_STEP_TITLE.test(step.title ?? ""));
9912
+ }
9913
+ function loginFormAlertTexts(body) {
9914
+ const text = body.length > ERROR_CONTEXT_CAP ? body.slice(0, ERROR_CONTEXT_CAP) : body;
9915
+ const blocks = [];
9916
+ let open = null;
9917
+ for (const line of text.split("\n")) {
9918
+ const fence = /^```(\w*)$/.exec(line.trim());
9919
+ if (fence) {
9920
+ if (open) {
9921
+ blocks.push(open);
9922
+ open = null;
9923
+ } else if (fence[1].toLowerCase() === "yaml")
9924
+ open = [];
9925
+ continue;
9926
+ }
9927
+ if (open)
9928
+ open.push(line);
9929
+ }
9930
+ const alerts = [];
9931
+ for (const lines of blocks) {
9932
+ if (!lines.some((l) => SNAPSHOT_PASSWORD_FIELD.test(l)))
9933
+ continue;
9934
+ for (let i = 0; i < lines.length && alerts.length < MAX_ALERTS; i++) {
9935
+ const m = SNAPSHOT_ALERT.exec(lines[i]);
9936
+ if (!m)
9937
+ continue;
9938
+ const indent = m[1].length;
9939
+ const parts = [m[2]];
9940
+ let j = i + 1;
9941
+ for (; j < lines.length; j++) {
9942
+ const lead = /^\s*/.exec(lines[j])?.[0].length ?? 0;
9943
+ if (lines[j].trim() === "" || lead <= indent)
9944
+ break;
9945
+ parts.push(lines[j].trim());
9946
+ }
9947
+ alerts.push(parts.join(" ").slice(0, MAX_ALERT_CHARS));
9948
+ i = j - 1;
9949
+ }
9950
+ }
9951
+ return alerts;
9952
+ }
9953
+ function stepLoginFormAlerts(steps) {
9954
+ const out = [];
9955
+ for (const sr of steps ?? []) {
9956
+ for (const a of sr.attachments ?? []) {
9957
+ if (a?.name === "error-context" && typeof a.body === "string")
9958
+ out.push(...loginFormAlertTexts(a.body));
9959
+ }
9960
+ }
9961
+ return out;
9962
+ }
9599
9963
  function detectAuthBlock(input) {
9600
9964
  const tags = input.tags ?? [];
9601
9965
  const isAuthed = tags.some((t) => AUTHED_TAGS.includes(t));
9602
- if (!isAuthed)
9966
+ const signsInInline2 = !isAuthed && codeReadsStoredCredentials(input.code);
9967
+ if (!isAuthed && !signsInInline2)
9603
9968
  return NO_DETECTION;
9604
9969
  if (isIntentionalNegativeAuthTest(input))
9605
9970
  return NO_DETECTION;
9971
+ if (signInStepPassedBeforeFailure(input.stepResults))
9972
+ return NO_DETECTION;
9606
9973
  const sources = [];
9607
- if (input.error)
9608
- sources.push(stripEchoLines(input.error));
9609
- if (input.logs)
9610
- sources.push(stripEchoLines(input.logs));
9611
- for (const sr of input.stepResults ?? []) {
9612
- if (sr.error)
9613
- sources.push(stripEchoLines(sr.error));
9974
+ if (isAuthed) {
9975
+ if (input.error)
9976
+ sources.push(stripEchoLines(input.error));
9977
+ if (input.logs)
9978
+ sources.push(stripEchoLines(input.logs));
9979
+ for (const sr of input.stepResults ?? []) {
9980
+ if (sr.error)
9981
+ sources.push(stripEchoLines(sr.error));
9982
+ }
9614
9983
  }
9984
+ sources.push(...stepLoginFormAlerts(input.stepResults));
9615
9985
  for (const text of sources) {
9616
9986
  if (!text)
9617
9987
  continue;
@@ -9660,10 +10030,11 @@ function detectApiColdSessionBlock(input) {
9660
10030
  function coldSessionConfigMessage(context, reason, evidence) {
9661
10031
  return `CONFIG_ISSUE: ${context} without valid injected auth (${reason}) \u2014 no session cookie was injected this run. Ensure the project login (auth-setup recipe / prewarm) is configured so the shared session jar warms (a project with no login: set TEST_USER_EMAIL and TEST_USER_PASSWORD in Test Data \u2192 Test Variables so a session can be captured); for endpoints that authenticate with a standing API key or bearer header instead, set them in the project's API_AUTH_HEADERS variable (injected as request headers). Evidence: ${evidence.join("; ")}`;
9662
10032
  }
9663
- var NO_DETECTION, AUTHED_TAGS, LOGIN_CTX, AUTH_BLOCK_SIGNATURES, INTENTIONAL_NEGATIVE_AUTH, ECHO_LINE_RE, UNAUTHORIZED_TEXT_RE;
10033
+ var NO_DETECTION, AUTHED_TAGS, LOGIN_CTX, AUTH_BLOCK_SIGNATURES, INTENTIONAL_NEGATIVE_AUTH, ECHO_LINE_RE, SIGN_IN_STEP_TITLE, ERROR_CONTEXT_CAP, MAX_ALERTS, MAX_ALERT_CHARS, SNAPSHOT_PASSWORD_FIELD, SNAPSHOT_ALERT, UNAUTHORIZED_TEXT_RE;
9664
10034
  var init_auth_block_detector = __esm({
9665
10035
  "../shared/dist/auth-block-detector.js"() {
9666
10036
  "use strict";
10037
+ init_auth_plan();
9667
10038
  NO_DETECTION = { detected: false, reason: "", evidence: [] };
9668
10039
  AUTHED_TAGS = ["@needs-auth", "@inline-login"];
9669
10040
  LOGIN_CTX = "\\b(?:log\\s*in|login|logging\\s+in|sign[\\s-]?in|signing\\s+in|continue)\\b";
@@ -9673,6 +10044,10 @@ var init_auth_block_detector = __esm({
9673
10044
  { reason: "invalid credentials", re: /\b(invalid|incorrect|wrong|bad)\s+(login\s+)?credentials\b/i },
9674
10045
  { reason: "credentials do not match", re: /\bcredentials\b[^\n]{0,40}\b(do(?:es)?\s*n['’]?t|do\s+not)\s+match\b/i },
9675
10046
  { reason: "incorrect email or password", re: /\b(incorrect|invalid|wrong)\s+(email|username)\s+or\s+password\b/i },
10047
+ { reason: "username and password do not match", re: /\b(?:user\s*name|e-?mail)\s+and\s+password\s+(?:do(?:es)?\s*n['’]?t|do(?:es)?\s+not)\s+match\b/i },
10048
+ // A locked account is a blocker of the same kind: the configured user can't sign in until it is unlocked. The
10049
+ // rate-limit detector keeps the timed lockouts ("temporarily locked", "too many attempts").
10050
+ { reason: "account locked out", re: /\b(?:user|account)\b[^\n]{0,30}\b(?:has\s+been|is)\s+locked\s+out\b/i },
9676
10051
  // Require an EXPLICIT negative (codex #21): the old `(not\s+)?(confirmed|verified)` made the negation
9677
10052
  // optional, so a benign "Email verified" success message / feature assertion matched and hid a real
9678
10053
  // feature failure as a CONFIG_ISSUE. Only "email not (yet) confirmed/verified" or "unconfirmed/unverified".
@@ -9704,6 +10079,12 @@ var init_auth_block_detector = __esm({
9704
10079
  /\b(reject|deny|denied|block(ed)?)\b[^\n]{0,20}\b(login|sign[\s-]?in|auth|credential|password)\b/i
9705
10080
  ];
9706
10081
  ECHO_LINE_RE = /^\s*(?:(?:📋|🎭|🌐|⚙️?)|(?:stderr:\s*|stdout:\s*)?Locator:)/u;
10082
+ SIGN_IN_STEP_TITLE = /\b(?:log(?:ging)?\s*in(?:to)?|sign(?:ing)?[\s-]?in(?:to)?|authenticat(?:e|es|ed|ing|ion))\b/i;
10083
+ ERROR_CONTEXT_CAP = 64 * 1024;
10084
+ MAX_ALERTS = 20;
10085
+ MAX_ALERT_CHARS = 1e3;
10086
+ SNAPSHOT_PASSWORD_FIELD = /^\s*- textbox "[^"\n]{0,80}\bpass(?:word|code)?\b/i;
10087
+ SNAPSHOT_ALERT = /^(\s*)- alert\b(.*)$/;
9707
10088
  UNAUTHORIZED_TEXT_RE = /(?:return(?:s|ed|ing)?|respond(?:s|ed|ing)?|got|receiv(?:e|ed|ing)|gives?|sends?|yield(?:s|ed|ing)?|results?\s+in)\s+(?:a\s+|an\s+|the\s+)?(?:401|403)\b|\bstatus(?:\s+code)?\s*(?::\s*)?(?:401|403)\b|\bHTTP\/[\d.]+\s+(?:401|403)\b|(?:401|403)['’]d\b|(?:[→⇒]|->)\s*(?:401|403)\b|(?:return(?:s|ed|ing)?|respond(?:s|ed|ing)?|got|receiv(?:e|ed|ing)|threw|throws?)\s+(?:a\s+|an\s+)?(?:unauthorized|forbidden)\b|\bnot\s+authenticated\b/i;
9708
10089
  }
9709
10090
  });
@@ -10138,6 +10519,77 @@ var init_redact_needles = __esm({
10138
10519
  }
10139
10520
  });
10140
10521
 
10522
+ // ../shared/dist/credential-header-scrub.js
10523
+ function scrubCredentialHeaders(text) {
10524
+ if (text.length < 6)
10525
+ return text;
10526
+ let out = text.replace(HEADER_LINE, (match, keyQuote, name, sep, value) => {
10527
+ if (value === "" || ALREADY_REDACTED.test(value))
10528
+ return match;
10529
+ if (COOKIE_NAME.test(name) && !value.includes("="))
10530
+ return match;
10531
+ if (AUTHORIZATION_NAME.test(name) && WORDS_ONLY.test(value))
10532
+ return match;
10533
+ const q = value[0] === '"' || value[0] === "'" ? value[0] : "";
10534
+ return `${keyQuote}${name}${keyQuote}${sep}${q}${REDACTED2}${q}`;
10535
+ });
10536
+ out = out.replace(SET_COOKIE_PAIR, `$1${REDACTED2}`);
10537
+ return out;
10538
+ }
10539
+ function isCredentialHeaderName(name) {
10540
+ return name.length <= 100 && HEADER_NAME_ONLY.test(name);
10541
+ }
10542
+ function scrubCredentialHeaderValue(value) {
10543
+ if (typeof value === "string")
10544
+ return value === REDACTED2 || value === "" ? value : REDACTED2;
10545
+ if (Array.isArray(value) && value.every((item) => typeof item === "string")) {
10546
+ const next = value.map((item) => item === REDACTED2 || item === "" ? item : REDACTED2);
10547
+ return next.every((item, i) => item === value[i]) ? value : next;
10548
+ }
10549
+ return value;
10550
+ }
10551
+ function scrubCredentialHeadersDeep(value) {
10552
+ if (typeof value === "string")
10553
+ return scrubCredentialHeaders(value);
10554
+ if (Array.isArray(value)) {
10555
+ let changed = false;
10556
+ const next = value.map((item) => {
10557
+ const scrubbed = scrubCredentialHeadersDeep(item);
10558
+ if (scrubbed !== item)
10559
+ changed = true;
10560
+ return scrubbed;
10561
+ });
10562
+ return changed ? next : value;
10563
+ }
10564
+ if (value !== null && typeof value === "object") {
10565
+ let changed = false;
10566
+ const next = {};
10567
+ for (const [k, v] of Object.entries(value)) {
10568
+ const scrubbed = isCredentialHeaderName(k) ? scrubCredentialHeaderValue(v) : scrubCredentialHeadersDeep(v);
10569
+ if (scrubbed !== v)
10570
+ changed = true;
10571
+ next[k] = scrubbed;
10572
+ }
10573
+ return changed ? next : value;
10574
+ }
10575
+ return value;
10576
+ }
10577
+ var REDACTED2, HEADER_NAMES, COOKIE_NAME, AUTHORIZATION_NAME, WORDS_ONLY, HEADER_NAME_ONLY, HEADER_LINE, ALREADY_REDACTED, SET_COOKIE_PAIR;
10578
+ var init_credential_header_scrub = __esm({
10579
+ "../shared/dist/credential-header-scrub.js"() {
10580
+ "use strict";
10581
+ REDACTED2 = "[REDACTED]";
10582
+ HEADER_NAMES = "cookie2?|set-cookie2?|proxy-authorization|authorization|api-?key|(?:x-)?(?:csrf|xsrf)[a-z0-9-]{0,30}|x-[a-z0-9-]{0,40}?(?:token|session|secret|key|auth)[a-z0-9-]{0,40}";
10583
+ COOKIE_NAME = /^(?:set-)?cookie2?$/i;
10584
+ AUTHORIZATION_NAME = /^(?:proxy-)?authorization$/i;
10585
+ WORDS_ONLY = /^[A-Za-z]+(?: [A-Za-z]+){0,3}$/;
10586
+ HEADER_NAME_ONLY = new RegExp(`^(?:${HEADER_NAMES})$`, "i");
10587
+ HEADER_LINE = new RegExp(`(?<![A-Za-z0-9_-])(["']?)(${HEADER_NAMES})\\1(\\s*:\\s*)("(?:[^"\\\\\\r\\n]|\\\\.)*"|'(?:[^'\\\\\\r\\n]|\\\\.)*'|[^\\r\\n\\x1b]*)`, "gi");
10588
+ ALREADY_REDACTED = /^["']?(?:[A-Za-z][\w-]*\s+)?\[REDACTED\]["']?[,;]?\s*$/;
10589
+ SET_COOKIE_PAIR = /(?<![A-Za-z0-9_-])([A-Za-z0-9_.%-]{1,64}=)(?!\[REDACTED\])([^\s;,"'<>]{6,2048})(?=;\s*(?:Path|Domain|Expires|Max-Age|HttpOnly|Secure|SameSite|Partitioned)\b)/gi;
10590
+ }
10591
+ });
10592
+
10141
10593
  // ../shared/dist/deepgram-voices.js
10142
10594
  function voice(family, name, gender, accent, description) {
10143
10595
  const label = name.charAt(0).toUpperCase() + name.slice(1);
@@ -10714,13 +11166,13 @@ function isSensitiveQueryKey(key) {
10714
11166
  return SENSITIVE_QUERY_KEY_RE.test(key);
10715
11167
  }
10716
11168
  function redactSecretValues(value) {
10717
- return value.replace(MCP_SECRET_VALUE_RE, REDACTED2);
11169
+ return value.replace(MCP_SECRET_VALUE_RE, REDACTED3);
10718
11170
  }
10719
11171
  function scrubReturnUrlValue(value, depth) {
10720
11172
  if (!/[?%]/.test(value))
10721
11173
  return value;
10722
11174
  if (depth >= MAX_NESTED_URL_DEPTH)
10723
- return REDACTED2;
11175
+ return REDACTED3;
10724
11176
  const q = value.indexOf("?");
10725
11177
  if (q !== -1) {
10726
11178
  const hashIdx = value.indexOf("#", q);
@@ -10742,7 +11194,7 @@ function scrubReturnUrlValue(value, depth) {
10742
11194
  try {
10743
11195
  return encodeURIComponent(scrubbed);
10744
11196
  } catch {
10745
- return REDACTED2;
11197
+ return REDACTED3;
10746
11198
  }
10747
11199
  }
10748
11200
  function scrubQuery(search, depth) {
@@ -10758,7 +11210,7 @@ function scrubQuery(search, depth) {
10758
11210
  params.forEach((value, key) => {
10759
11211
  let scrubbed = value;
10760
11212
  if (isSensitiveQueryKey(key)) {
10761
- scrubbed = REDACTED2;
11213
+ scrubbed = REDACTED3;
10762
11214
  } else {
10763
11215
  if (RETURN_URL_QUERY_KEY_RE.test(key))
10764
11216
  scrubbed = scrubReturnUrlValue(scrubbed, depth);
@@ -10870,12 +11322,12 @@ function scrubUrlPropertyBag(props) {
10870
11322
  } catch {
10871
11323
  }
10872
11324
  }
10873
- var SENSITIVE_QUERY_KEY_RE, REDACTED2, MCP_SECRET_VALUE_RE, RETURN_URL_QUERY_KEY_RE, MAX_NESTED_URL_DEPTH, MAX_PROPERTY_SWEEP_DEPTH;
11325
+ var SENSITIVE_QUERY_KEY_RE, REDACTED3, MCP_SECRET_VALUE_RE, RETURN_URL_QUERY_KEY_RE, MAX_NESTED_URL_DEPTH, MAX_PROPERTY_SWEEP_DEPTH;
10874
11326
  var init_analytics_url = __esm({
10875
11327
  "../shared/dist/analytics-url.js"() {
10876
11328
  "use strict";
10877
11329
  SENSITIVE_QUERY_KEY_RE = /^(access_token|refresh_token|id_token|token|code|state|password|secret|api[_-]?key|auth|authorization|session|jwt|otp|email|phone|magic[_-]?link)$|token|secret|password|credential|email|phone|(^|[_-])(otp|verification|auth)[_-]?code($|[_-])|(^|[_-])(state|session|grant)$/i;
10878
- REDACTED2 = "[redacted]";
11330
+ REDACTED3 = "[redacted]";
10879
11331
  MCP_SECRET_VALUE_RE = /mcp_(consent|code|at|rt)_[A-Za-z0-9]+/g;
10880
11332
  RETURN_URL_QUERY_KEY_RE = /^(next|redirect|return|continue)([_-]?(to|url|uri|path))?$/i;
10881
11333
  MAX_NESTED_URL_DEPTH = 5;
@@ -13705,8 +14157,19 @@ function successContradicted(text) {
13705
14157
  }
13706
14158
  return SUCCESS_NO_STATUS_RE.test(text) || OFF_BY_RE.test(text);
13707
14159
  }
14160
+ function negatedSafeguard(text) {
14161
+ for (const match of matches(NEGATED_SAFEGUARD_RE, text)) {
14162
+ if (!NOT_KEPT_RE.test(match[1]))
14163
+ return true;
14164
+ const head = text.slice(Math.max(0, match.index - SAFEGUARD_LOOKBEHIND), match.index);
14165
+ const sentence = head.slice(Math.max(head.lastIndexOf("."), head.lastIndexOf(";"), head.lastIndexOf("\n")) + 1);
14166
+ if (!REFUSED_INPUT_RE.test(sentence))
14167
+ return true;
14168
+ }
14169
+ return false;
14170
+ }
13708
14171
  function claimsDefect(text) {
13709
- if (BUG_LABEL_RE.test(text) || SILENT_RE.test(text) || CONCESSION_RE.test(text) || successContradicted(text) || NEGATED_SAFEGUARD_RE.test(text)) {
14172
+ if (BUG_LABEL_RE.test(text) || SILENT_RE.test(text) || CONCESSION_RE.test(text) || successContradicted(text) || negatedSafeguard(text)) {
13710
14173
  return true;
13711
14174
  }
13712
14175
  return BAD_VALUE_RE.test(text) && !REFUSAL_RE.test(text);
@@ -13714,7 +14177,7 @@ function claimsDefect(text) {
13714
14177
  function hasTestFailContract(code) {
13715
14178
  return TEST_FAIL_CONTRACT_RE.test(code);
13716
14179
  }
13717
- var BUG_LABEL_RE, SILENT_RE, CONCESSION_RE, SUCCESS_REPORT, SUCCESS_CONTRADICTED_RE, CONTRADICTION_VETO_RE, NOTHING_PROVIDED_RE, UNTOUCHED_SUBSETS, ACTION_LOOKBEHIND, TRAILING_ACTION_RE, ADJECTIVE_BEFORE_NOUN_RE, SUCCESS_SAME_STATE_RE, GENERIC_REPORT_RE, SAME_STATE, OTHER_ITEM_RE, SUCCESS_STATUS_RE, STATUS_ACTION_RE, SUCCESS_WITHOUT_EFFECT_RE, COUNT_UNCHANGED_RE, CLEARING_ACTION_RE, NOT_A_CLEARING_RE, SUCCESS_NO_STATUS_RE, OFF_BY_RE, NEGATED_SAFEGUARD_RE, BAD_VALUE_RE, REFUSAL_RE, TEST_FAIL_CONTRACT_RE;
14180
+ var BUG_LABEL_RE, SILENT_RE, CONCESSION_RE, SUCCESS_REPORT, SUCCESS_CONTRADICTED_RE, CONTRADICTION_VETO_RE, NOTHING_PROVIDED_RE, UNTOUCHED_SUBSETS, ACTION_LOOKBEHIND, TRAILING_ACTION_RE, ADJECTIVE_BEFORE_NOUN_RE, SUCCESS_SAME_STATE_RE, GENERIC_REPORT_RE, SAME_STATE, OTHER_ITEM_RE, SUCCESS_STATUS_RE, STATUS_ACTION_RE, SUCCESS_WITHOUT_EFFECT_RE, COUNT_UNCHANGED_RE, CLEARING_ACTION_RE, NOT_A_CLEARING_RE, SUCCESS_NO_STATUS_RE, OFF_BY_RE, NEGATED_SAFEGUARD_RE, NOT_KEPT_RE, REFUSED_INPUT_RE, SAFEGUARD_LOOKBEHIND, BAD_VALUE_RE, REFUSAL_RE, TEST_FAIL_CONTRACT_RE;
13718
14181
  var init_claims_defect = __esm({
13719
14182
  "../shared/dist/claims-defect.js"() {
13720
14183
  "use strict";
@@ -13758,7 +14221,10 @@ var init_claims_defect = __esm({
13758
14221
  NOT_A_CLEARING_RE = /\b(?:cancel|refresh|reload|preview|open|view|fail|hover|scroll|sort|filter|search)\w*/i;
13759
14222
  SUCCESS_NO_STATUS_RE = /\bsuccess\w*\b[^.;\n]{0,30}?\b(?:(?:do|does|did) not|don't|doesn't|didn't) (?:change|update|move)\b[^.;\n]{0,20}?\bstatus\b/i;
13760
14223
  OFF_BY_RE = /\boff[- ]by[- ](?:one|two|\d{1,3})\b/i;
13761
- NEGATED_SAFEGUARD_RE = /\bnot (?:rejected|validated|enforced|applied|persisted|saved)\b/i;
14224
+ NEGATED_SAFEGUARD_RE = /\bnot (rejected|validated|enforced|applied|persisted|saved)\b/gi;
14225
+ NOT_KEPT_RE = /^(?:applied|persisted|saved)$/i;
14226
+ REFUSED_INPUT_RE = /\b(?:blank|empty|whitespace|invalid|malformed|expired|duplicate[sd]?|too (?:long|short)|cancel(?:s|led|ed|ling|ing)?|discard\w*|escape)\b/i;
14227
+ SAFEGUARD_LOOKBEHIND = 80;
13762
14228
  BAD_VALUE_RE = /\b(?:incorrect|wrong|stale|negative|epoch|unix) (?:total|amount|value|date|count|price|label|plan|balance)\b/i;
13763
14229
  REFUSAL_RE = /\b(?<!not )(?:rejected|refused|denied|blocked|disallowed|prevented)\b|\breturns? (?:4\d\d|an? (?:error|validation))\b|\bshows (?:an? )?(?:error|validation)\b/i;
13764
14230
  TEST_FAIL_CONTRACT_RE = /\btest\.fail\s{0,20}\(/;
@@ -14617,7 +15083,7 @@ function findExpectCalls(code) {
14617
15083
  return out;
14618
15084
  }
14619
15085
  function countExpectCalls(code) {
14620
- return findExpectCalls(stripComments2(code)).length;
15086
+ return findExpectCalls(stripComments2(withExpectAliasesAsExpect(code))).length;
14621
15087
  }
14622
15088
  function countActionCalls(code) {
14623
15089
  return [...stripComments2(code).matchAll(ACTION_CALL)].length;
@@ -16946,6 +17412,7 @@ var init_test_authoring_quality = __esm({
16946
17412
  "../shared/dist/test-authoring-quality.js"() {
16947
17413
  "use strict";
16948
17414
  init_claims_defect();
17415
+ init_expect_call();
16949
17416
  init_readiness_poll();
16950
17417
  init_persistence_reread();
16951
17418
  init_write_response_check();
@@ -17072,7 +17539,7 @@ var init_test_authoring_quality = __esm({
17072
17539
  MAX_BALANCED_SCAN = 2048;
17073
17540
  MAX_CALLS = 500;
17074
17541
  MAX_SEEN_SCAN = 200;
17075
- ACTION_CALL = /\.(?:click|dblclick|fill|press|pressSequentially|type|check|uncheck|selectOption|setInputFiles|hover|tap|dragTo|goto)\s*\(/g;
17542
+ ACTION_CALL = /\.(?:(?:click|dblclick|fill|clear|press|pressSequentially|type|check|uncheck|setChecked|selectOption|setInputFiles|hover|tap|dragTo|dragAndDrop|dispatchEvent|goto)|(?:keyboard|mouse)\s*\.\s*(?:down|up|insertText|move|wheel))\s*\(/g;
17076
17543
  BY_NAME_LOCATOR = /\.getByRole\(\s{0,20}['"][^'"\n]{1,100}['"]\s{0,20},\s{0,20}\{[^}\n]{0,200}\bname\s{0,20}:|\.getByText\(|\.getByLabel\(|\.getByTitle\(|\.getByAltText\(|\.getByPlaceholder\(/;
17077
17544
  ACTION_AFTER = /^\s{0,20}\.\s{0,20}(?:click|fill|check|uncheck|press|type|hover|focus|selectOption|setInputFiles|dblclick|tap)\s{0,20}\(/;
17078
17545
  MAX_CONTACTS = 200;
@@ -18581,7 +19048,7 @@ function credentialEntries(maps) {
18581
19048
  }
18582
19049
  function replacePattern(value, form) {
18583
19050
  const escaped = form.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
18584
- return new RegExp(value.length < WHOLE_WORD_BELOW ? `(?<![A-Za-z0-9_])${escaped}(?![A-Za-z0-9_])` : escaped, "gi");
19051
+ return value.length < WHOLE_WORD_BELOW ? `(?<![A-Za-z0-9_])${escaped}(?![A-Za-z0-9_])` : escaped;
18585
19052
  }
18586
19053
  function findCredentialTextLeftover(text, maps) {
18587
19054
  const bare = text.split(WITHHELD_CREDENTIAL_TEXT).join(" ").replace(REFERENCE, " ").toLowerCase();
@@ -18595,11 +19062,17 @@ function scrubCredentialText(text, maps) {
18595
19062
  const entries = credentialEntries(maps);
18596
19063
  if (entries.length === 0)
18597
19064
  return text;
18598
- let out = text;
19065
+ const alternatives = [];
18599
19066
  for (const [key, value] of entries) {
18600
19067
  for (const form of credentialValueForms(value))
18601
- out = out.replace(replacePattern(value, form), `{{secret:${key}}}`);
19068
+ alternatives.push({ pattern: replacePattern(value, form), key, length: form.length });
18602
19069
  }
19070
+ alternatives.sort((a, b) => b.length - a.length);
19071
+ const matcher = new RegExp([REFERENCE.source, ...alternatives.map((alt) => `(${alt.pattern})`)].join("|"), "gi");
19072
+ const out = text.replace(matcher, (...args) => {
19073
+ const index = args.slice(1, 1 + alternatives.length).findIndex((group) => group !== void 0);
19074
+ return index < 0 ? String(args[0]) : `{{secret:${alternatives[index].key}}}`;
19075
+ });
18603
19076
  return findCredentialTextLeftover(out, maps) ? WITHHELD_CREDENTIAL_TEXT : out;
18604
19077
  }
18605
19078
  function scrubCredentialStringsIn(value, maps) {
@@ -19093,10 +19566,24 @@ function onlyUrlAfter(code, writeAt) {
19093
19566
  i = at + 6;
19094
19567
  }
19095
19568
  }
19569
+ function readsOrderAfter(code, writeAt) {
19570
+ const rest = code.slice(writeAt + 1);
19571
+ if (ORDER_READ_MARKERS.some((m) => rest.includes(m)) && nextExpect(code, writeAt + 1) >= 0)
19572
+ return true;
19573
+ for (let i = writeAt + 1; ; ) {
19574
+ const at = nextExpect(code, i);
19575
+ if (at < 0)
19576
+ return false;
19577
+ if (ORDERED_ARRAY_EXPECT.test(expectStatement(code, at)))
19578
+ return true;
19579
+ i = at + 6;
19580
+ }
19581
+ }
19096
19582
  function assessTitlePromiseShape(claimText, code) {
19097
19583
  const persistence = PERSISTENCE_RE.test(claimText);
19098
19584
  const refusal = REFUSAL_RE2.test(claimText);
19099
- if (!persistence && !refusal)
19585
+ const order = ORDER_RE.test(claimText) || ORDER_RE_CASED.test(claimText);
19586
+ if (!persistence && !refusal && !order)
19100
19587
  return [];
19101
19588
  const blank = blankCommentsAndStringLiterals(code);
19102
19589
  const writeAt = proofAnchor(blank);
@@ -19109,9 +19596,12 @@ function assessTitlePromiseShape(claimText, code) {
19109
19596
  if (refusal && onlyUrlAfter(blank, writeAt)) {
19110
19597
  findings.push({ rule: "rejection-only-url", reason: REFUSAL_REASON });
19111
19598
  }
19599
+ if (order && !readsOrderAfter(blank, writeAt)) {
19600
+ findings.push({ rule: "sort-without-order", reason: ORDER_REASON });
19601
+ }
19112
19602
  return findings;
19113
19603
  }
19114
- var PERSIST_REASON, REFUSAL_REASON, PERSISTENCE_RE, REFUSAL_RE2, WRITE_MARKERS, VALUE_WRITE_MARKERS, REREAD_MARKERS;
19604
+ var PERSIST_REASON, REFUSAL_REASON, PERSISTENCE_RE, REFUSAL_RE2, ORDER_REASON, ORDER_RE, ORDER_RE_CASED, ORDER_READ_MARKERS, ORDERED_ARRAY_EXPECT, WRITE_MARKERS, VALUE_WRITE_MARKERS, REREAD_MARKERS;
19115
19605
  var init_title_promise_shape = __esm({
19116
19606
  "../shared/dist/title-promise-shape.js"() {
19117
19607
  "use strict";
@@ -19120,6 +19610,21 @@ var init_title_promise_shape = __esm({
19120
19610
  REFUSAL_REASON = "The title says the app refuses the input, but the only check after submitting is the URL: assert the error message, the field's invalid state or the response status.";
19121
19611
  PERSISTENCE_RE = /\b(?:is|are|gets|stays)\s{1,20}saved\b|\b(?:is|are)\s{1,20}persisted\b|\bpersists\s{1,20}(?:after|across|through)\s{1,20}(?:a\s{1,20})?(?:reloads?|refresh(?:es)?|sessions?)\b|\bis\s{1,20}(?:stored|kept|remembered)\b|\bafter\s{1,20}(?:a\s{1,20})?reload\b|\bafter\s{1,20}refresh\b|\bsurvives\s{1,20}(?:a\s{1,20})?reload\b/i;
19122
19612
  REFUSAL_RE2 = /\b(?:rejects|rejected|refuses|refused|prevents|prevented|disallows)\b|\b(?:is|are|gets)\s{1,20}blocked\b|\bis\s{1,20}denied\b|\bshows\s{1,20}an\s{1,20}error\b|\bshows\s{1,20}a\s{1,20}validation\b|\bshows\s{1,20}validation\b|\breturns\s{1,20}4\d\d\b/i;
19613
+ ORDER_REASON = "The title says the list is sorted, but no check after the sort reads the order: assert the items in order (toHaveText([...]) on the list, the first and last item, or allTextContents() compared with a sorted copy).";
19614
+ ORDER_RE = /\bsort(?:s|ed|ing)?\b[^\n.]{0,60}?\bby\b|\b(?:ascending|descending)\b|\b(?:low|lowest)\s{1,20}to\s{1,20}(?:high|highest)\b|\b(?:high|highest)\s{1,20}to\s{1,20}(?:low|lowest)\b|\balphabetical(?:ly)?\b|\b(?:newest|oldest)\s{1,20}first\b/i;
19615
+ ORDER_RE_CASED = /\b[AZ]\s{0,3}(?:to|-|→)\s{0,3}[AZ]\b/;
19616
+ ORDER_READ_MARKERS = [
19617
+ ".allTextContents(",
19618
+ ".allInnerTexts(",
19619
+ ".evaluateAll(",
19620
+ ".toMatchAriaSnapshot(",
19621
+ ".sort(",
19622
+ ".toSorted(",
19623
+ ".first()",
19624
+ ".last()",
19625
+ ".nth("
19626
+ ];
19627
+ ORDERED_ARRAY_EXPECT = /\.(?:toHaveText|toContainText|toEqual|toStrictEqual)\s*\(\s*\[/;
19123
19628
  WRITE_MARKERS = [
19124
19629
  ".fill(",
19125
19630
  ".click(",
@@ -19451,7 +19956,7 @@ function assertionsReadVisibleContent(code) {
19451
19956
  });
19452
19957
  }
19453
19958
  function isLegacyReviewFlag(tags, activity) {
19454
- const flagged = tags.includes(NEEDS_REVIEW_TAG) || activity?.needsReview === true;
19959
+ const flagged = tags.includes(NEEDS_REVIEW_TAG2) || activity?.needsReview === true;
19455
19960
  const source = activity?.needsReviewSource;
19456
19961
  return flagged && !(typeof source === "string" && source in SOURCE_RANK);
19457
19962
  }
@@ -19513,7 +20018,7 @@ function recheckGeneratorNotes(notes, input) {
19513
20018
  }
19514
20019
  function reviewHolds(tags, activity, cleared = /* @__PURE__ */ new Set()) {
19515
20020
  const out = [];
19516
- const tagged = tags.includes(NEEDS_REVIEW_TAG);
20021
+ const tagged = tags.includes(NEEDS_REVIEW_TAG2);
19517
20022
  const generatorAsked = activity?.needsReview === true;
19518
20023
  if (tagged) {
19519
20024
  if (activity?.needsReviewSource === "human")
@@ -19573,7 +20078,7 @@ function evaluatePromotionGate(input, checks = PROMOTION_QUALITY_CHECKS) {
19573
20078
  ...suspiciousTagged ? { suspiciousPass: { mode: suspicious, covered: suspiciousCovered, wouldHold: suspiciousHold } } : {}
19574
20079
  };
19575
20080
  }
19576
- var SOURCE_RANK, strengthCheck, STRING_CONST, TYPED_VALUE, TYPED_CONSTANT, MAX_STATEMENTS, MAX_STATEMENT_CHARS, LITERAL_OR_IDENTIFIER, MUTATING_ACTION, FEEDBACK_REGION, ONE_SHOT_MESSAGE_REASON, FEEDBACK_REGION_ALL, dynamicAssertionCheck, OUTCOME_KIND_MATCHER, ROUTE_TERM, ROUTE_TRAILING_PUNCTUATION, QUOTED_LITERAL, GENERIC_LITERAL, MAX_OUTCOME_STATEMENTS, MAX_OUTCOMES, MAX_OUTCOME_TEXT_CHARS, expectedOutcomeCheck, testDataCheck, sharedAccountCheck, clockLiteralCheck, readinessPollCheck, knownBugTestCheck, bugClaimContractCheck, suspiciousExpectationCheck, bugCandidateCheck, conventionOutcomesCheck, pendingHook, locatorFragmentCheck, FILTER_STEP, NEXT_ACTION, REDUCTION_PROOF, MAX_FILTER_LINES, MAX_FILTER_LINE_CHARS, filterPositiveOnlyCheck, PROMOTION_QUALITY_CHECKS, PROMOTION_GATE_MAX_CODE_CHARS, NOT_EVALUATED, SUSPICIOUS_PASS_TAG, NOT_CONTENT_MATCHER, CONTENT_PROOF, NAMED_ELEMENT, NEEDS_REVIEW_TAG, count2, RECHECKED_NOTES, EVIDENCE_NOTE_PREFIXES, PENDING_RECHECK_NOTE_PREFIXES, STRENGTH_RANK;
20081
+ var SOURCE_RANK, strengthCheck, STRING_CONST, TYPED_VALUE, TYPED_CONSTANT, MAX_STATEMENTS, MAX_STATEMENT_CHARS, LITERAL_OR_IDENTIFIER, MUTATING_ACTION, FEEDBACK_REGION, ONE_SHOT_MESSAGE_REASON, FEEDBACK_REGION_ALL, dynamicAssertionCheck, OUTCOME_KIND_MATCHER, ROUTE_TERM, ROUTE_TRAILING_PUNCTUATION, QUOTED_LITERAL, GENERIC_LITERAL, MAX_OUTCOME_STATEMENTS, MAX_OUTCOMES, MAX_OUTCOME_TEXT_CHARS, expectedOutcomeCheck, testDataCheck, sharedAccountCheck, clockLiteralCheck, readinessPollCheck, knownBugTestCheck, bugClaimContractCheck, suspiciousExpectationCheck, bugCandidateCheck, conventionOutcomesCheck, pendingHook, locatorFragmentCheck, FILTER_STEP, NEXT_ACTION, REDUCTION_PROOF, MAX_FILTER_LINES, MAX_FILTER_LINE_CHARS, filterPositiveOnlyCheck, PROMOTION_QUALITY_CHECKS, PROMOTION_GATE_MAX_CODE_CHARS, NOT_EVALUATED, SUSPICIOUS_PASS_TAG, NOT_CONTENT_MATCHER, CONTENT_PROOF, NAMED_ELEMENT, NEEDS_REVIEW_TAG2, count2, RECHECKED_NOTES, EVIDENCE_NOTE_PREFIXES, PENDING_RECHECK_NOTE_PREFIXES, STRENGTH_RANK;
19577
20082
  var init_promotion_quality_gate = __esm({
19578
20083
  "../shared/dist/promotion-quality-gate.js"() {
19579
20084
  "use strict";
@@ -19592,8 +20097,8 @@ var init_promotion_quality_gate = __esm({
19592
20097
  SOURCE_RANK = { generator: 0, classifier: 0, healer: 1, human: 2 };
19593
20098
  strengthCheck = {
19594
20099
  id: "assertion-strength",
19595
- run({ code, tags, strengthGate, flagContradictedAdditions, lastActionProof }) {
19596
- const verdict = validateAssertionStrength(code, { flagContradictedAdditions: flagContradictedAdditions === true, lastActionProof });
20100
+ run({ code, tags, strengthGate, flagContradictedAdditions, lastActionProof, absenceProof }) {
20101
+ const verdict = validateAssertionStrength(code, { flagContradictedAdditions: flagContradictedAdditions === true, lastActionProof, absenceProof });
19597
20102
  if (verdict.severity === "route-bug")
19598
20103
  return [{ check: "route-bug" }];
19599
20104
  const authDowngrade = verdict.severity === "downgrade" && tags.some((t) => t.toLowerCase() === "@auth");
@@ -19851,7 +20356,7 @@ ${goalText}`);
19851
20356
  NOT_CONTENT_MATCHER = /\.(?:toHaveURL|toHaveTitle|toBeOK)\s*\(|\bwaitForURL\s*\(|\.(?:status|ok)\s*\(\s*\)/;
19852
20357
  CONTENT_PROOF = /* @__PURE__ */ new Set(["controlled", "revealed", "value"]);
19853
20358
  NAMED_ELEMENT = /\bgetByRole\s*\(\s*['"`](?!(?:navigation|banner|main|contentinfo|complementary|region|search)['"`])\w{1,30}['"`]\s*,\s*\{[^}]{0,200}?\bname\s*:\s*(['"`])[^'"`$\n]{1,200}\1|\bgetByLabel\s*\(\s*(['"`])[^'"`$\n]{1,200}\2/;
19854
- NEEDS_REVIEW_TAG = "@needs-review";
20359
+ NEEDS_REVIEW_TAG2 = "@needs-review";
19855
20360
  count2 = (v) => typeof v === "number" && Number.isFinite(v) ? v : 0;
19856
20361
  RECHECKED_NOTES = [
19857
20362
  {
@@ -19972,11 +20477,18 @@ var init_regex_alternation = __esm({
19972
20477
  function isPageEvidenceEntry(entry) {
19973
20478
  return typeof entry === "object" && entry !== null && entry.outcomeType === PAGE_LOADING_OUTCOME_TYPE;
19974
20479
  }
19975
- var PAGE_LOADING_OUTCOME_TYPE;
20480
+ function pageScreenshotKey(projectId, safeName, stillLoading = false) {
20481
+ return `discovery/${projectId}/screenshots/${stillLoading ? `${STILL_LOADING_SCREENSHOT_DIR}/` : ""}${safeName}.png`;
20482
+ }
20483
+ function isStillLoadingScreenshotKey(key) {
20484
+ return typeof key === "string" && key.includes(`/screenshots/${STILL_LOADING_SCREENSHOT_DIR}/`);
20485
+ }
20486
+ var PAGE_LOADING_OUTCOME_TYPE, STILL_LOADING_SCREENSHOT_DIR;
19976
20487
  var init_page_evidence = __esm({
19977
20488
  "../shared/dist/page-evidence.js"() {
19978
20489
  "use strict";
19979
20490
  PAGE_LOADING_OUTCOME_TYPE = "LOADING";
20491
+ STILL_LOADING_SCREENSHOT_DIR = "still-loading";
19980
20492
  }
19981
20493
  });
19982
20494
 
@@ -20381,11 +20893,13 @@ __export(dist_exports, {
20381
20893
  SERVER_INTERRUPT_ERROR_PREFIX: () => SERVER_INTERRUPT_ERROR_PREFIX,
20382
20894
  SHARED_PRICING_PLAN_MAP: () => SHARED_PRICING_PLAN_MAP,
20383
20895
  SHARED_SERVER_APPLICATION_NAME: () => SHARED_SERVER_APPLICATION_NAME,
20896
+ SMS_NOT_RECEIVED_PREFIXES: () => SMS_NOT_RECEIVED_PREFIXES,
20384
20897
  SPEND_SOURCE_KEY: () => SPEND_SOURCE_KEY,
20385
20898
  SPEND_SOURCE_SERVER: () => SPEND_SOURCE_SERVER,
20386
20899
  STATED_LIMITS_VERSION: () => STATED_LIMITS_VERSION,
20387
20900
  STATED_PATTERN_MAX: () => STATED_PATTERN_MAX,
20388
20901
  STATIC_STANDARD_MIN_INTERACTIVE: () => STATIC_STANDARD_MIN_INTERACTIVE,
20902
+ STILL_LOADING_SCREENSHOT_DIR: () => STILL_LOADING_SCREENSHOT_DIR,
20389
20903
  STT_MODEL_OPTIONS: () => STT_MODEL_OPTIONS,
20390
20904
  SUCCESS_NOTICE_TEXT: () => SUCCESS_NOTICE_TEXT,
20391
20905
  SUPPORT_EMAIL: () => SUPPORT_EMAIL,
@@ -20413,6 +20927,7 @@ __export(dist_exports, {
20413
20927
  TTS_VOICE_OPTIONS: () => TTS_VOICE_OPTIONS,
20414
20928
  UI_VARIANTS: () => UI_VARIANTS,
20415
20929
  UNEXPECTED_PASS_ERROR_PREFIX: () => UNEXPECTED_PASS_ERROR_PREFIX,
20930
+ UNVERIFIED_HEAL_CONFIDENCES: () => UNVERIFIED_HEAL_CONFIDENCES,
20416
20931
  USER_CANCELLED_RUN_ERROR: () => USER_CANCELLED_RUN_ERROR,
20417
20932
  VISUAL_MASK_SELECTORS_MAX: () => VISUAL_MASK_SELECTORS_MAX,
20418
20933
  VISUAL_MASK_SELECTOR_FORBIDDEN: () => VISUAL_MASK_SELECTOR_FORBIDDEN,
@@ -20466,6 +20981,7 @@ __export(dist_exports, {
20466
20981
  blankBlockComments: () => blankBlockComments3,
20467
20982
  blankCommentsAndStringLiterals: () => blankCommentsAndStringLiterals,
20468
20983
  blankKeepingLines: () => blankKeepingLines,
20984
+ blockedRunLogLine: () => blockedRunLogLine,
20469
20985
  boundedCallCostUsd: () => boundedCallCostUsd,
20470
20986
  brokerResponseBody: () => brokerResponseBody,
20471
20987
  bugFoundQuarantineWhere: () => bugFoundQuarantineWhere,
@@ -20571,6 +21087,7 @@ __export(dist_exports, {
20571
21087
  evaluatePromotionGate: () => evaluatePromotionGate,
20572
21088
  excludeCancelledRunsAnd: () => excludeCancelledRunsAnd,
20573
21089
  excludeCancelledRunsWhere: () => excludeCancelledRunsWhere,
21090
+ expectAliasNames: () => expectAliasNames,
20574
21091
  explicitAgenticCliOverrideError: () => explicitAgenticCliOverrideError,
20575
21092
  extractAssertedControlSignatures: () => extractAssertedControlSignatures,
20576
21093
  extractMuseAssistantJson: () => extractMuseAssistantJson,
@@ -20637,6 +21154,8 @@ __export(dist_exports, {
20637
21154
  googleGisRequestUrl: () => googleGisRequestUrl,
20638
21155
  googleSignInLimitLines: () => googleSignInLimitLines,
20639
21156
  googleSignInPolicyProblems: () => googleSignInPolicyProblems,
21157
+ googleSignInRefusedLine: () => googleSignInRefusedLine,
21158
+ googleSignInRefusedReason: () => googleSignInRefusedReason,
20640
21159
  hasCrossPlatformIdentity: () => hasCrossPlatformIdentity,
20641
21160
  hasEncodedRequestBody: () => hasEncodedRequestBody,
20642
21161
  hasEngineSpecParts: () => hasEngineSpecParts,
@@ -20680,6 +21199,7 @@ __export(dist_exports, {
20680
21199
  isCodeFactNote: () => isCodeFactNote,
20681
21200
  isConfigLikeReason: () => isConfigLikeReason,
20682
21201
  isCountedFailure: () => isCountedFailure,
21202
+ isCredentialHeaderName: () => isCredentialHeaderName,
20683
21203
  isCreditPackAmountCents: () => isCreditPackAmountCents,
20684
21204
  isDbPricingPlan: () => isDbPricingPlan,
20685
21205
  isDeniedGoogleBrokerRedirectHost: () => isDeniedGoogleBrokerRedirectHost,
@@ -20735,8 +21255,10 @@ __export(dist_exports, {
20735
21255
  isSharedAuthTenantHost: () => isSharedAuthTenantHost,
20736
21256
  isSharedSuffix: () => isSharedSuffix,
20737
21257
  isSingleEmailAddress: () => isSingleEmailAddress,
21258
+ isSmsNotReceivedError: () => isSmsNotReceivedError,
20738
21259
  isSoftOutcome: () => isSoftOutcome,
20739
21260
  isStateValueMobileClassName: () => isStateValueMobileClassName,
21261
+ isStillLoadingScreenshotKey: () => isStillLoadingScreenshotKey,
20740
21262
  isSttModelForProvider: () => isSttModelForProvider,
20741
21263
  isTestReadyForPlatform: () => isTestReadyForPlatform,
20742
21264
  isTestVariableSecretKey: () => isTestVariableSecretKey,
@@ -20744,6 +21266,7 @@ __export(dist_exports, {
20744
21266
  isUIVariant: () => isUIVariant,
20745
21267
  isUnexpectedPassError: () => isUnexpectedPassError,
20746
21268
  isUnsafePrecondition: () => isUnsafePrecondition,
21269
+ isUnverifiedHealPass: () => isUnverifiedHealPass,
20747
21270
  isUsableDeepgramKey: () => isUsableDeepgramKey,
20748
21271
  isUserCancelledError: () => isUserCancelledError,
20749
21272
  isValidAgenticCliModel: () => isValidAgenticCliModel,
@@ -20768,8 +21291,10 @@ __export(dist_exports, {
20768
21291
  lintTestData: () => lintTestData,
20769
21292
  lintTestQuality: () => lintTestQuality,
20770
21293
  literalUrlRegex: () => literalUrlRegex,
21294
+ loginFormAlertTexts: () => loginFormAlertTexts,
20771
21295
  longCredentialValues: () => longCredentialValues,
20772
21296
  maskedOutsideStrings: () => maskedOutsideStrings,
21297
+ mayBeUnverifiedHealPass: () => mayBeUnverifiedHealPass,
20773
21298
  mediumForMobilePlatform: () => mediumForMobilePlatform,
20774
21299
  mergePageType: () => mergePageType,
20775
21300
  mergeSuspectedBugsAcrossSurveys: () => mergeSuspectedBugsAcrossSurveys,
@@ -20803,7 +21328,9 @@ __export(dist_exports, {
20803
21328
  opensRegexLiteral: () => opensRegexLiteral,
20804
21329
  otherMobilePlatform: () => otherMobilePlatform,
20805
21330
  outcomeNamedByAssertion: () => outcomeNamedByAssertion,
21331
+ pageScreenshotKey: () => pageScreenshotKey,
20806
21332
  parenDepthDelta: () => parenDepthDelta,
21333
+ parseAbsenceProofMode: () => parseAbsenceProofMode,
20807
21334
  parseApiCardModelUpdate: () => parseApiCardModelUpdate,
20808
21335
  parseAppTimezone: () => parseAppTimezone,
20809
21336
  parseAuditGroupScopeId: () => parseAuditGroupScopeId,
@@ -20879,7 +21406,7 @@ __export(dist_exports, {
20879
21406
  redactNeedlesText: () => redactNeedlesText,
20880
21407
  redirectOnAppSite: () => redirectOnAppSite,
20881
21408
  refuseEncodedRequestBodies: () => refuseEncodedRequestBodies,
20882
- regexLiteralEnd: () => regexLiteralEnd2,
21409
+ regexLiteralEnd: () => regexLiteralEnd,
20883
21410
  regexOpensAfter: () => regexOpensAfter,
20884
21411
  registrableDomain: () => registrableDomain,
20885
21412
  renderIdentity: () => renderIdentity,
@@ -20921,6 +21448,9 @@ __export(dist_exports, {
20921
21448
  saveOpensConfirm: () => saveOpensConfirm,
20922
21449
  scanClockDependentLiterals: () => scanClockDependentLiterals,
20923
21450
  scanHtmlTags: () => scanHtmlTags,
21451
+ scrubCredentialHeaderValue: () => scrubCredentialHeaderValue,
21452
+ scrubCredentialHeaders: () => scrubCredentialHeaders,
21453
+ scrubCredentialHeadersDeep: () => scrubCredentialHeadersDeep,
20924
21454
  scrubCredentialStringsIn: () => scrubCredentialStringsIn,
20925
21455
  scrubCredentialText: () => scrubCredentialText,
20926
21456
  scrubSearchParams: () => scrubSearchParams,
@@ -20931,9 +21461,12 @@ __export(dist_exports, {
20931
21461
  selectorForValue: () => selectorForValue,
20932
21462
  setAgenticCliRuntime: () => setAgenticCliRuntime,
20933
21463
  setProviderChainOverrides: () => setProviderChainOverrides,
21464
+ signInStepPassedBeforeFailure: () => signInStepPassedBeforeFailure,
21465
+ signsInInline: () => signsInInline,
20934
21466
  signupPlanFor: () => signupPlanFor,
20935
21467
  simplifyMobileWanderingInPlace: () => simplifyMobileWanderingInPlace,
20936
21468
  siteOf: () => siteOf,
21469
+ specCallsExpect: () => specCallsExpect,
20937
21470
  specItemsFromTestPlans: () => specItemsFromTestPlans,
20938
21471
  splitAnswerLines: () => splitAnswerLines,
20939
21472
  statedLimitsFromAttrs: () => statedLimitsFromAttrs,
@@ -20981,6 +21514,7 @@ __export(dist_exports, {
20981
21514
  visualProDailyBudget: () => visualProDailyBudget,
20982
21515
  voiceMatchesTtsModel: () => voiceMatchesTtsModel,
20983
21516
  voicesForTtsModel: () => voicesForTtsModel,
21517
+ withExpectAliasesAsExpect: () => withExpectAliasesAsExpect,
20984
21518
  withGeneratorReviewSource: () => withGeneratorReviewSource,
20985
21519
  withReviewSource: () => withReviewSource,
20986
21520
  withoutNotChargedClaim: () => withoutNotChargedClaim,
@@ -21035,6 +21569,7 @@ var init_dist = __esm({
21035
21569
  init_encoded_forms();
21036
21570
  init_block_comments();
21037
21571
  init_redact_needles();
21572
+ init_credential_header_scrub();
21038
21573
  init_voice_config();
21039
21574
  init_deepgram_speech();
21040
21575
  init_deepgram_voices();
@@ -23402,7 +23937,13 @@ await expect(page).not.toHaveURL(/accounts\\.google\\.com/, { timeout: 60_000 })
23402
23937
  \`\`\`
23403
23938
  Never emit code that clicks, fills or asserts anything on accounts.google.com.`;
23404
23939
  }
23405
- function buildGoogleSignInTestGenSection(_opts) {
23940
+ function buildGoogleSignInTestGenSection(_opts, ctx = {}) {
23941
+ if (ctx.appLoginGiven) {
23942
+ return `
23943
+ ## SIGN-IN: USE THE TEST ACCOUNT
23944
+ This project gave a test account (the Browser Credentials / \`process.env.TEST_USER_*\` values). Sign in with it on the app's own email (or username) and password form, even when the app also offers Continue with Google. Do not click Continue with Google / Sign in with Google / Sign up with Google to sign in.
23945
+ Use Continue with Google only when this scenario's goal is the Google sign-in itself, or the app has no email/username and password sign-in at all. Then click that control on the app and wait until the browser is back on the app: \`await expect(page).not.toHaveURL(/accounts\\.google\\.com/, { timeout: 60_000 })\` (the runner completes Google's step). Never click, fill or assert anything on accounts.google.com, never type a password or code there.`;
23946
+ }
23406
23947
  return `
23407
23948
  ## GOOGLE SIGN-IN (the runner completes Google's step)
23408
23949
  When this scenario uses Continue with Google / Sign in with Google / Sign up with Google:
@@ -23988,7 +24529,6 @@ var require_browser_config = __commonJS({
23988
24529
  exports2.NO_ERROR_PAGE_RELOAD_ARG,
23989
24530
  "--disable-dev-shm-usage",
23990
24531
  "--disable-gpu",
23991
- "--disable-software-rasterizer",
23992
24532
  "--no-zygote",
23993
24533
  "--disable-accelerated-2d-canvas",
23994
24534
  "--js-flags=--max-old-space-size=512"
@@ -94798,6 +95338,7 @@ var require_cli_browser_socket = __commonJS({
94798
95338
  exports2.assertSlotLease = assertSlotLease;
94799
95339
  exports2.prepareSessionSocket = prepareSessionSocket;
94800
95340
  exports2.assertLaunchOpensNoPort = assertLaunchOpensNoPort;
95341
+ exports2.devtoolsSwitchInCmdline = devtoolsSwitchInCmdline;
94801
95342
  exports2.assertBrowserProcessOpensNoPort = assertBrowserProcessOpensNoPort;
94802
95343
  exports2.confineOutputDirPaths = confineOutputDirPaths;
94803
95344
  exports2.openCliBrowserSocket = openCliBrowserSocket;
@@ -95110,6 +95651,9 @@ var require_cli_browser_socket = __commonJS({
95110
95651
  }
95111
95652
  return children;
95112
95653
  }
95654
+ function devtoolsSwitchInCmdline(cmdline) {
95655
+ return cmdline.split(/[\0\s]+/).find((arg) => DEVTOOLS_ENDPOINT_SWITCH.test(arg));
95656
+ }
95113
95657
  async function assertBrowserProcessOpensNoPort(rootPid) {
95114
95658
  if (process.platform !== "linux")
95115
95659
  return;
@@ -95122,15 +95666,15 @@ var require_cli_browser_socket = __commonJS({
95122
95666
  if (pids.length === 0)
95123
95667
  throw new CliBrowserSocketUnsafeError("could not find the browser process to check");
95124
95668
  for (const pid of pids) {
95125
- let argv;
95669
+ let cmdline;
95126
95670
  try {
95127
- argv = (await (0, promises_1.readFile)(`/proc/${pid}/cmdline`, "utf8")).split("\0");
95671
+ cmdline = await (0, promises_1.readFile)(`/proc/${pid}/cmdline`, "utf8");
95128
95672
  } catch (error2) {
95129
95673
  if (!rootPid && error2.code === "ENOENT")
95130
95674
  continue;
95131
95675
  throw new CliBrowserSocketUnsafeError(`could not read the command line of the browser (pid ${pid})`);
95132
95676
  }
95133
- const flag = argv.find((arg) => DEVTOOLS_ENDPOINT_SWITCH.test(arg));
95677
+ const flag = devtoolsSwitchInCmdline(cmdline);
95134
95678
  if (flag)
95135
95679
  throw new CliBrowserSocketUnsafeError(`the browser (pid ${pid}) was started with ${flag}`);
95136
95680
  }