@validate.qa/runner 1.0.100 → 1.0.102
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 +3 -0
- package/dist/{chunk-MBKGTELT.mjs → chunk-3JM6XTSZ.mjs} +1862 -530
- package/dist/{chunk-2XD63NU5.mjs → chunk-4WLP3RWN.mjs} +941 -86
- package/dist/{chunk-MHFP3QXU.mjs → chunk-5DFBEL72.mjs} +1 -1
- package/dist/{chunk-II7QW7MZ.mjs → chunk-BXLEPT4M.mjs} +31 -18
- package/dist/{chunk-MAO6ADAO.mjs → chunk-CBVHPYVJ.mjs} +62 -2
- package/dist/{chunk-5P4ANVDY.mjs → chunk-DN3O6EYT.mjs} +43073 -23357
- package/dist/{chunk-GIIIVO2W.mjs → chunk-E2RRAE27.mjs} +155 -11
- package/dist/chunk-E2VEMBQQ.mjs +291 -0
- package/dist/{chunk-HMUC3DKS.mjs → chunk-HJ6FKHMM.mjs} +2281 -2
- package/dist/{chunk-WDXLAZ2J.mjs → chunk-PWFWMVZU.mjs} +3 -2
- package/dist/{chunk-6NIT2CCH.mjs → chunk-ZZYBRIBT.mjs} +18 -12
- package/dist/cli.js +139723 -114754
- package/dist/cli.mjs +536 -247
- package/dist/credential-mcp-server.js +1761 -358
- package/dist/credential-mcp-server.mjs +3 -3
- package/dist/discovery-mcp-server.js +1874 -379
- package/dist/discovery-mcp-server.mjs +4 -4
- package/dist/dist-52M5RDZ6.mjs +17 -0
- package/dist/inbox-mcp-server.js +1902 -505
- package/dist/inbox-mcp-server.mjs +3 -3
- package/dist/route-seed-worker.js +3976 -356
- package/dist/route-seed-worker.mjs +14 -7
- package/dist/run-write-guard-preload.js +2908 -572
- package/dist/run-write-guard-preload.mjs +5 -285
- package/dist/run-write-guard.js +2908 -572
- package/dist/run-write-guard.mjs +3 -3
- package/dist/spec-driver.js +1 -1
- package/dist/spec-driver.mjs +1 -1
- package/package.json +4 -3
- package/dist/dist-DFNLMH6L.mjs +0 -16
|
@@ -368,13 +368,13 @@ function isBlockedEnvVar(key) {
|
|
|
368
368
|
function sanitizeTestVariables(vars) {
|
|
369
369
|
if (!vars)
|
|
370
370
|
return {};
|
|
371
|
-
const
|
|
371
|
+
const clean3 = {};
|
|
372
372
|
for (const [key, value] of Object.entries(vars)) {
|
|
373
373
|
if (!isBlockedEnvVar(key)) {
|
|
374
|
-
|
|
374
|
+
clean3[key] = value;
|
|
375
375
|
}
|
|
376
376
|
}
|
|
377
|
-
return
|
|
377
|
+
return clean3;
|
|
378
378
|
}
|
|
379
379
|
function buildSandboxedEnv(processEnv, testVariables) {
|
|
380
380
|
const env = {};
|
|
@@ -1831,9 +1831,40 @@ function semanticTarget(label, platform, base2) {
|
|
|
1831
1831
|
}
|
|
1832
1832
|
};
|
|
1833
1833
|
}
|
|
1834
|
+
function androidResourceOwners(value) {
|
|
1835
|
+
const owners = [];
|
|
1836
|
+
for (const match of value.matchAll(ANDROID_RESOURCE_ID_SUFFIX_RE)) {
|
|
1837
|
+
let runStart = match.index;
|
|
1838
|
+
while (runStart > 0 && /[a-z0-9_.]/i.test(value[runStart - 1]))
|
|
1839
|
+
runStart--;
|
|
1840
|
+
const owner = ownerAtEndOfRun(value.slice(runStart, match.index));
|
|
1841
|
+
if (owner !== null)
|
|
1842
|
+
owners.push(owner);
|
|
1843
|
+
}
|
|
1844
|
+
return owners;
|
|
1845
|
+
}
|
|
1846
|
+
function ownerAtEndOfRun(run) {
|
|
1847
|
+
const segments = run.split(".");
|
|
1848
|
+
const last = segments.length - 1;
|
|
1849
|
+
const startsWithLetter = (segment) => /^[a-z]/i.test(segment);
|
|
1850
|
+
if (last < 1 || !startsWithLetter(segments[last]))
|
|
1851
|
+
return null;
|
|
1852
|
+
let first = last;
|
|
1853
|
+
while (first > 0 && startsWithLetter(segments[first - 1]))
|
|
1854
|
+
first--;
|
|
1855
|
+
let offset = 0;
|
|
1856
|
+
for (let i = 0; i < first - 1; i++)
|
|
1857
|
+
offset += segments[i].length + 1;
|
|
1858
|
+
if (first > 0) {
|
|
1859
|
+
const letterAt = segments[first - 1].search(/[a-z]/i);
|
|
1860
|
+
if (letterAt >= 0)
|
|
1861
|
+
return run.slice(offset + letterAt);
|
|
1862
|
+
offset += segments[first - 1].length + 1;
|
|
1863
|
+
}
|
|
1864
|
+
return first < last ? run.slice(offset) : null;
|
|
1865
|
+
}
|
|
1834
1866
|
function containsForeignAppPackage(value, targetAppId) {
|
|
1835
|
-
for (const
|
|
1836
|
-
const owner = match[1];
|
|
1867
|
+
for (const owner of androidResourceOwners(value)) {
|
|
1837
1868
|
if (targetAppId && owner !== targetAppId && !owner.startsWith(`${targetAppId}.`))
|
|
1838
1869
|
return true;
|
|
1839
1870
|
}
|
|
@@ -1970,7 +2001,7 @@ function healMobileTestSteps(input) {
|
|
|
1970
2001
|
summary: changed ? uniq(summary).slice(0, 12) : []
|
|
1971
2002
|
};
|
|
1972
2003
|
}
|
|
1973
|
-
var MOBILE_STEP_ACTIONS, FOREIGN_PACKAGE_RE,
|
|
2004
|
+
var MOBILE_STEP_ACTIONS, FOREIGN_PACKAGE_RE, ANDROID_RESOURCE_ID_SUFFIX_RE, COMMON_EXTERNAL_PREFIXES;
|
|
1974
2005
|
var init_mobile_step_healer = __esm({
|
|
1975
2006
|
"../shared/dist/mobile-step-healer.js"() {
|
|
1976
2007
|
"use strict";
|
|
@@ -1991,7 +2022,7 @@ var init_mobile_step_healer = __esm({
|
|
|
1991
2022
|
"assertStateDelta"
|
|
1992
2023
|
]);
|
|
1993
2024
|
FOREIGN_PACKAGE_RE = /\b[a-z][a-z0-9_]*(?:\.[a-z][a-z0-9_]*){2,}\b/gi;
|
|
1994
|
-
|
|
2025
|
+
ANDROID_RESOURCE_ID_SUFFIX_RE = /:id\//gi;
|
|
1995
2026
|
COMMON_EXTERNAL_PREFIXES = [
|
|
1996
2027
|
"com.android.",
|
|
1997
2028
|
"com.google.",
|
|
@@ -3304,7 +3335,7 @@ function unescapeQuotedBody(raw) {
|
|
|
3304
3335
|
return raw.replace(/\\(['"`\\])/g, "$1");
|
|
3305
3336
|
}
|
|
3306
3337
|
function extractRoleNameValue(line) {
|
|
3307
|
-
const m = line.match(new RegExp(`getByRole\\s*\\([^,]
|
|
3338
|
+
const m = line.match(new RegExp(`getByRole\\s*\\([^,]{1,200},\\s*\\{[^}]{0,200}?name\\s*:\\s*(?:${QUOTED_BODY}|\\/([^/]*?)\\/)`, "i"));
|
|
3308
3339
|
if (!m)
|
|
3309
3340
|
return null;
|
|
3310
3341
|
const raw = m[1] ?? m[2] ?? m[3];
|
|
@@ -3701,7 +3732,7 @@ var init_assertion_classifier = __esm({
|
|
|
3701
3732
|
// toHaveCount(0) stays tier 4 (absence proof) via TIER4_PATTERNS.
|
|
3702
3733
|
[/\.toHaveCount\s*\(\s*[1-9]\d*\s*\)/, "non-zero toHaveCount asserts a record count that changes between runs"],
|
|
3703
3734
|
// parenthesised counts like "(3)" or "[12]" in text - but not Playwright method args
|
|
3704
|
-
[
|
|
3735
|
+
[/\((?<!\.\w{3,20}\s*\()\s*\d+\s*\)(?!\s*(?:[,;]\s*)?\))/i, "parenthesised count is session-specific"],
|
|
3705
3736
|
// bare email addresses in assertion content
|
|
3706
3737
|
[/\b[a-z0-9._%+-]+@[a-z0-9.-]+\.[a-z]{2,}\b/i, "email address is user-specific"],
|
|
3707
3738
|
// currency/balance amounts (dynamic account data)
|
|
@@ -3947,7 +3978,7 @@ var init_assertion_proof = __esm({
|
|
|
3947
3978
|
PRESENCE_MATCHER = /\.(?:toBeVisible|toBeAttached|toBeEnabled|toBeInViewport|toBeEditable|toBeFocused)\s*\(|\.toHaveCount\s*\(\s*1\s*\)/;
|
|
3948
3979
|
TEXT_MATCHER = /\.(?:toHaveText|toContainText)\s*\(/;
|
|
3949
3980
|
ANY_VALUE_SOURCE = /^(?:\.|\\S|\\w|\\d|\[\^\\s\]|\[0-9\]|\[\\d\]|\[\\w\]|\[\\s\\S\])[+*]?\??$/;
|
|
3950
|
-
ANY_VALUE_MATCHER = /\.(?:toHaveValue|toHaveText|toContainText)\s*\(\s*\/((?:\\.|[^/\\]){1,300})\/[a-z]*\s
|
|
3981
|
+
ANY_VALUE_MATCHER = /\.(?:toHaveValue|toHaveText|toContainText)\s*\(\s*\/((?:\\.|[^/\\]){1,300})\/[a-z]*\s*[,)]/;
|
|
3951
3982
|
EMPTY_NOT_VALUE = /\.not\s*\.\s*toHaveValue\s*\(\s*(?:['"`]{2}|\/\^?\$?\/)/;
|
|
3952
3983
|
TEXT_MATCHER_LITERAL = /\.(?:toHaveText|toContainText)\s*\(\s*(['"`])((?:(?!\1).){1,300})\1/;
|
|
3953
3984
|
GET_BY_TEXT_LITERAL = /getByText\s*\(\s*(['"`])((?:(?!\1).){1,300})\1/;
|
|
@@ -3964,7 +3995,7 @@ var init_assertion_proof = __esm({
|
|
|
3964
3995
|
DATA_ROLE = /getByRole\s*\(\s*['"`](?:dialog|alertdialog|alert|cell|gridcell|row|rowheader|columnheader|listitem|article|option|status|note|tooltip|paragraph|term|definition|log|marquee|timer|meter)['"`]/gi;
|
|
3965
3996
|
TEXT_LOCATOR = /\.getByText\s*\(|\.filter\s*\(\s*\{\s*hasText\s*:/g;
|
|
3966
3997
|
CSS_LOCATOR = /\.locator\s*\(\s*(['"`])((?:(?!\1).){1,300})\1/g;
|
|
3967
|
-
SPECIFIC_CSS = /\[[^\]]*=|#[\w-]|has-text|:text/;
|
|
3998
|
+
SPECIFIC_CSS = /\[[^\][=]*=|#[\w-]|has-text|:text/;
|
|
3968
3999
|
}
|
|
3969
4000
|
});
|
|
3970
4001
|
|
|
@@ -4012,6 +4043,8 @@ function stripCoverageComments(code) {
|
|
|
4012
4043
|
let copyFrom = 0;
|
|
4013
4044
|
let i = 0;
|
|
4014
4045
|
let quote = null;
|
|
4046
|
+
let prev = "";
|
|
4047
|
+
let noRegexBefore = 0;
|
|
4015
4048
|
while (i < code.length) {
|
|
4016
4049
|
const ch = code[i];
|
|
4017
4050
|
if (quote) {
|
|
@@ -4019,12 +4052,24 @@ function stripCoverageComments(code) {
|
|
|
4019
4052
|
i += 2;
|
|
4020
4053
|
continue;
|
|
4021
4054
|
}
|
|
4022
|
-
if (ch === quote || ch === "\n" && quote !== "`")
|
|
4055
|
+
if (ch === quote || ch === "\n" && quote !== "`") {
|
|
4023
4056
|
quote = null;
|
|
4057
|
+
prev = ch === "\n" ? prev : ch;
|
|
4058
|
+
}
|
|
4024
4059
|
i++;
|
|
4025
4060
|
continue;
|
|
4026
4061
|
}
|
|
4027
4062
|
const next = code[i + 1];
|
|
4063
|
+
if (ch === "/" && next !== "*" && next !== "/" && i >= noRegexBefore && (prev === "" || REGEX_PRECEDER2.includes(prev))) {
|
|
4064
|
+
const end = regexLiteralEnd3(code, i);
|
|
4065
|
+
if (end > 0) {
|
|
4066
|
+
i = end;
|
|
4067
|
+
prev = "a";
|
|
4068
|
+
continue;
|
|
4069
|
+
}
|
|
4070
|
+
const eol = code.indexOf("\n", i);
|
|
4071
|
+
noRegexBefore = eol === -1 ? code.length : eol;
|
|
4072
|
+
}
|
|
4028
4073
|
if (ch === "/" && next === "*") {
|
|
4029
4074
|
const end = code.indexOf("*/", i + 2);
|
|
4030
4075
|
const stop = end === -1 ? code.length : end + 2;
|
|
@@ -4043,11 +4088,39 @@ function stripCoverageComments(code) {
|
|
|
4043
4088
|
}
|
|
4044
4089
|
if (ch === '"' || ch === "'" || ch === "`")
|
|
4045
4090
|
quote = ch;
|
|
4091
|
+
if (ch !== " " && ch !== " " && ch !== "\n" && ch !== "\r")
|
|
4092
|
+
prev = ch;
|
|
4046
4093
|
i++;
|
|
4047
4094
|
}
|
|
4048
4095
|
out.push(code.slice(copyFrom));
|
|
4049
4096
|
return out.join("");
|
|
4050
4097
|
}
|
|
4098
|
+
function regexLiteralEnd3(code, start) {
|
|
4099
|
+
let inClass = false;
|
|
4100
|
+
for (let j = start + 1; j < code.length; j++) {
|
|
4101
|
+
const c = code[j];
|
|
4102
|
+
if (c === "\n")
|
|
4103
|
+
return -1;
|
|
4104
|
+
if (c === "\\") {
|
|
4105
|
+
j++;
|
|
4106
|
+
continue;
|
|
4107
|
+
}
|
|
4108
|
+
if (inClass) {
|
|
4109
|
+
if (c === "]")
|
|
4110
|
+
inClass = false;
|
|
4111
|
+
continue;
|
|
4112
|
+
}
|
|
4113
|
+
if (c === "[")
|
|
4114
|
+
inClass = true;
|
|
4115
|
+
else if (c === "/") {
|
|
4116
|
+
let k = j + 1;
|
|
4117
|
+
while (k < code.length && isIdentChar(code[k]))
|
|
4118
|
+
k++;
|
|
4119
|
+
return k;
|
|
4120
|
+
}
|
|
4121
|
+
}
|
|
4122
|
+
return -1;
|
|
4123
|
+
}
|
|
4051
4124
|
function isIdentChar(ch) {
|
|
4052
4125
|
if (!ch)
|
|
4053
4126
|
return false;
|
|
@@ -4705,7 +4778,7 @@ function assessTitleCoverage(title, code) {
|
|
|
4705
4778
|
const repeatedHit = [...repeated].some((term) => hay.includes(term));
|
|
4706
4779
|
return { covered: hit >= needed || repeatedHit, missingTerms };
|
|
4707
4780
|
}
|
|
4708
|
-
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;
|
|
4781
|
+
var COVERAGE_STOP_WORDS, MAX_ASSERTION_JOIN_CHARS, MAX_COVERAGE_LINE_CHARS, REGEX_PRECEDER2, BLOCK_SCAFFOLD, cachedAssertionInput, cachedAssertionSource, LOCAL_BINDING, ASSERTED_STATUS_RE, MAX_RESOLVED_NAMES_PER_LINE, WORD_CHAR_RE, NOT_CONTAIN_TEXT_RE, PLACE_PHRASE_RE, PLACE_WORD_MIN_CHARS, PLACE_FUNCTION_WORDS, HIDDEN_STATE_TERMS, HIDDEN_ELEMENT_LINE_RE, HIDDEN_LINE_RE, MAX_SHOWN_EARLIER_LINES, SHOWN_EARLIER_LITERAL_RE, SHARED_PREFIX_MIN_CHARS, MAX_SHOWN_EARLIER_LITERALS, VALUE_SHAPE_RE, VALUE_SHAPE_ALL_RE, TITLE_TERM_MIN;
|
|
4709
4782
|
var init_expected_outcome_coverage = __esm({
|
|
4710
4783
|
"../shared/dist/expected-outcome-coverage.js"() {
|
|
4711
4784
|
"use strict";
|
|
@@ -4742,6 +4815,7 @@ var init_expected_outcome_coverage = __esm({
|
|
|
4742
4815
|
]);
|
|
4743
4816
|
MAX_ASSERTION_JOIN_CHARS = 8e3;
|
|
4744
4817
|
MAX_COVERAGE_LINE_CHARS = 2e3;
|
|
4818
|
+
REGEX_PRECEDER2 = "(,=:[!&|?{};+-*%<>~^";
|
|
4745
4819
|
BLOCK_SCAFFOLD = /=>|\b(?:test|describe|it|beforeEach|afterEach|beforeAll|afterAll|function)\b/;
|
|
4746
4820
|
cachedAssertionInput = null;
|
|
4747
4821
|
cachedAssertionSource = { raw: "", normalized: "", bindings: /* @__PURE__ */ new Map(), bindingRefs: /* @__PURE__ */ new Map() };
|
|
@@ -4885,22 +4959,37 @@ function classifyAuthHandoffIntent(text) {
|
|
|
4885
4959
|
function withoutIdpUrlAsserts(code) {
|
|
4886
4960
|
return code.replace(/\.(?:toHaveURL|waitForURL)\s*\((?:[^)(]|\([^)]*\))*\)/g, (match) => /accountchooser|accounts\.google|appleid\.apple|login\.microsoftonline|\/v\d+\/signin|oauth/i.test(match) ? "" : match);
|
|
4887
4961
|
}
|
|
4962
|
+
function stripLineComment(line) {
|
|
4963
|
+
let from = line.length;
|
|
4964
|
+
while (from > 0) {
|
|
4965
|
+
const c = line.charCodeAt(from - 1);
|
|
4966
|
+
if (c === 10 || c === 13 || c === 8232 || c === 8233)
|
|
4967
|
+
break;
|
|
4968
|
+
from--;
|
|
4969
|
+
}
|
|
4970
|
+
const at = line.indexOf("//", from);
|
|
4971
|
+
return at < 0 ? line : line.slice(0, at);
|
|
4972
|
+
}
|
|
4888
4973
|
function stripComments(code) {
|
|
4889
|
-
return replaceBlockComments(code, () => "").split("\n").map(
|
|
4974
|
+
return replaceBlockComments(code, () => "").split("\n").map(stripLineComment).join("\n");
|
|
4890
4975
|
}
|
|
4891
|
-
function
|
|
4892
|
-
const
|
|
4893
|
-
|
|
4894
|
-
|
|
4895
|
-
let depth = 0;
|
|
4896
|
-
for (let i = open; i < code.length; i++) {
|
|
4976
|
+
function closingParens(code) {
|
|
4977
|
+
const close = new Int32Array(code.length).fill(-1);
|
|
4978
|
+
const open = [];
|
|
4979
|
+
for (let i = 0; i < code.length; i++) {
|
|
4897
4980
|
const c = code[i];
|
|
4898
4981
|
if (c === "(")
|
|
4899
|
-
|
|
4900
|
-
else if (c === ")" &&
|
|
4901
|
-
|
|
4982
|
+
open.push(i);
|
|
4983
|
+
else if (c === ")" && open.length > 0)
|
|
4984
|
+
close[open.pop()] = i;
|
|
4902
4985
|
}
|
|
4903
|
-
return
|
|
4986
|
+
return close;
|
|
4987
|
+
}
|
|
4988
|
+
function balancedCall(code, close, start) {
|
|
4989
|
+
const open = code.indexOf("(", start);
|
|
4990
|
+
if (open < 0 || close[open] < 0)
|
|
4991
|
+
return null;
|
|
4992
|
+
return { inner: code.slice(open + 1, close[open]), end: close[open] };
|
|
4904
4993
|
}
|
|
4905
4994
|
function resolveLocator(code, subject) {
|
|
4906
4995
|
const id = subject.trim().match(/^([A-Za-z_$][\w$]*)$/);
|
|
@@ -4948,8 +5037,10 @@ function isAuthHandoffDismissCode(code) {
|
|
|
4948
5037
|
const expects = [];
|
|
4949
5038
|
const re = new RegExp(EXPECT_CALL_RE.source, "g");
|
|
4950
5039
|
let match;
|
|
5040
|
+
let close = null;
|
|
4951
5041
|
while (match = re.exec(body)) {
|
|
4952
|
-
|
|
5042
|
+
close ??= closingParens(body);
|
|
5043
|
+
const found = balancedCall(body, close, match.index);
|
|
4953
5044
|
if (found)
|
|
4954
5045
|
expects.push({ inner: found.inner, tail: body.slice(found.end, found.end + 48) });
|
|
4955
5046
|
}
|
|
@@ -4994,12 +5085,209 @@ var init_auth_handoff_coverage = __esm({
|
|
|
4994
5085
|
}
|
|
4995
5086
|
});
|
|
4996
5087
|
|
|
5088
|
+
// ../shared/dist/auth-plan.js
|
|
5089
|
+
function hasInlineLoginSteps(playwrightCode) {
|
|
5090
|
+
if (!playwrightCode)
|
|
5091
|
+
return false;
|
|
5092
|
+
return /process\.env\.TEST_USER_EMAIL!?/.test(playwrightCode) && /process\.env\.TEST_USER_PASSWORD!?/.test(playwrightCode);
|
|
5093
|
+
}
|
|
5094
|
+
function hasGeneratedAuthGuard(playwrightCode) {
|
|
5095
|
+
if (!playwrightCode)
|
|
5096
|
+
return false;
|
|
5097
|
+
return /\/\/ >>> VALIDATE_QA_AUTH v(\d+)\b[\s\S]*?process\.env\.AUTH_FILE[\s\S]*?\/\/ <<< VALIDATE_QA_AUTH v\1\b/.test(playwrightCode);
|
|
5098
|
+
}
|
|
5099
|
+
function legacyNeedsCookieState(tags, code, sharedAuthInjectEnabled) {
|
|
5100
|
+
if (!sharedAuthInjectEnabled)
|
|
5101
|
+
return false;
|
|
5102
|
+
if (tags.includes("@auth"))
|
|
5103
|
+
return false;
|
|
5104
|
+
if (tags.includes("@api"))
|
|
5105
|
+
return true;
|
|
5106
|
+
if (!tags.includes("@needs-auth"))
|
|
5107
|
+
return false;
|
|
5108
|
+
if (sharedAuthInjectEnabled && tags.includes("@auth-guarded") && hasGeneratedAuthGuard(code))
|
|
5109
|
+
return true;
|
|
5110
|
+
const isGeneratedVariant = tags.includes("@edge") || tags.includes("@bug");
|
|
5111
|
+
const hasLegacyInlineLogin = tags.includes("@discovered") && !isGeneratedVariant && hasInlineLoginSteps(code);
|
|
5112
|
+
return !tags.includes("@inline-login") && !hasLegacyInlineLogin;
|
|
5113
|
+
}
|
|
5114
|
+
function codeReadsStoredCredentials(code) {
|
|
5115
|
+
if (!code)
|
|
5116
|
+
return false;
|
|
5117
|
+
if (/__vqaLogin\b/.test(code))
|
|
5118
|
+
return true;
|
|
5119
|
+
for (const m of code.matchAll(/TEST_[A-Z0-9_]+/g)) {
|
|
5120
|
+
if (/_(?:EMAIL|PASSWORD)/.test(m[0]))
|
|
5121
|
+
return true;
|
|
5122
|
+
}
|
|
5123
|
+
for (const m of code.matchAll(/process\.env\s*\[([^\]]*)\]?/g)) {
|
|
5124
|
+
if (!/^\s*(['"])[^'"`]*\1\s*$/.test(m[1]))
|
|
5125
|
+
return true;
|
|
5126
|
+
}
|
|
5127
|
+
return false;
|
|
5128
|
+
}
|
|
5129
|
+
function explicitAuthTypeTags(t) {
|
|
5130
|
+
const found = /* @__PURE__ */ new Set();
|
|
5131
|
+
for (const tag of t) {
|
|
5132
|
+
if (AUTH_TYPE_TAGS.includes(tag))
|
|
5133
|
+
found.add(tag);
|
|
5134
|
+
else if (Object.hasOwn(AUTH_TAG_ALIASES, tag))
|
|
5135
|
+
found.add(AUTH_TAG_ALIASES[tag]);
|
|
5136
|
+
}
|
|
5137
|
+
return [...found];
|
|
5138
|
+
}
|
|
5139
|
+
function invalidPlan(configurationError) {
|
|
5140
|
+
return {
|
|
5141
|
+
version: 1,
|
|
5142
|
+
source: "invalid",
|
|
5143
|
+
acquisition: "unsupported",
|
|
5144
|
+
presentation: "none",
|
|
5145
|
+
needsCookieState: false,
|
|
5146
|
+
needsStandingHeaders: false,
|
|
5147
|
+
standingHeadersMode: "none",
|
|
5148
|
+
needsLoginCreds: false,
|
|
5149
|
+
requiredEnvKeys: [],
|
|
5150
|
+
configurationError
|
|
5151
|
+
};
|
|
5152
|
+
}
|
|
5153
|
+
function explicitPlan(authTypeTag2, acquisition, presentation, opts) {
|
|
5154
|
+
return {
|
|
5155
|
+
version: 1,
|
|
5156
|
+
source: "explicit",
|
|
5157
|
+
authTypeTag: authTypeTag2,
|
|
5158
|
+
acquisition,
|
|
5159
|
+
presentation,
|
|
5160
|
+
needsCookieState: opts.cookie,
|
|
5161
|
+
needsStandingHeaders: opts.standingMode === "required",
|
|
5162
|
+
standingHeadersMode: opts.standingMode,
|
|
5163
|
+
needsLoginCreds: TEST_USER_ENV_KEYS.every((k) => opts.requiredEnvKeys.includes(k)),
|
|
5164
|
+
requiredEnvKeys: opts.requiredEnvKeys
|
|
5165
|
+
};
|
|
5166
|
+
}
|
|
5167
|
+
function deriveAuthPlan(tags, code, opts) {
|
|
5168
|
+
const t = Array.isArray(tags) ? tags : [];
|
|
5169
|
+
const sharedFlag = opts?.sharedAuthInjectEnabled ?? false;
|
|
5170
|
+
const isApi = t.includes("@api");
|
|
5171
|
+
const explicit = isApi ? explicitAuthTypeTags(t) : [];
|
|
5172
|
+
if (explicit.length > 0) {
|
|
5173
|
+
if (t.includes("@auth")) {
|
|
5174
|
+
return invalidPlan("@auth (self-login UI) cannot be combined with an explicit @api auth-type tag");
|
|
5175
|
+
}
|
|
5176
|
+
const loginCompose = explicit.filter((e) => e === AUTH_TAG_COOKIE_STATE || e === AUTH_TAG_LOGIN_COOKIE || e === AUTH_TAG_LOGIN_BEARER);
|
|
5177
|
+
if (explicit.length === 2 && explicit.includes(AUTH_TAG_STANDING) && loginCompose.length === 1) {
|
|
5178
|
+
const composedKeys = [...TEST_USER_ENV_KEYS, API_AUTH_HEADERS_ENV_KEY];
|
|
5179
|
+
switch (loginCompose[0]) {
|
|
5180
|
+
case AUTH_TAG_COOKIE_STATE:
|
|
5181
|
+
return explicitPlan(AUTH_TAG_COOKIE_STATE, "browser-bootstrap", "cookie", { cookie: sharedFlag, standingMode: "required", requiredEnvKeys: composedKeys });
|
|
5182
|
+
case AUTH_TAG_LOGIN_COOKIE:
|
|
5183
|
+
return explicitPlan(AUTH_TAG_LOGIN_COOKIE, "request-login", "cookie", { cookie: false, standingMode: "required", requiredEnvKeys: composedKeys });
|
|
5184
|
+
case AUTH_TAG_LOGIN_BEARER:
|
|
5185
|
+
return explicitPlan(AUTH_TAG_LOGIN_BEARER, "request-login", "bearer", { cookie: false, standingMode: "required", requiredEnvKeys: composedKeys });
|
|
5186
|
+
}
|
|
5187
|
+
}
|
|
5188
|
+
if (explicit.length > 1) {
|
|
5189
|
+
return invalidPlan(`conflicting auth-type tags: ${explicit.join(", ")}`);
|
|
5190
|
+
}
|
|
5191
|
+
switch (explicit[0]) {
|
|
5192
|
+
case AUTH_TAG_UNSUPPORTED:
|
|
5193
|
+
return {
|
|
5194
|
+
...invalidPlan("auth scheme not yet drivable (e.g. HMAC / mTLS / provider-replay) \u2014 needs manual configuration")
|
|
5195
|
+
};
|
|
5196
|
+
case AUTH_TAG_NONE:
|
|
5197
|
+
return explicitPlan(AUTH_TAG_NONE, "none", "none", { cookie: false, standingMode: "none", requiredEnvKeys: [] });
|
|
5198
|
+
case AUTH_TAG_STANDING:
|
|
5199
|
+
return explicitPlan(AUTH_TAG_STANDING, "standing-secret", "standing-header", { cookie: false, standingMode: "required", requiredEnvKeys: [API_AUTH_HEADERS_ENV_KEY] });
|
|
5200
|
+
case AUTH_TAG_LOGIN_BEARER:
|
|
5201
|
+
return explicitPlan(AUTH_TAG_LOGIN_BEARER, "request-login", "bearer", { cookie: false, standingMode: "none", requiredEnvKeys: [...TEST_USER_ENV_KEYS] });
|
|
5202
|
+
case AUTH_TAG_LOGIN_COOKIE:
|
|
5203
|
+
return explicitPlan(AUTH_TAG_LOGIN_COOKIE, "request-login", "cookie", { cookie: false, standingMode: "legacy-if-configured", requiredEnvKeys: [...TEST_USER_ENV_KEYS] });
|
|
5204
|
+
case AUTH_TAG_COOKIE_STATE:
|
|
5205
|
+
return explicitPlan(AUTH_TAG_COOKIE_STATE, "browser-bootstrap", "cookie", { cookie: sharedFlag, standingMode: "legacy-if-configured", requiredEnvKeys: [...TEST_USER_ENV_KEYS] });
|
|
5206
|
+
}
|
|
5207
|
+
}
|
|
5208
|
+
const cookie = legacyNeedsCookieState(t, code, sharedFlag);
|
|
5209
|
+
const authedLegacy = t.includes("@auth") || t.includes("@needs-auth") || isApi;
|
|
5210
|
+
const selfLoginWithoutStoredCreds = t.includes("@auth") && !t.includes("@needs-auth") && !isApi && !!code?.trim() && !/TEST_USER_EMAIL|TEST_USER_PASSWORD|__vqaLogin\b/.test(code);
|
|
5211
|
+
const apiWithoutStoredCreds = isApi && !t.includes("@auth") && !t.includes("@needs-auth") && !!code?.trim() && !codeReadsStoredCredentials(code);
|
|
5212
|
+
const needsLoginCreds = authedLegacy && !selfLoginWithoutStoredCreds && !apiWithoutStoredCreds;
|
|
5213
|
+
return {
|
|
5214
|
+
version: 1,
|
|
5215
|
+
source: "legacy",
|
|
5216
|
+
acquisition: cookie ? "browser-bootstrap" : authedLegacy ? "request-login" : "none",
|
|
5217
|
+
presentation: authedLegacy ? "cookie" : "none",
|
|
5218
|
+
needsCookieState: cookie,
|
|
5219
|
+
needsStandingHeaders: false,
|
|
5220
|
+
// A legacy bare-@api test keeps injecting standing headers IF the project configured them
|
|
5221
|
+
// (preserving today's behavior) — but does not require them. Non-@api legacy tests never inject.
|
|
5222
|
+
standingHeadersMode: isApi ? "legacy-if-configured" : "none",
|
|
5223
|
+
needsLoginCreds,
|
|
5224
|
+
requiredEnvKeys: needsLoginCreds ? [...TEST_USER_ENV_KEYS] : []
|
|
5225
|
+
};
|
|
5226
|
+
}
|
|
5227
|
+
function authTypeTag(tags) {
|
|
5228
|
+
const t = Array.isArray(tags) ? tags : [];
|
|
5229
|
+
const explicit = explicitAuthTypeTags(t);
|
|
5230
|
+
return explicit.length === 1 ? explicit[0] : void 0;
|
|
5231
|
+
}
|
|
5232
|
+
var AUTH_TAG_COOKIE_STATE, AUTH_TAG_LOGIN_COOKIE, AUTH_TAG_LOGIN_BEARER, AUTH_TAG_STANDING, AUTH_TAG_NONE, AUTH_TAG_UNSUPPORTED, AUTH_TYPE_TAGS, AUTH_TAG_ALIASES, TEST_USER_ENV_KEYS, API_AUTH_HEADERS_ENV_KEY;
|
|
5233
|
+
var init_auth_plan = __esm({
|
|
5234
|
+
"../shared/dist/auth-plan.js"() {
|
|
5235
|
+
"use strict";
|
|
5236
|
+
AUTH_TAG_COOKIE_STATE = "@auth-cookie-state";
|
|
5237
|
+
AUTH_TAG_LOGIN_COOKIE = "@auth-login-cookie";
|
|
5238
|
+
AUTH_TAG_LOGIN_BEARER = "@auth-login-bearer";
|
|
5239
|
+
AUTH_TAG_STANDING = "@auth-standing";
|
|
5240
|
+
AUTH_TAG_NONE = "@auth-none";
|
|
5241
|
+
AUTH_TAG_UNSUPPORTED = "@auth-unsupported";
|
|
5242
|
+
AUTH_TYPE_TAGS = [
|
|
5243
|
+
AUTH_TAG_COOKIE_STATE,
|
|
5244
|
+
AUTH_TAG_LOGIN_COOKIE,
|
|
5245
|
+
AUTH_TAG_LOGIN_BEARER,
|
|
5246
|
+
AUTH_TAG_STANDING,
|
|
5247
|
+
AUTH_TAG_NONE,
|
|
5248
|
+
AUTH_TAG_UNSUPPORTED
|
|
5249
|
+
];
|
|
5250
|
+
AUTH_TAG_ALIASES = {
|
|
5251
|
+
"@auth-cookie": AUTH_TAG_COOKIE_STATE,
|
|
5252
|
+
"@auth-bearer": AUTH_TAG_LOGIN_BEARER,
|
|
5253
|
+
"@auth-token": AUTH_TAG_LOGIN_BEARER,
|
|
5254
|
+
"@auth-api-key": AUTH_TAG_STANDING,
|
|
5255
|
+
"@auth-standing-header": AUTH_TAG_STANDING
|
|
5256
|
+
};
|
|
5257
|
+
TEST_USER_ENV_KEYS = ["TEST_USER_EMAIL", "TEST_USER_PASSWORD"];
|
|
5258
|
+
API_AUTH_HEADERS_ENV_KEY = "API_AUTH_HEADERS";
|
|
5259
|
+
}
|
|
5260
|
+
});
|
|
5261
|
+
|
|
4997
5262
|
// ../shared/dist/assertion-strength.js
|
|
5263
|
+
function parseLastActionProofMode(raw) {
|
|
5264
|
+
const v = (raw ?? "").trim().toLowerCase();
|
|
5265
|
+
return v === "on" || v === "shadow" ? v : "off";
|
|
5266
|
+
}
|
|
5267
|
+
function isUrlProof(stmt) {
|
|
5268
|
+
if (URL_PROOF.test(stmt))
|
|
5269
|
+
return true;
|
|
5270
|
+
let calls = 0;
|
|
5271
|
+
for (const open of stmt.matchAll(POLL_OPEN)) {
|
|
5272
|
+
if (++calls > POLL_CALLS_MAX)
|
|
5273
|
+
break;
|
|
5274
|
+
const from = open.index + open[0].length;
|
|
5275
|
+
const args = balancedArguments(stmt, from, MATCHER_ARGS_MAX);
|
|
5276
|
+
if (args === null)
|
|
5277
|
+
continue;
|
|
5278
|
+
const subject = pollSubject(args);
|
|
5279
|
+
if (!POLL_PAGE_URL.test(subject))
|
|
5280
|
+
continue;
|
|
5281
|
+
if (!POLL_PAGE_URL_ONLY.test(subject) || PAGE_VALUE_MATCHER.test(stmt.slice(from + args.length)))
|
|
5282
|
+
return true;
|
|
5283
|
+
}
|
|
5284
|
+
return false;
|
|
5285
|
+
}
|
|
4998
5286
|
function liveExpectStatements(uncommented) {
|
|
4999
5287
|
const masked = maskedOutsideStrings(uncommented).split("\n");
|
|
5000
5288
|
return accumulateStatements(uncommented.split("\n")).filter((s) => EXPECT_STMT.test(s.stmt) && EXPECT_STMT.test(masked[s.startLine - 1] ?? ""));
|
|
5001
5289
|
}
|
|
5002
|
-
function
|
|
5290
|
+
function stripLineComment2(ln) {
|
|
5003
5291
|
let quote = null;
|
|
5004
5292
|
for (let i = 0; i < ln.length; i++) {
|
|
5005
5293
|
const c = ln[i];
|
|
@@ -5022,7 +5310,7 @@ function splitInteractionStatementsWithLines(lines) {
|
|
|
5022
5310
|
let depth = 0;
|
|
5023
5311
|
let scaffold = false;
|
|
5024
5312
|
for (let i = 0; i < lines.length; i++) {
|
|
5025
|
-
const ln =
|
|
5313
|
+
const ln = stripLineComment2(lines[i]).trim();
|
|
5026
5314
|
if (!ln)
|
|
5027
5315
|
continue;
|
|
5028
5316
|
const isContinuation = out.length > 0 && (ln.startsWith(".") || depth > 0 && !scaffold);
|
|
@@ -5046,7 +5334,7 @@ function testBlockIds(lines) {
|
|
|
5046
5334
|
let any = false;
|
|
5047
5335
|
let block = 0;
|
|
5048
5336
|
for (let i = 0; i < lines.length; i++) {
|
|
5049
|
-
const ln =
|
|
5337
|
+
const ln = stripLineComment2(lines[i]).trim();
|
|
5050
5338
|
if (!open) {
|
|
5051
5339
|
if (!TEST_BLOCK_OPEN.test(ln))
|
|
5052
5340
|
continue;
|
|
@@ -5130,7 +5418,7 @@ function expectSubjectSpan(stmt) {
|
|
|
5130
5418
|
}
|
|
5131
5419
|
return null;
|
|
5132
5420
|
}
|
|
5133
|
-
function resolveSubjectBindings(stmts, bindings, blockOfLine) {
|
|
5421
|
+
function resolveSubjectBindings(stmts, bindings, blockOfLine, boundAt) {
|
|
5134
5422
|
if (bindings.length === 0)
|
|
5135
5423
|
return [...stmts];
|
|
5136
5424
|
return stmts.map((item) => {
|
|
@@ -5153,14 +5441,22 @@ function resolveSubjectBindings(stmts, bindings, blockOfLine) {
|
|
|
5153
5441
|
subject = `${lead[1]}${found.expr.replace(/;\s*$/, "")}${subject.slice(lead[0].length)}`;
|
|
5154
5442
|
line = found.line;
|
|
5155
5443
|
}
|
|
5156
|
-
|
|
5444
|
+
const out = { ...item, stmt: `${item.stmt.slice(0, span.from)}${subject}${item.stmt.slice(span.to)}` };
|
|
5445
|
+
if (boundAt !== void 0 && line !== item.startLine)
|
|
5446
|
+
boundAt.set(out, line);
|
|
5447
|
+
return out;
|
|
5157
5448
|
});
|
|
5158
5449
|
}
|
|
5159
5450
|
function stripLoginScaffolding(code) {
|
|
5160
|
-
const
|
|
5451
|
+
const lines = code.split("\n");
|
|
5452
|
+
return keptScaffoldingLines(lines).map((i) => lines[i]).join("\n");
|
|
5453
|
+
}
|
|
5454
|
+
function keptScaffoldingLines(lines) {
|
|
5455
|
+
const kept = [];
|
|
5161
5456
|
let inAuth = false;
|
|
5162
5457
|
let inInbox = false;
|
|
5163
|
-
for (
|
|
5458
|
+
for (let i = 0; i < lines.length; i++) {
|
|
5459
|
+
const ln = lines[i];
|
|
5164
5460
|
if (!inAuth && !inInbox && AUTH_START.test(ln)) {
|
|
5165
5461
|
inAuth = true;
|
|
5166
5462
|
continue;
|
|
@@ -5181,9 +5477,9 @@ function stripLoginScaffolding(code) {
|
|
|
5181
5477
|
}
|
|
5182
5478
|
if (LOGIN_CALL_LINE.test(ln))
|
|
5183
5479
|
continue;
|
|
5184
|
-
|
|
5480
|
+
kept.push(i);
|
|
5185
5481
|
}
|
|
5186
|
-
return
|
|
5482
|
+
return kept;
|
|
5187
5483
|
}
|
|
5188
5484
|
function stripCommentsAndLogs(code) {
|
|
5189
5485
|
return stripCoverageComments(code).split("\n").filter((ln) => !/^\s*console\./.test(ln)).join("\n");
|
|
@@ -5192,7 +5488,7 @@ function detectsErrorBannerAssertion(code) {
|
|
|
5192
5488
|
const cleaned = stripCommentsAndLogs(stripLoginScaffolding(code));
|
|
5193
5489
|
const lines = cleaned.split("\n");
|
|
5194
5490
|
const candidates = [];
|
|
5195
|
-
const positive = (stmt) => !
|
|
5491
|
+
const positive = (stmt) => !claimsAbsence(stmt);
|
|
5196
5492
|
const statements = liveExpectStatements(cleaned).map((s) => s.stmt);
|
|
5197
5493
|
for (const stmt of statements) {
|
|
5198
5494
|
if (positive(stmt))
|
|
@@ -5321,6 +5617,12 @@ function detectsObservedFailureAsExpected(code) {
|
|
|
5321
5617
|
return FAILURE_STATUS_ASSERT.some((re) => re.test(stmt)) || hasFailedListener && FAILED_LIST_ASSERT.test(stmt);
|
|
5322
5618
|
});
|
|
5323
5619
|
}
|
|
5620
|
+
function claimsAbsence(stmt) {
|
|
5621
|
+
const negated = NEGATED_ASSERT_NOT.test(stmt);
|
|
5622
|
+
const bare = negated ? stmt.replace(NOT_BEFORE_MATCHER, ".") : stmt;
|
|
5623
|
+
const absenceMatcher = /\.toBeHidden\s*\(|\.toHaveCount\s*\(\s*0\s*\)/.test(bare) || VALUE_ABSENCE.test(bare) || OPTION_ABSENCE.test(bare);
|
|
5624
|
+
return negated !== absenceMatcher;
|
|
5625
|
+
}
|
|
5324
5626
|
function isVacuousErrorAbsence(stmt, presence) {
|
|
5325
5627
|
if (!ABSENCE_MATCHER.test(stmt))
|
|
5326
5628
|
return false;
|
|
@@ -5329,6 +5631,41 @@ function isVacuousErrorAbsence(stmt, presence) {
|
|
|
5329
5631
|
return false;
|
|
5330
5632
|
return !presence().includes(m[1]);
|
|
5331
5633
|
}
|
|
5634
|
+
function isVacuousAccountAbsence(stmt, shown, account) {
|
|
5635
|
+
if (!claimsAbsence(stmt))
|
|
5636
|
+
return false;
|
|
5637
|
+
if (!ABSENCE_MATCHER.test(stmt)) {
|
|
5638
|
+
const negated = NEGATED_TEXT.exec(stmt);
|
|
5639
|
+
if (!negated)
|
|
5640
|
+
return false;
|
|
5641
|
+
const read = account.exec(stmt.slice(negated.index));
|
|
5642
|
+
return read !== null && !shown().has(read[0]);
|
|
5643
|
+
}
|
|
5644
|
+
const m = account.exec(stmt);
|
|
5645
|
+
if (!m || shown().has(m[0]))
|
|
5646
|
+
return false;
|
|
5647
|
+
for (let at = endOfEnclosingCall(stmt, m.index + m[0].length); at < stmt.length; at = endOfEnclosingCall(stmt, at)) {
|
|
5648
|
+
SCOPING_STEP.lastIndex = at;
|
|
5649
|
+
if (SCOPING_STEP.test(stmt))
|
|
5650
|
+
return false;
|
|
5651
|
+
}
|
|
5652
|
+
return true;
|
|
5653
|
+
}
|
|
5654
|
+
function endOfEnclosingCall(stmt, from) {
|
|
5655
|
+
let depth = 0;
|
|
5656
|
+
for (let i = from; i < stmt.length; i++) {
|
|
5657
|
+
const c = stmt[i];
|
|
5658
|
+
if (c === "'" || c === '"') {
|
|
5659
|
+
for (i++; i < stmt.length && stmt[i] !== c; i++)
|
|
5660
|
+
if (stmt[i] === "\\")
|
|
5661
|
+
i++;
|
|
5662
|
+
} else if (c === "(")
|
|
5663
|
+
depth++;
|
|
5664
|
+
else if (c === ")" && depth-- === 0)
|
|
5665
|
+
return i + 1;
|
|
5666
|
+
}
|
|
5667
|
+
return stmt.length;
|
|
5668
|
+
}
|
|
5332
5669
|
function isBareContainerVisibility(stmt) {
|
|
5333
5670
|
return BARE_CONTAINER_ROLE.test(stmt) && !GET_BY_TEXT.test(stmt) && !HAS_TEXT_FILTER.test(stmt) && !TEXT_CONTENT_MATCHER.test(stmt);
|
|
5334
5671
|
}
|
|
@@ -5336,19 +5673,53 @@ function isChromeTarget(stmt) {
|
|
|
5336
5673
|
return CHROME_LOCATOR.test(stmt) && !CONTENT_ROLE.test(stmt) && !/getByText\s*\(/.test(stmt);
|
|
5337
5674
|
}
|
|
5338
5675
|
function countsAsContentText(stmt) {
|
|
5339
|
-
if (!TEXT_CONTENT_MATCHER.test(stmt))
|
|
5676
|
+
if (!TEXT_CONTENT_MATCHER.test(stmt) || NEGATED_ASSERT.test(stmt))
|
|
5340
5677
|
return false;
|
|
5341
5678
|
if (isSymbolOnlyArgument(stmt, TEXT_CONTENT_MATCHER))
|
|
5342
5679
|
return false;
|
|
5343
|
-
|
|
5680
|
+
return onContentTarget(stmt);
|
|
5681
|
+
}
|
|
5682
|
+
function onContentTarget(stmt) {
|
|
5683
|
+
return !isChromeTarget(stmt) || /getByRole\s*\(\s*['"`]main['"`]/i.test(stmt);
|
|
5684
|
+
}
|
|
5685
|
+
function provesTextRemoved(stmt, presence) {
|
|
5686
|
+
const m = stmt.match(NEGATED_TEXT);
|
|
5687
|
+
if (!m || !onContentTarget(stmt))
|
|
5688
|
+
return false;
|
|
5689
|
+
const text = m[1] ?? m[2];
|
|
5690
|
+
if (text === void 0)
|
|
5344
5691
|
return true;
|
|
5345
|
-
|
|
5692
|
+
if (!/[\p{L}\p{N}]/u.test(text))
|
|
5693
|
+
return false;
|
|
5694
|
+
return !ERROR_TEXT_WORDS.test(text) || presence().includes(text);
|
|
5346
5695
|
}
|
|
5347
5696
|
function isDataContentRole(stmt) {
|
|
5348
5697
|
return CONTENT_ROLE.test(stmt) && !ANNOUNCE_ROLE.test(stmt);
|
|
5349
5698
|
}
|
|
5699
|
+
function destructuresRequest(code) {
|
|
5700
|
+
let readTo = -1;
|
|
5701
|
+
for (const m of code.matchAll(ASYNC_DESTRUCTURE_OPEN)) {
|
|
5702
|
+
const start = m.index + m[0].length;
|
|
5703
|
+
if (start <= readTo)
|
|
5704
|
+
continue;
|
|
5705
|
+
let end = start;
|
|
5706
|
+
while (end < code.length && code[end] !== "}" && code[end] !== ")")
|
|
5707
|
+
end++;
|
|
5708
|
+
readTo = end;
|
|
5709
|
+
if (end === code.length)
|
|
5710
|
+
return false;
|
|
5711
|
+
if (code[end] !== "}" || !REQUEST_WORD.test(code.slice(start, end)))
|
|
5712
|
+
continue;
|
|
5713
|
+
let close = end + 1;
|
|
5714
|
+
while (close < code.length && /\s/.test(code[close]))
|
|
5715
|
+
close++;
|
|
5716
|
+
if (code[close] === ")")
|
|
5717
|
+
return true;
|
|
5718
|
+
}
|
|
5719
|
+
return false;
|
|
5720
|
+
}
|
|
5350
5721
|
function isApiTest(code) {
|
|
5351
|
-
const usesRequestFixture =
|
|
5722
|
+
const usesRequestFixture = destructuresRequest(code) || /\brequest\.(get|post|put|patch|delete|head|fetch)\s*\(/.test(code);
|
|
5352
5723
|
if (!usesRequestFixture)
|
|
5353
5724
|
return false;
|
|
5354
5725
|
const drivesUi = /\bpage\.(getBy\w+|locator|click|fill|goto|waitForURL|reload)\s*\(/.test(code) || /\.(click|fill|check|uncheck|selectOption|pressSequentially)\s*\(/.test(code);
|
|
@@ -5376,7 +5747,7 @@ function roleNames(s) {
|
|
|
5376
5747
|
function interactedControlNames(lines) {
|
|
5377
5748
|
const names = /* @__PURE__ */ new Set();
|
|
5378
5749
|
for (const raw of lines) {
|
|
5379
|
-
const ln =
|
|
5750
|
+
const ln = stripLineComment2(raw);
|
|
5380
5751
|
if (!INTERACT_VERB.test(ln))
|
|
5381
5752
|
continue;
|
|
5382
5753
|
for (const n of roleNames(ln))
|
|
@@ -5572,6 +5943,14 @@ function balancedArguments(code, from, limit = code.length) {
|
|
|
5572
5943
|
}
|
|
5573
5944
|
return null;
|
|
5574
5945
|
}
|
|
5946
|
+
function expectCallTail(stmt) {
|
|
5947
|
+
const at = stmt.search(EXPECT_CALL_RE);
|
|
5948
|
+
if (at < 0)
|
|
5949
|
+
return null;
|
|
5950
|
+
const from = stmt.indexOf("(", at) + 1;
|
|
5951
|
+
const args = balancedArguments(stmt, from, MATCHER_ARGS_MAX);
|
|
5952
|
+
return args === null ? null : stmt.slice(from + args.length).trimStart();
|
|
5953
|
+
}
|
|
5575
5954
|
function matcherArgumentsAreConstant(body, from) {
|
|
5576
5955
|
MATCHER_OPEN.lastIndex = from;
|
|
5577
5956
|
const opened = MATCHER_OPEN.exec(body);
|
|
@@ -5607,32 +5986,70 @@ function hasOnlyConstantAssertions(code) {
|
|
|
5607
5986
|
const { constant, real } = constantAssertions(code);
|
|
5608
5987
|
return constant.length > 0 && real === 0;
|
|
5609
5988
|
}
|
|
5989
|
+
function envValueKey(name) {
|
|
5990
|
+
return `\0env:${name}`;
|
|
5991
|
+
}
|
|
5610
5992
|
function enteredValues(stmts) {
|
|
5611
5993
|
const values = /* @__PURE__ */ new Map();
|
|
5612
5994
|
const navLines = [];
|
|
5995
|
+
const accountConsts = /* @__PURE__ */ new Set();
|
|
5613
5996
|
for (const { stmt, startLine } of stmts) {
|
|
5614
5997
|
if (PAGE_NAV.test(stmt))
|
|
5615
5998
|
navLines.push(startLine);
|
|
5999
|
+
for (const c of stmt.matchAll(ACCOUNT_CONST))
|
|
6000
|
+
accountConsts.add(c[1].replace(/\$/g, "\\$"));
|
|
5616
6001
|
let m;
|
|
5617
6002
|
ENTERED_VALUE.lastIndex = 0;
|
|
5618
|
-
|
|
5619
|
-
const
|
|
5620
|
-
if (!
|
|
5621
|
-
values.set(value, startLine);
|
|
5622
|
-
|
|
6003
|
+
const add = (value) => {
|
|
6004
|
+
const lines = values.get(value);
|
|
6005
|
+
if (!lines)
|
|
6006
|
+
values.set(value, [startLine]);
|
|
6007
|
+
else if (lines[lines.length - 1] !== startLine)
|
|
6008
|
+
lines.push(startLine);
|
|
6009
|
+
};
|
|
6010
|
+
while (m = ENTERED_VALUE.exec(stmt))
|
|
6011
|
+
add((m[1] ?? m[2] ?? m[3]).toLowerCase());
|
|
6012
|
+
ENTERED_ENV.lastIndex = 0;
|
|
6013
|
+
while (m = ENTERED_ENV.exec(stmt))
|
|
6014
|
+
add(envValueKey(m[1] ?? m[2] ?? m[3] ?? m[4]));
|
|
5623
6015
|
}
|
|
5624
|
-
|
|
6016
|
+
const consts = accountConsts.size > 0 ? `|(?<![\\w$.])(?:${[...accountConsts].join("|")})(?![\\w$])` : "";
|
|
6017
|
+
return { values, navLines, account: new RegExp(`${ACCOUNT_ENV}${consts}`) };
|
|
5625
6018
|
}
|
|
5626
|
-
function
|
|
6019
|
+
function countBelow(lines, line) {
|
|
6020
|
+
let lo = 0;
|
|
6021
|
+
let hi = lines.length;
|
|
6022
|
+
while (lo < hi) {
|
|
6023
|
+
const mid = lo + hi >> 1;
|
|
6024
|
+
if (lines[mid] < line)
|
|
6025
|
+
lo = mid + 1;
|
|
6026
|
+
else
|
|
6027
|
+
hi = mid;
|
|
6028
|
+
}
|
|
6029
|
+
return lo;
|
|
6030
|
+
}
|
|
6031
|
+
function enteredLinesOf(stmt, entered) {
|
|
5627
6032
|
const m = stmt.match(/\.toHaveValue\s*\(\s*(['"`])((?:(?!\1).)+)\1/);
|
|
5628
|
-
if (
|
|
6033
|
+
if (m)
|
|
6034
|
+
return entered.values.get(m[2].toLowerCase());
|
|
6035
|
+
const env = stmt.match(HAVE_VALUE_ENV);
|
|
6036
|
+
return env ? entered.values.get(envValueKey(env[1] ?? env[2])) : void 0;
|
|
6037
|
+
}
|
|
6038
|
+
function typedSinceLastNav(stmt, entered, startLine, lines = enteredLinesOf(stmt, entered)) {
|
|
6039
|
+
if (!lines || startLine === void 0)
|
|
5629
6040
|
return false;
|
|
5630
|
-
const
|
|
5631
|
-
if (
|
|
6041
|
+
const before = countBelow(lines, startLine);
|
|
6042
|
+
if (before === 0)
|
|
5632
6043
|
return false;
|
|
5633
|
-
|
|
6044
|
+
return countBelow(entered.navLines, startLine) === countBelow(entered.navLines, lines[before - 1] + 1);
|
|
6045
|
+
}
|
|
6046
|
+
function isEnteredValueEcho(stmt, entered, startLine) {
|
|
6047
|
+
const lines = enteredLinesOf(stmt, entered);
|
|
6048
|
+
if (!lines)
|
|
6049
|
+
return false;
|
|
6050
|
+
if (startLine === void 0 || lines[0] >= startLine)
|
|
5634
6051
|
return true;
|
|
5635
|
-
return
|
|
6052
|
+
return typedSinceLastNav(stmt, entered, startLine, lines);
|
|
5636
6053
|
}
|
|
5637
6054
|
function isAnyValueMatcher(stmt) {
|
|
5638
6055
|
if (/\.not\.toHaveValue\s*\(\s*(?:['"`]{2}|\/\^?\$?\/)/.test(stmt))
|
|
@@ -5722,6 +6139,49 @@ function hasAttributeTransition(stmts) {
|
|
|
5722
6139
|
return true;
|
|
5723
6140
|
return false;
|
|
5724
6141
|
}
|
|
6142
|
+
function comparesWithItself(subject, tail) {
|
|
6143
|
+
const m = SELF_COMPARE_MATCHER.exec(tail);
|
|
6144
|
+
if (!m)
|
|
6145
|
+
return false;
|
|
6146
|
+
const args = balancedArguments(tail, m[0].length, MATCHER_ARGS_MAX);
|
|
6147
|
+
if (args === null)
|
|
6148
|
+
return false;
|
|
6149
|
+
const squash2 = (x) => x.replace(/^\s*await\s+/, "").replace(/\s+/g, "");
|
|
6150
|
+
const expected = squash2(args);
|
|
6151
|
+
return expected === squash2(m[1] === "toHaveCount" ? `${subject}.count()` : subject);
|
|
6152
|
+
}
|
|
6153
|
+
function asPlainAssertion(s) {
|
|
6154
|
+
return plainAssertion(s, false);
|
|
6155
|
+
}
|
|
6156
|
+
function plainAssertion(s, keepSelfCompare) {
|
|
6157
|
+
const { stmt } = s;
|
|
6158
|
+
if (COUNT_AT_LEAST_ZERO.test(stmt))
|
|
6159
|
+
return [];
|
|
6160
|
+
const subject = extractExpectSubject(stmt);
|
|
6161
|
+
if (subject === null)
|
|
6162
|
+
return [s];
|
|
6163
|
+
const plain = subject.replace(/^await\s+/, "").trim();
|
|
6164
|
+
const subjectAt = stmt.indexOf(subject, stmt.search(EXPECT_CALL_RE));
|
|
6165
|
+
const tail = (stmt.startsWith("expect.poll", stmt.search(EXPECT_CALL_RE)) ? expectCallTail(stmt) : null) ?? stmt.slice(subjectAt + subject.length).trimStart();
|
|
6166
|
+
const as = (rewritten) => [{ ...s, stmt: rewritten }];
|
|
6167
|
+
if (LOCATOR_EXPR.test(plain) && !LOCATOR_VALUE_READ.test(plain) && ALWAYS_TRUE_ON_OBJECT.test(tail))
|
|
6168
|
+
return [];
|
|
6169
|
+
if (DEFINED_VALUE_READ.test(plain) && ALWAYS_DEFINED.test(tail))
|
|
6170
|
+
return [];
|
|
6171
|
+
if (!keepSelfCompare && comparesWithItself(plain, tail))
|
|
6172
|
+
return [];
|
|
6173
|
+
const visible = /^(.+)\.\s*isVisible\s*\(\s*\)$/s.exec(plain);
|
|
6174
|
+
if (visible && IS_VISIBLE_TRUE.test(tail))
|
|
6175
|
+
return as(`await expect(${visible[1]}).toBeVisible();`);
|
|
6176
|
+
if (/^page\s*\.\s*(?:url|title)\s*\(\s*\)$/.test(plain) && PAGE_VALUE_MATCHER.test(tail)) {
|
|
6177
|
+
const matcher = plain.includes("url") ? "toHaveURL" : "toHaveTitle";
|
|
6178
|
+
return as(`await expect(page)${tail.replace(/^\)\s*\.\s*\w+\s*\(/, `.${matcher}(`)}`);
|
|
6179
|
+
}
|
|
6180
|
+
return [s];
|
|
6181
|
+
}
|
|
6182
|
+
function assertionStrengthHoistedReadsOn(value = typeof process !== "undefined" ? process.env?.ASSERTION_STRENGTH_HOISTED_READS : void 0) {
|
|
6183
|
+
return /^(1|true|on)$/i.test((value ?? "").trim());
|
|
6184
|
+
}
|
|
5725
6185
|
function creditsOutcomeAlways(stmt, entered, startLine) {
|
|
5726
6186
|
if (!OUTCOME_ALWAYS.some((re) => re.test(stmt)))
|
|
5727
6187
|
return false;
|
|
@@ -5754,6 +6214,22 @@ function successNoticeQuote(stmt) {
|
|
|
5754
6214
|
}
|
|
5755
6215
|
return null;
|
|
5756
6216
|
}
|
|
6217
|
+
function namedPagePath(arg) {
|
|
6218
|
+
const text = arg.trim();
|
|
6219
|
+
let path;
|
|
6220
|
+
const quoted = /^(['"`])([^'"`]*)\1$/.exec(text);
|
|
6221
|
+
const regex = quoted ? null : /^\/((?:\\.|[^/\\])+)\/[a-z]*$/.exec(text);
|
|
6222
|
+
if (quoted) {
|
|
6223
|
+
path = quoted[2].replace(/^\$\{[^}]*\}/, "").replace(/^\*\*/, "").replace(/^[a-z][a-z\d+.-]*:\/\/[^/]*/i, "").replace(/\*\*$/, "");
|
|
6224
|
+
} else if (regex) {
|
|
6225
|
+
path = regex[1].replace(/^\^/, "").replace(/^\.\*/, "").replace(/\$$/, "").replace(/\\([/.-])/g, "$1");
|
|
6226
|
+
} else {
|
|
6227
|
+
return null;
|
|
6228
|
+
}
|
|
6229
|
+
if (!PLAIN_PAGE_PATH.test(path))
|
|
6230
|
+
return null;
|
|
6231
|
+
return path.length > 1 && path.endsWith("/") ? path.slice(0, -1) : path;
|
|
6232
|
+
}
|
|
5757
6233
|
function provesStoredControlValue(stmt, startLine, persistence) {
|
|
5758
6234
|
if (!persistence || startLine === void 0)
|
|
5759
6235
|
return false;
|
|
@@ -5780,7 +6256,64 @@ function reopenedAfterSave(startLine, persistence, reopens = persistence.reloadL
|
|
|
5780
6256
|
}
|
|
5781
6257
|
return lo < reopens.length && reopens[lo] < startLine;
|
|
5782
6258
|
}
|
|
5783
|
-
function
|
|
6259
|
+
function actsBetween(withLines, from, to) {
|
|
6260
|
+
let acts = actLinesOf.get(withLines);
|
|
6261
|
+
if (acts === void 0) {
|
|
6262
|
+
acts = withLines.filter((s) => ACTS.test(s.stmt)).map((s) => s.startLine).sort((a, b) => a - b);
|
|
6263
|
+
actLinesOf.set(withLines, acts);
|
|
6264
|
+
}
|
|
6265
|
+
let lo = 0;
|
|
6266
|
+
let hi = acts.length;
|
|
6267
|
+
while (lo < hi) {
|
|
6268
|
+
const mid = lo + hi >> 1;
|
|
6269
|
+
if (acts[mid] <= from)
|
|
6270
|
+
lo = mid + 1;
|
|
6271
|
+
else
|
|
6272
|
+
hi = mid;
|
|
6273
|
+
}
|
|
6274
|
+
return lo < acts.length && acts[lo] < to;
|
|
6275
|
+
}
|
|
6276
|
+
function signInProofWindow(stmts, withLines, lines, blockIds) {
|
|
6277
|
+
if (blockIds === null)
|
|
6278
|
+
return void 0;
|
|
6279
|
+
const testBodies2 = /* @__PURE__ */ new Set();
|
|
6280
|
+
for (let i = 0; i < lines.length; i++) {
|
|
6281
|
+
const id = blockIds[i];
|
|
6282
|
+
if (id > 0 && (i === 0 || blockIds[i - 1] !== id) && TEST_BODY_OPEN.test(stripLineComment2(lines[i]).trim()))
|
|
6283
|
+
testBodies2.add(id);
|
|
6284
|
+
}
|
|
6285
|
+
const blockOf = (i) => blockIds[withLines[i].startLine - 1];
|
|
6286
|
+
const passwordAt = stmts.findIndex((stmt, i) => testBodies2.has(blockOf(i)) && ENV_PASSWORD_ENTRY.test(stmt));
|
|
6287
|
+
if (passwordAt < 0)
|
|
6288
|
+
return void 0;
|
|
6289
|
+
const body = blockOf(passwordAt);
|
|
6290
|
+
let clickAt = passwordAt + 1;
|
|
6291
|
+
while (clickAt < stmts.length && FORM_INPUT.test(stmts[clickAt]) && !ANY_CLICK.test(stmts[clickAt]))
|
|
6292
|
+
clickAt++;
|
|
6293
|
+
if (clickAt >= stmts.length || blockOf(clickAt) !== body || !ANY_CLICK.test(stmts[clickAt]) || NAV_LOCATOR.test(stmts[clickAt]))
|
|
6294
|
+
return void 0;
|
|
6295
|
+
let end = clickAt + 1;
|
|
6296
|
+
for (; end < stmts.length && blockOf(end) === body; end++) {
|
|
6297
|
+
if (CLOSERS_ONLY.test(stmts[end]))
|
|
6298
|
+
continue;
|
|
6299
|
+
if (!ASSERT_OR_WAIT.test(stmts[end]) || ACTS.test(stmts[end]))
|
|
6300
|
+
return void 0;
|
|
6301
|
+
}
|
|
6302
|
+
return { after: withLines[clickAt].startLine, until: end < stmts.length ? withLines[end].startLine : Number.POSITIVE_INFINITY };
|
|
6303
|
+
}
|
|
6304
|
+
function isAccountControlCheck(stmt) {
|
|
6305
|
+
if (!VISIBILITY_MATCHER.test(stmt) || NEGATED_ASSERT.test(stmt) || OPTION_ABSENCE.test(stmt))
|
|
6306
|
+
return false;
|
|
6307
|
+
const role = ACCOUNT_CONTROL_ROLE.exec(stmt);
|
|
6308
|
+
if (!role)
|
|
6309
|
+
return false;
|
|
6310
|
+
const rest = stmt.slice(role.index + role[0].length, role.index + role[0].length + 300);
|
|
6311
|
+
const close = rest.indexOf("}");
|
|
6312
|
+
const options = close < 0 ? rest : rest.slice(0, close);
|
|
6313
|
+
const name = options.match(/(?:^|[\s,])name\s*:\s*/);
|
|
6314
|
+
return name !== null && ACCOUNT_EMAIL_ENV.test(options.slice(name.index + name[0].length));
|
|
6315
|
+
}
|
|
6316
|
+
function classifyOutcomeProof(stmts, mutated, reloaded, clicked, entered, firstMutLine, persistence, signedIn) {
|
|
5784
6317
|
if (mutated && hasAttributeTransition(stmts))
|
|
5785
6318
|
return { kind: "durable" };
|
|
5786
6319
|
const notices = [];
|
|
@@ -5790,11 +6323,25 @@ function classifyOutcomeProof(stmts, mutated, reloaded, clicked, entered, firstM
|
|
|
5790
6323
|
};
|
|
5791
6324
|
let presenceText;
|
|
5792
6325
|
const presence = () => presenceText ??= stmts.filter((o) => !ABSENCE_MATCHER.test(o.stmt) && !NEGATED_ASSERT.test(o.stmt)).map((o) => o.stmt).join("\n");
|
|
6326
|
+
let shownAccounts;
|
|
6327
|
+
const shown = () => {
|
|
6328
|
+
if (shownAccounts)
|
|
6329
|
+
return shownAccounts;
|
|
6330
|
+
const ref = new RegExp(entered.account.source, "g");
|
|
6331
|
+
return shownAccounts = new Set(stmts.filter((o) => !claimsAbsence(o.stmt)).flatMap((o) => Array.from(o.stmt.matchAll(ref), (m) => m[0])));
|
|
6332
|
+
};
|
|
5793
6333
|
for (const { stmt, startLine } of stmts) {
|
|
5794
6334
|
if (firstMutLine !== void 0 && startLine !== void 0 && startLine < firstMutLine)
|
|
5795
6335
|
continue;
|
|
5796
6336
|
if (isVacuousErrorAbsence(stmt, presence))
|
|
5797
6337
|
continue;
|
|
6338
|
+
if (COUNT_ONE.test(stmt) && quotedAssertedStrings(stmt).some((t) => clicked.has(t.toLowerCase())))
|
|
6339
|
+
continue;
|
|
6340
|
+
if (isVacuousAccountAbsence(stmt, shown, entered.account))
|
|
6341
|
+
continue;
|
|
6342
|
+
if (signedIn && startLine !== void 0 && startLine > signedIn.after && startLine < signedIn.until && isAccountControlCheck(stmt)) {
|
|
6343
|
+
return { kind: "durable" };
|
|
6344
|
+
}
|
|
5798
6345
|
if (creditsOutcomeAlways(stmt, entered, startLine))
|
|
5799
6346
|
return { kind: "durable" };
|
|
5800
6347
|
if (mutated && !NEGATED_ASSERT.test(stmt) && MESSAGE_SURFACE.test(stmt) && OUTCOME_TEXT.test(assertedText(stmt))) {
|
|
@@ -5804,12 +6351,15 @@ function classifyOutcomeProof(stmts, mutated, reloaded, clicked, entered, firstM
|
|
|
5804
6351
|
else
|
|
5805
6352
|
return { kind: "durable" };
|
|
5806
6353
|
}
|
|
5807
|
-
|
|
6354
|
+
const retyped = typedSinceLastNav(stmt, entered, startLine);
|
|
6355
|
+
if (!retyped && STATE_MATCHER.test(stmt) && (persistence && startLine !== void 0 ? reopenedAfterSave(startLine, persistence, persistence.reloadCalls) : reloaded))
|
|
5808
6356
|
return { kind: "durable" };
|
|
5809
|
-
if (provesStoredControlValue(stmt, startLine, persistence))
|
|
6357
|
+
if (!retyped && provesStoredControlValue(stmt, startLine, persistence))
|
|
5810
6358
|
return { kind: "durable" };
|
|
5811
6359
|
if (countsAsContentText(stmt))
|
|
5812
6360
|
return { kind: "durable" };
|
|
6361
|
+
if (provesTextRemoved(stmt, presence))
|
|
6362
|
+
return { kind: "durable" };
|
|
5813
6363
|
if (mutated && VISIBILITY_MATCHER.test(stmt)) {
|
|
5814
6364
|
const revealsText = GET_BY_TEXT.test(stmt) || HAS_TEXT_FILTER.test(stmt) || CONTENT_ROLE.test(stmt);
|
|
5815
6365
|
if (revealsText && !isChromeTarget(stmt)) {
|
|
@@ -5873,10 +6423,57 @@ function noticeOnlyExemption(clicked, noticeText, interactionStmts) {
|
|
|
5873
6423
|
function detectOutcomeProof(stmts, mutated, reloaded, clicked, entered, firstMutLine) {
|
|
5874
6424
|
return classifyOutcomeProof(stmts, mutated, reloaded, clicked, entered, firstMutLine).kind !== "none";
|
|
5875
6425
|
}
|
|
6426
|
+
function clickLabel(stmt) {
|
|
6427
|
+
let label = null;
|
|
6428
|
+
for (const m of stmt.matchAll(CONTROL_LABEL))
|
|
6429
|
+
label = m[2] ?? m[3] ?? null;
|
|
6430
|
+
return label;
|
|
6431
|
+
}
|
|
6432
|
+
function teardownLines(lines) {
|
|
6433
|
+
const out = new Array(lines.length).fill(false);
|
|
6434
|
+
let open = false;
|
|
6435
|
+
let depth = 0;
|
|
6436
|
+
for (let i = 0; i < lines.length; i++) {
|
|
6437
|
+
const ln = stripLineComment2(lines[i]).trim();
|
|
6438
|
+
let from = 0;
|
|
6439
|
+
if (!open) {
|
|
6440
|
+
const m = TEARDOWN_OPEN.exec(ln);
|
|
6441
|
+
if (!m)
|
|
6442
|
+
continue;
|
|
6443
|
+
open = true;
|
|
6444
|
+
depth = 0;
|
|
6445
|
+
from = m.index;
|
|
6446
|
+
}
|
|
6447
|
+
out[i] = true;
|
|
6448
|
+
let closed = false;
|
|
6449
|
+
let quote = null;
|
|
6450
|
+
for (let k = from; k < ln.length; k++) {
|
|
6451
|
+
const ch = ln[k];
|
|
6452
|
+
if (quote !== null) {
|
|
6453
|
+
if (ch === "\\")
|
|
6454
|
+
k++;
|
|
6455
|
+
else if (ch === quote)
|
|
6456
|
+
quote = null;
|
|
6457
|
+
continue;
|
|
6458
|
+
}
|
|
6459
|
+
if (ch === "'" || ch === '"' || ch === "`")
|
|
6460
|
+
quote = ch;
|
|
6461
|
+
else if (ch === "{")
|
|
6462
|
+
depth++;
|
|
6463
|
+
else if (ch === "}") {
|
|
6464
|
+
depth--;
|
|
6465
|
+
closed = true;
|
|
6466
|
+
}
|
|
6467
|
+
}
|
|
6468
|
+
if (depth <= 0 && closed)
|
|
6469
|
+
open = false;
|
|
6470
|
+
}
|
|
6471
|
+
return out;
|
|
6472
|
+
}
|
|
5876
6473
|
function validateAssertionStrength(code, opts = {}) {
|
|
5877
6474
|
const body = stripLoginScaffolding(code);
|
|
5878
6475
|
const lines = body.split("\n");
|
|
5879
|
-
const stmts = liveExpectStatements(stripCoverageComments(body));
|
|
6476
|
+
const stmts = liveExpectStatements(stripCoverageComments(body)).flatMap(asPlainAssertion);
|
|
5880
6477
|
const base2 = (extra) => ({
|
|
5881
6478
|
apiTest: false,
|
|
5882
6479
|
interacted: false,
|
|
@@ -5916,7 +6513,13 @@ function validateAssertionStrength(code, opts = {}) {
|
|
|
5916
6513
|
const interactionStmts = inlineLocatorBindings(interactionStmtsRaw);
|
|
5917
6514
|
const blockIds = testBlockIds(lines);
|
|
5918
6515
|
const blockOfLine = (line) => blockIds?.[line - 1] ?? 0;
|
|
5919
|
-
const
|
|
6516
|
+
const hoistedReads = assertionStrengthHoistedReadsOn();
|
|
6517
|
+
const boundAt = hoistedReads ? /* @__PURE__ */ new Map() : void 0;
|
|
6518
|
+
const resolved = resolveSubjectBindings(stmts, scopedLocatorBindings(interactionStmtsWithLines, blockOfLine), blockOfLine, boundAt);
|
|
6519
|
+
const checks = boundAt === void 0 ? resolved : resolved.flatMap((s) => {
|
|
6520
|
+
const from = boundAt.get(s);
|
|
6521
|
+
return plainAssertion(s, from !== void 0 && actsBetween(interactionStmtsWithLines, from, s.startLine));
|
|
6522
|
+
});
|
|
5920
6523
|
const insideTest = linesInsideTestBlocks(lines);
|
|
5921
6524
|
const inTestOrder = (i) => insideTest === null || insideTest[interactionStmtsWithLines[i].startLine - 1] === true;
|
|
5922
6525
|
let mutated = opts.interactionPerformed === true;
|
|
@@ -5926,9 +6529,34 @@ function validateAssertionStrength(code, opts = {}) {
|
|
|
5926
6529
|
let navClicks = 0;
|
|
5927
6530
|
const persistence = { mutLines: [], reloadLines: [], reloadCalls: [] };
|
|
5928
6531
|
const openedPages = /* @__PURE__ */ new Set();
|
|
6532
|
+
let checkedPage = null;
|
|
6533
|
+
const savedOnCheckedPage = /* @__PURE__ */ new Set();
|
|
6534
|
+
const saveAt = (i, at) => {
|
|
6535
|
+
lastMutIdx = i;
|
|
6536
|
+
if (!inTestOrder(i))
|
|
6537
|
+
return;
|
|
6538
|
+
persistence.mutLines.push(at);
|
|
6539
|
+
if (firstMutIdx < 0)
|
|
6540
|
+
firstMutIdx = i;
|
|
6541
|
+
if (checkedPage !== null)
|
|
6542
|
+
savedOnCheckedPage.add(checkedPage);
|
|
6543
|
+
};
|
|
6544
|
+
const lastActionProof = opts.lastActionProof ?? "off";
|
|
6545
|
+
const teardown = lastActionProof === "off" ? null : teardownLines(lines);
|
|
6546
|
+
const acts = { last: null, prev: null, count: 0 };
|
|
6547
|
+
const noteAct = (i, at, kind) => {
|
|
6548
|
+
if (teardown === null || teardown[at - 1] || !inTestOrder(i))
|
|
6549
|
+
return;
|
|
6550
|
+
if (kind !== "enter")
|
|
6551
|
+
acts.count++;
|
|
6552
|
+
acts.prev = acts.last;
|
|
6553
|
+
acts.last = { i, kind };
|
|
6554
|
+
};
|
|
5929
6555
|
for (let i = 0; i < interactionStmts.length; i++) {
|
|
5930
6556
|
const ln = interactionStmts[i];
|
|
5931
6557
|
const at = interactionStmtsWithLines[i].startLine;
|
|
6558
|
+
if (teardown !== null && acts.last?.kind === "fill" && ENTER_PRESS.test(ln))
|
|
6559
|
+
noteAct(i, at, "enter");
|
|
5932
6560
|
if (RELOAD.test(ln)) {
|
|
5933
6561
|
persistence.reloadLines.push(at);
|
|
5934
6562
|
persistence.reloadCalls.push(at);
|
|
@@ -5936,32 +6564,31 @@ function validateAssertionStrength(code, opts = {}) {
|
|
|
5936
6564
|
const opened = ln.match(/\.goto\s*\(\s*([^,)]+)/);
|
|
5937
6565
|
if (opened) {
|
|
5938
6566
|
const page = opened[1].trim();
|
|
5939
|
-
|
|
6567
|
+
const path = namedPagePath(page);
|
|
6568
|
+
if (openedPages.has(page) || path !== null && savedOnCheckedPage.has(path))
|
|
5940
6569
|
persistence.reloadLines.push(at);
|
|
5941
|
-
|
|
5942
|
-
|
|
5943
|
-
}
|
|
6570
|
+
openedPages.add(page);
|
|
6571
|
+
checkedPage = null;
|
|
6572
|
+
}
|
|
6573
|
+
if (/\bpage\s*\.\s*go(?:Back|Forward)\s*\(/.test(ln))
|
|
6574
|
+
checkedPage = null;
|
|
6575
|
+
const checked = URL_CHECK_ARG.exec(ln);
|
|
6576
|
+
if (checked)
|
|
6577
|
+
checkedPage = namedPagePath(checked[1] ?? checked[2]);
|
|
5944
6578
|
if (FORM_INPUT.test(ln)) {
|
|
5945
6579
|
mutated = true;
|
|
5946
|
-
|
|
5947
|
-
|
|
5948
|
-
persistence.mutLines.push(at);
|
|
5949
|
-
if (firstMutIdx < 0)
|
|
5950
|
-
firstMutIdx = i;
|
|
5951
|
-
}
|
|
6580
|
+
saveAt(i, at);
|
|
6581
|
+
noteAct(i, at, "fill");
|
|
5952
6582
|
continue;
|
|
5953
6583
|
}
|
|
5954
6584
|
if (ANY_CLICK.test(ln)) {
|
|
5955
6585
|
if (NAV_LOCATOR.test(ln)) {
|
|
5956
6586
|
navClicks++;
|
|
6587
|
+
checkedPage = null;
|
|
5957
6588
|
} else {
|
|
5958
6589
|
mutated = true;
|
|
5959
|
-
|
|
5960
|
-
|
|
5961
|
-
persistence.mutLines.push(at);
|
|
5962
|
-
if (firstMutIdx < 0)
|
|
5963
|
-
firstMutIdx = i;
|
|
5964
|
-
}
|
|
6590
|
+
saveAt(i, at);
|
|
6591
|
+
noteAct(i, at, "click");
|
|
5965
6592
|
}
|
|
5966
6593
|
}
|
|
5967
6594
|
}
|
|
@@ -5994,9 +6621,28 @@ function validateAssertionStrength(code, opts = {}) {
|
|
|
5994
6621
|
};
|
|
5995
6622
|
}
|
|
5996
6623
|
const orderedPersistence = firstMutLine !== void 0 ? persistence : void 0;
|
|
5997
|
-
const
|
|
6624
|
+
const signedIn = signInProofWindow(interactionStmts, interactionStmtsWithLines, lines, blockIds);
|
|
6625
|
+
const proof = classifyOutcomeProof(checks, mutated, reloaded, clickedControlNames(interactionStmts), entered, firstMutLine, orderedPersistence, signedIn);
|
|
5998
6626
|
if (proof.kind === "durable") {
|
|
5999
6627
|
const signals2 = base2({ interacted, mutated, navOnly, hasOutcomeProof: true, terminal });
|
|
6628
|
+
const { last, prev } = acts;
|
|
6629
|
+
if (teardown !== null && last !== null && acts.count >= 2 && last.kind !== "fill" && (last.kind === "enter" || isCommitClick(interactionStmts[last.i]) && !(prev?.kind === "click" && isDestructiveClick(interactionStmts[prev.i])))) {
|
|
6630
|
+
const lastLine = interactionStmtsWithLines[last.i].startLine;
|
|
6631
|
+
const clicked = clickedControlNames(interactionStmts);
|
|
6632
|
+
const after = classifyOutcomeProof(checks, mutated, reloaded, clicked, entered, lastLine, orderedPersistence, signedIn);
|
|
6633
|
+
const unproven = after.kind === "none" || after.kind === "notice" && noticeOnlyExemption(clicked, after.noticeText, interactionStmts) === null;
|
|
6634
|
+
if (unproven && lastActionProof === "on") {
|
|
6635
|
+
const line = (keptScaffoldingLines(code.split("\n"))[lastLine - 1] ?? lastLine - 1) + 1;
|
|
6636
|
+
return {
|
|
6637
|
+
ok: false,
|
|
6638
|
+
severity: "block",
|
|
6639
|
+
reason: `the last save (line ${line}) has no outcome check after it (the earlier check proves only the earlier action)`,
|
|
6640
|
+
signals: { ...signals2, lastActionUnproven: true }
|
|
6641
|
+
};
|
|
6642
|
+
}
|
|
6643
|
+
if (unproven)
|
|
6644
|
+
signals2.lastActionUnproven = true;
|
|
6645
|
+
}
|
|
6000
6646
|
return { ok: true, severity: "none", reason: "proves an outcome (count / diff / value / revealed content)", signals: signals2 };
|
|
6001
6647
|
}
|
|
6002
6648
|
if (mutated && proof.kind === "notice") {
|
|
@@ -6045,7 +6691,7 @@ function validateAssertionStrength(code, opts = {}) {
|
|
|
6045
6691
|
signals
|
|
6046
6692
|
};
|
|
6047
6693
|
}
|
|
6048
|
-
const urlProofAfter = interactionStmts.some((s, i) => i > lastMutIdx &&
|
|
6694
|
+
const urlProofAfter = interactionStmts.some((s, i) => i > lastMutIdx && isUrlProof(s));
|
|
6049
6695
|
if (urlProofAfter) {
|
|
6050
6696
|
return {
|
|
6051
6697
|
ok: false,
|
|
@@ -6082,7 +6728,7 @@ function validateAssertionStrength(code, opts = {}) {
|
|
|
6082
6728
|
signals
|
|
6083
6729
|
};
|
|
6084
6730
|
}
|
|
6085
|
-
var ERROR_BANNER, OUTCOME_ALWAYS, FORM_INPUT, ANY_CLICK, NAV_LOCATOR, URL_PROOF, 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, 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, ERROR_TEXT_WORDS, TEXT_REGEX_ARG, 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, IN_FLIGHT_TEXT, FORM_CONTROL_LOCATOR, CONTROL_VALUE_MATCHER, COPY_CONTROL, COPIED_NOTICE, SEND_CONTROL, SEND_NOTICE, MARK_READ_CONTROL, APP_DATA_FIELD;
|
|
6731
|
+
var ERROR_BANNER, OUTCOME_ALWAYS, FORM_INPUT, ANY_CLICK, NAV_LOCATOR, URL_PROOF, POLL_OPEN, POLL_PAGE_URL, POLL_PAGE_URL_ONLY, POLL_CALLS_MAX, RELOAD, EXPECT_STMT, BLOCK_SCAFFOLD2, TEST_BLOCK_OPEN, LOCATOR_BINDING, EXPECT_OPEN, STATE_MATCHER, CHROME_LOCATOR, CONTENT_ROLE, GET_BY_TEXT, BARE_CONTAINER_ROLE, HAS_TEXT_FILTER, OUTCOME_TEXT, SUCCESS_NOTICE_TEXT, NOT_A_SUCCESS_NOTICE, ANNOUNCE_ROLE, MESSAGE_SURFACE, TEXT_CONTENT_MATCHER, VISIBILITY_MATCHER, ADDITIVE_AFFORDANCE, EXCLUDE_AFFORDANCE, EMPTY_STATE_TEXT, AUTH_START, AUTH_END, LOGIN_CALL_LINE, INBOX_START, INBOX_END, NEGATED_ASSERT, NEGATED_ASSERT_NOT, CONST_CHAIN_MAX_DEPTH, TYPED_INPUT, TITLE_LINE, FAILURE_STATUS_ASSERT, FAILED_LIST_ASSERT, ENDPOINT_DOWN, NAVIGATION_RESPONSE_VAR, DIRECT_REQUEST_VAR, CATCH_ALL_PATTERN, GUARD_HANDLER, ABSENCE_MATCHER, VALUE_ABSENCE, OPTION_ABSENCE, NOT_BEFORE_MATCHER, ERROR_TEXT_WORDS, TEXT_REGEX_ARG, SCOPING_STEP, NEGATED_TEXT, ASYNC_DESTRUCTURE_OPEN, REQUEST_WORD, ROLE_NAME_RE, INTERACT_VERB, ROLE_SIG_RE, TRANSIENT_CONTROL_ROLES, POLL_CALLBACK, CONSTANT_WORD, CONSTANT_GLUE_CHARS, WORD_CHAR, MATCHER_ARGS_MAX, MATCHER_OPEN, ENTERED_VALUE, PAGE_NAV, ENV_READ, ENTERED_ENV, HAVE_VALUE_ENV, ACCOUNT_KEYS, ACCOUNT_ENV, ACCOUNT_CONST, IN_FLIGHT_TEXT, LOCATOR_VALUE_READ, LOCATOR_EXPR, ALWAYS_TRUE_ON_OBJECT, COUNT_AT_LEAST_ZERO, DEFINED_VALUE_READ, ALWAYS_DEFINED, SELF_COMPARE_MATCHER, IS_VISIBLE_TRUE, PAGE_VALUE_MATCHER, COUNT_ONE, URL_CHECK_ARG, PLAIN_PAGE_PATH, FORM_CONTROL_LOCATOR, CONTROL_VALUE_MATCHER, ACCOUNT_EMAIL_ENV, ENV_PASSWORD_ENTRY, ACCOUNT_CONTROL_ROLE, ASSERT_OR_WAIT, ACTS, actLinesOf, CLOSERS_ONLY, TEST_BODY_OPEN, COPY_CONTROL, COPIED_NOTICE, SEND_CONTROL, SEND_NOTICE, MARK_READ_CONTROL, APP_DATA_FIELD, COMMIT_CONTROL, NOT_COMMIT_CONTROL, DESTRUCTIVE_CONTROL, ENTER_PRESS, CONTROL_LABEL, isCommitClick, isDestructiveClick, TEARDOWN_OPEN;
|
|
6086
6732
|
var init_assertion_strength = __esm({
|
|
6087
6733
|
"../shared/dist/assertion-strength.js"() {
|
|
6088
6734
|
"use strict";
|
|
@@ -6091,6 +6737,7 @@ var init_assertion_strength = __esm({
|
|
|
6091
6737
|
init_assertion_proof();
|
|
6092
6738
|
init_auth_handoff_coverage();
|
|
6093
6739
|
init_expect_call();
|
|
6740
|
+
init_auth_plan();
|
|
6094
6741
|
ERROR_BANNER = /(client-side exception|application error|something (?:went wrong|broke)|unhandled (?:exception|error|rejection)|runtime error|error boundary|an unexpected error|internal server error|\b5\d\d\s+(?:internal\s+)?server error|stack trace|chunkloaderror|hydration failed|maximum update depth exceeded)/i;
|
|
6095
6742
|
OUTCOME_ALWAYS = [
|
|
6096
6743
|
/\.toHaveCount\s*\(/,
|
|
@@ -6107,8 +6754,8 @@ var init_assertion_strength = __esm({
|
|
|
6107
6754
|
// form field value
|
|
6108
6755
|
/\.toMatch\s*\(/,
|
|
6109
6756
|
// string/regex content match (e.g. textContent() vs /[2-9] selected/)
|
|
6110
|
-
/\.toBeDisabled\s*\(/,
|
|
6111
|
-
// a control became DISABLED — a gating/validation outcome
|
|
6757
|
+
/\.(?<!\.not\s*\.)\s*toBeDisabled\s*\(/,
|
|
6758
|
+
// a control became DISABLED — a gating/validation outcome (not.toBeDisabled is the toBeEnabled twin)
|
|
6112
6759
|
/\.not\.toBeVisible\s*\(/,
|
|
6113
6760
|
/\.toBeHidden\s*\(/
|
|
6114
6761
|
// something the action removed/closed (disappearance proof)
|
|
@@ -6116,12 +6763,16 @@ var init_assertion_strength = __esm({
|
|
|
6116
6763
|
FORM_INPUT = /\.(fill|selectOption|setInputFiles|check|uncheck|setChecked|pressSequentially)\s*\(|keyboard\.type\s*\(/;
|
|
6117
6764
|
ANY_CLICK = /\.click\s*\(/;
|
|
6118
6765
|
NAV_LOCATOR = /getByRole\s*\(\s*['"`](?:link|menuitem)['"`]/i;
|
|
6119
|
-
URL_PROOF = /\.toHaveURL\s*\(|\.waitForURL\s*\(/;
|
|
6766
|
+
URL_PROOF = /\.toHaveURL\s*\(|\.waitForURL\s*\(|\bexpect\s*\(\s*(?:await\s+)?page\s*\.\s*url\s*\(\s*\)\s*\)\s*\.\s*(?:toBe|toEqual|toStrictEqual|toMatch|toContain)\s*\(/;
|
|
6767
|
+
POLL_OPEN = /\bexpect\s*\.\s*poll\s*\(/g;
|
|
6768
|
+
POLL_PAGE_URL = /^page\s*\.\s*url\s*\(\s*\)/;
|
|
6769
|
+
POLL_PAGE_URL_ONLY = /^page\s*\.\s*url\s*\(\s*\)$/;
|
|
6770
|
+
POLL_CALLS_MAX = 8;
|
|
6120
6771
|
RELOAD = /\.reload\s*\(/;
|
|
6121
6772
|
EXPECT_STMT = /\bexpect\s*[.(]/;
|
|
6122
6773
|
BLOCK_SCAFFOLD2 = /=>|\b(?:test|describe|it|beforeEach|afterEach|beforeAll|afterAll|function)\b/;
|
|
6123
6774
|
TEST_BLOCK_OPEN = /^(?:test|it)(?:\.(?:only|skip|fixme|slow|serial|describe\.serial))?\s*\(|^(?:test\.)?(?:beforeEach|beforeAll)\s*\(/;
|
|
6124
|
-
LOCATOR_BINDING = /^(?:const|let|var)\s+([A-Za-z_$][\w$]*)\s
|
|
6775
|
+
LOCATOR_BINDING = /^(?:const|let|var)\s+([A-Za-z_$][\w$]*)\s*=(?:\s*(?!\s)|\s*(?=\s(?:getByRole|getByText|getByLabel|getByPlaceholder|getByTestId|getByAltText|getByTitle|locator)\s*\())(.+?(?:getByRole|getByText|getByLabel|getByPlaceholder|getByTestId|getByAltText|getByTitle|locator)\s*\(.+)$/;
|
|
6125
6776
|
EXPECT_OPEN = /\bexpect(?:\.soft)?\s*\(/;
|
|
6126
6777
|
STATE_MATCHER = /\.(toBeChecked|toBeVisible|toHaveValue|toBeDisabled|toBeEnabled)\s*\(/;
|
|
6127
6778
|
CHROME_LOCATOR = /getByRole\s*\(\s*['"`](?:heading|navigation|banner|main|contentinfo|complementary|region|search|tablist|tab|button|link|menuitem|textbox|searchbox|combobox|spinbutton)['"`]/i;
|
|
@@ -6141,16 +6792,17 @@ var init_assertion_strength = __esm({
|
|
|
6141
6792
|
EMPTY_STATE_TEXT = /\bno\b[\s\S]{0,24}?\b(yet|found|results?|items?|favou?rites?|entries|records?|matches|posts?|comments?)\b|nothing (?:here|yet|to show|found)|\bempty\b|\(\s*0\s*\)|\b0\s+(?:favou?rites?|results?|items?|entries|records?|matches|posts?|comments?)\b/i;
|
|
6142
6793
|
AUTH_START = /^\s*\/\/\s*>>>\s*VALIDATE_QA_AUTH/;
|
|
6143
6794
|
AUTH_END = /^\s*\/\/\s*<<<\s*VALIDATE_QA_AUTH/;
|
|
6144
|
-
LOGIN_CALL_LINE = /^\s*await\s+__vqaLogin\s*\(\s*page\s*\)\s
|
|
6795
|
+
LOGIN_CALL_LINE = /^\s*await\s+__vqaLogin\s*\(\s*page\s*\)\s*(?:;\s*)?$/;
|
|
6145
6796
|
INBOX_START = /^\s*\/\/\s*>>>\s*VALIDATE_QA_INBOX/;
|
|
6146
6797
|
INBOX_END = /^\s*\/\/\s*<<<\s*VALIDATE_QA_INBOX/;
|
|
6147
6798
|
NEGATED_ASSERT = /\.not\s*\.\s*to[A-Z]\w*|\.toBeHidden\s*\(/;
|
|
6799
|
+
NEGATED_ASSERT_NOT = /\.not\s*\.\s*to[A-Z]/;
|
|
6148
6800
|
CONST_CHAIN_MAX_DEPTH = 5;
|
|
6149
6801
|
TYPED_INPUT = /\.(fill|type|pressSequentially|setInputFiles)\s*\(/;
|
|
6150
6802
|
TITLE_LINE = /^\s*(?:test|describe)(?:\.\w+)?\s*\(/;
|
|
6151
6803
|
FAILURE_STATUS_ASSERT = [
|
|
6152
6804
|
/\.status\s*\(\s*\)[^;]{0,200}\.(?:toBeGreaterThanOrEqual|toBeGreaterThan)\s*\(\s*(?:399|[45]\d\d)\b/,
|
|
6153
|
-
/\bexpect\s*\(\s*[^;]{0,200}\bstatus\s*(?:\(\s*\))
|
|
6805
|
+
/\bexpect\s*\(\s*[^;]{0,200}\bstatus\s*(?:\(\s*\)\s*)?>=?\s*(?:399|[45]\d\d)\b[^;]{0,200}\)\s*\.\s*(?:toBe\s*\(\s*true|toBeTruthy)/,
|
|
6154
6806
|
/\bexpect\s*\([^;]{0,200}\.ok\s*\(\s*\)\s*\)\s*\.\s*(?:toBe\s*\(\s*false\s*\)|toBeFalsy\s*\()/
|
|
6155
6807
|
];
|
|
6156
6808
|
FAILED_LIST_ASSERT = /\bexpect\s*\(\s*[^;]{0,200}(?:fail|abort|error)\w{0,40}(?:\.length)?\s*\)\s*\.\s*(?:toBeGreaterThan\s*\(\s*0|toHaveLength\s*\(\s*[1-9]|not\s*\.\s*toHaveLength\s*\(\s*0|not\s*\.\s*toEqual\s*\(\s*\[\s*\])/i;
|
|
@@ -6160,8 +6812,15 @@ var init_assertion_strength = __esm({
|
|
|
6160
6812
|
CATCH_ALL_PATTERN = /^(['"`])\*\*(?:\/\*)?\1$|^\/\.\*\/[a-z]*$/;
|
|
6161
6813
|
GUARD_HANDLER = /\bMUTATING\.has\s*\(|blockedbyclient|Read-only guard/;
|
|
6162
6814
|
ABSENCE_MATCHER = /\.not\.toBeVisible\s*\(|\.toBeHidden\s*\(|\.toHaveCount\s*\(\s*0\s*\)/;
|
|
6815
|
+
VALUE_ABSENCE = /\.(?:count\s*\(\s*\)[\s)]{0,10}\.(?:(?:toBe|toEqual|toStrictEqual)\s*\(\s*0\s*\)|toBeLessThan\s*\(\s*1\s*\))|isVisible\s*\(\s*\)[\s)]{0,10}\.(?:(?:toBe|toEqual|toStrictEqual)\s*\(\s*false\s*\)|toBeFalsy\s*\()|isHidden\s*\(\s*\)[\s)]{0,10}\.(?:(?:toBe|toEqual|toStrictEqual)\s*\(\s*true\s*\)|toBeTruthy\s*\())/;
|
|
6816
|
+
OPTION_ABSENCE = /\.(?:toBeVisible\s*\(\s*\{[^{}()]{0,200}\bvisible|toBeAttached\s*\(\s*\{[^{}()]{0,200}\battached)\s*:\s*false\b/;
|
|
6817
|
+
NOT_BEFORE_MATCHER = /\.not\s*\.\s*(?=to[A-Z])/g;
|
|
6163
6818
|
ERROR_TEXT_WORDS = /error|fail|wrong|try again|unable|could ?n.?t|can.?t|problem|invalid|unavailable/i;
|
|
6164
6819
|
TEXT_REGEX_ARG = /(?:getByText|hasText|getByRole\([^)]{0,200}name)\s*[:(]\s*(\/(?:\\.|[^/\\\n])+\/[a-z]*)/;
|
|
6820
|
+
SCOPING_STEP = /(?:\s*\.\s*(?:first|last|nth)\s*\([^()]*\))*\s*\.\s*(?:getBy[A-Z]\w*|locator|filter)\s*\(/y;
|
|
6821
|
+
NEGATED_TEXT = /\.not\s*\.\s*(?:toHaveText|toContainText)\s*\(\s*(?:['"`]([^'"`]*)['"`]|\/((?:\\.|[^/\\\n])+)\/)?/;
|
|
6822
|
+
ASYNC_DESTRUCTURE_OPEN = /async\s*\(\s*\{/g;
|
|
6823
|
+
REQUEST_WORD = /\brequest\b/;
|
|
6165
6824
|
ROLE_NAME_RE = /getByRole\s*\(\s*['"`][a-z]+['"`]\s*,\s*\{[^}]*?name\s*:\s*(?:['"`]([^'"`]+)['"`]|\/([^/]+)\/)/gi;
|
|
6166
6825
|
INTERACT_VERB = /\.(click|fill|check|uncheck|setChecked|selectOption|pressSequentially)\s*\(/;
|
|
6167
6826
|
ROLE_SIG_RE = /getByRole\s*\(\s*['"`]([a-z]+)['"`]\s*,\s*\{[^}]*?name\s*:\s*(?:['"`]([^'"`]+)['"`]|\/([^/]+)\/)/gi;
|
|
@@ -6182,21 +6841,64 @@ var init_assertion_strength = __esm({
|
|
|
6182
6841
|
MATCHER_OPEN = /\s*(?:\.\s*(?:not|resolves|rejects)\s*)*\.\s*[A-Za-z_$][\w$]*\s*\(/y;
|
|
6183
6842
|
ENTERED_VALUE = /\.(?:fill|pressSequentially)\s*\(\s*['"`]([^'"`]+)['"`]|keyboard\.type\s*\(\s*['"`]([^'"`]+)['"`]|\.selectOption\s*\(\s*(?:\[\s*)?(?:\{\s*(?:label|value)\s*:\s*)?['"`]([^'"`]+)['"`]/g;
|
|
6184
6843
|
PAGE_NAV = /\.(?:goto|reload)\s*\(/;
|
|
6844
|
+
ENV_READ = String.raw`process\.env(?:(?:\?\.|\.)([A-Za-z_]\w*)|(?:\?\.)?\[\s*['"]([A-Za-z_]\w*)['"]\s*\])`;
|
|
6845
|
+
ENTERED_ENV = new RegExp(String.raw`\.(?:fill|pressSequentially)\s*\(\s*${ENV_READ}|keyboard\.type\s*\(\s*${ENV_READ}`, "g");
|
|
6846
|
+
HAVE_VALUE_ENV = new RegExp(String.raw`\.toHaveValue\s*\(\s*${ENV_READ}`);
|
|
6847
|
+
ACCOUNT_KEYS = TEST_USER_ENV_KEYS.join("|");
|
|
6848
|
+
ACCOUNT_ENV = `process\\.env(?:\\??\\.(?:${ACCOUNT_KEYS})\\b|(?:\\?\\.)?\\[\\s*['"\`](?:${ACCOUNT_KEYS})['"\`]\\s*\\])`;
|
|
6849
|
+
ACCOUNT_CONST = new RegExp(`\\b(?:const|let|var)\\s+([A-Za-z_$][\\w$]*)\\s*(?::\\s*string\\s*)?=\\s*${ACCOUNT_ENV}`, "g");
|
|
6185
6850
|
IN_FLIGHT_TEXT = /^(?:(?:sending|saving|loading|searching|checking|processing|submitting|preparing|updating|please wait|working|one moment)\b[^.]{0,40}\.{2,3}|\.{3}|…)$/i;
|
|
6851
|
+
LOCATOR_VALUE_READ = /\.(?:count|textContent|innerText|innerHTML|inputValue|getAttribute|isVisible|isHidden|isChecked|isEnabled|isDisabled|isEditable|allTextContents|allInnerTexts|evaluate|evaluateAll|boundingBox|all)\s*\(/;
|
|
6852
|
+
LOCATOR_EXPR = /^(?:this\.)?page\s*\.\s*(?:getBy\w+|locator)\s*\(/;
|
|
6853
|
+
ALWAYS_TRUE_ON_OBJECT = /^\)\s*\.\s*(?:toBeTruthy|toBeDefined|not\s*\.\s*(?:toBeNull|toBeUndefined|toBeFalsy))\s*\(/;
|
|
6854
|
+
COUNT_AT_LEAST_ZERO = /\.(?:count\s*\(\s*\)|length)[\s)]{0,10}\.(?:toBeGreaterThanOrEqual\s*\(\s*0\s*\)|toBeGreaterThan\s*\(\s*-\s*1\s*\))/;
|
|
6855
|
+
DEFINED_VALUE_READ = /\.\s*(?:count|textContent|innerText|innerHTML|inputValue|getAttribute|title|url|isVisible|isHidden|isChecked|isEnabled|isDisabled|isEditable|allTextContents|allInnerTexts)\s*\([^()]*\)$/;
|
|
6856
|
+
ALWAYS_DEFINED = /^\)\s*\.\s*(?:toBeDefined|not\s*\.\s*toBeUndefined)\s*\(/;
|
|
6857
|
+
SELF_COMPARE_MATCHER = /^\)\s*\.\s*(toBe|toEqual|toStrictEqual|toHaveCount)\s*\(/;
|
|
6858
|
+
IS_VISIBLE_TRUE = /^\)*\s*\.\s*(?:toBe\s*\(\s*true\s*\)|toBeTruthy\s*\(\s*\))/;
|
|
6859
|
+
PAGE_VALUE_MATCHER = /^\)\s*\.\s*(?:toBe|toEqual|toStrictEqual|toMatch|toContain)\s*\(/;
|
|
6860
|
+
COUNT_ONE = /\.\s*toHaveCount\s*\(\s*1\s*\)/;
|
|
6861
|
+
URL_CHECK_ARG = /\bpage\s*\.\s*waitForURL\s*\(\s*([^,)]+)|\bexpect(?:\s*\.\s*soft)?\s*\(\s*page\s*\)\s*\.\s*toHaveURL\s*\(\s*([^,)]+)/;
|
|
6862
|
+
PLAIN_PAGE_PATH = /^\/[\w\-./~%]*(?:[?#][^\s]*)?$/;
|
|
6186
6863
|
FORM_CONTROL_LOCATOR = /getByRole\s*\(\s*['"`](?:combobox|switch|checkbox|radio|textbox|searchbox|spinbutton|slider)['"`]|getByLabel\s*\(|getByPlaceholder\s*\(/;
|
|
6187
6864
|
CONTROL_VALUE_MATCHER = /\.(?:toHaveValue|toHaveText|toContainText)\s*\(|\.toHaveAttribute\s*\(\s*['"`]aria-checked['"`]/;
|
|
6865
|
+
ACCOUNT_EMAIL_ENV = /^process\.env(?:(?:\?\.|\.)TEST_USER_EMAIL\b|(?:\?\.)?\[\s*['"]TEST_USER_EMAIL['"]\s*\])/;
|
|
6866
|
+
ENV_PASSWORD_ENTRY = /\.(?:fill|pressSequentially)\s*\(\s*process\.env(?:(?:\?\.|\.)TEST_USER_PASSWORD\b|(?:\?\.)?\[\s*['"]TEST_USER_PASSWORD['"]\s*\])/;
|
|
6867
|
+
ACCOUNT_CONTROL_ROLE = /getByRole\s*\(\s*['"`](?:button|link|menuitem)['"`]\s*,\s*\{/;
|
|
6868
|
+
ASSERT_OR_WAIT = /^(?:await\s+)?(?:expect(?:\.soft|\.poll)?\s*\(|[\w$]+(?:\.[\w$]+)*\.waitFor[A-Za-z]*\s*\()/;
|
|
6869
|
+
ACTS = /\.(?:click|dblclick|tap|press|pressSequentially|fill|clear|type|check|uncheck|setChecked|selectOption|setInputFiles|dispatchEvent|hover|focus|dragTo|goto|reload|goBack|goForward|evaluate|evaluateHandle)\s*\(|\b(?:keyboard|mouse|touchscreen)\s*\.|\.toPass\s*\(/;
|
|
6870
|
+
actLinesOf = /* @__PURE__ */ new WeakMap();
|
|
6871
|
+
CLOSERS_ONLY = /^[})\];,\s]+$/;
|
|
6872
|
+
TEST_BODY_OPEN = /^(?:test|it)(?:\.(?:only|skip|fixme|slow))?\s*\(/;
|
|
6188
6873
|
COPY_CONTROL = /\bcopy\b/i;
|
|
6189
6874
|
COPIED_NOTICE = /\bcopied\b/i;
|
|
6190
6875
|
SEND_CONTROL = /\b(send|submit|subscribe|sign up for|contact|feedback|join (the )?waitlist)\b/i;
|
|
6191
6876
|
SEND_NOTICE = /\b(thank|thanks|sent|subscribed|received|in touch)\b/i;
|
|
6192
6877
|
MARK_READ_CONTROL = /\bmark(?:ed)? as read\b|\bmark all(?: as)? read\b/i;
|
|
6193
6878
|
APP_DATA_FIELD = /\b(title|name of|amount|price|quantity|sku|status|due|assignee|project|ticket|invoice|order)\b/i;
|
|
6879
|
+
COMMIT_CONTROL = /\b(?:save|submit|update|apply|add|create|post|send|publish)\b|\bconfirm\s+changes\b/i;
|
|
6880
|
+
NOT_COMMIT_CONTROL = /\b(?:delete|remove|cancel|close|dismiss|discard|back|sign\s*out|log\s*out|logout|done|ok|got\s+it|export|download)\b/i;
|
|
6881
|
+
DESTRUCTIVE_CONTROL = /\b(?:delete|remove|discard)\b/i;
|
|
6882
|
+
ENTER_PRESS = /\.press\s*\(\s*['"`]Enter['"`]/;
|
|
6883
|
+
CONTROL_LABEL = /(?:name\s*:\s*|getBy(?:Text|Label|Title|TestId)\s*\(\s*|has-text\(\s*)(?:(['"`])((?:(?!\1).)*)\1|\/((?:\\.|[^/\\])+)\/)/g;
|
|
6884
|
+
isCommitClick = (stmt) => {
|
|
6885
|
+
const label = clickLabel(stmt);
|
|
6886
|
+
return label !== null && COMMIT_CONTROL.test(label) && !NOT_COMMIT_CONTROL.test(label) && !EXCLUDE_AFFORDANCE.test(label);
|
|
6887
|
+
};
|
|
6888
|
+
isDestructiveClick = (stmt) => {
|
|
6889
|
+
const label = clickLabel(stmt);
|
|
6890
|
+
return label !== null && DESTRUCTIVE_CONTROL.test(label);
|
|
6891
|
+
};
|
|
6892
|
+
TEARDOWN_OPEN = /\bfinally\s*\{|^(?:test\.)?(?:after|before)(?:Each|All)\s*\(/;
|
|
6194
6893
|
}
|
|
6195
6894
|
});
|
|
6196
6895
|
|
|
6197
6896
|
// ../shared/dist/run-classification.js
|
|
6198
|
-
function
|
|
6199
|
-
|
|
6897
|
+
function isCancelledReason(error2) {
|
|
6898
|
+
if (typeof error2 !== "string")
|
|
6899
|
+
return false;
|
|
6900
|
+
const text = error2.trim();
|
|
6901
|
+
return CANCELLED_REASON_PREFIXES.some((prefix) => text.startsWith(prefix));
|
|
6200
6902
|
}
|
|
6201
6903
|
function isHeldHealRun(run) {
|
|
6202
6904
|
return run?.status === "PASSED" && typeof run.error === "string" && run.error.startsWith(HEAL_PATCH_HELD_RUN_ERROR_PREFIX);
|
|
@@ -6229,7 +6931,7 @@ function pickLatestHealthRun(runs) {
|
|
|
6229
6931
|
for (const run of runs) {
|
|
6230
6932
|
if (run.mode === "auth-setup")
|
|
6231
6933
|
continue;
|
|
6232
|
-
if (run.status === "ERROR" &&
|
|
6934
|
+
if (run.status === "ERROR" && isCancelledReason(run.error))
|
|
6233
6935
|
continue;
|
|
6234
6936
|
if (isHeldHealRun(run))
|
|
6235
6937
|
continue;
|
|
@@ -6247,6 +6949,15 @@ function pickDisplayRun(runs) {
|
|
|
6247
6949
|
return latest;
|
|
6248
6950
|
return pickLatestHealthRun(runs) ?? latest;
|
|
6249
6951
|
}
|
|
6952
|
+
function featureRowRunStatus(runs) {
|
|
6953
|
+
const latest = runs.find((run) => run.mode !== "auth-setup");
|
|
6954
|
+
if (!latest)
|
|
6955
|
+
return null;
|
|
6956
|
+
const decisive = pickLatestHealthRun(runs.filter((run) => !isEnvironmentalError(run)));
|
|
6957
|
+
if (decisive)
|
|
6958
|
+
return decisive.status;
|
|
6959
|
+
return latest.status === "PENDING" || latest.status === "RUNNING" ? latest.status : null;
|
|
6960
|
+
}
|
|
6250
6961
|
function isEnvironmentalError(run) {
|
|
6251
6962
|
if (!run)
|
|
6252
6963
|
return false;
|
|
@@ -6278,7 +6989,7 @@ function classifyErrorMessage(error2) {
|
|
|
6278
6989
|
return "config_issue";
|
|
6279
6990
|
if (EMAIL_NOT_RECEIVED_PREFIXES.some((prefix) => normalized.startsWith(prefix)))
|
|
6280
6991
|
return "email_not_received";
|
|
6281
|
-
if (
|
|
6992
|
+
if (isCancelledReason(error2))
|
|
6282
6993
|
return "cancelled";
|
|
6283
6994
|
if (isRetriedRunInterruption(error2))
|
|
6284
6995
|
return "interrupted";
|
|
@@ -6313,7 +7024,7 @@ function heldHealRunWhere() {
|
|
|
6313
7024
|
function excludeCancelledRunsWhere() {
|
|
6314
7025
|
return {
|
|
6315
7026
|
NOT: [
|
|
6316
|
-
{ status: "ERROR", error: { not: null, startsWith:
|
|
7027
|
+
{ status: "ERROR", OR: CANCELLED_REASON_PREFIXES.map((prefix) => ({ error: { not: null, startsWith: prefix } })) },
|
|
6317
7028
|
heldHealRunWhere()
|
|
6318
7029
|
]
|
|
6319
7030
|
};
|
|
@@ -6391,11 +7102,25 @@ function reportedBugWhere() {
|
|
|
6391
7102
|
function bugReviewStatus(status) {
|
|
6392
7103
|
return status === "QUARANTINED" ? "confirmed" : "needs-review";
|
|
6393
7104
|
}
|
|
6394
|
-
var USER_CANCELLED_RUN_ERROR, 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, EMAIL_NOT_RECEIVED_PREFIXES, ENVIRONMENTAL_ERROR_PREFIXES, ENVIRONMENTAL_ERROR_STORED_PREFIXES, BUG_SURFACE_SUPPRESSING_TAGS, QUARANTINE_CLOSE_TAGS, BUG_CANDIDATE_TAG, BUG_TEST_BROKEN_TAG;
|
|
7105
|
+
var USER_CANCELLED_RUN_ERROR, PLATFORM_SUPPORT_CANCELLED_RUN_ERROR, PLATFORM_ADMIN_CANCELLED_RUN_ERROR, ADMIN_FORCE_CANCELLED_RUN_ERROR, CHAIN_CANCELLED_RUN_ERROR, RETRY_HELD_RUN_ERROR, CANCELLED_REASON_PREFIXES, isUserCancelledError, HEAL_PATCH_HELD_RUN_ERROR_PREFIX, RUNNER_INTERRUPT_ERROR_PREFIX, SERVER_INTERRUPT_ERROR_PREFIX, RUN_LOST_ERROR_PREFIX, RUN_INTERRUPTED_ERROR_PREFIXES, RUN_INTERRUPTION_RETRIED_MARKER, RUN_INTERRUPTION_NOT_RETRIED_MARKERS, BUG_ASSERTION_TAG, UNEXPECTED_PASS_ERROR_PREFIX, BUG_TEST_BROKEN_ERROR_PREFIX, BUG_TEST_BROKEN_REASON, TEST_DID_NOT_RUN_ERROR, EMAIL_NOT_RECEIVED_PREFIXES, ENVIRONMENTAL_ERROR_PREFIXES, ENVIRONMENTAL_ERROR_STORED_PREFIXES, FEATURE_ROW_RUN_WINDOW, BUG_SURFACE_SUPPRESSING_TAGS, QUARANTINE_CLOSE_TAGS, BUG_CANDIDATE_TAG, BUG_TEST_BROKEN_TAG;
|
|
6395
7106
|
var init_run_classification = __esm({
|
|
6396
7107
|
"../shared/dist/run-classification.js"() {
|
|
6397
7108
|
"use strict";
|
|
6398
7109
|
USER_CANCELLED_RUN_ERROR = "Cancelled by user";
|
|
7110
|
+
PLATFORM_SUPPORT_CANCELLED_RUN_ERROR = "Cancelled by platform support";
|
|
7111
|
+
PLATFORM_ADMIN_CANCELLED_RUN_ERROR = "Cancelled by platform admin";
|
|
7112
|
+
ADMIN_FORCE_CANCELLED_RUN_ERROR = "Admin force-cancelled all active runs";
|
|
7113
|
+
CHAIN_CANCELLED_RUN_ERROR = "Cancelled: parent run cancelled";
|
|
7114
|
+
RETRY_HELD_RUN_ERROR = "Cancelled: auto generation paused";
|
|
7115
|
+
CANCELLED_REASON_PREFIXES = [
|
|
7116
|
+
USER_CANCELLED_RUN_ERROR,
|
|
7117
|
+
PLATFORM_SUPPORT_CANCELLED_RUN_ERROR,
|
|
7118
|
+
PLATFORM_ADMIN_CANCELLED_RUN_ERROR,
|
|
7119
|
+
ADMIN_FORCE_CANCELLED_RUN_ERROR,
|
|
7120
|
+
CHAIN_CANCELLED_RUN_ERROR,
|
|
7121
|
+
RETRY_HELD_RUN_ERROR
|
|
7122
|
+
];
|
|
7123
|
+
isUserCancelledError = isCancelledReason;
|
|
6399
7124
|
HEAL_PATCH_HELD_RUN_ERROR_PREFIX = "Heal patch held";
|
|
6400
7125
|
RUNNER_INTERRUPT_ERROR_PREFIX = "Runner shutting down";
|
|
6401
7126
|
SERVER_INTERRUPT_ERROR_PREFIX = "Run interrupted by server restart";
|
|
@@ -6411,6 +7136,7 @@ var init_run_classification = __esm({
|
|
|
6411
7136
|
UNEXPECTED_PASS_ERROR_PREFIX = "UNEXPECTED_PASS:";
|
|
6412
7137
|
BUG_TEST_BROKEN_ERROR_PREFIX = "BUG_TEST_BROKEN:";
|
|
6413
7138
|
BUG_TEST_BROKEN_REASON = "bug test broken (not a bug verdict)";
|
|
7139
|
+
TEST_DID_NOT_RUN_ERROR = "Test did not run (a dependency failed or the run was interrupted)";
|
|
6414
7140
|
EMAIL_NOT_RECEIVED_PREFIXES = [
|
|
6415
7141
|
"VERIFICATION_EMAIL_NOT_RECEIVED:",
|
|
6416
7142
|
"VERIFICATION_EMAIL_HAS_NO_CODE:",
|
|
@@ -6423,9 +7149,10 @@ var init_run_classification = __esm({
|
|
|
6423
7149
|
];
|
|
6424
7150
|
ENVIRONMENTAL_ERROR_STORED_PREFIXES = [
|
|
6425
7151
|
...ENVIRONMENTAL_ERROR_PREFIXES.flatMap((prefix) => [prefix, `Error: ${prefix}`]),
|
|
6426
|
-
// Written by the
|
|
6427
|
-
|
|
7152
|
+
// Written by the cancels as is, with no prefix wrapper.
|
|
7153
|
+
...CANCELLED_REASON_PREFIXES
|
|
6428
7154
|
];
|
|
7155
|
+
FEATURE_ROW_RUN_WINDOW = 10;
|
|
6429
7156
|
BUG_SURFACE_SUPPRESSING_TAGS = ["@rate-limit-blocked", "@quota-exceeded", "@auth-lost"];
|
|
6430
7157
|
QUARANTINE_CLOSE_TAGS = [
|
|
6431
7158
|
"@bug-found",
|
|
@@ -8858,180 +9585,6 @@ var init_admin_view_return_to = __esm({
|
|
|
8858
9585
|
}
|
|
8859
9586
|
});
|
|
8860
9587
|
|
|
8861
|
-
// ../shared/dist/auth-plan.js
|
|
8862
|
-
function hasInlineLoginSteps(playwrightCode) {
|
|
8863
|
-
if (!playwrightCode)
|
|
8864
|
-
return false;
|
|
8865
|
-
return /process\.env\.TEST_USER_EMAIL!?/.test(playwrightCode) && /process\.env\.TEST_USER_PASSWORD!?/.test(playwrightCode);
|
|
8866
|
-
}
|
|
8867
|
-
function hasGeneratedAuthGuard(playwrightCode) {
|
|
8868
|
-
if (!playwrightCode)
|
|
8869
|
-
return false;
|
|
8870
|
-
return /\/\/ >>> VALIDATE_QA_AUTH v(\d+)\b[\s\S]*?process\.env\.AUTH_FILE[\s\S]*?\/\/ <<< VALIDATE_QA_AUTH v\1\b/.test(playwrightCode);
|
|
8871
|
-
}
|
|
8872
|
-
function legacyNeedsCookieState(tags, code, sharedAuthInjectEnabled) {
|
|
8873
|
-
if (!sharedAuthInjectEnabled)
|
|
8874
|
-
return false;
|
|
8875
|
-
if (tags.includes("@auth"))
|
|
8876
|
-
return false;
|
|
8877
|
-
if (tags.includes("@api"))
|
|
8878
|
-
return true;
|
|
8879
|
-
if (!tags.includes("@needs-auth"))
|
|
8880
|
-
return false;
|
|
8881
|
-
if (sharedAuthInjectEnabled && tags.includes("@auth-guarded") && hasGeneratedAuthGuard(code))
|
|
8882
|
-
return true;
|
|
8883
|
-
const isGeneratedVariant = tags.includes("@edge") || tags.includes("@bug");
|
|
8884
|
-
const hasLegacyInlineLogin = tags.includes("@discovered") && !isGeneratedVariant && hasInlineLoginSteps(code);
|
|
8885
|
-
return !tags.includes("@inline-login") && !hasLegacyInlineLogin;
|
|
8886
|
-
}
|
|
8887
|
-
function codeReadsStoredCredentials(code) {
|
|
8888
|
-
if (!code)
|
|
8889
|
-
return false;
|
|
8890
|
-
if (/__vqaLogin\b/.test(code))
|
|
8891
|
-
return true;
|
|
8892
|
-
for (const m of code.matchAll(/TEST_[A-Z0-9_]+/g)) {
|
|
8893
|
-
if (/_(?:EMAIL|PASSWORD)/.test(m[0]))
|
|
8894
|
-
return true;
|
|
8895
|
-
}
|
|
8896
|
-
for (const m of code.matchAll(/process\.env\s*\[([^\]]*)\]?/g)) {
|
|
8897
|
-
if (!/^\s*(['"])[^'"`]*\1\s*$/.test(m[1]))
|
|
8898
|
-
return true;
|
|
8899
|
-
}
|
|
8900
|
-
return false;
|
|
8901
|
-
}
|
|
8902
|
-
function explicitAuthTypeTags(t) {
|
|
8903
|
-
const found = /* @__PURE__ */ new Set();
|
|
8904
|
-
for (const tag of t) {
|
|
8905
|
-
if (AUTH_TYPE_TAGS.includes(tag))
|
|
8906
|
-
found.add(tag);
|
|
8907
|
-
else if (Object.hasOwn(AUTH_TAG_ALIASES, tag))
|
|
8908
|
-
found.add(AUTH_TAG_ALIASES[tag]);
|
|
8909
|
-
}
|
|
8910
|
-
return [...found];
|
|
8911
|
-
}
|
|
8912
|
-
function invalidPlan(configurationError) {
|
|
8913
|
-
return {
|
|
8914
|
-
version: 1,
|
|
8915
|
-
source: "invalid",
|
|
8916
|
-
acquisition: "unsupported",
|
|
8917
|
-
presentation: "none",
|
|
8918
|
-
needsCookieState: false,
|
|
8919
|
-
needsStandingHeaders: false,
|
|
8920
|
-
standingHeadersMode: "none",
|
|
8921
|
-
needsLoginCreds: false,
|
|
8922
|
-
requiredEnvKeys: [],
|
|
8923
|
-
configurationError
|
|
8924
|
-
};
|
|
8925
|
-
}
|
|
8926
|
-
function explicitPlan(authTypeTag2, acquisition, presentation, opts) {
|
|
8927
|
-
return {
|
|
8928
|
-
version: 1,
|
|
8929
|
-
source: "explicit",
|
|
8930
|
-
authTypeTag: authTypeTag2,
|
|
8931
|
-
acquisition,
|
|
8932
|
-
presentation,
|
|
8933
|
-
needsCookieState: opts.cookie,
|
|
8934
|
-
needsStandingHeaders: opts.standingMode === "required",
|
|
8935
|
-
standingHeadersMode: opts.standingMode,
|
|
8936
|
-
needsLoginCreds: TEST_USER_ENV_KEYS.every((k) => opts.requiredEnvKeys.includes(k)),
|
|
8937
|
-
requiredEnvKeys: opts.requiredEnvKeys
|
|
8938
|
-
};
|
|
8939
|
-
}
|
|
8940
|
-
function deriveAuthPlan(tags, code, opts) {
|
|
8941
|
-
const t = Array.isArray(tags) ? tags : [];
|
|
8942
|
-
const sharedFlag = opts?.sharedAuthInjectEnabled ?? false;
|
|
8943
|
-
const isApi = t.includes("@api");
|
|
8944
|
-
const explicit = isApi ? explicitAuthTypeTags(t) : [];
|
|
8945
|
-
if (explicit.length > 0) {
|
|
8946
|
-
if (t.includes("@auth")) {
|
|
8947
|
-
return invalidPlan("@auth (self-login UI) cannot be combined with an explicit @api auth-type tag");
|
|
8948
|
-
}
|
|
8949
|
-
const loginCompose = explicit.filter((e) => e === AUTH_TAG_COOKIE_STATE || e === AUTH_TAG_LOGIN_COOKIE || e === AUTH_TAG_LOGIN_BEARER);
|
|
8950
|
-
if (explicit.length === 2 && explicit.includes(AUTH_TAG_STANDING) && loginCompose.length === 1) {
|
|
8951
|
-
const composedKeys = [...TEST_USER_ENV_KEYS, API_AUTH_HEADERS_ENV_KEY];
|
|
8952
|
-
switch (loginCompose[0]) {
|
|
8953
|
-
case AUTH_TAG_COOKIE_STATE:
|
|
8954
|
-
return explicitPlan(AUTH_TAG_COOKIE_STATE, "browser-bootstrap", "cookie", { cookie: sharedFlag, standingMode: "required", requiredEnvKeys: composedKeys });
|
|
8955
|
-
case AUTH_TAG_LOGIN_COOKIE:
|
|
8956
|
-
return explicitPlan(AUTH_TAG_LOGIN_COOKIE, "request-login", "cookie", { cookie: false, standingMode: "required", requiredEnvKeys: composedKeys });
|
|
8957
|
-
case AUTH_TAG_LOGIN_BEARER:
|
|
8958
|
-
return explicitPlan(AUTH_TAG_LOGIN_BEARER, "request-login", "bearer", { cookie: false, standingMode: "required", requiredEnvKeys: composedKeys });
|
|
8959
|
-
}
|
|
8960
|
-
}
|
|
8961
|
-
if (explicit.length > 1) {
|
|
8962
|
-
return invalidPlan(`conflicting auth-type tags: ${explicit.join(", ")}`);
|
|
8963
|
-
}
|
|
8964
|
-
switch (explicit[0]) {
|
|
8965
|
-
case AUTH_TAG_UNSUPPORTED:
|
|
8966
|
-
return {
|
|
8967
|
-
...invalidPlan("auth scheme not yet drivable (e.g. HMAC / mTLS / provider-replay) \u2014 needs manual configuration")
|
|
8968
|
-
};
|
|
8969
|
-
case AUTH_TAG_NONE:
|
|
8970
|
-
return explicitPlan(AUTH_TAG_NONE, "none", "none", { cookie: false, standingMode: "none", requiredEnvKeys: [] });
|
|
8971
|
-
case AUTH_TAG_STANDING:
|
|
8972
|
-
return explicitPlan(AUTH_TAG_STANDING, "standing-secret", "standing-header", { cookie: false, standingMode: "required", requiredEnvKeys: [API_AUTH_HEADERS_ENV_KEY] });
|
|
8973
|
-
case AUTH_TAG_LOGIN_BEARER:
|
|
8974
|
-
return explicitPlan(AUTH_TAG_LOGIN_BEARER, "request-login", "bearer", { cookie: false, standingMode: "none", requiredEnvKeys: [...TEST_USER_ENV_KEYS] });
|
|
8975
|
-
case AUTH_TAG_LOGIN_COOKIE:
|
|
8976
|
-
return explicitPlan(AUTH_TAG_LOGIN_COOKIE, "request-login", "cookie", { cookie: false, standingMode: "legacy-if-configured", requiredEnvKeys: [...TEST_USER_ENV_KEYS] });
|
|
8977
|
-
case AUTH_TAG_COOKIE_STATE:
|
|
8978
|
-
return explicitPlan(AUTH_TAG_COOKIE_STATE, "browser-bootstrap", "cookie", { cookie: sharedFlag, standingMode: "legacy-if-configured", requiredEnvKeys: [...TEST_USER_ENV_KEYS] });
|
|
8979
|
-
}
|
|
8980
|
-
}
|
|
8981
|
-
const cookie = legacyNeedsCookieState(t, code, sharedFlag);
|
|
8982
|
-
const authedLegacy = t.includes("@auth") || t.includes("@needs-auth") || isApi;
|
|
8983
|
-
const selfLoginWithoutStoredCreds = t.includes("@auth") && !t.includes("@needs-auth") && !isApi && !!code?.trim() && !/TEST_USER_EMAIL|TEST_USER_PASSWORD|__vqaLogin\b/.test(code);
|
|
8984
|
-
const apiWithoutStoredCreds = isApi && !t.includes("@auth") && !t.includes("@needs-auth") && !!code?.trim() && !codeReadsStoredCredentials(code);
|
|
8985
|
-
const needsLoginCreds = authedLegacy && !selfLoginWithoutStoredCreds && !apiWithoutStoredCreds;
|
|
8986
|
-
return {
|
|
8987
|
-
version: 1,
|
|
8988
|
-
source: "legacy",
|
|
8989
|
-
acquisition: cookie ? "browser-bootstrap" : authedLegacy ? "request-login" : "none",
|
|
8990
|
-
presentation: authedLegacy ? "cookie" : "none",
|
|
8991
|
-
needsCookieState: cookie,
|
|
8992
|
-
needsStandingHeaders: false,
|
|
8993
|
-
// A legacy bare-@api test keeps injecting standing headers IF the project configured them
|
|
8994
|
-
// (preserving today's behavior) — but does not require them. Non-@api legacy tests never inject.
|
|
8995
|
-
standingHeadersMode: isApi ? "legacy-if-configured" : "none",
|
|
8996
|
-
needsLoginCreds,
|
|
8997
|
-
requiredEnvKeys: needsLoginCreds ? [...TEST_USER_ENV_KEYS] : []
|
|
8998
|
-
};
|
|
8999
|
-
}
|
|
9000
|
-
function authTypeTag(tags) {
|
|
9001
|
-
const t = Array.isArray(tags) ? tags : [];
|
|
9002
|
-
const explicit = explicitAuthTypeTags(t);
|
|
9003
|
-
return explicit.length === 1 ? explicit[0] : void 0;
|
|
9004
|
-
}
|
|
9005
|
-
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;
|
|
9006
|
-
var init_auth_plan = __esm({
|
|
9007
|
-
"../shared/dist/auth-plan.js"() {
|
|
9008
|
-
"use strict";
|
|
9009
|
-
AUTH_TAG_COOKIE_STATE = "@auth-cookie-state";
|
|
9010
|
-
AUTH_TAG_LOGIN_COOKIE = "@auth-login-cookie";
|
|
9011
|
-
AUTH_TAG_LOGIN_BEARER = "@auth-login-bearer";
|
|
9012
|
-
AUTH_TAG_STANDING = "@auth-standing";
|
|
9013
|
-
AUTH_TAG_NONE = "@auth-none";
|
|
9014
|
-
AUTH_TAG_UNSUPPORTED = "@auth-unsupported";
|
|
9015
|
-
AUTH_TYPE_TAGS = [
|
|
9016
|
-
AUTH_TAG_COOKIE_STATE,
|
|
9017
|
-
AUTH_TAG_LOGIN_COOKIE,
|
|
9018
|
-
AUTH_TAG_LOGIN_BEARER,
|
|
9019
|
-
AUTH_TAG_STANDING,
|
|
9020
|
-
AUTH_TAG_NONE,
|
|
9021
|
-
AUTH_TAG_UNSUPPORTED
|
|
9022
|
-
];
|
|
9023
|
-
AUTH_TAG_ALIASES = {
|
|
9024
|
-
"@auth-cookie": AUTH_TAG_COOKIE_STATE,
|
|
9025
|
-
"@auth-bearer": AUTH_TAG_LOGIN_BEARER,
|
|
9026
|
-
"@auth-token": AUTH_TAG_LOGIN_BEARER,
|
|
9027
|
-
"@auth-api-key": AUTH_TAG_STANDING,
|
|
9028
|
-
"@auth-standing-header": AUTH_TAG_STANDING
|
|
9029
|
-
};
|
|
9030
|
-
TEST_USER_ENV_KEYS = ["TEST_USER_EMAIL", "TEST_USER_PASSWORD"];
|
|
9031
|
-
API_AUTH_HEADERS_ENV_KEY = "API_AUTH_HEADERS";
|
|
9032
|
-
}
|
|
9033
|
-
});
|
|
9034
|
-
|
|
9035
9588
|
// ../shared/dist/auth-block-detector.js
|
|
9036
9589
|
function stripEchoLines(text) {
|
|
9037
9590
|
return text.split("\n").filter((line) => !ECHO_LINE_RE.test(line)).join("\n");
|
|
@@ -9039,13 +9592,16 @@ function stripEchoLines(text) {
|
|
|
9039
9592
|
function matchesAny(text, patterns) {
|
|
9040
9593
|
return patterns.some((p) => p.test(text));
|
|
9041
9594
|
}
|
|
9595
|
+
function isIntentionalNegativeAuthTest(input) {
|
|
9596
|
+
const meta = [input.testName, input.description].filter(Boolean).join(" ");
|
|
9597
|
+
return matchesAny(meta, INTENTIONAL_NEGATIVE_AUTH);
|
|
9598
|
+
}
|
|
9042
9599
|
function detectAuthBlock(input) {
|
|
9043
9600
|
const tags = input.tags ?? [];
|
|
9044
9601
|
const isAuthed = tags.some((t) => AUTHED_TAGS.includes(t));
|
|
9045
9602
|
if (!isAuthed)
|
|
9046
9603
|
return NO_DETECTION;
|
|
9047
|
-
|
|
9048
|
-
if (matchesAny(meta, INTENTIONAL_NEGATIVE_AUTH))
|
|
9604
|
+
if (isIntentionalNegativeAuthTest(input))
|
|
9049
9605
|
return NO_DETECTION;
|
|
9050
9606
|
const sources = [];
|
|
9051
9607
|
if (input.error)
|
|
@@ -9148,7 +9704,24 @@ var init_auth_block_detector = __esm({
|
|
|
9148
9704
|
/\b(reject|deny|denied|block(ed)?)\b[^\n]{0,20}\b(login|sign[\s-]?in|auth|credential|password)\b/i
|
|
9149
9705
|
];
|
|
9150
9706
|
ECHO_LINE_RE = /^\s*(?:(?:📋|🎭|🌐|⚙️?)|(?:stderr:\s*|stdout:\s*)?Locator:)/u;
|
|
9151
|
-
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
|
|
9707
|
+
UNAUTHORIZED_TEXT_RE = /(?:return(?:s|ed|ing)?|respond(?:s|ed|ing)?|got|receiv(?:e|ed|ing)|gives?|sends?|yield(?:s|ed|ing)?|results?\s+in)\s+(?:a\s+|an\s+|the\s+)?(?:401|403)\b|\bstatus(?:\s+code)?\s*(?::\s*)?(?:401|403)\b|\bHTTP\/[\d.]+\s+(?:401|403)\b|(?:401|403)['’]d\b|(?:[→⇒]|->)\s*(?:401|403)\b|(?:return(?:s|ed|ing)?|respond(?:s|ed|ing)?|got|receiv(?:e|ed|ing)|threw|throws?)\s+(?:a\s+|an\s+)?(?:unauthorized|forbidden)\b|\bnot\s+authenticated\b/i;
|
|
9708
|
+
}
|
|
9709
|
+
});
|
|
9710
|
+
|
|
9711
|
+
// ../shared/dist/auth-session-stamp.js
|
|
9712
|
+
function parseAuthSessionStamp(value) {
|
|
9713
|
+
if (!value || typeof value !== "object" || Array.isArray(value))
|
|
9714
|
+
return null;
|
|
9715
|
+
const { shared, role, generation } = value;
|
|
9716
|
+
if (shared !== true || role !== "producer" && role !== "follower")
|
|
9717
|
+
return null;
|
|
9718
|
+
return typeof generation === "string" && AUTH_SESSION_GENERATION_RE.test(generation) ? { shared: true, role, generation } : { shared: true, role };
|
|
9719
|
+
}
|
|
9720
|
+
var AUTH_SESSION_GENERATION_RE;
|
|
9721
|
+
var init_auth_session_stamp = __esm({
|
|
9722
|
+
"../shared/dist/auth-session-stamp.js"() {
|
|
9723
|
+
"use strict";
|
|
9724
|
+
AUTH_SESSION_GENERATION_RE = /^[0-9a-z]{1,16}$/;
|
|
9152
9725
|
}
|
|
9153
9726
|
});
|
|
9154
9727
|
|
|
@@ -10072,12 +10645,70 @@ function isSharedAuthInjectEnabled(value = process.env.SHARED_AUTH_INJECT) {
|
|
|
10072
10645
|
function isAuthReadinessGuardEnabled(value = process.env.AUTH_READINESS_GUARD) {
|
|
10073
10646
|
return value?.trim().toLowerCase() !== "false";
|
|
10074
10647
|
}
|
|
10648
|
+
function visualBaselinesEnabled(projectId, raw = process.env.VISUAL_BASELINES_PROJECTS) {
|
|
10649
|
+
if (typeof projectId !== "string" || projectId.trim() === "")
|
|
10650
|
+
return false;
|
|
10651
|
+
const listed = (raw ?? "").split(",").map((entry) => entry.trim()).filter(Boolean);
|
|
10652
|
+
return listed.includes("*") || listed.includes(projectId);
|
|
10653
|
+
}
|
|
10075
10654
|
var init_feature_flags = __esm({
|
|
10076
10655
|
"../shared/dist/feature-flags.js"() {
|
|
10077
10656
|
"use strict";
|
|
10078
10657
|
}
|
|
10079
10658
|
});
|
|
10080
10659
|
|
|
10660
|
+
// ../shared/dist/visual-compare-limits.js
|
|
10661
|
+
function isSafeMaskSelector(selector) {
|
|
10662
|
+
if (typeof selector !== "string")
|
|
10663
|
+
return false;
|
|
10664
|
+
if (selector.trim().length === 0)
|
|
10665
|
+
return false;
|
|
10666
|
+
if (selector.length > VISUAL_MASK_SELECTOR_MAX_LENGTH)
|
|
10667
|
+
return false;
|
|
10668
|
+
return !VISUAL_MASK_SELECTOR_FORBIDDEN.test(selector);
|
|
10669
|
+
}
|
|
10670
|
+
var VISUAL_MASK_SELECTORS_MAX, VISUAL_MASK_SELECTOR_MAX_LENGTH, VISUAL_MASK_SELECTOR_FORBIDDEN;
|
|
10671
|
+
var init_visual_compare_limits = __esm({
|
|
10672
|
+
"../shared/dist/visual-compare-limits.js"() {
|
|
10673
|
+
"use strict";
|
|
10674
|
+
VISUAL_MASK_SELECTORS_MAX = 50;
|
|
10675
|
+
VISUAL_MASK_SELECTOR_MAX_LENGTH = 300;
|
|
10676
|
+
VISUAL_MASK_SELECTOR_FORBIDDEN = /[{};@<\r\n]/;
|
|
10677
|
+
}
|
|
10678
|
+
});
|
|
10679
|
+
|
|
10680
|
+
// ../shared/dist/visual-baseline-identity.js
|
|
10681
|
+
function isBrowserVersion(value) {
|
|
10682
|
+
return typeof value === "string" && value.length <= 32 && BROWSER_VERSION.test(value);
|
|
10683
|
+
}
|
|
10684
|
+
function renderIdentity(flavour, version, image) {
|
|
10685
|
+
if (!/^[a-z][a-z-]{0,31}$/.test(flavour) || !/^[a-z][a-z-]{0,31}$/.test(image) || !isBrowserVersion(version))
|
|
10686
|
+
return null;
|
|
10687
|
+
const identity = `${flavour}/${version}/${image}`;
|
|
10688
|
+
return identity.length <= RENDER_IDENTITY_MAX_LENGTH ? identity : null;
|
|
10689
|
+
}
|
|
10690
|
+
function v1RenderIdentity(version) {
|
|
10691
|
+
return renderIdentity(VISUAL_V1_IDENTITY.flavour, version, VISUAL_V1_IDENTITY.image);
|
|
10692
|
+
}
|
|
10693
|
+
function parseRenderIdentity(value) {
|
|
10694
|
+
if (typeof value !== "string" || value.length > RENDER_IDENTITY_MAX_LENGTH)
|
|
10695
|
+
return null;
|
|
10696
|
+
const parts = value.split("/");
|
|
10697
|
+
if (parts.length !== 3)
|
|
10698
|
+
return null;
|
|
10699
|
+
const [flavour, version, image] = parts;
|
|
10700
|
+
return renderIdentity(flavour, version, image) === value ? { flavour, version, image } : null;
|
|
10701
|
+
}
|
|
10702
|
+
var VISUAL_V1_IDENTITY, RENDER_IDENTITY_MAX_LENGTH, BROWSER_VERSION;
|
|
10703
|
+
var init_visual_baseline_identity = __esm({
|
|
10704
|
+
"../shared/dist/visual-baseline-identity.js"() {
|
|
10705
|
+
"use strict";
|
|
10706
|
+
VISUAL_V1_IDENTITY = { platform: "linux", image: "runner-fast", flavour: "headless-shell" };
|
|
10707
|
+
RENDER_IDENTITY_MAX_LENGTH = 64;
|
|
10708
|
+
BROWSER_VERSION = /^\d{1,4}(\.\d{1,6}){0,3}$/;
|
|
10709
|
+
}
|
|
10710
|
+
});
|
|
10711
|
+
|
|
10081
10712
|
// ../shared/dist/analytics-url.js
|
|
10082
10713
|
function isSensitiveQueryKey(key) {
|
|
10083
10714
|
return SENSITIVE_QUERY_KEY_RE.test(key);
|
|
@@ -10380,7 +11011,7 @@ var init_agentic_cli_runtime = __esm({
|
|
|
10380
11011
|
GROK_CLI_DEFAULT_MODEL = "grok-4.7";
|
|
10381
11012
|
GROK_CLI_DEFAULT_REASONING_EFFORT = "high";
|
|
10382
11013
|
CLAUDE_CLI_DEFAULT_MODEL = "claude-sonnet-5-5";
|
|
10383
|
-
CLAUDE_CLI_DEFAULT_EFFORT = "
|
|
11014
|
+
CLAUDE_CLI_DEFAULT_EFFORT = "high";
|
|
10384
11015
|
MUSE_CLI_DEFAULT_MODEL = "muse-spark-1.3-contributor";
|
|
10385
11016
|
MUSE_CLI_DEFAULT_EFFORT = "high";
|
|
10386
11017
|
CODEX_CLI_DEFAULT_MODEL = "gpt-6.1-sol";
|
|
@@ -10435,6 +11066,17 @@ function openCodeGoWireModel(id) {
|
|
|
10435
11066
|
const trimmed = id?.trim() ?? "";
|
|
10436
11067
|
return isOpenCodeGoModelId(trimmed) ? trimmed.slice(OPENCODE_GO_PREFIX.length) : null;
|
|
10437
11068
|
}
|
|
11069
|
+
function isZaiModelId(id) {
|
|
11070
|
+
const trimmed = id?.trim() ?? "";
|
|
11071
|
+
return ZAI_MODEL_PATTERN.test(trimmed) && !trimmed.includes("..");
|
|
11072
|
+
}
|
|
11073
|
+
function hasZaiPrefix(id) {
|
|
11074
|
+
return (id?.trim() ?? "").startsWith(ZAI_PREFIX);
|
|
11075
|
+
}
|
|
11076
|
+
function zaiWireModel(id) {
|
|
11077
|
+
const trimmed = id?.trim() ?? "";
|
|
11078
|
+
return isZaiModelId(trimmed) ? trimmed.slice(ZAI_PREFIX.length) : null;
|
|
11079
|
+
}
|
|
10438
11080
|
function isEngineId(value) {
|
|
10439
11081
|
return CLI_ENGINE_MODEL_IDS.includes(value);
|
|
10440
11082
|
}
|
|
@@ -10531,7 +11173,7 @@ function museCliContributorMode(env = process.env) {
|
|
|
10531
11173
|
const raw = (env.MUSE_CLI_CONTRIBUTOR_MODE ?? "").trim().toLowerCase();
|
|
10532
11174
|
return raw === "off" || raw === "false" || raw === "0" || raw === "no" ? "off" : "on";
|
|
10533
11175
|
}
|
|
10534
|
-
var CLI_ENGINE_MODEL_IDS, BROWSER_CLI_ENGINE_IDS, PIPELINE_STEPS, CLI_ENGINE_RULES, ENGINE_NAMES, OPENROUTER_PREFIX, OPENROUTER_MODEL_PATTERN, OPENCODE_GO_PREFIX, OPENCODE_GO_MODEL_PATTERN;
|
|
11176
|
+
var CLI_ENGINE_MODEL_IDS, BROWSER_CLI_ENGINE_IDS, PIPELINE_STEPS, CLI_ENGINE_RULES, ENGINE_NAMES, OPENROUTER_PREFIX, OPENROUTER_MODEL_PATTERN, OPENCODE_GO_PREFIX, OPENCODE_GO_MODEL_PATTERN, ZAI_PREFIX, ZAI_MODEL_PATTERN;
|
|
10535
11177
|
var init_engine_spec = __esm({
|
|
10536
11178
|
"../shared/dist/engine-spec.js"() {
|
|
10537
11179
|
"use strict";
|
|
@@ -10591,6 +11233,8 @@ var init_engine_spec = __esm({
|
|
|
10591
11233
|
OPENROUTER_MODEL_PATTERN = /^openrouter:[a-z0-9][a-z0-9._-]{0,40}\/[A-Za-z0-9._:-]{1,100}$/;
|
|
10592
11234
|
OPENCODE_GO_PREFIX = "opencode-go:";
|
|
10593
11235
|
OPENCODE_GO_MODEL_PATTERN = /^opencode-go:[a-z0-9][a-z0-9._-]{0,100}$/;
|
|
11236
|
+
ZAI_PREFIX = "zai:";
|
|
11237
|
+
ZAI_MODEL_PATTERN = /^zai:glm-[a-z0-9][a-z0-9._-]{0,60}$/;
|
|
10594
11238
|
}
|
|
10595
11239
|
});
|
|
10596
11240
|
|
|
@@ -10875,6 +11519,8 @@ var init_api_model_catalog = __esm({
|
|
|
10875
11519
|
authCaptureModel: "Login capture",
|
|
10876
11520
|
plannerModel: "Scenario Planner",
|
|
10877
11521
|
plannerGateModel: "Planner Gate",
|
|
11522
|
+
pwAgentsHealerModel: "Test Healer (Playwright agents)",
|
|
11523
|
+
pwAgentsReviewModel: "Plan Review (Playwright agents)",
|
|
10878
11524
|
generationModel: "Code Generation",
|
|
10879
11525
|
reviewModel: "Code Review",
|
|
10880
11526
|
apiTestModel: "API Test Generator",
|
|
@@ -10899,11 +11545,6 @@ var init_api_model_catalog = __esm({
|
|
|
10899
11545
|
});
|
|
10900
11546
|
|
|
10901
11547
|
// ../shared/dist/provider-chains.js
|
|
10902
|
-
function isClaudeModel(id) {
|
|
10903
|
-
if (/claude|anthropic/i.test(id))
|
|
10904
|
-
return true;
|
|
10905
|
-
return API_MODEL_CATALOG.some((m) => m.provider === "anthropic" && (m.id === id || m.apiModel === id));
|
|
10906
|
-
}
|
|
10907
11548
|
function chainKey(modelId) {
|
|
10908
11549
|
const id = canonicalModelId(modelId);
|
|
10909
11550
|
if (!id)
|
|
@@ -10911,35 +11552,334 @@ function chainKey(modelId) {
|
|
|
10911
11552
|
const entry = API_MODEL_CATALOG.find((m) => m.id === id || m.apiModel === id);
|
|
10912
11553
|
return entry?.id ?? id;
|
|
10913
11554
|
}
|
|
11555
|
+
function has(map2, key) {
|
|
11556
|
+
return Object.prototype.hasOwnProperty.call(map2, key);
|
|
11557
|
+
}
|
|
10914
11558
|
function providerChainFor(modelId) {
|
|
10915
11559
|
const key = chainKey(modelId);
|
|
10916
|
-
if (
|
|
10917
|
-
return [];
|
|
10918
|
-
return
|
|
11560
|
+
if (key && has(_overrideHops, key))
|
|
11561
|
+
return _overrideHops[key];
|
|
11562
|
+
return key && has(PROVIDER_CHAINS, key) ? PROVIDER_CHAINS[key] : [];
|
|
11563
|
+
}
|
|
11564
|
+
function providerChainLeadsFor(modelId) {
|
|
11565
|
+
const key = chainKey(modelId);
|
|
11566
|
+
if (key && has(_overrideLeads, key))
|
|
11567
|
+
return _overrideLeads[key];
|
|
11568
|
+
return key && has(PROVIDER_CHAIN_LEADS, key) ? PROVIDER_CHAIN_LEADS[key] : [];
|
|
11569
|
+
}
|
|
11570
|
+
function allChains() {
|
|
11571
|
+
const ordered = (map2, override) => [...Object.entries(map2).filter(([key]) => !has(override, key)), ...Object.entries(override)];
|
|
11572
|
+
return [...ordered(PROVIDER_CHAINS, _overrideHops), ...ordered(PROVIDER_CHAIN_LEADS, _overrideLeads)];
|
|
10919
11573
|
}
|
|
10920
11574
|
function isProviderChainHopId(modelId) {
|
|
10921
11575
|
const id = modelId ?? "";
|
|
10922
|
-
return id !== "" &&
|
|
11576
|
+
return id !== "" && allChains().some(([, hops]) => hops.includes(id));
|
|
10923
11577
|
}
|
|
10924
11578
|
function providerChainPrimariesForHop(hopId) {
|
|
10925
11579
|
const id = hopId ?? "";
|
|
10926
11580
|
if (!id)
|
|
10927
11581
|
return [];
|
|
10928
|
-
return
|
|
11582
|
+
return allChains().filter(([, hops]) => hops.includes(id)).map(([primary]) => primary);
|
|
11583
|
+
}
|
|
11584
|
+
function providerChainHostOf(value, modelValue) {
|
|
11585
|
+
if (value === modelValue)
|
|
11586
|
+
return "own";
|
|
11587
|
+
if (hasZaiPrefix(value))
|
|
11588
|
+
return "zai";
|
|
11589
|
+
if (hasOpenCodeGoPrefix(value))
|
|
11590
|
+
return "opencode-go";
|
|
11591
|
+
if (hasOpenRouterPrefix(value))
|
|
11592
|
+
return "openrouter";
|
|
11593
|
+
return "own";
|
|
11594
|
+
}
|
|
11595
|
+
function providerChainHostProvider(value) {
|
|
11596
|
+
if (typeof value !== "string" || !value)
|
|
11597
|
+
return null;
|
|
11598
|
+
if (hasOpenRouterPrefix(value))
|
|
11599
|
+
return "openrouter";
|
|
11600
|
+
if (hasOpenCodeGoPrefix(value))
|
|
11601
|
+
return "opencode-go";
|
|
11602
|
+
if (hasZaiPrefix(value))
|
|
11603
|
+
return "zai";
|
|
11604
|
+
const key = chainKey(value);
|
|
11605
|
+
return API_MODEL_CATALOG.find((m) => m.id === key)?.provider ?? GEMINI_ASSISTANT_MODELS.find((m) => m.id === key)?.provider ?? null;
|
|
11606
|
+
}
|
|
11607
|
+
function providerHostPlan(modelId) {
|
|
11608
|
+
const key = chainKey(modelId);
|
|
11609
|
+
if (!key)
|
|
11610
|
+
return [];
|
|
11611
|
+
const own = { host: hasOpenRouterPrefix(key) ? "openrouter" : "own", value: key };
|
|
11612
|
+
const steps = [
|
|
11613
|
+
...providerChainLeadsFor(key).map((value) => ({ host: providerChainHostOf(value, key), value })),
|
|
11614
|
+
own,
|
|
11615
|
+
...providerChainFor(key).map((value) => ({ host: providerChainHostOf(value, key), value }))
|
|
11616
|
+
];
|
|
11617
|
+
const notOffered = Object.prototype.hasOwnProperty.call(PROVIDER_HOST_NOT_OFFERED, key) ? PROVIDER_HOST_NOT_OFFERED[key] : {};
|
|
11618
|
+
for (const [host, reason] of Object.entries(notOffered)) {
|
|
11619
|
+
const at = steps.findIndex((step) => step === own) + 1;
|
|
11620
|
+
steps.splice(at, 0, { host, value: null, notOffered: reason });
|
|
11621
|
+
}
|
|
11622
|
+
return steps;
|
|
11623
|
+
}
|
|
11624
|
+
function providerChainModelKey(modelId) {
|
|
11625
|
+
return chainKey(modelId);
|
|
11626
|
+
}
|
|
11627
|
+
function hasProviderChainLeadsByDefault(modelId) {
|
|
11628
|
+
const key = chainKey(modelId);
|
|
11629
|
+
return key !== null && has(PROVIDER_CHAIN_LEADS, key);
|
|
11630
|
+
}
|
|
11631
|
+
function isChainablePrimary(id) {
|
|
11632
|
+
return typeof id === "string" && id !== "" && (has(PROVIDER_CHAINS, id) || has(PROVIDER_CHAIN_LEADS, id));
|
|
11633
|
+
}
|
|
11634
|
+
function defaultProviderChainEntries(primary) {
|
|
11635
|
+
if (!isChainablePrimary(primary))
|
|
11636
|
+
return [];
|
|
11637
|
+
const leads = has(PROVIDER_CHAIN_LEADS, primary) ? PROVIDER_CHAIN_LEADS[primary] : [];
|
|
11638
|
+
const hops = has(PROVIDER_CHAINS, primary) ? PROVIDER_CHAINS[primary] : [];
|
|
11639
|
+
return [...leads, primary, ...hops].map((hop) => ({ hop, enabled: true }));
|
|
11640
|
+
}
|
|
11641
|
+
function allowedProviderChainHosts(primary) {
|
|
11642
|
+
return defaultProviderChainEntries(primary).map((entry) => entry.hop);
|
|
11643
|
+
}
|
|
11644
|
+
function isPlainObject2(value) {
|
|
11645
|
+
return value !== null && typeof value === "object" && !Array.isArray(value) && Object.getPrototypeOf(value) === Object.prototype;
|
|
11646
|
+
}
|
|
11647
|
+
function validateProviderChainEntries(primary, entries) {
|
|
11648
|
+
if (!isChainablePrimary(primary))
|
|
11649
|
+
return `${String(primary).slice(0, 80)} has no provider chain to order`;
|
|
11650
|
+
if (!Array.isArray(entries))
|
|
11651
|
+
return `${primary}: the chain must be a list`;
|
|
11652
|
+
if (entries.length > PROVIDER_CHAIN_MAX_ENTRIES)
|
|
11653
|
+
return `${primary}: at most ${PROVIDER_CHAIN_MAX_ENTRIES} hosts`;
|
|
11654
|
+
const allowed = allowedProviderChainHosts(primary);
|
|
11655
|
+
const seen = /* @__PURE__ */ new Set();
|
|
11656
|
+
for (const entry of entries) {
|
|
11657
|
+
if (!isPlainObject2(entry))
|
|
11658
|
+
return `${primary}: each host must be { hop, enabled }`;
|
|
11659
|
+
const keys = Object.keys(entry);
|
|
11660
|
+
if (keys.length !== 2 || !keys.includes("hop") || !keys.includes("enabled"))
|
|
11661
|
+
return `${primary}: each host must be { hop, enabled } and nothing else`;
|
|
11662
|
+
const { hop, enabled } = entry;
|
|
11663
|
+
if (typeof enabled !== "boolean")
|
|
11664
|
+
return `${primary}: enabled must be true or false`;
|
|
11665
|
+
if (typeof hop !== "string" || !hop)
|
|
11666
|
+
return `${primary}: hop must be a model value`;
|
|
11667
|
+
if (!allowed.includes(hop))
|
|
11668
|
+
return `${primary}: ${hop.slice(0, 80)} is not a host of this model`;
|
|
11669
|
+
if (seen.has(hop))
|
|
11670
|
+
return `${primary}: ${hop} is listed twice`;
|
|
11671
|
+
if (hop === primary && !enabled)
|
|
11672
|
+
return `${primary}: the model's own value stays on (pick another model on AI Models instead)`;
|
|
11673
|
+
seen.add(hop);
|
|
11674
|
+
}
|
|
11675
|
+
if (!seen.has(primary))
|
|
11676
|
+
return `${primary}: list the model's own value once, where its own host sits`;
|
|
11677
|
+
return null;
|
|
11678
|
+
}
|
|
11679
|
+
function decodeProviderChains(raw) {
|
|
11680
|
+
let value = raw;
|
|
11681
|
+
if (typeof raw === "string") {
|
|
11682
|
+
if (raw.length > PROVIDER_CHAINS_SETTING_MAX_CHARS)
|
|
11683
|
+
return { ok: false, error: `AI_PROVIDER_CHAINS is too long (at most ${PROVIDER_CHAINS_SETTING_MAX_CHARS} characters)` };
|
|
11684
|
+
try {
|
|
11685
|
+
value = JSON.parse(raw);
|
|
11686
|
+
} catch {
|
|
11687
|
+
return { ok: false, error: "AI_PROVIDER_CHAINS must be valid JSON" };
|
|
11688
|
+
}
|
|
11689
|
+
} else if (JSON.stringify(raw ?? null).length > PROVIDER_CHAINS_SETTING_MAX_CHARS) {
|
|
11690
|
+
return { ok: false, error: `AI_PROVIDER_CHAINS is too long (at most ${PROVIDER_CHAINS_SETTING_MAX_CHARS} characters)` };
|
|
11691
|
+
}
|
|
11692
|
+
if (!isPlainObject2(value))
|
|
11693
|
+
return { ok: false, error: 'AI_PROVIDER_CHAINS must be an object: { "<model>": [ { "hop": "<model value>", "enabled": true } ] }' };
|
|
11694
|
+
if (Object.keys(value).length > PROVIDER_CHAIN_MAX_PRIMARIES)
|
|
11695
|
+
return { ok: false, error: `AI_PROVIDER_CHAINS names at most ${PROVIDER_CHAIN_MAX_PRIMARIES} models` };
|
|
11696
|
+
return { ok: true, value };
|
|
11697
|
+
}
|
|
11698
|
+
function checkedProviderChains(value) {
|
|
11699
|
+
const out = {};
|
|
11700
|
+
for (const [primary, entries] of Object.entries(value)) {
|
|
11701
|
+
const problem = validateProviderChainEntries(primary, entries);
|
|
11702
|
+
if (problem)
|
|
11703
|
+
return { ok: false, error: problem };
|
|
11704
|
+
out[primary] = entries.map(({ hop, enabled }) => ({ hop, enabled }));
|
|
11705
|
+
}
|
|
11706
|
+
return { ok: true, value: out };
|
|
11707
|
+
}
|
|
11708
|
+
function parseProviderChainOverrides(raw) {
|
|
11709
|
+
const decoded = decodeProviderChains(raw);
|
|
11710
|
+
return decoded.ok ? checkedProviderChains(decoded.value) : decoded;
|
|
11711
|
+
}
|
|
11712
|
+
function parseStoredProviderChainOverrides(raw) {
|
|
11713
|
+
const decoded = decodeProviderChains(raw);
|
|
11714
|
+
if (!decoded.ok)
|
|
11715
|
+
return decoded;
|
|
11716
|
+
const dropped = [];
|
|
11717
|
+
const kept = {};
|
|
11718
|
+
for (const [primary, entries] of Object.entries(decoded.value)) {
|
|
11719
|
+
if (!isChainablePrimary(primary) || !Array.isArray(entries)) {
|
|
11720
|
+
kept[primary] = entries;
|
|
11721
|
+
continue;
|
|
11722
|
+
}
|
|
11723
|
+
const allowed = allowedProviderChainHosts(primary);
|
|
11724
|
+
kept[primary] = entries.filter((entry) => {
|
|
11725
|
+
const hop = isPlainObject2(entry) ? entry.hop : void 0;
|
|
11726
|
+
if (typeof hop !== "string" || hop === "" || allowed.includes(hop))
|
|
11727
|
+
return true;
|
|
11728
|
+
dropped.push({ primary, hop: hop.slice(0, 80) });
|
|
11729
|
+
return false;
|
|
11730
|
+
});
|
|
11731
|
+
}
|
|
11732
|
+
const checked = checkedProviderChains(kept);
|
|
11733
|
+
return checked.ok ? { ...checked, dropped } : checked;
|
|
11734
|
+
}
|
|
11735
|
+
function validateProviderChainsSetting(raw) {
|
|
11736
|
+
const parsed = parseProviderChainOverrides(raw);
|
|
11737
|
+
return parsed.ok ? null : parsed.error;
|
|
11738
|
+
}
|
|
11739
|
+
function setProviderChainOverrides(value, onDropped) {
|
|
11740
|
+
const parsed = value === null || value === void 0 || value === "" ? null : parseStoredProviderChainOverrides(value);
|
|
11741
|
+
if (parsed?.ok && parsed.dropped.length > 0)
|
|
11742
|
+
onDropped?.(parsed.dropped);
|
|
11743
|
+
_overrides = parsed?.ok ? parsed.value : null;
|
|
11744
|
+
_overrideLeads = {};
|
|
11745
|
+
_overrideHops = {};
|
|
11746
|
+
for (const [primary, entries] of Object.entries(_overrides ?? {})) {
|
|
11747
|
+
const at = entries.findIndex((entry) => entry.hop === primary);
|
|
11748
|
+
const on = (list) => list.filter((entry) => entry.enabled).map((entry) => entry.hop);
|
|
11749
|
+
_overrideLeads[primary] = on(entries.slice(0, at));
|
|
11750
|
+
_overrideHops[primary] = on(entries.slice(at + 1));
|
|
11751
|
+
}
|
|
11752
|
+
return parsed === null || parsed.ok;
|
|
11753
|
+
}
|
|
11754
|
+
function getProviderChainOverrides() {
|
|
11755
|
+
return _overrides;
|
|
10929
11756
|
}
|
|
10930
|
-
var PROVIDER_CHAINS;
|
|
11757
|
+
var PROVIDER_CHAINS, PROVIDER_CHAIN_LEADS, PROVIDER_HOST_NOT_OFFERED, PROVIDER_CHAIN_MAX_ENTRIES, PROVIDER_CHAINS_SETTING_MAX_CHARS, PROVIDER_CHAIN_MAX_PRIMARIES, ALLOWED_PROVIDER_CHAIN_HOPS, _overrides, _overrideLeads, _overrideHops;
|
|
10931
11758
|
var init_provider_chains = __esm({
|
|
10932
11759
|
"../shared/dist/provider-chains.js"() {
|
|
10933
11760
|
"use strict";
|
|
10934
11761
|
init_api_model_catalog();
|
|
11762
|
+
init_engine_spec();
|
|
10935
11763
|
PROVIDER_CHAINS = {
|
|
10936
|
-
"minimax-m3": ["
|
|
10937
|
-
"minimax-m3.1-flash": ["
|
|
11764
|
+
"minimax-m3": ["opencode-go:minimax-m3", "openrouter:minimax/minimax-m3"],
|
|
11765
|
+
"minimax-m3.1-flash": ["opencode-go:minimax-m3.1-flash", "openrouter:minimax/minimax-m3.1-flash"],
|
|
10938
11766
|
"muse-spark-1.3-contributor": ["openrouter:meta/muse-spark-1.3-contributor"],
|
|
10939
|
-
"
|
|
10940
|
-
"
|
|
10941
|
-
"
|
|
11767
|
+
"claude-haiku-5-5": ["opencode-go:claude-haiku-5-5", "openrouter:anthropic/claude-haiku-5.5"],
|
|
11768
|
+
"gemini-3.1-pro-preview": ["openrouter:google/gemini-3.1-pro-preview"],
|
|
11769
|
+
"deepseek-flash": ["opencode-go:deepseek-v4.1-flash", "openrouter:deepseek/deepseek-v4.1-flash"],
|
|
11770
|
+
"deepseek-v4-pro": ["opencode-go:deepseek-v4-pro", "openrouter:deepseek/deepseek-v4-pro"]
|
|
11771
|
+
};
|
|
11772
|
+
PROVIDER_CHAIN_LEADS = {
|
|
11773
|
+
"openrouter:z-ai/glm-5.3-flash": ["zai:glm-5.3-flash", "opencode-go:glm-5.3-flash"],
|
|
11774
|
+
"openrouter:z-ai/glm-5.3": ["zai:glm-5.3", "opencode-go:glm-5.3"],
|
|
11775
|
+
"openrouter:z-ai/glm-5.2": ["zai:glm-5.2", "opencode-go:glm-5.2"]
|
|
11776
|
+
};
|
|
11777
|
+
PROVIDER_HOST_NOT_OFFERED = {
|
|
11778
|
+
"muse-spark-1.3-contributor": { "opencode-go": "OpenCode Go serves Muse only on its Responses API" },
|
|
11779
|
+
"gemini-3.1-pro-preview": { "opencode-go": "OpenCode Go does not serve Gemini" }
|
|
10942
11780
|
};
|
|
11781
|
+
PROVIDER_CHAIN_MAX_ENTRIES = 8;
|
|
11782
|
+
PROVIDER_CHAINS_SETTING_MAX_CHARS = 16e3;
|
|
11783
|
+
PROVIDER_CHAIN_MAX_PRIMARIES = 32;
|
|
11784
|
+
ALLOWED_PROVIDER_CHAIN_HOPS = /* @__PURE__ */ new Set([
|
|
11785
|
+
...Object.values(PROVIDER_CHAINS).flat(),
|
|
11786
|
+
...Object.values(PROVIDER_CHAIN_LEADS).flat()
|
|
11787
|
+
]);
|
|
11788
|
+
_overrides = null;
|
|
11789
|
+
_overrideLeads = {};
|
|
11790
|
+
_overrideHops = {};
|
|
11791
|
+
}
|
|
11792
|
+
});
|
|
11793
|
+
|
|
11794
|
+
// ../shared/dist/provider-accounts.js
|
|
11795
|
+
function modelNames(modelId) {
|
|
11796
|
+
const value = modelId.trim();
|
|
11797
|
+
const names = /* @__PURE__ */ new Set([value]);
|
|
11798
|
+
for (const prefix of HOST_PREFIXES) {
|
|
11799
|
+
if (!value.startsWith(prefix))
|
|
11800
|
+
continue;
|
|
11801
|
+
const bare = value.slice(prefix.length);
|
|
11802
|
+
names.add(bare);
|
|
11803
|
+
const slash = bare.lastIndexOf("/");
|
|
11804
|
+
if (slash >= 0)
|
|
11805
|
+
names.add(bare.slice(slash + 1));
|
|
11806
|
+
}
|
|
11807
|
+
const canonical = canonicalModelId(value);
|
|
11808
|
+
if (canonical) {
|
|
11809
|
+
names.add(canonical);
|
|
11810
|
+
const entry = API_MODEL_CATALOG.find((m) => m.id === canonical || m.apiModel === canonical);
|
|
11811
|
+
if (entry) {
|
|
11812
|
+
names.add(entry.id);
|
|
11813
|
+
if (entry.apiModel)
|
|
11814
|
+
names.add(entry.apiModel);
|
|
11815
|
+
}
|
|
11816
|
+
}
|
|
11817
|
+
return names;
|
|
11818
|
+
}
|
|
11819
|
+
function accountServesModel(models, modelId) {
|
|
11820
|
+
if (!models || models.length === 0)
|
|
11821
|
+
return true;
|
|
11822
|
+
if (!modelId)
|
|
11823
|
+
return false;
|
|
11824
|
+
const names = modelNames(modelId);
|
|
11825
|
+
return models.some((model) => names.has(model.trim()));
|
|
11826
|
+
}
|
|
11827
|
+
function normalizeProviderAccountModels(models) {
|
|
11828
|
+
if (!Array.isArray(models))
|
|
11829
|
+
return null;
|
|
11830
|
+
const out = [];
|
|
11831
|
+
for (const model of models) {
|
|
11832
|
+
if (typeof model !== "string")
|
|
11833
|
+
return null;
|
|
11834
|
+
const trimmed = model.trim();
|
|
11835
|
+
if (!PROVIDER_ACCOUNT_MODEL_PATTERN.test(trimmed) || trimmed.includes(".."))
|
|
11836
|
+
return null;
|
|
11837
|
+
if (!out.includes(trimmed))
|
|
11838
|
+
out.push(trimmed);
|
|
11839
|
+
}
|
|
11840
|
+
return out.length > MAX_PROVIDER_ACCOUNT_MODELS ? null : out;
|
|
11841
|
+
}
|
|
11842
|
+
function parseProviderAccountLists(value, providers) {
|
|
11843
|
+
if (!value || typeof value !== "object" || Array.isArray(value))
|
|
11844
|
+
return null;
|
|
11845
|
+
const parsed = {};
|
|
11846
|
+
for (const provider of providers) {
|
|
11847
|
+
const list = value[provider];
|
|
11848
|
+
if (!Array.isArray(list))
|
|
11849
|
+
continue;
|
|
11850
|
+
const accounts = [];
|
|
11851
|
+
for (const raw of list) {
|
|
11852
|
+
if (!raw || typeof raw !== "object")
|
|
11853
|
+
continue;
|
|
11854
|
+
const { id, label, key, models } = raw;
|
|
11855
|
+
if (typeof id !== "string" || !id.trim() || typeof key !== "string" || !key.trim())
|
|
11856
|
+
continue;
|
|
11857
|
+
const modelList = Array.isArray(models) ? models.filter((m) => typeof m === "string" && m.trim() !== "").map((m) => m.trim()) : [];
|
|
11858
|
+
accounts.push({ id: id.trim(), label: typeof label === "string" && label.trim() ? label.trim() : null, key: key.trim(), models: modelList });
|
|
11859
|
+
}
|
|
11860
|
+
if (accounts.length > 0)
|
|
11861
|
+
parsed[provider] = accounts;
|
|
11862
|
+
}
|
|
11863
|
+
return parsed;
|
|
11864
|
+
}
|
|
11865
|
+
var DEFAULT_PROVIDER_ACCOUNT_ID, PROVIDER_ACCOUNT_ID_PATTERN, MAX_PROVIDER_ACCOUNTS, MAX_PROVIDER_ACCOUNT_MODELS, PROVIDER_ACCOUNT_MODEL_PATTERN, OPENCODE_GO_DEEPSEEK_GLM_MODELS, HOST_PREFIXES;
|
|
11866
|
+
var init_provider_accounts = __esm({
|
|
11867
|
+
"../shared/dist/provider-accounts.js"() {
|
|
11868
|
+
"use strict";
|
|
11869
|
+
init_api_model_catalog();
|
|
11870
|
+
DEFAULT_PROVIDER_ACCOUNT_ID = "primary";
|
|
11871
|
+
PROVIDER_ACCOUNT_ID_PATTERN = /^[a-z0-9][a-z0-9-]{0,39}$/;
|
|
11872
|
+
MAX_PROVIDER_ACCOUNTS = 10;
|
|
11873
|
+
MAX_PROVIDER_ACCOUNT_MODELS = 50;
|
|
11874
|
+
PROVIDER_ACCOUNT_MODEL_PATTERN = /^[A-Za-z0-9][A-Za-z0-9._:/-]{0,120}$/;
|
|
11875
|
+
OPENCODE_GO_DEEPSEEK_GLM_MODELS = [
|
|
11876
|
+
"deepseek-v4.1-flash",
|
|
11877
|
+
"deepseek-v4-pro",
|
|
11878
|
+
"glm-5.3-flash",
|
|
11879
|
+
"glm-5.3",
|
|
11880
|
+
"glm-5.2"
|
|
11881
|
+
];
|
|
11882
|
+
HOST_PREFIXES = ["opencode-go:", "zai:", "openrouter:"];
|
|
10943
11883
|
}
|
|
10944
11884
|
});
|
|
10945
11885
|
|
|
@@ -11058,6 +11998,25 @@ var init_project_engine_overrides = __esm({
|
|
|
11058
11998
|
}
|
|
11059
11999
|
});
|
|
11060
12000
|
|
|
12001
|
+
// ../shared/dist/trailing-slashes.js
|
|
12002
|
+
function stripTrailingSlashes(value) {
|
|
12003
|
+
let end = value.length;
|
|
12004
|
+
while (end > 0 && value.charCodeAt(end - 1) === 47)
|
|
12005
|
+
end--;
|
|
12006
|
+
return end === value.length ? value : value.slice(0, end);
|
|
12007
|
+
}
|
|
12008
|
+
function stripTrailingChars(value, isTrailing) {
|
|
12009
|
+
let end = value.length;
|
|
12010
|
+
while (end > 0 && isTrailing.test(value[end - 1]))
|
|
12011
|
+
end--;
|
|
12012
|
+
return end === value.length ? value : value.slice(0, end);
|
|
12013
|
+
}
|
|
12014
|
+
var init_trailing_slashes = __esm({
|
|
12015
|
+
"../shared/dist/trailing-slashes.js"() {
|
|
12016
|
+
"use strict";
|
|
12017
|
+
}
|
|
12018
|
+
});
|
|
12019
|
+
|
|
11061
12020
|
// ../shared/dist/gemini-visual.js
|
|
11062
12021
|
function visualProDailyBudget(raw) {
|
|
11063
12022
|
const parsed = raw && /^\s*\d{1,6}\s*$/.test(raw) ? Number(raw.trim()) : NaN;
|
|
@@ -11071,7 +12030,7 @@ function isRetiredGeminiModel(id) {
|
|
|
11071
12030
|
}
|
|
11072
12031
|
function geminiNativeModelsUrl(base2 = process.env.GEMINI_API_BASE) {
|
|
11073
12032
|
const raw = base2 && base2.trim() || GEMINI_OPENAI_BASE;
|
|
11074
|
-
const native = raw.replace(/\/openai\/?$/, "/")
|
|
12033
|
+
const native = stripTrailingSlashes(raw.replace(/\/openai\/?$/, "/"));
|
|
11075
12034
|
return `${native}/models?pageSize=1000`;
|
|
11076
12035
|
}
|
|
11077
12036
|
function asRecord(value) {
|
|
@@ -11191,6 +12150,7 @@ var KNOWN_INPUT_USD_PER_M, GEMINI_OPENAI_BASE, DEFAULT_GEMINI_SCREEN_MODEL, DEFA
|
|
|
11191
12150
|
var init_gemini_visual = __esm({
|
|
11192
12151
|
"../shared/dist/gemini-visual.js"() {
|
|
11193
12152
|
"use strict";
|
|
12153
|
+
init_trailing_slashes();
|
|
11194
12154
|
KNOWN_INPUT_USD_PER_M = {
|
|
11195
12155
|
"gemini-2.5-flash-lite": 0.1,
|
|
11196
12156
|
"gemini-3.1-flash-lite": 0.25,
|
|
@@ -12554,12 +13514,13 @@ function truncateAtWord(text, max) {
|
|
|
12554
13514
|
const slice = trimmed.slice(0, room + 1);
|
|
12555
13515
|
const lastSpace = slice.lastIndexOf(" ");
|
|
12556
13516
|
const cut = lastSpace > room * 0.5 ? slice.slice(0, lastSpace) : trimmed.slice(0, room);
|
|
12557
|
-
return `${cut
|
|
13517
|
+
return `${stripTrailingChars(cut, TRAILING_PUNCTUATION)}\u2026`;
|
|
12558
13518
|
}
|
|
12559
|
-
var CANVAS_UNSUPPORTED_CODE, CANVAS_UNSUPPORTED_TITLE, CANVAS_UNSUPPORTED_BODY, CANVAS_UNSUPPORTED_NOT_CHARGED, CANVAS_UNSUPPORTED_OVERRIDE_HINT, GAME_ENGINES, RENDERER_ENGINES, ENGINE_TEXT_SIGNATURES, GAME_LIBRARY_TEXT_PATTERNS, ENGINE_PACKAGES, GAME_HINT_PACKAGES, MAX_TAG_CHARS, MAX_CSS_SELECTOR_CHARS, MAX_CSS_BLOCK_CHARS, TAG_NAME_RE, ATTR_RE, INTERACTIVE_TAGS, INTERACTIVE_ROLES, ROOT_SELECTOR_RE, OVERFLOW_HIDDEN_RE, TOUCH_NONE_RE, CODE_TAGS, STATIC_STANDARD_MIN_INTERACTIVE;
|
|
13519
|
+
var CANVAS_UNSUPPORTED_CODE, CANVAS_UNSUPPORTED_TITLE, CANVAS_UNSUPPORTED_BODY, CANVAS_UNSUPPORTED_NOT_CHARGED, CANVAS_UNSUPPORTED_OVERRIDE_HINT, GAME_ENGINES, RENDERER_ENGINES, ENGINE_TEXT_SIGNATURES, GAME_LIBRARY_TEXT_PATTERNS, ENGINE_PACKAGES, GAME_HINT_PACKAGES, MAX_TAG_CHARS, MAX_CSS_SELECTOR_CHARS, MAX_CSS_BLOCK_CHARS, TAG_NAME_RE, ATTR_RE, INTERACTIVE_TAGS, INTERACTIVE_ROLES, ROOT_SELECTOR_RE, OVERFLOW_HIDDEN_RE, TOUCH_NONE_RE, CODE_TAGS, STATIC_STANDARD_MIN_INTERACTIVE, TRAILING_PUNCTUATION;
|
|
12560
13520
|
var init_canvas_support = __esm({
|
|
12561
13521
|
"../shared/dist/canvas-support.js"() {
|
|
12562
13522
|
"use strict";
|
|
13523
|
+
init_trailing_slashes();
|
|
12563
13524
|
CANVAS_UNSUPPORTED_CODE = "canvas_unsupported";
|
|
12564
13525
|
CANVAS_UNSUPPORTED_TITLE = "Games and canvas apps aren't supported yet";
|
|
12565
13526
|
CANVAS_UNSUPPORTED_BODY = "This app draws its screen on a canvas (like a game), so there are no page elements for Validate.QA to read and test. Support for games and canvas apps is coming.";
|
|
@@ -12684,6 +13645,7 @@ var init_canvas_support = __esm({
|
|
|
12684
13645
|
TOUCH_NONE_RE = /touch-action\s{0,8}:\s{0,8}none/;
|
|
12685
13646
|
CODE_TAGS = /* @__PURE__ */ new Set(["script", "link", "flt-glass-pane", "flutter-view", "flt-scene-host"]);
|
|
12686
13647
|
STATIC_STANDARD_MIN_INTERACTIVE = 3;
|
|
13648
|
+
TRAILING_PUNCTUATION = /[\s,;:.\-–—]/u;
|
|
12687
13649
|
}
|
|
12688
13650
|
});
|
|
12689
13651
|
|
|
@@ -12866,22 +13828,22 @@ function joinChainedLines(code) {
|
|
|
12866
13828
|
const trimmed = lines[i].trimStart();
|
|
12867
13829
|
if (last !== -1 && trimmed.startsWith(".") && !trimmed.startsWith("...")) {
|
|
12868
13830
|
if (!lastStripped) {
|
|
12869
|
-
lines[last] =
|
|
13831
|
+
lines[last] = stripLineComment3(lines[last]).trimEnd();
|
|
12870
13832
|
lastStripped = true;
|
|
12871
13833
|
}
|
|
12872
13834
|
if (lines[last].length <= MAX_LINE_CHARS)
|
|
12873
|
-
lines[last] = `${lines[last]} ${
|
|
13835
|
+
lines[last] = `${lines[last]} ${stripLineComment3(trimmed).trimEnd()}`;
|
|
12874
13836
|
lines[i] = "";
|
|
12875
13837
|
continue;
|
|
12876
13838
|
}
|
|
12877
|
-
if (
|
|
13839
|
+
if (stripLineComment3(lines[i]).trim()) {
|
|
12878
13840
|
last = i;
|
|
12879
13841
|
lastStripped = false;
|
|
12880
13842
|
}
|
|
12881
13843
|
}
|
|
12882
13844
|
return lines.join("\n");
|
|
12883
13845
|
}
|
|
12884
|
-
function
|
|
13846
|
+
function stripLineComment3(rawLine) {
|
|
12885
13847
|
if (rawLine.trimStart().startsWith("//"))
|
|
12886
13848
|
return "";
|
|
12887
13849
|
const at = rawLine.indexOf(" // ");
|
|
@@ -12897,7 +13859,7 @@ function stateLiteralsOf(line) {
|
|
|
12897
13859
|
const out = [];
|
|
12898
13860
|
for (const m of line.matchAll(STRING_LITERAL_RE)) {
|
|
12899
13861
|
const text = m[1] ?? m[2] ?? m[3] ?? "";
|
|
12900
|
-
if (
|
|
13862
|
+
if (PLACEHOLDER_ATTR_RE.test(text))
|
|
12901
13863
|
continue;
|
|
12902
13864
|
if (PLACEHOLDER_CALL_TAIL_RE.test(line.slice(Math.max(0, m.index - 40), m.index)))
|
|
12903
13865
|
continue;
|
|
@@ -12930,7 +13892,7 @@ function analyzeReadinessPolling(rawCode) {
|
|
|
12930
13892
|
offset += rawLine.length + 1;
|
|
12931
13893
|
if (rawLine.length > MAX_LINE_CHARS)
|
|
12932
13894
|
continue;
|
|
12933
|
-
const line =
|
|
13895
|
+
const line = stripLineComment3(rawLine);
|
|
12934
13896
|
if (!line.trim())
|
|
12935
13897
|
continue;
|
|
12936
13898
|
const literals = literalsOf(line);
|
|
@@ -12993,7 +13955,7 @@ function formatReadinessPollAdvisory(findings) {
|
|
|
12993
13955
|
const where = findings.length === 1 ? "One place" : `${findings.length} places`;
|
|
12994
13956
|
return `${where} re-check a background state once and assert the finished state at once (${short(first.line, 90)} then ${short(first.assertion, 90)}). ${first.reason}`;
|
|
12995
13957
|
}
|
|
12996
|
-
var READINESS_POLL_MAX_CODE_CHARS, MAX_LINE_CHARS, MAX_FINDINGS, ASSERTION_WINDOW_LINES, MAX_BLOCK_CHARS, MAX_HEAD_CHARS, STRING_LITERAL_RE, RECHECK_NAME_RE, RETRY_NAME_RE, PENDING_STATE_RE, LOCATOR_LINE_RE, CLICK_RE, RELOAD_RE, GOTO_RE, ACTION_RE, EXPECT_RE, NEGATED_RE, PENDING_ENDED_RE, POSITIVE_MATCHER_RE, ZERO_COUNT_RE, TEST_END_RE, TEST_START_RE, POLL_OPEN_RE, PLACEHOLDER_CALL_TAIL_RE, POLL_SHAPE;
|
|
13958
|
+
var READINESS_POLL_MAX_CODE_CHARS, MAX_LINE_CHARS, MAX_FINDINGS, ASSERTION_WINDOW_LINES, MAX_BLOCK_CHARS, MAX_HEAD_CHARS, STRING_LITERAL_RE, RECHECK_NAME_RE, RETRY_NAME_RE, PENDING_STATE_RE, LOCATOR_LINE_RE, CLICK_RE, RELOAD_RE, GOTO_RE, ACTION_RE, EXPECT_RE, NEGATED_RE, PENDING_ENDED_RE, POSITIVE_MATCHER_RE, ZERO_COUNT_RE, TEST_END_RE, TEST_START_RE, POLL_OPEN_RE, PLACEHOLDER_ATTR_RE, PLACEHOLDER_CALL_TAIL_RE, POLL_SHAPE;
|
|
12997
13959
|
var init_readiness_poll = __esm({
|
|
12998
13960
|
"../shared/dist/readiness-poll.js"() {
|
|
12999
13961
|
"use strict";
|
|
@@ -13006,7 +13968,7 @@ var init_readiness_poll = __esm({
|
|
|
13006
13968
|
STRING_LITERAL_RE = /'((?:\\.|[^'\\\n]){0,300})'|"((?:\\.|[^"\\\n]){0,300})"|`((?:\\.|[^`\\\n]){0,300})`/g;
|
|
13007
13969
|
RECHECK_NAME_RE = /^\s{0,10}(?:check again|check (?:status|availability|now|for updates|progress)|re-?check(?: status)?|update status|see (?:latest|updates))\s{0,10}(?:\.\.\.|…)?\s{0,10}$/i;
|
|
13008
13970
|
RETRY_NAME_RE = /^\s{0,10}(?:refresh(?: (?:status|list|page|results|now))?|reload(?: (?:page|results|list))?|try again|retry|load (?:again|latest))\s{0,10}(?:\.\.\.|…)?\s{0,10}$/i;
|
|
13009
|
-
PENDING_STATE_RE = /\b(?:searching|checking|sending|pending|processing|queued|in progress|loading|generating|calculating|preparing|verifying|syncing|refreshing|not ready|please wait|waiting)\b|\b[a-z]{3,
|
|
13971
|
+
PENDING_STATE_RE = /\b(?:searching|checking|sending|pending|processing|queued|in progress|loading|generating|calculating|preparing|verifying|syncing|refreshing|not ready|please wait|waiting)\b|\b(?:saving|uploading|downloading|signing|logging|submitting|creating|updating|deleting|importing|exporting|publishing|connecting|redirecting|authenticating|installing|retrieving|fetching|analy[sz]ing|running|rendering|restarting|building|compiling|booting|provisioning|deploying|indexing|migrating|launching|initiali[sz]ing|optimi[sz]ing|converting|processing)(?:\s{1,3}[a-z]{1,20}){0,2}\s{0,3}(?:\.\.\.|…)\s{0,10}$|^\s{0,10}(?:starting|scanning|working|applying|finishing)(?:\s{1,3}[a-z]{1,20}){0,2}\s{0,3}(?:\.\.\.|…)\s{0,10}$|^\s{0,10}[a-z]{3,40}(?<!th)ing\s{0,3}(?:\.\.\.|…)\s{0,10}$/i;
|
|
13010
13972
|
LOCATOR_LINE_RE = /\.(?:getBy[A-Z]\w{0,30}|locator|toHaveText|toContainText)\s{0,20}\(/;
|
|
13011
13973
|
CLICK_RE = /\.(?:click|dblclick|tap)\s{0,20}\(/;
|
|
13012
13974
|
RELOAD_RE = /\bpage\.reload\s{0,20}\(/;
|
|
@@ -13020,6 +13982,7 @@ var init_readiness_poll = __esm({
|
|
|
13020
13982
|
TEST_END_RE = /^\s{0,20}\}\s{0,20}\)\s{0,20};?\s{0,20}$/;
|
|
13021
13983
|
TEST_START_RE = /\btest(?:\.\w{1,20})?\s{0,20}\(/;
|
|
13022
13984
|
POLL_OPEN_RE = /\bexpect(?:\.soft)?\s{0,20}\(\s{0,20}async\b|\bexpect\.poll\s{0,20}\(|\bfor\s{0,20}\(|\bwhile\s{0,20}\(|\bdo\s{0,20}\{/g;
|
|
13985
|
+
PLACEHOLDER_ATTR_RE = /placeholder\s{0,4}[*^$~|]?=/;
|
|
13023
13986
|
PLACEHOLDER_CALL_TAIL_RE = /getByPlaceholder\s{0,20}\(\s{0,20}$/;
|
|
13024
13987
|
POLL_SHAPE = "await expect(async () => { await page.reload(); await expect(<the finished state>).toBeVisible({ timeout: 1500 }); }).toPass({ timeout: 20000 });";
|
|
13025
13988
|
}
|
|
@@ -13041,7 +14004,7 @@ function blankBlockComments2(code) {
|
|
|
13041
14004
|
from = close + 2;
|
|
13042
14005
|
}
|
|
13043
14006
|
}
|
|
13044
|
-
function
|
|
14007
|
+
function stripLineComment4(rawLine) {
|
|
13045
14008
|
if (rawLine.trimStart().startsWith("//"))
|
|
13046
14009
|
return "";
|
|
13047
14010
|
const at = rawLine.indexOf(" // ");
|
|
@@ -13052,7 +14015,7 @@ function logicalLines(code) {
|
|
|
13052
14015
|
for (const raw of code.split("\n")) {
|
|
13053
14016
|
if (raw.length > MAX_LINE_CHARS2)
|
|
13054
14017
|
continue;
|
|
13055
|
-
const line =
|
|
14018
|
+
const line = stripLineComment4(raw).trimEnd();
|
|
13056
14019
|
if (!line.trim())
|
|
13057
14020
|
continue;
|
|
13058
14021
|
const trimmed = line.trimStart();
|
|
@@ -13414,13 +14377,33 @@ function short4(text, max) {
|
|
|
13414
14377
|
const flat = text.replace(/\s+/g, " ").trim();
|
|
13415
14378
|
return flat.length > max ? `${flat.slice(0, max - 1)}\u2026` : flat;
|
|
13416
14379
|
}
|
|
14380
|
+
function stripFractionZeros(s) {
|
|
14381
|
+
let end = s.length;
|
|
14382
|
+
while (end > 0 && s[end - 1] === "0")
|
|
14383
|
+
end--;
|
|
14384
|
+
if (end === s.length)
|
|
14385
|
+
return s;
|
|
14386
|
+
const dot = s.lastIndexOf(".");
|
|
14387
|
+
if (dot < 0)
|
|
14388
|
+
return s;
|
|
14389
|
+
return s.slice(0, end === dot + 1 ? dot : end);
|
|
14390
|
+
}
|
|
13417
14391
|
function digitsOf(token) {
|
|
13418
|
-
return token.replace(/[^\d.]/g, "")
|
|
14392
|
+
return stripFractionZeros(token.replace(/[^\d.]/g, "")).replace(/^0+(?=\d)/, "");
|
|
13419
14393
|
}
|
|
13420
14394
|
function computes(line) {
|
|
13421
14395
|
const code = line.replace(STRING_RE, '""').replace(REGEX_LITERAL_RE, "$1RX").replace(LITERAL_ARITH_RE, "N");
|
|
13422
14396
|
return COMPUTE_RE.test(code) || RELATIONAL_RE.test(line);
|
|
13423
14397
|
}
|
|
14398
|
+
function identifiersIn(line) {
|
|
14399
|
+
const code = line.replace(STRING_RE, '""').replace(REGEX_LITERAL_RE, "$1RX");
|
|
14400
|
+
return [code, ...[...line.matchAll(TEMPLATE_EXPR_RE)].map((m) => m[1])].flatMap((part) => part.match(IDENTIFIER_RE) ?? []);
|
|
14401
|
+
}
|
|
14402
|
+
function addTyped(typed, value) {
|
|
14403
|
+
const d = digitsOf(value);
|
|
14404
|
+
if (/\d/.test(d))
|
|
14405
|
+
typed.add(d);
|
|
14406
|
+
}
|
|
13424
14407
|
function reasonFor4(kind, label, value) {
|
|
13425
14408
|
const what = kind === "derived-value-shape-only" ? `The ${label} is checked only for its shape, so any amount passes, including a wrongly computed one.` : `The ${label} is checked against a number written into the test (${value}). That number was read off the page, so when the page computes it wrong the check still passes.`;
|
|
13426
14409
|
return `${what} Instead, ${COMPUTE_SHAPE} If the number is not computed from other values (a list price, an id, a fixed fee), leave it as it is. Keep the existing checks.`;
|
|
@@ -13432,25 +14415,32 @@ function analyzeDerivedValues(rawCode) {
|
|
|
13432
14415
|
for (const body of testBodies(rawCode)) {
|
|
13433
14416
|
if (findings.length >= MAX_FINDINGS4)
|
|
13434
14417
|
break;
|
|
13435
|
-
if (body.some(computes))
|
|
13436
|
-
continue;
|
|
13437
14418
|
const typed = /* @__PURE__ */ new Set();
|
|
13438
14419
|
const definitions = /* @__PURE__ */ new Map();
|
|
14420
|
+
const computed = /* @__PURE__ */ new Set();
|
|
14421
|
+
const usesComputed = (text) => identifiersIn(text).some((id) => computed.has(id));
|
|
13439
14422
|
for (const line of body) {
|
|
13440
14423
|
if (FILL_RE2.test(line) && !EXPECT_RE3.test(line)) {
|
|
13441
|
-
for (const m of line.matchAll(
|
|
13442
|
-
|
|
13443
|
-
|
|
13444
|
-
|
|
13445
|
-
|
|
14424
|
+
for (const m of line.matchAll(QUOTED_RE))
|
|
14425
|
+
addTyped(typed, m[1]);
|
|
14426
|
+
const arg = FILL_ARG_RE.exec(line)?.[1];
|
|
14427
|
+
const literal = arg === void 0 ? null : LITERAL_VALUE_RE.exec(definitions.get(arg) ?? "");
|
|
14428
|
+
const value = literal?.slice(1).find((v) => v !== void 0);
|
|
14429
|
+
if (value !== void 0)
|
|
14430
|
+
addTyped(typed, value);
|
|
13446
14431
|
}
|
|
13447
14432
|
const def = DEFINITION_RE.exec(line);
|
|
13448
|
-
if (def)
|
|
14433
|
+
if (def) {
|
|
13449
14434
|
definitions.set(def[1], def[2]);
|
|
14435
|
+
if (computes(def[2]) || usesComputed(def[2]))
|
|
14436
|
+
computed.add(def[1]);
|
|
14437
|
+
}
|
|
13450
14438
|
}
|
|
13451
14439
|
for (const line of body) {
|
|
13452
14440
|
if (!EXPECT_RE3.test(line) || !VALUE_MATCHER_RE2.test(line) || /\.not\s{0,20}\./.test(line))
|
|
13453
14441
|
continue;
|
|
14442
|
+
if (computes(line) || usesComputed(line))
|
|
14443
|
+
continue;
|
|
13454
14444
|
const subject = EXPECT_SUBJECT_RE.exec(line)?.[1];
|
|
13455
14445
|
const label = DERIVED_WORD_RE.exec(`${line} ${(subject && definitions.get(subject)) ?? ""}`)?.[0];
|
|
13456
14446
|
if (!label)
|
|
@@ -13480,7 +14470,7 @@ function formatDerivedValueAdvisory(findings) {
|
|
|
13480
14470
|
const where = findings.length === 1 ? "One computed value is" : `${findings.length} computed values are`;
|
|
13481
14471
|
return `${where} checked against a copied number or only its shape (${short4(first.line, 90)}). ${first.reason}`;
|
|
13482
14472
|
}
|
|
13483
|
-
var MAX_FINDINGS4, EXPECT_RE3, DERIVED_WORD_RE, VALUE_MATCHER_RE2, MONEY_TOKEN_RE, SHAPE_ARG_RE, FILL_RE2, EXPECT_MESSAGE_RE, STRING_RE, REGEX_LITERAL_RE, COMPUTE_RE, RELATIONAL_RE, DEFINITION_RE, EXPECT_SUBJECT_RE, LITERAL_ARITH_RE, COMPUTE_SHAPE;
|
|
14473
|
+
var MAX_FINDINGS4, EXPECT_RE3, DERIVED_WORD_RE, VALUE_MATCHER_RE2, MONEY_TOKEN_RE, SHAPE_ARG_RE, FILL_RE2, EXPECT_MESSAGE_RE, STRING_RE, REGEX_LITERAL_RE, COMPUTE_RE, RELATIONAL_RE, DEFINITION_RE, EXPECT_SUBJECT_RE, LITERAL_ARITH_RE, TEMPLATE_EXPR_RE, IDENTIFIER_RE, QUOTED_RE, FILL_ARG_RE, LITERAL_VALUE_RE, COMPUTE_SHAPE;
|
|
13484
14474
|
var init_derived_value_check = __esm({
|
|
13485
14475
|
"../shared/dist/derived-value-check.js"() {
|
|
13486
14476
|
"use strict";
|
|
@@ -13500,6 +14490,11 @@ var init_derived_value_check = __esm({
|
|
|
13500
14490
|
DEFINITION_RE = /\b(?:const|let)\s{1,10}([\w$]{1,60})\s{0,10}=\s{0,10}([^\n]{0,300})/;
|
|
13501
14491
|
EXPECT_SUBJECT_RE = /\bexpect(?:\.soft)?\s{0,20}\(\s{0,20}([\w$]{1,60})/;
|
|
13502
14492
|
LITERAL_ARITH_RE = /\b\d[\d_.]{0,15}(?:\s{0,5}[*/+-]\s{0,5}\d[\d_.]{0,15}){1,8}\b/g;
|
|
14493
|
+
TEMPLATE_EXPR_RE = /\$\{([^{}\n]{0,200})\}/g;
|
|
14494
|
+
IDENTIFIER_RE = /[A-Za-z_$][\w$]{0,60}/g;
|
|
14495
|
+
QUOTED_RE = /['"`]([^'"`\n]{0,40})['"`]/g;
|
|
14496
|
+
FILL_ARG_RE = /\.(?:fill|type|pressSequentially|selectOption)\s{0,20}\(\s{0,20}([A-Za-z_$][\w$]{0,60})\s{0,20}[,)]/;
|
|
14497
|
+
LITERAL_VALUE_RE = /^\s{0,10}(?:'([^'\n]{0,40})'|"([^"\n]{0,40})"|`([^`$\n]{0,40})`|(\d[\d_,]{0,15}(?:\.\d{1,4})?))\s{0,10};?\s{0,10}$/;
|
|
13503
14498
|
COMPUTE_SHAPE = "compute the expected value in the test from its inputs (the values the test typed, or each input read from the page) and compare: const toNumber = (s) => Number(s.replace(/[^\\d.-]/g, '')); const unit = toNumber(await <unit price locator>.innerText()); then expect(toNumber(await <shown value locator>.innerText())).toBeCloseTo(unit * quantity, 2); for several lines, read them all and sum them with reduce. For a comma-decimal price like \u20AC1.234,56, drop the dots and turn the comma into a point before Number, since the replace above would read it as 1.23456.";
|
|
13504
14499
|
}
|
|
13505
14500
|
});
|
|
@@ -13612,7 +14607,7 @@ function findExpectCalls(code) {
|
|
|
13612
14607
|
if (!subj)
|
|
13613
14608
|
continue;
|
|
13614
14609
|
const rest = code.slice(subj.end + 1, subj.end + 1 + 80);
|
|
13615
|
-
const mm = /^\s*((?:\.\s*not)
|
|
14610
|
+
const mm = /^\s*((?:\.\s*not\s*)?\.\s*(\w+))\s{0,20}\(/.exec(rest);
|
|
13616
14611
|
if (!mm)
|
|
13617
14612
|
continue;
|
|
13618
14613
|
const argStart = subj.end + 1 + mm[0].length;
|
|
@@ -15534,6 +16529,133 @@ function formatStaleAfterSubmitAdvisory(rawCode) {
|
|
|
15534
16529
|
const more = described.length > 3 ? ` (+${described.length - 3} more)` : "";
|
|
15535
16530
|
return `stale-after-submit: ${described.slice(0, 3).join("; ")}${more}; these pass before the request lands and still pass if the server rejects or ignores the save. Wait for the result instead: page.waitForResponse around the click, or assert something the submit creates (the new row, the confirmation message, a path with an end anchor such as /\\/orders\\/\\d+$/)`;
|
|
15536
16531
|
}
|
|
16532
|
+
function regexLiteralEnd4(text, at) {
|
|
16533
|
+
let inClass = false;
|
|
16534
|
+
for (let i = at + 1; i < text.length; i++) {
|
|
16535
|
+
const ch = text[i];
|
|
16536
|
+
if (ch === "\n")
|
|
16537
|
+
return -1;
|
|
16538
|
+
if (ch === "\\") {
|
|
16539
|
+
i++;
|
|
16540
|
+
continue;
|
|
16541
|
+
}
|
|
16542
|
+
if (inClass) {
|
|
16543
|
+
if (ch === "]")
|
|
16544
|
+
inClass = false;
|
|
16545
|
+
continue;
|
|
16546
|
+
}
|
|
16547
|
+
if (ch === "[") {
|
|
16548
|
+
inClass = true;
|
|
16549
|
+
continue;
|
|
16550
|
+
}
|
|
16551
|
+
if (ch === "/")
|
|
16552
|
+
return i + 1;
|
|
16553
|
+
}
|
|
16554
|
+
return -1;
|
|
16555
|
+
}
|
|
16556
|
+
function startsWithRegexLiteral(text) {
|
|
16557
|
+
const t = text.trimStart();
|
|
16558
|
+
return t[0] === "/" && t[1] !== "/" && t[1] !== "*" && regexLiteralEnd4(t, 0) !== -1;
|
|
16559
|
+
}
|
|
16560
|
+
function isBareRegexLiteral(text) {
|
|
16561
|
+
const t = text.trimStart();
|
|
16562
|
+
if (t[0] !== "/" || t[1] === "/" || t[1] === "*")
|
|
16563
|
+
return false;
|
|
16564
|
+
const end = regexLiteralEnd4(t, 0);
|
|
16565
|
+
if (end === -1)
|
|
16566
|
+
return false;
|
|
16567
|
+
let i = end;
|
|
16568
|
+
while (i < t.length && /[a-z]/.test(t[i]))
|
|
16569
|
+
i++;
|
|
16570
|
+
while (i < t.length && /\s/.test(t[i]))
|
|
16571
|
+
i++;
|
|
16572
|
+
return i >= t.length || t[i] === ",";
|
|
16573
|
+
}
|
|
16574
|
+
function regexDeclaredNames(code) {
|
|
16575
|
+
const names = /* @__PURE__ */ new Set();
|
|
16576
|
+
const decl = /\b(?:const|let|var)\s{1,20}([A-Za-z_$][\w$]{0,80})\s{0,20}=\s{0,20}(?:\/(?![/*])|new\s{1,20}RegExp\b)/g;
|
|
16577
|
+
let m;
|
|
16578
|
+
while ((m = decl.exec(code)) !== null)
|
|
16579
|
+
names.add(m[1]);
|
|
16580
|
+
return names;
|
|
16581
|
+
}
|
|
16582
|
+
function afterFirstArg(args) {
|
|
16583
|
+
let depth = 0;
|
|
16584
|
+
let quote = null;
|
|
16585
|
+
for (let i = 0; i < args.length; i++) {
|
|
16586
|
+
const ch = args[i];
|
|
16587
|
+
if (quote) {
|
|
16588
|
+
if (ch === "\\") {
|
|
16589
|
+
i++;
|
|
16590
|
+
continue;
|
|
16591
|
+
}
|
|
16592
|
+
if (ch === quote)
|
|
16593
|
+
quote = null;
|
|
16594
|
+
continue;
|
|
16595
|
+
}
|
|
16596
|
+
if (ch === '"' || ch === "'" || ch === "`") {
|
|
16597
|
+
quote = ch;
|
|
16598
|
+
continue;
|
|
16599
|
+
}
|
|
16600
|
+
if (ch === "(" || ch === "[" || ch === "{")
|
|
16601
|
+
depth++;
|
|
16602
|
+
else if (ch === ")" || ch === "]" || ch === "}")
|
|
16603
|
+
depth--;
|
|
16604
|
+
else if (ch === "," && depth === 0)
|
|
16605
|
+
return args.slice(i + 1);
|
|
16606
|
+
}
|
|
16607
|
+
return "";
|
|
16608
|
+
}
|
|
16609
|
+
function analyzeRegexMatchers(rawCode) {
|
|
16610
|
+
if (!rawCode || typeof rawCode !== "string")
|
|
16611
|
+
return [];
|
|
16612
|
+
const code = stripComments2(rawCode.slice(0, MAX_LINT_INPUT));
|
|
16613
|
+
const out = [];
|
|
16614
|
+
const re = /\bexpect(?:\.soft)?\s{0,20}\(/g;
|
|
16615
|
+
let m;
|
|
16616
|
+
let examined = 0;
|
|
16617
|
+
let line = 1;
|
|
16618
|
+
let lineAt2 = 0;
|
|
16619
|
+
let regexNames;
|
|
16620
|
+
while ((m = re.exec(code)) !== null && examined++ < MAX_CALLS && out.length < MAX_REGEX_MATCHER_FINDINGS) {
|
|
16621
|
+
const subj = readBalanced(code, m.index + m[0].length);
|
|
16622
|
+
if (!subj)
|
|
16623
|
+
continue;
|
|
16624
|
+
const mm = /^\s{0,20}(\.\s{0,20}not)?\s{0,20}\.\s{0,20}(\w{1,40})\s{0,20}\(/.exec(code.slice(subj.end + 1, subj.end + 1 + 80));
|
|
16625
|
+
if (!mm || !REGEX_EQUALITY_MATCHERS.has(mm[2]))
|
|
16626
|
+
continue;
|
|
16627
|
+
const args = readBalanced(code, subj.end + 1 + mm[0].length);
|
|
16628
|
+
if (!args)
|
|
16629
|
+
continue;
|
|
16630
|
+
const expected = mm[2] === "toHaveJSProperty" ? afterFirstArg(args.text) : args.text;
|
|
16631
|
+
if (!isBareRegexLiteral(expected))
|
|
16632
|
+
continue;
|
|
16633
|
+
if (mm[2] !== "toHaveJSProperty" && mm[2] !== "toContain") {
|
|
16634
|
+
const subject = subj.text.trim();
|
|
16635
|
+
if (startsWithRegexLiteral(subject) || /^new\s+RegExp\b/.test(subject))
|
|
16636
|
+
continue;
|
|
16637
|
+
if (/^[A-Za-z_$][\w$]{0,80}$/.test(subject)) {
|
|
16638
|
+
regexNames ??= regexDeclaredNames(code);
|
|
16639
|
+
if (regexNames.has(subject))
|
|
16640
|
+
continue;
|
|
16641
|
+
}
|
|
16642
|
+
}
|
|
16643
|
+
for (let i = code.indexOf("\n", lineAt2); i !== -1 && i < m.index; i = code.indexOf("\n", i + 1)) {
|
|
16644
|
+
line++;
|
|
16645
|
+
lineAt2 = i + 1;
|
|
16646
|
+
}
|
|
16647
|
+
out.push({ line, matcher: mm[2], negated: mm[1] !== void 0 });
|
|
16648
|
+
}
|
|
16649
|
+
return out;
|
|
16650
|
+
}
|
|
16651
|
+
function formatRegexMatcherNote(findings) {
|
|
16652
|
+
const lines = findings.map((f) => {
|
|
16653
|
+
const negated = f.matcher === "toContain" ? "throws on a string and otherwise always passes (a regex never equals an item), so it checks nothing" : "always passes (a regex never equals the value), so it checks nothing";
|
|
16654
|
+
return `Line ${f.line}: .${f.negated ? "not." : ""}${f.matcher}(\u2026 /regex/ \u2026) ${f.negated ? negated : "can never pass: this matcher compares by equality and a regex never equals a value"}.`;
|
|
16655
|
+
});
|
|
16656
|
+
return `${lines.join("\n")}
|
|
16657
|
+
A regex only works in toMatch, toHaveText, toContainText, toHaveValue and similar text matchers. For the \`value\` of an input, textarea or select, use await expect(loc).toHaveValue(/re/). For any other DOM property (or element), poll it so the check still retries: await expect.poll(() => loc.evaluate((n) => n.<prop>)).toMatch(/re/). Never read it once with expect(await loc.evaluate(\u2026)), which sees the value before the page updates it. Or give toHaveJSProperty the exact string (or boolean) the page shows. Keep every other line as-is.`;
|
|
16658
|
+
}
|
|
15537
16659
|
function lintTestQuality(rawCode, opts = {}) {
|
|
15538
16660
|
if (!rawCode || typeof rawCode !== "string")
|
|
15539
16661
|
return [];
|
|
@@ -15541,6 +16663,10 @@ function lintTestQuality(rawCode, opts = {}) {
|
|
|
15541
16663
|
return lintTestData(rawCode, { pageContacts: opts.pageContacts });
|
|
15542
16664
|
if (opts.only === "test-data-blocking")
|
|
15543
16665
|
return analyzeTestData(rawCode, { pageContacts: opts.pageContacts }).blocking;
|
|
16666
|
+
if (opts.only === "regex-matcher") {
|
|
16667
|
+
const found = analyzeRegexMatchers(rawCode);
|
|
16668
|
+
return found.length > 0 ? [formatRegexMatcherNote(found)] : [];
|
|
16669
|
+
}
|
|
15544
16670
|
const warnings = [];
|
|
15545
16671
|
if (rawCode.length > MAX_LINT_INPUT) {
|
|
15546
16672
|
warnings.push(`The code is ${Math.round(rawCode.length / 1024)} KB; the quality lint checked only the first ${MAX_LINT_INPUT / 1e3} KB. Split it into smaller tests (one behaviour per test).`);
|
|
@@ -15815,7 +16941,7 @@ function specItemsFromTestPlans(testPlans) {
|
|
|
15815
16941
|
}
|
|
15816
16942
|
return [...new Set(out)].slice(0, 200);
|
|
15817
16943
|
}
|
|
15818
|
-
var READ_ONLY_TAGS, PRODUCTION_READ_ONLY_GUARD_TEMPLATE, HOST_AGNOSTIC_URL_TEMPLATE, TEST_QUALITY_CONVENTIONS, NON_PROD_HOST_LABEL, NON_PROD_HOST_TOKEN, TRANSIENT_TEXT, STRING_LITERAL, FIRST_LITERAL, GET_BY_TEXT_LITERAL2, ROLE_NAME_LITERAL2, MAX_LINT_INPUT, MAX_BALANCED_SCAN, MAX_CALLS, MAX_SEEN_SCAN, ACTION_CALL, BY_NAME_LOCATOR, ACTION_AFTER, MAX_CONTACTS, MAX_SNAPSHOT_SCAN, PHONE_IN_TEXT, EMAIL_IN_TEXT, TEL_HREF, MAILTO_HREF, FIELD_LINE, INBOX_EMAIL_READ, MAIL_READ_CALL, RESERVED_EMAIL_TLD, PHONE_LABEL, NON_PHONE_NUMBER_LABEL, IDENTITY_LABEL, LOOKUP_LABEL, NON_NAME_SUFFIX_LABEL, DATE_LIKE, SIGNUP_ACTION_NAME, SIGNUP_SUBMIT_NAME, LOGIN_ACTION_NAME, SIGNUP_GOTO, LOGIN_GOTO, MINTED_INBOX_ALIAS, STAMP_EVAL, RECORDING_VALUE_PLACEHOLDERS, NEGATIVE_TEST_TITLE, NEGATIVE_SIGNUP_ASSERT, STORED_LOGIN_RE, CONFIRM_CLICK_RE, SAVE_UPDATE_RE, NOT_A_CONFIRM_RE, ENTITY_WORDS, CREATE_ENTITY_CLICK_RE, RESTORE_CLICK_RE, MAX_ACCOUNT_ACTIONS, MAX_TRY_BLOCKS, MAX_STATEMENT_LOOKBACK, MAX_FINDINGS5, MAX_TRY_BODY, AUTH_REGION_START, AUTH_REGION_END, INBOX_REGION_START, INBOX_REGION_END, ACCOUNT_ROUTE_RE, ENTITY_WORD_RES, PERSIST_CALL_RE, FILL_IDENT_ARG_RE, PERSIST_ARGS_RE, HOST_IN_URL_ARG, URL_WAIT_TOKEN, GLOB_ESCAPED, URL_WAIT_MAX_PATH, URL_WAIT_MAX_GLOB_STARS, UNREACHED_MAX_PATHS, UNREACHED_MATCH_BUDGET, STALE_TOKEN, RESULT_WAIT, WRITE_CALL_PRESENT, SUBMIT_CALL_PRESENT, TEST_CALL, VALUE_WRITE_CALLS, SUBMIT_CALLS, DISTINCT_PAGE_CALLS, OWN_CONTROL_MATCHERS, OUTCOME_CONTENT_MATCHERS, OUTCOME_STATE_MATCHERS, ANY_CONTENT_ARG, ROOT_SUBJECT, LITERAL_SUBJECT, MATCHER_AFTER_EXPECT, STALE_MAX_EVENTS, CHECKED, UNCHECKED, squash, NOT_JUDGED, STALE_REASON_TEXT, LIVE_WRITE_NAME, BUNDLED_CHECKS_BEFORE_WRITE, STOP, SYNONYMS, AREA_TOKENS;
|
|
16944
|
+
var READ_ONLY_TAGS, PRODUCTION_READ_ONLY_GUARD_TEMPLATE, HOST_AGNOSTIC_URL_TEMPLATE, TEST_QUALITY_CONVENTIONS, NON_PROD_HOST_LABEL, NON_PROD_HOST_TOKEN, TRANSIENT_TEXT, STRING_LITERAL, FIRST_LITERAL, GET_BY_TEXT_LITERAL2, ROLE_NAME_LITERAL2, MAX_LINT_INPUT, MAX_BALANCED_SCAN, MAX_CALLS, MAX_SEEN_SCAN, ACTION_CALL, BY_NAME_LOCATOR, ACTION_AFTER, MAX_CONTACTS, MAX_SNAPSHOT_SCAN, PHONE_IN_TEXT, EMAIL_IN_TEXT, TEL_HREF, MAILTO_HREF, FIELD_LINE, INBOX_EMAIL_READ, MAIL_READ_CALL, RESERVED_EMAIL_TLD, PHONE_LABEL, NON_PHONE_NUMBER_LABEL, IDENTITY_LABEL, LOOKUP_LABEL, NON_NAME_SUFFIX_LABEL, DATE_LIKE, SIGNUP_ACTION_NAME, SIGNUP_SUBMIT_NAME, LOGIN_ACTION_NAME, SIGNUP_GOTO, LOGIN_GOTO, MINTED_INBOX_ALIAS, STAMP_EVAL, RECORDING_VALUE_PLACEHOLDERS, NEGATIVE_TEST_TITLE, NEGATIVE_SIGNUP_ASSERT, STORED_LOGIN_RE, CONFIRM_CLICK_RE, SAVE_UPDATE_RE, NOT_A_CONFIRM_RE, ENTITY_WORDS, CREATE_ENTITY_CLICK_RE, RESTORE_CLICK_RE, MAX_ACCOUNT_ACTIONS, MAX_TRY_BLOCKS, MAX_STATEMENT_LOOKBACK, MAX_FINDINGS5, MAX_TRY_BODY, AUTH_REGION_START, AUTH_REGION_END, INBOX_REGION_START, INBOX_REGION_END, ACCOUNT_ROUTE_RE, ENTITY_WORD_RES, PERSIST_CALL_RE, FILL_IDENT_ARG_RE, PERSIST_ARGS_RE, HOST_IN_URL_ARG, URL_WAIT_TOKEN, GLOB_ESCAPED, URL_WAIT_MAX_PATH, URL_WAIT_MAX_GLOB_STARS, UNREACHED_MAX_PATHS, UNREACHED_MATCH_BUDGET, STALE_TOKEN, RESULT_WAIT, WRITE_CALL_PRESENT, SUBMIT_CALL_PRESENT, TEST_CALL, VALUE_WRITE_CALLS, SUBMIT_CALLS, DISTINCT_PAGE_CALLS, OWN_CONTROL_MATCHERS, OUTCOME_CONTENT_MATCHERS, OUTCOME_STATE_MATCHERS, ANY_CONTENT_ARG, ROOT_SUBJECT, LITERAL_SUBJECT, MATCHER_AFTER_EXPECT, STALE_MAX_EVENTS, CHECKED, UNCHECKED, squash, NOT_JUDGED, STALE_REASON_TEXT, LIVE_WRITE_NAME, BUNDLED_CHECKS_BEFORE_WRITE, MAX_REGEX_MATCHER_FINDINGS, REGEX_EQUALITY_MATCHERS, STOP, SYNONYMS, AREA_TOKENS;
|
|
15819
16945
|
var init_test_authoring_quality = __esm({
|
|
15820
16946
|
"../shared/dist/test-authoring-quality.js"() {
|
|
15821
16947
|
"use strict";
|
|
@@ -16047,6 +17173,8 @@ var init_test_authoring_quality = __esm({
|
|
|
16047
17173
|
};
|
|
16048
17174
|
LIVE_WRITE_NAME = /\b(?:submit|place|book|reserve|confirm|pay|send|checkout|order|register|sign\s?up|create account)\b/i;
|
|
16049
17175
|
BUNDLED_CHECKS_BEFORE_WRITE = 5;
|
|
17176
|
+
MAX_REGEX_MATCHER_FINDINGS = 10;
|
|
17177
|
+
REGEX_EQUALITY_MATCHERS = /* @__PURE__ */ new Set(["toBe", "toEqual", "toStrictEqual", "toContain", "toHaveJSProperty"]);
|
|
16050
17178
|
STOP = /* @__PURE__ */ new Set([
|
|
16051
17179
|
"a",
|
|
16052
17180
|
"an",
|
|
@@ -16285,7 +17413,7 @@ function normalizeBugPath(route) {
|
|
|
16285
17413
|
if (hash >= 0 && path[hash + 1] !== "/")
|
|
16286
17414
|
path = path.slice(0, hash);
|
|
16287
17415
|
const segments = path.toLowerCase().split("/").map((segment) => /\d/.test(segment) ? ":id" : segment);
|
|
16288
|
-
const joined = segments.join("/")
|
|
17416
|
+
const joined = stripTrailingSlashes(segments.join("/"));
|
|
16289
17417
|
return joined || (path.startsWith("/") ? "/" : "");
|
|
16290
17418
|
}
|
|
16291
17419
|
function isGenericConsoleMessage(message) {
|
|
@@ -16298,14 +17426,17 @@ function requestSignatures(text) {
|
|
|
16298
17426
|
const statuses = [...text.matchAll(HTTP_STATUS)].map((m) => ({ status: m[1], at: m.index ?? 0, end: (m.index ?? 0) + m[0].length }));
|
|
16299
17427
|
if (statuses.length === 0)
|
|
16300
17428
|
return [];
|
|
16301
|
-
const paths = [...text.matchAll(PATH_IN_TEXT)].map((m) =>
|
|
16302
|
-
|
|
16303
|
-
|
|
16304
|
-
|
|
16305
|
-
|
|
16306
|
-
|
|
16307
|
-
|
|
16308
|
-
|
|
17429
|
+
const paths = [...text.matchAll(PATH_IN_TEXT)].map((m) => {
|
|
17430
|
+
const rawPath = stripTrailingChars(m[2] ?? m[3] ?? "", PATH_TRAILING_PUNCTUATION);
|
|
17431
|
+
return {
|
|
17432
|
+
method: m[1] ?? null,
|
|
17433
|
+
path: normalizeBugPath(rawPath),
|
|
17434
|
+
// Before normalisation, which turns a segment with a digit (favicon-32x32.png) into :id.
|
|
17435
|
+
incidental: isIncidentalAssetPath(rawPath.split(/[?#]/, 1)[0].toLowerCase()),
|
|
17436
|
+
at: m.index ?? 0,
|
|
17437
|
+
end: (m.index ?? 0) + m[0].length
|
|
17438
|
+
};
|
|
17439
|
+
}).filter((p) => p.path.length > 1);
|
|
16309
17440
|
const out = /* @__PURE__ */ new Map();
|
|
16310
17441
|
for (const { status, at, end } of statuses) {
|
|
16311
17442
|
const lead = STATUS_TO_PATH.exec(text.slice(end, end + 40));
|
|
@@ -16415,10 +17546,12 @@ function dedupeSiteWideSuspectedBugs(drafts) {
|
|
|
16415
17546
|
delete plans.suspectedBugs;
|
|
16416
17547
|
}
|
|
16417
17548
|
}
|
|
16418
|
-
var SIGNATURE_SOURCE_MAX_CHARS, UUID, PREFIXED_ID, HEX_RUN, NUMBER_RUN, GENERIC_JS_MESSAGES, GENERIC_JS_PREFIXES, JS_ERROR, MESSAGE_CLAUSE_END, HTTP_STATUS, PATH_IN_TEXT, STATUS_TO_PATH, INCIDENTAL_ASSET;
|
|
17549
|
+
var PATH_TRAILING_PUNCTUATION, SIGNATURE_SOURCE_MAX_CHARS, UUID, PREFIXED_ID, HEX_RUN, NUMBER_RUN, GENERIC_JS_MESSAGES, GENERIC_JS_PREFIXES, JS_ERROR, MESSAGE_CLAUSE_END, HTTP_STATUS, PATH_IN_TEXT, STATUS_TO_PATH, INCIDENTAL_ASSET;
|
|
16419
17550
|
var init_survey_bug_signature = __esm({
|
|
16420
17551
|
"../shared/dist/survey-bug-signature.js"() {
|
|
16421
17552
|
"use strict";
|
|
17553
|
+
init_trailing_slashes();
|
|
17554
|
+
PATH_TRAILING_PUNCTUATION = /[.,;:]/;
|
|
16422
17555
|
SIGNATURE_SOURCE_MAX_CHARS = 800;
|
|
16423
17556
|
UUID = /\b[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}\b/g;
|
|
16424
17557
|
PREFIXED_ID = /\b[a-z]{1,8}[-_]\d+\b/g;
|
|
@@ -17699,9 +18832,9 @@ function lastLocatorLabels(chain) {
|
|
|
17699
18832
|
if (t)
|
|
17700
18833
|
labels.push(t[1]);
|
|
17701
18834
|
}
|
|
17702
|
-
const
|
|
17703
|
-
if (
|
|
17704
|
-
labels.push(
|
|
18835
|
+
const has2 = optionText(last.args, HAS_TEXT_OPTION_RE);
|
|
18836
|
+
if (has2 !== null)
|
|
18837
|
+
labels.push(has2);
|
|
17705
18838
|
}
|
|
17706
18839
|
return labels.map((l) => l.replace(/^[\s^(?:]+/, "").trim());
|
|
17707
18840
|
}
|
|
@@ -17749,7 +18882,7 @@ function pathIdsIn(text) {
|
|
|
17749
18882
|
function findCounterIdPins(code) {
|
|
17750
18883
|
if (code.length > CLOCK_MAX_CODE_CHARS)
|
|
17751
18884
|
return [];
|
|
17752
|
-
const
|
|
18885
|
+
const clean3 = stripLineComments(blankBlockComments3(code));
|
|
17753
18886
|
const fillValues = [];
|
|
17754
18887
|
const candidates = [];
|
|
17755
18888
|
let seenWrite = false;
|
|
@@ -17757,24 +18890,24 @@ function findCounterIdPins(code) {
|
|
|
17757
18890
|
CALL_RE.lastIndex = 0;
|
|
17758
18891
|
let m;
|
|
17759
18892
|
let calls = 0;
|
|
17760
|
-
while (calls++ < MAX_CALLS2 && (m = CALL_RE.exec(
|
|
18893
|
+
while (calls++ < MAX_CALLS2 && (m = CALL_RE.exec(clean3)) !== null) {
|
|
17761
18894
|
const name = m[1];
|
|
17762
18895
|
const open = m.index + m[0].length - 1;
|
|
17763
18896
|
if (VALUE_WRITES.has(name)) {
|
|
17764
18897
|
seenWrite = true;
|
|
17765
|
-
const args2 = argsAt(
|
|
18898
|
+
const args2 = argsAt(clean3, open);
|
|
17766
18899
|
if (args2 !== null)
|
|
17767
18900
|
fillValues.push(...staticStrings(args2));
|
|
17768
18901
|
continue;
|
|
17769
18902
|
}
|
|
17770
18903
|
if (createIndex === -1) {
|
|
17771
|
-
if (name === "click" && seenWrite && isCreateClick(
|
|
18904
|
+
if (name === "click" && seenWrite && isCreateClick(clean3, m.index))
|
|
17772
18905
|
createIndex = m.index;
|
|
17773
18906
|
continue;
|
|
17774
18907
|
}
|
|
17775
18908
|
if (name === "click")
|
|
17776
18909
|
continue;
|
|
17777
|
-
const args = argsAt(
|
|
18910
|
+
const args = argsAt(clean3, open);
|
|
17778
18911
|
if (args === null)
|
|
17779
18912
|
continue;
|
|
17780
18913
|
const isUrl = URL_SINKS.has(name);
|
|
@@ -17793,7 +18926,7 @@ function findCounterIdPins(code) {
|
|
|
17793
18926
|
}
|
|
17794
18927
|
if (createIndex === -1)
|
|
17795
18928
|
return [];
|
|
17796
|
-
const before =
|
|
18929
|
+
const before = clean3.slice(0, createIndex);
|
|
17797
18930
|
let pathIdsBefore = null;
|
|
17798
18931
|
const findings = [];
|
|
17799
18932
|
const seen = /* @__PURE__ */ new Set();
|
|
@@ -17817,7 +18950,7 @@ function findCounterIdPins(code) {
|
|
|
17817
18950
|
continue;
|
|
17818
18951
|
}
|
|
17819
18952
|
}
|
|
17820
|
-
findings.push({ kind: c.kind, literal: c.literal, line: lineAt(
|
|
18953
|
+
findings.push({ kind: c.kind, literal: c.literal, line: lineAt(clean3, c.index) });
|
|
17821
18954
|
}
|
|
17822
18955
|
return findings;
|
|
17823
18956
|
}
|
|
@@ -18112,7 +19245,7 @@ var init_suspicious_expectation = __esm({
|
|
|
18112
19245
|
NOT_STUCK_RE = /\b(?:not|never|no longer|isn't|doesn't|don't|without)\s+(?:\w+\s+){0,2}?(?:stuck|hang|hangs|hung|freeze|freezes|frozen|spin|spins|spinning)\b/i;
|
|
18113
19246
|
LISTS_STUCK_RE = /\b(?:appears? in (?:the )?[\w ]{0,25}(?:report|list|dashboard|queue)|(?:lists?|monitors?|shows?|reports?) (?:\w+ ){0,2}(?:that|which|whose) (?:are |is )?(?:stuck|hangs?|hung|frozen)|can (?:retry|re-?run) (?:\w+ ){0,2}(?:that (?:are )?)?stuck)\b/i;
|
|
18114
19247
|
STUCK_VETO_RE = new RegExp(`${NOT_STUCK_RE.source}|${LISTS_STUCK_RE.source}`, "i");
|
|
18115
|
-
NOT_SILENT_RE = /\b(?:not|never|\w+n't|instead of|rather than|avoids?|prevents?)\s+(?:\w+\s+){0,1}?(?:fail(?:s|ing|ed)?|ignor\w*)
|
|
19248
|
+
NOT_SILENT_RE = /\b(?:not|never|\w+n't|instead of|rather than|avoids?|prevents?)\s+(?:\w+\s+){0,1}?(?:(?:fail(?:s|ing|ed)?|ignor\w*)\s*)?silent(?:ly)?\b/i;
|
|
18116
19249
|
PENDING_WORD = "(?:sending|loading|saving|searching|checking(?!\\s+(?:account|balance|out|in)\\b)|syncing|refreshing|fetching|validating|processing|submitting|uploading|posting|publishing|creating|updating|deleting|connecting|verifying|signing in|logging in)";
|
|
18117
19250
|
ELLIPSIS = "(?:\\.{3}|\u2026)";
|
|
18118
19251
|
NOT_STAY_RE = /\b(?:not|never|no longer|without|n't|cannot)\s+(?:\w+\s+){0,2}?(?:stay|stays|stayed|remain|remains|remained|leave|leaves|left)\b/i;
|
|
@@ -18268,7 +19401,7 @@ function outcomeNamedByAssertion(outcome, code) {
|
|
|
18268
19401
|
function namedByStatements(outcome, statements) {
|
|
18269
19402
|
const text = outcomeText(outcome);
|
|
18270
19403
|
if (outcome.type === "url") {
|
|
18271
|
-
const routes = (text.match(ROUTE_TERM) ?? []).map((r) => r
|
|
19404
|
+
const routes = (text.match(ROUTE_TERM) ?? []).map((r) => stripTrailingChars(r, ROUTE_TRAILING_PUNCTUATION)).filter((r) => r.length > 1);
|
|
18272
19405
|
return statements.some((stmt) => {
|
|
18273
19406
|
if (!/\.(?:waitForURL|toHaveURL)\s{0,20}\(/.test(stmt))
|
|
18274
19407
|
return false;
|
|
@@ -18440,7 +19573,7 @@ function evaluatePromotionGate(input, checks = PROMOTION_QUALITY_CHECKS) {
|
|
|
18440
19573
|
...suspiciousTagged ? { suspiciousPass: { mode: suspicious, covered: suspiciousCovered, wouldHold: suspiciousHold } } : {}
|
|
18441
19574
|
};
|
|
18442
19575
|
}
|
|
18443
|
-
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, QUOTED_LITERAL, GENERIC_LITERAL, MAX_OUTCOME_STATEMENTS, MAX_OUTCOMES, MAX_OUTCOME_TEXT_CHARS, expectedOutcomeCheck, testDataCheck, sharedAccountCheck, clockLiteralCheck, readinessPollCheck, knownBugTestCheck, bugClaimContractCheck, suspiciousExpectationCheck, bugCandidateCheck, conventionOutcomesCheck, pendingHook, locatorFragmentCheck, FILTER_STEP, NEXT_ACTION, REDUCTION_PROOF, MAX_FILTER_LINES, MAX_FILTER_LINE_CHARS, filterPositiveOnlyCheck, PROMOTION_QUALITY_CHECKS, PROMOTION_GATE_MAX_CODE_CHARS, NOT_EVALUATED, SUSPICIOUS_PASS_TAG, NOT_CONTENT_MATCHER, CONTENT_PROOF, NAMED_ELEMENT, NEEDS_REVIEW_TAG, count2, RECHECKED_NOTES, EVIDENCE_NOTE_PREFIXES, PENDING_RECHECK_NOTE_PREFIXES, STRENGTH_RANK;
|
|
19576
|
+
var SOURCE_RANK, strengthCheck, STRING_CONST, TYPED_VALUE, TYPED_CONSTANT, MAX_STATEMENTS, MAX_STATEMENT_CHARS, LITERAL_OR_IDENTIFIER, MUTATING_ACTION, FEEDBACK_REGION, ONE_SHOT_MESSAGE_REASON, FEEDBACK_REGION_ALL, dynamicAssertionCheck, OUTCOME_KIND_MATCHER, ROUTE_TERM, ROUTE_TRAILING_PUNCTUATION, QUOTED_LITERAL, GENERIC_LITERAL, MAX_OUTCOME_STATEMENTS, MAX_OUTCOMES, MAX_OUTCOME_TEXT_CHARS, expectedOutcomeCheck, testDataCheck, sharedAccountCheck, clockLiteralCheck, readinessPollCheck, knownBugTestCheck, bugClaimContractCheck, suspiciousExpectationCheck, bugCandidateCheck, conventionOutcomesCheck, pendingHook, locatorFragmentCheck, FILTER_STEP, NEXT_ACTION, REDUCTION_PROOF, MAX_FILTER_LINES, MAX_FILTER_LINE_CHARS, filterPositiveOnlyCheck, PROMOTION_QUALITY_CHECKS, PROMOTION_GATE_MAX_CODE_CHARS, NOT_EVALUATED, SUSPICIOUS_PASS_TAG, NOT_CONTENT_MATCHER, CONTENT_PROOF, NAMED_ELEMENT, NEEDS_REVIEW_TAG, count2, RECHECKED_NOTES, EVIDENCE_NOTE_PREFIXES, PENDING_RECHECK_NOTE_PREFIXES, STRENGTH_RANK;
|
|
18444
19577
|
var init_promotion_quality_gate = __esm({
|
|
18445
19578
|
"../shared/dist/promotion-quality-gate.js"() {
|
|
18446
19579
|
"use strict";
|
|
@@ -18455,17 +19588,21 @@ var init_promotion_quality_gate = __esm({
|
|
|
18455
19588
|
init_run_classification();
|
|
18456
19589
|
init_expected_outcome_coverage();
|
|
18457
19590
|
init_expect_call();
|
|
19591
|
+
init_trailing_slashes();
|
|
18458
19592
|
SOURCE_RANK = { generator: 0, classifier: 0, healer: 1, human: 2 };
|
|
18459
19593
|
strengthCheck = {
|
|
18460
19594
|
id: "assertion-strength",
|
|
18461
|
-
run({ code, strengthGate, flagContradictedAdditions }) {
|
|
18462
|
-
const verdict = validateAssertionStrength(code, { flagContradictedAdditions: flagContradictedAdditions === true });
|
|
19595
|
+
run({ code, tags, strengthGate, flagContradictedAdditions, lastActionProof }) {
|
|
19596
|
+
const verdict = validateAssertionStrength(code, { flagContradictedAdditions: flagContradictedAdditions === true, lastActionProof });
|
|
18463
19597
|
if (verdict.severity === "route-bug")
|
|
18464
19598
|
return [{ check: "route-bug" }];
|
|
18465
|
-
|
|
19599
|
+
const authDowngrade = verdict.severity === "downgrade" && tags.some((t) => t.toLowerCase() === "@auth");
|
|
19600
|
+
if (verdict.severity !== "block" && !authDowngrade)
|
|
18466
19601
|
return [];
|
|
18467
19602
|
const revealed = strengthGate?.severity === "none" && strengthGate.revealEvidence === "revealed" && strengthGate.source !== "heal";
|
|
18468
|
-
|
|
19603
|
+
if (revealed)
|
|
19604
|
+
return [];
|
|
19605
|
+
return [{ check: authDowngrade ? "strength-downgrade-auth" : "strength-block" }];
|
|
18469
19606
|
}
|
|
18470
19607
|
};
|
|
18471
19608
|
STRING_CONST = /\b(?:const|let|var)\s{1,20}([A-Za-z_$][\w$]{0,60})\s{0,20}=\s{0,20}(?:'([^'\\\n]{0,300})'|"([^"\\\n]{0,300})"|`([^`\\$\n]{0,300})`)\s{0,20}[;\n]/g;
|
|
@@ -18474,7 +19611,7 @@ var init_promotion_quality_gate = __esm({
|
|
|
18474
19611
|
MAX_STATEMENTS = 500;
|
|
18475
19612
|
MAX_STATEMENT_CHARS = 400;
|
|
18476
19613
|
LITERAL_OR_IDENTIFIER = /'(?:[^'\\\n]|\\.){0,500}'|"(?:[^"\\\n]|\\.){0,500}"|`(?:[^`\\]|\\.){0,2000}`|(?<![\w$.])[A-Za-z_$][\w$]{0,60}/g;
|
|
18477
|
-
MUTATING_ACTION = /\.(?:click|dblclick|tap|fill|type|pressSequentially|press|check|uncheck|selectOption|setInputFiles|dragTo|clear)\s{0,20}\(|\brequest\s{0,20}\.\s{0,20}(?:post|put|patch|delete|fetch)\s{0,20}\(/;
|
|
19614
|
+
MUTATING_ACTION = /\.(?:click|dblclick|tap|fill|type|pressSequentially|press|check|uncheck|setChecked|selectOption|setInputFiles|dragTo|dragAndDrop|clear)\s{0,20}\(|\brequest\s{0,20}\.\s{0,20}(?:post|put|patch|delete|fetch)\s{0,20}\(/;
|
|
18478
19615
|
FEEDBACK_REGION = /getByRole\s{0,20}\(\s{0,20}['"`](?:alert|status)['"`]|\baria-live\b|\btoast|\bsnackbar\b|\bflash.?message\b/i;
|
|
18479
19616
|
ONE_SHOT_MESSAGE_REASON = "one-shot success/error banner text";
|
|
18480
19617
|
FEEDBACK_REGION_ALL = /getByRole\s{0,20}\(\s{0,20}['"`](?:alert|status)['"`]|\baria-live\b|\btoast|\bsnackbar\b|\bflash.?message\b/gi;
|
|
@@ -18533,6 +19670,7 @@ var init_promotion_quality_gate = __esm({
|
|
|
18533
19670
|
"element-hidden": /\.(?:toBeHidden|not\s{0,20}\.\s{0,20}toBeVisible|not\s{0,20}\.\s{0,20}toBeAttached|not\s{0,20}\.\s{0,20}toContainText)\s{0,20}\(|\.toHaveCount\s{0,20}\(\s{0,20}0\s{0,20}\)/
|
|
18534
19671
|
};
|
|
18535
19672
|
ROUTE_TERM = /\/[A-Za-z0-9_./?&=#-]{1,200}/g;
|
|
19673
|
+
ROUTE_TRAILING_PUNCTUATION = /[.,;:!?)]/;
|
|
18536
19674
|
QUOTED_LITERAL = /'([^'\\\n]{3,200})'|"([^"\\\n]{3,200})"|`([^`\\$\n]{3,200})`/g;
|
|
18537
19675
|
GENERIC_LITERAL = /^(?:true|false|button|link|heading|dialog|alertdialog|article|main|region|status|alert|textbox|combobox|checkbox|radio|tab|tabpanel|option|list|listitem|row|cell|img|navigation|banner|complementary|contentinfo|form|search|menu|menuitem|visible|hidden|attached|detached|domcontentloaded|load|networkidle)$/i;
|
|
18538
19676
|
MAX_OUTCOME_STATEMENTS = 300;
|
|
@@ -18652,8 +19790,8 @@ ${goalText}`);
|
|
|
18652
19790
|
return count3 > 0 ? [{ check: "locator-fragment", detail: String(count3), soft: true }] : [];
|
|
18653
19791
|
}
|
|
18654
19792
|
};
|
|
18655
|
-
FILTER_STEP = /\.(?:getByLabel|getByPlaceholder|getByRole|getByTitle)\s*\([^)\n]{0,200}?\b(?:filter|sort|search|status|delete|remove|dismiss|archive|clear)\b[^)\n]{0,200}\)[^\n]{0,200}\.(?:selectOption|fill|type|pressSequentially|click|check|uncheck|press)\s*\(/i;
|
|
18656
|
-
NEXT_ACTION = /\.(?:goto|reload|click|dblclick|tap|fill|type|pressSequentially|press|check|uncheck|selectOption|setInputFiles|dragTo|clear)\s*\(/;
|
|
19793
|
+
FILTER_STEP = /\.(?:getByLabel|getByPlaceholder|getByRole|getByTitle)\s*\([^)\n]{0,200}?\b(?:filter|sort|search|status|delete|remove|dismiss|archive|clear)\b[^)\n]{0,200}\)[^\n]{0,200}\.(?:selectOption|fill|type|pressSequentially|click|check|uncheck|setChecked|press)\s*\(/i;
|
|
19794
|
+
NEXT_ACTION = /\.(?:goto|reload|click|dblclick|tap|fill|type|pressSequentially|press|check|uncheck|setChecked|selectOption|setInputFiles|dragTo|dragAndDrop|clear)\s*\(/;
|
|
18657
19795
|
REDUCTION_PROOF = /\.toHaveCount\s*\(|\.not\s*\.\s*(?:toBeVisible|toBeAttached|toContainText|toHaveText)\s*\(|\.toBeHidden\s*\(|\bShowing\b|\bof \d+\b|\b\d+ (?:results?|rows?|items?|candidates?|entries|matches|records)\b|\bNo (?:[\w'-]+ ){0,3}(?:match|matches|results?|found|left|remain|yet)\b|\bNothing (?:to show|found|matches)\b|\bis empty\b|\btoHaveURL\b/i;
|
|
18658
19796
|
MAX_FILTER_LINES = 2e3;
|
|
18659
19797
|
MAX_FILTER_LINE_CHARS = 600;
|
|
@@ -18768,7 +19906,7 @@ ${goalText}`);
|
|
|
18768
19906
|
});
|
|
18769
19907
|
|
|
18770
19908
|
// ../shared/dist/regex-alternation.js
|
|
18771
|
-
function
|
|
19909
|
+
function closingParens2(source) {
|
|
18772
19910
|
const close = new Int32Array(source.length).fill(-1);
|
|
18773
19911
|
const open = [];
|
|
18774
19912
|
let inClass = false;
|
|
@@ -18793,7 +19931,7 @@ function closingParens(source) {
|
|
|
18793
19931
|
return close;
|
|
18794
19932
|
}
|
|
18795
19933
|
function alternatesAtTop(source) {
|
|
18796
|
-
const close =
|
|
19934
|
+
const close = closingParens2(source);
|
|
18797
19935
|
let start = source.startsWith("^") ? 1 : 0;
|
|
18798
19936
|
let end = source.endsWith("$") ? source.length - 1 : source.length;
|
|
18799
19937
|
while (start < end && source[start] === "(" && close[start] === end - 1) {
|
|
@@ -18842,6 +19980,124 @@ var init_page_evidence = __esm({
|
|
|
18842
19980
|
}
|
|
18843
19981
|
});
|
|
18844
19982
|
|
|
19983
|
+
// ../shared/dist/stated-limits.js
|
|
19984
|
+
function validDate(v) {
|
|
19985
|
+
const [y, m, d] = v.split("-").map(Number);
|
|
19986
|
+
const t = new Date(Date.UTC(y, m - 1, d));
|
|
19987
|
+
return t.getUTCFullYear() === y && t.getUTCMonth() === m - 1 && t.getUTCDate() === d;
|
|
19988
|
+
}
|
|
19989
|
+
function isoFor(type, v) {
|
|
19990
|
+
const re = DATE_RE[type];
|
|
19991
|
+
if (!re || !re.test(v))
|
|
19992
|
+
return false;
|
|
19993
|
+
if (type === "date")
|
|
19994
|
+
return validDate(v);
|
|
19995
|
+
if (type === "datetime-local")
|
|
19996
|
+
return validDate(v.slice(0, 10)) && Number(v.slice(11, 13)) < 24 && Number(v.slice(14, 16)) < 60;
|
|
19997
|
+
if (type === "time")
|
|
19998
|
+
return Number(v.slice(0, 2)) < 24 && Number(v.slice(3, 5)) < 60;
|
|
19999
|
+
if (type === "month")
|
|
20000
|
+
return Number(v.slice(5, 7)) >= 1 && Number(v.slice(5, 7)) <= 12;
|
|
20001
|
+
return Number(v.slice(6, 8)) >= 1 && Number(v.slice(6, 8)) <= 53;
|
|
20002
|
+
}
|
|
20003
|
+
function clean2(raw) {
|
|
20004
|
+
return typeof raw === "string" ? raw.trim() : "";
|
|
20005
|
+
}
|
|
20006
|
+
function statedLimitsFromAttrs(attrs, inputType) {
|
|
20007
|
+
const type = (inputType ?? "").trim().toLowerCase();
|
|
20008
|
+
const limits = [];
|
|
20009
|
+
const add = (k, v) => {
|
|
20010
|
+
limits.push({ k, v });
|
|
20011
|
+
};
|
|
20012
|
+
if (NUMBER_TYPES.has(type)) {
|
|
20013
|
+
for (const [attr, k] of [["min", "min"], ["max", "max"]]) {
|
|
20014
|
+
const v = clean2(attrs[attr]);
|
|
20015
|
+
if (NUMBER_RE.test(v))
|
|
20016
|
+
add(k, v);
|
|
20017
|
+
}
|
|
20018
|
+
const step = clean2(attrs.step);
|
|
20019
|
+
if (NUMBER_RE.test(step) && Number(step) > 0)
|
|
20020
|
+
add("step", step);
|
|
20021
|
+
} else if (type in DATE_RE) {
|
|
20022
|
+
for (const [attr, k] of [["min", "min"], ["max", "max"]]) {
|
|
20023
|
+
const v = clean2(attrs[attr]);
|
|
20024
|
+
if (isoFor(type, v))
|
|
20025
|
+
add(k, v);
|
|
20026
|
+
}
|
|
20027
|
+
} else if (TEXT_TYPES.has(type)) {
|
|
20028
|
+
for (const [attr, k] of [["minlength", "minLength"], ["maxlength", "maxLength"]]) {
|
|
20029
|
+
const v = clean2(attrs[attr]);
|
|
20030
|
+
if (LENGTH_RE.test(v))
|
|
20031
|
+
add(k, String(Number(v)));
|
|
20032
|
+
}
|
|
20033
|
+
const pattern = cleanPattern(clean2(attrs.pattern).slice(0, STATED_PATTERN_MAX));
|
|
20034
|
+
if (pattern)
|
|
20035
|
+
add("pattern", pattern);
|
|
20036
|
+
}
|
|
20037
|
+
return limits.length > 0 ? { v: STATED_LIMITS_VERSION, limits } : null;
|
|
20038
|
+
}
|
|
20039
|
+
function cleanPattern(text) {
|
|
20040
|
+
return text.replace(/[\u0000-\u001f\u007f]/g, "").replace(/[\ud800-\udbff](?![\udc00-\udfff])|(?<![\ud800-\udbff])[\udc00-\udfff]/g, "");
|
|
20041
|
+
}
|
|
20042
|
+
function readStatedLimits(stored) {
|
|
20043
|
+
if (!stored || typeof stored !== "object" || Array.isArray(stored))
|
|
20044
|
+
return [];
|
|
20045
|
+
const rec = stored;
|
|
20046
|
+
if (rec.v !== STATED_LIMITS_VERSION || !Array.isArray(rec.limits))
|
|
20047
|
+
return [];
|
|
20048
|
+
const out = [];
|
|
20049
|
+
const seen = /* @__PURE__ */ new Set();
|
|
20050
|
+
for (const item of rec.limits.slice(0, MAX_LIMITS * 2)) {
|
|
20051
|
+
if (!item || typeof item !== "object")
|
|
20052
|
+
continue;
|
|
20053
|
+
const { k, v } = item;
|
|
20054
|
+
if (typeof k !== "string" || typeof v !== "string" || !KINDS.includes(k) || seen.has(k))
|
|
20055
|
+
continue;
|
|
20056
|
+
if (v.length === 0 || v.length > STATED_PATTERN_MAX)
|
|
20057
|
+
continue;
|
|
20058
|
+
const text = k === "pattern" ? cleanPattern(v) : v;
|
|
20059
|
+
if (text === "")
|
|
20060
|
+
continue;
|
|
20061
|
+
if (k === "minLength" || k === "maxLength") {
|
|
20062
|
+
if (!LENGTH_RE.test(v))
|
|
20063
|
+
continue;
|
|
20064
|
+
} else if (k === "step") {
|
|
20065
|
+
if (!NUMBER_RE.test(v))
|
|
20066
|
+
continue;
|
|
20067
|
+
} else if (k === "min" || k === "max") {
|
|
20068
|
+
if (!NUMBER_RE.test(v) && !Object.values(DATE_RE).some((re) => re.test(v)))
|
|
20069
|
+
continue;
|
|
20070
|
+
}
|
|
20071
|
+
seen.add(k);
|
|
20072
|
+
out.push({ k, v: text });
|
|
20073
|
+
}
|
|
20074
|
+
return out;
|
|
20075
|
+
}
|
|
20076
|
+
function statedLimitsRecord(limits) {
|
|
20077
|
+
return limits.length > 0 ? { v: STATED_LIMITS_VERSION, limits } : null;
|
|
20078
|
+
}
|
|
20079
|
+
var STATED_LIMITS_VERSION, STATED_PATTERN_MAX, KINDS, MAX_LIMITS, TEXT_TYPES, NUMBER_TYPES, DATE_RE, NUMBER_RE, LENGTH_RE;
|
|
20080
|
+
var init_stated_limits = __esm({
|
|
20081
|
+
"../shared/dist/stated-limits.js"() {
|
|
20082
|
+
"use strict";
|
|
20083
|
+
STATED_LIMITS_VERSION = 1;
|
|
20084
|
+
STATED_PATTERN_MAX = 200;
|
|
20085
|
+
KINDS = ["min", "max", "minLength", "maxLength", "step", "pattern"];
|
|
20086
|
+
MAX_LIMITS = KINDS.length;
|
|
20087
|
+
TEXT_TYPES = /* @__PURE__ */ new Set(["text", "search", "email", "url", "tel", "password", "textarea", ""]);
|
|
20088
|
+
NUMBER_TYPES = /* @__PURE__ */ new Set(["number", "range"]);
|
|
20089
|
+
DATE_RE = {
|
|
20090
|
+
date: /^\d{4}-\d{2}-\d{2}$/,
|
|
20091
|
+
"datetime-local": /^\d{4}-\d{2}-\d{2}T\d{2}:\d{2}(?::\d{2})?$/,
|
|
20092
|
+
time: /^\d{2}:\d{2}(?::\d{2})?$/,
|
|
20093
|
+
month: /^\d{4}-\d{2}$/,
|
|
20094
|
+
week: /^\d{4}-W\d{2}$/
|
|
20095
|
+
};
|
|
20096
|
+
NUMBER_RE = /^-?\d{1,15}(?:\.\d{1,10})?$/;
|
|
20097
|
+
LENGTH_RE = /^\d{1,7}$/;
|
|
20098
|
+
}
|
|
20099
|
+
});
|
|
20100
|
+
|
|
18845
20101
|
// ../shared/dist/index.js
|
|
18846
20102
|
var dist_exports = {};
|
|
18847
20103
|
__export(dist_exports, {
|
|
@@ -18849,9 +20105,11 @@ __export(dist_exports, {
|
|
|
18849
20105
|
ACTION_BATCH_MAX_SIZE: () => ACTION_BATCH_MAX_SIZE,
|
|
18850
20106
|
ADDON_DEFINITIONS: () => ADDON_DEFINITIONS,
|
|
18851
20107
|
ADMIN_EMAIL_LIMITS: () => ADMIN_EMAIL_LIMITS,
|
|
20108
|
+
ADMIN_FORCE_CANCELLED_RUN_ERROR: () => ADMIN_FORCE_CANCELLED_RUN_ERROR,
|
|
18852
20109
|
ADMIN_VIEW_DEFAULT_RETURN_TO: () => ADMIN_VIEW_DEFAULT_RETURN_TO,
|
|
18853
20110
|
AGENT_AI_PROVIDER_PREFIXES: () => AGENT_AI_PROVIDER_PREFIXES,
|
|
18854
20111
|
AI_CREDIT_MARGIN: () => AI_CREDIT_MARGIN,
|
|
20112
|
+
ALLOWED_PROVIDER_CHAIN_HOPS: () => ALLOWED_PROVIDER_CHAIN_HOPS,
|
|
18855
20113
|
API_AUTH_HEADERS_ENV_KEY: () => API_AUTH_HEADERS_ENV_KEY,
|
|
18856
20114
|
API_MODEL_CATALOG: () => API_MODEL_CATALOG,
|
|
18857
20115
|
API_MODEL_FIELDS: () => API_MODEL_FIELDS,
|
|
@@ -18872,6 +20130,7 @@ __export(dist_exports, {
|
|
|
18872
20130
|
AUDIO_CHUNK_DURATION_MS: () => AUDIO_CHUNK_DURATION_MS,
|
|
18873
20131
|
AUTH_PATH_SIGNATURE: () => AUTH_PATH_SIGNATURE,
|
|
18874
20132
|
AUTH_PROVIDER_HOST_SIGNATURES: () => AUTH_PROVIDER_HOST_SIGNATURES,
|
|
20133
|
+
AUTH_SESSION_GENERATION_RE: () => AUTH_SESSION_GENERATION_RE,
|
|
18875
20134
|
AUTH_STATE_EXPIRY_SKEW_MS: () => AUTH_STATE_EXPIRY_SKEW_MS,
|
|
18876
20135
|
AUTH_TAG_COOKIE_STATE: () => AUTH_TAG_COOKIE_STATE,
|
|
18877
20136
|
AUTH_TAG_LOGIN_BEARER: () => AUTH_TAG_LOGIN_BEARER,
|
|
@@ -18890,12 +20149,14 @@ __export(dist_exports, {
|
|
|
18890
20149
|
BUG_TEST_BROKEN_REASON: () => BUG_TEST_BROKEN_REASON,
|
|
18891
20150
|
BUG_TEST_BROKEN_TAG: () => BUG_TEST_BROKEN_TAG,
|
|
18892
20151
|
CACHE_PRICE_PER_M: () => CACHE_PRICE_PER_M,
|
|
20152
|
+
CANCELLED_REASON_PREFIXES: () => CANCELLED_REASON_PREFIXES,
|
|
18893
20153
|
CANVAS_UNSUPPORTED_BODY: () => CANVAS_UNSUPPORTED_BODY,
|
|
18894
20154
|
CANVAS_UNSUPPORTED_CODE: () => CANVAS_UNSUPPORTED_CODE,
|
|
18895
20155
|
CANVAS_UNSUPPORTED_NOT_CHARGED: () => CANVAS_UNSUPPORTED_NOT_CHARGED,
|
|
18896
20156
|
CANVAS_UNSUPPORTED_OVERRIDE_HINT: () => CANVAS_UNSUPPORTED_OVERRIDE_HINT,
|
|
18897
20157
|
CANVAS_UNSUPPORTED_TITLE: () => CANVAS_UNSUPPORTED_TITLE,
|
|
18898
20158
|
CAPTURE_JSON_STRUCTURE_LIMITS: () => CAPTURE_JSON_STRUCTURE_LIMITS,
|
|
20159
|
+
CHAIN_CANCELLED_RUN_ERROR: () => CHAIN_CANCELLED_RUN_ERROR,
|
|
18899
20160
|
CHAT_FAILOVER_KEY: () => CHAT_FAILOVER_KEY,
|
|
18900
20161
|
CLAUDE_CLI_DEFAULT_EFFORT: () => CLAUDE_CLI_DEFAULT_EFFORT,
|
|
18901
20162
|
CLAUDE_CLI_DEFAULT_MODEL: () => CLAUDE_CLI_DEFAULT_MODEL,
|
|
@@ -18932,6 +20193,7 @@ __export(dist_exports, {
|
|
|
18932
20193
|
DEFAULT_GEMINI_SCREEN_MODEL: () => DEFAULT_GEMINI_SCREEN_MODEL,
|
|
18933
20194
|
DEFAULT_JSON_STRUCTURE_LIMITS: () => DEFAULT_JSON_STRUCTURE_LIMITS,
|
|
18934
20195
|
DEFAULT_PLATFORM_DB_POOL_SIZE: () => DEFAULT_PLATFORM_DB_POOL_SIZE,
|
|
20196
|
+
DEFAULT_PROVIDER_ACCOUNT_ID: () => DEFAULT_PROVIDER_ACCOUNT_ID,
|
|
18935
20197
|
DEFAULT_SHARED_DB_POOL_SIZE: () => DEFAULT_SHARED_DB_POOL_SIZE,
|
|
18936
20198
|
DEFAULT_STT_MODEL: () => DEFAULT_STT_MODEL,
|
|
18937
20199
|
DEFAULT_STT_MODEL_BY_PROVIDER: () => DEFAULT_STT_MODEL_BY_PROVIDER,
|
|
@@ -18948,6 +20210,7 @@ __export(dist_exports, {
|
|
|
18948
20210
|
ENVIRONMENTAL_ERROR_STORED_PREFIXES: () => ENVIRONMENTAL_ERROR_STORED_PREFIXES,
|
|
18949
20211
|
EVIDENCE_NOTE_PREFIXES: () => EVIDENCE_NOTE_PREFIXES,
|
|
18950
20212
|
EXPECT_CALL_RE: () => EXPECT_CALL_RE,
|
|
20213
|
+
FEATURE_ROW_RUN_WINDOW: () => FEATURE_ROW_RUN_WINDOW,
|
|
18951
20214
|
FLOW_PHASE_LABELS: () => FLOW_PHASE_LABELS,
|
|
18952
20215
|
FLOW_PHASE_TYPES: () => FLOW_PHASE_TYPES,
|
|
18953
20216
|
FLOW_SERVER_TYPES: () => FLOW_SERVER_TYPES,
|
|
@@ -19004,6 +20267,8 @@ __export(dist_exports, {
|
|
|
19004
20267
|
LIVE_FRAME_STALE_MS: () => LIVE_FRAME_STALE_MS,
|
|
19005
20268
|
LIVE_FRAME_UNFOCUSED_INTERVAL_MS: () => LIVE_FRAME_UNFOCUSED_INTERVAL_MS,
|
|
19006
20269
|
LIVE_THUMBNAIL_MAX_BASE64_BYTES: () => LIVE_THUMBNAIL_MAX_BASE64_BYTES,
|
|
20270
|
+
MAX_BALANCED_SCAN: () => MAX_BALANCED_SCAN,
|
|
20271
|
+
MAX_CALLS: () => MAX_CALLS,
|
|
19007
20272
|
MAX_CREDIT_TOPUP_CENTS: () => MAX_CREDIT_TOPUP_CENTS,
|
|
19008
20273
|
MAX_DEEPLINK_LENGTH: () => MAX_DEEPLINK_LENGTH,
|
|
19009
20274
|
MAX_EMAIL_ADDRESS_LENGTH: () => MAX_EMAIL_ADDRESS_LENGTH,
|
|
@@ -19013,6 +20278,8 @@ __export(dist_exports, {
|
|
|
19013
20278
|
MAX_ORG_NAME_LENGTH: () => MAX_ORG_NAME_LENGTH,
|
|
19014
20279
|
MAX_PASSWORD_BYTES: () => MAX_PASSWORD_BYTES,
|
|
19015
20280
|
MAX_PLAYWRIGHT_CODE_LENGTH: () => MAX_PLAYWRIGHT_CODE_LENGTH,
|
|
20281
|
+
MAX_PROVIDER_ACCOUNTS: () => MAX_PROVIDER_ACCOUNTS,
|
|
20282
|
+
MAX_PROVIDER_ACCOUNT_MODELS: () => MAX_PROVIDER_ACCOUNT_MODELS,
|
|
19016
20283
|
MAX_SURVEY_ENTRY_URLS: () => MAX_SURVEY_ENTRY_URLS,
|
|
19017
20284
|
MAX_SURVEY_ENTRY_URL_LENGTH: () => MAX_SURVEY_ENTRY_URL_LENGTH,
|
|
19018
20285
|
MCP_ARMY_LIMITS: () => MCP_ARMY_LIMITS,
|
|
@@ -19048,6 +20315,7 @@ __export(dist_exports, {
|
|
|
19048
20315
|
MUSE_ENGINE_DEFAULT_MODEL: () => MUSE_ENGINE_DEFAULT_MODEL,
|
|
19049
20316
|
MUSE_ENGINE_MODELS: () => MUSE_ENGINE_MODELS,
|
|
19050
20317
|
NOREPLY_EMAIL: () => NOREPLY_EMAIL,
|
|
20318
|
+
OPENCODE_GO_DEEPSEEK_GLM_MODELS: () => OPENCODE_GO_DEEPSEEK_GLM_MODELS,
|
|
19051
20319
|
OPENCODE_GO_MODEL_PATTERN: () => OPENCODE_GO_MODEL_PATTERN,
|
|
19052
20320
|
OPENCODE_GO_PREFIX: () => OPENCODE_GO_PREFIX,
|
|
19053
20321
|
OPENROUTER_MAX_CALL_COST_USD: () => OPENROUTER_MAX_CALL_COST_USD,
|
|
@@ -19067,10 +20335,12 @@ __export(dist_exports, {
|
|
|
19067
20335
|
PLAN_FEATURES: () => PLAN_FEATURES,
|
|
19068
20336
|
PLAN_FEATURE_LOCKED_CODE: () => PLAN_FEATURE_LOCKED_CODE,
|
|
19069
20337
|
PLAN_ORDER: () => PLAN_ORDER,
|
|
20338
|
+
PLATFORM_ADMIN_CANCELLED_RUN_ERROR: () => PLATFORM_ADMIN_CANCELLED_RUN_ERROR,
|
|
19070
20339
|
PLATFORM_ADMIN_EMAIL: () => PLATFORM_ADMIN_EMAIL,
|
|
19071
20340
|
PLATFORM_DEFAULT_PROVIDER: () => PLATFORM_DEFAULT_PROVIDER,
|
|
19072
20341
|
PLATFORM_PLAN_BILLING: () => PLATFORM_PLAN_BILLING,
|
|
19073
20342
|
PLATFORM_SERVER_APPLICATION_NAME: () => PLATFORM_SERVER_APPLICATION_NAME,
|
|
20343
|
+
PLATFORM_SUPPORT_CANCELLED_RUN_ERROR: () => PLATFORM_SUPPORT_CANCELLED_RUN_ERROR,
|
|
19074
20344
|
POST_SPECIAL_TRIAL_DAYS: () => POST_SPECIAL_TRIAL_DAYS,
|
|
19075
20345
|
PRESET_DIRECT_FALLBACK: () => PRESET_DIRECT_FALLBACK,
|
|
19076
20346
|
PRODUCTION_READ_ONLY_GUARD_TEMPLATE: () => PRODUCTION_READ_ONLY_GUARD_TEMPLATE,
|
|
@@ -19082,17 +20352,25 @@ __export(dist_exports, {
|
|
|
19082
20352
|
PROJECT_OVERRIDE_FIELDS: () => PROJECT_OVERRIDE_FIELDS,
|
|
19083
20353
|
PROMOTION_GATE_MAX_CODE_CHARS: () => PROMOTION_GATE_MAX_CODE_CHARS,
|
|
19084
20354
|
PROMOTION_QUALITY_CHECKS: () => PROMOTION_QUALITY_CHECKS,
|
|
20355
|
+
PROVIDER_ACCOUNT_ID_PATTERN: () => PROVIDER_ACCOUNT_ID_PATTERN,
|
|
20356
|
+
PROVIDER_ACCOUNT_MODEL_PATTERN: () => PROVIDER_ACCOUNT_MODEL_PATTERN,
|
|
19085
20357
|
PROVIDER_CHAINS: () => PROVIDER_CHAINS,
|
|
20358
|
+
PROVIDER_CHAINS_SETTING_MAX_CHARS: () => PROVIDER_CHAINS_SETTING_MAX_CHARS,
|
|
20359
|
+
PROVIDER_CHAIN_LEADS: () => PROVIDER_CHAIN_LEADS,
|
|
20360
|
+
PROVIDER_CHAIN_MAX_ENTRIES: () => PROVIDER_CHAIN_MAX_ENTRIES,
|
|
20361
|
+
PROVIDER_HOST_NOT_OFFERED: () => PROVIDER_HOST_NOT_OFFERED,
|
|
19086
20362
|
PUBLIC_PLANS: () => PUBLIC_PLANS,
|
|
19087
20363
|
QUARANTINE_CLOSE_TAGS: () => QUARANTINE_CLOSE_TAGS,
|
|
19088
20364
|
READINESS_POLL_MAX_CODE_CHARS: () => READINESS_POLL_MAX_CODE_CHARS,
|
|
19089
20365
|
READ_ONLY_TAGS: () => READ_ONLY_TAGS,
|
|
19090
20366
|
RECHECKED_NOTES: () => RECHECKED_NOTES,
|
|
19091
20367
|
RECORDING_VALUE_PLACEHOLDERS: () => RECORDING_VALUE_PLACEHOLDERS,
|
|
20368
|
+
RENDER_IDENTITY_MAX_LENGTH: () => RENDER_IDENTITY_MAX_LENGTH,
|
|
19092
20369
|
REPOSITORY_CLIS: () => REPOSITORY_CLIS,
|
|
19093
20370
|
RESERVED_PROJECT_KEYS: () => RESERVED_PROJECT_KEYS,
|
|
19094
20371
|
RESULT_STRUCTURE_TOO_COSTLY_CODE: () => RESULT_STRUCTURE_TOO_COSTLY_CODE,
|
|
19095
20372
|
RETRIED_ATTEMPTS_KEY: () => RETRIED_ATTEMPTS_KEY,
|
|
20373
|
+
RETRY_HELD_RUN_ERROR: () => RETRY_HELD_RUN_ERROR,
|
|
19096
20374
|
RUNNER_INTERRUPT_ERROR_PREFIX: () => RUNNER_INTERRUPT_ERROR_PREFIX,
|
|
19097
20375
|
RUN_INTERRUPTED_ERROR_PREFIXES: () => RUN_INTERRUPTED_ERROR_PREFIXES,
|
|
19098
20376
|
RUN_INTERRUPTION_NOT_RETRIED_MARKERS: () => RUN_INTERRUPTION_NOT_RETRIED_MARKERS,
|
|
@@ -19105,6 +20383,8 @@ __export(dist_exports, {
|
|
|
19105
20383
|
SHARED_SERVER_APPLICATION_NAME: () => SHARED_SERVER_APPLICATION_NAME,
|
|
19106
20384
|
SPEND_SOURCE_KEY: () => SPEND_SOURCE_KEY,
|
|
19107
20385
|
SPEND_SOURCE_SERVER: () => SPEND_SOURCE_SERVER,
|
|
20386
|
+
STATED_LIMITS_VERSION: () => STATED_LIMITS_VERSION,
|
|
20387
|
+
STATED_PATTERN_MAX: () => STATED_PATTERN_MAX,
|
|
19108
20388
|
STATIC_STANDARD_MIN_INTERACTIVE: () => STATIC_STANDARD_MIN_INTERACTIVE,
|
|
19109
20389
|
STT_MODEL_OPTIONS: () => STT_MODEL_OPTIONS,
|
|
19110
20390
|
SUCCESS_NOTICE_TEXT: () => SUCCESS_NOTICE_TEXT,
|
|
@@ -19121,6 +20401,7 @@ __export(dist_exports, {
|
|
|
19121
20401
|
SURVEY_NOTES_RENDER_MAX_CHARS: () => SURVEY_NOTES_RENDER_MAX_CHARS,
|
|
19122
20402
|
SURVEY_NOTE_KEYS: () => SURVEY_NOTE_KEYS,
|
|
19123
20403
|
SUSPICIOUS_PASS_TAG: () => SUSPICIOUS_PASS_TAG,
|
|
20404
|
+
TEST_DID_NOT_RUN_ERROR: () => TEST_DID_NOT_RUN_ERROR,
|
|
19124
20405
|
TEST_QUALITY_CONVENTIONS: () => TEST_QUALITY_CONVENTIONS,
|
|
19125
20406
|
TEST_USER_ENV_KEYS: () => TEST_USER_ENV_KEYS,
|
|
19126
20407
|
TEST_VARIABLE_SECRET_KEY_PATTERN: () => TEST_VARIABLE_SECRET_KEY_PATTERN,
|
|
@@ -19133,23 +20414,33 @@ __export(dist_exports, {
|
|
|
19133
20414
|
UI_VARIANTS: () => UI_VARIANTS,
|
|
19134
20415
|
UNEXPECTED_PASS_ERROR_PREFIX: () => UNEXPECTED_PASS_ERROR_PREFIX,
|
|
19135
20416
|
USER_CANCELLED_RUN_ERROR: () => USER_CANCELLED_RUN_ERROR,
|
|
20417
|
+
VISUAL_MASK_SELECTORS_MAX: () => VISUAL_MASK_SELECTORS_MAX,
|
|
20418
|
+
VISUAL_MASK_SELECTOR_FORBIDDEN: () => VISUAL_MASK_SELECTOR_FORBIDDEN,
|
|
20419
|
+
VISUAL_MASK_SELECTOR_MAX_LENGTH: () => VISUAL_MASK_SELECTOR_MAX_LENGTH,
|
|
20420
|
+
VISUAL_V1_IDENTITY: () => VISUAL_V1_IDENTITY,
|
|
19136
20421
|
VOICE_PREVIEW_SAMPLE: () => VOICE_PREVIEW_SAMPLE,
|
|
19137
20422
|
WEB_PROXY_CHAIN_HEADER: () => WEB_PROXY_CHAIN_HEADER,
|
|
19138
20423
|
WITHHELD_CREDENTIAL_TEXT: () => WITHHELD_CREDENTIAL_TEXT,
|
|
20424
|
+
ZAI_MODEL_PATTERN: () => ZAI_MODEL_PATTERN,
|
|
20425
|
+
ZAI_PREFIX: () => ZAI_PREFIX,
|
|
20426
|
+
accountServesModel: () => accountServesModel,
|
|
19139
20427
|
accumulateStatements: () => accumulateStatements,
|
|
19140
20428
|
adoptBrokerFailover: () => adoptBrokerFailover,
|
|
19141
20429
|
aiCostToCreditCents: () => aiCostToCreditCents,
|
|
20430
|
+
allowedProviderChainHosts: () => allowedProviderChainHosts,
|
|
19142
20431
|
alternatesAtTop: () => alternatesAtTop,
|
|
19143
20432
|
analyzeDerivedValues: () => analyzeDerivedValues,
|
|
19144
20433
|
analyzeMobileSelectorQuality: () => analyzeMobileSelectorQuality,
|
|
19145
20434
|
analyzePersistenceReread: () => analyzePersistenceReread,
|
|
19146
20435
|
analyzeReadinessPolling: () => analyzeReadinessPolling,
|
|
20436
|
+
analyzeRegexMatchers: () => analyzeRegexMatchers,
|
|
19147
20437
|
analyzeSharedAccountUse: () => analyzeSharedAccountUse,
|
|
19148
20438
|
analyzeStaleAfterSubmit: () => analyzeStaleAfterSubmit,
|
|
19149
20439
|
analyzeTestData: () => analyzeTestData,
|
|
19150
20440
|
analyzeUnreachedUrlWaits: () => analyzeUnreachedUrlWaits,
|
|
19151
20441
|
analyzeUrlWaits: () => analyzeUrlWaits,
|
|
19152
20442
|
analyzeWriteResponse: () => analyzeWriteResponse,
|
|
20443
|
+
androidResourceOwners: () => androidResourceOwners,
|
|
19153
20444
|
apiModelCardsFromConfig: () => apiModelCardsFromConfig,
|
|
19154
20445
|
apiModelsForProvider: () => apiModelsForProvider,
|
|
19155
20446
|
apiProviderModelLabel: () => apiProviderModelLabel,
|
|
@@ -19160,6 +20451,7 @@ __export(dist_exports, {
|
|
|
19160
20451
|
applyLaunchSpecialPlan: () => applyLaunchSpecialPlan,
|
|
19161
20452
|
asPlainExpect: () => asPlainExpect,
|
|
19162
20453
|
assertionOnlyCode: () => assertionOnlyCode,
|
|
20454
|
+
assertionStrengthHoistedReadsOn: () => assertionStrengthHoistedReadsOn,
|
|
19163
20455
|
assertionsReadVisibleContent: () => assertionsReadVisibleContent,
|
|
19164
20456
|
assessCodeOutcomeCoverage: () => assessCodeOutcomeCoverage,
|
|
19165
20457
|
assessTitleCoverage: () => assessTitleCoverage,
|
|
@@ -19243,6 +20535,7 @@ __export(dist_exports, {
|
|
|
19243
20535
|
deepgramSpeak: () => deepgramSpeak,
|
|
19244
20536
|
deepgramTranscribe: () => deepgramTranscribe,
|
|
19245
20537
|
defaultConfirmModel: () => defaultConfirmModel,
|
|
20538
|
+
defaultProviderChainEntries: () => defaultProviderChainEntries,
|
|
19246
20539
|
defaultScreenModel: () => defaultScreenModel,
|
|
19247
20540
|
defaultVoiceForTtsModel: () => defaultVoiceForTtsModel,
|
|
19248
20541
|
deriveAuthPlan: () => deriveAuthPlan,
|
|
@@ -19285,6 +20578,7 @@ __export(dist_exports, {
|
|
|
19285
20578
|
extractOutcomeSearchTerms: () => extractOutcomeSearchTerms,
|
|
19286
20579
|
featureForAiCallType: () => featureForAiCallType,
|
|
19287
20580
|
featureForMcpTool: () => featureForMcpTool,
|
|
20581
|
+
featureRowRunStatus: () => featureRowRunStatus,
|
|
19288
20582
|
findBracedClause: () => findBracedClause,
|
|
19289
20583
|
findCounterIdPins: () => findCounterIdPins,
|
|
19290
20584
|
findCredentialTextLeftover: () => findCredentialTextLeftover,
|
|
@@ -19307,6 +20601,7 @@ __export(dist_exports, {
|
|
|
19307
20601
|
formatPersistenceRereadAdvisory: () => formatPersistenceRereadAdvisory,
|
|
19308
20602
|
formatPoolBudgetLine: () => formatPoolBudgetLine,
|
|
19309
20603
|
formatReadinessPollAdvisory: () => formatReadinessPollAdvisory,
|
|
20604
|
+
formatRegexMatcherNote: () => formatRegexMatcherNote,
|
|
19310
20605
|
formatStaleAfterSubmitAdvisory: () => formatStaleAfterSubmitAdvisory,
|
|
19311
20606
|
formatSuspiciousExpectation: () => formatSuspiciousExpectation,
|
|
19312
20607
|
formatUrlWaitAdvisory: () => formatUrlWaitAdvisory,
|
|
@@ -19332,6 +20627,7 @@ __export(dist_exports, {
|
|
|
19332
20627
|
getPlanChangeType: () => getPlanChangeType,
|
|
19333
20628
|
getPlanDefinition: () => getPlanDefinition,
|
|
19334
20629
|
getPlanLabel: () => getPlanLabel,
|
|
20630
|
+
getProviderChainOverrides: () => getProviderChainOverrides,
|
|
19335
20631
|
getTrialDaysRemaining: () => getTrialDaysRemaining,
|
|
19336
20632
|
googleBrokerNavigationUrl: () => googleBrokerNavigationUrl,
|
|
19337
20633
|
googleBrokerUnavailableLine: () => googleBrokerUnavailableLine,
|
|
@@ -19348,9 +20644,11 @@ __export(dist_exports, {
|
|
|
19348
20644
|
hasOpenCodeGoPrefix: () => hasOpenCodeGoPrefix,
|
|
19349
20645
|
hasOpenRouterPrefix: () => hasOpenRouterPrefix,
|
|
19350
20646
|
hasPlatformLocator: () => hasPlatformLocator,
|
|
20647
|
+
hasProviderChainLeadsByDefault: () => hasProviderChainLeadsByDefault,
|
|
19351
20648
|
hasReadOnlyMarker: () => hasReadOnlyMarker,
|
|
19352
20649
|
hasSurveyNotes: () => hasSurveyNotes,
|
|
19353
20650
|
hasTestFailContract: () => hasTestFailContract,
|
|
20651
|
+
hasZaiPrefix: () => hasZaiPrefix,
|
|
19354
20652
|
healMobileTestSteps: () => healMobileTestSteps,
|
|
19355
20653
|
healthRunWhere: () => healthRunWhere,
|
|
19356
20654
|
heldCodeRecord: () => heldCodeRecord,
|
|
@@ -19372,8 +20670,11 @@ __export(dist_exports, {
|
|
|
19372
20670
|
isAuthTokenName: () => isAuthTokenName,
|
|
19373
20671
|
isBlockedEnvVar: () => isBlockedEnvVar,
|
|
19374
20672
|
isBrowserCliEngine: () => isBrowserCliEngine,
|
|
20673
|
+
isBrowserVersion: () => isBrowserVersion,
|
|
19375
20674
|
isBugTestBrokenError: () => isBugTestBrokenError,
|
|
20675
|
+
isCancelledReason: () => isCancelledReason,
|
|
19376
20676
|
isCanvasUnsupportedMessage: () => isCanvasUnsupportedMessage,
|
|
20677
|
+
isChainablePrimary: () => isChainablePrimary,
|
|
19377
20678
|
isCliEngineModelId: () => isCliEngineModelId,
|
|
19378
20679
|
isCliEngineSpec: () => isCliEngineSpec,
|
|
19379
20680
|
isCodeFactNote: () => isCodeFactNote,
|
|
@@ -19397,6 +20698,7 @@ __export(dist_exports, {
|
|
|
19397
20698
|
isIdentityMobileXpath: () => isIdentityMobileXpath,
|
|
19398
20699
|
isIdentityProviderSurface: () => isIdentityProviderSurface,
|
|
19399
20700
|
isIncidentalAssetPath: () => isIncidentalAssetPath,
|
|
20701
|
+
isIntentionalNegativeAuthTest: () => isIntentionalNegativeAuthTest,
|
|
19400
20702
|
isIpOrLocalhost: () => isIpOrLocalhost,
|
|
19401
20703
|
isKnownPageType: () => isKnownPageType,
|
|
19402
20704
|
isLaunchSpecialActive: () => isLaunchSpecialActive,
|
|
@@ -19424,6 +20726,7 @@ __export(dist_exports, {
|
|
|
19424
20726
|
isRunInterruptedError: () => isRunInterruptedError,
|
|
19425
20727
|
isRunnableStep: () => isRunnableStep,
|
|
19426
20728
|
isSafeDeepLink: () => isSafeDeepLink,
|
|
20729
|
+
isSafeMaskSelector: () => isSafeMaskSelector,
|
|
19427
20730
|
isSameRegistrableDomainHost: () => isSameRegistrableDomainHost,
|
|
19428
20731
|
isSelectableApiModel: () => isSelectableApiModel,
|
|
19429
20732
|
isSelfServePlan: () => isSelfServePlan,
|
|
@@ -19454,6 +20757,7 @@ __export(dist_exports, {
|
|
|
19454
20757
|
isValidSttProvider: () => isValidSttProvider,
|
|
19455
20758
|
isValidTtsModel: () => isValidTtsModel,
|
|
19456
20759
|
isValidTtsVoiceId: () => isValidTtsVoiceId,
|
|
20760
|
+
isZaiModelId: () => isZaiModelId,
|
|
19457
20761
|
jsEscapedForms: () => jsEscapedForms,
|
|
19458
20762
|
jsonStructureRefusalOf: () => jsonStructureRefusalOf,
|
|
19459
20763
|
jsonStructureVerify: () => jsonStructureVerify,
|
|
@@ -19486,6 +20790,7 @@ __export(dist_exports, {
|
|
|
19486
20790
|
normalizeFinishReason: () => normalizeFinishReason,
|
|
19487
20791
|
normalizeNoteText: () => normalizeNoteText,
|
|
19488
20792
|
normalizePageType: () => normalizePageType,
|
|
20793
|
+
normalizeProviderAccountModels: () => normalizeProviderAccountModels,
|
|
19489
20794
|
normalizeTtsModel: () => normalizeTtsModel,
|
|
19490
20795
|
normalizeTtsVoiceId: () => normalizeTtsVoiceId,
|
|
19491
20796
|
noteChatFailover: () => noteChatFailover,
|
|
@@ -19502,6 +20807,7 @@ __export(dist_exports, {
|
|
|
19502
20807
|
parseApiCardModelUpdate: () => parseApiCardModelUpdate,
|
|
19503
20808
|
parseAppTimezone: () => parseAppTimezone,
|
|
19504
20809
|
parseAuditGroupScopeId: () => parseAuditGroupScopeId,
|
|
20810
|
+
parseAuthSessionStamp: () => parseAuthSessionStamp,
|
|
19505
20811
|
parseBoolFlag: () => parseBoolFlag,
|
|
19506
20812
|
parseDbPoolSize: () => parseDbPoolSize,
|
|
19507
20813
|
parseDeepgramListenResponse: () => parseDeepgramListenResponse,
|
|
@@ -19509,13 +20815,18 @@ __export(dist_exports, {
|
|
|
19509
20815
|
parseGeminiModelsPayload: () => parseGeminiModelsPayload,
|
|
19510
20816
|
parseGoogleAuthorizeUrl: () => parseGoogleAuthorizeUrl,
|
|
19511
20817
|
parseGoogleGisRequestUrl: () => parseGoogleGisRequestUrl,
|
|
20818
|
+
parseLastActionProofMode: () => parseLastActionProofMode,
|
|
19512
20819
|
parseLimitParam: () => parseLimitParam,
|
|
19513
20820
|
parseOffsetParam: () => parseOffsetParam,
|
|
19514
20821
|
parsePageParam: () => parsePageParam,
|
|
19515
20822
|
parsePgIntSetting: () => parsePgIntSetting,
|
|
19516
20823
|
parseProjectEngineOverrides: () => parseProjectEngineOverrides,
|
|
19517
20824
|
parsePromotionGateMode: () => parsePromotionGateMode,
|
|
20825
|
+
parseProviderAccountLists: () => parseProviderAccountLists,
|
|
20826
|
+
parseProviderChainOverrides: () => parseProviderChainOverrides,
|
|
19518
20827
|
parseRecheckClearMode: () => parseRecheckClearMode,
|
|
20828
|
+
parseRenderIdentity: () => parseRenderIdentity,
|
|
20829
|
+
parseStoredProviderChainOverrides: () => parseStoredProviderChainOverrides,
|
|
19519
20830
|
parseSurveyEntryUrls: () => parseSurveyEntryUrls,
|
|
19520
20831
|
parseSuspiciousPassMode: () => parseSuspiciousPassMode,
|
|
19521
20832
|
parseTaskModelUpdate: () => parseTaskModelUpdate,
|
|
@@ -19545,13 +20856,20 @@ __export(dist_exports, {
|
|
|
19545
20856
|
profileKeyRoute: () => profileKeyRoute,
|
|
19546
20857
|
projectAppHosts: () => projectAppHosts,
|
|
19547
20858
|
providerChainFor: () => providerChainFor,
|
|
20859
|
+
providerChainHostOf: () => providerChainHostOf,
|
|
20860
|
+
providerChainHostProvider: () => providerChainHostProvider,
|
|
20861
|
+
providerChainLeadsFor: () => providerChainLeadsFor,
|
|
20862
|
+
providerChainModelKey: () => providerChainModelKey,
|
|
19548
20863
|
providerChainPrimariesForHop: () => providerChainPrimariesForHop,
|
|
20864
|
+
providerHostPlan: () => providerHostPlan,
|
|
20865
|
+
readBalanced: () => readBalanced,
|
|
19549
20866
|
readChatFailover: () => readChatFailover,
|
|
19550
20867
|
readChatReasoningTokens: () => readChatReasoningTokens,
|
|
19551
20868
|
readChatUsage: () => readChatUsage,
|
|
19552
20869
|
readEnabledEngines: () => readEnabledEngines,
|
|
19553
20870
|
readProjectEngineOverrides: () => readProjectEngineOverrides,
|
|
19554
20871
|
readRetriedAttempts: () => readRetriedAttempts,
|
|
20872
|
+
readStatedLimits: () => readStatedLimits,
|
|
19555
20873
|
readStaticPageCanvasSignals: () => readStaticPageCanvasSignals,
|
|
19556
20874
|
readSurveyEntryUrls: () => readSurveyEntryUrls,
|
|
19557
20875
|
readSurveyNotes: () => readSurveyNotes,
|
|
@@ -19564,6 +20882,7 @@ __export(dist_exports, {
|
|
|
19564
20882
|
regexLiteralEnd: () => regexLiteralEnd2,
|
|
19565
20883
|
regexOpensAfter: () => regexOpensAfter,
|
|
19566
20884
|
registrableDomain: () => registrableDomain,
|
|
20885
|
+
renderIdentity: () => renderIdentity,
|
|
19567
20886
|
renderSurveyNotesForPrompt: () => renderSurveyNotesForPrompt,
|
|
19568
20887
|
repairPageType: () => repairPageType,
|
|
19569
20888
|
replaceBlockComments: () => replaceBlockComments,
|
|
@@ -19611,11 +20930,14 @@ __export(dist_exports, {
|
|
|
19611
20930
|
selectorCandidatesForStep: () => selectorCandidatesForStep,
|
|
19612
20931
|
selectorForValue: () => selectorForValue,
|
|
19613
20932
|
setAgenticCliRuntime: () => setAgenticCliRuntime,
|
|
20933
|
+
setProviderChainOverrides: () => setProviderChainOverrides,
|
|
19614
20934
|
signupPlanFor: () => signupPlanFor,
|
|
19615
20935
|
simplifyMobileWanderingInPlace: () => simplifyMobileWanderingInPlace,
|
|
19616
20936
|
siteOf: () => siteOf,
|
|
19617
20937
|
specItemsFromTestPlans: () => specItemsFromTestPlans,
|
|
19618
20938
|
splitAnswerLines: () => splitAnswerLines,
|
|
20939
|
+
statedLimitsFromAttrs: () => statedLimitsFromAttrs,
|
|
20940
|
+
statedLimitsRecord: () => statedLimitsRecord,
|
|
19619
20941
|
stepAppliesToPlatform: () => stepAppliesToPlatform,
|
|
19620
20942
|
stepConsoleLines: () => stepConsoleLines,
|
|
19621
20943
|
stepIsDivergentForPlatform: () => stepIsDivergentForPlatform,
|
|
@@ -19627,7 +20949,11 @@ __export(dist_exports, {
|
|
|
19627
20949
|
stripCodeFactPrefix: () => stripCodeFactPrefix,
|
|
19628
20950
|
stripCoverageComments: () => stripCoverageComments,
|
|
19629
20951
|
stripEnvironmentalPrefix: () => stripEnvironmentalPrefix,
|
|
20952
|
+
stripFractionZeros: () => stripFractionZeros,
|
|
20953
|
+
stripLineComment: () => stripLineComment,
|
|
19630
20954
|
stripLoginScaffolding: () => stripLoginScaffolding,
|
|
20955
|
+
stripTrailingChars: () => stripTrailingChars,
|
|
20956
|
+
stripTrailingSlashes: () => stripTrailingSlashes,
|
|
19631
20957
|
strongerNeedsReviewSource: () => strongerNeedsReviewSource,
|
|
19632
20958
|
sttModelsForProvider: () => sttModelsForProvider,
|
|
19633
20959
|
summarizeHealth: () => summarizeHealth,
|
|
@@ -19644,10 +20970,14 @@ __export(dist_exports, {
|
|
|
19644
20970
|
ttsFallbackModel: () => ttsFallbackModel,
|
|
19645
20971
|
ttsProviderForModel: () => ttsProviderForModel,
|
|
19646
20972
|
unexpectedPassError: () => unexpectedPassError,
|
|
20973
|
+
v1RenderIdentity: () => v1RenderIdentity,
|
|
19647
20974
|
validateAppMapDocument: () => validateAppMapDocument,
|
|
19648
20975
|
validateAssertionStrength: () => validateAssertionStrength,
|
|
19649
20976
|
validateEngineSpec: () => validateEngineSpec,
|
|
20977
|
+
validateProviderChainEntries: () => validateProviderChainEntries,
|
|
20978
|
+
validateProviderChainsSetting: () => validateProviderChainsSetting,
|
|
19650
20979
|
validateTaskModelId: () => validateTaskModelId,
|
|
20980
|
+
visualBaselinesEnabled: () => visualBaselinesEnabled,
|
|
19651
20981
|
visualProDailyBudget: () => visualProDailyBudget,
|
|
19652
20982
|
voiceMatchesTtsModel: () => voiceMatchesTtsModel,
|
|
19653
20983
|
voicesForTtsModel: () => voicesForTtsModel,
|
|
@@ -19655,7 +20985,8 @@ __export(dist_exports, {
|
|
|
19655
20985
|
withReviewSource: () => withReviewSource,
|
|
19656
20986
|
withoutNotChargedClaim: () => withoutNotChargedClaim,
|
|
19657
20987
|
withoutServerOnlySpendFields: () => withoutServerOnlySpendFields,
|
|
19658
|
-
withoutSurveyNotes: () => withoutSurveyNotes
|
|
20988
|
+
withoutSurveyNotes: () => withoutSurveyNotes,
|
|
20989
|
+
zaiWireModel: () => zaiWireModel
|
|
19659
20990
|
});
|
|
19660
20991
|
var init_dist = __esm({
|
|
19661
20992
|
"../shared/dist/index.js"() {
|
|
@@ -19697,6 +21028,7 @@ var init_dist = __esm({
|
|
|
19697
21028
|
init_admin_view_return_to();
|
|
19698
21029
|
init_auth_plan();
|
|
19699
21030
|
init_auth_block_detector();
|
|
21031
|
+
init_auth_session_stamp();
|
|
19700
21032
|
init_auth_state_freshness();
|
|
19701
21033
|
init_skip_coverage_detector();
|
|
19702
21034
|
init_feature_coverage();
|
|
@@ -19708,11 +21040,14 @@ var init_dist = __esm({
|
|
|
19708
21040
|
init_deepgram_voices();
|
|
19709
21041
|
init_feature_flags();
|
|
19710
21042
|
init_env_flags();
|
|
21043
|
+
init_visual_compare_limits();
|
|
21044
|
+
init_visual_baseline_identity();
|
|
19711
21045
|
init_analytics_url();
|
|
19712
21046
|
init_agentic_cli_runtime();
|
|
19713
21047
|
init_api_model_catalog();
|
|
19714
21048
|
init_engine_spec();
|
|
19715
21049
|
init_provider_chains();
|
|
21050
|
+
init_provider_accounts();
|
|
19716
21051
|
init_project_engine_overrides();
|
|
19717
21052
|
init_ai_profiles();
|
|
19718
21053
|
init_openrouter_usage();
|
|
@@ -19753,6 +21088,8 @@ var init_dist = __esm({
|
|
|
19753
21088
|
init_promotion_quality_gate();
|
|
19754
21089
|
init_regex_alternation();
|
|
19755
21090
|
init_page_evidence();
|
|
21091
|
+
init_trailing_slashes();
|
|
21092
|
+
init_stated_limits();
|
|
19756
21093
|
}
|
|
19757
21094
|
});
|
|
19758
21095
|
|
|
@@ -19840,7 +21177,7 @@ var require_runs_dir = __commonJS({
|
|
|
19840
21177
|
} catch (error2) {
|
|
19841
21178
|
if (error2.code === "EEXIST" && resolvesFrom(runsDir) === own)
|
|
19842
21179
|
return;
|
|
19843
|
-
throw new Error(`Could not link ${link} to the runner's node_modules (${error2.message}). Set RUNS_DIR to a writable folder outside node_modules that can resolve @playwright/test
|
|
21180
|
+
throw new Error(`Could not link ${link} to the runner's node_modules (${error2.message}). Set RUNS_DIR to a writable folder outside node_modules that can resolve @playwright/test.`, { cause: error2 });
|
|
19844
21181
|
}
|
|
19845
21182
|
}
|
|
19846
21183
|
}
|
|
@@ -21443,23 +22780,41 @@ var require_harness_init_script = __commonJS({
|
|
|
21443
22780
|
exports2.isHarnessInitConsoleLine = isHarnessInitConsoleLine;
|
|
21444
22781
|
exports2.stripHarnessConsoleEntries = stripHarnessConsoleEntries;
|
|
21445
22782
|
var node_crypto_1 = require("crypto");
|
|
22783
|
+
var shared_1 = (init_dist(), __toCommonJS(dist_exports));
|
|
21446
22784
|
exports2.HARNESS_INIT_SOURCE_URL = `vqa-harness-init-${(0, node_crypto_1.randomBytes)(4).toString("hex")}.js`;
|
|
22785
|
+
var TRANSITION_LOOP_LIMIT = 8;
|
|
22786
|
+
var TRANSITION_LOOP_WINDOW_MS = 1e3;
|
|
21447
22787
|
function withHarnessSourceUrl(content) {
|
|
21448
22788
|
return `${content}
|
|
21449
22789
|
//# sourceURL=${exports2.HARNESS_INIT_SOURCE_URL}`;
|
|
21450
22790
|
}
|
|
21451
22791
|
function buildAnimationDisablingSource(opts) {
|
|
22792
|
+
const finishTransitions = opts.finishTransitions ?? (0, shared_1.parseBoolFlag)(process.env.HARNESS_FINISH_CSS_TRANSITIONS);
|
|
22793
|
+
const css = finishTransitions ? "*, *::before, *::after { animation-duration: 0s !important; animation-delay: 0s !important; }" : "*, *::before, *::after { transition-duration: 0s !important; animation-duration: 0s !important; transition-delay: 0s !important; animation-delay: 0s !important; }";
|
|
21452
22794
|
return withHarnessSourceUrl(`(() => {
|
|
21453
22795
|
// Layer 1: CSS transitions and animations - instant, not removed
|
|
21454
22796
|
// (duration 0s preserves final state; 'none' would prevent keyframes from completing)
|
|
22797
|
+
// The rule goes in a constructed stylesheet on document.adoptedStyleSheets, which adds no node to the DOM:
|
|
22798
|
+
// a <style> element changed what the test sees (locator('style') and 'head > *' counts, document.styleSheets,
|
|
22799
|
+
// an iframe's empty head), so a spec stock Playwright passes failed. The <style> is the fallback for a
|
|
22800
|
+
// browser with no constructable stylesheets.
|
|
22801
|
+
let adopted = false;
|
|
21455
22802
|
try {
|
|
22803
|
+
if (typeof CSSStyleSheet === 'function' && typeof Document === 'function' && 'adoptedStyleSheets' in Document.prototype) {
|
|
22804
|
+
const sheet = new CSSStyleSheet();
|
|
22805
|
+
sheet.replaceSync(${JSON.stringify(css)});
|
|
22806
|
+
document.adoptedStyleSheets = [...document.adoptedStyleSheets, sheet];
|
|
22807
|
+
adopted = true;
|
|
22808
|
+
}
|
|
22809
|
+
} catch (e) {}
|
|
22810
|
+
if (!adopted) try {
|
|
21456
22811
|
let applied = false;
|
|
21457
22812
|
const apply = () => {
|
|
21458
22813
|
if (applied) return true;
|
|
21459
22814
|
const root = document.head || document.documentElement;
|
|
21460
22815
|
if (!root) return false;
|
|
21461
22816
|
const style = document.createElement('style');
|
|
21462
|
-
style.textContent =
|
|
22817
|
+
style.textContent = ${JSON.stringify(css)};
|
|
21463
22818
|
root.appendChild(style);
|
|
21464
22819
|
applied = true;
|
|
21465
22820
|
return true;
|
|
@@ -21481,6 +22836,48 @@ ${opts.hideWebdriver ? `
|
|
|
21481
22836
|
try {
|
|
21482
22837
|
Object.defineProperty(navigator, 'webdriver', { get: () => undefined });
|
|
21483
22838
|
} catch (e) {}
|
|
22839
|
+
` : ""}
|
|
22840
|
+
${finishTransitions ? `
|
|
22841
|
+
// Layer 1b: CSS transitions run, and are finished as they start, so transitionend fires.
|
|
22842
|
+
// - finish() is deferred to a task: called inside the transitionrun dispatch (or a microtask of it) after a
|
|
22843
|
+
// real input event, Chromium finishes the transition but never fires its transitionstart/transitionend.
|
|
22844
|
+
// - setTimeout and performance.now are the ones present when this script runs, before the page's own
|
|
22845
|
+
// scripts: fake timers the page installs later cannot hold a finish back.
|
|
22846
|
+
// - The subtree is walked only for a pseudo-element's transition (its animations hang off the element).
|
|
22847
|
+
// - An element whose same transition restarts more than ${TRANSITION_LOOP_LIMIT} times within ${TRANSITION_LOOP_WINDOW_MS} ms (an app that starts it again on
|
|
22848
|
+
// each transitionend: carousel, pulse) is left to run at its own pace from then on, instead of cycling at
|
|
22849
|
+
// frame rate for the rest of the run.
|
|
22850
|
+
try {
|
|
22851
|
+
const st = setTimeout;
|
|
22852
|
+
const perf = typeof performance === 'object' && performance && typeof performance.now === 'function' ? performance : null;
|
|
22853
|
+
const now = perf ? perf.now.bind(perf) : Date.now;
|
|
22854
|
+
const seen = new WeakMap();
|
|
22855
|
+
const looping = (el, key) => {
|
|
22856
|
+
let byKey = seen.get(el);
|
|
22857
|
+
if (!byKey) { byKey = new Map(); seen.set(el, byKey); }
|
|
22858
|
+
const t = now();
|
|
22859
|
+
let s = byKey.get(key);
|
|
22860
|
+
if (s && s.loop) return true;
|
|
22861
|
+
if (!s || t - s.since > ${TRANSITION_LOOP_WINDOW_MS}) { s = { n: 0, since: t, loop: false }; byKey.set(key, s); }
|
|
22862
|
+
s.n += 1;
|
|
22863
|
+
if (s.n > ${TRANSITION_LOOP_LIMIT}) s.loop = true;
|
|
22864
|
+
return s.loop;
|
|
22865
|
+
};
|
|
22866
|
+
window.addEventListener('transitionrun', (ev) => {
|
|
22867
|
+
try {
|
|
22868
|
+
const el = ev && ev.target;
|
|
22869
|
+
if (!el || typeof el.getAnimations !== 'function' || typeof CSSTransition !== 'function') return;
|
|
22870
|
+
const pseudo = ev.pseudoElement || '';
|
|
22871
|
+
if (looping(el, ev.propertyName + '|' + pseudo)) return;
|
|
22872
|
+
for (const a of (pseudo ? el.getAnimations({ subtree: true }) : el.getAnimations())) {
|
|
22873
|
+
if (a instanceof CSSTransition && a.transitionProperty === ev.propertyName && a.effect
|
|
22874
|
+
&& a.effect.target === el && (a.effect.pseudoElement || '') === pseudo) {
|
|
22875
|
+
st(() => { try { a.finish(); } catch (e) {} }, 0);
|
|
22876
|
+
}
|
|
22877
|
+
}
|
|
22878
|
+
} catch (e) {}
|
|
22879
|
+
}, true);
|
|
22880
|
+
} catch (e) {}
|
|
21484
22881
|
` : ""}
|
|
21485
22882
|
// Layer 2: Web Animations API - make instant instead of no-op
|
|
21486
22883
|
// (preserves animation completion callbacks that app code may depend on)
|
|
@@ -22372,6 +23769,7 @@ var require_browser_config = __commonJS({
|
|
|
22372
23769
|
exports2.fullChromiumCloudLaunch = fullChromiumCloudLaunch;
|
|
22373
23770
|
exports2.chooseCloudBrowser = chooseCloudBrowser;
|
|
22374
23771
|
exports2.appTimezoneConfigLine = appTimezoneConfigLine;
|
|
23772
|
+
exports2.discoveryTimezoneId = discoveryTimezoneId;
|
|
22375
23773
|
exports2.getMCPBrowserConfig = getMCPBrowserConfig;
|
|
22376
23774
|
exports2.getPlaywrightLaunchOptions = getPlaywrightLaunchOptions;
|
|
22377
23775
|
exports2.getLaunchOptions = getLaunchOptions;
|
|
@@ -22590,7 +23988,7 @@ var require_browser_config = __commonJS({
|
|
|
22590
23988
|
try {
|
|
22591
23989
|
(0, fs_1.cpSync)(localStateSrc, (0, path_1.join)(tempDir, "Local State"));
|
|
22592
23990
|
} catch (err) {
|
|
22593
|
-
throw new Error(`Cannot copy Chrome profile (${err instanceof Error ? err.message : String(err)}). Quit Google Chrome completely, or use getChromeUserDataDirSource() for seeding
|
|
23991
|
+
throw new Error(`Cannot copy Chrome profile (${err instanceof Error ? err.message : String(err)}). Quit Google Chrome completely, or use getChromeUserDataDirSource() for seeding.`, { cause: err });
|
|
22594
23992
|
}
|
|
22595
23993
|
}
|
|
22596
23994
|
for (const file of essentialFiles) {
|
|
@@ -22781,6 +24179,10 @@ var require_browser_config = __commonJS({
|
|
|
22781
24179
|
const zone = (0, shared_1.appTimezoneFromVariables)(vars);
|
|
22782
24180
|
return zone ? `timezoneId: ${JSON.stringify(zone)},` : "";
|
|
22783
24181
|
}
|
|
24182
|
+
function discoveryTimezoneId(ctx) {
|
|
24183
|
+
const vars = ctx.variableContext;
|
|
24184
|
+
return (0, shared_1.appTimezoneFromVariables)(vars?.publicTestVariables) ?? (0, shared_1.appTimezoneFromVariables)(vars?.allTestVariables);
|
|
24185
|
+
}
|
|
22784
24186
|
function getMCPBrowserConfig(opts) {
|
|
22785
24187
|
const userDataDir = opts?.forceIsolated ? null : getTestUserDataDir();
|
|
22786
24188
|
const timezoneOption = opts?.timezoneId ? { timezoneId: opts.timezoneId } : {};
|
|
@@ -22892,11 +24294,13 @@ var require_proc_table = __commonJS({
|
|
|
22892
24294
|
"../runner-core/dist/utils/proc-table.js"(exports2) {
|
|
22893
24295
|
"use strict";
|
|
22894
24296
|
Object.defineProperty(exports2, "__esModule", { value: true });
|
|
24297
|
+
exports2.PS_MAX_BUFFER = void 0;
|
|
22895
24298
|
exports2.isMissingBinaryError = isMissingBinaryError;
|
|
22896
24299
|
exports2.parseProcStat = parseProcStat;
|
|
22897
24300
|
exports2.readProcRssKb = readProcRssKb;
|
|
22898
24301
|
exports2.readProcTable = readProcTable;
|
|
22899
24302
|
var node_fs_1 = require("fs");
|
|
24303
|
+
exports2.PS_MAX_BUFFER = 32 * 1024 * 1024;
|
|
22900
24304
|
var CLOCK_TICKS_PER_SECOND = 100;
|
|
22901
24305
|
function isMissingBinaryError(error2) {
|
|
22902
24306
|
return error2?.code === "ENOENT";
|
|
@@ -22982,14 +24386,13 @@ var require_kill_tree = __commonJS({
|
|
|
22982
24386
|
var proc_table_js_1 = require_proc_table();
|
|
22983
24387
|
var execFileAsync = (0, node_util_1.promisify)(node_child_process_1.execFile);
|
|
22984
24388
|
var PS_TIMEOUT_MS = 2e3;
|
|
22985
|
-
var PS_BUFFER = 1024 * 1024;
|
|
22986
24389
|
async function listProcessRows(timeoutMs = PS_TIMEOUT_MS) {
|
|
22987
24390
|
const controller = new AbortController();
|
|
22988
24391
|
const timer = setTimeout(() => controller.abort(), timeoutMs);
|
|
22989
24392
|
timer.unref?.();
|
|
22990
24393
|
try {
|
|
22991
24394
|
const { stdout } = await execFileAsync("ps", ["-A", "-o", "pid=,ppid=,command="], {
|
|
22992
|
-
maxBuffer:
|
|
24395
|
+
maxBuffer: proc_table_js_1.PS_MAX_BUFFER,
|
|
22993
24396
|
signal: controller.signal
|
|
22994
24397
|
});
|
|
22995
24398
|
const rows = [];
|
|
@@ -23026,7 +24429,7 @@ var require_kill_tree = __commonJS({
|
|
|
23026
24429
|
timer.unref?.();
|
|
23027
24430
|
try {
|
|
23028
24431
|
const { stdout } = await execFileAsync("ps", ["-A", "-o", "pid=,ppid=,pgid=,stat="], {
|
|
23029
|
-
maxBuffer:
|
|
24432
|
+
maxBuffer: proc_table_js_1.PS_MAX_BUFFER,
|
|
23030
24433
|
signal: controller.signal
|
|
23031
24434
|
});
|
|
23032
24435
|
const tree = /* @__PURE__ */ new Map();
|
|
@@ -23136,15 +24539,15 @@ var require_kill_tree = __commonJS({
|
|
|
23136
24539
|
const rows = await listProcessRows(timeoutMs);
|
|
23137
24540
|
return new Set(rows.filter((row) => row.ppid === parentPid && commandRegex.test(row.command)).map((row) => row.pid));
|
|
23138
24541
|
} catch {
|
|
23139
|
-
return
|
|
24542
|
+
return null;
|
|
23140
24543
|
}
|
|
23141
24544
|
}
|
|
23142
24545
|
var chromiumLaunchTail = Promise.resolve();
|
|
23143
24546
|
async function launchWithChromiumRootPid(launch, options = {}) {
|
|
23144
24547
|
const run = async () => {
|
|
23145
|
-
const prior = await snapshotMatchingChildren(process.pid, options.commandRegex, options.timeoutMs).catch(() =>
|
|
24548
|
+
const prior = await snapshotMatchingChildren(process.pid, options.commandRegex, options.timeoutMs).catch(() => null);
|
|
23146
24549
|
const value = await launch();
|
|
23147
|
-
const rootPid = await findNewChromiumChildPid(process.pid, prior, options).catch(() => null);
|
|
24550
|
+
const rootPid = prior ? await findNewChromiumChildPid(process.pid, prior, options).catch(() => null) : null;
|
|
23148
24551
|
return { value, rootPid };
|
|
23149
24552
|
};
|
|
23150
24553
|
const turn = chromiumLaunchTail.then(run);
|
|
@@ -23566,7 +24969,7 @@ var require_survey_door_harvester = __commonJS({
|
|
|
23566
24969
|
}
|
|
23567
24970
|
if (!payload)
|
|
23568
24971
|
continue;
|
|
23569
|
-
const cleaned = payload.replace(/^\s*```(json)?\s*\n
|
|
24972
|
+
const cleaned = payload.replace(/^\s*```(json)?\s*\n?/i, "").replace(/\n?```\s*$/, "").trim();
|
|
23570
24973
|
if (!cleaned)
|
|
23571
24974
|
continue;
|
|
23572
24975
|
try {
|
|
@@ -76648,7 +78051,7 @@ var require_he = __commonJS({
|
|
|
76648
78051
|
var stringFromCharCode2 = String.fromCharCode;
|
|
76649
78052
|
var object = {};
|
|
76650
78053
|
var hasOwnProperty = object.hasOwnProperty;
|
|
76651
|
-
var
|
|
78054
|
+
var has2 = function(object2, propertyName) {
|
|
76652
78055
|
return hasOwnProperty.call(object2, propertyName);
|
|
76653
78056
|
};
|
|
76654
78057
|
var contains = function(array, value) {
|
|
@@ -76668,7 +78071,7 @@ var require_he = __commonJS({
|
|
|
76668
78071
|
var result = {};
|
|
76669
78072
|
var key2;
|
|
76670
78073
|
for (key2 in defaults) {
|
|
76671
|
-
result[key2] =
|
|
78074
|
+
result[key2] = has2(options, key2) ? options[key2] : defaults[key2];
|
|
76672
78075
|
}
|
|
76673
78076
|
return result;
|
|
76674
78077
|
};
|
|
@@ -76680,7 +78083,7 @@ var require_he = __commonJS({
|
|
|
76680
78083
|
}
|
|
76681
78084
|
return "\uFFFD";
|
|
76682
78085
|
}
|
|
76683
|
-
if (
|
|
78086
|
+
if (has2(decodeMapNumeric, codePoint)) {
|
|
76684
78087
|
if (strict) {
|
|
76685
78088
|
parseError("disallowed character reference");
|
|
76686
78089
|
}
|
|
@@ -76721,7 +78124,7 @@ var require_he = __commonJS({
|
|
|
76721
78124
|
};
|
|
76722
78125
|
if (encodeEverything) {
|
|
76723
78126
|
string = string.replace(regexAsciiWhitelist, function(symbol) {
|
|
76724
|
-
if (useNamedReferences &&
|
|
78127
|
+
if (useNamedReferences && has2(encodeMap, symbol)) {
|
|
76725
78128
|
return "&" + encodeMap[symbol] + ";";
|
|
76726
78129
|
}
|
|
76727
78130
|
return escapeBmpSymbol(symbol);
|
|
@@ -76846,7 +78249,7 @@ var require_he = __commonJS({
|
|
|
76846
78249
|
freeModule.exports = he;
|
|
76847
78250
|
} else {
|
|
76848
78251
|
for (var key in he) {
|
|
76849
|
-
|
|
78252
|
+
has2(he, key) && (freeExports[key] = he[key]);
|
|
76850
78253
|
}
|
|
76851
78254
|
}
|
|
76852
78255
|
} else {
|