@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.
- package/README.md +7 -2
- package/dist/{chunk-YI3ZC2XI.mjs → chunk-6N4QCCEH.mjs} +2 -2
- package/dist/{chunk-CMM4DOXC.mjs → chunk-EBWZ7NGU.mjs} +11 -8
- package/dist/{chunk-OTKJZIOD.mjs → chunk-EKY6CN4Y.mjs} +659 -122
- package/dist/{chunk-CG75I3ZZ.mjs → chunk-MEE4DXRX.mjs} +13437 -1386
- package/dist/{chunk-WVLBQDFP.mjs → chunk-OXDU6ARE.mjs} +8 -2
- package/dist/{chunk-AAUHXGDS.mjs → chunk-PUMRAFJS.mjs} +37 -13
- package/dist/{chunk-BECCGMU4.mjs → chunk-TGVKABFN.mjs} +45 -3
- package/dist/{chunk-YIM746WO.mjs → chunk-TS4OUJVT.mjs} +1 -1
- package/dist/{chunk-NNJYKR3O.mjs → chunk-UR7KDOTP.mjs} +29 -6
- package/dist/{chunk-36AWDKPL.mjs → chunk-UZ67UBXH.mjs} +14 -3
- package/dist/{chunk-7SH3COMQ.mjs → chunk-VWE3SQ2Z.mjs} +1 -1
- package/dist/{chunk-TK5KEGNW.mjs → chunk-XXZZMG6Q.mjs} +15 -3
- package/dist/cli.js +20962 -7343
- package/dist/cli.mjs +1159 -245
- package/dist/credential-mcp-server.js +671 -127
- package/dist/credential-mcp-server.mjs +3 -3
- package/dist/discovery-mcp-server.js +710 -129
- package/dist/discovery-mcp-server.mjs +4 -4
- package/dist/dist-BXHLIG5H.mjs +17 -0
- package/dist/inbox-mcp-server.js +664 -123
- package/dist/inbox-mcp-server.mjs +3 -3
- package/dist/route-seed-worker.js +671 -124
- package/dist/route-seed-worker.mjs +3 -3
- package/dist/run-write-guard-preload.js +713 -125
- package/dist/run-write-guard-preload.mjs +4 -4
- package/dist/run-write-guard.js +713 -125
- package/dist/run-write-guard.mjs +3 -3
- package/dist/tar-extract-child.js +38 -14
- package/dist/tar-extract-child.mjs +2 -2
- package/package.json +3 -2
- package/dist/dist-2G7YTNH5.mjs +0 -17
|
@@ -114,7 +114,13 @@ function buildSandboxedEnv(processEnv, testVariables) {
|
|
|
114
114
|
return env;
|
|
115
115
|
}
|
|
116
116
|
function escapeEnvValue(value) {
|
|
117
|
-
|
|
117
|
+
if (!value.includes("\r")) {
|
|
118
|
+
for (const quote of ["'", "`"]) {
|
|
119
|
+
if (!value.includes(quote))
|
|
120
|
+
return `${quote}${value}${quote}`;
|
|
121
|
+
}
|
|
122
|
+
}
|
|
123
|
+
const escaped = value.replace(/"/g, '\\"').replace(/\n/g, "\\n").replace(/\r/g, "\\r");
|
|
118
124
|
return `"${escaped}"`;
|
|
119
125
|
}
|
|
120
126
|
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;
|
|
@@ -3036,6 +3042,65 @@ var init_discovery_har_decision = __esm({
|
|
|
3036
3042
|
}
|
|
3037
3043
|
});
|
|
3038
3044
|
|
|
3045
|
+
// ../shared/dist/regex-literal.js
|
|
3046
|
+
function opensRegexLiteral(code, i) {
|
|
3047
|
+
let j = i - 1;
|
|
3048
|
+
while (j >= 0 && (code[j] === " " || code[j] === " "))
|
|
3049
|
+
j--;
|
|
3050
|
+
if (j < 0 || code[j] === "\n" || code[j] === "\r")
|
|
3051
|
+
return true;
|
|
3052
|
+
return regexOpensAfter(code, j);
|
|
3053
|
+
}
|
|
3054
|
+
function regexOpensAfter(code, j) {
|
|
3055
|
+
const prev = code[j];
|
|
3056
|
+
if (REGEX_OPENERS.includes(prev))
|
|
3057
|
+
return !((prev === "+" || prev === "-") && code[j - 1] === prev);
|
|
3058
|
+
if (!/[a-z]/.test(prev))
|
|
3059
|
+
return false;
|
|
3060
|
+
let start = j;
|
|
3061
|
+
while (start > 0 && /[A-Za-z0-9_$]/.test(code[start - 1]))
|
|
3062
|
+
start--;
|
|
3063
|
+
if (start > 0 && code[start - 1] === ".")
|
|
3064
|
+
return false;
|
|
3065
|
+
return REGEX_KEYWORDS.has(code.slice(start, j + 1));
|
|
3066
|
+
}
|
|
3067
|
+
function regexLiteralEnd(code, i) {
|
|
3068
|
+
let k = i + 1;
|
|
3069
|
+
let inClass = false;
|
|
3070
|
+
while (k < code.length) {
|
|
3071
|
+
const ch = code[k];
|
|
3072
|
+
if (ch === "\n" || ch === "\r")
|
|
3073
|
+
return k;
|
|
3074
|
+
if (ch === "\\") {
|
|
3075
|
+
if (code[k + 1] === "\n" || code[k + 1] === "\r")
|
|
3076
|
+
return k + 1;
|
|
3077
|
+
k += 2;
|
|
3078
|
+
continue;
|
|
3079
|
+
}
|
|
3080
|
+
if (inClass) {
|
|
3081
|
+
if (ch === "]")
|
|
3082
|
+
inClass = false;
|
|
3083
|
+
} else if (ch === "[") {
|
|
3084
|
+
inClass = true;
|
|
3085
|
+
} else if (ch === "/") {
|
|
3086
|
+
k++;
|
|
3087
|
+
while (k < code.length && /[a-z]/i.test(code[k]))
|
|
3088
|
+
k++;
|
|
3089
|
+
return k;
|
|
3090
|
+
}
|
|
3091
|
+
k++;
|
|
3092
|
+
}
|
|
3093
|
+
return code.length;
|
|
3094
|
+
}
|
|
3095
|
+
var REGEX_OPENERS, REGEX_KEYWORDS;
|
|
3096
|
+
var init_regex_literal = __esm({
|
|
3097
|
+
"../shared/dist/regex-literal.js"() {
|
|
3098
|
+
"use strict";
|
|
3099
|
+
REGEX_OPENERS = "(,=:[!&|?{};+-*%~^<>";
|
|
3100
|
+
REGEX_KEYWORDS = /* @__PURE__ */ new Set(["return", "typeof", "case", "do", "else", "in", "of", "new", "delete", "void", "throw", "await", "yield", "instanceof"]);
|
|
3101
|
+
}
|
|
3102
|
+
});
|
|
3103
|
+
|
|
3039
3104
|
// ../shared/dist/assertion-classifier.js
|
|
3040
3105
|
function isButtonOrLinkLocator(line) {
|
|
3041
3106
|
return /getByRole\s*\(\s*['"`](?:button|link)['"`]/i.test(line);
|
|
@@ -3344,7 +3409,7 @@ function classifyMobileAssertValue(value, context = {}) {
|
|
|
3344
3409
|
return { tier: 2, reason: t2[1] };
|
|
3345
3410
|
return null;
|
|
3346
3411
|
}
|
|
3347
|
-
function
|
|
3412
|
+
function regexLiteralEnd2(text, start) {
|
|
3348
3413
|
let inClass = false;
|
|
3349
3414
|
for (let i = start + 1; i < text.length; i++) {
|
|
3350
3415
|
const ch = text[i];
|
|
@@ -3370,7 +3435,7 @@ function parenDepthDelta(text) {
|
|
|
3370
3435
|
let depth = 0;
|
|
3371
3436
|
let quote = null;
|
|
3372
3437
|
let escaped = false;
|
|
3373
|
-
let
|
|
3438
|
+
let prevAt = -1;
|
|
3374
3439
|
let noRegexUntil = -1;
|
|
3375
3440
|
for (let i = 0; i < text.length; i++) {
|
|
3376
3441
|
const ch = text[i];
|
|
@@ -3385,7 +3450,7 @@ function parenDepthDelta(text) {
|
|
|
3385
3450
|
if (quote) {
|
|
3386
3451
|
if (ch === quote) {
|
|
3387
3452
|
quote = null;
|
|
3388
|
-
|
|
3453
|
+
prevAt = i;
|
|
3389
3454
|
}
|
|
3390
3455
|
continue;
|
|
3391
3456
|
}
|
|
@@ -3404,11 +3469,11 @@ function parenDepthDelta(text) {
|
|
|
3404
3469
|
i = close + 1;
|
|
3405
3470
|
continue;
|
|
3406
3471
|
}
|
|
3407
|
-
if (i > noRegexUntil && (
|
|
3408
|
-
const end =
|
|
3472
|
+
if (i > noRegexUntil && (prevAt < 0 || regexOpensAfter(text, prevAt))) {
|
|
3473
|
+
const end = regexLiteralEnd2(text, i);
|
|
3409
3474
|
if (end > 0) {
|
|
3410
3475
|
i = end - 1;
|
|
3411
|
-
|
|
3476
|
+
prevAt = end - 1;
|
|
3412
3477
|
continue;
|
|
3413
3478
|
}
|
|
3414
3479
|
const nl = text.indexOf("\n", i);
|
|
@@ -3420,14 +3485,15 @@ function parenDepthDelta(text) {
|
|
|
3420
3485
|
else if (ch === ")")
|
|
3421
3486
|
depth--;
|
|
3422
3487
|
if (ch !== " " && ch !== " " && ch !== "\n")
|
|
3423
|
-
|
|
3488
|
+
prevAt = i;
|
|
3424
3489
|
}
|
|
3425
3490
|
return depth;
|
|
3426
3491
|
}
|
|
3427
|
-
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
|
|
3492
|
+
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;
|
|
3428
3493
|
var init_assertion_classifier = __esm({
|
|
3429
3494
|
"../shared/dist/assertion-classifier.js"() {
|
|
3430
3495
|
"use strict";
|
|
3496
|
+
init_regex_literal();
|
|
3431
3497
|
PENDING_STATE_TEXT_REASON = 'pending-state text ("Sending...") is transient';
|
|
3432
3498
|
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}|…)/;
|
|
3433
3499
|
TIER1_PATTERNS = [
|
|
@@ -3504,7 +3570,6 @@ var init_assertion_classifier = __esm({
|
|
|
3504
3570
|
new RegExp(`\\.filter\\s*\\(\\s*\\{[^}]{0,200}?\\bhasText\\s*:\\s*${LOCATOR_ARG_VALUE}`, "gi")
|
|
3505
3571
|
];
|
|
3506
3572
|
EXPECT_TOKEN = /\bexpect\s*[.(]/;
|
|
3507
|
-
REGEX_PRECEDER = /* @__PURE__ */ new Set(["(", ",", "=", ":", "[", "!", "&", "|", "?", "{", "}", ";", "+", "-", "*", "%", "<", ">", "~", "^"]);
|
|
3508
3573
|
}
|
|
3509
3574
|
});
|
|
3510
3575
|
|
|
@@ -3512,14 +3577,80 @@ var init_assertion_classifier = __esm({
|
|
|
3512
3577
|
function isExpectCall(code) {
|
|
3513
3578
|
return EXPECT_CALL_RE.test(code);
|
|
3514
3579
|
}
|
|
3580
|
+
function specCallsExpect(code) {
|
|
3581
|
+
if (EXPECT_CALL_RE.test(code) || SPACED_EXPECT_CALL_RE.test(code) || CONFIGURE_IN_PLACE_RE.test(code))
|
|
3582
|
+
return true;
|
|
3583
|
+
const names = /* @__PURE__ */ new Set();
|
|
3584
|
+
for (const clause of code.matchAll(IMPORT_CLAUSE_RE)) {
|
|
3585
|
+
for (const alias of clause[1].matchAll(EXPECT_ALIAS_RE))
|
|
3586
|
+
names.add(alias[1]);
|
|
3587
|
+
}
|
|
3588
|
+
for (const decl of code.matchAll(DERIVED_EXPECT_DECL_RE)) {
|
|
3589
|
+
if (names.size >= MAX_DERIVED_EXPECT_NAMES)
|
|
3590
|
+
break;
|
|
3591
|
+
const base = decl[2];
|
|
3592
|
+
if (base === void 0 || base === "expect" || names.has(base))
|
|
3593
|
+
names.add(decl[1]);
|
|
3594
|
+
}
|
|
3595
|
+
names.delete("expect");
|
|
3596
|
+
if (names.size === 0)
|
|
3597
|
+
return false;
|
|
3598
|
+
const alts = [...names].map((n) => n.replace(/\$/g, "\\$")).join("|");
|
|
3599
|
+
return new RegExp(String.raw`(?<![\w$.])(?:${alts})${EXPECT_MODIFIERS}`).test(code);
|
|
3600
|
+
}
|
|
3515
3601
|
function asPlainExpect(code) {
|
|
3516
3602
|
return code.replace(/\bexpect\.soft(\s*\()/g, "expect$1");
|
|
3517
3603
|
}
|
|
3518
|
-
|
|
3604
|
+
function expectAliasNames(code) {
|
|
3605
|
+
if (!MAY_ALIAS.test(code))
|
|
3606
|
+
return [];
|
|
3607
|
+
const imported = [];
|
|
3608
|
+
for (const m of code.matchAll(PW_IMPORT)) {
|
|
3609
|
+
for (const a of m[1].matchAll(IMPORT_AS_EXPECT))
|
|
3610
|
+
imported.push(a[1]);
|
|
3611
|
+
}
|
|
3612
|
+
const known = /* @__PURE__ */ new Set(["expect", ...imported]);
|
|
3613
|
+
const derived = [];
|
|
3614
|
+
for (const m of code.matchAll(DECLARATION)) {
|
|
3615
|
+
const rhs = DERIVED.exec(code.slice(m.index + m[0].length, m.index + m[0].length + 200));
|
|
3616
|
+
if (!rhs)
|
|
3617
|
+
continue;
|
|
3618
|
+
const base = rhs[1];
|
|
3619
|
+
const fromKnown = base === void 0 || (rhs[2] ? base === "test" : known.has(base));
|
|
3620
|
+
if (!fromKnown || m[1] === "expect")
|
|
3621
|
+
continue;
|
|
3622
|
+
known.add(m[1]);
|
|
3623
|
+
derived.push(m[1]);
|
|
3624
|
+
}
|
|
3625
|
+
const fromImports = LOCAL_EXPECT.test(code) ? [] : imported;
|
|
3626
|
+
return [.../* @__PURE__ */ new Set([...fromImports, ...derived])].filter((n) => n !== "expect");
|
|
3627
|
+
}
|
|
3628
|
+
function withExpectAliasesAsExpect(code) {
|
|
3629
|
+
let out = code;
|
|
3630
|
+
for (const name of expectAliasNames(code)) {
|
|
3631
|
+
const escaped = name.replace(/\$/g, "\\$");
|
|
3632
|
+
out = out.replace(new RegExp(`(?<![\\w$.])${escaped}(?=\\s{0,20}(?:\\.\\s{0,20}(?:soft|poll)\\s{0,20})?\\()`, "g"), "expect");
|
|
3633
|
+
}
|
|
3634
|
+
return out;
|
|
3635
|
+
}
|
|
3636
|
+
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;
|
|
3519
3637
|
var init_expect_call = __esm({
|
|
3520
3638
|
"../shared/dist/expect-call.js"() {
|
|
3521
3639
|
"use strict";
|
|
3522
3640
|
EXPECT_CALL_RE = /\bexpect(?:\.(?:soft|poll))?\s*\(/;
|
|
3641
|
+
EXPECT_MODIFIERS = String.raw`\s*(?:\.\s*soft\s*)?(?:\.\s*poll\s*)?\(`;
|
|
3642
|
+
SPACED_EXPECT_CALL_RE = new RegExp(String.raw`\bexpect${EXPECT_MODIFIERS}`);
|
|
3643
|
+
CONFIGURE_IN_PLACE_RE = new RegExp(String.raw`\bexpect\s*\.\s*configure\s*\(\s*(?:\{[^{}()]{0,500}\}\s*)?\)${EXPECT_MODIFIERS}`);
|
|
3644
|
+
IMPORT_CLAUSE_RE = /\bimport\s+(?:type\s+)?\{([^{}]{0,2000})\}/g;
|
|
3645
|
+
EXPECT_ALIAS_RE = /\bexpect\s+as\s+([A-Za-z_$][\w$]*)/g;
|
|
3646
|
+
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;
|
|
3647
|
+
MAX_DERIVED_EXPECT_NAMES = 20;
|
|
3648
|
+
PW_IMPORT = /\bimport\s{0,20}\{([^}]{0,4000})\}\s{0,20}from\s{0,20}['"]@playwright\/test['"]/g;
|
|
3649
|
+
IMPORT_AS_EXPECT = /(?:^|,)\s{0,20}expect\s{1,20}as\s{1,20}([A-Za-z_$][\w$]{0,99})\s{0,20}(?=,|$)/g;
|
|
3650
|
+
DECLARATION = /\b(?:const|let|var)\s{1,20}([A-Za-z_$][\w$]{0,99})\s{0,20}(?::[^=;\n]{0,200})?=\s{0,20}/g;
|
|
3651
|
+
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}[.(]))/;
|
|
3652
|
+
LOCAL_EXPECT = /\b(?:const|let|var)\s{1,20}expect\b/;
|
|
3653
|
+
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/;
|
|
3523
3654
|
}
|
|
3524
3655
|
});
|
|
3525
3656
|
|
|
@@ -3549,65 +3680,6 @@ var init_braced_clause = __esm({
|
|
|
3549
3680
|
}
|
|
3550
3681
|
});
|
|
3551
3682
|
|
|
3552
|
-
// ../shared/dist/regex-literal.js
|
|
3553
|
-
function opensRegexLiteral(code, i) {
|
|
3554
|
-
let j = i - 1;
|
|
3555
|
-
while (j >= 0 && (code[j] === " " || code[j] === " "))
|
|
3556
|
-
j--;
|
|
3557
|
-
if (j < 0 || code[j] === "\n" || code[j] === "\r")
|
|
3558
|
-
return true;
|
|
3559
|
-
return regexOpensAfter(code, j);
|
|
3560
|
-
}
|
|
3561
|
-
function regexOpensAfter(code, j) {
|
|
3562
|
-
const prev = code[j];
|
|
3563
|
-
if (REGEX_OPENERS.includes(prev))
|
|
3564
|
-
return !((prev === "+" || prev === "-") && code[j - 1] === prev);
|
|
3565
|
-
if (!/[a-z]/.test(prev))
|
|
3566
|
-
return false;
|
|
3567
|
-
let start = j;
|
|
3568
|
-
while (start > 0 && /[A-Za-z0-9_$]/.test(code[start - 1]))
|
|
3569
|
-
start--;
|
|
3570
|
-
if (start > 0 && code[start - 1] === ".")
|
|
3571
|
-
return false;
|
|
3572
|
-
return REGEX_KEYWORDS.has(code.slice(start, j + 1));
|
|
3573
|
-
}
|
|
3574
|
-
function regexLiteralEnd2(code, i) {
|
|
3575
|
-
let k = i + 1;
|
|
3576
|
-
let inClass = false;
|
|
3577
|
-
while (k < code.length) {
|
|
3578
|
-
const ch = code[k];
|
|
3579
|
-
if (ch === "\n" || ch === "\r")
|
|
3580
|
-
return k;
|
|
3581
|
-
if (ch === "\\") {
|
|
3582
|
-
if (code[k + 1] === "\n" || code[k + 1] === "\r")
|
|
3583
|
-
return k + 1;
|
|
3584
|
-
k += 2;
|
|
3585
|
-
continue;
|
|
3586
|
-
}
|
|
3587
|
-
if (inClass) {
|
|
3588
|
-
if (ch === "]")
|
|
3589
|
-
inClass = false;
|
|
3590
|
-
} else if (ch === "[") {
|
|
3591
|
-
inClass = true;
|
|
3592
|
-
} else if (ch === "/") {
|
|
3593
|
-
k++;
|
|
3594
|
-
while (k < code.length && /[a-z]/i.test(code[k]))
|
|
3595
|
-
k++;
|
|
3596
|
-
return k;
|
|
3597
|
-
}
|
|
3598
|
-
k++;
|
|
3599
|
-
}
|
|
3600
|
-
return code.length;
|
|
3601
|
-
}
|
|
3602
|
-
var REGEX_OPENERS, REGEX_KEYWORDS;
|
|
3603
|
-
var init_regex_literal = __esm({
|
|
3604
|
-
"../shared/dist/regex-literal.js"() {
|
|
3605
|
-
"use strict";
|
|
3606
|
-
REGEX_OPENERS = "(,=:[!&|?{};+-*%~^<>";
|
|
3607
|
-
REGEX_KEYWORDS = /* @__PURE__ */ new Set(["return", "typeof", "case", "do", "else", "in", "of", "new", "delete", "void", "throw", "await", "yield", "instanceof"]);
|
|
3608
|
-
}
|
|
3609
|
-
});
|
|
3610
|
-
|
|
3611
3683
|
// ../shared/dist/assertion-proof.js
|
|
3612
3684
|
function fragmentKindOf(literal, fromText) {
|
|
3613
3685
|
const text = literal.trim();
|
|
@@ -3766,7 +3838,7 @@ function stripCoverageComments(code) {
|
|
|
3766
3838
|
let copyFrom = 0;
|
|
3767
3839
|
let i = 0;
|
|
3768
3840
|
let quote = null;
|
|
3769
|
-
let
|
|
3841
|
+
let prevAt = -1;
|
|
3770
3842
|
let noRegexBefore = 0;
|
|
3771
3843
|
while (i < code.length) {
|
|
3772
3844
|
const ch = code[i];
|
|
@@ -3777,17 +3849,18 @@ function stripCoverageComments(code) {
|
|
|
3777
3849
|
}
|
|
3778
3850
|
if (ch === quote || ch === "\n" && quote !== "`") {
|
|
3779
3851
|
quote = null;
|
|
3780
|
-
|
|
3852
|
+
if (ch !== "\n")
|
|
3853
|
+
prevAt = i;
|
|
3781
3854
|
}
|
|
3782
3855
|
i++;
|
|
3783
3856
|
continue;
|
|
3784
3857
|
}
|
|
3785
3858
|
const next = code[i + 1];
|
|
3786
|
-
if (ch === "/" && next !== "*" && next !== "/" && i >= noRegexBefore && (
|
|
3859
|
+
if (ch === "/" && next !== "*" && next !== "/" && i >= noRegexBefore && (prevAt < 0 || regexOpensAfter(code, prevAt))) {
|
|
3787
3860
|
const end = regexLiteralEnd3(code, i);
|
|
3788
3861
|
if (end > 0) {
|
|
3789
3862
|
i = end;
|
|
3790
|
-
|
|
3863
|
+
prevAt = end - 1;
|
|
3791
3864
|
continue;
|
|
3792
3865
|
}
|
|
3793
3866
|
const eol = code.indexOf("\n", i);
|
|
@@ -3812,7 +3885,7 @@ function stripCoverageComments(code) {
|
|
|
3812
3885
|
if (ch === '"' || ch === "'" || ch === "`")
|
|
3813
3886
|
quote = ch;
|
|
3814
3887
|
if (ch !== " " && ch !== " " && ch !== "\n" && ch !== "\r")
|
|
3815
|
-
|
|
3888
|
+
prevAt = i;
|
|
3816
3889
|
i++;
|
|
3817
3890
|
}
|
|
3818
3891
|
out.push(code.slice(copyFrom));
|
|
@@ -4501,11 +4574,12 @@ function assessTitleCoverage(title, code) {
|
|
|
4501
4574
|
const repeatedHit = [...repeated].some((term) => hay.includes(term));
|
|
4502
4575
|
return { covered: hit >= needed || repeatedHit, missingTerms };
|
|
4503
4576
|
}
|
|
4504
|
-
var COVERAGE_STOP_WORDS, MAX_ASSERTION_JOIN_CHARS, MAX_COVERAGE_LINE_CHARS,
|
|
4577
|
+
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;
|
|
4505
4578
|
var init_expected_outcome_coverage = __esm({
|
|
4506
4579
|
"../shared/dist/expected-outcome-coverage.js"() {
|
|
4507
4580
|
"use strict";
|
|
4508
4581
|
init_assertion_classifier();
|
|
4582
|
+
init_regex_literal();
|
|
4509
4583
|
COVERAGE_STOP_WORDS = /* @__PURE__ */ new Set([
|
|
4510
4584
|
"after",
|
|
4511
4585
|
"before",
|
|
@@ -4538,7 +4612,6 @@ var init_expected_outcome_coverage = __esm({
|
|
|
4538
4612
|
]);
|
|
4539
4613
|
MAX_ASSERTION_JOIN_CHARS = 8e3;
|
|
4540
4614
|
MAX_COVERAGE_LINE_CHARS = 2e3;
|
|
4541
|
-
REGEX_PRECEDER2 = "(,=:[!&|?{};+-*%<>~^";
|
|
4542
4615
|
BLOCK_SCAFFOLD = /=>|\b(?:test|describe|it|beforeEach|afterEach|beforeAll|afterAll|function)\b/;
|
|
4543
4616
|
cachedAssertionInput = null;
|
|
4544
4617
|
cachedAssertionSource = { raw: "", normalized: "", bindings: /* @__PURE__ */ new Map(), bindingRefs: /* @__PURE__ */ new Map() };
|
|
@@ -4634,7 +4707,7 @@ function replaceBlockComments(code, replacer) {
|
|
|
4634
4707
|
continue;
|
|
4635
4708
|
const next = code[i + 1];
|
|
4636
4709
|
if (next !== "/" && next !== "*" && opensRegexLiteral(code, i)) {
|
|
4637
|
-
i =
|
|
4710
|
+
i = regexLiteralEnd(code, i) - 1;
|
|
4638
4711
|
continue;
|
|
4639
4712
|
}
|
|
4640
4713
|
if (next === "/" && !(code[i - 1] === ":" && /[A-Za-z]/.test(code[i - 2] ?? ""))) {
|
|
@@ -4809,10 +4882,41 @@ var init_auth_handoff_coverage = __esm({
|
|
|
4809
4882
|
});
|
|
4810
4883
|
|
|
4811
4884
|
// ../shared/dist/auth-plan.js
|
|
4812
|
-
function
|
|
4885
|
+
function stripStampedHelperRegions(code) {
|
|
4886
|
+
let out = code;
|
|
4887
|
+
for (const [open, close] of STAMPED_HELPER_REGIONS) {
|
|
4888
|
+
if (out.indexOf(open) === -1)
|
|
4889
|
+
continue;
|
|
4890
|
+
let kept = "";
|
|
4891
|
+
let i = 0;
|
|
4892
|
+
while (i < out.length) {
|
|
4893
|
+
const start = out.indexOf(open, i);
|
|
4894
|
+
if (start === -1) {
|
|
4895
|
+
kept += out.slice(i);
|
|
4896
|
+
break;
|
|
4897
|
+
}
|
|
4898
|
+
const end = out.indexOf(close, start + open.length);
|
|
4899
|
+
if (end === -1) {
|
|
4900
|
+
kept += out.slice(i);
|
|
4901
|
+
break;
|
|
4902
|
+
}
|
|
4903
|
+
const lineEnd = out.indexOf("\n", end);
|
|
4904
|
+
kept += out.slice(i, start);
|
|
4905
|
+
i = lineEnd === -1 ? out.length : lineEnd + 1;
|
|
4906
|
+
}
|
|
4907
|
+
out = kept;
|
|
4908
|
+
}
|
|
4909
|
+
return out;
|
|
4910
|
+
}
|
|
4911
|
+
function signsInInline(playwrightCode) {
|
|
4813
4912
|
if (!playwrightCode)
|
|
4814
4913
|
return false;
|
|
4815
|
-
|
|
4914
|
+
if (!/process\.env\.TEST_USER_EMAIL!?/.test(playwrightCode))
|
|
4915
|
+
return false;
|
|
4916
|
+
if (/process\.env\.TEST_USER_PASSWORD!?/.test(playwrightCode))
|
|
4917
|
+
return true;
|
|
4918
|
+
const body = stripStampedHelperRegions(playwrightCode);
|
|
4919
|
+
return /process\.env\.TEST_USER_EMAIL/.test(body) && /__vqaWaitForEmail\s*\(/.test(body);
|
|
4816
4920
|
}
|
|
4817
4921
|
function hasGeneratedAuthGuard(playwrightCode) {
|
|
4818
4922
|
if (!playwrightCode)
|
|
@@ -4831,7 +4935,7 @@ function legacyNeedsCookieState(tags, code, sharedAuthInjectEnabled) {
|
|
|
4831
4935
|
if (sharedAuthInjectEnabled && tags.includes("@auth-guarded") && hasGeneratedAuthGuard(code))
|
|
4832
4936
|
return true;
|
|
4833
4937
|
const isGeneratedVariant = tags.includes("@edge") || tags.includes("@bug");
|
|
4834
|
-
const hasLegacyInlineLogin = tags.includes("@discovered") && !isGeneratedVariant &&
|
|
4938
|
+
const hasLegacyInlineLogin = tags.includes("@discovered") && !isGeneratedVariant && signsInInline(code);
|
|
4835
4939
|
return !tags.includes("@inline-login") && !hasLegacyInlineLogin;
|
|
4836
4940
|
}
|
|
4837
4941
|
function codeReadsStoredCredentials(code) {
|
|
@@ -4952,7 +5056,7 @@ function authTypeTag(tags) {
|
|
|
4952
5056
|
const explicit = explicitAuthTypeTags(t);
|
|
4953
5057
|
return explicit.length === 1 ? explicit[0] : void 0;
|
|
4954
5058
|
}
|
|
4955
|
-
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;
|
|
5059
|
+
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;
|
|
4956
5060
|
var init_auth_plan = __esm({
|
|
4957
5061
|
"../shared/dist/auth-plan.js"() {
|
|
4958
5062
|
"use strict";
|
|
@@ -4979,6 +5083,10 @@ var init_auth_plan = __esm({
|
|
|
4979
5083
|
};
|
|
4980
5084
|
TEST_USER_ENV_KEYS = ["TEST_USER_EMAIL", "TEST_USER_PASSWORD"];
|
|
4981
5085
|
API_AUTH_HEADERS_ENV_KEY = "API_AUTH_HEADERS";
|
|
5086
|
+
STAMPED_HELPER_REGIONS = [
|
|
5087
|
+
["// >>> VALIDATE_QA_INBOX", "// <<< VALIDATE_QA_INBOX"],
|
|
5088
|
+
["// >>> VALIDATE_QA_SMS", "// <<< VALIDATE_QA_SMS"]
|
|
5089
|
+
];
|
|
4982
5090
|
}
|
|
4983
5091
|
});
|
|
4984
5092
|
|
|
@@ -4987,6 +5095,9 @@ function parseLastActionProofMode(raw) {
|
|
|
4987
5095
|
const v = (raw ?? "").trim().toLowerCase();
|
|
4988
5096
|
return v === "on" || v === "shadow" ? v : "off";
|
|
4989
5097
|
}
|
|
5098
|
+
function parseAbsenceProofMode(raw) {
|
|
5099
|
+
return parseLastActionProofMode(raw);
|
|
5100
|
+
}
|
|
4990
5101
|
function isUrlProof(stmt) {
|
|
4991
5102
|
if (URL_PROOF.test(stmt))
|
|
4992
5103
|
return true;
|
|
@@ -5374,6 +5485,65 @@ function isVacuousAccountAbsence(stmt, shown, account) {
|
|
|
5374
5485
|
}
|
|
5375
5486
|
return true;
|
|
5376
5487
|
}
|
|
5488
|
+
function isAbsenceCheck(stmt) {
|
|
5489
|
+
if (!claimsAbsence(stmt))
|
|
5490
|
+
return false;
|
|
5491
|
+
const bare = stmt.replace(NOT_BEFORE_MATCHER, ".");
|
|
5492
|
+
return ABSENCE_READ.test(bare) || VALUE_ABSENCE.test(bare) || OPTION_ABSENCE.test(bare);
|
|
5493
|
+
}
|
|
5494
|
+
function comparableSubject(subject) {
|
|
5495
|
+
let s = subject.replace(/^\s*await\s+/, "").replace(/\s+/g, "");
|
|
5496
|
+
for (let prev = ""; prev !== s; ) {
|
|
5497
|
+
prev = s;
|
|
5498
|
+
s = s.replace(/\.(?:count|isVisible|isHidden|first|last)\(\)$/, "").replace(/\.nth\(\d+\)$/, "");
|
|
5499
|
+
}
|
|
5500
|
+
return s;
|
|
5501
|
+
}
|
|
5502
|
+
function subjectsThatExisted(checks, interactionStmts) {
|
|
5503
|
+
const out = [];
|
|
5504
|
+
const bound = locatorBindings(interactionStmts);
|
|
5505
|
+
const resolve = (expr) => {
|
|
5506
|
+
for (let depth = 0; depth < 4; depth++) {
|
|
5507
|
+
const lead = expr.match(/^\s*(?:await\s+)?([A-Za-z_$][\w$]*)(?![\w$(])/);
|
|
5508
|
+
const to = lead ? bound.get(lead[1]) : void 0;
|
|
5509
|
+
if (!lead || to === void 0)
|
|
5510
|
+
break;
|
|
5511
|
+
expr = `${to.replace(/;\s*$/, "")}${expr.slice(lead[0].length)}`;
|
|
5512
|
+
}
|
|
5513
|
+
return expr;
|
|
5514
|
+
};
|
|
5515
|
+
for (const { stmt } of checks) {
|
|
5516
|
+
if (isAbsenceCheck(stmt))
|
|
5517
|
+
continue;
|
|
5518
|
+
const subject = extractExpectSubject(stmt);
|
|
5519
|
+
if (subject)
|
|
5520
|
+
out.push(comparableSubject(subject));
|
|
5521
|
+
}
|
|
5522
|
+
for (const stmt of interactionStmts) {
|
|
5523
|
+
if (EXPECT_CALL_RE.test(stmt))
|
|
5524
|
+
continue;
|
|
5525
|
+
for (const m of stmt.matchAll(LOCATOR_USE)) {
|
|
5526
|
+
if (m[0].includes("waitFor") && /state\s*:\s*['"`](?:hidden|detached)/.test(stmt.slice(m.index)))
|
|
5527
|
+
continue;
|
|
5528
|
+
const before = stmt.slice(0, m.index).replace(/^\s*(?:(?:const|let|var)\s+[\w$]+\s*=|return\b)\s*/, "");
|
|
5529
|
+
if (before.trim())
|
|
5530
|
+
out.push(comparableSubject(resolve(before)));
|
|
5531
|
+
}
|
|
5532
|
+
}
|
|
5533
|
+
return out;
|
|
5534
|
+
}
|
|
5535
|
+
function isUnshownAbsence(stmt, existed, entered) {
|
|
5536
|
+
if (!isAbsenceCheck(stmt))
|
|
5537
|
+
return false;
|
|
5538
|
+
const raw = extractExpectSubject(stmt);
|
|
5539
|
+
if (!raw || !/\b(?:locator|getBy[A-Z]\w*|frameLocator)\s*\(/.test(raw))
|
|
5540
|
+
return false;
|
|
5541
|
+
const subject = comparableSubject(raw);
|
|
5542
|
+
if (existed.some((e) => e === subject || e.startsWith(`${subject}.`)))
|
|
5543
|
+
return false;
|
|
5544
|
+
const named = raw.match(/['"`][^'"`]+['"`]/g) ?? [];
|
|
5545
|
+
return !named.some((q) => entered.values.has(q.slice(1, -1).toLowerCase()));
|
|
5546
|
+
}
|
|
5377
5547
|
function endOfEnclosingCall(stmt, from) {
|
|
5378
5548
|
let depth = 0;
|
|
5379
5549
|
for (let i = from; i < stmt.length; i++) {
|
|
@@ -5873,10 +6043,31 @@ function comparesWithItself(subject, tail) {
|
|
|
5873
6043
|
const expected = squash2(args);
|
|
5874
6044
|
return expected === squash2(m[1] === "toHaveCount" ? `${subject}.count()` : subject);
|
|
5875
6045
|
}
|
|
6046
|
+
function ariaSnapshotTexts(template) {
|
|
6047
|
+
const out = [];
|
|
6048
|
+
for (const raw of template.split(ARIA_ENTRY_SPLIT)) {
|
|
6049
|
+
const m = ARIA_ENTRY.exec(raw.trim());
|
|
6050
|
+
if (!m || !ARIA_CONTENT_ROLES.has(m[1]))
|
|
6051
|
+
continue;
|
|
6052
|
+
for (const text of [m[2], m[3]]) {
|
|
6053
|
+
const t = text?.replace(/\\(.)/g, "$1").replace(/^["']|["']$/g, "").trim();
|
|
6054
|
+
if (!t || t.startsWith("/") || !/[\p{L}\p{N}]/u.test(t) || /["'`\\]/.test(t))
|
|
6055
|
+
continue;
|
|
6056
|
+
out.push(t);
|
|
6057
|
+
}
|
|
6058
|
+
}
|
|
6059
|
+
return out;
|
|
6060
|
+
}
|
|
6061
|
+
function presenceTwin(stmt) {
|
|
6062
|
+
return stmt.replace(NOT_HIDDEN, ".toBeVisible(").replace(NOT_COUNT_ZERO, ".toBeVisible(");
|
|
6063
|
+
}
|
|
5876
6064
|
function asPlainAssertion(s) {
|
|
5877
6065
|
return plainAssertion(s, false);
|
|
5878
6066
|
}
|
|
5879
6067
|
function plainAssertion(s, keepSelfCompare) {
|
|
6068
|
+
const twin = presenceTwin(s.stmt);
|
|
6069
|
+
if (twin !== s.stmt)
|
|
6070
|
+
return plainAssertion({ ...s, stmt: twin }, keepSelfCompare);
|
|
5880
6071
|
const { stmt } = s;
|
|
5881
6072
|
if (COUNT_AT_LEAST_ZERO.test(stmt))
|
|
5882
6073
|
return [];
|
|
@@ -5887,6 +6078,12 @@ function plainAssertion(s, keepSelfCompare) {
|
|
|
5887
6078
|
const subjectAt = stmt.indexOf(subject, stmt.search(EXPECT_CALL_RE));
|
|
5888
6079
|
const tail = (stmt.startsWith("expect.poll", stmt.search(EXPECT_CALL_RE)) ? expectCallTail(stmt) : null) ?? stmt.slice(subjectAt + subject.length).trimStart();
|
|
5889
6080
|
const as = (rewritten) => [{ ...s, stmt: rewritten }];
|
|
6081
|
+
const aria = ARIA_TEMPLATE_CALL.exec(tail);
|
|
6082
|
+
if (aria && !NEGATED_ASSERT.test(stmt)) {
|
|
6083
|
+
const texts = ariaSnapshotTexts(aria[2]);
|
|
6084
|
+
if (texts.length > 0)
|
|
6085
|
+
return texts.map((t) => ({ ...s, stmt: `await expect(${plain}).toContainText(${JSON.stringify(t)});` }));
|
|
6086
|
+
}
|
|
5890
6087
|
if (LOCATOR_EXPR.test(plain) && !LOCATOR_VALUE_READ.test(plain) && ALWAYS_TRUE_ON_OBJECT.test(tail))
|
|
5891
6088
|
return [];
|
|
5892
6089
|
if (DEFINED_VALUE_READ.test(plain) && ALWAYS_DEFINED.test(tail))
|
|
@@ -6193,7 +6390,8 @@ function teardownLines(lines) {
|
|
|
6193
6390
|
}
|
|
6194
6391
|
return out;
|
|
6195
6392
|
}
|
|
6196
|
-
function validateAssertionStrength(
|
|
6393
|
+
function validateAssertionStrength(source, opts = {}) {
|
|
6394
|
+
const code = withExpectAliasesAsExpect(source);
|
|
6197
6395
|
const body = stripLoginScaffolding(code);
|
|
6198
6396
|
const lines = body.split("\n");
|
|
6199
6397
|
const stmts = liveExpectStatements(stripCoverageComments(body)).flatMap(asPlainAssertion);
|
|
@@ -6346,8 +6544,23 @@ function validateAssertionStrength(code, opts = {}) {
|
|
|
6346
6544
|
const orderedPersistence = firstMutLine !== void 0 ? persistence : void 0;
|
|
6347
6545
|
const signedIn = signInProofWindow(interactionStmts, interactionStmtsWithLines, lines, blockIds);
|
|
6348
6546
|
const proof = classifyOutcomeProof(checks, mutated, reloaded, clickedControlNames(interactionStmts), entered, firstMutLine, orderedPersistence, signedIn);
|
|
6547
|
+
const absenceProof = opts.absenceProof ?? "off";
|
|
6548
|
+
let absenceOnly = false;
|
|
6549
|
+
if (absenceProof !== "off" && proof.kind === "durable") {
|
|
6550
|
+
const existed = subjectsThatExisted(checks, interactionStmtsRaw);
|
|
6551
|
+
const kept = checks.filter(({ stmt }) => !isUnshownAbsence(stmt, existed, entered));
|
|
6552
|
+
absenceOnly = kept.length < checks.length && classifyOutcomeProof(kept, mutated, reloaded, clickedControlNames(interactionStmts), entered, firstMutLine, orderedPersistence, signedIn).kind !== "durable";
|
|
6553
|
+
if (absenceOnly && absenceProof === "on") {
|
|
6554
|
+
return {
|
|
6555
|
+
ok: false,
|
|
6556
|
+
severity: mutated ? "block" : "downgrade",
|
|
6557
|
+
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",
|
|
6558
|
+
signals: base({ interacted, mutated, navOnly, hasOutcomeProof: false, terminal, absenceOnly: true })
|
|
6559
|
+
};
|
|
6560
|
+
}
|
|
6561
|
+
}
|
|
6349
6562
|
if (proof.kind === "durable") {
|
|
6350
|
-
const signals2 = base({ interacted, mutated, navOnly, hasOutcomeProof: true, terminal });
|
|
6563
|
+
const signals2 = base({ interacted, mutated, navOnly, hasOutcomeProof: true, terminal, ...absenceOnly ? { absenceOnly } : {} });
|
|
6351
6564
|
const { last, prev } = acts;
|
|
6352
6565
|
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])))) {
|
|
6353
6566
|
const lastLine = interactionStmtsWithLines[last.i].startLine;
|
|
@@ -6451,7 +6664,7 @@ function validateAssertionStrength(code, opts = {}) {
|
|
|
6451
6664
|
signals
|
|
6452
6665
|
};
|
|
6453
6666
|
}
|
|
6454
|
-
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;
|
|
6667
|
+
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;
|
|
6455
6668
|
var init_assertion_strength = __esm({
|
|
6456
6669
|
"../shared/dist/assertion-strength.js"() {
|
|
6457
6670
|
"use strict";
|
|
@@ -6540,6 +6753,8 @@ var init_assertion_strength = __esm({
|
|
|
6540
6753
|
NOT_BEFORE_MATCHER = /\.not\s*\.\s*(?=to[A-Z])/g;
|
|
6541
6754
|
ERROR_TEXT_WORDS = /error|fail|wrong|try again|unable|could ?n.?t|can.?t|problem|invalid|unavailable/i;
|
|
6542
6755
|
TEXT_REGEX_ARG = /(?:getByText|hasText|getByRole\([^)]{0,200}name)\s*[:(]\s*(\/(?:\\.|[^/\\\n])+\/[a-z]*)/;
|
|
6756
|
+
ABSENCE_READ = /\.toBeVisible\s*\(|\.toBeHidden\s*\(|\.toHaveCount\s*\(\s*0\s*\)|\.toBeAttached\s*\(|\.toHaveText\s*\(|\.toContainText\s*\(/;
|
|
6757
|
+
LOCATOR_USE = /\.\s*(?:click|dblclick|tap|hover|focus|fill|clear|press|pressSequentially|type|check|uncheck|setChecked|selectOption|setInputFiles|dragTo|selectText|waitFor)\s*\(/g;
|
|
6543
6758
|
SCOPING_STEP = /(?:\s*\.\s*(?:first|last|nth)\s*\([^()]*\))*\s*\.\s*(?:getBy[A-Z]\w*|locator|filter)\s*\(/y;
|
|
6544
6759
|
NEGATED_TEXT = /\.not\s*\.\s*(?:toHaveText|toContainText)\s*\(\s*(?:['"`]([^'"`]*)['"`]|\/((?:\\.|[^/\\\n])+)\/)?/;
|
|
6545
6760
|
ASYNC_DESTRUCTURE_OPEN = /async\s*\(\s*\{/g;
|
|
@@ -6581,6 +6796,29 @@ var init_assertion_strength = __esm({
|
|
|
6581
6796
|
IS_VISIBLE_TRUE = /^\)*\s*\.\s*(?:toBe\s*\(\s*true\s*\)|toBeTruthy\s*\(\s*\))/;
|
|
6582
6797
|
PAGE_VALUE_MATCHER = /^\)\s*\.\s*(?:toBe|toEqual|toStrictEqual|toMatch|toContain)\s*\(/;
|
|
6583
6798
|
COUNT_ONE = /\.\s*toHaveCount\s*\(\s*1\s*\)/;
|
|
6799
|
+
ARIA_TEMPLATE_CALL = /^\)\s*\.\s*toMatchAriaSnapshot\s*\(\s*(['"`])((?:\\[\s\S]|(?!\1)[^\\])*)\1/;
|
|
6800
|
+
ARIA_ENTRY_SPLIT = /(?:^|\s)-\s+(?=\/?[a-z][a-z-]*(?:\s|:|\[|$))/;
|
|
6801
|
+
ARIA_CONTENT_ROLES = /* @__PURE__ */ new Set([
|
|
6802
|
+
"listitem",
|
|
6803
|
+
"row",
|
|
6804
|
+
"cell",
|
|
6805
|
+
"gridcell",
|
|
6806
|
+
"article",
|
|
6807
|
+
"treeitem",
|
|
6808
|
+
"paragraph",
|
|
6809
|
+
"text",
|
|
6810
|
+
"strong",
|
|
6811
|
+
"emphasis",
|
|
6812
|
+
"code",
|
|
6813
|
+
"blockquote",
|
|
6814
|
+
"term",
|
|
6815
|
+
"definition",
|
|
6816
|
+
"alert",
|
|
6817
|
+
"status"
|
|
6818
|
+
]);
|
|
6819
|
+
ARIA_ENTRY = /^([a-z][a-z-]*)(?:\s+"((?:\\.|[^"\\])*)")?(?:\s*\[[^\]]*\])*\s*(?::\s*(\S.*)?)?$/s;
|
|
6820
|
+
NOT_HIDDEN = /\.not\s*\.\s*toBeHidden\s*\(/g;
|
|
6821
|
+
NOT_COUNT_ZERO = /\.not\s*\.\s*toHaveCount\s*\(\s*0\s*(?:,\s*|(?=\)))/g;
|
|
6584
6822
|
URL_CHECK_ARG = /\bpage\s*\.\s*waitForURL\s*\(\s*([^,)]+)|\bexpect(?:\s*\.\s*soft)?\s*\(\s*page\s*\)\s*\.\s*toHaveURL\s*\(\s*([^,)]+)/;
|
|
6585
6823
|
PLAIN_PAGE_PATH = /^\/[\w\-./~%]*(?:[?#][^\s]*)?$/;
|
|
6586
6824
|
FORM_CONTROL_LOCATOR = /getByRole\s*\(\s*['"`](?:combobox|switch|checkbox|radio|textbox|searchbox|spinbutton|slider)['"`]|getByLabel\s*\(|getByPlaceholder\s*\(/;
|
|
@@ -6626,6 +6864,12 @@ function isCancelledReason(error) {
|
|
|
6626
6864
|
function isHeldHealRun(run) {
|
|
6627
6865
|
return run?.status === "PASSED" && typeof run.error === "string" && run.error.startsWith(HEAL_PATCH_HELD_RUN_ERROR_PREFIX);
|
|
6628
6866
|
}
|
|
6867
|
+
function mayBeUnverifiedHealPass(run, tags) {
|
|
6868
|
+
return !!run && run.status === "PASSED" && run.mode === "heal" && !isHeldHealRun(run) && (tags ?? []).includes(NEEDS_REVIEW_TAG);
|
|
6869
|
+
}
|
|
6870
|
+
function isUnverifiedHealPass(run, tags, healConfidence) {
|
|
6871
|
+
return mayBeUnverifiedHealPass(run, tags) && typeof healConfidence === "string" && UNVERIFIED_HEAL_CONFIDENCES.has(healConfidence);
|
|
6872
|
+
}
|
|
6629
6873
|
function isRunInterruptedError(error) {
|
|
6630
6874
|
return typeof error === "string" && RUN_INTERRUPTED_ERROR_PREFIXES.some((prefix) => error.startsWith(prefix));
|
|
6631
6875
|
}
|
|
@@ -6712,12 +6956,25 @@ function classifyErrorMessage(error) {
|
|
|
6712
6956
|
return "config_issue";
|
|
6713
6957
|
if (EMAIL_NOT_RECEIVED_PREFIXES.some((prefix) => normalized.startsWith(prefix)))
|
|
6714
6958
|
return "email_not_received";
|
|
6959
|
+
if (SMS_NOT_RECEIVED_PREFIXES.some((prefix) => normalized.startsWith(prefix)))
|
|
6960
|
+
return "email_not_received";
|
|
6715
6961
|
if (isCancelledReason(error))
|
|
6716
6962
|
return "cancelled";
|
|
6717
6963
|
if (isRetriedRunInterruption(error))
|
|
6718
6964
|
return "interrupted";
|
|
6719
6965
|
return null;
|
|
6720
6966
|
}
|
|
6967
|
+
function isSmsNotReceivedError(error) {
|
|
6968
|
+
if (typeof error !== "string")
|
|
6969
|
+
return false;
|
|
6970
|
+
const normalized = error.replace(/^\s+/, "").replace(/^Error:\s*/i, "");
|
|
6971
|
+
return SMS_NOT_RECEIVED_PREFIXES.some((prefix) => normalized.startsWith(prefix));
|
|
6972
|
+
}
|
|
6973
|
+
function blockedRunLogLine(error, durationMs) {
|
|
6974
|
+
const reason = classifyErrorMessage(error);
|
|
6975
|
+
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";
|
|
6976
|
+
return `BLOCKED (${durationMs}ms) \u2014 ${tail}`;
|
|
6977
|
+
}
|
|
6721
6978
|
function stripEnvironmentalPrefix(error) {
|
|
6722
6979
|
for (const prefix of ENVIRONMENTAL_ERROR_PREFIXES) {
|
|
6723
6980
|
if (error.startsWith(prefix)) {
|
|
@@ -6780,8 +7037,19 @@ function computePassRate(passed, tested) {
|
|
|
6780
7037
|
}
|
|
6781
7038
|
function summarizeHealth(items) {
|
|
6782
7039
|
const c = { passed: 0, failed: 0, errored: 0, environmental: 0, never_run: 0 };
|
|
7040
|
+
let rateLimited = 0;
|
|
7041
|
+
let configIssue = 0;
|
|
6783
7042
|
for (const item of items) {
|
|
6784
|
-
|
|
7043
|
+
const weight = item.weight ?? 1;
|
|
7044
|
+
const bucket = classifyTestHealth(item.run);
|
|
7045
|
+
c[bucket] += weight;
|
|
7046
|
+
if (bucket === "environmental") {
|
|
7047
|
+
const reason = classifyErrorMessage(item.run?.error);
|
|
7048
|
+
if (reason === "rate_limit")
|
|
7049
|
+
rateLimited += weight;
|
|
7050
|
+
else if (isConfigLikeReason(reason))
|
|
7051
|
+
configIssue += weight;
|
|
7052
|
+
}
|
|
6785
7053
|
}
|
|
6786
7054
|
const tested = c.passed + c.failed + c.errored;
|
|
6787
7055
|
return {
|
|
@@ -6789,6 +7057,8 @@ function summarizeHealth(items) {
|
|
|
6789
7057
|
failed: c.failed,
|
|
6790
7058
|
errored: c.errored,
|
|
6791
7059
|
environmental: c.environmental,
|
|
7060
|
+
rateLimited,
|
|
7061
|
+
configIssue,
|
|
6792
7062
|
neverRun: c.never_run,
|
|
6793
7063
|
tested,
|
|
6794
7064
|
total: tested + c.environmental + c.never_run,
|
|
@@ -6825,7 +7095,7 @@ function reportedBugWhere() {
|
|
|
6825
7095
|
function bugReviewStatus(status) {
|
|
6826
7096
|
return status === "QUARANTINED" ? "confirmed" : "needs-review";
|
|
6827
7097
|
}
|
|
6828
|
-
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;
|
|
7098
|
+
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;
|
|
6829
7099
|
var init_run_classification = __esm({
|
|
6830
7100
|
"../shared/dist/run-classification.js"() {
|
|
6831
7101
|
"use strict";
|
|
@@ -6845,6 +7115,8 @@ var init_run_classification = __esm({
|
|
|
6845
7115
|
];
|
|
6846
7116
|
isUserCancelledError = isCancelledReason;
|
|
6847
7117
|
HEAL_PATCH_HELD_RUN_ERROR_PREFIX = "Heal patch held";
|
|
7118
|
+
UNVERIFIED_HEAL_CONFIDENCES = /* @__PURE__ */ new Set(["low", "medium"]);
|
|
7119
|
+
NEEDS_REVIEW_TAG = "@needs-review";
|
|
6848
7120
|
RUNNER_INTERRUPT_ERROR_PREFIX = "Runner shutting down";
|
|
6849
7121
|
SERVER_INTERRUPT_ERROR_PREFIX = "Run interrupted by server restart";
|
|
6850
7122
|
RUN_LOST_ERROR_PREFIX = "Runner lost this run";
|
|
@@ -6865,10 +7137,12 @@ var init_run_classification = __esm({
|
|
|
6865
7137
|
"VERIFICATION_EMAIL_HAS_NO_CODE:",
|
|
6866
7138
|
"VERIFICATION_EMAIL_HAS_NO_LINK:"
|
|
6867
7139
|
];
|
|
7140
|
+
SMS_NOT_RECEIVED_PREFIXES = ["VERIFICATION_SMS_NOT_RECEIVED:"];
|
|
6868
7141
|
ENVIRONMENTAL_ERROR_PREFIXES = [
|
|
6869
7142
|
"RATE_LIMIT_BLOCKED:",
|
|
6870
7143
|
"CONFIG_ISSUE:",
|
|
6871
|
-
...EMAIL_NOT_RECEIVED_PREFIXES
|
|
7144
|
+
...EMAIL_NOT_RECEIVED_PREFIXES,
|
|
7145
|
+
...SMS_NOT_RECEIVED_PREFIXES
|
|
6872
7146
|
];
|
|
6873
7147
|
ENVIRONMENTAL_ERROR_STORED_PREFIXES = [
|
|
6874
7148
|
...ENVIRONMENTAL_ERROR_PREFIXES.flatMap((prefix) => [prefix, `Error: ${prefix}`]),
|
|
@@ -9253,6 +9527,19 @@ function googleBrokerUnavailableLine(lines, testError) {
|
|
|
9253
9527
|
}
|
|
9254
9528
|
return last;
|
|
9255
9529
|
}
|
|
9530
|
+
function googleSignInRefusedLine(lines) {
|
|
9531
|
+
let last = null;
|
|
9532
|
+
for (const raw of lines ?? []) {
|
|
9533
|
+
for (const line of String(raw).split("\n")) {
|
|
9534
|
+
const text = line.trim();
|
|
9535
|
+
if (GOOGLE_SIGN_IN_REFUSED.test(text))
|
|
9536
|
+
last = text;
|
|
9537
|
+
else if (GOOGLE_SIGNED_IN.test(text))
|
|
9538
|
+
last = null;
|
|
9539
|
+
}
|
|
9540
|
+
}
|
|
9541
|
+
return last;
|
|
9542
|
+
}
|
|
9256
9543
|
function stepConsoleLines(result) {
|
|
9257
9544
|
const steps = Array.isArray(result) ? result : result && typeof result === "object" && Array.isArray(result.stepResults) ? result.stepResults : [];
|
|
9258
9545
|
return steps.flatMap((step) => {
|
|
@@ -9260,7 +9547,23 @@ function stepConsoleLines(result) {
|
|
|
9260
9547
|
return Array.isArray(logs) ? logs.filter((line) => typeof line === "string") : [];
|
|
9261
9548
|
});
|
|
9262
9549
|
}
|
|
9263
|
-
|
|
9550
|
+
function googleSignInRefusedReason(lines) {
|
|
9551
|
+
let reason = null;
|
|
9552
|
+
for (const raw of lines ?? []) {
|
|
9553
|
+
for (const line of String(raw).split("\n")) {
|
|
9554
|
+
const text = line.trim();
|
|
9555
|
+
if (GOOGLE_SIGNED_IN.test(text)) {
|
|
9556
|
+
reason = null;
|
|
9557
|
+
continue;
|
|
9558
|
+
}
|
|
9559
|
+
const m = GOOGLE_SIGN_IN_ANSWER.exec(text);
|
|
9560
|
+
if (m)
|
|
9561
|
+
reason = m[1];
|
|
9562
|
+
}
|
|
9563
|
+
}
|
|
9564
|
+
return reason;
|
|
9565
|
+
}
|
|
9566
|
+
var GOOGLE_BROKER_UNAVAILABLE, GOOGLE_BROKER_ASKED, GOOGLE_SIGNED_IN, TIMEOUT_ERROR, GOOGLE_SIGN_IN_REFUSED, GOOGLE_SIGN_IN_ANSWER;
|
|
9264
9567
|
var init_google_broker_lines = __esm({
|
|
9265
9568
|
"../shared/dist/google-broker-lines.js"() {
|
|
9266
9569
|
"use strict";
|
|
@@ -9268,6 +9571,8 @@ var init_google_broker_lines = __esm({
|
|
|
9268
9571
|
GOOGLE_BROKER_ASKED = /^\[google-sign-in\] the app started Google sign-in; asking the broker/;
|
|
9269
9572
|
GOOGLE_SIGNED_IN = /^\[google-sign-in\] returned to the app with Google sign-in/;
|
|
9270
9573
|
TIMEOUT_ERROR = /Test timeout of \d+ms exceeded|TimeoutError|Timeout:? \d+ ?ms/i;
|
|
9574
|
+
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): /;
|
|
9575
|
+
GOOGLE_SIGN_IN_ANSWER = /^\[google-sign-in\] ([a-z][a-z0-9_]*): /;
|
|
9271
9576
|
}
|
|
9272
9577
|
});
|
|
9273
9578
|
|
|
@@ -9319,22 +9624,87 @@ function isIntentionalNegativeAuthTest(input) {
|
|
|
9319
9624
|
const meta = [input.testName, input.description].filter(Boolean).join(" ");
|
|
9320
9625
|
return matchesAny(meta, INTENTIONAL_NEGATIVE_AUTH);
|
|
9321
9626
|
}
|
|
9627
|
+
function signInStepPassedBeforeFailure(steps) {
|
|
9628
|
+
const list = steps ?? [];
|
|
9629
|
+
const failedAt = list.findIndex((step) => typeof step.status === "string" && step.status !== "passed" && step.status !== "skipped");
|
|
9630
|
+
if (failedAt <= 0)
|
|
9631
|
+
return false;
|
|
9632
|
+
if (SIGN_IN_STEP_TITLE.test(list[failedAt].title ?? ""))
|
|
9633
|
+
return false;
|
|
9634
|
+
return list.slice(0, failedAt).some((step) => step.status === "passed" && SIGN_IN_STEP_TITLE.test(step.title ?? ""));
|
|
9635
|
+
}
|
|
9636
|
+
function loginFormAlertTexts(body) {
|
|
9637
|
+
const text = body.length > ERROR_CONTEXT_CAP ? body.slice(0, ERROR_CONTEXT_CAP) : body;
|
|
9638
|
+
const blocks = [];
|
|
9639
|
+
let open = null;
|
|
9640
|
+
for (const line of text.split("\n")) {
|
|
9641
|
+
const fence = /^```(\w*)$/.exec(line.trim());
|
|
9642
|
+
if (fence) {
|
|
9643
|
+
if (open) {
|
|
9644
|
+
blocks.push(open);
|
|
9645
|
+
open = null;
|
|
9646
|
+
} else if (fence[1].toLowerCase() === "yaml")
|
|
9647
|
+
open = [];
|
|
9648
|
+
continue;
|
|
9649
|
+
}
|
|
9650
|
+
if (open)
|
|
9651
|
+
open.push(line);
|
|
9652
|
+
}
|
|
9653
|
+
const alerts = [];
|
|
9654
|
+
for (const lines of blocks) {
|
|
9655
|
+
if (!lines.some((l) => SNAPSHOT_PASSWORD_FIELD.test(l)))
|
|
9656
|
+
continue;
|
|
9657
|
+
for (let i = 0; i < lines.length && alerts.length < MAX_ALERTS; i++) {
|
|
9658
|
+
const m = SNAPSHOT_ALERT.exec(lines[i]);
|
|
9659
|
+
if (!m)
|
|
9660
|
+
continue;
|
|
9661
|
+
const indent = m[1].length;
|
|
9662
|
+
const parts = [m[2]];
|
|
9663
|
+
let j = i + 1;
|
|
9664
|
+
for (; j < lines.length; j++) {
|
|
9665
|
+
const lead = /^\s*/.exec(lines[j])?.[0].length ?? 0;
|
|
9666
|
+
if (lines[j].trim() === "" || lead <= indent)
|
|
9667
|
+
break;
|
|
9668
|
+
parts.push(lines[j].trim());
|
|
9669
|
+
}
|
|
9670
|
+
alerts.push(parts.join(" ").slice(0, MAX_ALERT_CHARS));
|
|
9671
|
+
i = j - 1;
|
|
9672
|
+
}
|
|
9673
|
+
}
|
|
9674
|
+
return alerts;
|
|
9675
|
+
}
|
|
9676
|
+
function stepLoginFormAlerts(steps) {
|
|
9677
|
+
const out = [];
|
|
9678
|
+
for (const sr of steps ?? []) {
|
|
9679
|
+
for (const a of sr.attachments ?? []) {
|
|
9680
|
+
if (a?.name === "error-context" && typeof a.body === "string")
|
|
9681
|
+
out.push(...loginFormAlertTexts(a.body));
|
|
9682
|
+
}
|
|
9683
|
+
}
|
|
9684
|
+
return out;
|
|
9685
|
+
}
|
|
9322
9686
|
function detectAuthBlock(input) {
|
|
9323
9687
|
const tags = input.tags ?? [];
|
|
9324
9688
|
const isAuthed = tags.some((t) => AUTHED_TAGS.includes(t));
|
|
9325
|
-
|
|
9689
|
+
const signsInInline2 = !isAuthed && codeReadsStoredCredentials(input.code);
|
|
9690
|
+
if (!isAuthed && !signsInInline2)
|
|
9326
9691
|
return NO_DETECTION;
|
|
9327
9692
|
if (isIntentionalNegativeAuthTest(input))
|
|
9328
9693
|
return NO_DETECTION;
|
|
9694
|
+
if (signInStepPassedBeforeFailure(input.stepResults))
|
|
9695
|
+
return NO_DETECTION;
|
|
9329
9696
|
const sources = [];
|
|
9330
|
-
if (
|
|
9331
|
-
|
|
9332
|
-
|
|
9333
|
-
|
|
9334
|
-
|
|
9335
|
-
|
|
9336
|
-
|
|
9697
|
+
if (isAuthed) {
|
|
9698
|
+
if (input.error)
|
|
9699
|
+
sources.push(stripEchoLines(input.error));
|
|
9700
|
+
if (input.logs)
|
|
9701
|
+
sources.push(stripEchoLines(input.logs));
|
|
9702
|
+
for (const sr of input.stepResults ?? []) {
|
|
9703
|
+
if (sr.error)
|
|
9704
|
+
sources.push(stripEchoLines(sr.error));
|
|
9705
|
+
}
|
|
9337
9706
|
}
|
|
9707
|
+
sources.push(...stepLoginFormAlerts(input.stepResults));
|
|
9338
9708
|
for (const text of sources) {
|
|
9339
9709
|
if (!text)
|
|
9340
9710
|
continue;
|
|
@@ -9383,10 +9753,11 @@ function detectApiColdSessionBlock(input) {
|
|
|
9383
9753
|
function coldSessionConfigMessage(context, reason, evidence) {
|
|
9384
9754
|
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("; ")}`;
|
|
9385
9755
|
}
|
|
9386
|
-
var NO_DETECTION, AUTHED_TAGS, LOGIN_CTX, AUTH_BLOCK_SIGNATURES, INTENTIONAL_NEGATIVE_AUTH, ECHO_LINE_RE, UNAUTHORIZED_TEXT_RE;
|
|
9756
|
+
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;
|
|
9387
9757
|
var init_auth_block_detector = __esm({
|
|
9388
9758
|
"../shared/dist/auth-block-detector.js"() {
|
|
9389
9759
|
"use strict";
|
|
9760
|
+
init_auth_plan();
|
|
9390
9761
|
NO_DETECTION = { detected: false, reason: "", evidence: [] };
|
|
9391
9762
|
AUTHED_TAGS = ["@needs-auth", "@inline-login"];
|
|
9392
9763
|
LOGIN_CTX = "\\b(?:log\\s*in|login|logging\\s+in|sign[\\s-]?in|signing\\s+in|continue)\\b";
|
|
@@ -9396,6 +9767,10 @@ var init_auth_block_detector = __esm({
|
|
|
9396
9767
|
{ reason: "invalid credentials", re: /\b(invalid|incorrect|wrong|bad)\s+(login\s+)?credentials\b/i },
|
|
9397
9768
|
{ reason: "credentials do not match", re: /\bcredentials\b[^\n]{0,40}\b(do(?:es)?\s*n['’]?t|do\s+not)\s+match\b/i },
|
|
9398
9769
|
{ reason: "incorrect email or password", re: /\b(incorrect|invalid|wrong)\s+(email|username)\s+or\s+password\b/i },
|
|
9770
|
+
{ 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 },
|
|
9771
|
+
// A locked account is a blocker of the same kind: the configured user can't sign in until it is unlocked. The
|
|
9772
|
+
// rate-limit detector keeps the timed lockouts ("temporarily locked", "too many attempts").
|
|
9773
|
+
{ reason: "account locked out", re: /\b(?:user|account)\b[^\n]{0,30}\b(?:has\s+been|is)\s+locked\s+out\b/i },
|
|
9399
9774
|
// Require an EXPLICIT negative (codex #21): the old `(not\s+)?(confirmed|verified)` made the negation
|
|
9400
9775
|
// optional, so a benign "Email verified" success message / feature assertion matched and hid a real
|
|
9401
9776
|
// feature failure as a CONFIG_ISSUE. Only "email not (yet) confirmed/verified" or "unconfirmed/unverified".
|
|
@@ -9427,6 +9802,12 @@ var init_auth_block_detector = __esm({
|
|
|
9427
9802
|
/\b(reject|deny|denied|block(ed)?)\b[^\n]{0,20}\b(login|sign[\s-]?in|auth|credential|password)\b/i
|
|
9428
9803
|
];
|
|
9429
9804
|
ECHO_LINE_RE = /^\s*(?:(?:📋|🎭|🌐|⚙️?)|(?:stderr:\s*|stdout:\s*)?Locator:)/u;
|
|
9805
|
+
SIGN_IN_STEP_TITLE = /\b(?:log(?:ging)?\s*in(?:to)?|sign(?:ing)?[\s-]?in(?:to)?|authenticat(?:e|es|ed|ing|ion))\b/i;
|
|
9806
|
+
ERROR_CONTEXT_CAP = 64 * 1024;
|
|
9807
|
+
MAX_ALERTS = 20;
|
|
9808
|
+
MAX_ALERT_CHARS = 1e3;
|
|
9809
|
+
SNAPSHOT_PASSWORD_FIELD = /^\s*- textbox "[^"\n]{0,80}\bpass(?:word|code)?\b/i;
|
|
9810
|
+
SNAPSHOT_ALERT = /^(\s*)- alert\b(.*)$/;
|
|
9430
9811
|
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;
|
|
9431
9812
|
}
|
|
9432
9813
|
});
|
|
@@ -9861,6 +10242,77 @@ var init_redact_needles = __esm({
|
|
|
9861
10242
|
}
|
|
9862
10243
|
});
|
|
9863
10244
|
|
|
10245
|
+
// ../shared/dist/credential-header-scrub.js
|
|
10246
|
+
function scrubCredentialHeaders(text) {
|
|
10247
|
+
if (text.length < 6)
|
|
10248
|
+
return text;
|
|
10249
|
+
let out = text.replace(HEADER_LINE, (match, keyQuote, name, sep, value) => {
|
|
10250
|
+
if (value === "" || ALREADY_REDACTED.test(value))
|
|
10251
|
+
return match;
|
|
10252
|
+
if (COOKIE_NAME.test(name) && !value.includes("="))
|
|
10253
|
+
return match;
|
|
10254
|
+
if (AUTHORIZATION_NAME.test(name) && WORDS_ONLY.test(value))
|
|
10255
|
+
return match;
|
|
10256
|
+
const q = value[0] === '"' || value[0] === "'" ? value[0] : "";
|
|
10257
|
+
return `${keyQuote}${name}${keyQuote}${sep}${q}${REDACTED2}${q}`;
|
|
10258
|
+
});
|
|
10259
|
+
out = out.replace(SET_COOKIE_PAIR, `$1${REDACTED2}`);
|
|
10260
|
+
return out;
|
|
10261
|
+
}
|
|
10262
|
+
function isCredentialHeaderName(name) {
|
|
10263
|
+
return name.length <= 100 && HEADER_NAME_ONLY.test(name);
|
|
10264
|
+
}
|
|
10265
|
+
function scrubCredentialHeaderValue(value) {
|
|
10266
|
+
if (typeof value === "string")
|
|
10267
|
+
return value === REDACTED2 || value === "" ? value : REDACTED2;
|
|
10268
|
+
if (Array.isArray(value) && value.every((item) => typeof item === "string")) {
|
|
10269
|
+
const next = value.map((item) => item === REDACTED2 || item === "" ? item : REDACTED2);
|
|
10270
|
+
return next.every((item, i) => item === value[i]) ? value : next;
|
|
10271
|
+
}
|
|
10272
|
+
return value;
|
|
10273
|
+
}
|
|
10274
|
+
function scrubCredentialHeadersDeep(value) {
|
|
10275
|
+
if (typeof value === "string")
|
|
10276
|
+
return scrubCredentialHeaders(value);
|
|
10277
|
+
if (Array.isArray(value)) {
|
|
10278
|
+
let changed = false;
|
|
10279
|
+
const next = value.map((item) => {
|
|
10280
|
+
const scrubbed = scrubCredentialHeadersDeep(item);
|
|
10281
|
+
if (scrubbed !== item)
|
|
10282
|
+
changed = true;
|
|
10283
|
+
return scrubbed;
|
|
10284
|
+
});
|
|
10285
|
+
return changed ? next : value;
|
|
10286
|
+
}
|
|
10287
|
+
if (value !== null && typeof value === "object") {
|
|
10288
|
+
let changed = false;
|
|
10289
|
+
const next = {};
|
|
10290
|
+
for (const [k, v] of Object.entries(value)) {
|
|
10291
|
+
const scrubbed = isCredentialHeaderName(k) ? scrubCredentialHeaderValue(v) : scrubCredentialHeadersDeep(v);
|
|
10292
|
+
if (scrubbed !== v)
|
|
10293
|
+
changed = true;
|
|
10294
|
+
next[k] = scrubbed;
|
|
10295
|
+
}
|
|
10296
|
+
return changed ? next : value;
|
|
10297
|
+
}
|
|
10298
|
+
return value;
|
|
10299
|
+
}
|
|
10300
|
+
var REDACTED2, HEADER_NAMES, COOKIE_NAME, AUTHORIZATION_NAME, WORDS_ONLY, HEADER_NAME_ONLY, HEADER_LINE, ALREADY_REDACTED, SET_COOKIE_PAIR;
|
|
10301
|
+
var init_credential_header_scrub = __esm({
|
|
10302
|
+
"../shared/dist/credential-header-scrub.js"() {
|
|
10303
|
+
"use strict";
|
|
10304
|
+
REDACTED2 = "[REDACTED]";
|
|
10305
|
+
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}";
|
|
10306
|
+
COOKIE_NAME = /^(?:set-)?cookie2?$/i;
|
|
10307
|
+
AUTHORIZATION_NAME = /^(?:proxy-)?authorization$/i;
|
|
10308
|
+
WORDS_ONLY = /^[A-Za-z]+(?: [A-Za-z]+){0,3}$/;
|
|
10309
|
+
HEADER_NAME_ONLY = new RegExp(`^(?:${HEADER_NAMES})$`, "i");
|
|
10310
|
+
HEADER_LINE = new RegExp(`(?<![A-Za-z0-9_-])(["']?)(${HEADER_NAMES})\\1(\\s*:\\s*)("(?:[^"\\\\\\r\\n]|\\\\.)*"|'(?:[^'\\\\\\r\\n]|\\\\.)*'|[^\\r\\n\\x1b]*)`, "gi");
|
|
10311
|
+
ALREADY_REDACTED = /^["']?(?:[A-Za-z][\w-]*\s+)?\[REDACTED\]["']?[,;]?\s*$/;
|
|
10312
|
+
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;
|
|
10313
|
+
}
|
|
10314
|
+
});
|
|
10315
|
+
|
|
9864
10316
|
// ../shared/dist/deepgram-voices.js
|
|
9865
10317
|
function voice(family, name, gender, accent, description) {
|
|
9866
10318
|
const label = name.charAt(0).toUpperCase() + name.slice(1);
|
|
@@ -10437,13 +10889,13 @@ function isSensitiveQueryKey(key) {
|
|
|
10437
10889
|
return SENSITIVE_QUERY_KEY_RE.test(key);
|
|
10438
10890
|
}
|
|
10439
10891
|
function redactSecretValues(value) {
|
|
10440
|
-
return value.replace(MCP_SECRET_VALUE_RE,
|
|
10892
|
+
return value.replace(MCP_SECRET_VALUE_RE, REDACTED3);
|
|
10441
10893
|
}
|
|
10442
10894
|
function scrubReturnUrlValue(value, depth) {
|
|
10443
10895
|
if (!/[?%]/.test(value))
|
|
10444
10896
|
return value;
|
|
10445
10897
|
if (depth >= MAX_NESTED_URL_DEPTH)
|
|
10446
|
-
return
|
|
10898
|
+
return REDACTED3;
|
|
10447
10899
|
const q = value.indexOf("?");
|
|
10448
10900
|
if (q !== -1) {
|
|
10449
10901
|
const hashIdx = value.indexOf("#", q);
|
|
@@ -10465,7 +10917,7 @@ function scrubReturnUrlValue(value, depth) {
|
|
|
10465
10917
|
try {
|
|
10466
10918
|
return encodeURIComponent(scrubbed);
|
|
10467
10919
|
} catch {
|
|
10468
|
-
return
|
|
10920
|
+
return REDACTED3;
|
|
10469
10921
|
}
|
|
10470
10922
|
}
|
|
10471
10923
|
function scrubQuery(search, depth) {
|
|
@@ -10481,7 +10933,7 @@ function scrubQuery(search, depth) {
|
|
|
10481
10933
|
params.forEach((value, key) => {
|
|
10482
10934
|
let scrubbed = value;
|
|
10483
10935
|
if (isSensitiveQueryKey(key)) {
|
|
10484
|
-
scrubbed =
|
|
10936
|
+
scrubbed = REDACTED3;
|
|
10485
10937
|
} else {
|
|
10486
10938
|
if (RETURN_URL_QUERY_KEY_RE.test(key))
|
|
10487
10939
|
scrubbed = scrubReturnUrlValue(scrubbed, depth);
|
|
@@ -10593,12 +11045,12 @@ function scrubUrlPropertyBag(props) {
|
|
|
10593
11045
|
} catch {
|
|
10594
11046
|
}
|
|
10595
11047
|
}
|
|
10596
|
-
var SENSITIVE_QUERY_KEY_RE,
|
|
11048
|
+
var SENSITIVE_QUERY_KEY_RE, REDACTED3, MCP_SECRET_VALUE_RE, RETURN_URL_QUERY_KEY_RE, MAX_NESTED_URL_DEPTH, MAX_PROPERTY_SWEEP_DEPTH;
|
|
10597
11049
|
var init_analytics_url = __esm({
|
|
10598
11050
|
"../shared/dist/analytics-url.js"() {
|
|
10599
11051
|
"use strict";
|
|
10600
11052
|
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;
|
|
10601
|
-
|
|
11053
|
+
REDACTED3 = "[redacted]";
|
|
10602
11054
|
MCP_SECRET_VALUE_RE = /mcp_(consent|code|at|rt)_[A-Za-z0-9]+/g;
|
|
10603
11055
|
RETURN_URL_QUERY_KEY_RE = /^(next|redirect|return|continue)([_-]?(to|url|uri|path))?$/i;
|
|
10604
11056
|
MAX_NESTED_URL_DEPTH = 5;
|
|
@@ -13428,8 +13880,19 @@ function successContradicted(text) {
|
|
|
13428
13880
|
}
|
|
13429
13881
|
return SUCCESS_NO_STATUS_RE.test(text) || OFF_BY_RE.test(text);
|
|
13430
13882
|
}
|
|
13883
|
+
function negatedSafeguard(text) {
|
|
13884
|
+
for (const match of matches(NEGATED_SAFEGUARD_RE, text)) {
|
|
13885
|
+
if (!NOT_KEPT_RE.test(match[1]))
|
|
13886
|
+
return true;
|
|
13887
|
+
const head = text.slice(Math.max(0, match.index - SAFEGUARD_LOOKBEHIND), match.index);
|
|
13888
|
+
const sentence = head.slice(Math.max(head.lastIndexOf("."), head.lastIndexOf(";"), head.lastIndexOf("\n")) + 1);
|
|
13889
|
+
if (!REFUSED_INPUT_RE.test(sentence))
|
|
13890
|
+
return true;
|
|
13891
|
+
}
|
|
13892
|
+
return false;
|
|
13893
|
+
}
|
|
13431
13894
|
function claimsDefect(text) {
|
|
13432
|
-
if (BUG_LABEL_RE.test(text) || SILENT_RE.test(text) || CONCESSION_RE.test(text) || successContradicted(text) ||
|
|
13895
|
+
if (BUG_LABEL_RE.test(text) || SILENT_RE.test(text) || CONCESSION_RE.test(text) || successContradicted(text) || negatedSafeguard(text)) {
|
|
13433
13896
|
return true;
|
|
13434
13897
|
}
|
|
13435
13898
|
return BAD_VALUE_RE.test(text) && !REFUSAL_RE.test(text);
|
|
@@ -13437,7 +13900,7 @@ function claimsDefect(text) {
|
|
|
13437
13900
|
function hasTestFailContract(code) {
|
|
13438
13901
|
return TEST_FAIL_CONTRACT_RE.test(code);
|
|
13439
13902
|
}
|
|
13440
|
-
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;
|
|
13903
|
+
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;
|
|
13441
13904
|
var init_claims_defect = __esm({
|
|
13442
13905
|
"../shared/dist/claims-defect.js"() {
|
|
13443
13906
|
"use strict";
|
|
@@ -13481,7 +13944,10 @@ var init_claims_defect = __esm({
|
|
|
13481
13944
|
NOT_A_CLEARING_RE = /\b(?:cancel|refresh|reload|preview|open|view|fail|hover|scroll|sort|filter|search)\w*/i;
|
|
13482
13945
|
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;
|
|
13483
13946
|
OFF_BY_RE = /\boff[- ]by[- ](?:one|two|\d{1,3})\b/i;
|
|
13484
|
-
NEGATED_SAFEGUARD_RE = /\bnot (
|
|
13947
|
+
NEGATED_SAFEGUARD_RE = /\bnot (rejected|validated|enforced|applied|persisted|saved)\b/gi;
|
|
13948
|
+
NOT_KEPT_RE = /^(?:applied|persisted|saved)$/i;
|
|
13949
|
+
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;
|
|
13950
|
+
SAFEGUARD_LOOKBEHIND = 80;
|
|
13485
13951
|
BAD_VALUE_RE = /\b(?:incorrect|wrong|stale|negative|epoch|unix) (?:total|amount|value|date|count|price|label|plan|balance)\b/i;
|
|
13486
13952
|
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;
|
|
13487
13953
|
TEST_FAIL_CONTRACT_RE = /\btest\.fail\s{0,20}\(/;
|
|
@@ -14340,7 +14806,7 @@ function findExpectCalls(code) {
|
|
|
14340
14806
|
return out;
|
|
14341
14807
|
}
|
|
14342
14808
|
function countExpectCalls(code) {
|
|
14343
|
-
return findExpectCalls(stripComments2(code)).length;
|
|
14809
|
+
return findExpectCalls(stripComments2(withExpectAliasesAsExpect(code))).length;
|
|
14344
14810
|
}
|
|
14345
14811
|
function countActionCalls(code) {
|
|
14346
14812
|
return [...stripComments2(code).matchAll(ACTION_CALL)].length;
|
|
@@ -16669,6 +17135,7 @@ var init_test_authoring_quality = __esm({
|
|
|
16669
17135
|
"../shared/dist/test-authoring-quality.js"() {
|
|
16670
17136
|
"use strict";
|
|
16671
17137
|
init_claims_defect();
|
|
17138
|
+
init_expect_call();
|
|
16672
17139
|
init_readiness_poll();
|
|
16673
17140
|
init_persistence_reread();
|
|
16674
17141
|
init_write_response_check();
|
|
@@ -16795,7 +17262,7 @@ var init_test_authoring_quality = __esm({
|
|
|
16795
17262
|
MAX_BALANCED_SCAN = 2048;
|
|
16796
17263
|
MAX_CALLS = 500;
|
|
16797
17264
|
MAX_SEEN_SCAN = 200;
|
|
16798
|
-
ACTION_CALL = /\.(?:click|dblclick|fill|press|pressSequentially|type|check|uncheck|selectOption|setInputFiles|hover|tap|dragTo|goto)\s*\(/g;
|
|
17265
|
+
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;
|
|
16799
17266
|
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\(/;
|
|
16800
17267
|
ACTION_AFTER = /^\s{0,20}\.\s{0,20}(?:click|fill|check|uncheck|press|type|hover|focus|selectOption|setInputFiles|dblclick|tap)\s{0,20}\(/;
|
|
16801
17268
|
MAX_CONTACTS = 200;
|
|
@@ -18304,7 +18771,7 @@ function credentialEntries(maps) {
|
|
|
18304
18771
|
}
|
|
18305
18772
|
function replacePattern(value, form) {
|
|
18306
18773
|
const escaped = form.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
|
|
18307
|
-
return
|
|
18774
|
+
return value.length < WHOLE_WORD_BELOW ? `(?<![A-Za-z0-9_])${escaped}(?![A-Za-z0-9_])` : escaped;
|
|
18308
18775
|
}
|
|
18309
18776
|
function findCredentialTextLeftover(text, maps) {
|
|
18310
18777
|
const bare = text.split(WITHHELD_CREDENTIAL_TEXT).join(" ").replace(REFERENCE, " ").toLowerCase();
|
|
@@ -18318,11 +18785,17 @@ function scrubCredentialText(text, maps) {
|
|
|
18318
18785
|
const entries = credentialEntries(maps);
|
|
18319
18786
|
if (entries.length === 0)
|
|
18320
18787
|
return text;
|
|
18321
|
-
|
|
18788
|
+
const alternatives = [];
|
|
18322
18789
|
for (const [key, value] of entries) {
|
|
18323
18790
|
for (const form of credentialValueForms(value))
|
|
18324
|
-
|
|
18791
|
+
alternatives.push({ pattern: replacePattern(value, form), key, length: form.length });
|
|
18325
18792
|
}
|
|
18793
|
+
alternatives.sort((a, b) => b.length - a.length);
|
|
18794
|
+
const matcher = new RegExp([REFERENCE.source, ...alternatives.map((alt) => `(${alt.pattern})`)].join("|"), "gi");
|
|
18795
|
+
const out = text.replace(matcher, (...args) => {
|
|
18796
|
+
const index = args.slice(1, 1 + alternatives.length).findIndex((group) => group !== void 0);
|
|
18797
|
+
return index < 0 ? String(args[0]) : `{{secret:${alternatives[index].key}}}`;
|
|
18798
|
+
});
|
|
18326
18799
|
return findCredentialTextLeftover(out, maps) ? WITHHELD_CREDENTIAL_TEXT : out;
|
|
18327
18800
|
}
|
|
18328
18801
|
function scrubCredentialStringsIn(value, maps) {
|
|
@@ -18816,10 +19289,24 @@ function onlyUrlAfter(code, writeAt) {
|
|
|
18816
19289
|
i = at + 6;
|
|
18817
19290
|
}
|
|
18818
19291
|
}
|
|
19292
|
+
function readsOrderAfter(code, writeAt) {
|
|
19293
|
+
const rest = code.slice(writeAt + 1);
|
|
19294
|
+
if (ORDER_READ_MARKERS.some((m) => rest.includes(m)) && nextExpect(code, writeAt + 1) >= 0)
|
|
19295
|
+
return true;
|
|
19296
|
+
for (let i = writeAt + 1; ; ) {
|
|
19297
|
+
const at = nextExpect(code, i);
|
|
19298
|
+
if (at < 0)
|
|
19299
|
+
return false;
|
|
19300
|
+
if (ORDERED_ARRAY_EXPECT.test(expectStatement(code, at)))
|
|
19301
|
+
return true;
|
|
19302
|
+
i = at + 6;
|
|
19303
|
+
}
|
|
19304
|
+
}
|
|
18819
19305
|
function assessTitlePromiseShape(claimText, code) {
|
|
18820
19306
|
const persistence = PERSISTENCE_RE.test(claimText);
|
|
18821
19307
|
const refusal = REFUSAL_RE2.test(claimText);
|
|
18822
|
-
|
|
19308
|
+
const order = ORDER_RE.test(claimText) || ORDER_RE_CASED.test(claimText);
|
|
19309
|
+
if (!persistence && !refusal && !order)
|
|
18823
19310
|
return [];
|
|
18824
19311
|
const blank = blankCommentsAndStringLiterals(code);
|
|
18825
19312
|
const writeAt = proofAnchor(blank);
|
|
@@ -18832,9 +19319,12 @@ function assessTitlePromiseShape(claimText, code) {
|
|
|
18832
19319
|
if (refusal && onlyUrlAfter(blank, writeAt)) {
|
|
18833
19320
|
findings.push({ rule: "rejection-only-url", reason: REFUSAL_REASON });
|
|
18834
19321
|
}
|
|
19322
|
+
if (order && !readsOrderAfter(blank, writeAt)) {
|
|
19323
|
+
findings.push({ rule: "sort-without-order", reason: ORDER_REASON });
|
|
19324
|
+
}
|
|
18835
19325
|
return findings;
|
|
18836
19326
|
}
|
|
18837
|
-
var PERSIST_REASON, REFUSAL_REASON, PERSISTENCE_RE, REFUSAL_RE2, WRITE_MARKERS, VALUE_WRITE_MARKERS, REREAD_MARKERS;
|
|
19327
|
+
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;
|
|
18838
19328
|
var init_title_promise_shape = __esm({
|
|
18839
19329
|
"../shared/dist/title-promise-shape.js"() {
|
|
18840
19330
|
"use strict";
|
|
@@ -18843,6 +19333,21 @@ var init_title_promise_shape = __esm({
|
|
|
18843
19333
|
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.";
|
|
18844
19334
|
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;
|
|
18845
19335
|
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;
|
|
19336
|
+
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).";
|
|
19337
|
+
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;
|
|
19338
|
+
ORDER_RE_CASED = /\b[AZ]\s{0,3}(?:to|-|→)\s{0,3}[AZ]\b/;
|
|
19339
|
+
ORDER_READ_MARKERS = [
|
|
19340
|
+
".allTextContents(",
|
|
19341
|
+
".allInnerTexts(",
|
|
19342
|
+
".evaluateAll(",
|
|
19343
|
+
".toMatchAriaSnapshot(",
|
|
19344
|
+
".sort(",
|
|
19345
|
+
".toSorted(",
|
|
19346
|
+
".first()",
|
|
19347
|
+
".last()",
|
|
19348
|
+
".nth("
|
|
19349
|
+
];
|
|
19350
|
+
ORDERED_ARRAY_EXPECT = /\.(?:toHaveText|toContainText|toEqual|toStrictEqual)\s*\(\s*\[/;
|
|
18846
19351
|
WRITE_MARKERS = [
|
|
18847
19352
|
".fill(",
|
|
18848
19353
|
".click(",
|
|
@@ -19174,7 +19679,7 @@ function assertionsReadVisibleContent(code) {
|
|
|
19174
19679
|
});
|
|
19175
19680
|
}
|
|
19176
19681
|
function isLegacyReviewFlag(tags, activity) {
|
|
19177
|
-
const flagged = tags.includes(
|
|
19682
|
+
const flagged = tags.includes(NEEDS_REVIEW_TAG2) || activity?.needsReview === true;
|
|
19178
19683
|
const source = activity?.needsReviewSource;
|
|
19179
19684
|
return flagged && !(typeof source === "string" && source in SOURCE_RANK);
|
|
19180
19685
|
}
|
|
@@ -19236,7 +19741,7 @@ function recheckGeneratorNotes(notes, input) {
|
|
|
19236
19741
|
}
|
|
19237
19742
|
function reviewHolds(tags, activity, cleared = /* @__PURE__ */ new Set()) {
|
|
19238
19743
|
const out = [];
|
|
19239
|
-
const tagged = tags.includes(
|
|
19744
|
+
const tagged = tags.includes(NEEDS_REVIEW_TAG2);
|
|
19240
19745
|
const generatorAsked = activity?.needsReview === true;
|
|
19241
19746
|
if (tagged) {
|
|
19242
19747
|
if (activity?.needsReviewSource === "human")
|
|
@@ -19296,7 +19801,7 @@ function evaluatePromotionGate(input, checks = PROMOTION_QUALITY_CHECKS) {
|
|
|
19296
19801
|
...suspiciousTagged ? { suspiciousPass: { mode: suspicious, covered: suspiciousCovered, wouldHold: suspiciousHold } } : {}
|
|
19297
19802
|
};
|
|
19298
19803
|
}
|
|
19299
|
-
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,
|
|
19804
|
+
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;
|
|
19300
19805
|
var init_promotion_quality_gate = __esm({
|
|
19301
19806
|
"../shared/dist/promotion-quality-gate.js"() {
|
|
19302
19807
|
"use strict";
|
|
@@ -19315,8 +19820,8 @@ var init_promotion_quality_gate = __esm({
|
|
|
19315
19820
|
SOURCE_RANK = { generator: 0, classifier: 0, healer: 1, human: 2 };
|
|
19316
19821
|
strengthCheck = {
|
|
19317
19822
|
id: "assertion-strength",
|
|
19318
|
-
run({ code, tags, strengthGate, flagContradictedAdditions, lastActionProof }) {
|
|
19319
|
-
const verdict = validateAssertionStrength(code, { flagContradictedAdditions: flagContradictedAdditions === true, lastActionProof });
|
|
19823
|
+
run({ code, tags, strengthGate, flagContradictedAdditions, lastActionProof, absenceProof }) {
|
|
19824
|
+
const verdict = validateAssertionStrength(code, { flagContradictedAdditions: flagContradictedAdditions === true, lastActionProof, absenceProof });
|
|
19320
19825
|
if (verdict.severity === "route-bug")
|
|
19321
19826
|
return [{ check: "route-bug" }];
|
|
19322
19827
|
const authDowngrade = verdict.severity === "downgrade" && tags.some((t) => t.toLowerCase() === "@auth");
|
|
@@ -19574,7 +20079,7 @@ ${goalText}`);
|
|
|
19574
20079
|
NOT_CONTENT_MATCHER = /\.(?:toHaveURL|toHaveTitle|toBeOK)\s*\(|\bwaitForURL\s*\(|\.(?:status|ok)\s*\(\s*\)/;
|
|
19575
20080
|
CONTENT_PROOF = /* @__PURE__ */ new Set(["controlled", "revealed", "value"]);
|
|
19576
20081
|
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/;
|
|
19577
|
-
|
|
20082
|
+
NEEDS_REVIEW_TAG2 = "@needs-review";
|
|
19578
20083
|
count2 = (v) => typeof v === "number" && Number.isFinite(v) ? v : 0;
|
|
19579
20084
|
RECHECKED_NOTES = [
|
|
19580
20085
|
{
|
|
@@ -19695,11 +20200,18 @@ var init_regex_alternation = __esm({
|
|
|
19695
20200
|
function isPageEvidenceEntry(entry) {
|
|
19696
20201
|
return typeof entry === "object" && entry !== null && entry.outcomeType === PAGE_LOADING_OUTCOME_TYPE;
|
|
19697
20202
|
}
|
|
19698
|
-
|
|
20203
|
+
function pageScreenshotKey(projectId, safeName, stillLoading = false) {
|
|
20204
|
+
return `discovery/${projectId}/screenshots/${stillLoading ? `${STILL_LOADING_SCREENSHOT_DIR}/` : ""}${safeName}.png`;
|
|
20205
|
+
}
|
|
20206
|
+
function isStillLoadingScreenshotKey(key) {
|
|
20207
|
+
return typeof key === "string" && key.includes(`/screenshots/${STILL_LOADING_SCREENSHOT_DIR}/`);
|
|
20208
|
+
}
|
|
20209
|
+
var PAGE_LOADING_OUTCOME_TYPE, STILL_LOADING_SCREENSHOT_DIR;
|
|
19699
20210
|
var init_page_evidence = __esm({
|
|
19700
20211
|
"../shared/dist/page-evidence.js"() {
|
|
19701
20212
|
"use strict";
|
|
19702
20213
|
PAGE_LOADING_OUTCOME_TYPE = "LOADING";
|
|
20214
|
+
STILL_LOADING_SCREENSHOT_DIR = "still-loading";
|
|
19703
20215
|
}
|
|
19704
20216
|
});
|
|
19705
20217
|
|
|
@@ -20104,11 +20616,13 @@ __export(dist_exports, {
|
|
|
20104
20616
|
SERVER_INTERRUPT_ERROR_PREFIX: () => SERVER_INTERRUPT_ERROR_PREFIX,
|
|
20105
20617
|
SHARED_PRICING_PLAN_MAP: () => SHARED_PRICING_PLAN_MAP,
|
|
20106
20618
|
SHARED_SERVER_APPLICATION_NAME: () => SHARED_SERVER_APPLICATION_NAME,
|
|
20619
|
+
SMS_NOT_RECEIVED_PREFIXES: () => SMS_NOT_RECEIVED_PREFIXES,
|
|
20107
20620
|
SPEND_SOURCE_KEY: () => SPEND_SOURCE_KEY,
|
|
20108
20621
|
SPEND_SOURCE_SERVER: () => SPEND_SOURCE_SERVER,
|
|
20109
20622
|
STATED_LIMITS_VERSION: () => STATED_LIMITS_VERSION,
|
|
20110
20623
|
STATED_PATTERN_MAX: () => STATED_PATTERN_MAX,
|
|
20111
20624
|
STATIC_STANDARD_MIN_INTERACTIVE: () => STATIC_STANDARD_MIN_INTERACTIVE,
|
|
20625
|
+
STILL_LOADING_SCREENSHOT_DIR: () => STILL_LOADING_SCREENSHOT_DIR,
|
|
20112
20626
|
STT_MODEL_OPTIONS: () => STT_MODEL_OPTIONS,
|
|
20113
20627
|
SUCCESS_NOTICE_TEXT: () => SUCCESS_NOTICE_TEXT,
|
|
20114
20628
|
SUPPORT_EMAIL: () => SUPPORT_EMAIL,
|
|
@@ -20136,6 +20650,7 @@ __export(dist_exports, {
|
|
|
20136
20650
|
TTS_VOICE_OPTIONS: () => TTS_VOICE_OPTIONS,
|
|
20137
20651
|
UI_VARIANTS: () => UI_VARIANTS,
|
|
20138
20652
|
UNEXPECTED_PASS_ERROR_PREFIX: () => UNEXPECTED_PASS_ERROR_PREFIX,
|
|
20653
|
+
UNVERIFIED_HEAL_CONFIDENCES: () => UNVERIFIED_HEAL_CONFIDENCES,
|
|
20139
20654
|
USER_CANCELLED_RUN_ERROR: () => USER_CANCELLED_RUN_ERROR,
|
|
20140
20655
|
VISUAL_MASK_SELECTORS_MAX: () => VISUAL_MASK_SELECTORS_MAX,
|
|
20141
20656
|
VISUAL_MASK_SELECTOR_FORBIDDEN: () => VISUAL_MASK_SELECTOR_FORBIDDEN,
|
|
@@ -20189,6 +20704,7 @@ __export(dist_exports, {
|
|
|
20189
20704
|
blankBlockComments: () => blankBlockComments3,
|
|
20190
20705
|
blankCommentsAndStringLiterals: () => blankCommentsAndStringLiterals,
|
|
20191
20706
|
blankKeepingLines: () => blankKeepingLines,
|
|
20707
|
+
blockedRunLogLine: () => blockedRunLogLine,
|
|
20192
20708
|
boundedCallCostUsd: () => boundedCallCostUsd,
|
|
20193
20709
|
brokerResponseBody: () => brokerResponseBody,
|
|
20194
20710
|
bugFoundQuarantineWhere: () => bugFoundQuarantineWhere,
|
|
@@ -20294,6 +20810,7 @@ __export(dist_exports, {
|
|
|
20294
20810
|
evaluatePromotionGate: () => evaluatePromotionGate,
|
|
20295
20811
|
excludeCancelledRunsAnd: () => excludeCancelledRunsAnd,
|
|
20296
20812
|
excludeCancelledRunsWhere: () => excludeCancelledRunsWhere,
|
|
20813
|
+
expectAliasNames: () => expectAliasNames,
|
|
20297
20814
|
explicitAgenticCliOverrideError: () => explicitAgenticCliOverrideError,
|
|
20298
20815
|
extractAssertedControlSignatures: () => extractAssertedControlSignatures,
|
|
20299
20816
|
extractMuseAssistantJson: () => extractMuseAssistantJson,
|
|
@@ -20360,6 +20877,8 @@ __export(dist_exports, {
|
|
|
20360
20877
|
googleGisRequestUrl: () => googleGisRequestUrl,
|
|
20361
20878
|
googleSignInLimitLines: () => googleSignInLimitLines,
|
|
20362
20879
|
googleSignInPolicyProblems: () => googleSignInPolicyProblems,
|
|
20880
|
+
googleSignInRefusedLine: () => googleSignInRefusedLine,
|
|
20881
|
+
googleSignInRefusedReason: () => googleSignInRefusedReason,
|
|
20363
20882
|
hasCrossPlatformIdentity: () => hasCrossPlatformIdentity,
|
|
20364
20883
|
hasEncodedRequestBody: () => hasEncodedRequestBody,
|
|
20365
20884
|
hasEngineSpecParts: () => hasEngineSpecParts,
|
|
@@ -20403,6 +20922,7 @@ __export(dist_exports, {
|
|
|
20403
20922
|
isCodeFactNote: () => isCodeFactNote,
|
|
20404
20923
|
isConfigLikeReason: () => isConfigLikeReason,
|
|
20405
20924
|
isCountedFailure: () => isCountedFailure,
|
|
20925
|
+
isCredentialHeaderName: () => isCredentialHeaderName,
|
|
20406
20926
|
isCreditPackAmountCents: () => isCreditPackAmountCents,
|
|
20407
20927
|
isDbPricingPlan: () => isDbPricingPlan,
|
|
20408
20928
|
isDeniedGoogleBrokerRedirectHost: () => isDeniedGoogleBrokerRedirectHost,
|
|
@@ -20458,8 +20978,10 @@ __export(dist_exports, {
|
|
|
20458
20978
|
isSharedAuthTenantHost: () => isSharedAuthTenantHost,
|
|
20459
20979
|
isSharedSuffix: () => isSharedSuffix,
|
|
20460
20980
|
isSingleEmailAddress: () => isSingleEmailAddress,
|
|
20981
|
+
isSmsNotReceivedError: () => isSmsNotReceivedError,
|
|
20461
20982
|
isSoftOutcome: () => isSoftOutcome,
|
|
20462
20983
|
isStateValueMobileClassName: () => isStateValueMobileClassName,
|
|
20984
|
+
isStillLoadingScreenshotKey: () => isStillLoadingScreenshotKey,
|
|
20463
20985
|
isSttModelForProvider: () => isSttModelForProvider,
|
|
20464
20986
|
isTestReadyForPlatform: () => isTestReadyForPlatform,
|
|
20465
20987
|
isTestVariableSecretKey: () => isTestVariableSecretKey,
|
|
@@ -20467,6 +20989,7 @@ __export(dist_exports, {
|
|
|
20467
20989
|
isUIVariant: () => isUIVariant,
|
|
20468
20990
|
isUnexpectedPassError: () => isUnexpectedPassError,
|
|
20469
20991
|
isUnsafePrecondition: () => isUnsafePrecondition,
|
|
20992
|
+
isUnverifiedHealPass: () => isUnverifiedHealPass,
|
|
20470
20993
|
isUsableDeepgramKey: () => isUsableDeepgramKey,
|
|
20471
20994
|
isUserCancelledError: () => isUserCancelledError,
|
|
20472
20995
|
isValidAgenticCliModel: () => isValidAgenticCliModel,
|
|
@@ -20491,8 +21014,10 @@ __export(dist_exports, {
|
|
|
20491
21014
|
lintTestData: () => lintTestData,
|
|
20492
21015
|
lintTestQuality: () => lintTestQuality,
|
|
20493
21016
|
literalUrlRegex: () => literalUrlRegex,
|
|
21017
|
+
loginFormAlertTexts: () => loginFormAlertTexts,
|
|
20494
21018
|
longCredentialValues: () => longCredentialValues,
|
|
20495
21019
|
maskedOutsideStrings: () => maskedOutsideStrings,
|
|
21020
|
+
mayBeUnverifiedHealPass: () => mayBeUnverifiedHealPass,
|
|
20496
21021
|
mediumForMobilePlatform: () => mediumForMobilePlatform,
|
|
20497
21022
|
mergePageType: () => mergePageType,
|
|
20498
21023
|
mergeSuspectedBugsAcrossSurveys: () => mergeSuspectedBugsAcrossSurveys,
|
|
@@ -20526,7 +21051,9 @@ __export(dist_exports, {
|
|
|
20526
21051
|
opensRegexLiteral: () => opensRegexLiteral,
|
|
20527
21052
|
otherMobilePlatform: () => otherMobilePlatform,
|
|
20528
21053
|
outcomeNamedByAssertion: () => outcomeNamedByAssertion,
|
|
21054
|
+
pageScreenshotKey: () => pageScreenshotKey,
|
|
20529
21055
|
parenDepthDelta: () => parenDepthDelta,
|
|
21056
|
+
parseAbsenceProofMode: () => parseAbsenceProofMode,
|
|
20530
21057
|
parseApiCardModelUpdate: () => parseApiCardModelUpdate,
|
|
20531
21058
|
parseAppTimezone: () => parseAppTimezone,
|
|
20532
21059
|
parseAuditGroupScopeId: () => parseAuditGroupScopeId,
|
|
@@ -20602,7 +21129,7 @@ __export(dist_exports, {
|
|
|
20602
21129
|
redactNeedlesText: () => redactNeedlesText,
|
|
20603
21130
|
redirectOnAppSite: () => redirectOnAppSite,
|
|
20604
21131
|
refuseEncodedRequestBodies: () => refuseEncodedRequestBodies,
|
|
20605
|
-
regexLiteralEnd: () =>
|
|
21132
|
+
regexLiteralEnd: () => regexLiteralEnd,
|
|
20606
21133
|
regexOpensAfter: () => regexOpensAfter,
|
|
20607
21134
|
registrableDomain: () => registrableDomain,
|
|
20608
21135
|
renderIdentity: () => renderIdentity,
|
|
@@ -20644,6 +21171,9 @@ __export(dist_exports, {
|
|
|
20644
21171
|
saveOpensConfirm: () => saveOpensConfirm,
|
|
20645
21172
|
scanClockDependentLiterals: () => scanClockDependentLiterals,
|
|
20646
21173
|
scanHtmlTags: () => scanHtmlTags,
|
|
21174
|
+
scrubCredentialHeaderValue: () => scrubCredentialHeaderValue,
|
|
21175
|
+
scrubCredentialHeaders: () => scrubCredentialHeaders,
|
|
21176
|
+
scrubCredentialHeadersDeep: () => scrubCredentialHeadersDeep,
|
|
20647
21177
|
scrubCredentialStringsIn: () => scrubCredentialStringsIn,
|
|
20648
21178
|
scrubCredentialText: () => scrubCredentialText,
|
|
20649
21179
|
scrubSearchParams: () => scrubSearchParams,
|
|
@@ -20654,9 +21184,12 @@ __export(dist_exports, {
|
|
|
20654
21184
|
selectorForValue: () => selectorForValue,
|
|
20655
21185
|
setAgenticCliRuntime: () => setAgenticCliRuntime,
|
|
20656
21186
|
setProviderChainOverrides: () => setProviderChainOverrides,
|
|
21187
|
+
signInStepPassedBeforeFailure: () => signInStepPassedBeforeFailure,
|
|
21188
|
+
signsInInline: () => signsInInline,
|
|
20657
21189
|
signupPlanFor: () => signupPlanFor,
|
|
20658
21190
|
simplifyMobileWanderingInPlace: () => simplifyMobileWanderingInPlace,
|
|
20659
21191
|
siteOf: () => siteOf,
|
|
21192
|
+
specCallsExpect: () => specCallsExpect,
|
|
20660
21193
|
specItemsFromTestPlans: () => specItemsFromTestPlans,
|
|
20661
21194
|
splitAnswerLines: () => splitAnswerLines,
|
|
20662
21195
|
statedLimitsFromAttrs: () => statedLimitsFromAttrs,
|
|
@@ -20704,6 +21237,7 @@ __export(dist_exports, {
|
|
|
20704
21237
|
visualProDailyBudget: () => visualProDailyBudget,
|
|
20705
21238
|
voiceMatchesTtsModel: () => voiceMatchesTtsModel,
|
|
20706
21239
|
voicesForTtsModel: () => voicesForTtsModel,
|
|
21240
|
+
withExpectAliasesAsExpect: () => withExpectAliasesAsExpect,
|
|
20707
21241
|
withGeneratorReviewSource: () => withGeneratorReviewSource,
|
|
20708
21242
|
withReviewSource: () => withReviewSource,
|
|
20709
21243
|
withoutNotChargedClaim: () => withoutNotChargedClaim,
|
|
@@ -20758,6 +21292,7 @@ var init_dist = __esm({
|
|
|
20758
21292
|
init_encoded_forms();
|
|
20759
21293
|
init_block_comments();
|
|
20760
21294
|
init_redact_needles();
|
|
21295
|
+
init_credential_header_scrub();
|
|
20761
21296
|
init_voice_config();
|
|
20762
21297
|
init_deepgram_speech();
|
|
20763
21298
|
init_deepgram_voices();
|
|
@@ -20835,6 +21370,7 @@ var require_survey_network_guard = __commonJS({
|
|
|
20835
21370
|
exports2.graphqlUrlCarriesQuery = graphqlUrlCarriesQuery;
|
|
20836
21371
|
exports2.graphqlUrlQueriesAreReads = graphqlUrlQueriesAreReads;
|
|
20837
21372
|
exports2.graphqlGetMayWrite = graphqlGetMayWrite;
|
|
21373
|
+
exports2.queryMethodMayWrite = queryMethodMayWrite;
|
|
20838
21374
|
exports2.writeShapedGet = writeShapedGet;
|
|
20839
21375
|
exports2.decideSurveyRequest = decideSurveyRequest;
|
|
20840
21376
|
exports2.installSurveyNetworkGuard = installSurveyNetworkGuard;
|
|
@@ -21399,6 +21935,12 @@ var require_survey_network_guard = __commonJS({
|
|
|
21399
21935
|
const docs = url.searchParams.getAll("query");
|
|
21400
21936
|
return docs.length > 0 && (typeof body === "string" && body.length > 0 || docs.some((doc) => !graphqlDocumentIsQueryOnly(doc)));
|
|
21401
21937
|
}
|
|
21938
|
+
function queryMethodMayWrite(url, body, graphqlEndpoint) {
|
|
21939
|
+
if (graphqlEndpoint)
|
|
21940
|
+
return true;
|
|
21941
|
+
const operation = /\b(?:mutation|subscription)\b/i;
|
|
21942
|
+
return typeof body === "string" && operation.test(body) || [...url.searchParams.values()].some((v) => operation.test(v));
|
|
21943
|
+
}
|
|
21402
21944
|
function isGraphqlGetWrite(url, s, body) {
|
|
21403
21945
|
return isGraphqlEndpoint(url, s) && graphqlGetMayWrite(url, body);
|
|
21404
21946
|
}
|
|
@@ -21426,6 +21968,7 @@ var require_survey_network_guard = __commonJS({
|
|
|
21426
21968
|
"log-out"
|
|
21427
21969
|
]);
|
|
21428
21970
|
exports2.AMBIGUOUS_WRITE_VERBS = /* @__PURE__ */ new Set(["archive", "confirm", "disable"]);
|
|
21971
|
+
var LABEL_PAGE_NOUNS = /* @__PURE__ */ new Set(["archive"]);
|
|
21429
21972
|
var RESET_PAGE_WORDS = /* @__PURE__ */ new Set(["reset-password", "password-reset", "reset_password", "password_reset", "resetpassword", "forgot-password"]);
|
|
21430
21973
|
var isTokenLikeSegment = (seg) => exports2.ID_SEGMENT_RE.test(seg) || /^[A-Za-z0-9_-]{20,}$/.test(seg);
|
|
21431
21974
|
var WRITE_SHAPED_STATIC_EXTENSIONS = [
|
|
@@ -21571,6 +22114,8 @@ var require_survey_network_guard = __commonJS({
|
|
|
21571
22114
|
if (oneTimeToken)
|
|
21572
22115
|
return { reason: "one-time-token" };
|
|
21573
22116
|
const verb = opts.triggerLabel ? labelVerb(opts.triggerLabel) : void 0;
|
|
22117
|
+
if (verb && LABEL_PAGE_NOUNS.has(verb) && last === verb && params.length === 0)
|
|
22118
|
+
return null;
|
|
21574
22119
|
return verb && !opts.ignoreVerbs?.has(verb) ? { reason: "label", verb } : null;
|
|
21575
22120
|
}
|
|
21576
22121
|
function writeShapedText(w) {
|
|
@@ -21605,9 +22150,10 @@ var require_survey_network_guard = __commonJS({
|
|
|
21605
22150
|
return { action: "allow", rule: readMethod ? "read" : "non-http" };
|
|
21606
22151
|
const appSite = isAppSite(url.hostname, s.appBaseUrl);
|
|
21607
22152
|
const graphqlGet = method === "GET" && isGraphqlGetWrite(url, s, req.postData);
|
|
22153
|
+
const queryRead = method === "QUERY" && !queryMethodMayWrite(url, req.postData, isGraphqlEndpoint(url, s));
|
|
21608
22154
|
const inActionWindow = s.lastActionAt > 0 && s.now - s.lastActionAt <= ACTION_NAVIGATION_WINDOW_MS;
|
|
21609
22155
|
const typedLoad = s.agentLoad === true && req.resourceType === "document";
|
|
21610
|
-
const writeShaped = method === "GET" && appSite && !graphqlGet && (inActionWindow || typedLoad) ? writeShapedGet(req.url, {
|
|
22156
|
+
const writeShaped = (method === "GET" || queryRead) && appSite && !graphqlGet && (inActionWindow || typedLoad) ? writeShapedGet(req.url, {
|
|
21611
22157
|
appSites: appSitesOf(s.appBaseUrl),
|
|
21612
22158
|
resourceType: req.resourceType ?? "",
|
|
21613
22159
|
// A label says what a click opens: it only judges a page load, never the app's own fetches, and never a typed URL.
|
|
@@ -21615,7 +22161,7 @@ var require_survey_network_guard = __commonJS({
|
|
|
21615
22161
|
}) : null;
|
|
21616
22162
|
if (writeShaped)
|
|
21617
22163
|
return decideWriteShapedGet(req, s, writeShaped);
|
|
21618
|
-
if (readMethod && !graphqlGet)
|
|
22164
|
+
if ((readMethod || queryRead) && !graphqlGet)
|
|
21619
22165
|
return { action: "allow", rule: "read" };
|
|
21620
22166
|
if (!appSite && ((0, shared_1.isAuthProviderHost)(url.hostname) || isTelemetryHost(url.hostname)))
|
|
21621
22167
|
return { action: "allow", rule: "third-party" };
|
|
@@ -22432,6 +22978,13 @@ var require_run_write_guard = __commonJS({
|
|
|
22432
22978
|
function currentRunSignInSecrets() {
|
|
22433
22979
|
return policyScope.getStore()?.signInSecrets ?? [];
|
|
22434
22980
|
}
|
|
22981
|
+
function isSocketIoPollingPost(pathname, search) {
|
|
22982
|
+
if (pathname !== "/socket.io" && pathname !== "/socket.io/")
|
|
22983
|
+
return false;
|
|
22984
|
+
const eio = search.getAll("EIO");
|
|
22985
|
+
const transport = search.getAll("transport");
|
|
22986
|
+
return eio.length === 1 && /^[0-9]{1,2}$/.test(eio[0]) && transport.length === 1 && transport[0] === "polling";
|
|
22987
|
+
}
|
|
22435
22988
|
var READ_METHODS = /* @__PURE__ */ new Set(["GET", "HEAD", "OPTIONS"]);
|
|
22436
22989
|
var MAX_BODY_CHARS = 262144;
|
|
22437
22990
|
var MAX_SIGN_IN_LOG_LINES = 20;
|
|
@@ -23266,7 +23819,8 @@ var require_run_write_guard = __commonJS({
|
|
|
23266
23819
|
const method = facts.method.toUpperCase();
|
|
23267
23820
|
const host = url.hostname.toLowerCase().replace(/\.$/, "");
|
|
23268
23821
|
const pathname = url.pathname;
|
|
23269
|
-
|
|
23822
|
+
const queryRead = method === "QUERY" && !(0, survey_network_guard_js_1.queryMethodMayWrite)(url, facts.postData, isGraphqlPath(pathname));
|
|
23823
|
+
if (READ_METHODS.has(method) || queryRead) {
|
|
23270
23824
|
if (method === "GET" && isGraphqlPath(pathname) && (0, survey_network_guard_js_1.graphqlGetMayWrite)(url, facts.postData)) {
|
|
23271
23825
|
return { action: "write", rule: "graphql-get-mutation", record: describe(facts, url, "graphql-get-mutation") };
|
|
23272
23826
|
}
|
|
@@ -23321,6 +23875,8 @@ var require_run_write_guard = __commonJS({
|
|
|
23321
23875
|
if (segs.length > 0 && SEARCH_LAST_SEGMENTS.has(segs[segs.length - 1]))
|
|
23322
23876
|
return { action: "allow", rule: "search-post" };
|
|
23323
23877
|
}
|
|
23878
|
+
if (appSite && method === "POST" && isSocketIoPollingPost(pathname, url.searchParams))
|
|
23879
|
+
return { action: "allow", rule: "realtime-poll" };
|
|
23324
23880
|
if (hostIs(host, PAYMENT_HOSTS))
|
|
23325
23881
|
return { action: "write", rule: "payment", record: describe(facts, url, "payment") };
|
|
23326
23882
|
if (appSite)
|
|
@@ -24125,6 +24681,32 @@ var require_run_write_guard = __commonJS({
|
|
|
24125
24681
|
return false;
|
|
24126
24682
|
}
|
|
24127
24683
|
}
|
|
24684
|
+
function contextStartsOffline(context) {
|
|
24685
|
+
try {
|
|
24686
|
+
return context._options?.offline === true;
|
|
24687
|
+
} catch {
|
|
24688
|
+
return false;
|
|
24689
|
+
}
|
|
24690
|
+
}
|
|
24691
|
+
function trackOffline(context) {
|
|
24692
|
+
const state = { offline: contextStartsOffline(context) };
|
|
24693
|
+
const original = context.setOffline;
|
|
24694
|
+
if (typeof original === "function") {
|
|
24695
|
+
Object.assign(context, {
|
|
24696
|
+
setOffline: async (offline) => {
|
|
24697
|
+
const before = state.offline;
|
|
24698
|
+
state.offline = offline === true;
|
|
24699
|
+
try {
|
|
24700
|
+
await original.call(context, offline);
|
|
24701
|
+
} catch (err) {
|
|
24702
|
+
state.offline = before;
|
|
24703
|
+
throw err;
|
|
24704
|
+
}
|
|
24705
|
+
}
|
|
24706
|
+
});
|
|
24707
|
+
}
|
|
24708
|
+
return state;
|
|
24709
|
+
}
|
|
24128
24710
|
function hopRequestHeaders(pageHeaders, cookie) {
|
|
24129
24711
|
const out = { accept: "*/*" };
|
|
24130
24712
|
for (const [name, value] of Object.entries(pageHeaders)) {
|
|
@@ -24454,6 +25036,7 @@ var require_run_write_guard = __commonJS({
|
|
|
24454
25036
|
return existing;
|
|
24455
25037
|
const guard = new RunWriteGuard(policy, opts);
|
|
24456
25038
|
countAppWebSocketMessages(context, guard);
|
|
25039
|
+
const emulated = policy.mode === "block" ? trackOffline(context) : { offline: false };
|
|
24457
25040
|
if (policy.mode === "observe") {
|
|
24458
25041
|
context.on("request", (request) => {
|
|
24459
25042
|
try {
|
|
@@ -24486,6 +25069,11 @@ var require_run_write_guard = __commonJS({
|
|
|
24486
25069
|
}
|
|
24487
25070
|
}
|
|
24488
25071
|
if (decision.action === "allow" && !READ_METHODS.has(request.method().toUpperCase()) && checksRedirect(decision.rule, request.url(), guard.policy)) {
|
|
25072
|
+
if (emulated.offline) {
|
|
25073
|
+
await route.fallback().catch(() => {
|
|
25074
|
+
});
|
|
25075
|
+
return;
|
|
25076
|
+
}
|
|
24489
25077
|
await sendCheckingRedirect(route, guard, context);
|
|
24490
25078
|
return;
|
|
24491
25079
|
}
|