@validate.qa/runner 1.0.102 → 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-E2VEMBQQ.mjs → chunk-6N4QCCEH.mjs} +2 -2
- package/dist/{chunk-ZZYBRIBT.mjs → chunk-EBWZ7NGU.mjs} +11 -8
- package/dist/{chunk-3JM6XTSZ.mjs → chunk-EKY6CN4Y.mjs} +660 -123
- package/dist/{chunk-DN3O6EYT.mjs → chunk-MEE4DXRX.mjs} +19834 -3064
- package/dist/{chunk-CBVHPYVJ.mjs → chunk-OXDU6ARE.mjs} +86 -7
- package/dist/{chunk-AAUHXGDS.mjs → chunk-PUMRAFJS.mjs} +37 -13
- package/dist/{chunk-4WLP3RWN.mjs → chunk-TGVKABFN.mjs} +45 -3
- package/dist/{chunk-5DFBEL72.mjs → chunk-TS4OUJVT.mjs} +1 -1
- package/dist/{chunk-BXLEPT4M.mjs → chunk-UR7KDOTP.mjs} +32 -8
- package/dist/{chunk-E2RRAE27.mjs → chunk-UZ67UBXH.mjs} +14 -3
- package/dist/{chunk-HJ6FKHMM.mjs → chunk-VWE3SQ2Z.mjs} +1 -1
- package/dist/{chunk-PWFWMVZU.mjs → chunk-XXZZMG6Q.mjs} +15 -3
- package/dist/cli.js +35519 -17101
- package/dist/cli.mjs +1171 -251
- package/dist/credential-mcp-server.js +750 -133
- package/dist/credential-mcp-server.mjs +3 -3
- package/dist/discovery-mcp-server.js +714 -132
- package/dist/discovery-mcp-server.mjs +4 -4
- package/dist/dist-BXHLIG5H.mjs +17 -0
- package/dist/inbox-mcp-server.js +743 -129
- package/dist/inbox-mcp-server.mjs +3 -3
- package/dist/route-seed-worker.js +672 -125
- package/dist/route-seed-worker.mjs +3 -3
- package/dist/run-write-guard-preload.js +714 -126
- package/dist/run-write-guard-preload.mjs +4 -4
- package/dist/run-write-guard.js +714 -126
- 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-52M5RDZ6.mjs +0 -17
|
@@ -128,7 +128,13 @@ function buildSandboxedEnv(processEnv, testVariables) {
|
|
|
128
128
|
return env;
|
|
129
129
|
}
|
|
130
130
|
function escapeEnvValue(value) {
|
|
131
|
-
|
|
131
|
+
if (!value.includes("\r")) {
|
|
132
|
+
for (const quote of ["'", "`"]) {
|
|
133
|
+
if (!value.includes(quote))
|
|
134
|
+
return `${quote}${value}${quote}`;
|
|
135
|
+
}
|
|
136
|
+
}
|
|
137
|
+
const escaped = value.replace(/"/g, '\\"').replace(/\n/g, "\\n").replace(/\r/g, "\\r");
|
|
132
138
|
return `"${escaped}"`;
|
|
133
139
|
}
|
|
134
140
|
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;
|
|
@@ -1628,6 +1634,12 @@ function isCancelledReason(error) {
|
|
|
1628
1634
|
function isHeldHealRun(run) {
|
|
1629
1635
|
return run?.status === "PASSED" && typeof run.error === "string" && run.error.startsWith(HEAL_PATCH_HELD_RUN_ERROR_PREFIX);
|
|
1630
1636
|
}
|
|
1637
|
+
function mayBeUnverifiedHealPass(run, tags) {
|
|
1638
|
+
return !!run && run.status === "PASSED" && run.mode === "heal" && !isHeldHealRun(run) && (tags ?? []).includes(NEEDS_REVIEW_TAG);
|
|
1639
|
+
}
|
|
1640
|
+
function isUnverifiedHealPass(run, tags, healConfidence) {
|
|
1641
|
+
return mayBeUnverifiedHealPass(run, tags) && typeof healConfidence === "string" && UNVERIFIED_HEAL_CONFIDENCES.has(healConfidence);
|
|
1642
|
+
}
|
|
1631
1643
|
function isRunInterruptedError(error) {
|
|
1632
1644
|
return typeof error === "string" && RUN_INTERRUPTED_ERROR_PREFIXES.some((prefix) => error.startsWith(prefix));
|
|
1633
1645
|
}
|
|
@@ -1714,12 +1726,25 @@ function classifyErrorMessage(error) {
|
|
|
1714
1726
|
return "config_issue";
|
|
1715
1727
|
if (EMAIL_NOT_RECEIVED_PREFIXES.some((prefix) => normalized.startsWith(prefix)))
|
|
1716
1728
|
return "email_not_received";
|
|
1729
|
+
if (SMS_NOT_RECEIVED_PREFIXES.some((prefix) => normalized.startsWith(prefix)))
|
|
1730
|
+
return "email_not_received";
|
|
1717
1731
|
if (isCancelledReason(error))
|
|
1718
1732
|
return "cancelled";
|
|
1719
1733
|
if (isRetriedRunInterruption(error))
|
|
1720
1734
|
return "interrupted";
|
|
1721
1735
|
return null;
|
|
1722
1736
|
}
|
|
1737
|
+
function isSmsNotReceivedError(error) {
|
|
1738
|
+
if (typeof error !== "string")
|
|
1739
|
+
return false;
|
|
1740
|
+
const normalized = error.replace(/^\s+/, "").replace(/^Error:\s*/i, "");
|
|
1741
|
+
return SMS_NOT_RECEIVED_PREFIXES.some((prefix) => normalized.startsWith(prefix));
|
|
1742
|
+
}
|
|
1743
|
+
function blockedRunLogLine(error, durationMs) {
|
|
1744
|
+
const reason = classifyErrorMessage(error);
|
|
1745
|
+
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";
|
|
1746
|
+
return `BLOCKED (${durationMs}ms) \u2014 ${tail}`;
|
|
1747
|
+
}
|
|
1723
1748
|
function stripEnvironmentalPrefix(error) {
|
|
1724
1749
|
for (const prefix of ENVIRONMENTAL_ERROR_PREFIXES) {
|
|
1725
1750
|
if (error.startsWith(prefix)) {
|
|
@@ -1782,8 +1807,19 @@ function computePassRate(passed, tested) {
|
|
|
1782
1807
|
}
|
|
1783
1808
|
function summarizeHealth(items) {
|
|
1784
1809
|
const c = { passed: 0, failed: 0, errored: 0, environmental: 0, never_run: 0 };
|
|
1810
|
+
let rateLimited = 0;
|
|
1811
|
+
let configIssue = 0;
|
|
1785
1812
|
for (const item of items) {
|
|
1786
|
-
|
|
1813
|
+
const weight = item.weight ?? 1;
|
|
1814
|
+
const bucket = classifyTestHealth(item.run);
|
|
1815
|
+
c[bucket] += weight;
|
|
1816
|
+
if (bucket === "environmental") {
|
|
1817
|
+
const reason = classifyErrorMessage(item.run?.error);
|
|
1818
|
+
if (reason === "rate_limit")
|
|
1819
|
+
rateLimited += weight;
|
|
1820
|
+
else if (isConfigLikeReason(reason))
|
|
1821
|
+
configIssue += weight;
|
|
1822
|
+
}
|
|
1787
1823
|
}
|
|
1788
1824
|
const tested = c.passed + c.failed + c.errored;
|
|
1789
1825
|
return {
|
|
@@ -1791,6 +1827,8 @@ function summarizeHealth(items) {
|
|
|
1791
1827
|
failed: c.failed,
|
|
1792
1828
|
errored: c.errored,
|
|
1793
1829
|
environmental: c.environmental,
|
|
1830
|
+
rateLimited,
|
|
1831
|
+
configIssue,
|
|
1794
1832
|
neverRun: c.never_run,
|
|
1795
1833
|
tested,
|
|
1796
1834
|
total: tested + c.environmental + c.never_run,
|
|
@@ -1827,7 +1865,7 @@ function reportedBugWhere() {
|
|
|
1827
1865
|
function bugReviewStatus(status) {
|
|
1828
1866
|
return status === "QUARANTINED" ? "confirmed" : "needs-review";
|
|
1829
1867
|
}
|
|
1830
|
-
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;
|
|
1868
|
+
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;
|
|
1831
1869
|
var init_run_classification = __esm({
|
|
1832
1870
|
"../shared/dist/run-classification.js"() {
|
|
1833
1871
|
"use strict";
|
|
@@ -1847,6 +1885,8 @@ var init_run_classification = __esm({
|
|
|
1847
1885
|
];
|
|
1848
1886
|
isUserCancelledError = isCancelledReason;
|
|
1849
1887
|
HEAL_PATCH_HELD_RUN_ERROR_PREFIX = "Heal patch held";
|
|
1888
|
+
UNVERIFIED_HEAL_CONFIDENCES = /* @__PURE__ */ new Set(["low", "medium"]);
|
|
1889
|
+
NEEDS_REVIEW_TAG = "@needs-review";
|
|
1850
1890
|
RUNNER_INTERRUPT_ERROR_PREFIX = "Runner shutting down";
|
|
1851
1891
|
SERVER_INTERRUPT_ERROR_PREFIX = "Run interrupted by server restart";
|
|
1852
1892
|
RUN_LOST_ERROR_PREFIX = "Runner lost this run";
|
|
@@ -1867,10 +1907,12 @@ var init_run_classification = __esm({
|
|
|
1867
1907
|
"VERIFICATION_EMAIL_HAS_NO_CODE:",
|
|
1868
1908
|
"VERIFICATION_EMAIL_HAS_NO_LINK:"
|
|
1869
1909
|
];
|
|
1910
|
+
SMS_NOT_RECEIVED_PREFIXES = ["VERIFICATION_SMS_NOT_RECEIVED:"];
|
|
1870
1911
|
ENVIRONMENTAL_ERROR_PREFIXES = [
|
|
1871
1912
|
"RATE_LIMIT_BLOCKED:",
|
|
1872
1913
|
"CONFIG_ISSUE:",
|
|
1873
|
-
...EMAIL_NOT_RECEIVED_PREFIXES
|
|
1914
|
+
...EMAIL_NOT_RECEIVED_PREFIXES,
|
|
1915
|
+
...SMS_NOT_RECEIVED_PREFIXES
|
|
1874
1916
|
];
|
|
1875
1917
|
ENVIRONMENTAL_ERROR_STORED_PREFIXES = [
|
|
1876
1918
|
...ENVIRONMENTAL_ERROR_PREFIXES.flatMap((prefix) => [prefix, `Error: ${prefix}`]),
|
|
@@ -4366,6 +4408,65 @@ var init_audit_grouping = __esm({
|
|
|
4366
4408
|
}
|
|
4367
4409
|
});
|
|
4368
4410
|
|
|
4411
|
+
// ../shared/dist/regex-literal.js
|
|
4412
|
+
function opensRegexLiteral(code, i) {
|
|
4413
|
+
let j = i - 1;
|
|
4414
|
+
while (j >= 0 && (code[j] === " " || code[j] === " "))
|
|
4415
|
+
j--;
|
|
4416
|
+
if (j < 0 || code[j] === "\n" || code[j] === "\r")
|
|
4417
|
+
return true;
|
|
4418
|
+
return regexOpensAfter(code, j);
|
|
4419
|
+
}
|
|
4420
|
+
function regexOpensAfter(code, j) {
|
|
4421
|
+
const prev = code[j];
|
|
4422
|
+
if (REGEX_OPENERS.includes(prev))
|
|
4423
|
+
return !((prev === "+" || prev === "-") && code[j - 1] === prev);
|
|
4424
|
+
if (!/[a-z]/.test(prev))
|
|
4425
|
+
return false;
|
|
4426
|
+
let start = j;
|
|
4427
|
+
while (start > 0 && /[A-Za-z0-9_$]/.test(code[start - 1]))
|
|
4428
|
+
start--;
|
|
4429
|
+
if (start > 0 && code[start - 1] === ".")
|
|
4430
|
+
return false;
|
|
4431
|
+
return REGEX_KEYWORDS.has(code.slice(start, j + 1));
|
|
4432
|
+
}
|
|
4433
|
+
function regexLiteralEnd(code, i) {
|
|
4434
|
+
let k = i + 1;
|
|
4435
|
+
let inClass = false;
|
|
4436
|
+
while (k < code.length) {
|
|
4437
|
+
const ch = code[k];
|
|
4438
|
+
if (ch === "\n" || ch === "\r")
|
|
4439
|
+
return k;
|
|
4440
|
+
if (ch === "\\") {
|
|
4441
|
+
if (code[k + 1] === "\n" || code[k + 1] === "\r")
|
|
4442
|
+
return k + 1;
|
|
4443
|
+
k += 2;
|
|
4444
|
+
continue;
|
|
4445
|
+
}
|
|
4446
|
+
if (inClass) {
|
|
4447
|
+
if (ch === "]")
|
|
4448
|
+
inClass = false;
|
|
4449
|
+
} else if (ch === "[") {
|
|
4450
|
+
inClass = true;
|
|
4451
|
+
} else if (ch === "/") {
|
|
4452
|
+
k++;
|
|
4453
|
+
while (k < code.length && /[a-z]/i.test(code[k]))
|
|
4454
|
+
k++;
|
|
4455
|
+
return k;
|
|
4456
|
+
}
|
|
4457
|
+
k++;
|
|
4458
|
+
}
|
|
4459
|
+
return code.length;
|
|
4460
|
+
}
|
|
4461
|
+
var REGEX_OPENERS, REGEX_KEYWORDS;
|
|
4462
|
+
var init_regex_literal = __esm({
|
|
4463
|
+
"../shared/dist/regex-literal.js"() {
|
|
4464
|
+
"use strict";
|
|
4465
|
+
REGEX_OPENERS = "(,=:[!&|?{};+-*%~^<>";
|
|
4466
|
+
REGEX_KEYWORDS = /* @__PURE__ */ new Set(["return", "typeof", "case", "do", "else", "in", "of", "new", "delete", "void", "throw", "await", "yield", "instanceof"]);
|
|
4467
|
+
}
|
|
4468
|
+
});
|
|
4469
|
+
|
|
4369
4470
|
// ../shared/dist/assertion-classifier.js
|
|
4370
4471
|
function isButtonOrLinkLocator(line) {
|
|
4371
4472
|
return /getByRole\s*\(\s*['"`](?:button|link)['"`]/i.test(line);
|
|
@@ -4674,7 +4775,7 @@ function classifyMobileAssertValue(value, context = {}) {
|
|
|
4674
4775
|
return { tier: 2, reason: t2[1] };
|
|
4675
4776
|
return null;
|
|
4676
4777
|
}
|
|
4677
|
-
function
|
|
4778
|
+
function regexLiteralEnd2(text, start) {
|
|
4678
4779
|
let inClass = false;
|
|
4679
4780
|
for (let i = start + 1; i < text.length; i++) {
|
|
4680
4781
|
const ch = text[i];
|
|
@@ -4700,7 +4801,7 @@ function parenDepthDelta(text) {
|
|
|
4700
4801
|
let depth = 0;
|
|
4701
4802
|
let quote = null;
|
|
4702
4803
|
let escaped = false;
|
|
4703
|
-
let
|
|
4804
|
+
let prevAt = -1;
|
|
4704
4805
|
let noRegexUntil = -1;
|
|
4705
4806
|
for (let i = 0; i < text.length; i++) {
|
|
4706
4807
|
const ch = text[i];
|
|
@@ -4715,7 +4816,7 @@ function parenDepthDelta(text) {
|
|
|
4715
4816
|
if (quote) {
|
|
4716
4817
|
if (ch === quote) {
|
|
4717
4818
|
quote = null;
|
|
4718
|
-
|
|
4819
|
+
prevAt = i;
|
|
4719
4820
|
}
|
|
4720
4821
|
continue;
|
|
4721
4822
|
}
|
|
@@ -4734,11 +4835,11 @@ function parenDepthDelta(text) {
|
|
|
4734
4835
|
i = close + 1;
|
|
4735
4836
|
continue;
|
|
4736
4837
|
}
|
|
4737
|
-
if (i > noRegexUntil && (
|
|
4738
|
-
const end =
|
|
4838
|
+
if (i > noRegexUntil && (prevAt < 0 || regexOpensAfter(text, prevAt))) {
|
|
4839
|
+
const end = regexLiteralEnd2(text, i);
|
|
4739
4840
|
if (end > 0) {
|
|
4740
4841
|
i = end - 1;
|
|
4741
|
-
|
|
4842
|
+
prevAt = end - 1;
|
|
4742
4843
|
continue;
|
|
4743
4844
|
}
|
|
4744
4845
|
const nl = text.indexOf("\n", i);
|
|
@@ -4750,14 +4851,15 @@ function parenDepthDelta(text) {
|
|
|
4750
4851
|
else if (ch === ")")
|
|
4751
4852
|
depth--;
|
|
4752
4853
|
if (ch !== " " && ch !== " " && ch !== "\n")
|
|
4753
|
-
|
|
4854
|
+
prevAt = i;
|
|
4754
4855
|
}
|
|
4755
4856
|
return depth;
|
|
4756
4857
|
}
|
|
4757
|
-
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
|
|
4858
|
+
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;
|
|
4758
4859
|
var init_assertion_classifier = __esm({
|
|
4759
4860
|
"../shared/dist/assertion-classifier.js"() {
|
|
4760
4861
|
"use strict";
|
|
4862
|
+
init_regex_literal();
|
|
4761
4863
|
PENDING_STATE_TEXT_REASON = 'pending-state text ("Sending...") is transient';
|
|
4762
4864
|
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}|…)/;
|
|
4763
4865
|
TIER1_PATTERNS = [
|
|
@@ -4834,7 +4936,6 @@ var init_assertion_classifier = __esm({
|
|
|
4834
4936
|
new RegExp(`\\.filter\\s*\\(\\s*\\{[^}]{0,200}?\\bhasText\\s*:\\s*${LOCATOR_ARG_VALUE}`, "gi")
|
|
4835
4937
|
];
|
|
4836
4938
|
EXPECT_TOKEN = /\bexpect\s*[.(]/;
|
|
4837
|
-
REGEX_PRECEDER = /* @__PURE__ */ new Set(["(", ",", "=", ":", "[", "!", "&", "|", "?", "{", "}", ";", "+", "-", "*", "%", "<", ">", "~", "^"]);
|
|
4838
4939
|
}
|
|
4839
4940
|
});
|
|
4840
4941
|
|
|
@@ -4842,14 +4943,80 @@ var init_assertion_classifier = __esm({
|
|
|
4842
4943
|
function isExpectCall(code) {
|
|
4843
4944
|
return EXPECT_CALL_RE.test(code);
|
|
4844
4945
|
}
|
|
4946
|
+
function specCallsExpect(code) {
|
|
4947
|
+
if (EXPECT_CALL_RE.test(code) || SPACED_EXPECT_CALL_RE.test(code) || CONFIGURE_IN_PLACE_RE.test(code))
|
|
4948
|
+
return true;
|
|
4949
|
+
const names = /* @__PURE__ */ new Set();
|
|
4950
|
+
for (const clause of code.matchAll(IMPORT_CLAUSE_RE)) {
|
|
4951
|
+
for (const alias of clause[1].matchAll(EXPECT_ALIAS_RE))
|
|
4952
|
+
names.add(alias[1]);
|
|
4953
|
+
}
|
|
4954
|
+
for (const decl of code.matchAll(DERIVED_EXPECT_DECL_RE)) {
|
|
4955
|
+
if (names.size >= MAX_DERIVED_EXPECT_NAMES)
|
|
4956
|
+
break;
|
|
4957
|
+
const base = decl[2];
|
|
4958
|
+
if (base === void 0 || base === "expect" || names.has(base))
|
|
4959
|
+
names.add(decl[1]);
|
|
4960
|
+
}
|
|
4961
|
+
names.delete("expect");
|
|
4962
|
+
if (names.size === 0)
|
|
4963
|
+
return false;
|
|
4964
|
+
const alts = [...names].map((n) => n.replace(/\$/g, "\\$")).join("|");
|
|
4965
|
+
return new RegExp(String.raw`(?<![\w$.])(?:${alts})${EXPECT_MODIFIERS}`).test(code);
|
|
4966
|
+
}
|
|
4845
4967
|
function asPlainExpect(code) {
|
|
4846
4968
|
return code.replace(/\bexpect\.soft(\s*\()/g, "expect$1");
|
|
4847
4969
|
}
|
|
4848
|
-
|
|
4970
|
+
function expectAliasNames(code) {
|
|
4971
|
+
if (!MAY_ALIAS.test(code))
|
|
4972
|
+
return [];
|
|
4973
|
+
const imported = [];
|
|
4974
|
+
for (const m of code.matchAll(PW_IMPORT)) {
|
|
4975
|
+
for (const a of m[1].matchAll(IMPORT_AS_EXPECT))
|
|
4976
|
+
imported.push(a[1]);
|
|
4977
|
+
}
|
|
4978
|
+
const known = /* @__PURE__ */ new Set(["expect", ...imported]);
|
|
4979
|
+
const derived = [];
|
|
4980
|
+
for (const m of code.matchAll(DECLARATION)) {
|
|
4981
|
+
const rhs = DERIVED.exec(code.slice(m.index + m[0].length, m.index + m[0].length + 200));
|
|
4982
|
+
if (!rhs)
|
|
4983
|
+
continue;
|
|
4984
|
+
const base = rhs[1];
|
|
4985
|
+
const fromKnown = base === void 0 || (rhs[2] ? base === "test" : known.has(base));
|
|
4986
|
+
if (!fromKnown || m[1] === "expect")
|
|
4987
|
+
continue;
|
|
4988
|
+
known.add(m[1]);
|
|
4989
|
+
derived.push(m[1]);
|
|
4990
|
+
}
|
|
4991
|
+
const fromImports = LOCAL_EXPECT.test(code) ? [] : imported;
|
|
4992
|
+
return [.../* @__PURE__ */ new Set([...fromImports, ...derived])].filter((n) => n !== "expect");
|
|
4993
|
+
}
|
|
4994
|
+
function withExpectAliasesAsExpect(code) {
|
|
4995
|
+
let out = code;
|
|
4996
|
+
for (const name of expectAliasNames(code)) {
|
|
4997
|
+
const escaped = name.replace(/\$/g, "\\$");
|
|
4998
|
+
out = out.replace(new RegExp(`(?<![\\w$.])${escaped}(?=\\s{0,20}(?:\\.\\s{0,20}(?:soft|poll)\\s{0,20})?\\()`, "g"), "expect");
|
|
4999
|
+
}
|
|
5000
|
+
return out;
|
|
5001
|
+
}
|
|
5002
|
+
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;
|
|
4849
5003
|
var init_expect_call = __esm({
|
|
4850
5004
|
"../shared/dist/expect-call.js"() {
|
|
4851
5005
|
"use strict";
|
|
4852
5006
|
EXPECT_CALL_RE = /\bexpect(?:\.(?:soft|poll))?\s*\(/;
|
|
5007
|
+
EXPECT_MODIFIERS = String.raw`\s*(?:\.\s*soft\s*)?(?:\.\s*poll\s*)?\(`;
|
|
5008
|
+
SPACED_EXPECT_CALL_RE = new RegExp(String.raw`\bexpect${EXPECT_MODIFIERS}`);
|
|
5009
|
+
CONFIGURE_IN_PLACE_RE = new RegExp(String.raw`\bexpect\s*\.\s*configure\s*\(\s*(?:\{[^{}()]{0,500}\}\s*)?\)${EXPECT_MODIFIERS}`);
|
|
5010
|
+
IMPORT_CLAUSE_RE = /\bimport\s+(?:type\s+)?\{([^{}]{0,2000})\}/g;
|
|
5011
|
+
EXPECT_ALIAS_RE = /\bexpect\s+as\s+([A-Za-z_$][\w$]*)/g;
|
|
5012
|
+
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;
|
|
5013
|
+
MAX_DERIVED_EXPECT_NAMES = 20;
|
|
5014
|
+
PW_IMPORT = /\bimport\s{0,20}\{([^}]{0,4000})\}\s{0,20}from\s{0,20}['"]@playwright\/test['"]/g;
|
|
5015
|
+
IMPORT_AS_EXPECT = /(?:^|,)\s{0,20}expect\s{1,20}as\s{1,20}([A-Za-z_$][\w$]{0,99})\s{0,20}(?=,|$)/g;
|
|
5016
|
+
DECLARATION = /\b(?:const|let|var)\s{1,20}([A-Za-z_$][\w$]{0,99})\s{0,20}(?::[^=;\n]{0,200})?=\s{0,20}/g;
|
|
5017
|
+
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}[.(]))/;
|
|
5018
|
+
LOCAL_EXPECT = /\b(?:const|let|var)\s{1,20}expect\b/;
|
|
5019
|
+
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/;
|
|
4853
5020
|
}
|
|
4854
5021
|
});
|
|
4855
5022
|
|
|
@@ -4879,65 +5046,6 @@ var init_braced_clause = __esm({
|
|
|
4879
5046
|
}
|
|
4880
5047
|
});
|
|
4881
5048
|
|
|
4882
|
-
// ../shared/dist/regex-literal.js
|
|
4883
|
-
function opensRegexLiteral(code, i) {
|
|
4884
|
-
let j = i - 1;
|
|
4885
|
-
while (j >= 0 && (code[j] === " " || code[j] === " "))
|
|
4886
|
-
j--;
|
|
4887
|
-
if (j < 0 || code[j] === "\n" || code[j] === "\r")
|
|
4888
|
-
return true;
|
|
4889
|
-
return regexOpensAfter(code, j);
|
|
4890
|
-
}
|
|
4891
|
-
function regexOpensAfter(code, j) {
|
|
4892
|
-
const prev = code[j];
|
|
4893
|
-
if (REGEX_OPENERS.includes(prev))
|
|
4894
|
-
return !((prev === "+" || prev === "-") && code[j - 1] === prev);
|
|
4895
|
-
if (!/[a-z]/.test(prev))
|
|
4896
|
-
return false;
|
|
4897
|
-
let start = j;
|
|
4898
|
-
while (start > 0 && /[A-Za-z0-9_$]/.test(code[start - 1]))
|
|
4899
|
-
start--;
|
|
4900
|
-
if (start > 0 && code[start - 1] === ".")
|
|
4901
|
-
return false;
|
|
4902
|
-
return REGEX_KEYWORDS.has(code.slice(start, j + 1));
|
|
4903
|
-
}
|
|
4904
|
-
function regexLiteralEnd2(code, i) {
|
|
4905
|
-
let k = i + 1;
|
|
4906
|
-
let inClass = false;
|
|
4907
|
-
while (k < code.length) {
|
|
4908
|
-
const ch = code[k];
|
|
4909
|
-
if (ch === "\n" || ch === "\r")
|
|
4910
|
-
return k;
|
|
4911
|
-
if (ch === "\\") {
|
|
4912
|
-
if (code[k + 1] === "\n" || code[k + 1] === "\r")
|
|
4913
|
-
return k + 1;
|
|
4914
|
-
k += 2;
|
|
4915
|
-
continue;
|
|
4916
|
-
}
|
|
4917
|
-
if (inClass) {
|
|
4918
|
-
if (ch === "]")
|
|
4919
|
-
inClass = false;
|
|
4920
|
-
} else if (ch === "[") {
|
|
4921
|
-
inClass = true;
|
|
4922
|
-
} else if (ch === "/") {
|
|
4923
|
-
k++;
|
|
4924
|
-
while (k < code.length && /[a-z]/i.test(code[k]))
|
|
4925
|
-
k++;
|
|
4926
|
-
return k;
|
|
4927
|
-
}
|
|
4928
|
-
k++;
|
|
4929
|
-
}
|
|
4930
|
-
return code.length;
|
|
4931
|
-
}
|
|
4932
|
-
var REGEX_OPENERS, REGEX_KEYWORDS;
|
|
4933
|
-
var init_regex_literal = __esm({
|
|
4934
|
-
"../shared/dist/regex-literal.js"() {
|
|
4935
|
-
"use strict";
|
|
4936
|
-
REGEX_OPENERS = "(,=:[!&|?{};+-*%~^<>";
|
|
4937
|
-
REGEX_KEYWORDS = /* @__PURE__ */ new Set(["return", "typeof", "case", "do", "else", "in", "of", "new", "delete", "void", "throw", "await", "yield", "instanceof"]);
|
|
4938
|
-
}
|
|
4939
|
-
});
|
|
4940
|
-
|
|
4941
5049
|
// ../shared/dist/assertion-proof.js
|
|
4942
5050
|
function fragmentKindOf(literal, fromText) {
|
|
4943
5051
|
const text = literal.trim();
|
|
@@ -5096,7 +5204,7 @@ function stripCoverageComments(code) {
|
|
|
5096
5204
|
let copyFrom = 0;
|
|
5097
5205
|
let i = 0;
|
|
5098
5206
|
let quote = null;
|
|
5099
|
-
let
|
|
5207
|
+
let prevAt = -1;
|
|
5100
5208
|
let noRegexBefore = 0;
|
|
5101
5209
|
while (i < code.length) {
|
|
5102
5210
|
const ch = code[i];
|
|
@@ -5107,17 +5215,18 @@ function stripCoverageComments(code) {
|
|
|
5107
5215
|
}
|
|
5108
5216
|
if (ch === quote || ch === "\n" && quote !== "`") {
|
|
5109
5217
|
quote = null;
|
|
5110
|
-
|
|
5218
|
+
if (ch !== "\n")
|
|
5219
|
+
prevAt = i;
|
|
5111
5220
|
}
|
|
5112
5221
|
i++;
|
|
5113
5222
|
continue;
|
|
5114
5223
|
}
|
|
5115
5224
|
const next = code[i + 1];
|
|
5116
|
-
if (ch === "/" && next !== "*" && next !== "/" && i >= noRegexBefore && (
|
|
5225
|
+
if (ch === "/" && next !== "*" && next !== "/" && i >= noRegexBefore && (prevAt < 0 || regexOpensAfter(code, prevAt))) {
|
|
5117
5226
|
const end = regexLiteralEnd3(code, i);
|
|
5118
5227
|
if (end > 0) {
|
|
5119
5228
|
i = end;
|
|
5120
|
-
|
|
5229
|
+
prevAt = end - 1;
|
|
5121
5230
|
continue;
|
|
5122
5231
|
}
|
|
5123
5232
|
const eol = code.indexOf("\n", i);
|
|
@@ -5142,7 +5251,7 @@ function stripCoverageComments(code) {
|
|
|
5142
5251
|
if (ch === '"' || ch === "'" || ch === "`")
|
|
5143
5252
|
quote = ch;
|
|
5144
5253
|
if (ch !== " " && ch !== " " && ch !== "\n" && ch !== "\r")
|
|
5145
|
-
|
|
5254
|
+
prevAt = i;
|
|
5146
5255
|
i++;
|
|
5147
5256
|
}
|
|
5148
5257
|
out.push(code.slice(copyFrom));
|
|
@@ -5831,11 +5940,12 @@ function assessTitleCoverage(title, code) {
|
|
|
5831
5940
|
const repeatedHit = [...repeated].some((term) => hay.includes(term));
|
|
5832
5941
|
return { covered: hit >= needed || repeatedHit, missingTerms };
|
|
5833
5942
|
}
|
|
5834
|
-
var COVERAGE_STOP_WORDS, MAX_ASSERTION_JOIN_CHARS, MAX_COVERAGE_LINE_CHARS,
|
|
5943
|
+
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;
|
|
5835
5944
|
var init_expected_outcome_coverage = __esm({
|
|
5836
5945
|
"../shared/dist/expected-outcome-coverage.js"() {
|
|
5837
5946
|
"use strict";
|
|
5838
5947
|
init_assertion_classifier();
|
|
5948
|
+
init_regex_literal();
|
|
5839
5949
|
COVERAGE_STOP_WORDS = /* @__PURE__ */ new Set([
|
|
5840
5950
|
"after",
|
|
5841
5951
|
"before",
|
|
@@ -5868,7 +5978,6 @@ var init_expected_outcome_coverage = __esm({
|
|
|
5868
5978
|
]);
|
|
5869
5979
|
MAX_ASSERTION_JOIN_CHARS = 8e3;
|
|
5870
5980
|
MAX_COVERAGE_LINE_CHARS = 2e3;
|
|
5871
|
-
REGEX_PRECEDER2 = "(,=:[!&|?{};+-*%<>~^";
|
|
5872
5981
|
BLOCK_SCAFFOLD = /=>|\b(?:test|describe|it|beforeEach|afterEach|beforeAll|afterAll|function)\b/;
|
|
5873
5982
|
cachedAssertionInput = null;
|
|
5874
5983
|
cachedAssertionSource = { raw: "", normalized: "", bindings: /* @__PURE__ */ new Map(), bindingRefs: /* @__PURE__ */ new Map() };
|
|
@@ -5964,7 +6073,7 @@ function replaceBlockComments(code, replacer) {
|
|
|
5964
6073
|
continue;
|
|
5965
6074
|
const next = code[i + 1];
|
|
5966
6075
|
if (next !== "/" && next !== "*" && opensRegexLiteral(code, i)) {
|
|
5967
|
-
i =
|
|
6076
|
+
i = regexLiteralEnd(code, i) - 1;
|
|
5968
6077
|
continue;
|
|
5969
6078
|
}
|
|
5970
6079
|
if (next === "/" && !(code[i - 1] === ":" && /[A-Za-z]/.test(code[i - 2] ?? ""))) {
|
|
@@ -6139,10 +6248,41 @@ var init_auth_handoff_coverage = __esm({
|
|
|
6139
6248
|
});
|
|
6140
6249
|
|
|
6141
6250
|
// ../shared/dist/auth-plan.js
|
|
6142
|
-
function
|
|
6251
|
+
function stripStampedHelperRegions(code) {
|
|
6252
|
+
let out = code;
|
|
6253
|
+
for (const [open, close] of STAMPED_HELPER_REGIONS) {
|
|
6254
|
+
if (out.indexOf(open) === -1)
|
|
6255
|
+
continue;
|
|
6256
|
+
let kept = "";
|
|
6257
|
+
let i = 0;
|
|
6258
|
+
while (i < out.length) {
|
|
6259
|
+
const start = out.indexOf(open, i);
|
|
6260
|
+
if (start === -1) {
|
|
6261
|
+
kept += out.slice(i);
|
|
6262
|
+
break;
|
|
6263
|
+
}
|
|
6264
|
+
const end = out.indexOf(close, start + open.length);
|
|
6265
|
+
if (end === -1) {
|
|
6266
|
+
kept += out.slice(i);
|
|
6267
|
+
break;
|
|
6268
|
+
}
|
|
6269
|
+
const lineEnd = out.indexOf("\n", end);
|
|
6270
|
+
kept += out.slice(i, start);
|
|
6271
|
+
i = lineEnd === -1 ? out.length : lineEnd + 1;
|
|
6272
|
+
}
|
|
6273
|
+
out = kept;
|
|
6274
|
+
}
|
|
6275
|
+
return out;
|
|
6276
|
+
}
|
|
6277
|
+
function signsInInline(playwrightCode) {
|
|
6143
6278
|
if (!playwrightCode)
|
|
6144
6279
|
return false;
|
|
6145
|
-
|
|
6280
|
+
if (!/process\.env\.TEST_USER_EMAIL!?/.test(playwrightCode))
|
|
6281
|
+
return false;
|
|
6282
|
+
if (/process\.env\.TEST_USER_PASSWORD!?/.test(playwrightCode))
|
|
6283
|
+
return true;
|
|
6284
|
+
const body = stripStampedHelperRegions(playwrightCode);
|
|
6285
|
+
return /process\.env\.TEST_USER_EMAIL/.test(body) && /__vqaWaitForEmail\s*\(/.test(body);
|
|
6146
6286
|
}
|
|
6147
6287
|
function hasGeneratedAuthGuard(playwrightCode) {
|
|
6148
6288
|
if (!playwrightCode)
|
|
@@ -6161,7 +6301,7 @@ function legacyNeedsCookieState(tags, code, sharedAuthInjectEnabled) {
|
|
|
6161
6301
|
if (sharedAuthInjectEnabled && tags.includes("@auth-guarded") && hasGeneratedAuthGuard(code))
|
|
6162
6302
|
return true;
|
|
6163
6303
|
const isGeneratedVariant = tags.includes("@edge") || tags.includes("@bug");
|
|
6164
|
-
const hasLegacyInlineLogin = tags.includes("@discovered") && !isGeneratedVariant &&
|
|
6304
|
+
const hasLegacyInlineLogin = tags.includes("@discovered") && !isGeneratedVariant && signsInInline(code);
|
|
6165
6305
|
return !tags.includes("@inline-login") && !hasLegacyInlineLogin;
|
|
6166
6306
|
}
|
|
6167
6307
|
function codeReadsStoredCredentials(code) {
|
|
@@ -6282,7 +6422,7 @@ function authTypeTag(tags) {
|
|
|
6282
6422
|
const explicit = explicitAuthTypeTags(t);
|
|
6283
6423
|
return explicit.length === 1 ? explicit[0] : void 0;
|
|
6284
6424
|
}
|
|
6285
|
-
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;
|
|
6425
|
+
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;
|
|
6286
6426
|
var init_auth_plan = __esm({
|
|
6287
6427
|
"../shared/dist/auth-plan.js"() {
|
|
6288
6428
|
"use strict";
|
|
@@ -6309,6 +6449,10 @@ var init_auth_plan = __esm({
|
|
|
6309
6449
|
};
|
|
6310
6450
|
TEST_USER_ENV_KEYS = ["TEST_USER_EMAIL", "TEST_USER_PASSWORD"];
|
|
6311
6451
|
API_AUTH_HEADERS_ENV_KEY = "API_AUTH_HEADERS";
|
|
6452
|
+
STAMPED_HELPER_REGIONS = [
|
|
6453
|
+
["// >>> VALIDATE_QA_INBOX", "// <<< VALIDATE_QA_INBOX"],
|
|
6454
|
+
["// >>> VALIDATE_QA_SMS", "// <<< VALIDATE_QA_SMS"]
|
|
6455
|
+
];
|
|
6312
6456
|
}
|
|
6313
6457
|
});
|
|
6314
6458
|
|
|
@@ -6317,6 +6461,9 @@ function parseLastActionProofMode(raw) {
|
|
|
6317
6461
|
const v = (raw ?? "").trim().toLowerCase();
|
|
6318
6462
|
return v === "on" || v === "shadow" ? v : "off";
|
|
6319
6463
|
}
|
|
6464
|
+
function parseAbsenceProofMode(raw) {
|
|
6465
|
+
return parseLastActionProofMode(raw);
|
|
6466
|
+
}
|
|
6320
6467
|
function isUrlProof(stmt) {
|
|
6321
6468
|
if (URL_PROOF.test(stmt))
|
|
6322
6469
|
return true;
|
|
@@ -6704,6 +6851,65 @@ function isVacuousAccountAbsence(stmt, shown, account) {
|
|
|
6704
6851
|
}
|
|
6705
6852
|
return true;
|
|
6706
6853
|
}
|
|
6854
|
+
function isAbsenceCheck(stmt) {
|
|
6855
|
+
if (!claimsAbsence(stmt))
|
|
6856
|
+
return false;
|
|
6857
|
+
const bare = stmt.replace(NOT_BEFORE_MATCHER, ".");
|
|
6858
|
+
return ABSENCE_READ.test(bare) || VALUE_ABSENCE.test(bare) || OPTION_ABSENCE.test(bare);
|
|
6859
|
+
}
|
|
6860
|
+
function comparableSubject(subject) {
|
|
6861
|
+
let s = subject.replace(/^\s*await\s+/, "").replace(/\s+/g, "");
|
|
6862
|
+
for (let prev = ""; prev !== s; ) {
|
|
6863
|
+
prev = s;
|
|
6864
|
+
s = s.replace(/\.(?:count|isVisible|isHidden|first|last)\(\)$/, "").replace(/\.nth\(\d+\)$/, "");
|
|
6865
|
+
}
|
|
6866
|
+
return s;
|
|
6867
|
+
}
|
|
6868
|
+
function subjectsThatExisted(checks, interactionStmts) {
|
|
6869
|
+
const out = [];
|
|
6870
|
+
const bound = locatorBindings(interactionStmts);
|
|
6871
|
+
const resolve = (expr) => {
|
|
6872
|
+
for (let depth = 0; depth < 4; depth++) {
|
|
6873
|
+
const lead = expr.match(/^\s*(?:await\s+)?([A-Za-z_$][\w$]*)(?![\w$(])/);
|
|
6874
|
+
const to = lead ? bound.get(lead[1]) : void 0;
|
|
6875
|
+
if (!lead || to === void 0)
|
|
6876
|
+
break;
|
|
6877
|
+
expr = `${to.replace(/;\s*$/, "")}${expr.slice(lead[0].length)}`;
|
|
6878
|
+
}
|
|
6879
|
+
return expr;
|
|
6880
|
+
};
|
|
6881
|
+
for (const { stmt } of checks) {
|
|
6882
|
+
if (isAbsenceCheck(stmt))
|
|
6883
|
+
continue;
|
|
6884
|
+
const subject = extractExpectSubject(stmt);
|
|
6885
|
+
if (subject)
|
|
6886
|
+
out.push(comparableSubject(subject));
|
|
6887
|
+
}
|
|
6888
|
+
for (const stmt of interactionStmts) {
|
|
6889
|
+
if (EXPECT_CALL_RE.test(stmt))
|
|
6890
|
+
continue;
|
|
6891
|
+
for (const m of stmt.matchAll(LOCATOR_USE)) {
|
|
6892
|
+
if (m[0].includes("waitFor") && /state\s*:\s*['"`](?:hidden|detached)/.test(stmt.slice(m.index)))
|
|
6893
|
+
continue;
|
|
6894
|
+
const before = stmt.slice(0, m.index).replace(/^\s*(?:(?:const|let|var)\s+[\w$]+\s*=|return\b)\s*/, "");
|
|
6895
|
+
if (before.trim())
|
|
6896
|
+
out.push(comparableSubject(resolve(before)));
|
|
6897
|
+
}
|
|
6898
|
+
}
|
|
6899
|
+
return out;
|
|
6900
|
+
}
|
|
6901
|
+
function isUnshownAbsence(stmt, existed, entered) {
|
|
6902
|
+
if (!isAbsenceCheck(stmt))
|
|
6903
|
+
return false;
|
|
6904
|
+
const raw = extractExpectSubject(stmt);
|
|
6905
|
+
if (!raw || !/\b(?:locator|getBy[A-Z]\w*|frameLocator)\s*\(/.test(raw))
|
|
6906
|
+
return false;
|
|
6907
|
+
const subject = comparableSubject(raw);
|
|
6908
|
+
if (existed.some((e) => e === subject || e.startsWith(`${subject}.`)))
|
|
6909
|
+
return false;
|
|
6910
|
+
const named = raw.match(/['"`][^'"`]+['"`]/g) ?? [];
|
|
6911
|
+
return !named.some((q) => entered.values.has(q.slice(1, -1).toLowerCase()));
|
|
6912
|
+
}
|
|
6707
6913
|
function endOfEnclosingCall(stmt, from) {
|
|
6708
6914
|
let depth = 0;
|
|
6709
6915
|
for (let i = from; i < stmt.length; i++) {
|
|
@@ -7203,10 +7409,31 @@ function comparesWithItself(subject, tail) {
|
|
|
7203
7409
|
const expected = squash2(args);
|
|
7204
7410
|
return expected === squash2(m[1] === "toHaveCount" ? `${subject}.count()` : subject);
|
|
7205
7411
|
}
|
|
7412
|
+
function ariaSnapshotTexts(template) {
|
|
7413
|
+
const out = [];
|
|
7414
|
+
for (const raw of template.split(ARIA_ENTRY_SPLIT)) {
|
|
7415
|
+
const m = ARIA_ENTRY.exec(raw.trim());
|
|
7416
|
+
if (!m || !ARIA_CONTENT_ROLES.has(m[1]))
|
|
7417
|
+
continue;
|
|
7418
|
+
for (const text of [m[2], m[3]]) {
|
|
7419
|
+
const t = text?.replace(/\\(.)/g, "$1").replace(/^["']|["']$/g, "").trim();
|
|
7420
|
+
if (!t || t.startsWith("/") || !/[\p{L}\p{N}]/u.test(t) || /["'`\\]/.test(t))
|
|
7421
|
+
continue;
|
|
7422
|
+
out.push(t);
|
|
7423
|
+
}
|
|
7424
|
+
}
|
|
7425
|
+
return out;
|
|
7426
|
+
}
|
|
7427
|
+
function presenceTwin(stmt) {
|
|
7428
|
+
return stmt.replace(NOT_HIDDEN, ".toBeVisible(").replace(NOT_COUNT_ZERO, ".toBeVisible(");
|
|
7429
|
+
}
|
|
7206
7430
|
function asPlainAssertion(s) {
|
|
7207
7431
|
return plainAssertion(s, false);
|
|
7208
7432
|
}
|
|
7209
7433
|
function plainAssertion(s, keepSelfCompare) {
|
|
7434
|
+
const twin = presenceTwin(s.stmt);
|
|
7435
|
+
if (twin !== s.stmt)
|
|
7436
|
+
return plainAssertion({ ...s, stmt: twin }, keepSelfCompare);
|
|
7210
7437
|
const { stmt } = s;
|
|
7211
7438
|
if (COUNT_AT_LEAST_ZERO.test(stmt))
|
|
7212
7439
|
return [];
|
|
@@ -7217,6 +7444,12 @@ function plainAssertion(s, keepSelfCompare) {
|
|
|
7217
7444
|
const subjectAt = stmt.indexOf(subject, stmt.search(EXPECT_CALL_RE));
|
|
7218
7445
|
const tail = (stmt.startsWith("expect.poll", stmt.search(EXPECT_CALL_RE)) ? expectCallTail(stmt) : null) ?? stmt.slice(subjectAt + subject.length).trimStart();
|
|
7219
7446
|
const as = (rewritten) => [{ ...s, stmt: rewritten }];
|
|
7447
|
+
const aria = ARIA_TEMPLATE_CALL.exec(tail);
|
|
7448
|
+
if (aria && !NEGATED_ASSERT.test(stmt)) {
|
|
7449
|
+
const texts = ariaSnapshotTexts(aria[2]);
|
|
7450
|
+
if (texts.length > 0)
|
|
7451
|
+
return texts.map((t) => ({ ...s, stmt: `await expect(${plain}).toContainText(${JSON.stringify(t)});` }));
|
|
7452
|
+
}
|
|
7220
7453
|
if (LOCATOR_EXPR.test(plain) && !LOCATOR_VALUE_READ.test(plain) && ALWAYS_TRUE_ON_OBJECT.test(tail))
|
|
7221
7454
|
return [];
|
|
7222
7455
|
if (DEFINED_VALUE_READ.test(plain) && ALWAYS_DEFINED.test(tail))
|
|
@@ -7273,7 +7506,7 @@ function namedPagePath(arg) {
|
|
|
7273
7506
|
const quoted = /^(['"`])([^'"`]*)\1$/.exec(text);
|
|
7274
7507
|
const regex = quoted ? null : /^\/((?:\\.|[^/\\])+)\/[a-z]*$/.exec(text);
|
|
7275
7508
|
if (quoted) {
|
|
7276
|
-
path = quoted[2].replace(/^\$\{[^}]*\}/, "").replace(/^\*\*/, "").replace(/^[a-z][a-z\d+.-]*:\/\/[^/]*/i, "").replace(
|
|
7509
|
+
path = quoted[2].replace(/^\$\{[^}]*\}/, "").replace(/^\*\*/, "").replace(/^[a-z][a-z\d+.-]*:\/\/[^/]*/i, "").replace(/\/\*\*$/, "/");
|
|
7277
7510
|
} else if (regex) {
|
|
7278
7511
|
path = regex[1].replace(/^\^/, "").replace(/^\.\*/, "").replace(/\$$/, "").replace(/\\([/.-])/g, "$1");
|
|
7279
7512
|
} else {
|
|
@@ -7523,7 +7756,8 @@ function teardownLines(lines) {
|
|
|
7523
7756
|
}
|
|
7524
7757
|
return out;
|
|
7525
7758
|
}
|
|
7526
|
-
function validateAssertionStrength(
|
|
7759
|
+
function validateAssertionStrength(source, opts = {}) {
|
|
7760
|
+
const code = withExpectAliasesAsExpect(source);
|
|
7527
7761
|
const body = stripLoginScaffolding(code);
|
|
7528
7762
|
const lines = body.split("\n");
|
|
7529
7763
|
const stmts = liveExpectStatements(stripCoverageComments(body)).flatMap(asPlainAssertion);
|
|
@@ -7676,8 +7910,23 @@ function validateAssertionStrength(code, opts = {}) {
|
|
|
7676
7910
|
const orderedPersistence = firstMutLine !== void 0 ? persistence : void 0;
|
|
7677
7911
|
const signedIn = signInProofWindow(interactionStmts, interactionStmtsWithLines, lines, blockIds);
|
|
7678
7912
|
const proof = classifyOutcomeProof(checks, mutated, reloaded, clickedControlNames(interactionStmts), entered, firstMutLine, orderedPersistence, signedIn);
|
|
7913
|
+
const absenceProof = opts.absenceProof ?? "off";
|
|
7914
|
+
let absenceOnly = false;
|
|
7915
|
+
if (absenceProof !== "off" && proof.kind === "durable") {
|
|
7916
|
+
const existed = subjectsThatExisted(checks, interactionStmtsRaw);
|
|
7917
|
+
const kept = checks.filter(({ stmt }) => !isUnshownAbsence(stmt, existed, entered));
|
|
7918
|
+
absenceOnly = kept.length < checks.length && classifyOutcomeProof(kept, mutated, reloaded, clickedControlNames(interactionStmts), entered, firstMutLine, orderedPersistence, signedIn).kind !== "durable";
|
|
7919
|
+
if (absenceOnly && absenceProof === "on") {
|
|
7920
|
+
return {
|
|
7921
|
+
ok: false,
|
|
7922
|
+
severity: mutated ? "block" : "downgrade",
|
|
7923
|
+
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",
|
|
7924
|
+
signals: base({ interacted, mutated, navOnly, hasOutcomeProof: false, terminal, absenceOnly: true })
|
|
7925
|
+
};
|
|
7926
|
+
}
|
|
7927
|
+
}
|
|
7679
7928
|
if (proof.kind === "durable") {
|
|
7680
|
-
const signals2 = base({ interacted, mutated, navOnly, hasOutcomeProof: true, terminal });
|
|
7929
|
+
const signals2 = base({ interacted, mutated, navOnly, hasOutcomeProof: true, terminal, ...absenceOnly ? { absenceOnly } : {} });
|
|
7681
7930
|
const { last, prev } = acts;
|
|
7682
7931
|
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])))) {
|
|
7683
7932
|
const lastLine = interactionStmtsWithLines[last.i].startLine;
|
|
@@ -7781,7 +8030,7 @@ function validateAssertionStrength(code, opts = {}) {
|
|
|
7781
8030
|
signals
|
|
7782
8031
|
};
|
|
7783
8032
|
}
|
|
7784
|
-
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;
|
|
8033
|
+
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;
|
|
7785
8034
|
var init_assertion_strength = __esm({
|
|
7786
8035
|
"../shared/dist/assertion-strength.js"() {
|
|
7787
8036
|
"use strict";
|
|
@@ -7870,6 +8119,8 @@ var init_assertion_strength = __esm({
|
|
|
7870
8119
|
NOT_BEFORE_MATCHER = /\.not\s*\.\s*(?=to[A-Z])/g;
|
|
7871
8120
|
ERROR_TEXT_WORDS = /error|fail|wrong|try again|unable|could ?n.?t|can.?t|problem|invalid|unavailable/i;
|
|
7872
8121
|
TEXT_REGEX_ARG = /(?:getByText|hasText|getByRole\([^)]{0,200}name)\s*[:(]\s*(\/(?:\\.|[^/\\\n])+\/[a-z]*)/;
|
|
8122
|
+
ABSENCE_READ = /\.toBeVisible\s*\(|\.toBeHidden\s*\(|\.toHaveCount\s*\(\s*0\s*\)|\.toBeAttached\s*\(|\.toHaveText\s*\(|\.toContainText\s*\(/;
|
|
8123
|
+
LOCATOR_USE = /\.\s*(?:click|dblclick|tap|hover|focus|fill|clear|press|pressSequentially|type|check|uncheck|setChecked|selectOption|setInputFiles|dragTo|selectText|waitFor)\s*\(/g;
|
|
7873
8124
|
SCOPING_STEP = /(?:\s*\.\s*(?:first|last|nth)\s*\([^()]*\))*\s*\.\s*(?:getBy[A-Z]\w*|locator|filter)\s*\(/y;
|
|
7874
8125
|
NEGATED_TEXT = /\.not\s*\.\s*(?:toHaveText|toContainText)\s*\(\s*(?:['"`]([^'"`]*)['"`]|\/((?:\\.|[^/\\\n])+)\/)?/;
|
|
7875
8126
|
ASYNC_DESTRUCTURE_OPEN = /async\s*\(\s*\{/g;
|
|
@@ -7911,6 +8162,29 @@ var init_assertion_strength = __esm({
|
|
|
7911
8162
|
IS_VISIBLE_TRUE = /^\)*\s*\.\s*(?:toBe\s*\(\s*true\s*\)|toBeTruthy\s*\(\s*\))/;
|
|
7912
8163
|
PAGE_VALUE_MATCHER = /^\)\s*\.\s*(?:toBe|toEqual|toStrictEqual|toMatch|toContain)\s*\(/;
|
|
7913
8164
|
COUNT_ONE = /\.\s*toHaveCount\s*\(\s*1\s*\)/;
|
|
8165
|
+
ARIA_TEMPLATE_CALL = /^\)\s*\.\s*toMatchAriaSnapshot\s*\(\s*(['"`])((?:\\[\s\S]|(?!\1)[^\\])*)\1/;
|
|
8166
|
+
ARIA_ENTRY_SPLIT = /(?:^|\s)-\s+(?=\/?[a-z][a-z-]*(?:\s|:|\[|$))/;
|
|
8167
|
+
ARIA_CONTENT_ROLES = /* @__PURE__ */ new Set([
|
|
8168
|
+
"listitem",
|
|
8169
|
+
"row",
|
|
8170
|
+
"cell",
|
|
8171
|
+
"gridcell",
|
|
8172
|
+
"article",
|
|
8173
|
+
"treeitem",
|
|
8174
|
+
"paragraph",
|
|
8175
|
+
"text",
|
|
8176
|
+
"strong",
|
|
8177
|
+
"emphasis",
|
|
8178
|
+
"code",
|
|
8179
|
+
"blockquote",
|
|
8180
|
+
"term",
|
|
8181
|
+
"definition",
|
|
8182
|
+
"alert",
|
|
8183
|
+
"status"
|
|
8184
|
+
]);
|
|
8185
|
+
ARIA_ENTRY = /^([a-z][a-z-]*)(?:\s+"((?:\\.|[^"\\])*)")?(?:\s*\[[^\]]*\])*\s*(?::\s*(\S.*)?)?$/s;
|
|
8186
|
+
NOT_HIDDEN = /\.not\s*\.\s*toBeHidden\s*\(/g;
|
|
8187
|
+
NOT_COUNT_ZERO = /\.not\s*\.\s*toHaveCount\s*\(\s*0\s*(?:,\s*|(?=\)))/g;
|
|
7914
8188
|
URL_CHECK_ARG = /\bpage\s*\.\s*waitForURL\s*\(\s*([^,)]+)|\bexpect(?:\s*\.\s*soft)?\s*\(\s*page\s*\)\s*\.\s*toHaveURL\s*\(\s*([^,)]+)/;
|
|
7915
8189
|
PLAIN_PAGE_PATH = /^\/[\w\-./~%]*(?:[?#][^\s]*)?$/;
|
|
7916
8190
|
FORM_CONTROL_LOCATOR = /getByRole\s*\(\s*['"`](?:combobox|switch|checkbox|radio|textbox|searchbox|spinbutton|slider)['"`]|getByLabel\s*\(|getByPlaceholder\s*\(/;
|
|
@@ -9235,6 +9509,19 @@ function googleBrokerUnavailableLine(lines, testError) {
|
|
|
9235
9509
|
}
|
|
9236
9510
|
return last;
|
|
9237
9511
|
}
|
|
9512
|
+
function googleSignInRefusedLine(lines) {
|
|
9513
|
+
let last = null;
|
|
9514
|
+
for (const raw of lines ?? []) {
|
|
9515
|
+
for (const line of String(raw).split("\n")) {
|
|
9516
|
+
const text = line.trim();
|
|
9517
|
+
if (GOOGLE_SIGN_IN_REFUSED.test(text))
|
|
9518
|
+
last = text;
|
|
9519
|
+
else if (GOOGLE_SIGNED_IN.test(text))
|
|
9520
|
+
last = null;
|
|
9521
|
+
}
|
|
9522
|
+
}
|
|
9523
|
+
return last;
|
|
9524
|
+
}
|
|
9238
9525
|
function stepConsoleLines(result) {
|
|
9239
9526
|
const steps = Array.isArray(result) ? result : result && typeof result === "object" && Array.isArray(result.stepResults) ? result.stepResults : [];
|
|
9240
9527
|
return steps.flatMap((step) => {
|
|
@@ -9242,7 +9529,23 @@ function stepConsoleLines(result) {
|
|
|
9242
9529
|
return Array.isArray(logs) ? logs.filter((line) => typeof line === "string") : [];
|
|
9243
9530
|
});
|
|
9244
9531
|
}
|
|
9245
|
-
|
|
9532
|
+
function googleSignInRefusedReason(lines) {
|
|
9533
|
+
let reason = null;
|
|
9534
|
+
for (const raw of lines ?? []) {
|
|
9535
|
+
for (const line of String(raw).split("\n")) {
|
|
9536
|
+
const text = line.trim();
|
|
9537
|
+
if (GOOGLE_SIGNED_IN.test(text)) {
|
|
9538
|
+
reason = null;
|
|
9539
|
+
continue;
|
|
9540
|
+
}
|
|
9541
|
+
const m = GOOGLE_SIGN_IN_ANSWER.exec(text);
|
|
9542
|
+
if (m)
|
|
9543
|
+
reason = m[1];
|
|
9544
|
+
}
|
|
9545
|
+
}
|
|
9546
|
+
return reason;
|
|
9547
|
+
}
|
|
9548
|
+
var GOOGLE_BROKER_UNAVAILABLE, GOOGLE_BROKER_ASKED, GOOGLE_SIGNED_IN, TIMEOUT_ERROR, GOOGLE_SIGN_IN_REFUSED, GOOGLE_SIGN_IN_ANSWER;
|
|
9246
9549
|
var init_google_broker_lines = __esm({
|
|
9247
9550
|
"../shared/dist/google-broker-lines.js"() {
|
|
9248
9551
|
"use strict";
|
|
@@ -9250,6 +9553,8 @@ var init_google_broker_lines = __esm({
|
|
|
9250
9553
|
GOOGLE_BROKER_ASKED = /^\[google-sign-in\] the app started Google sign-in; asking the broker/;
|
|
9251
9554
|
GOOGLE_SIGNED_IN = /^\[google-sign-in\] returned to the app with Google sign-in/;
|
|
9252
9555
|
TIMEOUT_ERROR = /Test timeout of \d+ms exceeded|TimeoutError|Timeout:? \d+ ?ms/i;
|
|
9556
|
+
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): /;
|
|
9557
|
+
GOOGLE_SIGN_IN_ANSWER = /^\[google-sign-in\] ([a-z][a-z0-9_]*): /;
|
|
9253
9558
|
}
|
|
9254
9559
|
});
|
|
9255
9560
|
|
|
@@ -9301,22 +9606,87 @@ function isIntentionalNegativeAuthTest(input) {
|
|
|
9301
9606
|
const meta = [input.testName, input.description].filter(Boolean).join(" ");
|
|
9302
9607
|
return matchesAny(meta, INTENTIONAL_NEGATIVE_AUTH);
|
|
9303
9608
|
}
|
|
9609
|
+
function signInStepPassedBeforeFailure(steps) {
|
|
9610
|
+
const list = steps ?? [];
|
|
9611
|
+
const failedAt = list.findIndex((step) => typeof step.status === "string" && step.status !== "passed" && step.status !== "skipped");
|
|
9612
|
+
if (failedAt <= 0)
|
|
9613
|
+
return false;
|
|
9614
|
+
if (SIGN_IN_STEP_TITLE.test(list[failedAt].title ?? ""))
|
|
9615
|
+
return false;
|
|
9616
|
+
return list.slice(0, failedAt).some((step) => step.status === "passed" && SIGN_IN_STEP_TITLE.test(step.title ?? ""));
|
|
9617
|
+
}
|
|
9618
|
+
function loginFormAlertTexts(body) {
|
|
9619
|
+
const text = body.length > ERROR_CONTEXT_CAP ? body.slice(0, ERROR_CONTEXT_CAP) : body;
|
|
9620
|
+
const blocks = [];
|
|
9621
|
+
let open = null;
|
|
9622
|
+
for (const line of text.split("\n")) {
|
|
9623
|
+
const fence = /^```(\w*)$/.exec(line.trim());
|
|
9624
|
+
if (fence) {
|
|
9625
|
+
if (open) {
|
|
9626
|
+
blocks.push(open);
|
|
9627
|
+
open = null;
|
|
9628
|
+
} else if (fence[1].toLowerCase() === "yaml")
|
|
9629
|
+
open = [];
|
|
9630
|
+
continue;
|
|
9631
|
+
}
|
|
9632
|
+
if (open)
|
|
9633
|
+
open.push(line);
|
|
9634
|
+
}
|
|
9635
|
+
const alerts = [];
|
|
9636
|
+
for (const lines of blocks) {
|
|
9637
|
+
if (!lines.some((l) => SNAPSHOT_PASSWORD_FIELD.test(l)))
|
|
9638
|
+
continue;
|
|
9639
|
+
for (let i = 0; i < lines.length && alerts.length < MAX_ALERTS; i++) {
|
|
9640
|
+
const m = SNAPSHOT_ALERT.exec(lines[i]);
|
|
9641
|
+
if (!m)
|
|
9642
|
+
continue;
|
|
9643
|
+
const indent = m[1].length;
|
|
9644
|
+
const parts = [m[2]];
|
|
9645
|
+
let j = i + 1;
|
|
9646
|
+
for (; j < lines.length; j++) {
|
|
9647
|
+
const lead = /^\s*/.exec(lines[j])?.[0].length ?? 0;
|
|
9648
|
+
if (lines[j].trim() === "" || lead <= indent)
|
|
9649
|
+
break;
|
|
9650
|
+
parts.push(lines[j].trim());
|
|
9651
|
+
}
|
|
9652
|
+
alerts.push(parts.join(" ").slice(0, MAX_ALERT_CHARS));
|
|
9653
|
+
i = j - 1;
|
|
9654
|
+
}
|
|
9655
|
+
}
|
|
9656
|
+
return alerts;
|
|
9657
|
+
}
|
|
9658
|
+
function stepLoginFormAlerts(steps) {
|
|
9659
|
+
const out = [];
|
|
9660
|
+
for (const sr of steps ?? []) {
|
|
9661
|
+
for (const a of sr.attachments ?? []) {
|
|
9662
|
+
if (a?.name === "error-context" && typeof a.body === "string")
|
|
9663
|
+
out.push(...loginFormAlertTexts(a.body));
|
|
9664
|
+
}
|
|
9665
|
+
}
|
|
9666
|
+
return out;
|
|
9667
|
+
}
|
|
9304
9668
|
function detectAuthBlock(input) {
|
|
9305
9669
|
const tags = input.tags ?? [];
|
|
9306
9670
|
const isAuthed = tags.some((t) => AUTHED_TAGS.includes(t));
|
|
9307
|
-
|
|
9671
|
+
const signsInInline2 = !isAuthed && codeReadsStoredCredentials(input.code);
|
|
9672
|
+
if (!isAuthed && !signsInInline2)
|
|
9308
9673
|
return NO_DETECTION;
|
|
9309
9674
|
if (isIntentionalNegativeAuthTest(input))
|
|
9310
9675
|
return NO_DETECTION;
|
|
9676
|
+
if (signInStepPassedBeforeFailure(input.stepResults))
|
|
9677
|
+
return NO_DETECTION;
|
|
9311
9678
|
const sources = [];
|
|
9312
|
-
if (
|
|
9313
|
-
|
|
9314
|
-
|
|
9315
|
-
|
|
9316
|
-
|
|
9317
|
-
|
|
9318
|
-
|
|
9679
|
+
if (isAuthed) {
|
|
9680
|
+
if (input.error)
|
|
9681
|
+
sources.push(stripEchoLines(input.error));
|
|
9682
|
+
if (input.logs)
|
|
9683
|
+
sources.push(stripEchoLines(input.logs));
|
|
9684
|
+
for (const sr of input.stepResults ?? []) {
|
|
9685
|
+
if (sr.error)
|
|
9686
|
+
sources.push(stripEchoLines(sr.error));
|
|
9687
|
+
}
|
|
9319
9688
|
}
|
|
9689
|
+
sources.push(...stepLoginFormAlerts(input.stepResults));
|
|
9320
9690
|
for (const text of sources) {
|
|
9321
9691
|
if (!text)
|
|
9322
9692
|
continue;
|
|
@@ -9365,10 +9735,11 @@ function detectApiColdSessionBlock(input) {
|
|
|
9365
9735
|
function coldSessionConfigMessage(context, reason, evidence) {
|
|
9366
9736
|
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("; ")}`;
|
|
9367
9737
|
}
|
|
9368
|
-
var NO_DETECTION, AUTHED_TAGS, LOGIN_CTX, AUTH_BLOCK_SIGNATURES, INTENTIONAL_NEGATIVE_AUTH, ECHO_LINE_RE, UNAUTHORIZED_TEXT_RE;
|
|
9738
|
+
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;
|
|
9369
9739
|
var init_auth_block_detector = __esm({
|
|
9370
9740
|
"../shared/dist/auth-block-detector.js"() {
|
|
9371
9741
|
"use strict";
|
|
9742
|
+
init_auth_plan();
|
|
9372
9743
|
NO_DETECTION = { detected: false, reason: "", evidence: [] };
|
|
9373
9744
|
AUTHED_TAGS = ["@needs-auth", "@inline-login"];
|
|
9374
9745
|
LOGIN_CTX = "\\b(?:log\\s*in|login|logging\\s+in|sign[\\s-]?in|signing\\s+in|continue)\\b";
|
|
@@ -9378,6 +9749,10 @@ var init_auth_block_detector = __esm({
|
|
|
9378
9749
|
{ reason: "invalid credentials", re: /\b(invalid|incorrect|wrong|bad)\s+(login\s+)?credentials\b/i },
|
|
9379
9750
|
{ reason: "credentials do not match", re: /\bcredentials\b[^\n]{0,40}\b(do(?:es)?\s*n['’]?t|do\s+not)\s+match\b/i },
|
|
9380
9751
|
{ reason: "incorrect email or password", re: /\b(incorrect|invalid|wrong)\s+(email|username)\s+or\s+password\b/i },
|
|
9752
|
+
{ 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 },
|
|
9753
|
+
// A locked account is a blocker of the same kind: the configured user can't sign in until it is unlocked. The
|
|
9754
|
+
// rate-limit detector keeps the timed lockouts ("temporarily locked", "too many attempts").
|
|
9755
|
+
{ reason: "account locked out", re: /\b(?:user|account)\b[^\n]{0,30}\b(?:has\s+been|is)\s+locked\s+out\b/i },
|
|
9381
9756
|
// Require an EXPLICIT negative (codex #21): the old `(not\s+)?(confirmed|verified)` made the negation
|
|
9382
9757
|
// optional, so a benign "Email verified" success message / feature assertion matched and hid a real
|
|
9383
9758
|
// feature failure as a CONFIG_ISSUE. Only "email not (yet) confirmed/verified" or "unconfirmed/unverified".
|
|
@@ -9409,6 +9784,12 @@ var init_auth_block_detector = __esm({
|
|
|
9409
9784
|
/\b(reject|deny|denied|block(ed)?)\b[^\n]{0,20}\b(login|sign[\s-]?in|auth|credential|password)\b/i
|
|
9410
9785
|
];
|
|
9411
9786
|
ECHO_LINE_RE = /^\s*(?:(?:📋|🎭|🌐|⚙️?)|(?:stderr:\s*|stdout:\s*)?Locator:)/u;
|
|
9787
|
+
SIGN_IN_STEP_TITLE = /\b(?:log(?:ging)?\s*in(?:to)?|sign(?:ing)?[\s-]?in(?:to)?|authenticat(?:e|es|ed|ing|ion))\b/i;
|
|
9788
|
+
ERROR_CONTEXT_CAP = 64 * 1024;
|
|
9789
|
+
MAX_ALERTS = 20;
|
|
9790
|
+
MAX_ALERT_CHARS = 1e3;
|
|
9791
|
+
SNAPSHOT_PASSWORD_FIELD = /^\s*- textbox "[^"\n]{0,80}\bpass(?:word|code)?\b/i;
|
|
9792
|
+
SNAPSHOT_ALERT = /^(\s*)- alert\b(.*)$/;
|
|
9412
9793
|
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;
|
|
9413
9794
|
}
|
|
9414
9795
|
});
|
|
@@ -9843,6 +10224,77 @@ var init_redact_needles = __esm({
|
|
|
9843
10224
|
}
|
|
9844
10225
|
});
|
|
9845
10226
|
|
|
10227
|
+
// ../shared/dist/credential-header-scrub.js
|
|
10228
|
+
function scrubCredentialHeaders(text) {
|
|
10229
|
+
if (text.length < 6)
|
|
10230
|
+
return text;
|
|
10231
|
+
let out = text.replace(HEADER_LINE, (match, keyQuote, name, sep, value) => {
|
|
10232
|
+
if (value === "" || ALREADY_REDACTED.test(value))
|
|
10233
|
+
return match;
|
|
10234
|
+
if (COOKIE_NAME.test(name) && !value.includes("="))
|
|
10235
|
+
return match;
|
|
10236
|
+
if (AUTHORIZATION_NAME.test(name) && WORDS_ONLY.test(value))
|
|
10237
|
+
return match;
|
|
10238
|
+
const q = value[0] === '"' || value[0] === "'" ? value[0] : "";
|
|
10239
|
+
return `${keyQuote}${name}${keyQuote}${sep}${q}${REDACTED2}${q}`;
|
|
10240
|
+
});
|
|
10241
|
+
out = out.replace(SET_COOKIE_PAIR, `$1${REDACTED2}`);
|
|
10242
|
+
return out;
|
|
10243
|
+
}
|
|
10244
|
+
function isCredentialHeaderName(name) {
|
|
10245
|
+
return name.length <= 100 && HEADER_NAME_ONLY.test(name);
|
|
10246
|
+
}
|
|
10247
|
+
function scrubCredentialHeaderValue(value) {
|
|
10248
|
+
if (typeof value === "string")
|
|
10249
|
+
return value === REDACTED2 || value === "" ? value : REDACTED2;
|
|
10250
|
+
if (Array.isArray(value) && value.every((item) => typeof item === "string")) {
|
|
10251
|
+
const next = value.map((item) => item === REDACTED2 || item === "" ? item : REDACTED2);
|
|
10252
|
+
return next.every((item, i) => item === value[i]) ? value : next;
|
|
10253
|
+
}
|
|
10254
|
+
return value;
|
|
10255
|
+
}
|
|
10256
|
+
function scrubCredentialHeadersDeep(value) {
|
|
10257
|
+
if (typeof value === "string")
|
|
10258
|
+
return scrubCredentialHeaders(value);
|
|
10259
|
+
if (Array.isArray(value)) {
|
|
10260
|
+
let changed = false;
|
|
10261
|
+
const next = value.map((item) => {
|
|
10262
|
+
const scrubbed = scrubCredentialHeadersDeep(item);
|
|
10263
|
+
if (scrubbed !== item)
|
|
10264
|
+
changed = true;
|
|
10265
|
+
return scrubbed;
|
|
10266
|
+
});
|
|
10267
|
+
return changed ? next : value;
|
|
10268
|
+
}
|
|
10269
|
+
if (value !== null && typeof value === "object") {
|
|
10270
|
+
let changed = false;
|
|
10271
|
+
const next = {};
|
|
10272
|
+
for (const [k, v] of Object.entries(value)) {
|
|
10273
|
+
const scrubbed = isCredentialHeaderName(k) ? scrubCredentialHeaderValue(v) : scrubCredentialHeadersDeep(v);
|
|
10274
|
+
if (scrubbed !== v)
|
|
10275
|
+
changed = true;
|
|
10276
|
+
next[k] = scrubbed;
|
|
10277
|
+
}
|
|
10278
|
+
return changed ? next : value;
|
|
10279
|
+
}
|
|
10280
|
+
return value;
|
|
10281
|
+
}
|
|
10282
|
+
var REDACTED2, HEADER_NAMES, COOKIE_NAME, AUTHORIZATION_NAME, WORDS_ONLY, HEADER_NAME_ONLY, HEADER_LINE, ALREADY_REDACTED, SET_COOKIE_PAIR;
|
|
10283
|
+
var init_credential_header_scrub = __esm({
|
|
10284
|
+
"../shared/dist/credential-header-scrub.js"() {
|
|
10285
|
+
"use strict";
|
|
10286
|
+
REDACTED2 = "[REDACTED]";
|
|
10287
|
+
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}";
|
|
10288
|
+
COOKIE_NAME = /^(?:set-)?cookie2?$/i;
|
|
10289
|
+
AUTHORIZATION_NAME = /^(?:proxy-)?authorization$/i;
|
|
10290
|
+
WORDS_ONLY = /^[A-Za-z]+(?: [A-Za-z]+){0,3}$/;
|
|
10291
|
+
HEADER_NAME_ONLY = new RegExp(`^(?:${HEADER_NAMES})$`, "i");
|
|
10292
|
+
HEADER_LINE = new RegExp(`(?<![A-Za-z0-9_-])(["']?)(${HEADER_NAMES})\\1(\\s*:\\s*)("(?:[^"\\\\\\r\\n]|\\\\.)*"|'(?:[^'\\\\\\r\\n]|\\\\.)*'|[^\\r\\n\\x1b]*)`, "gi");
|
|
10293
|
+
ALREADY_REDACTED = /^["']?(?:[A-Za-z][\w-]*\s+)?\[REDACTED\]["']?[,;]?\s*$/;
|
|
10294
|
+
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;
|
|
10295
|
+
}
|
|
10296
|
+
});
|
|
10297
|
+
|
|
9846
10298
|
// ../shared/dist/deepgram-voices.js
|
|
9847
10299
|
function voice(family, name, gender, accent, description) {
|
|
9848
10300
|
const label = name.charAt(0).toUpperCase() + name.slice(1);
|
|
@@ -10419,13 +10871,13 @@ function isSensitiveQueryKey(key) {
|
|
|
10419
10871
|
return SENSITIVE_QUERY_KEY_RE.test(key);
|
|
10420
10872
|
}
|
|
10421
10873
|
function redactSecretValues(value) {
|
|
10422
|
-
return value.replace(MCP_SECRET_VALUE_RE,
|
|
10874
|
+
return value.replace(MCP_SECRET_VALUE_RE, REDACTED3);
|
|
10423
10875
|
}
|
|
10424
10876
|
function scrubReturnUrlValue(value, depth) {
|
|
10425
10877
|
if (!/[?%]/.test(value))
|
|
10426
10878
|
return value;
|
|
10427
10879
|
if (depth >= MAX_NESTED_URL_DEPTH)
|
|
10428
|
-
return
|
|
10880
|
+
return REDACTED3;
|
|
10429
10881
|
const q = value.indexOf("?");
|
|
10430
10882
|
if (q !== -1) {
|
|
10431
10883
|
const hashIdx = value.indexOf("#", q);
|
|
@@ -10447,7 +10899,7 @@ function scrubReturnUrlValue(value, depth) {
|
|
|
10447
10899
|
try {
|
|
10448
10900
|
return encodeURIComponent(scrubbed);
|
|
10449
10901
|
} catch {
|
|
10450
|
-
return
|
|
10902
|
+
return REDACTED3;
|
|
10451
10903
|
}
|
|
10452
10904
|
}
|
|
10453
10905
|
function scrubQuery(search, depth) {
|
|
@@ -10463,7 +10915,7 @@ function scrubQuery(search, depth) {
|
|
|
10463
10915
|
params.forEach((value, key) => {
|
|
10464
10916
|
let scrubbed = value;
|
|
10465
10917
|
if (isSensitiveQueryKey(key)) {
|
|
10466
|
-
scrubbed =
|
|
10918
|
+
scrubbed = REDACTED3;
|
|
10467
10919
|
} else {
|
|
10468
10920
|
if (RETURN_URL_QUERY_KEY_RE.test(key))
|
|
10469
10921
|
scrubbed = scrubReturnUrlValue(scrubbed, depth);
|
|
@@ -10575,12 +11027,12 @@ function scrubUrlPropertyBag(props) {
|
|
|
10575
11027
|
} catch {
|
|
10576
11028
|
}
|
|
10577
11029
|
}
|
|
10578
|
-
var SENSITIVE_QUERY_KEY_RE,
|
|
11030
|
+
var SENSITIVE_QUERY_KEY_RE, REDACTED3, MCP_SECRET_VALUE_RE, RETURN_URL_QUERY_KEY_RE, MAX_NESTED_URL_DEPTH, MAX_PROPERTY_SWEEP_DEPTH;
|
|
10579
11031
|
var init_analytics_url = __esm({
|
|
10580
11032
|
"../shared/dist/analytics-url.js"() {
|
|
10581
11033
|
"use strict";
|
|
10582
11034
|
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;
|
|
10583
|
-
|
|
11035
|
+
REDACTED3 = "[redacted]";
|
|
10584
11036
|
MCP_SECRET_VALUE_RE = /mcp_(consent|code|at|rt)_[A-Za-z0-9]+/g;
|
|
10585
11037
|
RETURN_URL_QUERY_KEY_RE = /^(next|redirect|return|continue)([_-]?(to|url|uri|path))?$/i;
|
|
10586
11038
|
MAX_NESTED_URL_DEPTH = 5;
|
|
@@ -13389,8 +13841,19 @@ function successContradicted(text) {
|
|
|
13389
13841
|
}
|
|
13390
13842
|
return SUCCESS_NO_STATUS_RE.test(text) || OFF_BY_RE.test(text);
|
|
13391
13843
|
}
|
|
13844
|
+
function negatedSafeguard(text) {
|
|
13845
|
+
for (const match of matches(NEGATED_SAFEGUARD_RE, text)) {
|
|
13846
|
+
if (!NOT_KEPT_RE.test(match[1]))
|
|
13847
|
+
return true;
|
|
13848
|
+
const head = text.slice(Math.max(0, match.index - SAFEGUARD_LOOKBEHIND), match.index);
|
|
13849
|
+
const sentence = head.slice(Math.max(head.lastIndexOf("."), head.lastIndexOf(";"), head.lastIndexOf("\n")) + 1);
|
|
13850
|
+
if (!REFUSED_INPUT_RE.test(sentence))
|
|
13851
|
+
return true;
|
|
13852
|
+
}
|
|
13853
|
+
return false;
|
|
13854
|
+
}
|
|
13392
13855
|
function claimsDefect(text) {
|
|
13393
|
-
if (BUG_LABEL_RE.test(text) || SILENT_RE.test(text) || CONCESSION_RE.test(text) || successContradicted(text) ||
|
|
13856
|
+
if (BUG_LABEL_RE.test(text) || SILENT_RE.test(text) || CONCESSION_RE.test(text) || successContradicted(text) || negatedSafeguard(text)) {
|
|
13394
13857
|
return true;
|
|
13395
13858
|
}
|
|
13396
13859
|
return BAD_VALUE_RE.test(text) && !REFUSAL_RE.test(text);
|
|
@@ -13398,7 +13861,7 @@ function claimsDefect(text) {
|
|
|
13398
13861
|
function hasTestFailContract(code) {
|
|
13399
13862
|
return TEST_FAIL_CONTRACT_RE.test(code);
|
|
13400
13863
|
}
|
|
13401
|
-
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;
|
|
13864
|
+
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;
|
|
13402
13865
|
var init_claims_defect = __esm({
|
|
13403
13866
|
"../shared/dist/claims-defect.js"() {
|
|
13404
13867
|
"use strict";
|
|
@@ -13442,7 +13905,10 @@ var init_claims_defect = __esm({
|
|
|
13442
13905
|
NOT_A_CLEARING_RE = /\b(?:cancel|refresh|reload|preview|open|view|fail|hover|scroll|sort|filter|search)\w*/i;
|
|
13443
13906
|
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;
|
|
13444
13907
|
OFF_BY_RE = /\boff[- ]by[- ](?:one|two|\d{1,3})\b/i;
|
|
13445
|
-
NEGATED_SAFEGUARD_RE = /\bnot (
|
|
13908
|
+
NEGATED_SAFEGUARD_RE = /\bnot (rejected|validated|enforced|applied|persisted|saved)\b/gi;
|
|
13909
|
+
NOT_KEPT_RE = /^(?:applied|persisted|saved)$/i;
|
|
13910
|
+
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;
|
|
13911
|
+
SAFEGUARD_LOOKBEHIND = 80;
|
|
13446
13912
|
BAD_VALUE_RE = /\b(?:incorrect|wrong|stale|negative|epoch|unix) (?:total|amount|value|date|count|price|label|plan|balance)\b/i;
|
|
13447
13913
|
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;
|
|
13448
13914
|
TEST_FAIL_CONTRACT_RE = /\btest\.fail\s{0,20}\(/;
|
|
@@ -14301,7 +14767,7 @@ function findExpectCalls(code) {
|
|
|
14301
14767
|
return out;
|
|
14302
14768
|
}
|
|
14303
14769
|
function countExpectCalls(code) {
|
|
14304
|
-
return findExpectCalls(stripComments2(code)).length;
|
|
14770
|
+
return findExpectCalls(stripComments2(withExpectAliasesAsExpect(code))).length;
|
|
14305
14771
|
}
|
|
14306
14772
|
function countActionCalls(code) {
|
|
14307
14773
|
return [...stripComments2(code).matchAll(ACTION_CALL)].length;
|
|
@@ -16630,6 +17096,7 @@ var init_test_authoring_quality = __esm({
|
|
|
16630
17096
|
"../shared/dist/test-authoring-quality.js"() {
|
|
16631
17097
|
"use strict";
|
|
16632
17098
|
init_claims_defect();
|
|
17099
|
+
init_expect_call();
|
|
16633
17100
|
init_readiness_poll();
|
|
16634
17101
|
init_persistence_reread();
|
|
16635
17102
|
init_write_response_check();
|
|
@@ -16756,7 +17223,7 @@ var init_test_authoring_quality = __esm({
|
|
|
16756
17223
|
MAX_BALANCED_SCAN = 2048;
|
|
16757
17224
|
MAX_CALLS = 500;
|
|
16758
17225
|
MAX_SEEN_SCAN = 200;
|
|
16759
|
-
ACTION_CALL = /\.(?:click|dblclick|fill|press|pressSequentially|type|check|uncheck|selectOption|setInputFiles|hover|tap|dragTo|goto)\s*\(/g;
|
|
17226
|
+
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;
|
|
16760
17227
|
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\(/;
|
|
16761
17228
|
ACTION_AFTER = /^\s{0,20}\.\s{0,20}(?:click|fill|check|uncheck|press|type|hover|focus|selectOption|setInputFiles|dblclick|tap)\s{0,20}\(/;
|
|
16762
17229
|
MAX_CONTACTS = 200;
|
|
@@ -18265,7 +18732,7 @@ function credentialEntries(maps) {
|
|
|
18265
18732
|
}
|
|
18266
18733
|
function replacePattern(value, form) {
|
|
18267
18734
|
const escaped = form.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
|
|
18268
|
-
return
|
|
18735
|
+
return value.length < WHOLE_WORD_BELOW ? `(?<![A-Za-z0-9_])${escaped}(?![A-Za-z0-9_])` : escaped;
|
|
18269
18736
|
}
|
|
18270
18737
|
function findCredentialTextLeftover(text, maps) {
|
|
18271
18738
|
const bare = text.split(WITHHELD_CREDENTIAL_TEXT).join(" ").replace(REFERENCE, " ").toLowerCase();
|
|
@@ -18279,11 +18746,17 @@ function scrubCredentialText(text, maps) {
|
|
|
18279
18746
|
const entries = credentialEntries(maps);
|
|
18280
18747
|
if (entries.length === 0)
|
|
18281
18748
|
return text;
|
|
18282
|
-
|
|
18749
|
+
const alternatives = [];
|
|
18283
18750
|
for (const [key, value] of entries) {
|
|
18284
18751
|
for (const form of credentialValueForms(value))
|
|
18285
|
-
|
|
18752
|
+
alternatives.push({ pattern: replacePattern(value, form), key, length: form.length });
|
|
18286
18753
|
}
|
|
18754
|
+
alternatives.sort((a, b) => b.length - a.length);
|
|
18755
|
+
const matcher = new RegExp([REFERENCE.source, ...alternatives.map((alt) => `(${alt.pattern})`)].join("|"), "gi");
|
|
18756
|
+
const out = text.replace(matcher, (...args) => {
|
|
18757
|
+
const index = args.slice(1, 1 + alternatives.length).findIndex((group) => group !== void 0);
|
|
18758
|
+
return index < 0 ? String(args[0]) : `{{secret:${alternatives[index].key}}}`;
|
|
18759
|
+
});
|
|
18287
18760
|
return findCredentialTextLeftover(out, maps) ? WITHHELD_CREDENTIAL_TEXT : out;
|
|
18288
18761
|
}
|
|
18289
18762
|
function scrubCredentialStringsIn(value, maps) {
|
|
@@ -18777,10 +19250,24 @@ function onlyUrlAfter(code, writeAt) {
|
|
|
18777
19250
|
i = at + 6;
|
|
18778
19251
|
}
|
|
18779
19252
|
}
|
|
19253
|
+
function readsOrderAfter(code, writeAt) {
|
|
19254
|
+
const rest = code.slice(writeAt + 1);
|
|
19255
|
+
if (ORDER_READ_MARKERS.some((m) => rest.includes(m)) && nextExpect(code, writeAt + 1) >= 0)
|
|
19256
|
+
return true;
|
|
19257
|
+
for (let i = writeAt + 1; ; ) {
|
|
19258
|
+
const at = nextExpect(code, i);
|
|
19259
|
+
if (at < 0)
|
|
19260
|
+
return false;
|
|
19261
|
+
if (ORDERED_ARRAY_EXPECT.test(expectStatement(code, at)))
|
|
19262
|
+
return true;
|
|
19263
|
+
i = at + 6;
|
|
19264
|
+
}
|
|
19265
|
+
}
|
|
18780
19266
|
function assessTitlePromiseShape(claimText, code) {
|
|
18781
19267
|
const persistence = PERSISTENCE_RE.test(claimText);
|
|
18782
19268
|
const refusal = REFUSAL_RE2.test(claimText);
|
|
18783
|
-
|
|
19269
|
+
const order = ORDER_RE.test(claimText) || ORDER_RE_CASED.test(claimText);
|
|
19270
|
+
if (!persistence && !refusal && !order)
|
|
18784
19271
|
return [];
|
|
18785
19272
|
const blank = blankCommentsAndStringLiterals(code);
|
|
18786
19273
|
const writeAt = proofAnchor(blank);
|
|
@@ -18793,9 +19280,12 @@ function assessTitlePromiseShape(claimText, code) {
|
|
|
18793
19280
|
if (refusal && onlyUrlAfter(blank, writeAt)) {
|
|
18794
19281
|
findings.push({ rule: "rejection-only-url", reason: REFUSAL_REASON });
|
|
18795
19282
|
}
|
|
19283
|
+
if (order && !readsOrderAfter(blank, writeAt)) {
|
|
19284
|
+
findings.push({ rule: "sort-without-order", reason: ORDER_REASON });
|
|
19285
|
+
}
|
|
18796
19286
|
return findings;
|
|
18797
19287
|
}
|
|
18798
|
-
var PERSIST_REASON, REFUSAL_REASON, PERSISTENCE_RE, REFUSAL_RE2, WRITE_MARKERS, VALUE_WRITE_MARKERS, REREAD_MARKERS;
|
|
19288
|
+
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;
|
|
18799
19289
|
var init_title_promise_shape = __esm({
|
|
18800
19290
|
"../shared/dist/title-promise-shape.js"() {
|
|
18801
19291
|
"use strict";
|
|
@@ -18804,6 +19294,21 @@ var init_title_promise_shape = __esm({
|
|
|
18804
19294
|
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.";
|
|
18805
19295
|
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;
|
|
18806
19296
|
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;
|
|
19297
|
+
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).";
|
|
19298
|
+
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;
|
|
19299
|
+
ORDER_RE_CASED = /\b[AZ]\s{0,3}(?:to|-|→)\s{0,3}[AZ]\b/;
|
|
19300
|
+
ORDER_READ_MARKERS = [
|
|
19301
|
+
".allTextContents(",
|
|
19302
|
+
".allInnerTexts(",
|
|
19303
|
+
".evaluateAll(",
|
|
19304
|
+
".toMatchAriaSnapshot(",
|
|
19305
|
+
".sort(",
|
|
19306
|
+
".toSorted(",
|
|
19307
|
+
".first()",
|
|
19308
|
+
".last()",
|
|
19309
|
+
".nth("
|
|
19310
|
+
];
|
|
19311
|
+
ORDERED_ARRAY_EXPECT = /\.(?:toHaveText|toContainText|toEqual|toStrictEqual)\s*\(\s*\[/;
|
|
18807
19312
|
WRITE_MARKERS = [
|
|
18808
19313
|
".fill(",
|
|
18809
19314
|
".click(",
|
|
@@ -19135,7 +19640,7 @@ function assertionsReadVisibleContent(code) {
|
|
|
19135
19640
|
});
|
|
19136
19641
|
}
|
|
19137
19642
|
function isLegacyReviewFlag(tags, activity) {
|
|
19138
|
-
const flagged = tags.includes(
|
|
19643
|
+
const flagged = tags.includes(NEEDS_REVIEW_TAG2) || activity?.needsReview === true;
|
|
19139
19644
|
const source = activity?.needsReviewSource;
|
|
19140
19645
|
return flagged && !(typeof source === "string" && source in SOURCE_RANK);
|
|
19141
19646
|
}
|
|
@@ -19197,7 +19702,7 @@ function recheckGeneratorNotes(notes, input) {
|
|
|
19197
19702
|
}
|
|
19198
19703
|
function reviewHolds(tags, activity, cleared = /* @__PURE__ */ new Set()) {
|
|
19199
19704
|
const out = [];
|
|
19200
|
-
const tagged = tags.includes(
|
|
19705
|
+
const tagged = tags.includes(NEEDS_REVIEW_TAG2);
|
|
19201
19706
|
const generatorAsked = activity?.needsReview === true;
|
|
19202
19707
|
if (tagged) {
|
|
19203
19708
|
if (activity?.needsReviewSource === "human")
|
|
@@ -19257,7 +19762,7 @@ function evaluatePromotionGate(input, checks = PROMOTION_QUALITY_CHECKS) {
|
|
|
19257
19762
|
...suspiciousTagged ? { suspiciousPass: { mode: suspicious, covered: suspiciousCovered, wouldHold: suspiciousHold } } : {}
|
|
19258
19763
|
};
|
|
19259
19764
|
}
|
|
19260
|
-
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,
|
|
19765
|
+
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;
|
|
19261
19766
|
var init_promotion_quality_gate = __esm({
|
|
19262
19767
|
"../shared/dist/promotion-quality-gate.js"() {
|
|
19263
19768
|
"use strict";
|
|
@@ -19276,8 +19781,8 @@ var init_promotion_quality_gate = __esm({
|
|
|
19276
19781
|
SOURCE_RANK = { generator: 0, classifier: 0, healer: 1, human: 2 };
|
|
19277
19782
|
strengthCheck = {
|
|
19278
19783
|
id: "assertion-strength",
|
|
19279
|
-
run({ code, tags, strengthGate, flagContradictedAdditions, lastActionProof }) {
|
|
19280
|
-
const verdict = validateAssertionStrength(code, { flagContradictedAdditions: flagContradictedAdditions === true, lastActionProof });
|
|
19784
|
+
run({ code, tags, strengthGate, flagContradictedAdditions, lastActionProof, absenceProof }) {
|
|
19785
|
+
const verdict = validateAssertionStrength(code, { flagContradictedAdditions: flagContradictedAdditions === true, lastActionProof, absenceProof });
|
|
19281
19786
|
if (verdict.severity === "route-bug")
|
|
19282
19787
|
return [{ check: "route-bug" }];
|
|
19283
19788
|
const authDowngrade = verdict.severity === "downgrade" && tags.some((t) => t.toLowerCase() === "@auth");
|
|
@@ -19535,7 +20040,7 @@ ${goalText}`);
|
|
|
19535
20040
|
NOT_CONTENT_MATCHER = /\.(?:toHaveURL|toHaveTitle|toBeOK)\s*\(|\bwaitForURL\s*\(|\.(?:status|ok)\s*\(\s*\)/;
|
|
19536
20041
|
CONTENT_PROOF = /* @__PURE__ */ new Set(["controlled", "revealed", "value"]);
|
|
19537
20042
|
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/;
|
|
19538
|
-
|
|
20043
|
+
NEEDS_REVIEW_TAG2 = "@needs-review";
|
|
19539
20044
|
count2 = (v) => typeof v === "number" && Number.isFinite(v) ? v : 0;
|
|
19540
20045
|
RECHECKED_NOTES = [
|
|
19541
20046
|
{
|
|
@@ -19656,11 +20161,18 @@ var init_regex_alternation = __esm({
|
|
|
19656
20161
|
function isPageEvidenceEntry(entry) {
|
|
19657
20162
|
return typeof entry === "object" && entry !== null && entry.outcomeType === PAGE_LOADING_OUTCOME_TYPE;
|
|
19658
20163
|
}
|
|
19659
|
-
|
|
20164
|
+
function pageScreenshotKey(projectId, safeName, stillLoading = false) {
|
|
20165
|
+
return `discovery/${projectId}/screenshots/${stillLoading ? `${STILL_LOADING_SCREENSHOT_DIR}/` : ""}${safeName}.png`;
|
|
20166
|
+
}
|
|
20167
|
+
function isStillLoadingScreenshotKey(key) {
|
|
20168
|
+
return typeof key === "string" && key.includes(`/screenshots/${STILL_LOADING_SCREENSHOT_DIR}/`);
|
|
20169
|
+
}
|
|
20170
|
+
var PAGE_LOADING_OUTCOME_TYPE, STILL_LOADING_SCREENSHOT_DIR;
|
|
19660
20171
|
var init_page_evidence = __esm({
|
|
19661
20172
|
"../shared/dist/page-evidence.js"() {
|
|
19662
20173
|
"use strict";
|
|
19663
20174
|
PAGE_LOADING_OUTCOME_TYPE = "LOADING";
|
|
20175
|
+
STILL_LOADING_SCREENSHOT_DIR = "still-loading";
|
|
19664
20176
|
}
|
|
19665
20177
|
});
|
|
19666
20178
|
|
|
@@ -20065,11 +20577,13 @@ __export(dist_exports, {
|
|
|
20065
20577
|
SERVER_INTERRUPT_ERROR_PREFIX: () => SERVER_INTERRUPT_ERROR_PREFIX,
|
|
20066
20578
|
SHARED_PRICING_PLAN_MAP: () => SHARED_PRICING_PLAN_MAP,
|
|
20067
20579
|
SHARED_SERVER_APPLICATION_NAME: () => SHARED_SERVER_APPLICATION_NAME,
|
|
20580
|
+
SMS_NOT_RECEIVED_PREFIXES: () => SMS_NOT_RECEIVED_PREFIXES,
|
|
20068
20581
|
SPEND_SOURCE_KEY: () => SPEND_SOURCE_KEY,
|
|
20069
20582
|
SPEND_SOURCE_SERVER: () => SPEND_SOURCE_SERVER,
|
|
20070
20583
|
STATED_LIMITS_VERSION: () => STATED_LIMITS_VERSION,
|
|
20071
20584
|
STATED_PATTERN_MAX: () => STATED_PATTERN_MAX,
|
|
20072
20585
|
STATIC_STANDARD_MIN_INTERACTIVE: () => STATIC_STANDARD_MIN_INTERACTIVE,
|
|
20586
|
+
STILL_LOADING_SCREENSHOT_DIR: () => STILL_LOADING_SCREENSHOT_DIR,
|
|
20073
20587
|
STT_MODEL_OPTIONS: () => STT_MODEL_OPTIONS,
|
|
20074
20588
|
SUCCESS_NOTICE_TEXT: () => SUCCESS_NOTICE_TEXT,
|
|
20075
20589
|
SUPPORT_EMAIL: () => SUPPORT_EMAIL,
|
|
@@ -20097,6 +20611,7 @@ __export(dist_exports, {
|
|
|
20097
20611
|
TTS_VOICE_OPTIONS: () => TTS_VOICE_OPTIONS,
|
|
20098
20612
|
UI_VARIANTS: () => UI_VARIANTS,
|
|
20099
20613
|
UNEXPECTED_PASS_ERROR_PREFIX: () => UNEXPECTED_PASS_ERROR_PREFIX,
|
|
20614
|
+
UNVERIFIED_HEAL_CONFIDENCES: () => UNVERIFIED_HEAL_CONFIDENCES,
|
|
20100
20615
|
USER_CANCELLED_RUN_ERROR: () => USER_CANCELLED_RUN_ERROR,
|
|
20101
20616
|
VISUAL_MASK_SELECTORS_MAX: () => VISUAL_MASK_SELECTORS_MAX,
|
|
20102
20617
|
VISUAL_MASK_SELECTOR_FORBIDDEN: () => VISUAL_MASK_SELECTOR_FORBIDDEN,
|
|
@@ -20150,6 +20665,7 @@ __export(dist_exports, {
|
|
|
20150
20665
|
blankBlockComments: () => blankBlockComments3,
|
|
20151
20666
|
blankCommentsAndStringLiterals: () => blankCommentsAndStringLiterals,
|
|
20152
20667
|
blankKeepingLines: () => blankKeepingLines,
|
|
20668
|
+
blockedRunLogLine: () => blockedRunLogLine,
|
|
20153
20669
|
boundedCallCostUsd: () => boundedCallCostUsd,
|
|
20154
20670
|
brokerResponseBody: () => brokerResponseBody,
|
|
20155
20671
|
bugFoundQuarantineWhere: () => bugFoundQuarantineWhere,
|
|
@@ -20255,6 +20771,7 @@ __export(dist_exports, {
|
|
|
20255
20771
|
evaluatePromotionGate: () => evaluatePromotionGate,
|
|
20256
20772
|
excludeCancelledRunsAnd: () => excludeCancelledRunsAnd,
|
|
20257
20773
|
excludeCancelledRunsWhere: () => excludeCancelledRunsWhere,
|
|
20774
|
+
expectAliasNames: () => expectAliasNames,
|
|
20258
20775
|
explicitAgenticCliOverrideError: () => explicitAgenticCliOverrideError,
|
|
20259
20776
|
extractAssertedControlSignatures: () => extractAssertedControlSignatures,
|
|
20260
20777
|
extractMuseAssistantJson: () => extractMuseAssistantJson,
|
|
@@ -20321,6 +20838,8 @@ __export(dist_exports, {
|
|
|
20321
20838
|
googleGisRequestUrl: () => googleGisRequestUrl,
|
|
20322
20839
|
googleSignInLimitLines: () => googleSignInLimitLines,
|
|
20323
20840
|
googleSignInPolicyProblems: () => googleSignInPolicyProblems,
|
|
20841
|
+
googleSignInRefusedLine: () => googleSignInRefusedLine,
|
|
20842
|
+
googleSignInRefusedReason: () => googleSignInRefusedReason,
|
|
20324
20843
|
hasCrossPlatformIdentity: () => hasCrossPlatformIdentity,
|
|
20325
20844
|
hasEncodedRequestBody: () => hasEncodedRequestBody,
|
|
20326
20845
|
hasEngineSpecParts: () => hasEngineSpecParts,
|
|
@@ -20364,6 +20883,7 @@ __export(dist_exports, {
|
|
|
20364
20883
|
isCodeFactNote: () => isCodeFactNote,
|
|
20365
20884
|
isConfigLikeReason: () => isConfigLikeReason,
|
|
20366
20885
|
isCountedFailure: () => isCountedFailure,
|
|
20886
|
+
isCredentialHeaderName: () => isCredentialHeaderName,
|
|
20367
20887
|
isCreditPackAmountCents: () => isCreditPackAmountCents,
|
|
20368
20888
|
isDbPricingPlan: () => isDbPricingPlan,
|
|
20369
20889
|
isDeniedGoogleBrokerRedirectHost: () => isDeniedGoogleBrokerRedirectHost,
|
|
@@ -20419,8 +20939,10 @@ __export(dist_exports, {
|
|
|
20419
20939
|
isSharedAuthTenantHost: () => isSharedAuthTenantHost,
|
|
20420
20940
|
isSharedSuffix: () => isSharedSuffix,
|
|
20421
20941
|
isSingleEmailAddress: () => isSingleEmailAddress,
|
|
20942
|
+
isSmsNotReceivedError: () => isSmsNotReceivedError,
|
|
20422
20943
|
isSoftOutcome: () => isSoftOutcome,
|
|
20423
20944
|
isStateValueMobileClassName: () => isStateValueMobileClassName,
|
|
20945
|
+
isStillLoadingScreenshotKey: () => isStillLoadingScreenshotKey,
|
|
20424
20946
|
isSttModelForProvider: () => isSttModelForProvider,
|
|
20425
20947
|
isTestReadyForPlatform: () => isTestReadyForPlatform,
|
|
20426
20948
|
isTestVariableSecretKey: () => isTestVariableSecretKey,
|
|
@@ -20428,6 +20950,7 @@ __export(dist_exports, {
|
|
|
20428
20950
|
isUIVariant: () => isUIVariant,
|
|
20429
20951
|
isUnexpectedPassError: () => isUnexpectedPassError,
|
|
20430
20952
|
isUnsafePrecondition: () => isUnsafePrecondition,
|
|
20953
|
+
isUnverifiedHealPass: () => isUnverifiedHealPass,
|
|
20431
20954
|
isUsableDeepgramKey: () => isUsableDeepgramKey,
|
|
20432
20955
|
isUserCancelledError: () => isUserCancelledError,
|
|
20433
20956
|
isValidAgenticCliModel: () => isValidAgenticCliModel,
|
|
@@ -20452,8 +20975,10 @@ __export(dist_exports, {
|
|
|
20452
20975
|
lintTestData: () => lintTestData,
|
|
20453
20976
|
lintTestQuality: () => lintTestQuality,
|
|
20454
20977
|
literalUrlRegex: () => literalUrlRegex,
|
|
20978
|
+
loginFormAlertTexts: () => loginFormAlertTexts,
|
|
20455
20979
|
longCredentialValues: () => longCredentialValues,
|
|
20456
20980
|
maskedOutsideStrings: () => maskedOutsideStrings,
|
|
20981
|
+
mayBeUnverifiedHealPass: () => mayBeUnverifiedHealPass,
|
|
20457
20982
|
mediumForMobilePlatform: () => mediumForMobilePlatform,
|
|
20458
20983
|
mergePageType: () => mergePageType,
|
|
20459
20984
|
mergeSuspectedBugsAcrossSurveys: () => mergeSuspectedBugsAcrossSurveys,
|
|
@@ -20487,7 +21012,9 @@ __export(dist_exports, {
|
|
|
20487
21012
|
opensRegexLiteral: () => opensRegexLiteral,
|
|
20488
21013
|
otherMobilePlatform: () => otherMobilePlatform,
|
|
20489
21014
|
outcomeNamedByAssertion: () => outcomeNamedByAssertion,
|
|
21015
|
+
pageScreenshotKey: () => pageScreenshotKey,
|
|
20490
21016
|
parenDepthDelta: () => parenDepthDelta,
|
|
21017
|
+
parseAbsenceProofMode: () => parseAbsenceProofMode,
|
|
20491
21018
|
parseApiCardModelUpdate: () => parseApiCardModelUpdate,
|
|
20492
21019
|
parseAppTimezone: () => parseAppTimezone,
|
|
20493
21020
|
parseAuditGroupScopeId: () => parseAuditGroupScopeId,
|
|
@@ -20563,7 +21090,7 @@ __export(dist_exports, {
|
|
|
20563
21090
|
redactNeedlesText: () => redactNeedlesText,
|
|
20564
21091
|
redirectOnAppSite: () => redirectOnAppSite,
|
|
20565
21092
|
refuseEncodedRequestBodies: () => refuseEncodedRequestBodies,
|
|
20566
|
-
regexLiteralEnd: () =>
|
|
21093
|
+
regexLiteralEnd: () => regexLiteralEnd,
|
|
20567
21094
|
regexOpensAfter: () => regexOpensAfter,
|
|
20568
21095
|
registrableDomain: () => registrableDomain,
|
|
20569
21096
|
renderIdentity: () => renderIdentity,
|
|
@@ -20605,6 +21132,9 @@ __export(dist_exports, {
|
|
|
20605
21132
|
saveOpensConfirm: () => saveOpensConfirm,
|
|
20606
21133
|
scanClockDependentLiterals: () => scanClockDependentLiterals,
|
|
20607
21134
|
scanHtmlTags: () => scanHtmlTags,
|
|
21135
|
+
scrubCredentialHeaderValue: () => scrubCredentialHeaderValue,
|
|
21136
|
+
scrubCredentialHeaders: () => scrubCredentialHeaders,
|
|
21137
|
+
scrubCredentialHeadersDeep: () => scrubCredentialHeadersDeep,
|
|
20608
21138
|
scrubCredentialStringsIn: () => scrubCredentialStringsIn,
|
|
20609
21139
|
scrubCredentialText: () => scrubCredentialText,
|
|
20610
21140
|
scrubSearchParams: () => scrubSearchParams,
|
|
@@ -20615,9 +21145,12 @@ __export(dist_exports, {
|
|
|
20615
21145
|
selectorForValue: () => selectorForValue,
|
|
20616
21146
|
setAgenticCliRuntime: () => setAgenticCliRuntime,
|
|
20617
21147
|
setProviderChainOverrides: () => setProviderChainOverrides,
|
|
21148
|
+
signInStepPassedBeforeFailure: () => signInStepPassedBeforeFailure,
|
|
21149
|
+
signsInInline: () => signsInInline,
|
|
20618
21150
|
signupPlanFor: () => signupPlanFor,
|
|
20619
21151
|
simplifyMobileWanderingInPlace: () => simplifyMobileWanderingInPlace,
|
|
20620
21152
|
siteOf: () => siteOf,
|
|
21153
|
+
specCallsExpect: () => specCallsExpect,
|
|
20621
21154
|
specItemsFromTestPlans: () => specItemsFromTestPlans,
|
|
20622
21155
|
splitAnswerLines: () => splitAnswerLines,
|
|
20623
21156
|
statedLimitsFromAttrs: () => statedLimitsFromAttrs,
|
|
@@ -20665,6 +21198,7 @@ __export(dist_exports, {
|
|
|
20665
21198
|
visualProDailyBudget: () => visualProDailyBudget,
|
|
20666
21199
|
voiceMatchesTtsModel: () => voiceMatchesTtsModel,
|
|
20667
21200
|
voicesForTtsModel: () => voicesForTtsModel,
|
|
21201
|
+
withExpectAliasesAsExpect: () => withExpectAliasesAsExpect,
|
|
20668
21202
|
withGeneratorReviewSource: () => withGeneratorReviewSource,
|
|
20669
21203
|
withReviewSource: () => withReviewSource,
|
|
20670
21204
|
withoutNotChargedClaim: () => withoutNotChargedClaim,
|
|
@@ -20719,6 +21253,7 @@ var init_dist = __esm({
|
|
|
20719
21253
|
init_encoded_forms();
|
|
20720
21254
|
init_block_comments();
|
|
20721
21255
|
init_redact_needles();
|
|
21256
|
+
init_credential_header_scrub();
|
|
20722
21257
|
init_voice_config();
|
|
20723
21258
|
init_deepgram_speech();
|
|
20724
21259
|
init_deepgram_voices();
|
|
@@ -20805,6 +21340,8 @@ export {
|
|
|
20805
21340
|
deriveAuthPlan,
|
|
20806
21341
|
isConfigLikeReason,
|
|
20807
21342
|
classifyErrorMessage,
|
|
21343
|
+
blockedRunLogLine,
|
|
21344
|
+
registrableDomain,
|
|
20808
21345
|
isAppOriginResolutionEnabled,
|
|
20809
21346
|
resolveCrossOriginConfigOverride,
|
|
20810
21347
|
isSharedAuthInjectEnabled,
|