@piwitests/reporter 0.63.0 → 0.64.0
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 +1 -1
- package/dist/cli/index.js +62 -27
- package/dist/global-setup-module.js +61 -2
- package/dist/index.d.ts +21 -10
- package/dist/index.js +1833 -546
- package/dist/internal/capture/attachments.d.ts +1 -1
- package/dist/internal/capture/capture-fixtures.d.ts +130 -7
- package/dist/internal/capture/capture-fixtures.js +1900 -776
- package/dist/internal/capture/locator-healing.d.ts +212 -4
- package/dist/internal/capture/pick-on-failure.d.ts +184 -6
- package/dist/types-CGr9jJZ_.d.ts +87 -0
- package/dist/wire-7zsqlBoY.d.ts +48 -0
- package/package.json +2 -2
- package/templates/skills/write-the-missing-test/SKILL.md +6 -4
|
@@ -61,7 +61,7 @@ __export(capture_fixtures_exports, {
|
|
|
61
61
|
stopResourceLedger: () => stopResourceLedger
|
|
62
62
|
});
|
|
63
63
|
module.exports = __toCommonJS(capture_fixtures_exports);
|
|
64
|
-
var
|
|
64
|
+
var path17 = __toESM(require("path"));
|
|
65
65
|
var import_node_zlib2 = require("zlib");
|
|
66
66
|
|
|
67
67
|
// ../picker-dom/src/probe.ts
|
|
@@ -520,8 +520,8 @@ function installPickerOverlay(arg) {
|
|
|
520
520
|
return root && root !== doc && root.host ? root.host : null;
|
|
521
521
|
};
|
|
522
522
|
const targetOf = (e) => {
|
|
523
|
-
const
|
|
524
|
-
const first =
|
|
523
|
+
const path18 = typeof e.composedPath === "function" ? e.composedPath() : null;
|
|
524
|
+
const first = path18 && path18.length ? path18[0] : null;
|
|
525
525
|
return first && first.nodeType === 1 ? first : e.target;
|
|
526
526
|
};
|
|
527
527
|
const buildChain = (raw) => {
|
|
@@ -826,14 +826,14 @@ function showAnchorPicker(arg) {
|
|
|
826
826
|
} catch {
|
|
827
827
|
shadowRoots.length = 0;
|
|
828
828
|
}
|
|
829
|
-
const
|
|
829
|
+
const within4 = (node2, scope) => {
|
|
830
830
|
if (scope === doc) return true;
|
|
831
831
|
for (let n = node2; n; n = parentOf(n)) if (n === scope) return true;
|
|
832
832
|
return false;
|
|
833
833
|
};
|
|
834
834
|
const queryAll = (scope, sel) => {
|
|
835
835
|
const found = Array.from(scope.querySelectorAll(sel));
|
|
836
|
-
for (const sr of shadowRoots) if (
|
|
836
|
+
for (const sr of shadowRoots) if (within4(sr.host, scope)) found.push(...Array.from(sr.querySelectorAll(sel)));
|
|
837
837
|
return found;
|
|
838
838
|
};
|
|
839
839
|
const leafCountable = !!leafTestId || roleOf2(el) === leafRole;
|
|
@@ -1222,8 +1222,22 @@ function showPauseBar(arg) {
|
|
|
1222
1222
|
cleanup();
|
|
1223
1223
|
g.__piwiPauseState = choice;
|
|
1224
1224
|
};
|
|
1225
|
+
const pageTakesEscape = (e) => {
|
|
1226
|
+
const path18 = typeof e.composedPath === "function" ? e.composedPath() : [e.target];
|
|
1227
|
+
const el = path18.find((node) => node && node.nodeType === 1);
|
|
1228
|
+
if (el && !bar.contains(el) && typeof el.closest === "function") {
|
|
1229
|
+
if (el.closest('dialog[open],[role="dialog"],[role="alertdialog"],[role="listbox"],[role="menu"]')) return true;
|
|
1230
|
+
const popup = '[aria-expanded="true"]:is([role="combobox"],[aria-haspopup]:not([aria-haspopup="false"]))';
|
|
1231
|
+
if (el.matches(popup)) return true;
|
|
1232
|
+
}
|
|
1233
|
+
try {
|
|
1234
|
+
return !!doc.querySelector("dialog:modal");
|
|
1235
|
+
} catch {
|
|
1236
|
+
return false;
|
|
1237
|
+
}
|
|
1238
|
+
};
|
|
1225
1239
|
const onKey = (e) => {
|
|
1226
|
-
if (e.key !== "Escape") return;
|
|
1240
|
+
if (e.key !== "Escape" || pageTakesEscape(e)) return;
|
|
1227
1241
|
e.preventDefault();
|
|
1228
1242
|
e.stopImmediatePropagation();
|
|
1229
1243
|
done("resume");
|
|
@@ -1240,7 +1254,9 @@ function showPauseBar(arg) {
|
|
|
1240
1254
|
...arg.record ? [
|
|
1241
1255
|
["Record here", "record", "Record steps in this page: the editor writes them before this line", false]
|
|
1242
1256
|
] : [],
|
|
1243
|
-
...arg.failure ? [] : [
|
|
1257
|
+
...arg.failure ? [] : [
|
|
1258
|
+
["Finish", "finish", "Run the test on without stopping at a breakpoint again in this attempt", false]
|
|
1259
|
+
],
|
|
1244
1260
|
...arg.end && !arg.failure ? [
|
|
1245
1261
|
["End the test", "end", "Skip the rest of this test: it is reported as skipped", false]
|
|
1246
1262
|
] : []
|
|
@@ -1276,6 +1292,102 @@ function takePauseState() {
|
|
|
1276
1292
|
return typeof state === "string" ? state : null;
|
|
1277
1293
|
}
|
|
1278
1294
|
|
|
1295
|
+
// ../picker-dom/src/overlay-stop.ts
|
|
1296
|
+
function showStopHighlight(elements, arg) {
|
|
1297
|
+
const g = globalThis;
|
|
1298
|
+
const doc = g.document;
|
|
1299
|
+
const gone = g.__piwiStopHighlightsGone instanceof Set ? g.__piwiStopHighlightsGone : /* @__PURE__ */ new Set();
|
|
1300
|
+
g.__piwiStopHighlightsGone = gone;
|
|
1301
|
+
if (!doc || !doc.documentElement) return 0;
|
|
1302
|
+
const drawn = doc.getElementById(`__piwi_stop_highlight_${arg.id}`);
|
|
1303
|
+
const targets = gone.has(arg.id) ? [] : elements.slice(0, 500);
|
|
1304
|
+
const shownIn = (layer2) => Array.from(layer2.children).filter((box) => box.dataset.box && box.style.display !== "none").length;
|
|
1305
|
+
const raise = (layer2) => {
|
|
1306
|
+
if (!layer2.hasAttribute("popover")) return;
|
|
1307
|
+
try {
|
|
1308
|
+
if (layer2.matches(":popover-open")) layer2.hidePopover();
|
|
1309
|
+
layer2.showPopover();
|
|
1310
|
+
} catch {
|
|
1311
|
+
layer2.removeAttribute("popover");
|
|
1312
|
+
}
|
|
1313
|
+
};
|
|
1314
|
+
const before = drawn?.__piwiTargets;
|
|
1315
|
+
if (drawn && before && before.length === targets.length && before.every((el, i) => el === targets[i])) {
|
|
1316
|
+
if (targets.length) raise(drawn);
|
|
1317
|
+
drawn.__piwiPlace?.();
|
|
1318
|
+
return shownIn(drawn);
|
|
1319
|
+
}
|
|
1320
|
+
drawn?.remove();
|
|
1321
|
+
if (!targets.length) return 0;
|
|
1322
|
+
const Z = 2147483600;
|
|
1323
|
+
const MONO = "ui-monospace,SFMono-Regular,Menlo,Consolas,monospace";
|
|
1324
|
+
const layer = doc.createElement("div");
|
|
1325
|
+
layer.id = `__piwi_stop_highlight_${arg.id}`;
|
|
1326
|
+
layer.setAttribute("data-piwi-stop-highlight", arg.kind);
|
|
1327
|
+
layer.setAttribute("aria-hidden", "true");
|
|
1328
|
+
layer.setAttribute("popover", "manual");
|
|
1329
|
+
layer.style.cssText = `position:fixed;inset:auto;top:0;left:0;width:0;height:0;margin:0;padding:0;border:0;background:transparent;overflow:visible;pointer-events:none;z-index:${Z - 1};`;
|
|
1330
|
+
layer.__piwiTargets = targets;
|
|
1331
|
+
const look = arg.kind === "call" ? "border:2px solid #a855f7;background:rgba(168,85,247,.14);border-radius:4px;box-shadow:0 0 0 1px rgba(255,255,255,.9),0 0 0 3px rgba(59,7,100,.55);" : "border:2px dashed #a78bfa;background:rgba(167,139,250,.15);border-radius:3px;box-shadow:0 0 0 1px rgba(255,255,255,.8);";
|
|
1332
|
+
const boxes = targets.map(() => {
|
|
1333
|
+
const box = doc.createElement("div");
|
|
1334
|
+
box.dataset.box = "1";
|
|
1335
|
+
box.style.cssText = `position:fixed;display:none;box-sizing:border-box;pointer-events:none;${look}`;
|
|
1336
|
+
layer.appendChild(box);
|
|
1337
|
+
return box;
|
|
1338
|
+
});
|
|
1339
|
+
const label = doc.createElement("div");
|
|
1340
|
+
label.style.cssText = `position:fixed;display:none;pointer-events:none;box-sizing:border-box;max-width:min(620px,92vw);background:#0b1120;color:#f9fafb;border:1px solid #7c3aed;border-radius:6px;padding:2px 7px;font:12px/1.45 ${MONO};white-space:nowrap;overflow:hidden;text-overflow:ellipsis;box-shadow:0 2px 10px rgba(0,0,0,.4);`;
|
|
1341
|
+
label.textContent = arg.label;
|
|
1342
|
+
layer.appendChild(label);
|
|
1343
|
+
doc.documentElement.appendChild(layer);
|
|
1344
|
+
raise(layer);
|
|
1345
|
+
const place = () => {
|
|
1346
|
+
let first = null;
|
|
1347
|
+
targets.forEach((el, i) => {
|
|
1348
|
+
const box = boxes[i];
|
|
1349
|
+
const rect = el && el.isConnected && typeof el.getBoundingClientRect === "function" ? el.getBoundingClientRect() : null;
|
|
1350
|
+
if (!rect || rect.width === 0 && rect.height === 0) {
|
|
1351
|
+
box.style.display = "none";
|
|
1352
|
+
return;
|
|
1353
|
+
}
|
|
1354
|
+
box.style.display = "block";
|
|
1355
|
+
box.style.left = `${rect.left - 2}px`;
|
|
1356
|
+
box.style.top = `${rect.top - 2}px`;
|
|
1357
|
+
box.style.width = `${rect.width + 4}px`;
|
|
1358
|
+
box.style.height = `${rect.height + 4}px`;
|
|
1359
|
+
first ??= rect;
|
|
1360
|
+
});
|
|
1361
|
+
if (first && arg.label) {
|
|
1362
|
+
label.style.display = "block";
|
|
1363
|
+
const height = label.offsetHeight || 20;
|
|
1364
|
+
const above = first.top - 2 - height - 4;
|
|
1365
|
+
label.style.left = `${Math.max(4, first.left - 2)}px`;
|
|
1366
|
+
label.style.top = `${above >= 4 ? above : first.bottom + 6}px`;
|
|
1367
|
+
} else {
|
|
1368
|
+
label.style.display = "none";
|
|
1369
|
+
}
|
|
1370
|
+
};
|
|
1371
|
+
layer.__piwiPlace = place;
|
|
1372
|
+
const follow = () => {
|
|
1373
|
+
if (!layer.isConnected) return;
|
|
1374
|
+
place();
|
|
1375
|
+
g.requestAnimationFrame(follow);
|
|
1376
|
+
};
|
|
1377
|
+
follow();
|
|
1378
|
+
return shownIn(layer);
|
|
1379
|
+
}
|
|
1380
|
+
function removeStopHighlight(id) {
|
|
1381
|
+
const g = globalThis;
|
|
1382
|
+
const gone = g.__piwiStopHighlightsGone instanceof Set ? g.__piwiStopHighlightsGone : /* @__PURE__ */ new Set();
|
|
1383
|
+
g.__piwiStopHighlightsGone = gone;
|
|
1384
|
+
gone.add(id);
|
|
1385
|
+
const layer = g.document?.getElementById(`__piwi_stop_highlight_${id}`);
|
|
1386
|
+
if (!layer) return false;
|
|
1387
|
+
layer.remove();
|
|
1388
|
+
return true;
|
|
1389
|
+
}
|
|
1390
|
+
|
|
1279
1391
|
// ../core/src/locator-chain.ts
|
|
1280
1392
|
var LOCATING_METHODS = /* @__PURE__ */ new Set([
|
|
1281
1393
|
"getByRole",
|
|
@@ -2004,21 +2116,21 @@ var MAX_INDETERMINATE = 20;
|
|
|
2004
2116
|
var STEP = String.raw`[a-zA-Z][\w.-]*:[1-9]\d*`;
|
|
2005
2117
|
var LEVEL = `${STEP}(?:/${STEP})*`;
|
|
2006
2118
|
var ELEMENT_PATH_RE = new RegExp(`^${LEVEL}(?:\\|${LEVEL})*$`);
|
|
2007
|
-
function isElementPath(
|
|
2008
|
-
return typeof
|
|
2119
|
+
function isElementPath(path18) {
|
|
2120
|
+
return typeof path18 === "string" && path18.length <= ELEMENT_PATH_MAX_CHARS && ELEMENT_PATH_RE.test(path18);
|
|
2009
2121
|
}
|
|
2010
2122
|
var offsetOf = (v) => typeof v === "number" && Number.isInteger(v) && v >= 0 && v <= 1e7 ? v : null;
|
|
2011
2123
|
function elementRef(v) {
|
|
2012
2124
|
if (!v || typeof v !== "object") return null;
|
|
2013
|
-
const { path:
|
|
2014
|
-
if (!isElementPath(
|
|
2015
|
-
return { path:
|
|
2125
|
+
const { path: path18, label } = v;
|
|
2126
|
+
if (!isElementPath(path18)) return null;
|
|
2127
|
+
return { path: path18, label: typeof label === "string" ? label.slice(0, ELEMENT_LABEL_MAX_CHARS) : "" };
|
|
2016
2128
|
}
|
|
2017
2129
|
function textPoint(v) {
|
|
2018
2130
|
if (!v || typeof v !== "object") return null;
|
|
2019
|
-
const { path:
|
|
2131
|
+
const { path: path18, offset } = v;
|
|
2020
2132
|
const at = offsetOf(offset);
|
|
2021
|
-
return isElementPath(
|
|
2133
|
+
return isElementPath(path18) && at !== null ? { path: path18, offset: at } : null;
|
|
2022
2134
|
}
|
|
2023
2135
|
function sanitizePageInteraction(raw) {
|
|
2024
2136
|
if (!raw || typeof raw !== "object") return {};
|
|
@@ -2847,8 +2959,8 @@ function parseRouteKey(routeKey2) {
|
|
|
2847
2959
|
return { method: routeKey2.slice(0, space).toUpperCase(), pattern: routeKey2.slice(space + 1) };
|
|
2848
2960
|
}
|
|
2849
2961
|
function patternToRegex(pattern) {
|
|
2850
|
-
const
|
|
2851
|
-
const escaped =
|
|
2962
|
+
const path18 = pattern.split("?")[0] ?? pattern;
|
|
2963
|
+
const escaped = path18.replace(/[.*+?^${}()|[\]\\]/g, "\\$&").replace(/:[A-Za-z0-9_]+/g, "[^/]+");
|
|
2852
2964
|
return new RegExp(`^${escaped}$`);
|
|
2853
2965
|
}
|
|
2854
2966
|
function suitePathEqual(a, b) {
|
|
@@ -2871,13 +2983,13 @@ function matchProbeItem(plan, test) {
|
|
|
2871
2983
|
function requestMatchesRoute(method, url, routeKey2) {
|
|
2872
2984
|
const { method: rm, pattern } = parseRouteKey(routeKey2);
|
|
2873
2985
|
if (rm && rm !== method.toUpperCase()) return false;
|
|
2874
|
-
let
|
|
2986
|
+
let path18;
|
|
2875
2987
|
try {
|
|
2876
|
-
|
|
2988
|
+
path18 = new URL(url).pathname;
|
|
2877
2989
|
} catch {
|
|
2878
|
-
|
|
2990
|
+
path18 = url.split("?")[0] ?? url;
|
|
2879
2991
|
}
|
|
2880
|
-
return patternToRegex(pattern).test(
|
|
2992
|
+
return patternToRegex(pattern).test(path18);
|
|
2881
2993
|
}
|
|
2882
2994
|
function createProbeState() {
|
|
2883
2995
|
return { navigated: false, matches: 0 };
|
|
@@ -2899,10 +3011,10 @@ function isProbeMode() {
|
|
|
2899
3011
|
function loadPlan() {
|
|
2900
3012
|
if (planCache !== void 0) return planCache;
|
|
2901
3013
|
planCache = null;
|
|
2902
|
-
const
|
|
2903
|
-
if (
|
|
3014
|
+
const path18 = process.env[PIWI_PROBE_ENV.plan];
|
|
3015
|
+
if (path18) {
|
|
2904
3016
|
try {
|
|
2905
|
-
planCache = JSON.parse(fs3.readFileSync(
|
|
3017
|
+
planCache = JSON.parse(fs3.readFileSync(path18, "utf8"));
|
|
2906
3018
|
} catch {
|
|
2907
3019
|
planCache = null;
|
|
2908
3020
|
}
|
|
@@ -2919,17 +3031,27 @@ function probeItemForTest(test) {
|
|
|
2919
3031
|
function classifyProbeHandled(signals) {
|
|
2920
3032
|
const blank = signals.blankPage === true;
|
|
2921
3033
|
if ((signals.pageErrors ?? 0) > 0 || signals.backendError && blank) return "unhandled";
|
|
2922
|
-
|
|
3034
|
+
const backendDegraded = signals.backendError && signals.faultIsTheError !== true;
|
|
3035
|
+
return signals.consoleErrors > 0 || signals.dialogs > 0 || backendDegraded || blank ? "degraded" : "graceful";
|
|
3036
|
+
}
|
|
3037
|
+
var SELF_ERROR_FAULTS = /* @__PURE__ */ new Set(["throw", "status"]);
|
|
3038
|
+
function faultIsTheError(fault) {
|
|
3039
|
+
return SELF_ERROR_FAULTS.has(fault);
|
|
3040
|
+
}
|
|
3041
|
+
var RESOURCE_ERROR_TEXT = /^Failed to load resource\b/;
|
|
3042
|
+
function probeConsoleErrors(entries) {
|
|
3043
|
+
return entries.filter((e) => e.type === "error" && !(typeof e.text === "string" && RESOURCE_ERROR_TEXT.test(e.text))).length;
|
|
2923
3044
|
}
|
|
2924
3045
|
function outcomeFromStatus(status, applied) {
|
|
2925
3046
|
if (!applied) return "inconclusive";
|
|
2926
|
-
|
|
3047
|
+
if (status === "failed" || status === "timedOut") return "noticed";
|
|
3048
|
+
return status === "passed" ? "not-noticed" : "inconclusive";
|
|
2927
3049
|
}
|
|
2928
3050
|
function recordProbeOutcome(line) {
|
|
2929
|
-
const
|
|
2930
|
-
if (!
|
|
3051
|
+
const path18 = process.env[PIWI_PROBE_ENV.results];
|
|
3052
|
+
if (!path18) return;
|
|
2931
3053
|
try {
|
|
2932
|
-
fs3.appendFileSync(
|
|
3054
|
+
fs3.appendFileSync(path18, `${JSON.stringify(line)}
|
|
2933
3055
|
`);
|
|
2934
3056
|
} catch {
|
|
2935
3057
|
}
|
|
@@ -2966,23 +3088,26 @@ function dropOneField(body) {
|
|
|
2966
3088
|
} catch {
|
|
2967
3089
|
return body;
|
|
2968
3090
|
}
|
|
3091
|
+
let dropped = false;
|
|
2969
3092
|
if (Array.isArray(parsed2)) {
|
|
2970
3093
|
let key;
|
|
2971
3094
|
for (const el of parsed2) {
|
|
2972
3095
|
if (el && typeof el === "object" && !Array.isArray(el)) {
|
|
2973
3096
|
key ??= Object.keys(el)[0];
|
|
2974
|
-
if (key
|
|
3097
|
+
if (key && Object.prototype.hasOwnProperty.call(el, key)) {
|
|
3098
|
+
delete el[key];
|
|
3099
|
+
dropped = true;
|
|
3100
|
+
}
|
|
2975
3101
|
}
|
|
2976
3102
|
}
|
|
2977
|
-
|
|
2978
|
-
|
|
2979
|
-
|
|
2980
|
-
|
|
2981
|
-
|
|
2982
|
-
|
|
2983
|
-
return JSON.stringify(obj);
|
|
3103
|
+
} else if (parsed2 && typeof parsed2 === "object") {
|
|
3104
|
+
const key = Object.keys(parsed2)[0];
|
|
3105
|
+
if (key) {
|
|
3106
|
+
delete parsed2[key];
|
|
3107
|
+
dropped = true;
|
|
3108
|
+
}
|
|
2984
3109
|
}
|
|
2985
|
-
return body;
|
|
3110
|
+
return dropped ? JSON.stringify(parsed2) : body;
|
|
2986
3111
|
}
|
|
2987
3112
|
function computeFault(fault, input2) {
|
|
2988
3113
|
const base = {
|
|
@@ -3059,7 +3184,7 @@ function originOf(url) {
|
|
|
3059
3184
|
return null;
|
|
3060
3185
|
}
|
|
3061
3186
|
}
|
|
3062
|
-
var sleep = (ms) => new Promise((
|
|
3187
|
+
var sleep = (ms) => new Promise((resolve6) => setTimeout(resolve6, Math.max(0, ms)));
|
|
3063
3188
|
async function performRouteAction(route, action, startedAt) {
|
|
3064
3189
|
switch (action.kind) {
|
|
3065
3190
|
case "abort":
|
|
@@ -3640,84 +3765,507 @@ function recordFlakeResult(line) {
|
|
|
3640
3765
|
}
|
|
3641
3766
|
}
|
|
3642
3767
|
|
|
3643
|
-
// ../core/src/
|
|
3644
|
-
|
|
3645
|
-
|
|
3646
|
-
|
|
3647
|
-
|
|
3648
|
-
|
|
3649
|
-
const msg = err.message ?? "";
|
|
3650
|
-
if (!msg) continue;
|
|
3651
|
-
const dedupeKey = msg.trim().slice(0, 200);
|
|
3652
|
-
if (seen.has(dedupeKey)) continue;
|
|
3653
|
-
seen.add(dedupeKey);
|
|
3654
|
-
parts.push(msg);
|
|
3655
|
-
}
|
|
3656
|
-
return parts.join("\n---\n") || null;
|
|
3657
|
-
}
|
|
3658
|
-
|
|
3659
|
-
// ../core/src/coverage.ts
|
|
3660
|
-
var MAX_COVERAGE_FILES = 2e3;
|
|
3661
|
-
var MAX_COVERAGE_RANGES = 1e4;
|
|
3662
|
-
var MAX_LINE = 1e6;
|
|
3663
|
-
var MAX_PATH_LENGTH = 500;
|
|
3664
|
-
var BLOB_ID = /^(?:[0-9a-f]{40}|[0-9a-f]{64})$/;
|
|
3665
|
-
function isRecord(value) {
|
|
3666
|
-
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
3768
|
+
// ../core/src/mask.ts
|
|
3769
|
+
var DATA_URI_RE = /\bdata:[a-z0-9.+-]+\/[a-z0-9.+-]+;base64,[A-Za-z0-9+/=]+/gi;
|
|
3770
|
+
var JWT_RE = /\beyJ[\w-]{10,}\.[\w-]{5,}\.[\w-]{5,}\b/g;
|
|
3771
|
+
var LONG_HEX_RE2 = /\b[0-9a-f]{32,}\b/gi;
|
|
3772
|
+
function maskTokenLike(text) {
|
|
3773
|
+
return text.replace(DATA_URI_RE, "data:[masked]").replace(JWT_RE, "[masked-token]").replace(LONG_HEX_RE2, "[masked-hex]");
|
|
3667
3774
|
}
|
|
3668
|
-
|
|
3669
|
-
|
|
3775
|
+
var CREDENTIAL_PARAM_RE = /token|code|secret|password|passwd|pwd|key|session|auth|sig|credential|assertion|nonce|otp|jwt/i;
|
|
3776
|
+
function maskUrlFragment(hash) {
|
|
3777
|
+
const body = hash.startsWith("#") ? hash.slice(1) : hash;
|
|
3778
|
+
const query = body.indexOf("?");
|
|
3779
|
+
const route = query >= 0 ? body.slice(0, query) : body.includes("=") ? "" : body;
|
|
3780
|
+
const params = query >= 0 ? body.slice(query + 1) : body.includes("=") ? body : null;
|
|
3781
|
+
const masked = params === null ? null : params.split("&").map((param) => {
|
|
3782
|
+
const eq = param.indexOf("=");
|
|
3783
|
+
if (eq < 0) return maskTokenLike(param);
|
|
3784
|
+
const name = param.slice(0, eq);
|
|
3785
|
+
const value = param.slice(eq + 1);
|
|
3786
|
+
return `${name}=${value && CREDENTIAL_PARAM_RE.test(name) ? "[masked]" : maskTokenLike(value)}`;
|
|
3787
|
+
}).join("&");
|
|
3788
|
+
const out = maskTokenLike(route) + (masked === null ? "" : query >= 0 ? `?${masked}` : masked);
|
|
3789
|
+
return hash.startsWith("#") ? `#${out}` : out;
|
|
3670
3790
|
}
|
|
3671
|
-
|
|
3672
|
-
|
|
3791
|
+
var HIDDEN_VALUE = "<hidden>";
|
|
3792
|
+
var SECRET_AUTOCOMPLETE = [
|
|
3793
|
+
"current-password",
|
|
3794
|
+
"new-password",
|
|
3795
|
+
"one-time-code",
|
|
3796
|
+
"cc-number",
|
|
3797
|
+
"cc-csc"
|
|
3798
|
+
];
|
|
3799
|
+
var SECRET_FIELD_SELECTOR = [
|
|
3800
|
+
'input[type="password" i]',
|
|
3801
|
+
...SECRET_AUTOCOMPLETE.flatMap((token) => [
|
|
3802
|
+
`input[autocomplete~="${token}" i]`,
|
|
3803
|
+
`textarea[autocomplete~="${token}" i]`
|
|
3804
|
+
])
|
|
3805
|
+
].join(", ");
|
|
3806
|
+
var SECRET_WORDS = /* @__PURE__ */ new Set([
|
|
3807
|
+
"pass",
|
|
3808
|
+
"password",
|
|
3809
|
+
"passwd",
|
|
3810
|
+
"passphrase",
|
|
3811
|
+
"passcode",
|
|
3812
|
+
"pwd",
|
|
3813
|
+
"secret",
|
|
3814
|
+
"secrets",
|
|
3815
|
+
"token",
|
|
3816
|
+
"tokens",
|
|
3817
|
+
"jwt",
|
|
3818
|
+
"bearer",
|
|
3819
|
+
"sig",
|
|
3820
|
+
"signature",
|
|
3821
|
+
"key",
|
|
3822
|
+
"keys",
|
|
3823
|
+
"apikey",
|
|
3824
|
+
"auth",
|
|
3825
|
+
"oauth",
|
|
3826
|
+
"authorization",
|
|
3827
|
+
"session",
|
|
3828
|
+
"sessionid",
|
|
3829
|
+
"jsessionid",
|
|
3830
|
+
"sid",
|
|
3831
|
+
"cookie",
|
|
3832
|
+
"cookies",
|
|
3833
|
+
"credential",
|
|
3834
|
+
"credentials",
|
|
3835
|
+
"otp",
|
|
3836
|
+
"refresh",
|
|
3837
|
+
"csrf",
|
|
3838
|
+
"xsrf"
|
|
3839
|
+
]);
|
|
3840
|
+
var SECRET_NAMES = /* @__PURE__ */ new Set(["code", "pin"]);
|
|
3841
|
+
function nameWords(name) {
|
|
3842
|
+
return name.replace(/([a-z0-9])([A-Z])/g, "$1 $2").replace(/([A-Z])(?=[A-Z][a-z])/g, "$1 ").toLowerCase().split(/[^a-z0-9]+/).filter(Boolean);
|
|
3673
3843
|
}
|
|
3674
|
-
function
|
|
3675
|
-
|
|
3676
|
-
|
|
3677
|
-
|
|
3678
|
-
if (
|
|
3679
|
-
|
|
3680
|
-
if (isLine(start) && isLine(end) && start <= end) out.push([start, end]);
|
|
3844
|
+
function isSecretName(name) {
|
|
3845
|
+
let secret = namesRead.get(name);
|
|
3846
|
+
if (secret === void 0) {
|
|
3847
|
+
secret = SECRET_NAMES.has(name.trim().toLowerCase()) || nameWords(name).some((word) => SECRET_WORDS.has(word));
|
|
3848
|
+
if (namesRead.size >= 1024) namesRead.clear();
|
|
3849
|
+
namesRead.set(name, secret);
|
|
3681
3850
|
}
|
|
3682
|
-
return
|
|
3851
|
+
return secret;
|
|
3683
3852
|
}
|
|
3684
|
-
|
|
3685
|
-
|
|
3686
|
-
|
|
3687
|
-
|
|
3688
|
-
|
|
3689
|
-
|
|
3853
|
+
var namesRead = /* @__PURE__ */ new Map();
|
|
3854
|
+
var SECRET_SHAPES = [
|
|
3855
|
+
/\beyJ[\w-]{5,}\.[\w-]{5,}\.[\w-]*/g,
|
|
3856
|
+
/\bBearer(?:\s+|%20)[\w.~+/-]{8,}=*/gi,
|
|
3857
|
+
/\b(?:sk|rk)_(?:live|test)_[A-Za-z0-9]{8,}/g,
|
|
3858
|
+
/\bwhsec_[A-Za-z0-9]{20,}/g,
|
|
3859
|
+
/\bsk-(?=[\w-]{0,200}\d)(?=[\w-]{0,200}[A-Z])[\w-]{32,}/g,
|
|
3860
|
+
/\bglpat-[\w-]{20,}/g,
|
|
3861
|
+
/\bnpm_[A-Za-z0-9]{36}\b/g,
|
|
3862
|
+
/\bSK[0-9a-f]{32}\b/g,
|
|
3863
|
+
/\bAKIA[0-9A-Z]{16}\b/g,
|
|
3864
|
+
/\bAIza[\w-]{30,}/g,
|
|
3865
|
+
/\bgh[opsu]_[A-Za-z0-9]{20,}/g,
|
|
3866
|
+
/\bgithub_pat_\w{20,}/g,
|
|
3867
|
+
/\bxox[abprs]-[\w-]{8,}/g
|
|
3868
|
+
];
|
|
3869
|
+
var WHOLE_SHAPES = SECRET_SHAPES.map(
|
|
3870
|
+
(re) => new RegExp(`^(?:${re.source})$`, re.flags.replace("g", ""))
|
|
3871
|
+
);
|
|
3872
|
+
var BASIC = /\bBasic\s+([A-Za-z0-9+/]{4,}={0,2})(?![\w+/=])/g;
|
|
3873
|
+
var PEM_BEGIN = /-----BEGIN ([A-Z0-9 ]{0,40}PRIVATE KEY)-----/g;
|
|
3874
|
+
var SET_COOKIE = /(\bSet-Cookie:[ \t]*[^=;\s]{1,256}=)([^;\r\n]{1,4096})/gi;
|
|
3875
|
+
var COOKIE_LINE = /((?<![\w-])Cookie:[ \t]*)([^\r\n]{1,16384})/gi;
|
|
3876
|
+
var JSON_FIELD = /("((?:[^"\\\r\n]|\\.){1,256})"\s*:\s*")((?:[^"\\\r\n]|\\.){1,4096})"/g;
|
|
3877
|
+
var TOKEN_RUN = /[A-Za-z0-9+/_-]{40,}={0,2}/g;
|
|
3878
|
+
var MIN_RANDOM = 40;
|
|
3879
|
+
var BASE64_DIGITS = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
|
|
3880
|
+
function base64OfText(run) {
|
|
3881
|
+
let bits = 0;
|
|
3882
|
+
let acc = 0;
|
|
3883
|
+
let bytes = 0;
|
|
3884
|
+
let printable = 0;
|
|
3885
|
+
for (const c of run.replace(/=+$/, "")) {
|
|
3886
|
+
const digit = BASE64_DIGITS.indexOf(c === "-" ? "+" : c === "_" ? "/" : c);
|
|
3887
|
+
if (digit < 0) return false;
|
|
3888
|
+
acc = (acc << 6 | digit) & 65535;
|
|
3889
|
+
bits += 6;
|
|
3890
|
+
if (bits < 8) continue;
|
|
3891
|
+
bits -= 8;
|
|
3892
|
+
const byte = acc >> bits & 255;
|
|
3893
|
+
bytes++;
|
|
3894
|
+
if (byte >= 32 && byte < 127 || byte === 9 || byte === 10 || byte === 13) printable++;
|
|
3690
3895
|
}
|
|
3691
|
-
return
|
|
3896
|
+
return bytes > 0 && printable * 10 >= bytes * 9;
|
|
3692
3897
|
}
|
|
3693
|
-
function
|
|
3694
|
-
const
|
|
3695
|
-
for (const
|
|
3696
|
-
|
|
3697
|
-
|
|
3698
|
-
|
|
3699
|
-
return out;
|
|
3898
|
+
function bitsPerChar(text) {
|
|
3899
|
+
const counts = /* @__PURE__ */ new Map();
|
|
3900
|
+
for (const c of text) counts.set(c, (counts.get(c) ?? 0) + 1);
|
|
3901
|
+
let bits = 0;
|
|
3902
|
+
for (const n of counts.values()) bits -= n / text.length * Math.log2(n / text.length);
|
|
3903
|
+
return bits;
|
|
3700
3904
|
}
|
|
3701
|
-
function
|
|
3702
|
-
if (
|
|
3703
|
-
|
|
3704
|
-
if (
|
|
3705
|
-
|
|
3706
|
-
|
|
3905
|
+
function looksRandom(text) {
|
|
3906
|
+
if (text.length < MIN_RANDOM || !/[A-Z]/.test(text) || !/[a-z]/.test(text) || !/\d/.test(text)) return false;
|
|
3907
|
+
let letters = 0;
|
|
3908
|
+
for (const c of text) if (c >= "a" && c <= "z" || c >= "A" && c <= "Z") letters++;
|
|
3909
|
+
let wordy = 0;
|
|
3910
|
+
for (const run of text.matchAll(/[a-z]{6,}|[A-Z]{6,}/g)) wordy += run[0].length;
|
|
3911
|
+
return wordy * 5 <= letters && bitsPerChar(text) >= 4 && !base64OfText(text);
|
|
3707
3912
|
}
|
|
3708
|
-
function
|
|
3709
|
-
const
|
|
3710
|
-
if (
|
|
3711
|
-
|
|
3712
|
-
|
|
3713
|
-
|
|
3913
|
+
function isBasicCredential(encoded) {
|
|
3914
|
+
const text = encoded.replace(/=+$/, "");
|
|
3915
|
+
if (text.length % 4 === 1) return false;
|
|
3916
|
+
let decoded = "";
|
|
3917
|
+
let bits = 0;
|
|
3918
|
+
let acc = 0;
|
|
3919
|
+
for (const c of text) {
|
|
3920
|
+
const digit = BASE64_DIGITS.indexOf(c);
|
|
3921
|
+
if (digit < 0) return false;
|
|
3922
|
+
acc = (acc << 6 | digit) & 65535;
|
|
3923
|
+
bits += 6;
|
|
3924
|
+
if (bits < 8) continue;
|
|
3925
|
+
bits -= 8;
|
|
3926
|
+
decoded += String.fromCharCode(acc >> bits & 255);
|
|
3927
|
+
}
|
|
3928
|
+
return /^[\x20-\x7e]+:[\x20-\x7e]*$/.test(decoded);
|
|
3714
3929
|
}
|
|
3715
|
-
function
|
|
3716
|
-
|
|
3930
|
+
function isSecretValue(value) {
|
|
3931
|
+
const text = value.trim();
|
|
3932
|
+
if (text.length < 10 || text.length > 4096) return false;
|
|
3933
|
+
if (WHOLE_SHAPES.some((re) => re.test(text))) return true;
|
|
3934
|
+
const basic = /^Basic\s+([A-Za-z0-9+/]{4,}={0,2})$/.exec(text);
|
|
3935
|
+
if (basic && isBasicCredential(basic[1])) return true;
|
|
3936
|
+
if (/^-----BEGIN [A-Z0-9 ]{0,40}PRIVATE KEY-----/.test(text)) return true;
|
|
3937
|
+
return /^[A-Za-z0-9+/_-]{40,}={0,2}$/.test(text) && looksRandom(text);
|
|
3717
3938
|
}
|
|
3718
|
-
|
|
3719
|
-
|
|
3720
|
-
|
|
3939
|
+
var URL_IN_TEXT = /\b[a-z][a-z0-9+.-]{0,31}:\/\/[^\s"'<>`]+/gi;
|
|
3940
|
+
var URL_PARTS = /^(?:([a-z][a-z0-9+.-]*:)?\/\/([^/?#]*))?([^?#]*)(\?[^#]*)?(#[\s\S]*)?$/i;
|
|
3941
|
+
function decodeSafe(text) {
|
|
3942
|
+
try {
|
|
3943
|
+
return decodeURIComponent(text);
|
|
3944
|
+
} catch {
|
|
3945
|
+
return text;
|
|
3946
|
+
}
|
|
3947
|
+
}
|
|
3948
|
+
function hider(count) {
|
|
3949
|
+
return () => {
|
|
3950
|
+
count.n++;
|
|
3951
|
+
return HIDDEN_VALUE;
|
|
3952
|
+
};
|
|
3953
|
+
}
|
|
3954
|
+
function hideParams(params, count) {
|
|
3955
|
+
const hide = hider(count);
|
|
3956
|
+
return params.split(/([&;])/).map((part, i) => {
|
|
3957
|
+
const eq = part.indexOf("=");
|
|
3958
|
+
if (i % 2 === 1 || eq < 0) return part;
|
|
3959
|
+
const name = part.slice(0, eq);
|
|
3960
|
+
const value = part.slice(eq + 1);
|
|
3961
|
+
const decoded = decodeSafe(value);
|
|
3962
|
+
if (!value || decoded === HIDDEN_VALUE) return part;
|
|
3963
|
+
return isSecretName(decodeSafe(name)) || isSecretValue(decoded) ? `${name}=${hide()}` : part;
|
|
3964
|
+
}).join("");
|
|
3965
|
+
}
|
|
3966
|
+
function hidePath(path18, count) {
|
|
3967
|
+
const hide = hider(count);
|
|
3968
|
+
return path18.split("/").map((segment) => {
|
|
3969
|
+
const [own = "", ...params] = segment.split(";");
|
|
3970
|
+
const decoded = decodeSafe(own);
|
|
3971
|
+
const kept = decoded !== HIDDEN_VALUE && isSecretValue(decoded) ? hide() : own;
|
|
3972
|
+
return params.length ? `${kept};${hideParams(params.join(";"), count)}` : kept;
|
|
3973
|
+
}).join("/");
|
|
3974
|
+
}
|
|
3975
|
+
function hideSecretsInUrl(url, count = { n: 0 }) {
|
|
3976
|
+
const parts = URL_PARTS.exec(url);
|
|
3977
|
+
if (!parts) return url;
|
|
3978
|
+
const [, scheme = "", authority, path18 = "", query = "", fragment = ""] = parts;
|
|
3979
|
+
let out = "";
|
|
3980
|
+
if (authority !== void 0) {
|
|
3981
|
+
const at = authority.lastIndexOf("@");
|
|
3982
|
+
const colon = at >= 0 ? authority.slice(0, at).indexOf(":") : -1;
|
|
3983
|
+
out += colon >= 0 && colon < at - 1 ? `${scheme}//${authority.slice(0, colon + 1)}${hider(count)()}${authority.slice(at)}` : `${scheme}//${authority}`;
|
|
3984
|
+
}
|
|
3985
|
+
out += hidePath(path18, count);
|
|
3986
|
+
if (query) out += `?${hideParams(query.slice(1), count)}`;
|
|
3987
|
+
if (fragment) {
|
|
3988
|
+
const body = fragment.slice(1);
|
|
3989
|
+
const ask = body.indexOf("?");
|
|
3990
|
+
out += `#${ask >= 0 ? `${hidePath(body.slice(0, ask), count)}?${hideParams(body.slice(ask + 1), count)}` : body.includes("=") ? hideParams(body, count) : hidePath(body, count)}`;
|
|
3991
|
+
}
|
|
3992
|
+
return out;
|
|
3993
|
+
}
|
|
3994
|
+
var QUERY_PAIR = /([?&;#])([^=&#?;\s"'<>]{1,256})=([^&;#\s"'<>]*)/g;
|
|
3995
|
+
var URL_PASSWORD = /(\/\/[^/:@\s"'<>]{1,256}:)([^@/\s"'<>]{1,256})(@)/g;
|
|
3996
|
+
function hidePem(text, hide) {
|
|
3997
|
+
let out = "";
|
|
3998
|
+
let last = 0;
|
|
3999
|
+
let nextEnd = -2;
|
|
4000
|
+
PEM_BEGIN.lastIndex = 0;
|
|
4001
|
+
for (let begin = PEM_BEGIN.exec(text); begin; begin = PEM_BEGIN.exec(text)) {
|
|
4002
|
+
if (begin.index < last) continue;
|
|
4003
|
+
if (nextEnd !== -1 && nextEnd < begin.index) nextEnd = text.indexOf("-----END ", begin.index);
|
|
4004
|
+
const footer = nextEnd >= 0 ? /^-----END [A-Z0-9 ]{0,40}-----/.exec(text.slice(nextEnd, nextEnd + 60)) : null;
|
|
4005
|
+
const until = footer ? nextEnd + footer[0].length : begin.index + begin[0].length + (/^(?:\s*[A-Za-z0-9+/=]+)*/.exec(text.slice(begin.index + begin[0].length))?.[0].length ?? 0);
|
|
4006
|
+
out += `${text.slice(last, begin.index)}${hide()}`;
|
|
4007
|
+
last = until;
|
|
4008
|
+
PEM_BEGIN.lastIndex = until;
|
|
4009
|
+
}
|
|
4010
|
+
return out + text.slice(last);
|
|
4011
|
+
}
|
|
4012
|
+
function hideInText(text, count) {
|
|
4013
|
+
if (text.length < 4 || !/[A-Za-z0-9]/.test(text)) return text;
|
|
4014
|
+
const hide = hider(count);
|
|
4015
|
+
let out = hidePem(text, hide);
|
|
4016
|
+
out = out.replace(QUERY_PAIR, (all, sep8, name, value) => {
|
|
4017
|
+
const decoded = decodeSafe(value);
|
|
4018
|
+
if (!value || decoded === HIDDEN_VALUE) return all;
|
|
4019
|
+
return isSecretName(name) || isSecretValue(decoded) ? `${sep8}${name}=${hide()}` : all;
|
|
4020
|
+
});
|
|
4021
|
+
out = out.replace(URL_PASSWORD, (_all, before, _password, at) => `${before}${hide()}${at}`);
|
|
4022
|
+
out = out.replace(SET_COOKIE, (_all, before) => `${before}${hide()}`);
|
|
4023
|
+
out = out.replace(COOKIE_LINE, (_all, before, pairs) => {
|
|
4024
|
+
const values = pairs.replace(
|
|
4025
|
+
/([^=;\s]{1,256}=)([^;]*)/g,
|
|
4026
|
+
(pair, name, value) => value.trim() && value.trim() !== HIDDEN_VALUE ? `${name}${hide()}` : pair
|
|
4027
|
+
);
|
|
4028
|
+
return `${before}${values}`;
|
|
4029
|
+
});
|
|
4030
|
+
out = out.replace(
|
|
4031
|
+
JSON_FIELD,
|
|
4032
|
+
(all, before, name, value) => isSecretName(name) && value !== HIDDEN_VALUE ? `${before}${hide()}"` : all
|
|
4033
|
+
);
|
|
4034
|
+
out = out.replace(BASIC, (all, encoded) => isBasicCredential(encoded) ? hide() : all);
|
|
4035
|
+
for (const shape of SECRET_SHAPES) out = out.replace(shape, () => hide());
|
|
4036
|
+
return out.replace(
|
|
4037
|
+
TOKEN_RUN,
|
|
4038
|
+
(run) => run.includes("/") && !/[+=]/.test(run) ? run.split("/").map((part) => looksRandom(part) ? hide() : part).join("/") : looksRandom(run) ? hide() : run
|
|
4039
|
+
);
|
|
4040
|
+
}
|
|
4041
|
+
function hideSecretsInText(text, count = { n: 0 }) {
|
|
4042
|
+
let out = "";
|
|
4043
|
+
let last = 0;
|
|
4044
|
+
for (const url of text.matchAll(URL_IN_TEXT)) {
|
|
4045
|
+
out += hideInText(text.slice(last, url.index), count) + hideSecretsInUrl(url[0], count);
|
|
4046
|
+
last = url.index + url[0].length;
|
|
4047
|
+
}
|
|
4048
|
+
return out + hideInText(text.slice(last), count);
|
|
4049
|
+
}
|
|
4050
|
+
var TYPED_TITLE = /^(?:Fill|Type|Insert) "/;
|
|
4051
|
+
function hideTypedText(title) {
|
|
4052
|
+
const typing = TYPED_TITLE.exec(title);
|
|
4053
|
+
if (!typing) return title;
|
|
4054
|
+
const start = typing[0].length;
|
|
4055
|
+
const end = title.endsWith('"') ? title.length - 1 : title.lastIndexOf('" ');
|
|
4056
|
+
return end >= start ? `${title.slice(0, start)}${HIDDEN_VALUE}${title.slice(end)}` : `${typing[0]}${HIDDEN_VALUE}"`;
|
|
4057
|
+
}
|
|
4058
|
+
var SECRET_FIELD_WORDS = /* @__PURE__ */ new Set([
|
|
4059
|
+
"passe",
|
|
4060
|
+
"mdp",
|
|
4061
|
+
"passwort",
|
|
4062
|
+
"kennwort",
|
|
4063
|
+
"contrasena",
|
|
4064
|
+
"clave",
|
|
4065
|
+
"senha",
|
|
4066
|
+
"cvv",
|
|
4067
|
+
"cvc",
|
|
4068
|
+
"csc",
|
|
4069
|
+
"pin",
|
|
4070
|
+
"pw",
|
|
4071
|
+
"totp",
|
|
4072
|
+
"mfa",
|
|
4073
|
+
"2fa"
|
|
4074
|
+
]);
|
|
4075
|
+
var SECRET_STEMS = ["password", "passwd", "passwort", "kennwort", "contrasena", "senha", "motdepasse"];
|
|
4076
|
+
var SECRET_CODE_KINDS = /* @__PURE__ */ new Set([
|
|
4077
|
+
"verification",
|
|
4078
|
+
"security",
|
|
4079
|
+
"confirmation",
|
|
4080
|
+
"auth",
|
|
4081
|
+
"authentication",
|
|
4082
|
+
"access",
|
|
4083
|
+
"time",
|
|
4084
|
+
"otp",
|
|
4085
|
+
"sms",
|
|
4086
|
+
"login",
|
|
4087
|
+
"mfa",
|
|
4088
|
+
"2fa"
|
|
4089
|
+
]);
|
|
4090
|
+
function isSecretLocator(locator) {
|
|
4091
|
+
const plain = locator.normalize("NFD").replace(/[̀-ͯ]/g, "");
|
|
4092
|
+
if (/type\s*=\s*["']?password\b/i.test(plain)) return true;
|
|
4093
|
+
for (const quoted of plain.matchAll(/(['"])((?:(?!\1)[^\\]|\\.)*)\1/g)) if (isSecretName(quoted[2])) return true;
|
|
4094
|
+
const words = nameWords(plain);
|
|
4095
|
+
return words.some(
|
|
4096
|
+
(word) => SECRET_WORDS.has(word) || SECRET_FIELD_WORDS.has(word) || SECRET_STEMS.some((stem) => word.includes(stem))
|
|
4097
|
+
) || words.includes("code") && words.some((word) => SECRET_CODE_KINDS.has(word));
|
|
4098
|
+
}
|
|
4099
|
+
var TYPED_CALL = (
|
|
4100
|
+
// eslint-disable-next-line no-control-regex -- matches the ESC byte of a color code
|
|
4101
|
+
/^((?:\u001b\[[\d;]*m)*[ \t]*(?:(?:-|\d+ ×)[ \t]+)?(?:\w+\.)?)(fill|type|insertText)\("/gm
|
|
4102
|
+
);
|
|
4103
|
+
var TYPED_END = /"\)(?=(?:\u001b\[[\d;]*m|[ \t])*(?:\r?\n|$))/g;
|
|
4104
|
+
function hideTypedTextInLog(text) {
|
|
4105
|
+
let out = "";
|
|
4106
|
+
let from = 0;
|
|
4107
|
+
TYPED_CALL.lastIndex = 0;
|
|
4108
|
+
for (let call = TYPED_CALL.exec(text); call; call = TYPED_CALL.exec(text)) {
|
|
4109
|
+
out += `${text.slice(from, call.index)}${call[1]}${call[2]}("${HIDDEN_VALUE}")`;
|
|
4110
|
+
TYPED_END.lastIndex = call.index + call[0].length;
|
|
4111
|
+
const end = TYPED_END.exec(text);
|
|
4112
|
+
if (!end) return out;
|
|
4113
|
+
from = end.index + 2;
|
|
4114
|
+
TYPED_CALL.lastIndex = from;
|
|
4115
|
+
}
|
|
4116
|
+
return out + text.slice(from);
|
|
4117
|
+
}
|
|
4118
|
+
var READ_LOCATOR = /(?:waiting for|Locator:)[ \t]+(.*)$/gm;
|
|
4119
|
+
var RESOLVED_PASSWORD = /resolved to <input\b[^>\n]*\btype="password"/i;
|
|
4120
|
+
var READ_VALUE = (
|
|
4121
|
+
// eslint-disable-next-line no-control-regex -- matches the ESC byte of a color code
|
|
4122
|
+
/(unexpected value |(?:Expected|Received)(?: string)?:[ \t]*(?:\u001b\[[\d;]*m)*(?:not[ \t]+)?)"(?:.*"|.*)/g
|
|
4123
|
+
);
|
|
4124
|
+
var RESOLVED_ELEMENT = /resolved to <[^>\n]*/g;
|
|
4125
|
+
var VALUE_ATTRIBUTE = /\bvalue="[^"\n]*"/g;
|
|
4126
|
+
var COLOR = /\u001b\[[\d;]*m/g;
|
|
4127
|
+
function namesSecretField(text) {
|
|
4128
|
+
if (RESOLVED_PASSWORD.test(text)) return true;
|
|
4129
|
+
for (const named of text.matchAll(READ_LOCATOR))
|
|
4130
|
+
if (isSecretLocator(named[1].replace(COLOR, "").trim())) return true;
|
|
4131
|
+
return false;
|
|
4132
|
+
}
|
|
4133
|
+
function hideReadValues(text) {
|
|
4134
|
+
return text.replace(READ_VALUE, (_, before) => `${before}"${HIDDEN_VALUE}"`);
|
|
4135
|
+
}
|
|
4136
|
+
function hideResolvedValues(text, secretField) {
|
|
4137
|
+
if (!text.includes("resolved to <")) return text;
|
|
4138
|
+
return text.replace(
|
|
4139
|
+
RESOLVED_ELEMENT,
|
|
4140
|
+
(element) => secretField || /\btype="password"/i.test(element) ? element.replace(VALUE_ATTRIBUTE, `value="${HIDDEN_VALUE}"`) : element
|
|
4141
|
+
);
|
|
4142
|
+
}
|
|
4143
|
+
var LOGGED_HEADER = (
|
|
4144
|
+
// eslint-disable-next-line no-control-regex -- matches the ESC byte of a color code
|
|
4145
|
+
/^(?:\u001b\[[\d;]*m)*(?:[ \t]+(?:(?:-|\d+ ×)[ \t]+)?|(?:-|\d+ ×)[ \t]+)([A-Za-z][\w-]*): /
|
|
4146
|
+
);
|
|
4147
|
+
function colorsAtEnd(line) {
|
|
4148
|
+
let end = line.length;
|
|
4149
|
+
while (end > 0 && line[end - 1] === "m") {
|
|
4150
|
+
const start = line.lastIndexOf("\x1B[", end - 1);
|
|
4151
|
+
if (start < 0 || !/^[\d;]*$/.test(line.slice(start + 2, end - 1))) break;
|
|
4152
|
+
end = start;
|
|
4153
|
+
}
|
|
4154
|
+
return end;
|
|
4155
|
+
}
|
|
4156
|
+
function hideSecretHeaders(text) {
|
|
4157
|
+
if (!text.includes(": ")) return text;
|
|
4158
|
+
return text.split("\n").map((line) => {
|
|
4159
|
+
const header = LOGGED_HEADER.exec(line);
|
|
4160
|
+
if (!header || !isSecretName(header[1])) return line;
|
|
4161
|
+
const rest = line.slice(header[0].length);
|
|
4162
|
+
const end = colorsAtEnd(rest);
|
|
4163
|
+
return end === 0 ? line : `${header[0]}${HIDDEN_VALUE}${rest.slice(end)}`;
|
|
4164
|
+
}).join("\n");
|
|
4165
|
+
}
|
|
4166
|
+
function hideSecretsInLog(text, secretField = false) {
|
|
4167
|
+
const secret = secretField || namesSecretField(text);
|
|
4168
|
+
let hidden2 = hideResolvedValues(hideTypedTextInLog(text), secret);
|
|
4169
|
+
if (secret) hidden2 = hideReadValues(hidden2);
|
|
4170
|
+
return hideSecretHeaders(hidden2);
|
|
4171
|
+
}
|
|
4172
|
+
var CALL_LOG = "\nCall log:";
|
|
4173
|
+
var CALL_LOG_END = /\n(?:---\n|[ \t]+at )/g;
|
|
4174
|
+
function hideSecretsInError(text) {
|
|
4175
|
+
const secretField = namesSecretField(text);
|
|
4176
|
+
let out = "";
|
|
4177
|
+
let from = 0;
|
|
4178
|
+
for (; ; ) {
|
|
4179
|
+
const log = text.indexOf(CALL_LOG, from);
|
|
4180
|
+
const message = text.slice(from, log === -1 ? text.length : log);
|
|
4181
|
+
out += secretField ? hideReadValues(message) : message;
|
|
4182
|
+
if (log === -1) return out;
|
|
4183
|
+
const start = log + CALL_LOG.length;
|
|
4184
|
+
CALL_LOG_END.lastIndex = start;
|
|
4185
|
+
const end = CALL_LOG_END.exec(text)?.index ?? text.length;
|
|
4186
|
+
out += CALL_LOG + hideSecretsInLog(text.slice(start, end), secretField);
|
|
4187
|
+
from = end;
|
|
4188
|
+
}
|
|
4189
|
+
}
|
|
4190
|
+
|
|
4191
|
+
// ../core/src/error-text.ts
|
|
4192
|
+
function joinErrorMessages(errors) {
|
|
4193
|
+
if (!errors || errors.length === 0) return null;
|
|
4194
|
+
const seen = /* @__PURE__ */ new Set();
|
|
4195
|
+
const parts = [];
|
|
4196
|
+
for (const err of errors) {
|
|
4197
|
+
const msg = err.message ?? "";
|
|
4198
|
+
if (!msg) continue;
|
|
4199
|
+
const dedupeKey = msg.trim().slice(0, 200);
|
|
4200
|
+
if (seen.has(dedupeKey)) continue;
|
|
4201
|
+
seen.add(dedupeKey);
|
|
4202
|
+
parts.push(hideSecretsInError(msg));
|
|
4203
|
+
}
|
|
4204
|
+
return parts.join("\n---\n") || null;
|
|
4205
|
+
}
|
|
4206
|
+
|
|
4207
|
+
// ../core/src/coverage.ts
|
|
4208
|
+
var MAX_COVERAGE_FILES = 2e3;
|
|
4209
|
+
var MAX_COVERAGE_RANGES = 1e4;
|
|
4210
|
+
var MAX_LINE = 1e6;
|
|
4211
|
+
var MAX_PATH_LENGTH = 500;
|
|
4212
|
+
var BLOB_ID = /^(?:[0-9a-f]{40}|[0-9a-f]{64})$/;
|
|
4213
|
+
function isRecord(value) {
|
|
4214
|
+
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
4215
|
+
}
|
|
4216
|
+
function isRuntime(value) {
|
|
4217
|
+
return value === "browser" || value === "server";
|
|
4218
|
+
}
|
|
4219
|
+
function isLine(value) {
|
|
4220
|
+
return typeof value === "number" && Number.isInteger(value) && value >= 1 && value <= MAX_LINE;
|
|
4221
|
+
}
|
|
4222
|
+
function validRanges(raw) {
|
|
4223
|
+
if (!Array.isArray(raw)) return [];
|
|
4224
|
+
const out = [];
|
|
4225
|
+
for (const item of raw) {
|
|
4226
|
+
if (!Array.isArray(item) || item.length !== 2) continue;
|
|
4227
|
+
const [start, end] = item;
|
|
4228
|
+
if (isLine(start) && isLine(end) && start <= end) out.push([start, end]);
|
|
4229
|
+
}
|
|
4230
|
+
return out.sort((a, b) => a[0] - b[0] || a[1] - b[1]);
|
|
4231
|
+
}
|
|
4232
|
+
function normalizeLineRanges(ranges) {
|
|
4233
|
+
const out = [];
|
|
4234
|
+
for (const range of validRanges(ranges)) {
|
|
4235
|
+
const last = out[out.length - 1];
|
|
4236
|
+
if (last && range[0] <= last[1] + 1) last[1] = Math.max(last[1], range[1]);
|
|
4237
|
+
else out.push(range);
|
|
4238
|
+
}
|
|
4239
|
+
return out;
|
|
4240
|
+
}
|
|
4241
|
+
function normalizeFunctionRanges(ranges) {
|
|
4242
|
+
const out = [];
|
|
4243
|
+
for (const range of validRanges(ranges)) {
|
|
4244
|
+
const last = out[out.length - 1];
|
|
4245
|
+
if (!last || last[0] !== range[0] || last[1] !== range[1]) out.push(range);
|
|
4246
|
+
}
|
|
4247
|
+
return out;
|
|
4248
|
+
}
|
|
4249
|
+
function normalizeCoveragePath(raw) {
|
|
4250
|
+
if (typeof raw !== "string" || raw.length > MAX_PATH_LENGTH || raw.includes("\0")) return null;
|
|
4251
|
+
const path18 = raw.replace(/\\/g, "/").replace(/^\.\//, "");
|
|
4252
|
+
if (!path18 || path18.startsWith("../") || path18.startsWith("/") || /^[A-Za-z]:\//.test(path18)) return null;
|
|
4253
|
+
if (/(^|\/)node_modules\//.test(path18)) return null;
|
|
4254
|
+
return path18;
|
|
4255
|
+
}
|
|
4256
|
+
function fileEntry(path18, runtime, blob, functions = [], lines = []) {
|
|
4257
|
+
const file = { path: path18, runtime };
|
|
4258
|
+
if (blob && (functions.length > 0 || lines.length > 0)) file.blob = blob;
|
|
4259
|
+
if (functions.length > 0) file.functions = functions;
|
|
4260
|
+
if (lines.length > 0) file.lines = lines;
|
|
4261
|
+
return file;
|
|
4262
|
+
}
|
|
4263
|
+
function captureOf(files, incomplete) {
|
|
4264
|
+
return incomplete ? { v: 1, files, incomplete: true } : { v: 1, files };
|
|
4265
|
+
}
|
|
4266
|
+
function compareFiles(a, b) {
|
|
4267
|
+
if (a.path !== b.path) return a.path < b.path ? -1 : 1;
|
|
4268
|
+
if (a.runtime !== b.runtime) return a.runtime < b.runtime ? -1 : 1;
|
|
3721
4269
|
return 0;
|
|
3722
4270
|
}
|
|
3723
4271
|
function sanitizeCoverage(raw) {
|
|
@@ -3725,11 +4273,11 @@ function sanitizeCoverage(raw) {
|
|
|
3725
4273
|
const files = [];
|
|
3726
4274
|
for (const item of raw.files) {
|
|
3727
4275
|
if (!isRecord(item) || !isRuntime(item.runtime)) continue;
|
|
3728
|
-
const
|
|
3729
|
-
if (
|
|
4276
|
+
const path18 = normalizeCoveragePath(item.path);
|
|
4277
|
+
if (path18 === null) continue;
|
|
3730
4278
|
const blob = typeof item.blob === "string" && BLOB_ID.test(item.blob) ? item.blob : void 0;
|
|
3731
4279
|
files.push(
|
|
3732
|
-
fileEntry(
|
|
4280
|
+
fileEntry(path18, item.runtime, blob, normalizeFunctionRanges(item.functions), normalizeLineRanges(item.lines))
|
|
3733
4281
|
);
|
|
3734
4282
|
}
|
|
3735
4283
|
const merged = mergeCoverage(captureOf(files, raw.incomplete === true));
|
|
@@ -4009,351 +4557,75 @@ function normalizeSourcePath(source) {
|
|
|
4009
4557
|
s = decodeURIComponent(s);
|
|
4010
4558
|
} catch {
|
|
4011
4559
|
}
|
|
4012
|
-
s = s.replace(/\\/g, "/");
|
|
4013
|
-
if (/^\/[A-Za-z]:\//.test(s)) s = s.slice(1);
|
|
4014
|
-
s = s.replace(/(^|\/)\.\//g, "$1");
|
|
4015
|
-
return s;
|
|
4016
|
-
}
|
|
4017
|
-
|
|
4018
|
-
// ../core/src/code-reach.ts
|
|
4019
|
-
var MAX_CODE_REACH_FILES = 2e3;
|
|
4020
|
-
function scriptText(source) {
|
|
4021
|
-
return { length: source.length, starts: Uint32Array.from(lineStarts(source)) };
|
|
4022
|
-
}
|
|
4023
|
-
var textOf = (entry, text) => text ?? (entry.source === void 0 ? void 0 : scriptText(entry.source));
|
|
4024
|
-
function executedFunctionRanges(entry, length, all = false) {
|
|
4025
|
-
length ??= entry.source?.length ?? Number.POSITIVE_INFINITY;
|
|
4026
|
-
const out = [];
|
|
4027
|
-
entry.functions.forEach((fn, i) => {
|
|
4028
|
-
const whole = fn.ranges[0];
|
|
4029
|
-
if (!whole || !all && whole.count <= 0) return;
|
|
4030
|
-
const topLevel = whole.startOffset === 0 && (whole.endOffset >= length || i === 0 && fn.functionName === "");
|
|
4031
|
-
if (topLevel) return;
|
|
4032
|
-
out.push({ start: whole.startOffset, end: whole.endOffset });
|
|
4033
|
-
});
|
|
4034
|
-
return out;
|
|
4035
|
-
}
|
|
4036
|
-
function scriptRan(entry, text) {
|
|
4037
|
-
return executedFunctionRanges(entry, text?.length).length > 0;
|
|
4038
|
-
}
|
|
4039
|
-
function reachedFunctionLines(entry, map, text) {
|
|
4040
|
-
return functionLines(entry, map, text, false);
|
|
4041
|
-
}
|
|
4042
|
-
function knownFunctionLines(entry, map, text) {
|
|
4043
|
-
return functionLines(entry, map, text, true);
|
|
4044
|
-
}
|
|
4045
|
-
function functionLines(entry, map, text, all) {
|
|
4046
|
-
const out = /* @__PURE__ */ new Map();
|
|
4047
|
-
const read = textOf(entry, text);
|
|
4048
|
-
const ranges = executedFunctionRanges(entry, read?.length, all);
|
|
4049
|
-
if (!ranges.length || !read) return out;
|
|
4050
|
-
const add2 = (source, range) => {
|
|
4051
|
-
const list = out.get(source);
|
|
4052
|
-
if (list) list.push(range);
|
|
4053
|
-
else out.set(source, [range]);
|
|
4054
|
-
};
|
|
4055
|
-
const starts = read.starts;
|
|
4056
|
-
for (const range of ranges) {
|
|
4057
|
-
const start = offsetToPosition(starts, range.start);
|
|
4058
|
-
if (!map) {
|
|
4059
|
-
const last = offsetToPosition(starts, Math.max(range.start, range.end - 1));
|
|
4060
|
-
add2(-1, [start.line + 1, last.line + 1]);
|
|
4061
|
-
continue;
|
|
4062
|
-
}
|
|
4063
|
-
const at = map.originalAt(start.line, start.column);
|
|
4064
|
-
if (at === null) continue;
|
|
4065
|
-
const end = offsetToPosition(starts, range.end);
|
|
4066
|
-
const first = map.firstOriginalLineBetween(at.source, start, end);
|
|
4067
|
-
if (first === null) continue;
|
|
4068
|
-
const header = first === at.line || first === at.line + 1 ? at.line : first;
|
|
4069
|
-
const lines = map.originalLinesBetween(at.source, start, end, header);
|
|
4070
|
-
if (lines) add2(at.source, [header + 1, lines[1] + 1]);
|
|
4071
|
-
}
|
|
4072
|
-
return out;
|
|
4073
|
-
}
|
|
4074
|
-
function finalizeCodeReach(files) {
|
|
4075
|
-
const out = /* @__PURE__ */ new Set();
|
|
4076
|
-
for (const raw of files) {
|
|
4077
|
-
const file = normalizeCoveragePath(raw);
|
|
4078
|
-
if (file !== null) out.add(file);
|
|
4079
|
-
}
|
|
4080
|
-
return [...out].sort().slice(0, MAX_CODE_REACH_FILES);
|
|
4081
|
-
}
|
|
4082
|
-
|
|
4083
|
-
// ../core/src/mask.ts
|
|
4084
|
-
var DATA_URI_RE = /\bdata:[a-z0-9.+-]+\/[a-z0-9.+-]+;base64,[A-Za-z0-9+/=]+/gi;
|
|
4085
|
-
var JWT_RE = /\beyJ[\w-]{10,}\.[\w-]{5,}\.[\w-]{5,}\b/g;
|
|
4086
|
-
var LONG_HEX_RE2 = /\b[0-9a-f]{32,}\b/gi;
|
|
4087
|
-
function maskTokenLike(text) {
|
|
4088
|
-
return text.replace(DATA_URI_RE, "data:[masked]").replace(JWT_RE, "[masked-token]").replace(LONG_HEX_RE2, "[masked-hex]");
|
|
4089
|
-
}
|
|
4090
|
-
var CREDENTIAL_PARAM_RE = /token|code|secret|password|passwd|pwd|key|session|auth|sig|credential|assertion|nonce|otp|jwt/i;
|
|
4091
|
-
function maskUrlFragment(hash) {
|
|
4092
|
-
const body = hash.startsWith("#") ? hash.slice(1) : hash;
|
|
4093
|
-
const query = body.indexOf("?");
|
|
4094
|
-
const route = query >= 0 ? body.slice(0, query) : body.includes("=") ? "" : body;
|
|
4095
|
-
const params = query >= 0 ? body.slice(query + 1) : body.includes("=") ? body : null;
|
|
4096
|
-
const masked = params === null ? null : params.split("&").map((param) => {
|
|
4097
|
-
const eq = param.indexOf("=");
|
|
4098
|
-
if (eq < 0) return maskTokenLike(param);
|
|
4099
|
-
const name = param.slice(0, eq);
|
|
4100
|
-
const value = param.slice(eq + 1);
|
|
4101
|
-
return `${name}=${value && CREDENTIAL_PARAM_RE.test(name) ? "[masked]" : maskTokenLike(value)}`;
|
|
4102
|
-
}).join("&");
|
|
4103
|
-
const out = maskTokenLike(route) + (masked === null ? "" : query >= 0 ? `?${masked}` : masked);
|
|
4104
|
-
return hash.startsWith("#") ? `#${out}` : out;
|
|
4105
|
-
}
|
|
4106
|
-
var HIDDEN_VALUE = "<hidden>";
|
|
4107
|
-
var SECRET_AUTOCOMPLETE = [
|
|
4108
|
-
"current-password",
|
|
4109
|
-
"new-password",
|
|
4110
|
-
"one-time-code",
|
|
4111
|
-
"cc-number",
|
|
4112
|
-
"cc-csc"
|
|
4113
|
-
];
|
|
4114
|
-
var SECRET_WORDS = /* @__PURE__ */ new Set([
|
|
4115
|
-
"pass",
|
|
4116
|
-
"password",
|
|
4117
|
-
"passwd",
|
|
4118
|
-
"passphrase",
|
|
4119
|
-
"passcode",
|
|
4120
|
-
"pwd",
|
|
4121
|
-
"secret",
|
|
4122
|
-
"secrets",
|
|
4123
|
-
"token",
|
|
4124
|
-
"tokens",
|
|
4125
|
-
"jwt",
|
|
4126
|
-
"bearer",
|
|
4127
|
-
"sig",
|
|
4128
|
-
"signature",
|
|
4129
|
-
"key",
|
|
4130
|
-
"keys",
|
|
4131
|
-
"apikey",
|
|
4132
|
-
"auth",
|
|
4133
|
-
"oauth",
|
|
4134
|
-
"authorization",
|
|
4135
|
-
"session",
|
|
4136
|
-
"sessionid",
|
|
4137
|
-
"jsessionid",
|
|
4138
|
-
"sid",
|
|
4139
|
-
"cookie",
|
|
4140
|
-
"cookies",
|
|
4141
|
-
"credential",
|
|
4142
|
-
"credentials",
|
|
4143
|
-
"otp",
|
|
4144
|
-
"refresh",
|
|
4145
|
-
"csrf",
|
|
4146
|
-
"xsrf"
|
|
4147
|
-
]);
|
|
4148
|
-
var SECRET_NAMES = /* @__PURE__ */ new Set(["code", "pin"]);
|
|
4149
|
-
function nameWords(name) {
|
|
4150
|
-
return name.replace(/([a-z0-9])([A-Z])/g, "$1 $2").replace(/([A-Z]+)([A-Z][a-z])/g, "$1 $2").toLowerCase().split(/[^a-z0-9]+/).filter(Boolean);
|
|
4151
|
-
}
|
|
4152
|
-
function isSecretName(name) {
|
|
4153
|
-
let secret = namesRead.get(name);
|
|
4154
|
-
if (secret === void 0) {
|
|
4155
|
-
secret = SECRET_NAMES.has(name.trim().toLowerCase()) || nameWords(name).some((word) => SECRET_WORDS.has(word));
|
|
4156
|
-
if (namesRead.size >= 1024) namesRead.clear();
|
|
4157
|
-
namesRead.set(name, secret);
|
|
4158
|
-
}
|
|
4159
|
-
return secret;
|
|
4160
|
-
}
|
|
4161
|
-
var namesRead = /* @__PURE__ */ new Map();
|
|
4162
|
-
var SECRET_SHAPES = [
|
|
4163
|
-
/\beyJ[\w-]{5,}\.[\w-]{5,}\.[\w-]*/g,
|
|
4164
|
-
/\bBearer(?:\s+|%20)[\w.~+/-]{8,}=*/gi,
|
|
4165
|
-
/\b(?:sk|rk)_(?:live|test)_[A-Za-z0-9]{8,}/g,
|
|
4166
|
-
/\bwhsec_[A-Za-z0-9]{20,}/g,
|
|
4167
|
-
/\bsk-(?=[\w-]{0,200}\d)(?=[\w-]{0,200}[A-Z])[\w-]{32,}/g,
|
|
4168
|
-
/\bglpat-[\w-]{20,}/g,
|
|
4169
|
-
/\bnpm_[A-Za-z0-9]{36}\b/g,
|
|
4170
|
-
/\bSK[0-9a-f]{32}\b/g,
|
|
4171
|
-
/\bAKIA[0-9A-Z]{16}\b/g,
|
|
4172
|
-
/\bAIza[\w-]{30,}/g,
|
|
4173
|
-
/\bgh[opsu]_[A-Za-z0-9]{20,}/g,
|
|
4174
|
-
/\bgithub_pat_\w{20,}/g,
|
|
4175
|
-
/\bxox[abprs]-[\w-]{8,}/g
|
|
4176
|
-
];
|
|
4177
|
-
var WHOLE_SHAPES = SECRET_SHAPES.map(
|
|
4178
|
-
(re) => new RegExp(`^(?:${re.source})$`, re.flags.replace("g", ""))
|
|
4179
|
-
);
|
|
4180
|
-
var BASIC = /\bBasic\s+([A-Za-z0-9+/]{4,}={0,2})(?![\w+/=])/g;
|
|
4181
|
-
var PEM_BEGIN = /-----BEGIN ([A-Z0-9 ]{0,40}PRIVATE KEY)-----/g;
|
|
4182
|
-
var SET_COOKIE = /(\bSet-Cookie:[ \t]*[^=;\s]{1,256}=)([^;\r\n]{1,4096})/gi;
|
|
4183
|
-
var COOKIE_LINE = /((?<![\w-])Cookie:[ \t]*)([^\r\n]{1,16384})/gi;
|
|
4184
|
-
var JSON_FIELD = /("((?:[^"\\\r\n]|\\.){1,256})"\s*:\s*")((?:[^"\\\r\n]|\\.){1,4096})"/g;
|
|
4185
|
-
var TOKEN_RUN = /[A-Za-z0-9+/_-]{40,}={0,2}/g;
|
|
4186
|
-
var MIN_RANDOM = 40;
|
|
4187
|
-
var BASE64_DIGITS = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
|
|
4188
|
-
function base64OfText(run) {
|
|
4189
|
-
let bits = 0;
|
|
4190
|
-
let acc = 0;
|
|
4191
|
-
let bytes = 0;
|
|
4192
|
-
let printable = 0;
|
|
4193
|
-
for (const c of run.replace(/=+$/, "")) {
|
|
4194
|
-
const digit = BASE64_DIGITS.indexOf(c === "-" ? "+" : c === "_" ? "/" : c);
|
|
4195
|
-
if (digit < 0) return false;
|
|
4196
|
-
acc = (acc << 6 | digit) & 65535;
|
|
4197
|
-
bits += 6;
|
|
4198
|
-
if (bits < 8) continue;
|
|
4199
|
-
bits -= 8;
|
|
4200
|
-
const byte = acc >> bits & 255;
|
|
4201
|
-
bytes++;
|
|
4202
|
-
if (byte >= 32 && byte < 127 || byte === 9 || byte === 10 || byte === 13) printable++;
|
|
4203
|
-
}
|
|
4204
|
-
return bytes > 0 && printable * 10 >= bytes * 9;
|
|
4205
|
-
}
|
|
4206
|
-
function bitsPerChar(text) {
|
|
4207
|
-
const counts = /* @__PURE__ */ new Map();
|
|
4208
|
-
for (const c of text) counts.set(c, (counts.get(c) ?? 0) + 1);
|
|
4209
|
-
let bits = 0;
|
|
4210
|
-
for (const n of counts.values()) bits -= n / text.length * Math.log2(n / text.length);
|
|
4211
|
-
return bits;
|
|
4212
|
-
}
|
|
4213
|
-
function looksRandom(text) {
|
|
4214
|
-
if (text.length < MIN_RANDOM || !/[A-Z]/.test(text) || !/[a-z]/.test(text) || !/\d/.test(text)) return false;
|
|
4215
|
-
let letters = 0;
|
|
4216
|
-
for (const c of text) if (c >= "a" && c <= "z" || c >= "A" && c <= "Z") letters++;
|
|
4217
|
-
let wordy = 0;
|
|
4218
|
-
for (const run of text.matchAll(/[a-z]{6,}|[A-Z]{6,}/g)) wordy += run[0].length;
|
|
4219
|
-
return wordy * 5 <= letters && bitsPerChar(text) >= 4 && !base64OfText(text);
|
|
4220
|
-
}
|
|
4221
|
-
function isBasicCredential(encoded) {
|
|
4222
|
-
const text = encoded.replace(/=+$/, "");
|
|
4223
|
-
if (text.length % 4 === 1) return false;
|
|
4224
|
-
let decoded = "";
|
|
4225
|
-
let bits = 0;
|
|
4226
|
-
let acc = 0;
|
|
4227
|
-
for (const c of text) {
|
|
4228
|
-
const digit = BASE64_DIGITS.indexOf(c);
|
|
4229
|
-
if (digit < 0) return false;
|
|
4230
|
-
acc = (acc << 6 | digit) & 65535;
|
|
4231
|
-
bits += 6;
|
|
4232
|
-
if (bits < 8) continue;
|
|
4233
|
-
bits -= 8;
|
|
4234
|
-
decoded += String.fromCharCode(acc >> bits & 255);
|
|
4235
|
-
}
|
|
4236
|
-
return /^[\x20-\x7e]+:[\x20-\x7e]*$/.test(decoded);
|
|
4237
|
-
}
|
|
4238
|
-
function isSecretValue(value) {
|
|
4239
|
-
const text = value.trim();
|
|
4240
|
-
if (text.length < 10 || text.length > 4096) return false;
|
|
4241
|
-
if (WHOLE_SHAPES.some((re) => re.test(text))) return true;
|
|
4242
|
-
const basic = /^Basic\s+([A-Za-z0-9+/]{4,}={0,2})$/.exec(text);
|
|
4243
|
-
if (basic && isBasicCredential(basic[1])) return true;
|
|
4244
|
-
if (/^-----BEGIN [A-Z0-9 ]{0,40}PRIVATE KEY-----/.test(text)) return true;
|
|
4245
|
-
return /^[A-Za-z0-9+/_-]{40,}={0,2}$/.test(text) && looksRandom(text);
|
|
4246
|
-
}
|
|
4247
|
-
var URL_IN_TEXT = /\b[a-z][a-z0-9+.-]{0,31}:\/\/[^\s"'<>`]+/gi;
|
|
4248
|
-
var URL_PARTS = /^(?:([a-z][a-z0-9+.-]*:)?\/\/([^/?#]*))?([^?#]*)(\?[^#]*)?(#[\s\S]*)?$/i;
|
|
4249
|
-
function decodeSafe(text) {
|
|
4250
|
-
try {
|
|
4251
|
-
return decodeURIComponent(text);
|
|
4252
|
-
} catch {
|
|
4253
|
-
return text;
|
|
4254
|
-
}
|
|
4560
|
+
s = s.replace(/\\/g, "/");
|
|
4561
|
+
if (/^\/[A-Za-z]:\//.test(s)) s = s.slice(1);
|
|
4562
|
+
s = s.replace(/(^|\/)\.\//g, "$1");
|
|
4563
|
+
return s;
|
|
4255
4564
|
}
|
|
4256
|
-
|
|
4257
|
-
|
|
4258
|
-
|
|
4259
|
-
|
|
4260
|
-
};
|
|
4565
|
+
|
|
4566
|
+
// ../core/src/code-reach.ts
|
|
4567
|
+
var MAX_CODE_REACH_FILES = 2e3;
|
|
4568
|
+
function scriptText(source) {
|
|
4569
|
+
return { length: source.length, starts: Uint32Array.from(lineStarts(source)) };
|
|
4261
4570
|
}
|
|
4262
|
-
|
|
4263
|
-
|
|
4264
|
-
|
|
4265
|
-
|
|
4266
|
-
|
|
4267
|
-
const
|
|
4268
|
-
|
|
4269
|
-
const
|
|
4270
|
-
if (
|
|
4271
|
-
|
|
4272
|
-
})
|
|
4571
|
+
var textOf = (entry, text) => text ?? (entry.source === void 0 ? void 0 : scriptText(entry.source));
|
|
4572
|
+
function executedFunctionRanges(entry, length, all = false) {
|
|
4573
|
+
length ??= entry.source?.length ?? Number.POSITIVE_INFINITY;
|
|
4574
|
+
const out = [];
|
|
4575
|
+
entry.functions.forEach((fn, i) => {
|
|
4576
|
+
const whole = fn.ranges[0];
|
|
4577
|
+
if (!whole || !all && whole.count <= 0) return;
|
|
4578
|
+
const topLevel = whole.startOffset === 0 && (whole.endOffset >= length || i === 0 && fn.functionName === "");
|
|
4579
|
+
if (topLevel) return;
|
|
4580
|
+
out.push({ start: whole.startOffset, end: whole.endOffset });
|
|
4581
|
+
});
|
|
4582
|
+
return out;
|
|
4273
4583
|
}
|
|
4274
|
-
function
|
|
4275
|
-
|
|
4276
|
-
return path16.split("/").map((segment) => {
|
|
4277
|
-
const [own = "", ...params] = segment.split(";");
|
|
4278
|
-
const decoded = decodeSafe(own);
|
|
4279
|
-
const kept = decoded !== HIDDEN_VALUE && isSecretValue(decoded) ? hide() : own;
|
|
4280
|
-
return params.length ? `${kept};${hideParams(params.join(";"), count)}` : kept;
|
|
4281
|
-
}).join("/");
|
|
4584
|
+
function scriptRan(entry, text) {
|
|
4585
|
+
return executedFunctionRanges(entry, text?.length).length > 0;
|
|
4282
4586
|
}
|
|
4283
|
-
function
|
|
4284
|
-
|
|
4285
|
-
if (!parts) return url;
|
|
4286
|
-
const [, scheme = "", authority, path16 = "", query = "", fragment = ""] = parts;
|
|
4287
|
-
let out = "";
|
|
4288
|
-
if (authority !== void 0) {
|
|
4289
|
-
const at = authority.lastIndexOf("@");
|
|
4290
|
-
const colon = at >= 0 ? authority.slice(0, at).indexOf(":") : -1;
|
|
4291
|
-
out += colon >= 0 && colon < at - 1 ? `${scheme}//${authority.slice(0, colon + 1)}${hider(count)()}${authority.slice(at)}` : `${scheme}//${authority}`;
|
|
4292
|
-
}
|
|
4293
|
-
out += hidePath(path16, count);
|
|
4294
|
-
if (query) out += `?${hideParams(query.slice(1), count)}`;
|
|
4295
|
-
if (fragment) {
|
|
4296
|
-
const body = fragment.slice(1);
|
|
4297
|
-
const ask = body.indexOf("?");
|
|
4298
|
-
out += `#${ask >= 0 ? `${hidePath(body.slice(0, ask), count)}?${hideParams(body.slice(ask + 1), count)}` : body.includes("=") ? hideParams(body, count) : hidePath(body, count)}`;
|
|
4299
|
-
}
|
|
4300
|
-
return out;
|
|
4587
|
+
function reachedFunctionLines(entry, map, text) {
|
|
4588
|
+
return functionLines(entry, map, text, false);
|
|
4301
4589
|
}
|
|
4302
|
-
|
|
4303
|
-
|
|
4304
|
-
function hidePem(text, hide) {
|
|
4305
|
-
let out = "";
|
|
4306
|
-
let last = 0;
|
|
4307
|
-
let nextEnd = -2;
|
|
4308
|
-
PEM_BEGIN.lastIndex = 0;
|
|
4309
|
-
for (let begin = PEM_BEGIN.exec(text); begin; begin = PEM_BEGIN.exec(text)) {
|
|
4310
|
-
if (begin.index < last) continue;
|
|
4311
|
-
if (nextEnd !== -1 && nextEnd < begin.index) nextEnd = text.indexOf("-----END ", begin.index);
|
|
4312
|
-
const footer = nextEnd >= 0 ? /^-----END [A-Z0-9 ]{0,40}-----/.exec(text.slice(nextEnd, nextEnd + 60)) : null;
|
|
4313
|
-
const until = footer ? nextEnd + footer[0].length : begin.index + begin[0].length + (/^(?:\s*[A-Za-z0-9+/=]+)*/.exec(text.slice(begin.index + begin[0].length))?.[0].length ?? 0);
|
|
4314
|
-
out += `${text.slice(last, begin.index)}${hide()}`;
|
|
4315
|
-
last = until;
|
|
4316
|
-
PEM_BEGIN.lastIndex = until;
|
|
4317
|
-
}
|
|
4318
|
-
return out + text.slice(last);
|
|
4590
|
+
function knownFunctionLines(entry, map, text) {
|
|
4591
|
+
return functionLines(entry, map, text, true);
|
|
4319
4592
|
}
|
|
4320
|
-
function
|
|
4321
|
-
|
|
4322
|
-
const
|
|
4323
|
-
|
|
4324
|
-
|
|
4325
|
-
|
|
4326
|
-
|
|
4327
|
-
|
|
4328
|
-
|
|
4329
|
-
|
|
4330
|
-
|
|
4331
|
-
|
|
4332
|
-
const
|
|
4333
|
-
|
|
4334
|
-
|
|
4335
|
-
|
|
4336
|
-
|
|
4337
|
-
|
|
4338
|
-
|
|
4339
|
-
|
|
4340
|
-
|
|
4341
|
-
|
|
4342
|
-
|
|
4343
|
-
|
|
4344
|
-
|
|
4345
|
-
|
|
4346
|
-
|
|
4347
|
-
|
|
4593
|
+
function functionLines(entry, map, text, all) {
|
|
4594
|
+
const out = /* @__PURE__ */ new Map();
|
|
4595
|
+
const read = textOf(entry, text);
|
|
4596
|
+
const ranges = executedFunctionRanges(entry, read?.length, all);
|
|
4597
|
+
if (!ranges.length || !read) return out;
|
|
4598
|
+
const add2 = (source, range) => {
|
|
4599
|
+
const list = out.get(source);
|
|
4600
|
+
if (list) list.push(range);
|
|
4601
|
+
else out.set(source, [range]);
|
|
4602
|
+
};
|
|
4603
|
+
const starts = read.starts;
|
|
4604
|
+
for (const range of ranges) {
|
|
4605
|
+
const start = offsetToPosition(starts, range.start);
|
|
4606
|
+
if (!map) {
|
|
4607
|
+
const last = offsetToPosition(starts, Math.max(range.start, range.end - 1));
|
|
4608
|
+
add2(-1, [start.line + 1, last.line + 1]);
|
|
4609
|
+
continue;
|
|
4610
|
+
}
|
|
4611
|
+
const at = map.originalAt(start.line, start.column);
|
|
4612
|
+
if (at === null) continue;
|
|
4613
|
+
const end = offsetToPosition(starts, range.end);
|
|
4614
|
+
const first = map.firstOriginalLineBetween(at.source, start, end);
|
|
4615
|
+
if (first === null) continue;
|
|
4616
|
+
const header = first === at.line || first === at.line + 1 ? at.line : first;
|
|
4617
|
+
const lines = map.originalLinesBetween(at.source, start, end, header);
|
|
4618
|
+
if (lines) add2(at.source, [header + 1, lines[1] + 1]);
|
|
4619
|
+
}
|
|
4620
|
+
return out;
|
|
4348
4621
|
}
|
|
4349
|
-
function
|
|
4350
|
-
|
|
4351
|
-
|
|
4352
|
-
|
|
4353
|
-
|
|
4354
|
-
last = url.index + url[0].length;
|
|
4622
|
+
function finalizeCodeReach(files) {
|
|
4623
|
+
const out = /* @__PURE__ */ new Set();
|
|
4624
|
+
for (const raw of files) {
|
|
4625
|
+
const file = normalizeCoveragePath(raw);
|
|
4626
|
+
if (file !== null) out.add(file);
|
|
4355
4627
|
}
|
|
4356
|
-
return out
|
|
4628
|
+
return [...out].sort().slice(0, MAX_CODE_REACH_FILES);
|
|
4357
4629
|
}
|
|
4358
4630
|
|
|
4359
4631
|
// src/internal/capture/inspect-on-failure.ts
|
|
@@ -4442,9 +4714,9 @@ function unlessEnded(run, ended) {
|
|
|
4442
4714
|
if (!ended) return run;
|
|
4443
4715
|
return Promise.race([
|
|
4444
4716
|
run,
|
|
4445
|
-
new Promise((
|
|
4446
|
-
if (ended.aborted)
|
|
4447
|
-
else ended.addEventListener("abort", () =>
|
|
4717
|
+
new Promise((resolve6) => {
|
|
4718
|
+
if (ended.aborted) resolve6(void 0);
|
|
4719
|
+
else ended.addEventListener("abort", () => resolve6(void 0), { once: true });
|
|
4448
4720
|
})
|
|
4449
4721
|
]);
|
|
4450
4722
|
}
|
|
@@ -4590,6 +4862,7 @@ function applyPickToSnapshots(snapshots, pick) {
|
|
|
4590
4862
|
}
|
|
4591
4863
|
|
|
4592
4864
|
// src/internal/capture/pause-at.ts
|
|
4865
|
+
var fs6 = __toESM(require("fs"));
|
|
4593
4866
|
var path8 = __toESM(require("path"));
|
|
4594
4867
|
|
|
4595
4868
|
// ../core/src/editor-send.ts
|
|
@@ -4709,10 +4982,23 @@ function internalCall(owner, run) {
|
|
|
4709
4982
|
return wrap.call(owner, run, { internal: true, title: "Piwi capture" });
|
|
4710
4983
|
}
|
|
4711
4984
|
|
|
4985
|
+
// src/internal/support/playwright-version.ts
|
|
4986
|
+
function playwrightVersionAtLeast(version, minimum) {
|
|
4987
|
+
const have = version ? /^(\d+)\.(\d+)/.exec(version) : null;
|
|
4988
|
+
const want = /^(\d+)\.(\d+)/.exec(minimum);
|
|
4989
|
+
if (!have || !want) return false;
|
|
4990
|
+
const major = Number(have[1]);
|
|
4991
|
+
const wantMajor = Number(want[1]);
|
|
4992
|
+
return major > wantMajor || major === wantMajor && Number(have[2]) >= Number(want[2]);
|
|
4993
|
+
}
|
|
4994
|
+
|
|
4712
4995
|
// src/internal/capture/pause-at.ts
|
|
4996
|
+
function lineKey(line, windows = process.platform === "win32") {
|
|
4997
|
+
return windows ? line.toLowerCase() : line;
|
|
4998
|
+
}
|
|
4713
4999
|
var PauseSet = class {
|
|
4714
5000
|
constructor(lines) {
|
|
4715
|
-
this.lines = lines;
|
|
5001
|
+
this.lines = new Set([...lines].map((line) => lineKey(line)));
|
|
4716
5002
|
}
|
|
4717
5003
|
get size() {
|
|
4718
5004
|
return this.lines.size;
|
|
@@ -4721,18 +5007,27 @@ var PauseSet = class {
|
|
|
4721
5007
|
matches(callerLocation) {
|
|
4722
5008
|
if (!callerLocation) return false;
|
|
4723
5009
|
const col = callerLocation.lastIndexOf(":");
|
|
4724
|
-
return col > 0 && this.lines.has(callerLocation.slice(0, col));
|
|
5010
|
+
return col > 0 && this.lines.has(lineKey(callerLocation.slice(0, col)));
|
|
4725
5011
|
}
|
|
4726
5012
|
};
|
|
4727
5013
|
var NO_BREAKPOINTS = new PauseSet(/* @__PURE__ */ new Set());
|
|
4728
5014
|
function callSiteFile(file, cwd) {
|
|
4729
5015
|
const slashed = file.replace(/\\/g, "/");
|
|
4730
5016
|
const absolute = path8.isAbsolute(file) || path8.isAbsolute(slashed) || /^[A-Za-z]:\//.test(slashed);
|
|
4731
|
-
|
|
5017
|
+
const resolved = path8.resolve(cwd, absolute ? file : slashed);
|
|
5018
|
+
const real = realPath(resolved);
|
|
5019
|
+
let rel = real !== null && real !== resolved ? path8.relative(realPath(cwd) ?? cwd, real) : absolute ? path8.relative(cwd, file) : path8.normalize(slashed);
|
|
4732
5020
|
rel = rel.split(path8.sep).join("/").replace(/\\/g, "/");
|
|
4733
5021
|
if (rel.startsWith("./")) rel = rel.slice(2);
|
|
4734
5022
|
return rel;
|
|
4735
5023
|
}
|
|
5024
|
+
function realPath(file) {
|
|
5025
|
+
try {
|
|
5026
|
+
return fs6.realpathSync(file);
|
|
5027
|
+
} catch {
|
|
5028
|
+
return null;
|
|
5029
|
+
}
|
|
5030
|
+
}
|
|
4736
5031
|
function stopCallSite(location, cwd = process.cwd()) {
|
|
4737
5032
|
if (!location.line) return null;
|
|
4738
5033
|
return `${callSiteFile(location.file, cwd)}:${location.line}:${location.column ?? 0}`;
|
|
@@ -4793,10 +5088,20 @@ function callSitePlace(callSite) {
|
|
|
4793
5088
|
function placeLine(place, cwd = process.cwd()) {
|
|
4794
5089
|
return `${callSiteFile(place.file, cwd)}:${place.line}`;
|
|
4795
5090
|
}
|
|
4796
|
-
function pauseGateFromTestInfo(testInfo) {
|
|
5091
|
+
function pauseGateFromTestInfo(testInfo, headless) {
|
|
4797
5092
|
const use = testInfo.project?.use ?? {};
|
|
4798
5093
|
const debug = testInfo._configInternal?.configCLIOverrides?.debug;
|
|
4799
|
-
return {
|
|
5094
|
+
return {
|
|
5095
|
+
ci: process.env.CI,
|
|
5096
|
+
headless: headless !== void 0 ? headless : use.headless,
|
|
5097
|
+
pwdebug: process.env.PWDEBUG,
|
|
5098
|
+
debug
|
|
5099
|
+
};
|
|
5100
|
+
}
|
|
5101
|
+
function mayPause(gate) {
|
|
5102
|
+
if (process.env.PIWI_PAUSE_AT) return true;
|
|
5103
|
+
if (gate.pwdebug && !["0", "false", "console"].includes(gate.pwdebug)) return true;
|
|
5104
|
+
return typeof gate.debug === "string" && gate.debug.length > 0;
|
|
4800
5105
|
}
|
|
4801
5106
|
function pauseSkipReason(gate) {
|
|
4802
5107
|
if (isCi(gate.ci)) return "running under CI \u2014 breakpoints pause local runs only";
|
|
@@ -4819,7 +5124,7 @@ function atBreakpoint(state, callSite) {
|
|
|
4819
5124
|
}
|
|
4820
5125
|
function atStop(state, callSite) {
|
|
4821
5126
|
if (callSite) {
|
|
4822
|
-
const reason = state.step ? "step" : state.pause ? "pause" : state.runTo !== null && callSiteLine(callSite) === state.runTo ? "run-to" : atBreakpoint(state, callSite) ? "breakpoint" : null;
|
|
5127
|
+
const reason = state.step ? "step" : state.pause ? "pause" : state.runTo !== null && lineKey(callSiteLine(callSite)) === lineKey(state.runTo) ? "run-to" : atBreakpoint(state, callSite) ? "breakpoint" : null;
|
|
4823
5128
|
if (reason) {
|
|
4824
5129
|
stopShown(state);
|
|
4825
5130
|
return { show: reason };
|
|
@@ -4850,6 +5155,9 @@ function goOn(state, answer) {
|
|
|
4850
5155
|
return wantsStops(state) ? "next" : "resume";
|
|
4851
5156
|
}
|
|
4852
5157
|
var ENDED_REASON = "Ended from Piwi";
|
|
5158
|
+
function takesEndSignal(version) {
|
|
5159
|
+
return playwrightVersionAtLeast(version, "1.62");
|
|
5160
|
+
}
|
|
4853
5161
|
var OPTIONS_AT = {
|
|
4854
5162
|
fill: 1,
|
|
4855
5163
|
selectOption: 1,
|
|
@@ -4907,11 +5215,23 @@ function syncInspectorSwitch() {
|
|
|
4907
5215
|
if (closed && process.env[NO_INSPECTOR_ENV] === void 0) {
|
|
4908
5216
|
process.env[NO_INSPECTOR_ENV] = "1";
|
|
4909
5217
|
inspectorSwitch.set = true;
|
|
5218
|
+
clearSwitchAtStop();
|
|
4910
5219
|
} else if (!closed && inspectorSwitch.set) {
|
|
4911
|
-
|
|
4912
|
-
inspectorSwitch.set = false;
|
|
5220
|
+
clearSwitch();
|
|
4913
5221
|
}
|
|
4914
5222
|
}
|
|
5223
|
+
function clearSwitch() {
|
|
5224
|
+
if (inspectorSwitch.set && process.env[NO_INSPECTOR_ENV] === "1") delete process.env[NO_INSPECTOR_ENV];
|
|
5225
|
+
inspectorSwitch.set = false;
|
|
5226
|
+
}
|
|
5227
|
+
var clearsAtStop = false;
|
|
5228
|
+
function clearSwitchAtStop() {
|
|
5229
|
+
if (clearsAtStop) return;
|
|
5230
|
+
clearsAtStop = true;
|
|
5231
|
+
process.prependListener("message", (message) => {
|
|
5232
|
+
if (message?.method === "__stop__") clearSwitch();
|
|
5233
|
+
});
|
|
5234
|
+
}
|
|
4915
5235
|
function markPausesWrapped() {
|
|
4916
5236
|
inspectorSwitch.wrapped = true;
|
|
4917
5237
|
syncInspectorSwitch();
|
|
@@ -5019,9 +5339,9 @@ var BarIntents = class {
|
|
|
5019
5339
|
/** Hand the bar an intent; false while it runs one, or holds one already. */
|
|
5020
5340
|
offer(intent) {
|
|
5021
5341
|
if (this.busy || this.queued) return false;
|
|
5022
|
-
const
|
|
5342
|
+
const take2 = this.take;
|
|
5023
5343
|
this.take = null;
|
|
5024
|
-
if (
|
|
5344
|
+
if (take2) take2(intent);
|
|
5025
5345
|
else this.queued = intent;
|
|
5026
5346
|
return true;
|
|
5027
5347
|
}
|
|
@@ -5031,8 +5351,8 @@ var BarIntents = class {
|
|
|
5031
5351
|
const queued = this.queued;
|
|
5032
5352
|
this.queued = null;
|
|
5033
5353
|
if (queued) return Promise.resolve(queued);
|
|
5034
|
-
return new Promise((
|
|
5035
|
-
this.take =
|
|
5354
|
+
return new Promise((resolve6) => {
|
|
5355
|
+
this.take = resolve6;
|
|
5036
5356
|
});
|
|
5037
5357
|
}
|
|
5038
5358
|
/** The bar got its answer: it takes no intent until it waits again. */
|
|
@@ -5047,7 +5367,7 @@ async function within(run, ms) {
|
|
|
5047
5367
|
try {
|
|
5048
5368
|
return await Promise.race([
|
|
5049
5369
|
run,
|
|
5050
|
-
new Promise((
|
|
5370
|
+
new Promise((resolve6) => timer = setTimeout(() => resolve6(void 0), ms))
|
|
5051
5371
|
]);
|
|
5052
5372
|
} finally {
|
|
5053
5373
|
clearTimeout(timer);
|
|
@@ -5080,13 +5400,68 @@ async function viewHere(page, probe, onPick, onRecord, ended, intents, onIntent,
|
|
|
5080
5400
|
}
|
|
5081
5401
|
}
|
|
5082
5402
|
}
|
|
5403
|
+
var REDRAW_MS = 500;
|
|
5404
|
+
var stopHighlights = 0;
|
|
5405
|
+
var FRAME_HOP = " >> internal:control=enter-frame >> ";
|
|
5406
|
+
async function frameOf(locator) {
|
|
5407
|
+
const own = locator;
|
|
5408
|
+
if (!own._frame || typeof own._selector !== "string") return null;
|
|
5409
|
+
const at = own._selector.lastIndexOf(FRAME_HOP);
|
|
5410
|
+
if (at < 0) return { frame: own._frame, selector: own._selector };
|
|
5411
|
+
const [iframe] = await own._frame.locator(own._selector.slice(0, at)).first().elementHandles();
|
|
5412
|
+
if (!iframe) return null;
|
|
5413
|
+
try {
|
|
5414
|
+
const frame = await iframe.contentFrame();
|
|
5415
|
+
return frame ? { frame, selector: own._selector.slice(at + FRAME_HOP.length) } : null;
|
|
5416
|
+
} finally {
|
|
5417
|
+
await iframe.dispose().catch(() => void 0);
|
|
5418
|
+
}
|
|
5419
|
+
}
|
|
5420
|
+
function highlightAtStop(page, locator, what, label) {
|
|
5421
|
+
const id = `${what}-${++stopHighlights}`;
|
|
5422
|
+
const arg = { id, kind: what, label };
|
|
5423
|
+
const drawnIn = /* @__PURE__ */ new Set();
|
|
5424
|
+
let hidden2 = false;
|
|
5425
|
+
let timer;
|
|
5426
|
+
const draw = async () => {
|
|
5427
|
+
const target = await frameOf(locator);
|
|
5428
|
+
if (hidden2) return 0;
|
|
5429
|
+
if (!target) return locator.evaluateAll(showStopHighlight, arg);
|
|
5430
|
+
drawnIn.add(target.frame);
|
|
5431
|
+
return target.frame.locator(target.selector).evaluateAll(showStopHighlight, arg);
|
|
5432
|
+
};
|
|
5433
|
+
const again = () => {
|
|
5434
|
+
if (hidden2 || page.isClosed?.()) return;
|
|
5435
|
+
timer = setTimeout(
|
|
5436
|
+
() => void internalCall(page, draw).catch(() => 0).then(again),
|
|
5437
|
+
REDRAW_MS
|
|
5438
|
+
);
|
|
5439
|
+
timer.unref?.();
|
|
5440
|
+
};
|
|
5441
|
+
const first = internalCall(page, draw).catch(() => 0);
|
|
5442
|
+
void first.then(again);
|
|
5443
|
+
const removeIn = (frame) => internalCall(page, async () => frame.evaluate(removeStopHighlight, id)).catch(() => false);
|
|
5444
|
+
return {
|
|
5445
|
+
shown: first.then((boxes) => boxes > 0),
|
|
5446
|
+
hide: async () => {
|
|
5447
|
+
hidden2 = true;
|
|
5448
|
+
clearTimeout(timer);
|
|
5449
|
+
try {
|
|
5450
|
+
const frames = drawnIn.size ? [...drawnIn] : [page.mainFrame()];
|
|
5451
|
+
const removed = await within(Promise.all(frames.map(removeIn)), TAKE_DOWN_MS);
|
|
5452
|
+
return removed?.includes(true) ?? false;
|
|
5453
|
+
} catch {
|
|
5454
|
+
return false;
|
|
5455
|
+
}
|
|
5456
|
+
}
|
|
5457
|
+
};
|
|
5458
|
+
}
|
|
5083
5459
|
async function pauseHere(page, point, probe, onPick, onRecord, ended, intents, onIntent) {
|
|
5084
|
-
let
|
|
5460
|
+
let highlight = null;
|
|
5085
5461
|
try {
|
|
5086
|
-
|
|
5087
|
-
|
|
5088
|
-
|
|
5089
|
-
shown2 = await within(highlighted, HIGHLIGHT_MS) ?? null;
|
|
5462
|
+
if (point.target) {
|
|
5463
|
+
highlight = point.target.highlight();
|
|
5464
|
+
await within(highlight.shown, HIGHLIGHT_MS);
|
|
5090
5465
|
}
|
|
5091
5466
|
const arg = {
|
|
5092
5467
|
place: pausePlace(point.location),
|
|
@@ -5145,17 +5520,10 @@ async function pauseHere(page, point, probe, onPick, onRecord, ended, intents, o
|
|
|
5145
5520
|
} catch {
|
|
5146
5521
|
return "resume";
|
|
5147
5522
|
} finally {
|
|
5148
|
-
|
|
5149
|
-
|
|
5150
|
-
|
|
5151
|
-
|
|
5152
|
-
const hide = page.hideHighlight;
|
|
5153
|
-
if (typeof dispose === "function") await dispose.call(shown2);
|
|
5154
|
-
else if (typeof hide === "function") await hide.call(page);
|
|
5155
|
-
} catch {
|
|
5156
|
-
}
|
|
5157
|
-
};
|
|
5158
|
-
await within(takeDown(), TAKE_DOWN_MS);
|
|
5523
|
+
await Promise.all([
|
|
5524
|
+
ended?.aborted ? within(page.evaluate(takePauseState), TAKE_DOWN_MS).catch(() => void 0) : void 0,
|
|
5525
|
+
highlight?.hide()
|
|
5526
|
+
]);
|
|
5159
5527
|
}
|
|
5160
5528
|
}
|
|
5161
5529
|
|
|
@@ -5201,6 +5569,12 @@ function locatorOn(page, source) {
|
|
|
5201
5569
|
}
|
|
5202
5570
|
}
|
|
5203
5571
|
|
|
5572
|
+
// src/internal/capture/aria-ref.ts
|
|
5573
|
+
function locatorOfRef(page, ref) {
|
|
5574
|
+
const getByRef = page.getByRef;
|
|
5575
|
+
return typeof getByRef === "function" ? getByRef.call(page, ref) : page.locator(`aria-ref=${ref}`);
|
|
5576
|
+
}
|
|
5577
|
+
|
|
5204
5578
|
// src/internal/capture/pick-at.ts
|
|
5205
5579
|
var LINE = /^(\s*)- ([a-z][\w-]*)(?: "(?:[^"\\]|\\.)*")?((?: \[[^\]\n]*\])*)(?::.*)?$/;
|
|
5206
5580
|
var REF = /\[ref=(e\d+)\]/;
|
|
@@ -5240,18 +5614,16 @@ var PIWI_OVERLAY = '[id^="__piwi"], [id^="piwi-"][id$="-host"]';
|
|
|
5240
5614
|
var MAX_UNDER = 24;
|
|
5241
5615
|
var MAX_CHECKED = 8;
|
|
5242
5616
|
var NORMALIZED_SCORE = 60;
|
|
5243
|
-
var
|
|
5617
|
+
var PICK_AT_SINCE = "1.60";
|
|
5618
|
+
var PICK_AT_NEEDS = "Pick on the image needs Playwright 1.60 or later (the boxes of page.ariaSnapshot): update @playwright/test.";
|
|
5244
5619
|
var NO_ELEMENT = "No element there: pick an element of the page, not its background.";
|
|
5245
5620
|
var IN_IFRAME = "That point is in an iframe, whose elements Pick on the image does not reach: use Pick in the test's window.";
|
|
5246
|
-
function canPickAt(page) {
|
|
5247
|
-
return typeof page.getByRef === "function";
|
|
5248
|
-
}
|
|
5249
5621
|
var hitIn = ({ x, y, overlay, elements }) => {
|
|
5250
5622
|
let scope = globalThis.document;
|
|
5251
5623
|
let hit = null;
|
|
5252
5624
|
for (let depth = 0; scope && depth < 32; depth++) {
|
|
5253
|
-
const
|
|
5254
|
-
const top =
|
|
5625
|
+
const within4 = scope;
|
|
5626
|
+
const top = within4.elementsFromPoint(x, y).find((el) => el.getRootNode?.() === within4 && !el.closest?.(overlay));
|
|
5255
5627
|
if (!top) break;
|
|
5256
5628
|
hit = top;
|
|
5257
5629
|
scope = top.shadowRoot ?? null;
|
|
@@ -5265,14 +5637,12 @@ var hitIn = ({ x, y, overlay, elements }) => {
|
|
|
5265
5637
|
return -1;
|
|
5266
5638
|
};
|
|
5267
5639
|
async function pickAtPoint(page, probe, x, y) {
|
|
5268
|
-
if (!canPickAt(page)) return { refusal: PICK_AT_NEEDS };
|
|
5269
5640
|
const snapshot = await page.ariaSnapshot({ mode: "ai", boxes: true, timeout: 5e3 });
|
|
5270
5641
|
if (!/\[box=/.test(snapshot)) return { refusal: PICK_AT_NEEDS };
|
|
5271
5642
|
const under = boxesAt(snapshotBoxes(snapshot), x, y).slice(0, MAX_UNDER);
|
|
5272
|
-
const byRef2 = (ref) => page.getByRef(ref);
|
|
5273
5643
|
const handles = await Promise.all(
|
|
5274
5644
|
under.map(
|
|
5275
|
-
(box) =>
|
|
5645
|
+
(box) => locatorOfRef(page, box.ref).elementHandle({ timeout: 2e3 }).catch(() => null)
|
|
5276
5646
|
)
|
|
5277
5647
|
);
|
|
5278
5648
|
try {
|
|
@@ -5289,7 +5659,7 @@ async function pickAtPoint(page, probe, x, y) {
|
|
|
5289
5659
|
(el, overlay) => !!el.closest(overlay),
|
|
5290
5660
|
PIWI_OVERLAY
|
|
5291
5661
|
);
|
|
5292
|
-
if (!piwi) return await rank(page, probe,
|
|
5662
|
+
if (!piwi) return await rank(page, probe, locatorOfRef(page, box.ref), handle);
|
|
5293
5663
|
}
|
|
5294
5664
|
return { refusal: NO_ELEMENT };
|
|
5295
5665
|
} finally {
|
|
@@ -5655,10 +6025,10 @@ var FrameAcks = class {
|
|
|
5655
6025
|
const now = this.now();
|
|
5656
6026
|
this.due = Math.min(Math.max(now, this.due + this.intervalMs), now + this.intervalMs);
|
|
5657
6027
|
if (this.due <= now) return void 0;
|
|
5658
|
-
return new Promise((
|
|
6028
|
+
return new Promise((resolve6) => {
|
|
5659
6029
|
const timer = setTimeout(() => this.release(), this.due - now);
|
|
5660
6030
|
timer.unref?.();
|
|
5661
|
-
this.waiting = { timer, resolve:
|
|
6031
|
+
this.waiting = { timer, resolve: resolve6 };
|
|
5662
6032
|
});
|
|
5663
6033
|
}
|
|
5664
6034
|
/** Whether a frame waits for its acknowledgment. */
|
|
@@ -5682,6 +6052,7 @@ var FrameAcks = class {
|
|
|
5682
6052
|
// ../core/src/run-channel.ts
|
|
5683
6053
|
var RUN_CHANNEL_ENV = "PIWI_RUN_CHANNEL";
|
|
5684
6054
|
var RUN_REF_ENV = "PIWI_RUN_REF";
|
|
6055
|
+
var TERMINAL_REF_PREFIX = "terminal-";
|
|
5685
6056
|
var RUN_HOLDS = ["failure"];
|
|
5686
6057
|
var RUN_INTENTS = ["pick", "record", "assert", "assert-region"];
|
|
5687
6058
|
var RUN_TAKES = [
|
|
@@ -5822,65 +6193,394 @@ function parseHubToRun(line) {
|
|
|
5822
6193
|
const act = readAct(value);
|
|
5823
6194
|
return act ? { type: "request", request, what, stop, ...act } : null;
|
|
5824
6195
|
}
|
|
5825
|
-
if (what === "ref-locator") {
|
|
5826
|
-
return isSnapshotRef(value.ref) ? { type: "request", request, what, stop, ref: value.ref } : null;
|
|
6196
|
+
if (what === "ref-locator") {
|
|
6197
|
+
return isSnapshotRef(value.ref) ? { type: "request", request, what, stop, ref: value.ref } : null;
|
|
6198
|
+
}
|
|
6199
|
+
if (what === "bind") {
|
|
6200
|
+
return typeof value.on === "boolean" ? { type: "request", request, what, stop, on: value.on } : null;
|
|
6201
|
+
}
|
|
6202
|
+
const { locator } = value;
|
|
6203
|
+
return locator === null || isLocatorText(locator) ? { type: "request", request, what: "locator", stop, locator } : null;
|
|
6204
|
+
}
|
|
6205
|
+
case "events":
|
|
6206
|
+
return typeof value.on === "boolean" ? { type: "events", on: value.on } : null;
|
|
6207
|
+
case "share":
|
|
6208
|
+
return typeof value.on === "boolean" ? { type: "share", on: value.on } : null;
|
|
6209
|
+
default:
|
|
6210
|
+
return typeof value.type === "string" && value.type ? unknown(value.type) : null;
|
|
6211
|
+
}
|
|
6212
|
+
}
|
|
6213
|
+
function readAct(value) {
|
|
6214
|
+
if (!isObject3(value) || !isSnapshotRef(value.ref)) return null;
|
|
6215
|
+
const action = value.action;
|
|
6216
|
+
if (typeof action !== "string" || !RUN_ACT_ACTIONS.includes(action)) return null;
|
|
6217
|
+
const given = value.value;
|
|
6218
|
+
switch (action) {
|
|
6219
|
+
case "fill":
|
|
6220
|
+
return typeof given === "string" && given.length <= MAX_ACT_VALUE ? { ref: value.ref, action: "fill", value: given } : null;
|
|
6221
|
+
case "press":
|
|
6222
|
+
return isViewKeyCombo(given) ? { ref: value.ref, action: "press", value: given } : null;
|
|
6223
|
+
case "select":
|
|
6224
|
+
return typeof given === "string" && given.length <= MAX_ACT_VALUE && !/[\r\n\0]/.test(given) ? { ref: value.ref, action: "select", value: given } : null;
|
|
6225
|
+
default:
|
|
6226
|
+
return given === null || given === void 0 ? { ref: value.ref, action, value: null } : null;
|
|
6227
|
+
}
|
|
6228
|
+
}
|
|
6229
|
+
|
|
6230
|
+
// src/internal/capture/live-mask.ts
|
|
6231
|
+
var import_node_module2 = require("module");
|
|
6232
|
+
var MASKED_SPACING_MS = 500;
|
|
6233
|
+
var FRESH_MS = 1e3;
|
|
6234
|
+
var READS_KEPT = 4;
|
|
6235
|
+
var MAX_FRAMES = 32;
|
|
6236
|
+
var PAD = 2;
|
|
6237
|
+
var SAME_WITHIN = 1;
|
|
6238
|
+
var FRAME_READ_MS = 1e3;
|
|
6239
|
+
var MAX_FAILED_READS = 3;
|
|
6240
|
+
var MASK_RGB = [96, 96, 96];
|
|
6241
|
+
var TOO_MANY_FRAMES = `This page has more than ${MAX_FRAMES} frames: the live view cannot check them all for secret fields, so no image of it goes.`;
|
|
6242
|
+
var NO_CODEC = "A secret field shows on this page, and this Playwright gives the reporter nothing to mask it with: no image goes while it shows.";
|
|
6243
|
+
var UNREAD = "The live view cannot read where the secret fields of this page are, so no image of it goes until it can.";
|
|
6244
|
+
var ZOOMED = "This page is zoomed in: no image of it goes until it is not, its secret fields shown larger than read.";
|
|
6245
|
+
var UNREADABLE_MASKS = "The project masks elements this reporter cannot read: no image goes. Update @piwitests/reporter.";
|
|
6246
|
+
var codec;
|
|
6247
|
+
function jpegCodec() {
|
|
6248
|
+
if (codec !== void 0) return codec;
|
|
6249
|
+
codec = null;
|
|
6250
|
+
try {
|
|
6251
|
+
const playwright = resolvePlaywrightPackageJson();
|
|
6252
|
+
const bundle = playwright ? (0, import_node_module2.createRequire)(playwright)("playwright-core/lib/utilsBundle") : null;
|
|
6253
|
+
const found = bundle?.jpegjs;
|
|
6254
|
+
if (typeof found?.decode === "function" && typeof found.encode === "function") codec = found;
|
|
6255
|
+
} catch {
|
|
6256
|
+
}
|
|
6257
|
+
return codec;
|
|
6258
|
+
}
|
|
6259
|
+
function maskedIn(frame, masks) {
|
|
6260
|
+
let locator = frame.locator(SECRET_FIELD_SELECTOR);
|
|
6261
|
+
for (const source of masks) {
|
|
6262
|
+
const own = locatorOn(frame, source);
|
|
6263
|
+
if (!own) throw new Error(`The locator ${source} cannot be built.`);
|
|
6264
|
+
locator = locator.or(own);
|
|
6265
|
+
}
|
|
6266
|
+
return locator;
|
|
6267
|
+
}
|
|
6268
|
+
var unbuilt = (source) => `The project masks ${source}, which this Playwright cannot find elements by: no image of the page goes.`;
|
|
6269
|
+
var near = (a, b) => Math.abs(a - b) <= SAME_WITHIN;
|
|
6270
|
+
function readFrameRects(page, frame, masks) {
|
|
6271
|
+
return internalCall(
|
|
6272
|
+
page,
|
|
6273
|
+
() => maskedIn(frame, masks).evaluateAll((elements) => {
|
|
6274
|
+
const view3 = globalThis;
|
|
6275
|
+
const visual = view3.visualViewport;
|
|
6276
|
+
const layout = view3.document.scrollingElement;
|
|
6277
|
+
const zoomed = !!visual && (Math.abs(visual.offsetLeft) > 0.5 || Math.abs(visual.offsetTop) > 0.5 || (layout ? visual.width < layout.clientWidth - 1 || visual.height < layout.clientHeight - 1 : visual.scale > 1.001));
|
|
6278
|
+
return {
|
|
6279
|
+
rects: elements.map((element) => {
|
|
6280
|
+
const rect = element.getBoundingClientRect();
|
|
6281
|
+
return [rect.x, rect.y, rect.width, rect.height];
|
|
6282
|
+
}).filter(([, , width, height]) => width > 0 && height > 0),
|
|
6283
|
+
width: view3.innerWidth,
|
|
6284
|
+
height: view3.innerHeight,
|
|
6285
|
+
zoomed
|
|
6286
|
+
};
|
|
6287
|
+
})
|
|
6288
|
+
);
|
|
6289
|
+
}
|
|
6290
|
+
async function childOrigin(page, frame, inner) {
|
|
6291
|
+
return internalCall(page, async () => {
|
|
6292
|
+
const element = await frame.frameElement();
|
|
6293
|
+
try {
|
|
6294
|
+
const outer = await element.boundingBox();
|
|
6295
|
+
if (!outer) return null;
|
|
6296
|
+
const own = await element.evaluate((iframe) => {
|
|
6297
|
+
const el = iframe;
|
|
6298
|
+
const style = globalThis.getComputedStyle(iframe);
|
|
6299
|
+
const pad = (side) => parseFloat(side) || 0;
|
|
6300
|
+
return {
|
|
6301
|
+
left: el.clientLeft + pad(style.paddingLeft),
|
|
6302
|
+
top: el.clientTop + pad(style.paddingTop),
|
|
6303
|
+
width: el.clientWidth - pad(style.paddingLeft) - pad(style.paddingRight),
|
|
6304
|
+
height: el.clientHeight - pad(style.paddingTop) - pad(style.paddingBottom),
|
|
6305
|
+
offsetWidth: el.offsetWidth,
|
|
6306
|
+
offsetHeight: el.offsetHeight
|
|
6307
|
+
};
|
|
6308
|
+
});
|
|
6309
|
+
const drawnAsLaidOut = near(outer.width, own.offsetWidth) && near(outer.height, own.offsetHeight);
|
|
6310
|
+
const contentIsViewport = near(own.width, inner.width) && near(own.height, inner.height);
|
|
6311
|
+
if (!drawnAsLaidOut || !contentIsViewport) return { whole: outer };
|
|
6312
|
+
return { x: outer.x + own.left, y: outer.y + own.top };
|
|
6313
|
+
} finally {
|
|
6314
|
+
await element.dispose();
|
|
6315
|
+
}
|
|
6316
|
+
});
|
|
6317
|
+
}
|
|
6318
|
+
async function within2(run, ms) {
|
|
6319
|
+
let timer;
|
|
6320
|
+
try {
|
|
6321
|
+
return await Promise.race([
|
|
6322
|
+
run,
|
|
6323
|
+
new Promise((resolve6) => timer = setTimeout(() => resolve6(void 0), ms))
|
|
6324
|
+
]);
|
|
6325
|
+
} finally {
|
|
6326
|
+
clearTimeout(timer);
|
|
6327
|
+
}
|
|
6328
|
+
}
|
|
6329
|
+
async function wholeFrame(page, frame) {
|
|
6330
|
+
const box = await within2(
|
|
6331
|
+
internalCall(page, async () => {
|
|
6332
|
+
const element = await frame.frameElement();
|
|
6333
|
+
try {
|
|
6334
|
+
return await element.boundingBox();
|
|
6335
|
+
} finally {
|
|
6336
|
+
await element.dispose();
|
|
6337
|
+
}
|
|
6338
|
+
}),
|
|
6339
|
+
FRAME_READ_MS
|
|
6340
|
+
).catch(() => void 0);
|
|
6341
|
+
if (box === void 0) return null;
|
|
6342
|
+
return { rects: box ? [[box.x, box.y, box.width, box.height]] : [], width: 0, height: 0 };
|
|
6343
|
+
}
|
|
6344
|
+
async function readFrame(page, frame, masks) {
|
|
6345
|
+
try {
|
|
6346
|
+
const read = await within2(readFrameRects(page, frame, masks), FRAME_READ_MS);
|
|
6347
|
+
if (!read) return frame === page.mainFrame() ? null : await wholeFrame(page, frame);
|
|
6348
|
+
if (!read.rects.length || frame === page.mainFrame()) return read;
|
|
6349
|
+
const origin = await childOrigin(page, frame, read);
|
|
6350
|
+
if (!origin) return { ...read, rects: [] };
|
|
6351
|
+
if ("whole" in origin) {
|
|
6352
|
+
const { x, y, width, height } = origin.whole;
|
|
6353
|
+
return { ...read, rects: [[x, y, width, height]] };
|
|
6354
|
+
}
|
|
6355
|
+
return {
|
|
6356
|
+
...read,
|
|
6357
|
+
rects: read.rects.map(([left, top, width, height]) => [origin.x + left, origin.y + top, width, height])
|
|
6358
|
+
};
|
|
6359
|
+
} catch {
|
|
6360
|
+
return frame.isDetached() ? { rects: [], width: 0, height: 0 } : null;
|
|
6361
|
+
}
|
|
6362
|
+
}
|
|
6363
|
+
async function readBoxes(page, masks) {
|
|
6364
|
+
if (!masks) return UNREADABLE_MASKS;
|
|
6365
|
+
const frames = page.frames();
|
|
6366
|
+
if (frames.length > MAX_FRAMES) return TOO_MANY_FRAMES;
|
|
6367
|
+
for (const source of masks) if (!locatorOn(page.mainFrame(), source)) return unbuilt(source);
|
|
6368
|
+
const main = page.mainFrame();
|
|
6369
|
+
const read = await Promise.all(frames.map((frame) => readFrame(page, frame, masks)));
|
|
6370
|
+
if (read.some((frame) => frame === null)) return null;
|
|
6371
|
+
const top = read[frames.indexOf(main)];
|
|
6372
|
+
if (!top) return null;
|
|
6373
|
+
if (top.zoomed) return ZOOMED;
|
|
6374
|
+
return {
|
|
6375
|
+
boxes: read.flatMap(
|
|
6376
|
+
(frame) => frame.rects.map(([x, y, width, height]) => ({ x, y, width, height }))
|
|
6377
|
+
),
|
|
6378
|
+
viewport: { width: top.width, height: top.height }
|
|
6379
|
+
};
|
|
6380
|
+
}
|
|
6381
|
+
function sameBoxes(a, b) {
|
|
6382
|
+
return a.length === b.length && a.every((box, i) => {
|
|
6383
|
+
const other = b[i];
|
|
6384
|
+
return Math.abs(box.x - other.x) <= SAME_WITHIN && Math.abs(box.y - other.y) <= SAME_WITHIN && Math.abs(box.width - other.width) <= SAME_WITHIN && Math.abs(box.height - other.height) <= SAME_WITHIN;
|
|
6385
|
+
});
|
|
6386
|
+
}
|
|
6387
|
+
function jpegSize(data) {
|
|
6388
|
+
for (let i = 2; i + 9 < data.length; ) {
|
|
6389
|
+
if (data[i] !== 255) return null;
|
|
6390
|
+
const marker = data[i + 1];
|
|
6391
|
+
if (marker >= 192 && marker <= 195) return { height: data.readUInt16BE(i + 5), width: data.readUInt16BE(i + 7) };
|
|
6392
|
+
i += 2 + data.readUInt16BE(i + 2);
|
|
6393
|
+
}
|
|
6394
|
+
return null;
|
|
6395
|
+
}
|
|
6396
|
+
function shrink(image, width, height) {
|
|
6397
|
+
const data = new Uint8Array(width * height * 4);
|
|
6398
|
+
const sx = image.width / width;
|
|
6399
|
+
const sy = image.height / height;
|
|
6400
|
+
for (let y = 0; y < height; y++) {
|
|
6401
|
+
const y0 = Math.floor(y * sy);
|
|
6402
|
+
const y1 = Math.max(y0 + 1, Math.min(image.height, Math.floor((y + 1) * sy)));
|
|
6403
|
+
for (let x = 0; x < width; x++) {
|
|
6404
|
+
const x0 = Math.floor(x * sx);
|
|
6405
|
+
const x1 = Math.max(x0 + 1, Math.min(image.width, Math.floor((x + 1) * sx)));
|
|
6406
|
+
let r = 0;
|
|
6407
|
+
let g = 0;
|
|
6408
|
+
let b = 0;
|
|
6409
|
+
for (let yy = y0; yy < y1; yy++) {
|
|
6410
|
+
for (let xx = x0; xx < x1; xx++) {
|
|
6411
|
+
const i = (yy * image.width + xx) * 4;
|
|
6412
|
+
r += image.data[i];
|
|
6413
|
+
g += image.data[i + 1];
|
|
6414
|
+
b += image.data[i + 2];
|
|
6415
|
+
}
|
|
5827
6416
|
}
|
|
5828
|
-
|
|
5829
|
-
|
|
6417
|
+
const n = (y1 - y0) * (x1 - x0);
|
|
6418
|
+
const o = (y * width + x) * 4;
|
|
6419
|
+
data[o] = r / n;
|
|
6420
|
+
data[o + 1] = g / n;
|
|
6421
|
+
data[o + 2] = b / n;
|
|
6422
|
+
data[o + 3] = 255;
|
|
6423
|
+
}
|
|
6424
|
+
}
|
|
6425
|
+
return { width, height, data };
|
|
6426
|
+
}
|
|
6427
|
+
function paintBoxes(jpeg, frame, boxes, size, quality) {
|
|
6428
|
+
const image = jpeg.decode(frame.data, { useTArray: true, formatAsRGBA: true });
|
|
6429
|
+
const sx = image.width / frame.width;
|
|
6430
|
+
const sy = image.height / frame.height;
|
|
6431
|
+
for (const box of boxes) {
|
|
6432
|
+
const x0 = Math.max(0, Math.floor((box.x - PAD) * sx));
|
|
6433
|
+
const y0 = Math.max(0, Math.floor((box.y - PAD) * sy));
|
|
6434
|
+
const x1 = Math.min(image.width, Math.ceil((box.x + box.width + PAD) * sx));
|
|
6435
|
+
const y1 = Math.min(image.height, Math.ceil((box.y + box.height + PAD) * sy));
|
|
6436
|
+
for (let y = y0; y < y1; y++) {
|
|
6437
|
+
for (let x = x0; x < x1; x++) {
|
|
6438
|
+
const i = (y * image.width + x) * 4;
|
|
6439
|
+
image.data[i] = MASK_RGB[0];
|
|
6440
|
+
image.data[i + 1] = MASK_RGB[1];
|
|
6441
|
+
image.data[i + 2] = MASK_RGB[2];
|
|
6442
|
+
image.data[i + 3] = 255;
|
|
5830
6443
|
}
|
|
5831
|
-
const { locator } = value;
|
|
5832
|
-
return locator === null || isLocatorText(locator) ? { type: "request", request, what: "locator", stop, locator } : null;
|
|
5833
6444
|
}
|
|
5834
|
-
case "events":
|
|
5835
|
-
return typeof value.on === "boolean" ? { type: "events", on: value.on } : null;
|
|
5836
|
-
case "share":
|
|
5837
|
-
return typeof value.on === "boolean" ? { type: "share", on: value.on } : null;
|
|
5838
|
-
default:
|
|
5839
|
-
return typeof value.type === "string" && value.type ? unknown(value.type) : null;
|
|
5840
6445
|
}
|
|
6446
|
+
const fits = image.width <= size.width && image.height <= size.height;
|
|
6447
|
+
return jpeg.encode(fits ? image : shrink(image, size.width, size.height), quality).data;
|
|
5841
6448
|
}
|
|
5842
|
-
function
|
|
5843
|
-
|
|
5844
|
-
|
|
5845
|
-
|
|
5846
|
-
|
|
5847
|
-
|
|
5848
|
-
|
|
5849
|
-
|
|
5850
|
-
|
|
5851
|
-
|
|
5852
|
-
|
|
5853
|
-
|
|
5854
|
-
|
|
5855
|
-
|
|
6449
|
+
async function measureFrame(page, frame) {
|
|
6450
|
+
try {
|
|
6451
|
+
const measured = await within2(
|
|
6452
|
+
internalCall(page, async () => {
|
|
6453
|
+
const fields = await frame.locator(SECRET_FIELD_SELECTOR).elementHandles();
|
|
6454
|
+
try {
|
|
6455
|
+
return await Promise.all(fields.map((field) => field.boundingBox()));
|
|
6456
|
+
} finally {
|
|
6457
|
+
await Promise.all(fields.map((field) => field.dispose().catch(() => void 0)));
|
|
6458
|
+
}
|
|
6459
|
+
}),
|
|
6460
|
+
FRAME_READ_MS
|
|
6461
|
+
);
|
|
6462
|
+
if (measured) return measured.filter((box) => !!box && box.width > 0 && box.height > 0);
|
|
6463
|
+
const whole = frame === page.mainFrame() ? null : await wholeFrame(page, frame);
|
|
6464
|
+
return whole && whole.rects.map(([x, y, width, height]) => ({ x, y, width, height }));
|
|
6465
|
+
} catch {
|
|
6466
|
+
return frame.isDetached() ? [] : null;
|
|
6467
|
+
}
|
|
6468
|
+
}
|
|
6469
|
+
async function secretBoxes(page) {
|
|
6470
|
+
const [read, measured] = await Promise.all([
|
|
6471
|
+
readBoxes(page, []),
|
|
6472
|
+
Promise.all(
|
|
6473
|
+
page.frames().slice(0, MAX_FRAMES).map((frame) => measureFrame(page, frame))
|
|
6474
|
+
)
|
|
6475
|
+
]);
|
|
6476
|
+
if (!read || typeof read === "string" || measured.some((boxes) => !boxes)) return null;
|
|
6477
|
+
return { ...read, boxes: [...read.boxes, ...measured.flat()] };
|
|
6478
|
+
}
|
|
6479
|
+
async function maskedImage(page, options) {
|
|
6480
|
+
const before = await secretBoxes(page);
|
|
6481
|
+
if (!before) return null;
|
|
6482
|
+
const shot = await internalCall(
|
|
6483
|
+
page,
|
|
6484
|
+
() => page.screenshot({
|
|
6485
|
+
type: "jpeg",
|
|
6486
|
+
quality: options.quality,
|
|
6487
|
+
timeout: options.timeout,
|
|
6488
|
+
...options.style ? { style: options.style } : {}
|
|
6489
|
+
})
|
|
6490
|
+
).catch(() => null);
|
|
6491
|
+
if (!shot) return null;
|
|
6492
|
+
const after = await secretBoxes(page);
|
|
6493
|
+
if (!after || !sameBoxes(before.boxes, after.boxes)) return null;
|
|
6494
|
+
const { width, height } = after.viewport;
|
|
6495
|
+
if (!near(width, before.viewport.width) || !near(height, before.viewport.height)) return null;
|
|
6496
|
+
if (!after.boxes.length) return shot;
|
|
6497
|
+
const jpeg = jpegCodec();
|
|
6498
|
+
const size = jpegSize(shot);
|
|
6499
|
+
if (!jpeg || !size) return null;
|
|
6500
|
+
try {
|
|
6501
|
+
return paintBoxes(jpeg, { data: shot, width, height, at: Date.now() }, after.boxes, size, options.quality);
|
|
6502
|
+
} catch {
|
|
6503
|
+
return null;
|
|
5856
6504
|
}
|
|
5857
6505
|
}
|
|
6506
|
+
function maskSecretFields(page, options) {
|
|
6507
|
+
const reads = [];
|
|
6508
|
+
let maskedAt = -Infinity;
|
|
6509
|
+
let failed2 = 0;
|
|
6510
|
+
const read = async () => {
|
|
6511
|
+
const began = Date.now();
|
|
6512
|
+
const found = await readBoxes(page, options.masks());
|
|
6513
|
+
if (typeof found === "string") return found;
|
|
6514
|
+
if (!found) return ++failed2 >= MAX_FAILED_READS ? UNREAD : null;
|
|
6515
|
+
failed2 = 0;
|
|
6516
|
+
const done = { began, ended: Date.now(), ...found };
|
|
6517
|
+
reads.push(done);
|
|
6518
|
+
if (reads.length > READS_KEPT) reads.shift();
|
|
6519
|
+
return done;
|
|
6520
|
+
};
|
|
6521
|
+
const showsViewportOf = (frame, read2) => near(frame.width, read2.viewport.width) && near(frame.height, read2.viewport.height);
|
|
6522
|
+
const shown2 = (frame, boxes, size) => {
|
|
6523
|
+
const own = jpegSize(frame.data);
|
|
6524
|
+
const fits = own === null || own.width <= size.width && own.height <= size.height;
|
|
6525
|
+
const jpeg = jpegCodec();
|
|
6526
|
+
if (!boxes.length && (fits || !jpeg)) return frame.data.length <= options.maxBytes ? frame : null;
|
|
6527
|
+
if (!jpeg) return NO_CODEC;
|
|
6528
|
+
let data;
|
|
6529
|
+
try {
|
|
6530
|
+
data = paintBoxes(jpeg, frame, boxes, size, options.quality);
|
|
6531
|
+
} catch {
|
|
6532
|
+
return null;
|
|
6533
|
+
}
|
|
6534
|
+
if (!boxes.length) return data.length <= options.maxBytes ? { ...frame, data } : null;
|
|
6535
|
+
if (data.length > options.maxBytes) return null;
|
|
6536
|
+
maskedAt = Date.now();
|
|
6537
|
+
return { ...frame, data };
|
|
6538
|
+
};
|
|
6539
|
+
return {
|
|
6540
|
+
get resting() {
|
|
6541
|
+
return Date.now() - maskedAt < MASKED_SPACING_MS;
|
|
6542
|
+
},
|
|
6543
|
+
async screen(frame) {
|
|
6544
|
+
const before = [...reads].reverse().find((r) => r.ended <= frame.at && r.ended >= frame.at - FRESH_MS) ?? null;
|
|
6545
|
+
const after = await read();
|
|
6546
|
+
if (typeof after === "string" || !after) return after;
|
|
6547
|
+
const size = jpegSize(frame.data) ?? frame;
|
|
6548
|
+
if (before && sameBoxes(before.boxes, after.boxes) && showsViewportOf(frame, before) && showsViewportOf(frame, after)) {
|
|
6549
|
+
return shown2(frame, after.boxes, size);
|
|
6550
|
+
}
|
|
6551
|
+
if (page.isClosed()) return null;
|
|
6552
|
+
const at = Date.now();
|
|
6553
|
+
const shot = await internalCall(
|
|
6554
|
+
page,
|
|
6555
|
+
() => page.screenshot({ type: "jpeg", quality: options.quality, scale: "css", caret: "initial", timeout: 5e3 })
|
|
6556
|
+
).catch(() => null);
|
|
6557
|
+
if (!shot) return null;
|
|
6558
|
+
const again = await read();
|
|
6559
|
+
if (typeof again === "string" || !again) return again;
|
|
6560
|
+
if (!sameBoxes(after.boxes, again.boxes)) return null;
|
|
6561
|
+
const { width, height } = again.viewport;
|
|
6562
|
+
if (!near(width, after.viewport.width) || !near(height, after.viewport.height)) return null;
|
|
6563
|
+
return shown2(
|
|
6564
|
+
{ data: shot, width, height, at },
|
|
6565
|
+
again.boxes,
|
|
6566
|
+
fitViewSize({ width, height }, size.width, size.height)
|
|
6567
|
+
);
|
|
6568
|
+
}
|
|
6569
|
+
};
|
|
6570
|
+
}
|
|
5858
6571
|
|
|
5859
6572
|
// src/internal/capture/agent-page.ts
|
|
5860
6573
|
var ACT_MS = 5e3;
|
|
5861
|
-
var SECRET_FIELDS =
|
|
5862
|
-
'input[type="password" i]',
|
|
5863
|
-
...SECRET_AUTOCOMPLETE.flatMap((token) => [
|
|
5864
|
-
`input[autocomplete~="${token}" i]`,
|
|
5865
|
-
`textarea[autocomplete~="${token}" i]`
|
|
5866
|
-
])
|
|
5867
|
-
].join(", ");
|
|
6574
|
+
var SECRET_FIELDS = SECRET_FIELD_SELECTOR;
|
|
5868
6575
|
var BAR_HIDDEN = "#__piwi-pause-bar { display: none !important; }";
|
|
5869
|
-
var AGENT_NEEDS = "An agent reads and acts on a stopped page with Playwright 1.64 or later (page.getByRef and the AI snapshot): update @playwright/test.";
|
|
5870
|
-
function canActByRef(page) {
|
|
5871
|
-
return typeof page.getByRef === "function";
|
|
5872
|
-
}
|
|
5873
|
-
function byRef(page, ref) {
|
|
5874
|
-
return page.getByRef(ref);
|
|
5875
|
-
}
|
|
5876
6576
|
function failed(prefix, error) {
|
|
5877
6577
|
const first = errorMessage(error).split("\n")[0].trim();
|
|
5878
6578
|
return hideSecretsInText(`${prefix}: ${first}`).slice(0, 300);
|
|
5879
6579
|
}
|
|
5880
|
-
var
|
|
6580
|
+
var MAX_FRAMES2 = 32;
|
|
5881
6581
|
async function fieldSecrets(page) {
|
|
5882
6582
|
const values = await Promise.all(
|
|
5883
|
-
page.frames().slice(0,
|
|
6583
|
+
page.frames().slice(0, MAX_FRAMES2).map(
|
|
5884
6584
|
(frame) => frame.locator(SECRET_FIELDS).evaluateAll((fields) => fields.map((field) => field.value)).catch(() => [])
|
|
5885
6585
|
)
|
|
5886
6586
|
);
|
|
@@ -5934,13 +6634,7 @@ async function readSnapshot(page, depth, image) {
|
|
|
5934
6634
|
const cut = hidden2.length > MAX_AGENT_SNAPSHOT;
|
|
5935
6635
|
let shot = null;
|
|
5936
6636
|
if (image) {
|
|
5937
|
-
const data = await page
|
|
5938
|
-
type: "jpeg",
|
|
5939
|
-
quality: 60,
|
|
5940
|
-
style: BAR_HIDDEN,
|
|
5941
|
-
mask: page.frames().slice(0, MAX_FRAMES).map((frame) => frame.locator(SECRET_FIELDS)),
|
|
5942
|
-
timeout: ACT_MS
|
|
5943
|
-
}).catch(() => null);
|
|
6637
|
+
const data = await maskedImage(page, { quality: 60, style: BAR_HIDDEN, timeout: ACT_MS });
|
|
5944
6638
|
const viewport = page.viewportSize();
|
|
5945
6639
|
if (data) shot = { mime: "image/jpeg", data: data.toString("base64"), ...viewport };
|
|
5946
6640
|
}
|
|
@@ -6009,14 +6703,13 @@ async function ranked(page, probe, locator, handle, testIdAttribute) {
|
|
|
6009
6703
|
}
|
|
6010
6704
|
var NO_REF = (ref) => `No element of the stopped page has the ref ${ref}: take the page's snapshot again, its refs may have changed.`;
|
|
6011
6705
|
async function handleOf(page, ref) {
|
|
6012
|
-
return
|
|
6706
|
+
return locatorOfRef(page, ref).elementHandle({ timeout: 2e3 }).catch(() => null);
|
|
6013
6707
|
}
|
|
6014
6708
|
async function refLocators(page, probe, ref, testIdAttribute) {
|
|
6015
|
-
if (!canActByRef(page)) return { ok: false, message: AGENT_NEEDS, element: null, locators: [] };
|
|
6016
6709
|
const handle = await handleOf(page, ref);
|
|
6017
6710
|
if (!handle) return { ok: false, message: NO_REF(ref), element: null, locators: [] };
|
|
6018
6711
|
try {
|
|
6019
|
-
const { element, locators } = await ranked(page, probe,
|
|
6712
|
+
const { element, locators } = await ranked(page, probe, locatorOfRef(page, ref), handle, testIdAttribute);
|
|
6020
6713
|
const alone = locators.filter((l) => l.unique).length;
|
|
6021
6714
|
return {
|
|
6022
6715
|
ok: true,
|
|
@@ -6060,12 +6753,11 @@ var PLAYED = {
|
|
|
6060
6753
|
select: "Selected an option of"
|
|
6061
6754
|
};
|
|
6062
6755
|
async function actOnRef(page, probe, act, testIdAttribute) {
|
|
6063
|
-
if (!canActByRef(page)) return { ok: false, message: AGENT_NEEDS, element: null, locators: [] };
|
|
6064
6756
|
const handle = await handleOf(page, act.ref);
|
|
6065
6757
|
if (!handle) return { ok: false, message: NO_REF(act.ref), element: null, locators: [] };
|
|
6066
6758
|
let before;
|
|
6067
6759
|
try {
|
|
6068
|
-
before = await ranked(page, probe,
|
|
6760
|
+
before = await ranked(page, probe, locatorOfRef(page, act.ref), handle, testIdAttribute);
|
|
6069
6761
|
} catch (error) {
|
|
6070
6762
|
return { ok: false, message: failed("The element could not be read", error), element: null, locators: [] };
|
|
6071
6763
|
} finally {
|
|
@@ -6073,7 +6765,7 @@ async function actOnRef(page, probe, act, testIdAttribute) {
|
|
|
6073
6765
|
}
|
|
6074
6766
|
try {
|
|
6075
6767
|
await page.evaluate(barLetsThrough, true).catch(() => void 0);
|
|
6076
|
-
await play(
|
|
6768
|
+
await play(locatorOfRef(page, act.ref), act.action, act.value);
|
|
6077
6769
|
} catch (error) {
|
|
6078
6770
|
return { ok: false, message: failed(`${PLAYED[act.action]} ${act.ref} failed`, error), ...before };
|
|
6079
6771
|
} finally {
|
|
@@ -6117,7 +6809,7 @@ async function bindBrowser(browser, name, workspaceDir, on) {
|
|
|
6117
6809
|
}
|
|
6118
6810
|
|
|
6119
6811
|
// src/internal/capture/run-events.ts
|
|
6120
|
-
var
|
|
6812
|
+
var path11 = __toESM(require("path"));
|
|
6121
6813
|
|
|
6122
6814
|
// src/internal/capture/run-channel.ts
|
|
6123
6815
|
var net = __toESM(require("net"));
|
|
@@ -6132,8 +6824,8 @@ function parseRecordChannel(value) {
|
|
|
6132
6824
|
const hash = text.lastIndexOf("#");
|
|
6133
6825
|
if (hash <= 0) return null;
|
|
6134
6826
|
const token = text.slice(hash + 1);
|
|
6135
|
-
const
|
|
6136
|
-
return /^[A-Za-z0-9_-]{16,}$/.test(token) && !
|
|
6827
|
+
const path18 = text.slice(0, hash);
|
|
6828
|
+
return /^[A-Za-z0-9_-]{16,}$/.test(token) && !path18.includes("\0") ? { path: path18, token } : null;
|
|
6137
6829
|
}
|
|
6138
6830
|
function isObject4(value) {
|
|
6139
6831
|
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
@@ -6177,6 +6869,16 @@ function lineReader(onLine, onOverflow) {
|
|
|
6177
6869
|
};
|
|
6178
6870
|
}
|
|
6179
6871
|
|
|
6872
|
+
// src/internal/config/editors.ts
|
|
6873
|
+
var fs7 = __toESM(require("fs"));
|
|
6874
|
+
var os2 = __toESM(require("os"));
|
|
6875
|
+
var path9 = __toESM(require("path"));
|
|
6876
|
+
var TEST_RUNNER_REPORTER_ENV = "PW_TEST_REPORTER";
|
|
6877
|
+
function startedByTestRunner(env) {
|
|
6878
|
+
const reporter = env[TEST_RUNNER_REPORTER_ENV]?.trim();
|
|
6879
|
+
return !!reporter && /[\\/]/.test(reporter);
|
|
6880
|
+
}
|
|
6881
|
+
|
|
6180
6882
|
// src/internal/capture/kept-responses.ts
|
|
6181
6883
|
var keeping = null;
|
|
6182
6884
|
var TEXT_TYPE = /^(?:text\/[\w.+-]+|application\/(?:[\w.+-]+\+)?(?:json|xml)|application\/(?:javascript|x-www-form-urlencoded|graphql))$/i;
|
|
@@ -6261,6 +6963,8 @@ var LIVE_FRAME_BYTE_RATE = 256 * 1024;
|
|
|
6261
6963
|
var MAX_LIVE_FRAME = 192 * 1024;
|
|
6262
6964
|
var MAX_LIVE_FRAME_BASE64 = 4 * Math.ceil(MAX_LIVE_FRAME / 3);
|
|
6263
6965
|
var MAX_LIVE_MESSAGE = MAX_LIVE_FRAME_BASE64 + 8 * 1024;
|
|
6966
|
+
var MAX_LIVE_CALL_LOG_LINES = 20;
|
|
6967
|
+
var LIVE_CALL_LOGS_PER_SECOND = 4;
|
|
6264
6968
|
var KEY = /^pdl_[A-Za-z0-9_-]{43}$/;
|
|
6265
6969
|
function isLiveViewKey(value) {
|
|
6266
6970
|
return typeof value === "string" && KEY.test(value);
|
|
@@ -6318,12 +7022,68 @@ function liveShareRefusal(status) {
|
|
|
6318
7022
|
return "Sharing stopped: the instance did not answer. Share the run again to try once more.";
|
|
6319
7023
|
}
|
|
6320
7024
|
}
|
|
7025
|
+
var MAX_LIVE_MASKS = 20;
|
|
7026
|
+
var MAX_LIVE_MASK_LENGTH = 200;
|
|
7027
|
+
var FRAME_CALLS = /* @__PURE__ */ new Set(["frameLocator", "contentFrame", "owner"]);
|
|
7028
|
+
var NARROWING_CALLS = /* @__PURE__ */ new Set(["filter", "and", "or", "visible", "first", "last", "nth"]);
|
|
7029
|
+
function liveMaskProblem(locator) {
|
|
7030
|
+
if (!locator.trim()) return "A masked element needs a locator.";
|
|
7031
|
+
if (locator.length > MAX_LIVE_MASK_LENGTH) return `A locator is ${MAX_LIVE_MASK_LENGTH} characters at most.`;
|
|
7032
|
+
const source = locator.trim();
|
|
7033
|
+
const chain = tryParseLocatorChain(source);
|
|
7034
|
+
if (!chain?.calls.length || NARROWING_CALLS.has(chain.calls[0].method)) {
|
|
7035
|
+
return `\`${source}\` is not a Playwright locator, such as getByLabel('IBAN').`;
|
|
7036
|
+
}
|
|
7037
|
+
if (chain.calls.some((call) => FRAME_CALLS.has(call.method))) {
|
|
7038
|
+
return `\`${source}\` enters a frame: masks are looked for in every frame of the page, so name the element alone, such as getByLabel('Card number'), or the iframe, such as locator('iframe#card').`;
|
|
7039
|
+
}
|
|
7040
|
+
return null;
|
|
7041
|
+
}
|
|
7042
|
+
function parseLiveMasks(value) {
|
|
7043
|
+
if (!Array.isArray(value) || value.length > MAX_LIVE_MASKS) return null;
|
|
7044
|
+
const locators = [];
|
|
7045
|
+
for (const locator of value) {
|
|
7046
|
+
if (typeof locator !== "string" || liveMaskProblem(locator)) return null;
|
|
7047
|
+
if (!locators.includes(locator.trim())) locators.push(locator.trim());
|
|
7048
|
+
}
|
|
7049
|
+
return locators;
|
|
7050
|
+
}
|
|
6321
7051
|
function isObject5(value) {
|
|
6322
7052
|
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
6323
7053
|
}
|
|
6324
7054
|
function isInt2(value, min, max) {
|
|
6325
7055
|
return typeof value === "number" && Number.isInteger(value) && value >= min && value <= max;
|
|
6326
7056
|
}
|
|
7057
|
+
var LONGEST_REPEAT = 12;
|
|
7058
|
+
function sameLines(lines, a, b, size) {
|
|
7059
|
+
for (let k = 0; k < size; k++) if (lines[a + k] !== lines[b + k]) return false;
|
|
7060
|
+
return true;
|
|
7061
|
+
}
|
|
7062
|
+
function compressCallLog(lines) {
|
|
7063
|
+
const out = [];
|
|
7064
|
+
let i = 0;
|
|
7065
|
+
while (i < lines.length) {
|
|
7066
|
+
let size = 1;
|
|
7067
|
+
let count = 1;
|
|
7068
|
+
for (let length = 1; length <= LONGEST_REPEAT && i + length * 2 <= lines.length; length++) {
|
|
7069
|
+
let times = 1;
|
|
7070
|
+
while (i + length * (times + 1) <= lines.length && sameLines(lines, i, i + length * times, length)) times++;
|
|
7071
|
+
if (times > 1 && times * length > count * size) {
|
|
7072
|
+
size = length;
|
|
7073
|
+
count = times;
|
|
7074
|
+
}
|
|
7075
|
+
}
|
|
7076
|
+
for (let k = 0; k < size; k++) {
|
|
7077
|
+
const line = lines[i + k];
|
|
7078
|
+
const indent = /^\s*/.exec(line)[0];
|
|
7079
|
+
if (count === 1) out.push(`${indent}- ${line.trim()}`);
|
|
7080
|
+
else if (k === 0) out.push(`${indent}${count} \xD7 ${line.trim()}`);
|
|
7081
|
+
else out.push(`${indent}${" ".repeat(`${count} \xD7 `.length - 2)}- ${line.trim()}`);
|
|
7082
|
+
}
|
|
7083
|
+
i += size * count;
|
|
7084
|
+
}
|
|
7085
|
+
return out;
|
|
7086
|
+
}
|
|
6327
7087
|
function parseInstanceToWorker(data) {
|
|
6328
7088
|
let value;
|
|
6329
7089
|
try {
|
|
@@ -6331,7 +7091,9 @@ function parseInstanceToWorker(data) {
|
|
|
6331
7091
|
} catch {
|
|
6332
7092
|
return null;
|
|
6333
7093
|
}
|
|
6334
|
-
if (!isObject5(value)
|
|
7094
|
+
if (!isObject5(value)) return null;
|
|
7095
|
+
if (value.type === "masks") return { type: "masks", locators: parseLiveMasks(value.locators) };
|
|
7096
|
+
if (value.type !== "viewers" || !isInt2(value.count, 0, 1e6)) return null;
|
|
6335
7097
|
return { type: "viewers", count: value.count };
|
|
6336
7098
|
}
|
|
6337
7099
|
|
|
@@ -6349,7 +7111,198 @@ function resolveShardInfo(config, env = process.env) {
|
|
|
6349
7111
|
return parseShardSpec(env[PIWI_SHARD_ENV]);
|
|
6350
7112
|
}
|
|
6351
7113
|
|
|
7114
|
+
// src/internal/capture/live-call-log.ts
|
|
7115
|
+
var path10 = __toESM(require("path"));
|
|
7116
|
+
var TARGET_EVERY_MS = 1e3;
|
|
7117
|
+
var KEPT_LINES = 1e3;
|
|
7118
|
+
var MAX_LINE2 = 600;
|
|
7119
|
+
var MAX_TITLE = 500;
|
|
7120
|
+
var MAX_HEARD = 200;
|
|
7121
|
+
function callStreamOf(context) {
|
|
7122
|
+
const dbg = context.debugger;
|
|
7123
|
+
return typeof dbg?._enable === "function" && typeof dbg.on === "function" && typeof dbg.off === "function" ? dbg : null;
|
|
7124
|
+
}
|
|
7125
|
+
var heard = /* @__PURE__ */ new Map();
|
|
7126
|
+
var tracked = /* @__PURE__ */ new WeakSet();
|
|
7127
|
+
function trackCalls(playwright) {
|
|
7128
|
+
const instrumentation = playwright?._instrumentation;
|
|
7129
|
+
if (!playwright || tracked.has(playwright) || typeof instrumentation?.addListener !== "function") return;
|
|
7130
|
+
tracked.add(playwright);
|
|
7131
|
+
instrumentation.addListener({
|
|
7132
|
+
onApiCallBegin: (zone, channel) => {
|
|
7133
|
+
if (channel?.method === "__waitInfo__") return;
|
|
7134
|
+
const selector = channel?.params?.selector;
|
|
7135
|
+
heard.set(zone, {
|
|
7136
|
+
zone,
|
|
7137
|
+
selector: typeof selector === "string" && selector ? selector : null,
|
|
7138
|
+
began: Date.now()
|
|
7139
|
+
});
|
|
7140
|
+
if (heard.size > MAX_HEARD) heard.delete(heard.keys().next().value);
|
|
7141
|
+
},
|
|
7142
|
+
onApiCallEnd: (zone) => {
|
|
7143
|
+
heard.delete(zone);
|
|
7144
|
+
}
|
|
7145
|
+
});
|
|
7146
|
+
}
|
|
7147
|
+
function heardAs(id) {
|
|
7148
|
+
for (const call of heard.values()) if (call.zone.callId === id) return call;
|
|
7149
|
+
return null;
|
|
7150
|
+
}
|
|
7151
|
+
function userFile(file) {
|
|
7152
|
+
if (typeof file !== "string" || !file || /[\\/]node_modules[\\/]/.test(file)) return null;
|
|
7153
|
+
const relative7 = path10.relative(process.cwd(), file).split(path10.sep).join("/");
|
|
7154
|
+
return relative7 && !relative7.startsWith("..") && !path10.isAbsolute(relative7) ? relative7 : null;
|
|
7155
|
+
}
|
|
7156
|
+
var followed = /* @__PURE__ */ new WeakMap();
|
|
7157
|
+
function take(state, update) {
|
|
7158
|
+
if (typeof update?.id !== "string" || typeof update.title !== "string") return;
|
|
7159
|
+
const status = update.status === "running" ? "running" : update.status === "error" ? "failed" : "passed";
|
|
7160
|
+
let call = state.calls.get(update.id);
|
|
7161
|
+
if (!call) {
|
|
7162
|
+
const heardOne = heardAs(update.id);
|
|
7163
|
+
call = {
|
|
7164
|
+
id: update.id,
|
|
7165
|
+
title: hideSecretsInText(hideTypedText(update.title)).slice(0, MAX_TITLE),
|
|
7166
|
+
file: userFile(update.location?.file),
|
|
7167
|
+
line: typeof update.location?.line === "number" ? update.location.line : null,
|
|
7168
|
+
status,
|
|
7169
|
+
lines: [],
|
|
7170
|
+
point: null,
|
|
7171
|
+
error: null,
|
|
7172
|
+
began: heardOne?.began ?? Date.now(),
|
|
7173
|
+
ended: null,
|
|
7174
|
+
order: ++state.told,
|
|
7175
|
+
selector: heardOne?.selector ?? null,
|
|
7176
|
+
secretField: heardOne?.selector ? isSecretLocator(heardOne.selector) : false
|
|
7177
|
+
};
|
|
7178
|
+
state.calls.set(update.id, call);
|
|
7179
|
+
}
|
|
7180
|
+
if (Array.isArray(update.newLogEntries)) {
|
|
7181
|
+
for (const entry of update.newLogEntries) {
|
|
7182
|
+
if (typeof entry !== "string") continue;
|
|
7183
|
+
call.secretField ||= namesSecretField(entry);
|
|
7184
|
+
call.lines.push(hideSecretsInText(hideSecretsInLog(entry, call.secretField)).slice(0, MAX_LINE2));
|
|
7185
|
+
}
|
|
7186
|
+
if (call.lines.length > KEPT_LINES) call.lines.splice(0, call.lines.length - KEPT_LINES);
|
|
7187
|
+
}
|
|
7188
|
+
const point = update.actionPoint;
|
|
7189
|
+
if (point && Number.isFinite(point.x) && Number.isFinite(point.y)) {
|
|
7190
|
+
call.point = { x: point.x, y: point.y };
|
|
7191
|
+
}
|
|
7192
|
+
if (typeof update.error === "string" && update.error) {
|
|
7193
|
+
const first = update.error.split("\n")[0];
|
|
7194
|
+
call.error = hideSecretsInText(hideSecretsInLog(first, call.secretField)).slice(0, MAX_LINE2);
|
|
7195
|
+
}
|
|
7196
|
+
call.status = status;
|
|
7197
|
+
if (status !== "running" && call.ended === null) call.ended = Date.now();
|
|
7198
|
+
}
|
|
7199
|
+
function shownCall(state, since) {
|
|
7200
|
+
let shown2 = null;
|
|
7201
|
+
for (const call of state.calls.values()) {
|
|
7202
|
+
if (call.ended !== null && call.ended < since) continue;
|
|
7203
|
+
if (!shown2) {
|
|
7204
|
+
shown2 = call;
|
|
7205
|
+
continue;
|
|
7206
|
+
}
|
|
7207
|
+
const running2 = call.status === "running";
|
|
7208
|
+
if (running2 !== (shown2.status === "running") ? running2 : call.order > shown2.order) shown2 = call;
|
|
7209
|
+
}
|
|
7210
|
+
return shown2;
|
|
7211
|
+
}
|
|
7212
|
+
function prune(state) {
|
|
7213
|
+
let last = null;
|
|
7214
|
+
for (const call of state.calls.values()) if (!last || call.order > last.order) last = call;
|
|
7215
|
+
for (const [id, call] of state.calls) if (call.status !== "running" && call !== last) state.calls.delete(id);
|
|
7216
|
+
}
|
|
7217
|
+
async function followContext(context) {
|
|
7218
|
+
const stream = callStreamOf(context);
|
|
7219
|
+
if (!stream) return null;
|
|
7220
|
+
const state = { calls: /* @__PURE__ */ new Map(), told: 0, views: /* @__PURE__ */ new Set() };
|
|
7221
|
+
const listener = (updates) => {
|
|
7222
|
+
if (!Array.isArray(updates)) return;
|
|
7223
|
+
for (const update of updates) take(state, update);
|
|
7224
|
+
prune(state);
|
|
7225
|
+
for (const view3 of state.views) view3();
|
|
7226
|
+
};
|
|
7227
|
+
stream.on("apicallsupdated", listener);
|
|
7228
|
+
try {
|
|
7229
|
+
await internalCall(context, () => stream._enable());
|
|
7230
|
+
} catch {
|
|
7231
|
+
stream.off("apicallsupdated", listener);
|
|
7232
|
+
return null;
|
|
7233
|
+
}
|
|
7234
|
+
return state;
|
|
7235
|
+
}
|
|
7236
|
+
async function followCallLog(page, since, onLog) {
|
|
7237
|
+
const context = page.context();
|
|
7238
|
+
let state = followed.get(context);
|
|
7239
|
+
if (!state) {
|
|
7240
|
+
state = followContext(context);
|
|
7241
|
+
followed.set(context, state);
|
|
7242
|
+
}
|
|
7243
|
+
const calls = await state;
|
|
7244
|
+
if (!calls) return null;
|
|
7245
|
+
let stopped = false;
|
|
7246
|
+
let reading = false;
|
|
7247
|
+
let target = null;
|
|
7248
|
+
const onThisPage = () => !page.isClosed() && context.pages().length === 1;
|
|
7249
|
+
const tell = () => {
|
|
7250
|
+
const shown2 = shownCall(calls, since);
|
|
7251
|
+
if (stopped || !shown2) return;
|
|
7252
|
+
const here = onThisPage();
|
|
7253
|
+
onLog({
|
|
7254
|
+
id: shown2.id,
|
|
7255
|
+
call: { title: shown2.title, file: shown2.file, line: shown2.line },
|
|
7256
|
+
status: shown2.status,
|
|
7257
|
+
lines: compressCallLog(shown2.lines).slice(-MAX_LIVE_CALL_LOG_LINES),
|
|
7258
|
+
point: here ? shown2.point : null,
|
|
7259
|
+
target: here && shown2.status === "running" && target?.id === shown2.id ? target.matches : null,
|
|
7260
|
+
viewport: page.viewportSize(),
|
|
7261
|
+
ms: (shown2.ended ?? Date.now()) - shown2.began,
|
|
7262
|
+
error: shown2.error
|
|
7263
|
+
});
|
|
7264
|
+
};
|
|
7265
|
+
calls.views.add(tell);
|
|
7266
|
+
tell();
|
|
7267
|
+
const timer = setInterval(() => {
|
|
7268
|
+
const shown2 = shownCall(calls, since);
|
|
7269
|
+
const selector = shown2?.selector;
|
|
7270
|
+
if (stopped || reading || !shown2 || shown2.status !== "running" || !selector || !onThisPage()) return;
|
|
7271
|
+
if (Date.now() - shown2.began < TARGET_EVERY_MS) return;
|
|
7272
|
+
reading = true;
|
|
7273
|
+
const framed = selector.includes("internal:control=enter-frame");
|
|
7274
|
+
void internalCall(
|
|
7275
|
+
page,
|
|
7276
|
+
() => page.locator(selector).evaluateAll((elements) => {
|
|
7277
|
+
const first = elements[0];
|
|
7278
|
+
if (!first) return { count: 0, box: null, enabled: null };
|
|
7279
|
+
const rect = first.getBoundingClientRect();
|
|
7280
|
+
const disabled = first.disabled === true || first.getAttribute("aria-disabled") === "true";
|
|
7281
|
+
return {
|
|
7282
|
+
count: elements.length,
|
|
7283
|
+
box: rect.width || rect.height ? { x: rect.x, y: rect.y, width: rect.width, height: rect.height } : null,
|
|
7284
|
+
enabled: !disabled
|
|
7285
|
+
};
|
|
7286
|
+
})
|
|
7287
|
+
).then((matches) => {
|
|
7288
|
+
if (stopped || shownCall(calls, since) !== shown2 || shown2.status !== "running") return;
|
|
7289
|
+
if (!matches.count && shown2.lines.some((line) => line.includes("locator resolved to"))) return;
|
|
7290
|
+
target = { id: shown2.id, matches: framed ? { ...matches, box: null } : matches };
|
|
7291
|
+
tell();
|
|
7292
|
+
}).catch(() => void 0).finally(() => {
|
|
7293
|
+
reading = false;
|
|
7294
|
+
});
|
|
7295
|
+
}, TARGET_EVERY_MS);
|
|
7296
|
+
timer.unref?.();
|
|
7297
|
+
return () => {
|
|
7298
|
+
stopped = true;
|
|
7299
|
+
clearInterval(timer);
|
|
7300
|
+
calls.views.delete(tell);
|
|
7301
|
+
};
|
|
7302
|
+
}
|
|
7303
|
+
|
|
6352
7304
|
// src/internal/capture/page-view.ts
|
|
7305
|
+
var SCREENCAST_LAG_MS = 100;
|
|
6353
7306
|
var HOLDERS = /* @__PURE__ */ new WeakMap();
|
|
6354
7307
|
var TEST_SCREENCASTS = /* @__PURE__ */ new WeakSet();
|
|
6355
7308
|
var OWN_SCREENCASTS = /* @__PURE__ */ new WeakMap();
|
|
@@ -6402,16 +7355,39 @@ async function openPageView(page, options) {
|
|
|
6402
7355
|
let last = null;
|
|
6403
7356
|
let sent2 = null;
|
|
6404
7357
|
let refused = false;
|
|
7358
|
+
let screening = false;
|
|
7359
|
+
let screened = null;
|
|
6405
7360
|
const interval = 1e3 / options.fps;
|
|
7361
|
+
const deliver = (frame) => {
|
|
7362
|
+
if (closed || !options.live()) return;
|
|
7363
|
+
refused = false;
|
|
7364
|
+
sent2 = frame;
|
|
7365
|
+
options.send(frame);
|
|
7366
|
+
};
|
|
7367
|
+
const refuse2 = (why) => {
|
|
7368
|
+
if (closed || refused || !options.live()) return;
|
|
7369
|
+
refused = true;
|
|
7370
|
+
sent2 = { refused: why };
|
|
7371
|
+
options.refuse(why);
|
|
7372
|
+
};
|
|
6406
7373
|
const pacer = new LatestPacer({
|
|
6407
7374
|
intervalMs: interval,
|
|
6408
7375
|
...options.spacing ? { spacing: options.spacing } : {},
|
|
6409
|
-
busy: options.busy,
|
|
7376
|
+
busy: () => screening || options.busy(),
|
|
6410
7377
|
send: (frame) => {
|
|
6411
7378
|
if (!options.live()) return;
|
|
6412
|
-
|
|
6413
|
-
|
|
6414
|
-
options.
|
|
7379
|
+
if (!options.screen) return deliver(frame);
|
|
7380
|
+
screening = true;
|
|
7381
|
+
screened = options.screen(frame).then(
|
|
7382
|
+
(shown2) => {
|
|
7383
|
+
if (typeof shown2 === "string") refuse2(shown2);
|
|
7384
|
+
else if (shown2) deliver(shown2);
|
|
7385
|
+
},
|
|
7386
|
+
() => void 0
|
|
7387
|
+
).finally(() => {
|
|
7388
|
+
screening = false;
|
|
7389
|
+
screened = null;
|
|
7390
|
+
});
|
|
6415
7391
|
}
|
|
6416
7392
|
});
|
|
6417
7393
|
let lighter = null;
|
|
@@ -6426,23 +7402,20 @@ async function openPageView(page, options) {
|
|
|
6426
7402
|
).then(
|
|
6427
7403
|
(shot) => {
|
|
6428
7404
|
if (closed) return;
|
|
6429
|
-
if (shot.length <= options.maxFrameBytes) return pacer.offer({ data: shot, ...viewport });
|
|
6430
|
-
|
|
6431
|
-
refused = true;
|
|
6432
|
-
sent2 = "refused";
|
|
6433
|
-
options.refuse();
|
|
7405
|
+
if (shot.length <= options.maxFrameBytes) return pacer.offer({ data: shot, ...viewport, at: now });
|
|
7406
|
+
refuse2();
|
|
6434
7407
|
},
|
|
6435
7408
|
() => void 0
|
|
6436
7409
|
).finally(() => {
|
|
6437
7410
|
lighter = null;
|
|
6438
7411
|
});
|
|
6439
7412
|
};
|
|
6440
|
-
const onFrame = (data, width, height) => {
|
|
7413
|
+
const onFrame = (data, width, height, at) => {
|
|
6441
7414
|
if (closed || last?.equals(data)) return;
|
|
6442
7415
|
last = data;
|
|
6443
7416
|
if (data.length > options.maxFrameBytes) return lighten();
|
|
6444
7417
|
const named = isViewSize(width, height);
|
|
6445
|
-
pacer.offer({ data, width: named ? width : viewport.width, height: named ? height : viewport.height });
|
|
7418
|
+
pacer.offer({ data, width: named ? width : viewport.width, height: named ? height : viewport.height, at });
|
|
6446
7419
|
};
|
|
6447
7420
|
const screencast = screencastOf(page);
|
|
6448
7421
|
let end = null;
|
|
@@ -6451,11 +7424,12 @@ async function openPageView(page, options) {
|
|
|
6451
7424
|
const shoot = async () => {
|
|
6452
7425
|
shots = null;
|
|
6453
7426
|
if (closed || page.isClosed()) return;
|
|
7427
|
+
const at = Date.now();
|
|
6454
7428
|
const shot = await internalCall(
|
|
6455
7429
|
page,
|
|
6456
7430
|
() => page.screenshot({ type: "jpeg", quality: options.quality, scale: "css", caret: "initial", timeout: 5e3 })
|
|
6457
7431
|
).catch(() => null);
|
|
6458
|
-
if (shot) onFrame(shot, viewport.width, viewport.height);
|
|
7432
|
+
if (shot) onFrame(shot, viewport.width, viewport.height, at);
|
|
6459
7433
|
if (!closed) {
|
|
6460
7434
|
shots = setTimeout(() => void shoot(), options.shotMs);
|
|
6461
7435
|
shots.unref?.();
|
|
@@ -6488,7 +7462,9 @@ async function openPageView(page, options) {
|
|
|
6488
7462
|
size: options.size(viewport),
|
|
6489
7463
|
quality: options.quality,
|
|
6490
7464
|
onFrame: (frame) => {
|
|
6491
|
-
|
|
7465
|
+
const now = Date.now();
|
|
7466
|
+
const at = typeof frame.timestamp === "number" && Number.isFinite(frame.timestamp) ? Math.min(frame.timestamp, now) - SCREENCAST_LAG_MS : Number.NEGATIVE_INFINITY;
|
|
7467
|
+
onFrame(frame.data, frame.viewportWidth, frame.viewportHeight, at);
|
|
6492
7468
|
return acks.next();
|
|
6493
7469
|
}
|
|
6494
7470
|
})
|
|
@@ -6512,9 +7488,22 @@ async function openPageView(page, options) {
|
|
|
6512
7488
|
},
|
|
6513
7489
|
resend: () => {
|
|
6514
7490
|
if (closed || !sent2 || !options.live()) return;
|
|
6515
|
-
if (
|
|
7491
|
+
if ("refused" in sent2) options.refuse(sent2.refused);
|
|
6516
7492
|
else options.send(sent2);
|
|
6517
7493
|
},
|
|
7494
|
+
refresh: () => {
|
|
7495
|
+
if (closed) return;
|
|
7496
|
+
sent2 = null;
|
|
7497
|
+
last = null;
|
|
7498
|
+
const at = Date.now();
|
|
7499
|
+
void internalCall(
|
|
7500
|
+
page,
|
|
7501
|
+
() => page.screenshot({ type: "jpeg", quality: options.quality, scale: "css", caret: "initial", timeout: 5e3 })
|
|
7502
|
+
).then(
|
|
7503
|
+
(shot) => onFrame(shot, viewport.width, viewport.height, at),
|
|
7504
|
+
() => void 0
|
|
7505
|
+
);
|
|
7506
|
+
},
|
|
6518
7507
|
close: async () => {
|
|
6519
7508
|
closed = true;
|
|
6520
7509
|
sent2 = null;
|
|
@@ -6522,7 +7511,7 @@ async function openPageView(page, options) {
|
|
|
6522
7511
|
pacer.close();
|
|
6523
7512
|
if (shots) clearTimeout(shots);
|
|
6524
7513
|
holders.delete(holder);
|
|
6525
|
-
await lighter;
|
|
7514
|
+
await Promise.all([lighter, screened]);
|
|
6526
7515
|
const stop = end;
|
|
6527
7516
|
end = null;
|
|
6528
7517
|
if (stop && !page.isClosed()) await stop().catch(() => void 0);
|
|
@@ -6540,6 +7529,7 @@ var MAX_EVENT_TEXT = 64 * 1024;
|
|
|
6540
7529
|
var MAX_STATES_WAITING = 8;
|
|
6541
7530
|
var LIGHT_QUALITY = 30;
|
|
6542
7531
|
var SHOT_MS = 1e3;
|
|
7532
|
+
var LOG_SPACING_MS = Math.ceil(1e3 / LIVE_CALL_LOGS_PER_SECOND);
|
|
6543
7533
|
var VIEW_END_MS = 1e3;
|
|
6544
7534
|
var GIVE_UP = /* @__PURE__ */ new Set([401, 403, 404, 410]);
|
|
6545
7535
|
var TOO_HEAVY = `This page is too heavy for the live view: each image of it is over ${MAX_LIVE_FRAME / 1024} KB, a lighter one included.`;
|
|
@@ -6612,7 +7602,8 @@ function openLiveView(worker, env = process.env, ci = detectCi(env)) {
|
|
|
6612
7602
|
address,
|
|
6613
7603
|
id: { shard: resolveShardInfo(worker.config, env)?.current ?? null, worker: worker.workerIndex },
|
|
6614
7604
|
client,
|
|
6615
|
-
|
|
7605
|
+
// The stream, and a frame, a call log and a stop or an end in flight at once: none waits behind another.
|
|
7606
|
+
agent: new client.Agent({ keepAlive: true, maxSockets: 4 }),
|
|
6616
7607
|
stream: null,
|
|
6617
7608
|
timer: null,
|
|
6618
7609
|
retries: 0,
|
|
@@ -6620,13 +7611,20 @@ function openLiveView(worker, env = process.env, ci = detectCi(env)) {
|
|
|
6620
7611
|
ended: false,
|
|
6621
7612
|
refused: null,
|
|
6622
7613
|
sharing: ci,
|
|
7614
|
+
ci,
|
|
6623
7615
|
lost: null,
|
|
6624
7616
|
answered: null,
|
|
6625
7617
|
viewers: 0,
|
|
6626
7618
|
states: Promise.resolve(),
|
|
6627
7619
|
statesWaiting: 0,
|
|
6628
7620
|
stop: null,
|
|
6629
|
-
posting: false
|
|
7621
|
+
posting: false,
|
|
7622
|
+
log: null,
|
|
7623
|
+
logPosting: false,
|
|
7624
|
+
logSentAt: 0,
|
|
7625
|
+
logTimer: null,
|
|
7626
|
+
logRefused: false,
|
|
7627
|
+
masks: []
|
|
6630
7628
|
};
|
|
6631
7629
|
if (ci) connect(link);
|
|
6632
7630
|
changed();
|
|
@@ -6676,9 +7674,9 @@ async function stopSharing() {
|
|
|
6676
7674
|
own.viewers = 0;
|
|
6677
7675
|
changed();
|
|
6678
7676
|
}
|
|
6679
|
-
function liveViewTestBegan(title, call) {
|
|
7677
|
+
function liveViewTestBegan(title, call, mayPause2 = false) {
|
|
6680
7678
|
if (!link) return;
|
|
6681
|
-
running = { title, page: null, call };
|
|
7679
|
+
running = { title, page: null, call, mayPause: mayPause2, began: Date.now() };
|
|
6682
7680
|
}
|
|
6683
7681
|
function liveViewPage(page) {
|
|
6684
7682
|
if (!link || !running || running.page) return;
|
|
@@ -6704,7 +7702,7 @@ function liveViewStopped(stop) {
|
|
|
6704
7702
|
};
|
|
6705
7703
|
}
|
|
6706
7704
|
function hideCallSecrets(call) {
|
|
6707
|
-
return { ...call, title: hideSecretsInText(call.title) };
|
|
7705
|
+
return { ...call, title: hideSecretsInText(hideTypedText(call.title)) };
|
|
6708
7706
|
}
|
|
6709
7707
|
function postState(own, message) {
|
|
6710
7708
|
if (!active(own) || own.statesWaiting >= MAX_STATES_WAITING) return;
|
|
@@ -6772,7 +7770,8 @@ function connect(own) {
|
|
|
6772
7770
|
text = text.slice(end + 2);
|
|
6773
7771
|
const data = block.split("\n").filter((line) => line.startsWith("data:")).map((line) => line.slice(5).trimStart()).join("\n");
|
|
6774
7772
|
const message = data ? parseInstanceToWorker(data) : null;
|
|
6775
|
-
if (message)
|
|
7773
|
+
if (message?.type === "masks") setMasks(own, message.locators);
|
|
7774
|
+
else if (message) setViewers(own, message.count);
|
|
6776
7775
|
}
|
|
6777
7776
|
});
|
|
6778
7777
|
response.on("error", () => drop(null));
|
|
@@ -6812,11 +7811,19 @@ function setViewers(own, count) {
|
|
|
6812
7811
|
return;
|
|
6813
7812
|
}
|
|
6814
7813
|
if (view) {
|
|
6815
|
-
if (count > before)
|
|
7814
|
+
if (count > before) {
|
|
7815
|
+
void view.opened.then((opened) => opened?.resend());
|
|
7816
|
+
if (own.log) postLog(own, own.log);
|
|
7817
|
+
}
|
|
6816
7818
|
return;
|
|
6817
7819
|
}
|
|
6818
7820
|
startView();
|
|
6819
7821
|
}
|
|
7822
|
+
function setMasks(own, locators) {
|
|
7823
|
+
if (JSON.stringify(locators) === JSON.stringify(own.masks)) return;
|
|
7824
|
+
own.masks = locators;
|
|
7825
|
+
if (own === link) void view?.opened.then((opened) => opened?.refresh());
|
|
7826
|
+
}
|
|
6820
7827
|
function startView() {
|
|
6821
7828
|
const own = link;
|
|
6822
7829
|
const page = running?.page ?? null;
|
|
@@ -6826,6 +7833,11 @@ function startView() {
|
|
|
6826
7833
|
return;
|
|
6827
7834
|
}
|
|
6828
7835
|
const live = () => view?.page === page && link === own && active(own) && own.viewers > 0;
|
|
7836
|
+
const mask = maskSecretFields(page, {
|
|
7837
|
+
masks: () => own.masks,
|
|
7838
|
+
quality: LIVE_FRAME_QUALITY,
|
|
7839
|
+
maxBytes: MAX_LIVE_FRAME
|
|
7840
|
+
});
|
|
6829
7841
|
const opened = openPageView(page, {
|
|
6830
7842
|
size: (viewport) => fitViewSize(viewport, LIVE_FRAME_WIDTH, LIVE_FRAME_HEIGHT),
|
|
6831
7843
|
quality: LIVE_FRAME_QUALITY,
|
|
@@ -6834,28 +7846,39 @@ function startView() {
|
|
|
6834
7846
|
maxFrameBytes: MAX_LIVE_FRAME,
|
|
6835
7847
|
lightQuality: LIGHT_QUALITY,
|
|
6836
7848
|
shotMs: SHOT_MS,
|
|
6837
|
-
busy: () => own.posting,
|
|
7849
|
+
busy: () => own.posting || mask.resting,
|
|
6838
7850
|
live,
|
|
7851
|
+
screen: (frame) => mask.screen(frame),
|
|
6839
7852
|
send: (frame) => postFrame(own, frame),
|
|
6840
|
-
refuse: () => void post(own, { type: "frame", ...own.id, refused: TOO_HEAVY })
|
|
7853
|
+
refuse: (why) => void post(own, { type: "frame", ...own.id, refused: why ?? TOO_HEAVY })
|
|
6841
7854
|
}).catch(() => null);
|
|
6842
7855
|
const onClose = () => {
|
|
6843
7856
|
if (view?.page === page) void stopView();
|
|
6844
7857
|
};
|
|
6845
7858
|
page.once("close", onClose);
|
|
6846
|
-
|
|
7859
|
+
const following = own.ci && running && !running.mayPause && !own.logRefused ? followCallLog(page, running.began, (log) => {
|
|
7860
|
+
if (live()) postLog(own, log);
|
|
7861
|
+
}).catch(() => null) : null;
|
|
7862
|
+
view = { page, opened, onClose, following };
|
|
6847
7863
|
}
|
|
6848
7864
|
async function stopView() {
|
|
6849
7865
|
const shown2 = view;
|
|
6850
7866
|
view = null;
|
|
6851
7867
|
if (!shown2) return;
|
|
6852
7868
|
shown2.page.off("close", shown2.onClose);
|
|
7869
|
+
void shown2.following?.then((stop) => stop?.());
|
|
7870
|
+
const own = link;
|
|
7871
|
+
if (own) {
|
|
7872
|
+
own.log = null;
|
|
7873
|
+
if (own.logTimer) clearTimeout(own.logTimer);
|
|
7874
|
+
own.logTimer = null;
|
|
7875
|
+
}
|
|
6853
7876
|
const closed = shown2.opened.then((opened) => opened?.close());
|
|
6854
7877
|
let timer;
|
|
6855
7878
|
await Promise.race([
|
|
6856
7879
|
closed.catch(() => void 0),
|
|
6857
|
-
new Promise((
|
|
6858
|
-
timer = setTimeout(
|
|
7880
|
+
new Promise((resolve6) => {
|
|
7881
|
+
timer = setTimeout(resolve6, VIEW_END_MS);
|
|
6859
7882
|
timer.unref?.();
|
|
6860
7883
|
})
|
|
6861
7884
|
]);
|
|
@@ -6876,19 +7899,47 @@ function postFrame(own, frame) {
|
|
|
6876
7899
|
own.posting = false;
|
|
6877
7900
|
});
|
|
6878
7901
|
}
|
|
7902
|
+
function postLog(own, log) {
|
|
7903
|
+
own.log = log;
|
|
7904
|
+
nextLog(own);
|
|
7905
|
+
}
|
|
7906
|
+
function nextLog(own) {
|
|
7907
|
+
if (own.logPosting || own.logTimer) return;
|
|
7908
|
+
const wait = LOG_SPACING_MS - (Date.now() - own.logSentAt);
|
|
7909
|
+
if (wait > 0) {
|
|
7910
|
+
own.logTimer = setTimeout(() => {
|
|
7911
|
+
own.logTimer = null;
|
|
7912
|
+
nextLog(own);
|
|
7913
|
+
}, wait);
|
|
7914
|
+
own.logTimer.unref?.();
|
|
7915
|
+
return;
|
|
7916
|
+
}
|
|
7917
|
+
const log = own.log;
|
|
7918
|
+
if (!active(own) || !view || !log || own.logRefused) return;
|
|
7919
|
+
own.logPosting = true;
|
|
7920
|
+
own.logSentAt = Date.now();
|
|
7921
|
+
let again = false;
|
|
7922
|
+
void post(own, { type: "call-log", ...own.id, log, test: running?.title ?? null }).then((status) => {
|
|
7923
|
+
if (status === 400) own.logRefused = true;
|
|
7924
|
+
again = status === 429;
|
|
7925
|
+
}).catch(() => void 0).finally(() => {
|
|
7926
|
+
own.logPosting = false;
|
|
7927
|
+
if (own.log && (own.log !== log || again)) nextLog(own);
|
|
7928
|
+
});
|
|
7929
|
+
}
|
|
6879
7930
|
function post(own, message) {
|
|
6880
7931
|
return active(own) ? send(own, message) : Promise.resolve(null);
|
|
6881
7932
|
}
|
|
6882
7933
|
function send(own, message) {
|
|
6883
7934
|
if (own.ended || own.refused !== null) return Promise.resolve(null);
|
|
6884
7935
|
const body = JSON.stringify(message);
|
|
6885
|
-
return new Promise((
|
|
7936
|
+
return new Promise((resolve6) => {
|
|
6886
7937
|
let settled = false;
|
|
6887
7938
|
const settle = (status) => {
|
|
6888
7939
|
if (settled) return;
|
|
6889
7940
|
settled = true;
|
|
6890
7941
|
if (status !== null && GIVE_UP.has(status)) refuse(own, status);
|
|
6891
|
-
|
|
7942
|
+
resolve6(status);
|
|
6892
7943
|
};
|
|
6893
7944
|
const request = own.client.request(own.address.url, {
|
|
6894
7945
|
method: "POST",
|
|
@@ -6915,7 +7966,6 @@ function send(own, message) {
|
|
|
6915
7966
|
// src/internal/capture/run-channel.ts
|
|
6916
7967
|
var CONNECT_MS = 2e3;
|
|
6917
7968
|
var BAR_HIDDEN2 = "#__piwi-pause-bar { display: none !important; }";
|
|
6918
|
-
var OUTLINE_STYLE = "outline: 2px dashed #a78bfa; outline-offset: 2px; background: rgba(167, 139, 250, 0.15);";
|
|
6919
7969
|
var WENT_ON = "The test went on.";
|
|
6920
7970
|
var MAX_FRAME_BACKLOG = 1024 * 1024;
|
|
6921
7971
|
var SHOT_MS2 = 500;
|
|
@@ -6923,6 +7973,7 @@ var LIGHT_QUALITY2 = 30;
|
|
|
6923
7973
|
var TOO_HEAVY2 = `This page is too heavy for the live view: each image of it is over ${MAX_VIEW_FRAME / 1024} KB, a lighter one included. The image of the stop still shows it.`;
|
|
6924
7974
|
var VIEW_END_MS2 = 1e3;
|
|
6925
7975
|
var PAGE_ANSWER_MS = 1e4;
|
|
7976
|
+
var OUTLINE_MS = 2e3;
|
|
6926
7977
|
var NO_ANSWER = "The page did not answer in time.";
|
|
6927
7978
|
var LATE_ACT = "The page did not answer in time: the action may still take effect, and is told when it does.";
|
|
6928
7979
|
var MAX_INPUT_WAITING = 4;
|
|
@@ -6962,11 +8013,12 @@ var sharedHere = false;
|
|
|
6962
8013
|
var liveSaid = "";
|
|
6963
8014
|
var unhearLive = null;
|
|
6964
8015
|
var NO_AGENT_PAGE = "This stop has no page an agent can read: its call does not name one among its context's pages.";
|
|
6965
|
-
function workerTakes(env = process.env) {
|
|
8016
|
+
function workerTakes(env = process.env, playwright) {
|
|
6966
8017
|
const pairing = !!env[PIWI_LOCAL_DEBUG_ENV.editorSend];
|
|
6967
8018
|
const recorder = parseRecordChannel(env[RECORD_CHANNEL_ENV]) !== null;
|
|
8019
|
+
const boxes = playwright === void 0 || playwrightVersionAtLeast(playwright, PICK_AT_SINCE);
|
|
6968
8020
|
return RUN_TAKES.filter(
|
|
6969
|
-
(
|
|
8021
|
+
(take2) => take2 === "pick" ? pairing : take2 === "record" || take2 === "assert" ? recorder : take2 === "pick-at" ? boxes : true
|
|
6970
8022
|
);
|
|
6971
8023
|
}
|
|
6972
8024
|
async function openRunChannel(worker, handlers, env = process.env) {
|
|
@@ -6975,15 +8027,15 @@ async function openRunChannel(worker, handlers, env = process.env) {
|
|
|
6975
8027
|
if (socket || !channel || !ref || !/^[\w-]{1,64}$/.test(ref)) return false;
|
|
6976
8028
|
const opening = net.connect(channel.path);
|
|
6977
8029
|
opening.setEncoding("utf8");
|
|
6978
|
-
const opened = await new Promise((
|
|
6979
|
-
const timer = setTimeout(() =>
|
|
8030
|
+
const opened = await new Promise((resolve6) => {
|
|
8031
|
+
const timer = setTimeout(() => resolve6(false), CONNECT_MS);
|
|
6980
8032
|
opening.once("connect", () => {
|
|
6981
8033
|
clearTimeout(timer);
|
|
6982
|
-
|
|
8034
|
+
resolve6(true);
|
|
6983
8035
|
});
|
|
6984
8036
|
opening.once("error", () => {
|
|
6985
8037
|
clearTimeout(timer);
|
|
6986
|
-
|
|
8038
|
+
resolve6(false);
|
|
6987
8039
|
});
|
|
6988
8040
|
});
|
|
6989
8041
|
if (!opened || opening.destroyed) {
|
|
@@ -7026,8 +8078,10 @@ async function openRunChannel(worker, handlers, env = process.env) {
|
|
|
7026
8078
|
pid: process.pid,
|
|
7027
8079
|
playwright: worker.config.version,
|
|
7028
8080
|
reporter: getReporterVersion(),
|
|
7029
|
-
takes: workerTakes(env),
|
|
7030
|
-
holds: [...RUN_HOLDS]
|
|
8081
|
+
takes: workerTakes(env, worker.config.version),
|
|
8082
|
+
holds: [...RUN_HOLDS],
|
|
8083
|
+
// A run the reporter follows from outside the editor, which its own test runner started.
|
|
8084
|
+
...ref.startsWith(TERMINAL_REF_PREFIX) && startedByTestRunner(env) ? { runner: true } : {}
|
|
7031
8085
|
});
|
|
7032
8086
|
liveSaid = "";
|
|
7033
8087
|
tellLive();
|
|
@@ -7100,7 +8154,7 @@ async function unbind() {
|
|
|
7100
8154
|
const shared = bound?.browser;
|
|
7101
8155
|
bound = null;
|
|
7102
8156
|
if (shared?.isConnected()) {
|
|
7103
|
-
await
|
|
8157
|
+
await within3(
|
|
7104
8158
|
internalCall(shared, () => bindBrowser(shared, runName, process.cwd(), false)),
|
|
7105
8159
|
VIEW_END_MS2
|
|
7106
8160
|
);
|
|
@@ -7160,7 +8214,7 @@ function viewEnded(stop) {
|
|
|
7160
8214
|
else viewEnds.add(() => ended.abort());
|
|
7161
8215
|
return ended.signal;
|
|
7162
8216
|
}
|
|
7163
|
-
async function
|
|
8217
|
+
async function within3(run, ms) {
|
|
7164
8218
|
await answerWithin(run, ms, void 0);
|
|
7165
8219
|
}
|
|
7166
8220
|
async function answerWithin(run, ms, late) {
|
|
@@ -7168,20 +8222,20 @@ async function answerWithin(run, ms, late) {
|
|
|
7168
8222
|
try {
|
|
7169
8223
|
return await Promise.race([
|
|
7170
8224
|
run.catch(() => late),
|
|
7171
|
-
new Promise((
|
|
8225
|
+
new Promise((resolve6) => timer = setTimeout(() => resolve6(late), ms))
|
|
7172
8226
|
]);
|
|
7173
8227
|
} finally {
|
|
7174
8228
|
clearTimeout(timer);
|
|
7175
8229
|
}
|
|
7176
8230
|
}
|
|
7177
8231
|
async function endView(stop, page) {
|
|
7178
|
-
if (viewStarting?.stop === stop) await
|
|
8232
|
+
if (viewStarting?.stop === stop) await within3(viewStarting.started, VIEW_END_MS2);
|
|
7179
8233
|
if (view2?.stop === stop) await stopView2();
|
|
7180
8234
|
const own = input;
|
|
7181
8235
|
input = freshInput();
|
|
7182
|
-
await
|
|
8236
|
+
await within3(own.playing, VIEW_END_MS2);
|
|
7183
8237
|
if (page && !page.isClosed() && (own.buttons.size || own.keys.size)) {
|
|
7184
|
-
await
|
|
8238
|
+
await within3(
|
|
7185
8239
|
internalCall(page, async () => {
|
|
7186
8240
|
for (const button of own.buttons) await page.mouse.up({ button });
|
|
7187
8241
|
for (const key of own.keys) await page.keyboard.up(key);
|
|
@@ -7195,7 +8249,7 @@ async function stopView2() {
|
|
|
7195
8249
|
view2 = null;
|
|
7196
8250
|
for (const end of viewEnds) end();
|
|
7197
8251
|
viewEnds.clear();
|
|
7198
|
-
if (shown2) await
|
|
8252
|
+
if (shown2) await within3(shown2.close(), VIEW_END_MS2);
|
|
7199
8253
|
}
|
|
7200
8254
|
function startView2(holding) {
|
|
7201
8255
|
if (view2?.stop === holding.stop) {
|
|
@@ -7215,7 +8269,7 @@ function startView2(holding) {
|
|
|
7215
8269
|
}
|
|
7216
8270
|
if (!opened) return { ok: false, message: "The page could not be shown: it closed, or does not answer." };
|
|
7217
8271
|
if (held !== holding || page.isClosed()) {
|
|
7218
|
-
await
|
|
8272
|
+
await within3(opened.close(), VIEW_END_MS2);
|
|
7219
8273
|
return { ok: false, message: WENT_ON };
|
|
7220
8274
|
}
|
|
7221
8275
|
view2 = opened;
|
|
@@ -7259,7 +8313,7 @@ async function openView(page, stop) {
|
|
|
7259
8313
|
};
|
|
7260
8314
|
}
|
|
7261
8315
|
async function stopViewOf(holding) {
|
|
7262
|
-
if (viewStarting?.stop === holding.stop) await
|
|
8316
|
+
if (viewStarting?.stop === holding.stop) await within3(viewStarting.started, VIEW_END_MS2);
|
|
7263
8317
|
if (view2?.stop === holding.stop) await stopView2();
|
|
7264
8318
|
return { ok: true, message: "The live view is off." };
|
|
7265
8319
|
}
|
|
@@ -7350,18 +8404,17 @@ async function countLocator(holding, source) {
|
|
|
7350
8404
|
if (!locator) return { ok: false, message: `${source} is not a locator Piwi can build.`, count: null };
|
|
7351
8405
|
try {
|
|
7352
8406
|
const count = await internalCall(page, () => locator.count());
|
|
8407
|
+
let outlined = false;
|
|
7353
8408
|
if (holding.bar && count > 0 && held === holding) {
|
|
7354
|
-
const shown2 =
|
|
7355
|
-
|
|
7356
|
-
|
|
7357
|
-
stop: holding.stop,
|
|
7358
|
-
remove: () => internalCall(page, () => typeof dispose === "function" ? dispose.call(shown2) : page.hideHighlight())
|
|
7359
|
-
};
|
|
8409
|
+
const shown2 = highlightAtStop(page, locator, "count", source);
|
|
8410
|
+
outline = { stop: holding.stop, remove: async () => void await shown2.hide() };
|
|
8411
|
+
outlined = await answerWithin(shown2.shown, OUTLINE_MS, false);
|
|
7360
8412
|
}
|
|
7361
8413
|
return {
|
|
7362
8414
|
ok: true,
|
|
7363
8415
|
message: count === 0 ? "No element matches." : count === 1 ? "1 element matches." : `${count} elements match.`,
|
|
7364
|
-
count
|
|
8416
|
+
count,
|
|
8417
|
+
outlined
|
|
7365
8418
|
};
|
|
7366
8419
|
} catch (error) {
|
|
7367
8420
|
return { ok: false, message: `The locator could not be counted: ${errorMessage(error)}`, count: null };
|
|
@@ -7548,17 +8601,17 @@ async function imageOf(page) {
|
|
|
7548
8601
|
|
|
7549
8602
|
// src/internal/capture/run-events.ts
|
|
7550
8603
|
var begun = /* @__PURE__ */ new WeakMap();
|
|
7551
|
-
var
|
|
8604
|
+
var heard2 = /* @__PURE__ */ new WeakSet();
|
|
7552
8605
|
var watched = /* @__PURE__ */ new WeakSet();
|
|
7553
|
-
function
|
|
8606
|
+
function userFile2(file, cwd, windows) {
|
|
7554
8607
|
if (!file || /[\\/]node_modules[\\/]/.test(file)) return null;
|
|
7555
|
-
const
|
|
7556
|
-
if (!
|
|
7557
|
-
return
|
|
8608
|
+
const relative7 = callSitePath(file, cwd, windows);
|
|
8609
|
+
if (!relative7 || (windows ? path11.win32 : path11.posix).isAbsolute(relative7) || relative7.startsWith("..")) return null;
|
|
8610
|
+
return relative7;
|
|
7558
8611
|
}
|
|
7559
8612
|
function callPlace(frames, cwd = process.cwd(), windows = process.platform === "win32") {
|
|
7560
8613
|
for (const frame of frames ?? []) {
|
|
7561
|
-
const file =
|
|
8614
|
+
const file = userFile2(frame.file, cwd, windows);
|
|
7562
8615
|
if (!file || !frame.line || frame.line < 1) continue;
|
|
7563
8616
|
return { file, line: frame.line, ...frame.column && frame.column > 0 ? { column: frame.column } : {} };
|
|
7564
8617
|
}
|
|
@@ -7575,9 +8628,9 @@ function errorText(error) {
|
|
|
7575
8628
|
}
|
|
7576
8629
|
function watchCalls(playwright) {
|
|
7577
8630
|
const instrumentation = playwright?._instrumentation;
|
|
7578
|
-
if (!playwright ||
|
|
8631
|
+
if (!playwright || heard2.has(playwright)) return true;
|
|
7579
8632
|
if (!instrumentation || typeof instrumentation.addListener !== "function") return false;
|
|
7580
|
-
|
|
8633
|
+
heard2.add(playwright);
|
|
7581
8634
|
const titles = /* @__PURE__ */ new WeakMap();
|
|
7582
8635
|
instrumentation.addListener({
|
|
7583
8636
|
onApiCallBegin: (zone, channel) => {
|
|
@@ -7694,9 +8747,9 @@ function yamlScalar(text) {
|
|
|
7694
8747
|
}
|
|
7695
8748
|
|
|
7696
8749
|
// src/internal/capture/record-handoff.ts
|
|
7697
|
-
var
|
|
8750
|
+
var fs8 = __toESM(require("fs"));
|
|
7698
8751
|
var net2 = __toESM(require("net"));
|
|
7699
|
-
var
|
|
8752
|
+
var path12 = __toESM(require("path"));
|
|
7700
8753
|
var START_WAIT_MS = 6e4;
|
|
7701
8754
|
var NEW_TAB_NOTICE = "A new tab opened: what you do there is not recorded yet. Record in the first tab.";
|
|
7702
8755
|
var relays = /* @__PURE__ */ new WeakMap();
|
|
@@ -7716,7 +8769,7 @@ function canRecordFromHere(channel = process.env[RECORD_CHANNEL_ENV]) {
|
|
|
7716
8769
|
function handoffPlace(location, cwd = process.cwd()) {
|
|
7717
8770
|
const m = /^(.+):(\d+):\d+$/.exec(location);
|
|
7718
8771
|
if (!m) return null;
|
|
7719
|
-
return { file:
|
|
8772
|
+
return { file: path12.resolve(cwd, m[1]), line: Number(m[2]) };
|
|
7720
8773
|
}
|
|
7721
8774
|
async function recordFromHere(options) {
|
|
7722
8775
|
const channel = parseRecordChannel(options.channel ?? process.env[RECORD_CHANNEL_ENV]);
|
|
@@ -7726,7 +8779,7 @@ async function recordFromHere(options) {
|
|
|
7726
8779
|
if (ended?.aborted) return "the test ended.";
|
|
7727
8780
|
const use = testInfo.project?.use ?? {};
|
|
7728
8781
|
const context = page.context();
|
|
7729
|
-
return new Promise((
|
|
8782
|
+
return new Promise((resolve6) => {
|
|
7730
8783
|
const socket2 = net2.connect(channel.path);
|
|
7731
8784
|
socket2.setEncoding("utf8");
|
|
7732
8785
|
const answers = /* @__PURE__ */ new Map();
|
|
@@ -7787,7 +8840,7 @@ async function recordFromHere(options) {
|
|
|
7787
8840
|
page.off("crash", onGone);
|
|
7788
8841
|
ended?.removeEventListener("abort", onEnded);
|
|
7789
8842
|
socket2.end();
|
|
7790
|
-
|
|
8843
|
+
resolve6(recording ? null : said);
|
|
7791
8844
|
};
|
|
7792
8845
|
const relayed = (source, payload) => new Promise((answer) => {
|
|
7793
8846
|
const call = ++calls;
|
|
@@ -7819,7 +8872,7 @@ async function recordFromHere(options) {
|
|
|
7819
8872
|
if (typeof initScript?.dispose !== "function") relay.lingering = true;
|
|
7820
8873
|
}
|
|
7821
8874
|
if (settled) return release();
|
|
7822
|
-
await page.evaluate(
|
|
8875
|
+
await page.evaluate(fs8.readFileSync(bundle, "utf-8"));
|
|
7823
8876
|
if (settled) return release();
|
|
7824
8877
|
recording = true;
|
|
7825
8878
|
clearTimeout(startWait);
|
|
@@ -7895,7 +8948,7 @@ function browserFunctionsInWorker() {
|
|
|
7895
8948
|
function browserFunctionCoverage(files) {
|
|
7896
8949
|
return sanitizeCoverage({
|
|
7897
8950
|
v: 1,
|
|
7898
|
-
files: [...files].map(([
|
|
8951
|
+
files: [...files].map(([path18, functions]) => ({ path: path18, runtime: "browser", functions }))
|
|
7899
8952
|
});
|
|
7900
8953
|
}
|
|
7901
8954
|
function newKnownFunctions(known, sent2) {
|
|
@@ -7908,19 +8961,19 @@ function newKnownFunctions(known, sent2) {
|
|
|
7908
8961
|
budget = MAX_COVERAGE_RANGES;
|
|
7909
8962
|
};
|
|
7910
8963
|
for (const [raw, ranges] of known) {
|
|
7911
|
-
const
|
|
7912
|
-
if (
|
|
7913
|
-
let named = sent2.get(
|
|
8964
|
+
const path18 = normalizeCoveragePath(raw);
|
|
8965
|
+
if (path18 === null) continue;
|
|
8966
|
+
let named = sent2.get(path18);
|
|
7914
8967
|
let fresh = normalizeFunctionRanges(ranges).filter((range) => !named?.has(`${range[0]}:${range[1]}`));
|
|
7915
8968
|
if (fresh.length === 0) continue;
|
|
7916
|
-
if (!named) sent2.set(
|
|
8969
|
+
if (!named) sent2.set(path18, named = /* @__PURE__ */ new Set());
|
|
7917
8970
|
for (const range of fresh) named.add(`${range[0]}:${range[1]}`);
|
|
7918
8971
|
while (fresh.length > 0) {
|
|
7919
8972
|
if (budget === 0 || files.length >= MAX_COVERAGE_FILES) close();
|
|
7920
8973
|
const part = fresh.slice(0, budget);
|
|
7921
8974
|
fresh = fresh.slice(part.length);
|
|
7922
8975
|
budget -= part.length;
|
|
7923
|
-
files.push({ path:
|
|
8976
|
+
files.push({ path: path18, allFunctions: part });
|
|
7924
8977
|
}
|
|
7925
8978
|
}
|
|
7926
8979
|
close();
|
|
@@ -7931,13 +8984,13 @@ function serverCoverageInWorker() {
|
|
|
7931
8984
|
}
|
|
7932
8985
|
|
|
7933
8986
|
// src/internal/support/aria-sampling.ts
|
|
7934
|
-
var
|
|
7935
|
-
var
|
|
7936
|
-
var
|
|
8987
|
+
var path13 = __toESM(require("path"));
|
|
8988
|
+
var os4 = __toESM(require("os"));
|
|
8989
|
+
var fs9 = __toESM(require("fs"));
|
|
7937
8990
|
|
|
7938
8991
|
// src/internal/support/instance-id.ts
|
|
7939
8992
|
var crypto2 = __toESM(require("crypto"));
|
|
7940
|
-
var
|
|
8993
|
+
var os3 = __toESM(require("os"));
|
|
7941
8994
|
function hashForProject(projectName) {
|
|
7942
8995
|
return crypto2.createHash("sha1").update(projectName).digest("hex").slice(0, 16);
|
|
7943
8996
|
}
|
|
@@ -7947,14 +9000,14 @@ function ariaSampleIdentity(filePath2, title) {
|
|
|
7947
9000
|
return `${filePath2}\0${title}`;
|
|
7948
9001
|
}
|
|
7949
9002
|
function getAriaSampleFilePath(projectName) {
|
|
7950
|
-
return
|
|
9003
|
+
return path13.join(os4.tmpdir(), `piwi-dashboard-aria-sample-${hashForProject(projectName)}.json`);
|
|
7951
9004
|
}
|
|
7952
9005
|
var cachedSets = /* @__PURE__ */ new Map();
|
|
7953
9006
|
function loadAriaSampleSet(projectName) {
|
|
7954
9007
|
if (cachedSets.has(projectName)) return cachedSets.get(projectName);
|
|
7955
9008
|
let set = null;
|
|
7956
9009
|
try {
|
|
7957
|
-
const raw =
|
|
9010
|
+
const raw = fs9.readFileSync(getAriaSampleFilePath(projectName), "utf8");
|
|
7958
9011
|
const parsed2 = JSON.parse(raw);
|
|
7959
9012
|
if (parsed2.projectName === projectName && Array.isArray(parsed2.identities)) {
|
|
7960
9013
|
set = new Set(parsed2.identities.filter((x) => typeof x === "string"));
|
|
@@ -7977,8 +9030,8 @@ function isDueForAriaSample(testInfo) {
|
|
|
7977
9030
|
|
|
7978
9031
|
// src/internal/capture/code-reach.ts
|
|
7979
9032
|
var crypto3 = __toESM(require("crypto"));
|
|
7980
|
-
var
|
|
7981
|
-
var
|
|
9033
|
+
var fs10 = __toESM(require("fs"));
|
|
9034
|
+
var path14 = __toESM(require("path"));
|
|
7982
9035
|
var import_node_child_process = require("child_process");
|
|
7983
9036
|
var MAX_MAP_BYTES = 20 * 1024 * 1024;
|
|
7984
9037
|
var MAP_TIMEOUT_MS = 5e3;
|
|
@@ -8021,18 +9074,18 @@ function repositoryRoot(from) {
|
|
|
8021
9074
|
}).trim();
|
|
8022
9075
|
} catch {
|
|
8023
9076
|
}
|
|
8024
|
-
cachedRepoRoot = { from, root:
|
|
9077
|
+
cachedRepoRoot = { from, root: path14.resolve(root) };
|
|
8025
9078
|
return cachedRepoRoot.root;
|
|
8026
9079
|
}
|
|
8027
9080
|
function codeReachRoots(configDir, configured) {
|
|
8028
9081
|
const repoRoot = repositoryRoot(configDir);
|
|
8029
|
-
const
|
|
8030
|
-
const global = Array.isArray(configured) ?
|
|
9082
|
+
const resolve6 = (dirs) => (Array.isArray(dirs) ? dirs : dirs ? [dirs] : []).filter(Boolean).map((r) => path14.resolve(configDir, r));
|
|
9083
|
+
const global = Array.isArray(configured) ? resolve6(configured) : resolve6(configured?.["*"]);
|
|
8031
9084
|
const byOrigin = /* @__PURE__ */ new Map();
|
|
8032
9085
|
if (configured && !Array.isArray(configured)) {
|
|
8033
9086
|
for (const [key, dirs] of Object.entries(configured)) {
|
|
8034
9087
|
const origin = key === "*" ? null : originOf2(key);
|
|
8035
|
-
const resolved =
|
|
9088
|
+
const resolved = resolve6(dirs);
|
|
8036
9089
|
if (origin && resolved.length) byOrigin.set(origin, resolved);
|
|
8037
9090
|
}
|
|
8038
9091
|
}
|
|
@@ -8055,7 +9108,7 @@ function scriptRoots(roots, scriptUrl, pageUrl) {
|
|
|
8055
9108
|
}
|
|
8056
9109
|
function isFile(file) {
|
|
8057
9110
|
try {
|
|
8058
|
-
return
|
|
9111
|
+
return fs10.statSync(file).isFile();
|
|
8059
9112
|
} catch {
|
|
8060
9113
|
return false;
|
|
8061
9114
|
}
|
|
@@ -8063,10 +9116,10 @@ function isFile(file) {
|
|
|
8063
9116
|
function resolveSourcePath(source, roots) {
|
|
8064
9117
|
const normalized = normalizeSourcePath(source);
|
|
8065
9118
|
if (!normalized || /(?:^|\/)node_modules\//.test(normalized)) return null;
|
|
8066
|
-
if (
|
|
8067
|
-
const
|
|
9119
|
+
if (path14.isAbsolute(normalized) && isFile(normalized)) return path14.resolve(normalized);
|
|
9120
|
+
const relative7 = normalized.replace(/^\/+/, "");
|
|
8068
9121
|
for (const root of roots) {
|
|
8069
|
-
const candidate =
|
|
9122
|
+
const candidate = path14.resolve(root, relative7);
|
|
8070
9123
|
if (isFile(candidate)) return candidate;
|
|
8071
9124
|
}
|
|
8072
9125
|
return null;
|
|
@@ -8098,7 +9151,7 @@ function fileForUrl(url, roots, bases) {
|
|
|
8098
9151
|
return file;
|
|
8099
9152
|
}
|
|
8100
9153
|
function relativeToRepo(file, repoRoot) {
|
|
8101
|
-
return
|
|
9154
|
+
return path14.relative(repoRoot, file).split(path14.sep).join("/");
|
|
8102
9155
|
}
|
|
8103
9156
|
function scriptVersion(entry) {
|
|
8104
9157
|
return entry.hash ?? crypto3.createHash("sha1").update(entry.source ?? "").digest("hex");
|
|
@@ -8161,10 +9214,10 @@ async function resolveFunctions(entries, roots, fetchMap, pageUrl, withKnown) {
|
|
|
8161
9214
|
const files = /* @__PURE__ */ new Map();
|
|
8162
9215
|
const known = /* @__PURE__ */ new Map();
|
|
8163
9216
|
const addTo = (into, file, functions) => {
|
|
8164
|
-
const
|
|
8165
|
-
const listed = into.get(
|
|
9217
|
+
const path18 = relativeToRepo(file, roots.repoRoot);
|
|
9218
|
+
const listed = into.get(path18);
|
|
8166
9219
|
if (listed) listed.push(...functions);
|
|
8167
|
-
else into.set(
|
|
9220
|
+
else into.set(path18, [...functions]);
|
|
8168
9221
|
};
|
|
8169
9222
|
const add2 = (file, functions) => addTo(files, file, functions);
|
|
8170
9223
|
const bases = viteBases(entries);
|
|
@@ -8319,8 +9372,8 @@ function onScriptParsed(page, capture, event) {
|
|
|
8319
9372
|
var READ_WAIT_MS = 5e3;
|
|
8320
9373
|
function readCoverage(page, cdp) {
|
|
8321
9374
|
let timer;
|
|
8322
|
-
const late = new Promise((
|
|
8323
|
-
timer = setTimeout(() =>
|
|
9375
|
+
const late = new Promise((resolve6) => {
|
|
9376
|
+
timer = setTimeout(() => resolve6([]), READ_WAIT_MS);
|
|
8324
9377
|
});
|
|
8325
9378
|
const taken = internalCall(page, () => cdp.send("Profiler.takePreciseCoverage")).then(
|
|
8326
9379
|
(answer) => answer.result,
|
|
@@ -8508,7 +9561,7 @@ async function stopPageCoverage(page) {
|
|
|
8508
9561
|
|
|
8509
9562
|
// src/internal/capture/css-coverage.ts
|
|
8510
9563
|
var crypto4 = __toESM(require("crypto"));
|
|
8511
|
-
var
|
|
9564
|
+
var fs11 = __toESM(require("fs"));
|
|
8512
9565
|
|
|
8513
9566
|
// ../core/src/css-rules.ts
|
|
8514
9567
|
var GROUPING = /* @__PURE__ */ new Set(["media", "supports", "layer", "container", "scope", "starting-style", "document"]);
|
|
@@ -8648,7 +9701,7 @@ function sourceFile(source, roots) {
|
|
|
8648
9701
|
function styleBlockLine(file, index) {
|
|
8649
9702
|
let text;
|
|
8650
9703
|
try {
|
|
8651
|
-
text =
|
|
9704
|
+
text = fs11.readFileSync(file, "utf-8");
|
|
8652
9705
|
} catch {
|
|
8653
9706
|
return 0;
|
|
8654
9707
|
}
|
|
@@ -8676,10 +9729,10 @@ async function sheetMap(sheet, text, fetchMap) {
|
|
|
8676
9729
|
return null;
|
|
8677
9730
|
}
|
|
8678
9731
|
}
|
|
8679
|
-
function
|
|
9732
|
+
function sameLines2(text, rules, file, lineOffset) {
|
|
8680
9733
|
let source;
|
|
8681
9734
|
try {
|
|
8682
|
-
source =
|
|
9735
|
+
source = fs11.readFileSync(file, "utf-8");
|
|
8683
9736
|
} catch {
|
|
8684
9737
|
return false;
|
|
8685
9738
|
}
|
|
@@ -8714,12 +9767,12 @@ async function resolveSheet(sheet, text, dirs, fetchMap) {
|
|
|
8714
9767
|
if (!file2) return null;
|
|
8715
9768
|
const index = /[?&]index=(\d+)/.exec(sheet.devId);
|
|
8716
9769
|
const lineOffset2 = /\.(?:vue|svelte|astro)$/i.test(file2) && index ? styleBlockLine(file2, Number(index[1])) : 0;
|
|
8717
|
-
return { rules, lookup: null, file: file2, lineOffset: lineOffset2, sources: [], exact:
|
|
9770
|
+
return { rules, lookup: null, file: file2, lineOffset: lineOffset2, sources: [], exact: sameLines2(text, rules, file2, lineOffset2) };
|
|
8718
9771
|
}
|
|
8719
9772
|
const file = fileForStyleUrl(sheet.sourceURL, dirs);
|
|
8720
9773
|
if (!file) return null;
|
|
8721
9774
|
const lineOffset = sheet.isInline ? sheet.startLine : 0;
|
|
8722
|
-
return { rules, lookup: null, file, lineOffset, sources: [], exact:
|
|
9775
|
+
return { rules, lookup: null, file, lineOffset, sources: [], exact: sameLines2(text, rules, file, lineOffset) };
|
|
8723
9776
|
}
|
|
8724
9777
|
function cachedResolution(sheet, text, dirs, fetchMap) {
|
|
8725
9778
|
const version = crypto4.createHash("sha1").update(text).digest("hex");
|
|
@@ -8769,21 +9822,21 @@ async function resolveCssCoverage(sheets, roots, fetchMap) {
|
|
|
8769
9822
|
|
|
8770
9823
|
// src/internal/capture/resource-ledger.ts
|
|
8771
9824
|
var childProcess = __toESM(require("child_process"));
|
|
8772
|
-
var
|
|
9825
|
+
var fs13 = __toESM(require("fs"));
|
|
8773
9826
|
var net3 = __toESM(require("net"));
|
|
8774
|
-
var
|
|
8775
|
-
var
|
|
9827
|
+
var path16 = __toESM(require("path"));
|
|
9828
|
+
var import_node_module3 = require("module");
|
|
8776
9829
|
|
|
8777
9830
|
// src/internal/capture/worker-metrics.ts
|
|
8778
9831
|
var import_node_perf_hooks = require("perf_hooks");
|
|
8779
9832
|
|
|
8780
9833
|
// src/internal/support/system-readers.ts
|
|
8781
|
-
var
|
|
8782
|
-
var
|
|
9834
|
+
var fs12 = __toESM(require("fs"));
|
|
9835
|
+
var path15 = __toESM(require("path"));
|
|
8783
9836
|
var CLK_TCK = 100;
|
|
8784
9837
|
function readText(file) {
|
|
8785
9838
|
try {
|
|
8786
|
-
return
|
|
9839
|
+
return fs12.readFileSync(file, "utf8");
|
|
8787
9840
|
} catch {
|
|
8788
9841
|
return null;
|
|
8789
9842
|
}
|
|
@@ -8822,7 +9875,7 @@ function parsePsiTotal(text) {
|
|
|
8822
9875
|
}
|
|
8823
9876
|
function roleOf(comm, cmdline) {
|
|
8824
9877
|
const command = `${comm} ${cmdline}`;
|
|
8825
|
-
if (/workerProcessEntry/.test(cmdline)) return "worker";
|
|
9878
|
+
if (/workerProcessEntry|[\\/]playwright[\\/]lib[\\/]common[\\/]process\.js/.test(cmdline)) return "worker";
|
|
8826
9879
|
if (/loaderProcessEntry/.test(cmdline)) return "runner";
|
|
8827
9880
|
if (/(^|[\s/])ffmpeg/.test(command)) return "ffmpeg";
|
|
8828
9881
|
const type = /--type=([\w-]+)/.exec(cmdline)?.[1];
|
|
@@ -8865,45 +9918,45 @@ var ProcFs = class {
|
|
|
8865
9918
|
}
|
|
8866
9919
|
/** Whether this looks like a Linux `/proc` at all. */
|
|
8867
9920
|
available() {
|
|
8868
|
-
return
|
|
9921
|
+
return fs12.existsSync(path15.join(this.root, "stat"));
|
|
8869
9922
|
}
|
|
8870
9923
|
/** Every process, keyed by pid. */
|
|
8871
9924
|
table() {
|
|
8872
9925
|
const out = /* @__PURE__ */ new Map();
|
|
8873
9926
|
let names;
|
|
8874
9927
|
try {
|
|
8875
|
-
names =
|
|
9928
|
+
names = fs12.readdirSync(this.root);
|
|
8876
9929
|
} catch {
|
|
8877
9930
|
return out;
|
|
8878
9931
|
}
|
|
8879
9932
|
for (const name of names) {
|
|
8880
9933
|
if (!/^\d+$/.test(name)) continue;
|
|
8881
|
-
const text = readText(
|
|
9934
|
+
const text = readText(path15.join(this.root, name, "stat"));
|
|
8882
9935
|
const stat = text ? parseProcStat(Number(name), text) : null;
|
|
8883
9936
|
if (stat) out.set(stat.pid, stat);
|
|
8884
9937
|
}
|
|
8885
9938
|
return out;
|
|
8886
9939
|
}
|
|
8887
9940
|
stat(pid) {
|
|
8888
|
-
const text = readText(
|
|
9941
|
+
const text = readText(path15.join(this.root, String(pid), "stat"));
|
|
8889
9942
|
return text ? parseProcStat(pid, text) : null;
|
|
8890
9943
|
}
|
|
8891
9944
|
runWaitMs(pid) {
|
|
8892
|
-
const text = readText(
|
|
9945
|
+
const text = readText(path15.join(this.root, String(pid), "schedstat"));
|
|
8893
9946
|
return text ? parseSchedstat(text) : null;
|
|
8894
9947
|
}
|
|
8895
9948
|
cmdline(pid) {
|
|
8896
|
-
return (readText(
|
|
9949
|
+
return (readText(path15.join(this.root, String(pid), "cmdline")) ?? "").split("\0").join(" ").trim();
|
|
8897
9950
|
}
|
|
8898
9951
|
/** A field of `/proc/<pid>/status`, in kB: `VmRSS`, `VmHWM`. */
|
|
8899
9952
|
statusKb(pid, key) {
|
|
8900
|
-
const text = readText(
|
|
9953
|
+
const text = readText(path15.join(this.root, String(pid), "status"));
|
|
8901
9954
|
return text ? parseKbField(text, key) : null;
|
|
8902
9955
|
}
|
|
8903
9956
|
/** Proportional set size: shared pages split between the processes sharing them, so a sum counts them once. */
|
|
8904
9957
|
async pssKb(pid) {
|
|
8905
9958
|
try {
|
|
8906
|
-
return parseKbField(await
|
|
9959
|
+
return parseKbField(await fs12.promises.readFile(path15.join(this.root, String(pid), "smaps_rollup"), "utf8"), "Pss");
|
|
8907
9960
|
} catch {
|
|
8908
9961
|
return null;
|
|
8909
9962
|
}
|
|
@@ -8911,29 +9964,29 @@ var ProcFs = class {
|
|
|
8911
9964
|
/** Reset a process's peak RSS (`VmHWM`) to its current RSS. */
|
|
8912
9965
|
resetPeakRss(pid) {
|
|
8913
9966
|
try {
|
|
8914
|
-
|
|
9967
|
+
fs12.writeFileSync(path15.join(this.root, String(pid), "clear_refs"), "5");
|
|
8915
9968
|
return true;
|
|
8916
9969
|
} catch {
|
|
8917
9970
|
return false;
|
|
8918
9971
|
}
|
|
8919
9972
|
}
|
|
8920
9973
|
cpuTicks() {
|
|
8921
|
-
const text = readText(
|
|
9974
|
+
const text = readText(path15.join(this.root, "stat"));
|
|
8922
9975
|
return text ? parseCpuTicks(text) : null;
|
|
8923
9976
|
}
|
|
8924
9977
|
/** Total stall time of a resource's pressure file, in µs. */
|
|
8925
9978
|
pressureUs(resource) {
|
|
8926
|
-
const text = readText(
|
|
9979
|
+
const text = readText(path15.join(this.root, "pressure", resource));
|
|
8927
9980
|
return text ? parsePsiTotal(text) : null;
|
|
8928
9981
|
}
|
|
8929
9982
|
memAvailableKb() {
|
|
8930
|
-
const text = readText(
|
|
9983
|
+
const text = readText(path15.join(this.root, "meminfo"));
|
|
8931
9984
|
return text ? parseKbField(text, "MemAvailable") : null;
|
|
8932
9985
|
}
|
|
8933
9986
|
/** Open file descriptors of a process. */
|
|
8934
9987
|
fdCount(pid) {
|
|
8935
9988
|
try {
|
|
8936
|
-
return
|
|
9989
|
+
return fs12.readdirSync(path15.join(this.root, String(pid), "fd")).length;
|
|
8937
9990
|
} catch {
|
|
8938
9991
|
return null;
|
|
8939
9992
|
}
|
|
@@ -9196,11 +10249,21 @@ function runnableOf(info) {
|
|
|
9196
10249
|
function fallbackRunnable(window) {
|
|
9197
10250
|
return { phase: window ? "test" : "worker", fixture: null };
|
|
9198
10251
|
}
|
|
10252
|
+
var GLOBALS_FILES = [
|
|
10253
|
+
["lib", "globals.js"],
|
|
10254
|
+
["lib", "common", "globals.js"]
|
|
10255
|
+
];
|
|
10256
|
+
function playwrightGlobalsFile() {
|
|
10257
|
+
const packageJson = resolvePlaywrightPackageJson();
|
|
10258
|
+
if (!packageJson) return null;
|
|
10259
|
+
const root = path16.dirname(packageJson);
|
|
10260
|
+
return GLOBALS_FILES.map((parts) => path16.join(root, ...parts)).find((file) => fs13.existsSync(file)) ?? null;
|
|
10261
|
+
}
|
|
9199
10262
|
function playwrightTestInfoSource() {
|
|
9200
10263
|
try {
|
|
9201
|
-
const
|
|
9202
|
-
if (!
|
|
9203
|
-
const globals = (0,
|
|
10264
|
+
const file = playwrightGlobalsFile();
|
|
10265
|
+
if (!file) return () => null;
|
|
10266
|
+
const globals = (0, import_node_module3.createRequire)(file)(file);
|
|
9204
10267
|
const read = globals.currentTestInfo;
|
|
9205
10268
|
if (typeof read !== "function") return () => null;
|
|
9206
10269
|
return () => {
|
|
@@ -9229,7 +10292,7 @@ function countHandles(types = TRACKED_HANDLE_TYPES) {
|
|
|
9229
10292
|
async function settledHandles(start, types = TRACKED_HANDLE_TYPES) {
|
|
9230
10293
|
const end = countHandles(types);
|
|
9231
10294
|
if (!Object.entries(end).some(([type, n]) => n > (start[type] ?? 0))) return end;
|
|
9232
|
-
await new Promise((
|
|
10295
|
+
await new Promise((resolve6) => setTimeout(resolve6, 0));
|
|
9233
10296
|
return countHandles(types);
|
|
9234
10297
|
}
|
|
9235
10298
|
function deepUserSite() {
|
|
@@ -9243,7 +10306,7 @@ function deepUserSite() {
|
|
|
9243
10306
|
}
|
|
9244
10307
|
var SHELLS = /^(?:sh|bash|dash|zsh|cmd|cmd\.exe|powershell|powershell\.exe|pwsh)$/i;
|
|
9245
10308
|
function commandOf(spawnfile, spawnargs) {
|
|
9246
|
-
const name = (file) =>
|
|
10309
|
+
const name = (file) => path16.basename(file.replace(/\\/g, "/").replace(/^["']|["']$/g, ""));
|
|
9247
10310
|
const program = typeof spawnfile === "string" ? name(spawnfile) : "";
|
|
9248
10311
|
const args = Array.isArray(spawnargs) ? spawnargs.map(String) : [];
|
|
9249
10312
|
const script = args.findIndex((arg) => arg === "-c" || /^\/c$/i.test(arg));
|
|
@@ -9257,8 +10320,8 @@ function settleWithin(promise, ms) {
|
|
|
9257
10320
|
let timer;
|
|
9258
10321
|
return Promise.race([
|
|
9259
10322
|
promise.catch(() => void 0),
|
|
9260
|
-
new Promise((
|
|
9261
|
-
timer = setTimeout(
|
|
10323
|
+
new Promise((resolve6) => {
|
|
10324
|
+
timer = setTimeout(resolve6, ms);
|
|
9262
10325
|
})
|
|
9263
10326
|
]).finally(() => clearTimeout(timer));
|
|
9264
10327
|
}
|
|
@@ -9378,10 +10441,10 @@ var ResourceLedger = class {
|
|
|
9378
10441
|
trackHandle(object, entry) {
|
|
9379
10442
|
const info = this.testInfoSource();
|
|
9380
10443
|
const { phase, fixture } = info ? runnableOf(info) : fallbackRunnable(this.window);
|
|
9381
|
-
const
|
|
9382
|
-
this.handles.add(
|
|
9383
|
-
this.byHandle.set(object,
|
|
9384
|
-
return
|
|
10444
|
+
const tracked2 = { ...entry, phase, fixture, test: testRef2(info) ?? this.window };
|
|
10445
|
+
this.handles.add(tracked2);
|
|
10446
|
+
this.byHandle.set(object, tracked2);
|
|
10447
|
+
return tracked2;
|
|
9385
10448
|
}
|
|
9386
10449
|
onServer(server, site) {
|
|
9387
10450
|
const known = this.byHandle.get(server);
|
|
@@ -9424,7 +10487,7 @@ var ResourceLedger = class {
|
|
|
9424
10487
|
const own = () => [...this.handles].filter(
|
|
9425
10488
|
(entry) => entry.test?.id === testId && !entry.fixture?.worker && (entry.phase === "test" || entry.phase === "beforeEach" || entry.phase === "afterEach")
|
|
9426
10489
|
);
|
|
9427
|
-
if (own().some((entry) => entry.pending?.())) await new Promise((
|
|
10490
|
+
if (own().some((entry) => entry.pending?.())) await new Promise((resolve6) => setTimeout(resolve6, 0));
|
|
9428
10491
|
return own().filter((entry) => entry.running()).map((entry) => ({ type: entry.type, site: entry.site, ...entry.command ? { command: entry.command } : {} }));
|
|
9429
10492
|
}
|
|
9430
10493
|
/** Mark a page used: a locator was built on it. */
|
|
@@ -9492,7 +10555,7 @@ var ResourceLedger = class {
|
|
|
9492
10555
|
const census = this.census(null);
|
|
9493
10556
|
if (this.resultsFile) {
|
|
9494
10557
|
try {
|
|
9495
|
-
|
|
10558
|
+
fs13.appendFileSync(this.resultsFile, `${JSON.stringify(census)}
|
|
9496
10559
|
`);
|
|
9497
10560
|
} catch {
|
|
9498
10561
|
}
|
|
@@ -10134,7 +11197,8 @@ function createSink() {
|
|
|
10134
11197
|
locatorPages: new LocatorPageLog(),
|
|
10135
11198
|
codeReach: { sessions: /* @__PURE__ */ new Map(), files: /* @__PURE__ */ new Map(), known: /* @__PURE__ */ new Map(), cssFiles: /* @__PURE__ */ new Map(), cssKnown: /* @__PURE__ */ new Map() },
|
|
10136
11199
|
serverCoverage: /* @__PURE__ */ new Map(),
|
|
10137
|
-
baseURL: null
|
|
11200
|
+
baseURL: null,
|
|
11201
|
+
headless: void 0
|
|
10138
11202
|
};
|
|
10139
11203
|
}
|
|
10140
11204
|
var MAX_CODE_REACH_PAGES = 16;
|
|
@@ -10159,7 +11223,7 @@ function maybeStartCodeReach(page, alreadyOpen = false) {
|
|
|
10159
11223
|
async function startCodeReachOnOpenPages() {
|
|
10160
11224
|
if (!pageCoverageInWorker() || OPEN_PAGES.size === 0) return;
|
|
10161
11225
|
const started = Promise.all([...OPEN_PAGES].map((page) => maybeStartCodeReach(page, true)));
|
|
10162
|
-
await Promise.race([started, new Promise((
|
|
11226
|
+
await Promise.race([started, new Promise((resolve6) => setTimeout(resolve6, CODE_REACH_START_WAIT_MS).unref())]);
|
|
10163
11227
|
}
|
|
10164
11228
|
function configuredCodeReachRoots() {
|
|
10165
11229
|
const raw = process.env.PIWI_CODE_REACH_ROOTS?.trim();
|
|
@@ -10173,7 +11237,7 @@ function configuredCodeReachRoots() {
|
|
|
10173
11237
|
}
|
|
10174
11238
|
} catch {
|
|
10175
11239
|
}
|
|
10176
|
-
return raw.split(
|
|
11240
|
+
return raw.split(path17.delimiter).filter(Boolean);
|
|
10177
11241
|
}
|
|
10178
11242
|
async function stopSinkCodeReach(sink, closing) {
|
|
10179
11243
|
const stopping = [];
|
|
@@ -10228,8 +11292,8 @@ async function drainPendingProbes(capMs) {
|
|
|
10228
11292
|
let cap;
|
|
10229
11293
|
await Promise.race([
|
|
10230
11294
|
Promise.allSettled(PENDING_PROBES),
|
|
10231
|
-
new Promise((
|
|
10232
|
-
cap = setTimeout(
|
|
11295
|
+
new Promise((resolve6) => {
|
|
11296
|
+
cap = setTimeout(resolve6, capMs);
|
|
10233
11297
|
})
|
|
10234
11298
|
]);
|
|
10235
11299
|
clearTimeout(cap);
|
|
@@ -10288,10 +11352,10 @@ async function readWebVitals(page) {
|
|
|
10288
11352
|
const key = entry.name.replace(/-([a-z])/g, (_, l) => l.toUpperCase());
|
|
10289
11353
|
paint[key] = Math.round(entry.startTime);
|
|
10290
11354
|
}
|
|
10291
|
-
const readBuffered = (type, extra) => new Promise((
|
|
11355
|
+
const readBuffered = (type, extra) => new Promise((resolve6) => {
|
|
10292
11356
|
try {
|
|
10293
11357
|
const PO = globalThis.PerformanceObserver;
|
|
10294
|
-
if (!PO || !(PO.supportedEntryTypes || []).includes(type)) return
|
|
11358
|
+
if (!PO || !(PO.supportedEntryTypes || []).includes(type)) return resolve6(null);
|
|
10295
11359
|
const out = [];
|
|
10296
11360
|
const po = new PO((list) => out.push(...list.getEntries()));
|
|
10297
11361
|
po.observe({ type, buffered: true, ...extra });
|
|
@@ -10301,10 +11365,10 @@ async function readWebVitals(page) {
|
|
|
10301
11365
|
po.disconnect();
|
|
10302
11366
|
} catch {
|
|
10303
11367
|
}
|
|
10304
|
-
|
|
11368
|
+
resolve6(out);
|
|
10305
11369
|
}, 0);
|
|
10306
11370
|
} catch {
|
|
10307
|
-
|
|
11371
|
+
resolve6(null);
|
|
10308
11372
|
}
|
|
10309
11373
|
});
|
|
10310
11374
|
const [lcpRaw, shiftRaw, eventRaw, firstInputRaw] = await Promise.all([
|
|
@@ -10421,12 +11485,12 @@ var PAGE_INVENTORY_IN_PAGE_CAP = 2e3;
|
|
|
10421
11485
|
var PAGE_INVENTORY_EVAL_TIMEOUT_MS = 2e3;
|
|
10422
11486
|
var MAX_PAGE_INVENTORY_WINDOWS = 64;
|
|
10423
11487
|
function withTimeout(promise, ms) {
|
|
10424
|
-
return new Promise((
|
|
11488
|
+
return new Promise((resolve6, reject) => {
|
|
10425
11489
|
const timer = setTimeout(() => reject(new Error("page inventory read timed out")), ms);
|
|
10426
11490
|
promise.then(
|
|
10427
11491
|
(value) => {
|
|
10428
11492
|
clearTimeout(timer);
|
|
10429
|
-
|
|
11493
|
+
resolve6(value);
|
|
10430
11494
|
},
|
|
10431
11495
|
(error) => {
|
|
10432
11496
|
clearTimeout(timer);
|
|
@@ -10545,6 +11609,20 @@ var PATCHED_BROWSERS = /* @__PURE__ */ new WeakSet();
|
|
|
10545
11609
|
var CHAIN_METHOD_SET = new Set(CHAIN_METHODS);
|
|
10546
11610
|
var ACTION_METHOD_SET = new Set(ACTION_METHODS);
|
|
10547
11611
|
var LOCATOR_METHOD_SET = new Set(LOCATOR_METHODS);
|
|
11612
|
+
var EARLY_READ_ACTIONS = /* @__PURE__ */ new Set([
|
|
11613
|
+
"click",
|
|
11614
|
+
"dblclick",
|
|
11615
|
+
"tap",
|
|
11616
|
+
"press",
|
|
11617
|
+
"pressSequentially",
|
|
11618
|
+
"type",
|
|
11619
|
+
"check",
|
|
11620
|
+
"uncheck",
|
|
11621
|
+
"selectOption",
|
|
11622
|
+
"dispatchEvent"
|
|
11623
|
+
]);
|
|
11624
|
+
var CAPTURE_DEADLINE_MS = 500;
|
|
11625
|
+
var EARLY_READ_TRUSTED_MS = 250;
|
|
10548
11626
|
var CAPTURED_ATTRS_ARG = {
|
|
10549
11627
|
keep: [...CAPTURED_ATTRIBUTES],
|
|
10550
11628
|
tagRoles: TAG_TO_ROLE,
|
|
@@ -10563,15 +11641,36 @@ var PROBE_INIT_SCRIPT = `(() => {
|
|
|
10563
11641
|
globalThis[${JSON.stringify(PROBE_GLOBAL)}] = (el) => probe(el, arg);
|
|
10564
11642
|
})();`;
|
|
10565
11643
|
var PROBE_UNSEEDED_PAGES = /* @__PURE__ */ new WeakSet();
|
|
10566
|
-
|
|
10567
|
-
|
|
11644
|
+
var PAGE_NAVIGATIONS = /* @__PURE__ */ new WeakMap();
|
|
11645
|
+
function readyProbeWorld(page) {
|
|
11646
|
+
if (!currentSink) return;
|
|
11647
|
+
try {
|
|
11648
|
+
const ready = internalCall(
|
|
11649
|
+
page,
|
|
11650
|
+
() => page.mainFrame().locator(":root").evaluate(() => 0, void 0, { timeout: CAPTURE_DEADLINE_MS })
|
|
11651
|
+
).then(
|
|
11652
|
+
() => void 0,
|
|
11653
|
+
() => void 0
|
|
11654
|
+
);
|
|
11655
|
+
PENDING_PROBES.add(ready);
|
|
11656
|
+
void ready.then(() => PENDING_PROBES.delete(ready));
|
|
11657
|
+
} catch {
|
|
11658
|
+
}
|
|
11659
|
+
}
|
|
11660
|
+
function probeElement(page, target, timeout) {
|
|
11661
|
+
const options = timeout != null ? { timeout } : void 0;
|
|
11662
|
+
const shipSource = () => target.evaluate(probeElementAttrs, CAPTURED_ATTRS_ARG, options);
|
|
10568
11663
|
if (!page || PROBE_UNSEEDED_PAGES.has(page)) return internalCall(page, shipSource);
|
|
10569
11664
|
return internalCall(
|
|
10570
11665
|
page,
|
|
10571
|
-
() => target.evaluate(
|
|
10572
|
-
|
|
10573
|
-
|
|
10574
|
-
|
|
11666
|
+
() => target.evaluate(
|
|
11667
|
+
(el, name) => {
|
|
11668
|
+
const seeded = globalThis[name];
|
|
11669
|
+
return typeof seeded === "function" ? seeded(el) : null;
|
|
11670
|
+
},
|
|
11671
|
+
PROBE_GLOBAL,
|
|
11672
|
+
options
|
|
11673
|
+
).then((attrs) => {
|
|
10575
11674
|
if (attrs) return attrs;
|
|
10576
11675
|
PROBE_UNSEEDED_PAGES.add(page);
|
|
10577
11676
|
return shipSource();
|
|
@@ -10606,54 +11705,60 @@ async function ariaSnapshotJSONBestEffort(target, timeout) {
|
|
|
10606
11705
|
return null;
|
|
10607
11706
|
}
|
|
10608
11707
|
}
|
|
10609
|
-
function
|
|
10610
|
-
|
|
10611
|
-
|
|
10612
|
-
|
|
10613
|
-
|
|
10614
|
-
|
|
10615
|
-
|
|
10616
|
-
|
|
10617
|
-
|
|
10618
|
-
|
|
10619
|
-
|
|
10620
|
-
|
|
10621
|
-
|
|
10622
|
-
|
|
10623
|
-
|
|
10624
|
-
|
|
10625
|
-
|
|
10626
|
-
|
|
10627
|
-
|
|
10628
|
-
|
|
10629
|
-
|
|
10630
|
-
|
|
10631
|
-
|
|
10632
|
-
|
|
10633
|
-
|
|
10634
|
-
|
|
10635
|
-
|
|
10636
|
-
|
|
10637
|
-
|
|
10638
|
-
|
|
10639
|
-
|
|
10640
|
-
|
|
10641
|
-
|
|
10642
|
-
|
|
10643
|
-
|
|
10644
|
-
|
|
10645
|
-
|
|
10646
|
-
|
|
10647
|
-
|
|
10648
|
-
|
|
10649
|
-
|
|
10650
|
-
|
|
10651
|
-
|
|
10652
|
-
|
|
10653
|
-
|
|
10654
|
-
|
|
11708
|
+
async function readElementSnapshot(sink, page, target, callerLocation, used) {
|
|
11709
|
+
let deadline;
|
|
11710
|
+
try {
|
|
11711
|
+
const probe = probeElement(page, target, CAPTURE_DEADLINE_MS);
|
|
11712
|
+
const settledProbe = probe.then(
|
|
11713
|
+
() => void 0,
|
|
11714
|
+
() => void 0
|
|
11715
|
+
);
|
|
11716
|
+
PENDING_PROBES.add(settledProbe);
|
|
11717
|
+
settledProbe.then(() => PENDING_PROBES.delete(settledProbe));
|
|
11718
|
+
sink.capturePromises.push(settledProbe);
|
|
11719
|
+
const attrs = await Promise.race([
|
|
11720
|
+
probe,
|
|
11721
|
+
new Promise((_, reject) => {
|
|
11722
|
+
deadline = setTimeout(() => reject(new Error("locator capture timeout")), CAPTURE_DEADLINE_MS);
|
|
11723
|
+
})
|
|
11724
|
+
]);
|
|
11725
|
+
const role = resolveAriaRole({ ...attrs, accessibleName: null });
|
|
11726
|
+
const isFormField = FORM_FIELD_TAGS.has(attrs.tagName);
|
|
11727
|
+
const exactName = exactAccessibleName(attrs, role);
|
|
11728
|
+
const aria = exactName === null && (role || isFormField) ? await internalCall(page, () => ariaSnapshotBestEffort(target, CAPTURE_DEADLINE_MS)) : null;
|
|
11729
|
+
const accessibleName = exactName ?? (extractAccessibleName(aria) || approximateAccessibleName({ ...attrs, accessibleName: null }));
|
|
11730
|
+
return {
|
|
11731
|
+
location: callerLocation,
|
|
11732
|
+
used,
|
|
11733
|
+
// hasLabel/selectorCounts inform alternative generation only —
|
|
11734
|
+
// keep the stored element to the wire shape. rolePosition and
|
|
11735
|
+
// ancestors ARE wire fields: the server's renamed-element match
|
|
11736
|
+
// uses them at heal time.
|
|
11737
|
+
element: {
|
|
11738
|
+
tagName: attrs.tagName,
|
|
11739
|
+
attributes: attrs.attributes,
|
|
11740
|
+
textContent: attrs.textContent,
|
|
11741
|
+
accessibleName,
|
|
11742
|
+
center: attrs.center,
|
|
11743
|
+
...attrs.rolePosition ? { rolePosition: attrs.rolePosition } : {},
|
|
11744
|
+
...attrs.ancestors && attrs.ancestors.length > 0 ? { ancestors: attrs.ancestors } : {}
|
|
11745
|
+
},
|
|
11746
|
+
alternatives: generateAlternatives({ ...attrs, accessibleName })
|
|
11747
|
+
};
|
|
11748
|
+
} catch {
|
|
11749
|
+
return null;
|
|
11750
|
+
} finally {
|
|
11751
|
+
clearTimeout(deadline);
|
|
11752
|
+
}
|
|
11753
|
+
}
|
|
11754
|
+
function startElementCapture(sink, page, target, seq, callerLocation, used, early = null, earlyFirst = false) {
|
|
11755
|
+
const capture = (async () => {
|
|
11756
|
+
const readNow = () => readElementSnapshot(sink, page, target, callerLocation, used);
|
|
11757
|
+
const snapshot = earlyFirst ? await early ?? await readNow() : await readNow() ?? await early;
|
|
11758
|
+
if (snapshot) sink.capturedLocators[seq] = snapshot;
|
|
11759
|
+
else if (callerLocation) sink.probedLocations.delete(callerLocation);
|
|
10655
11760
|
})();
|
|
10656
|
-
sink.capturePromises.push(
|
|
11761
|
+
sink.capturePromises.push(capture);
|
|
10657
11762
|
}
|
|
10658
11763
|
function recordLocatorPage(sink, page, locator, location, arrival) {
|
|
10659
11764
|
try {
|
|
@@ -10665,7 +11770,7 @@ function knowLocatorCall(sink, page, target, location, origin) {
|
|
|
10665
11770
|
if (!sink?.pause || !location) return null;
|
|
10666
11771
|
let settle = () => {
|
|
10667
11772
|
};
|
|
10668
|
-
const settled = new Promise((
|
|
11773
|
+
const settled = new Promise((resolve6) => settle = resolve6);
|
|
10669
11774
|
const call = { page, target, location, origin, picked: false, endable: false, settled, settle };
|
|
10670
11775
|
sink.locatorCalls.set(callSiteLine(location), call);
|
|
10671
11776
|
return call;
|
|
@@ -10773,7 +11878,7 @@ function endedWaiting(sink, call) {
|
|
|
10773
11878
|
return "finish";
|
|
10774
11879
|
}
|
|
10775
11880
|
function stopSite(sink, location) {
|
|
10776
|
-
if (
|
|
11881
|
+
if (path17.dirname(path17.resolve(location.file)) !== __dirname) return stopCallSite(location);
|
|
10777
11882
|
return inFlightCallSite([...sink.locatorCalls.values()].map((call) => call.location));
|
|
10778
11883
|
}
|
|
10779
11884
|
function stopBarPage(sink, context, of) {
|
|
@@ -10788,7 +11893,7 @@ async function showStop(sink, context, title, location, reason, call, of, failur
|
|
|
10788
11893
|
const page = stopBarPage(sink, context, of);
|
|
10789
11894
|
const guessed = !("page" in of);
|
|
10790
11895
|
if (!testInfo) return void 0;
|
|
10791
|
-
const bar = !!page && testInfo.
|
|
11896
|
+
const bar = !!page && pauseGateFromTestInfo(testInfo, sink.headless).headless === false;
|
|
10792
11897
|
const place = runChannelOpen() ? callSitePlace(location) : null;
|
|
10793
11898
|
if (!bar && !place) return void 0;
|
|
10794
11899
|
const failing = failure?.failed ?? null;
|
|
@@ -10896,6 +12001,7 @@ async function showStop(sink, context, title, location, reason, call, of, failur
|
|
|
10896
12001
|
}
|
|
10897
12002
|
return "resume";
|
|
10898
12003
|
}
|
|
12004
|
+
const stoppedOn = call?.page === page ? call : null;
|
|
10899
12005
|
return internalCall(
|
|
10900
12006
|
page,
|
|
10901
12007
|
() => pauseHere(
|
|
@@ -10904,7 +12010,14 @@ async function showStop(sink, context, title, location, reason, call, of, failur
|
|
|
10904
12010
|
location,
|
|
10905
12011
|
call: title,
|
|
10906
12012
|
attempt: testInfo.retry > 0 ? testInfo.retry + 1 : null,
|
|
10907
|
-
target:
|
|
12013
|
+
target: stoppedOn ? {
|
|
12014
|
+
highlight: () => highlightAtStop(
|
|
12015
|
+
page,
|
|
12016
|
+
stoppedOn.target,
|
|
12017
|
+
"call",
|
|
12018
|
+
locatorSource(stoppedOn.target, stoppedOn.origin.method, stoppedOn.origin.args)
|
|
12019
|
+
)
|
|
12020
|
+
} : null,
|
|
10908
12021
|
...failure ? { failure: true } : sink.functionEnded ? {} : { end: true }
|
|
10909
12022
|
},
|
|
10910
12023
|
probe,
|
|
@@ -10951,7 +12064,7 @@ function endTest(sink, call, title, location, reason) {
|
|
|
10951
12064
|
return "end";
|
|
10952
12065
|
}
|
|
10953
12066
|
const said = hideSecretsInText(
|
|
10954
|
-
`${title} at ${line} runs before the test ends: Piwi holds no locator call there. The test is skipped at its next locator call, or as it ends.`
|
|
12067
|
+
call && !takesEndSignal(sink.testInfo.config?.version) ? `${title} at ${line} runs before the test ends: a locator call is given up from Playwright 1.62 on. The test is skipped as the call returns.` : `${title} at ${line} runs before the test ends: Piwi holds no locator call there. The test is skipped at its next locator call, or as it ends.`
|
|
10955
12068
|
);
|
|
10956
12069
|
console.log(`[piwi] ${said}`);
|
|
10957
12070
|
tellHub({ type: "notice", message: said });
|
|
@@ -11110,8 +12223,8 @@ function errorPlace(error, windows = process.platform === "win32") {
|
|
|
11110
12223
|
for (const line of typeof stack === "string" ? stack.split("\n") : []) {
|
|
11111
12224
|
const frame = /^\s*at\s+(?:.*?\s+\()?(.+?):(\d+):(\d+)\)?$/.exec(line);
|
|
11112
12225
|
const file = frame ? framePath(frame[1], windows) : null;
|
|
11113
|
-
if (!frame || !file || !(windows ?
|
|
11114
|
-
if (/[\\/]node_modules[\\/]/.test(file) ||
|
|
12226
|
+
if (!frame || !file || !(windows ? path17.win32 : path17.posix).isAbsolute(file)) continue;
|
|
12227
|
+
if (/[\\/]node_modules[\\/]/.test(file) || path17.dirname(path17.resolve(file)) === __dirname) continue;
|
|
11115
12228
|
return { file, line: Number(frame[2]), column: Number(frame[3]) };
|
|
11116
12229
|
}
|
|
11117
12230
|
return null;
|
|
@@ -11122,7 +12235,7 @@ function callInProgress(testInfo) {
|
|
|
11122
12235
|
if (!Array.isArray(steps)) return;
|
|
11123
12236
|
for (const step of steps) {
|
|
11124
12237
|
if (step.endWallTime) continue;
|
|
11125
|
-
const at = step.stack?.[0];
|
|
12238
|
+
const at = step.stack?.[0] ?? step.location;
|
|
11126
12239
|
const called = step.category === "pw:api" || step.category === "expect" || step.category === "test.step";
|
|
11127
12240
|
if (called && typeof at?.file === "string" && typeof at.line === "number") {
|
|
11128
12241
|
found = {
|
|
@@ -11142,13 +12255,13 @@ function stepInProgress(testInfo) {
|
|
|
11142
12255
|
function runningCall(testInfo) {
|
|
11143
12256
|
const call = callInProgress(testInfo);
|
|
11144
12257
|
if (!call?.title) return null;
|
|
11145
|
-
const file =
|
|
12258
|
+
const file = path17.relative(process.cwd(), call.place.file).split(path17.sep).join("/");
|
|
11146
12259
|
return { title: call.title, file, line: call.place.line };
|
|
11147
12260
|
}
|
|
11148
12261
|
function abortion(signal) {
|
|
11149
|
-
return new Promise((
|
|
11150
|
-
if (signal.aborted)
|
|
11151
|
-
else signal.addEventListener("abort", () =>
|
|
12262
|
+
return new Promise((resolve6) => {
|
|
12263
|
+
if (signal.aborted) resolve6();
|
|
12264
|
+
else signal.addEventListener("abort", () => resolve6(), { once: true });
|
|
11152
12265
|
});
|
|
11153
12266
|
}
|
|
11154
12267
|
var END_STOP_MS = 2e3;
|
|
@@ -11183,7 +12296,7 @@ function releaseCall(sink, settled) {
|
|
|
11183
12296
|
}
|
|
11184
12297
|
function releasedRequestsEnded(sink) {
|
|
11185
12298
|
if (sink.releasedRequests.size === 0) return Promise.resolve();
|
|
11186
|
-
return new Promise((
|
|
12299
|
+
return new Promise((resolve6) => sink.onReleasedRequestsEnded = resolve6);
|
|
11187
12300
|
}
|
|
11188
12301
|
async function awaitReleasedCalls(sink) {
|
|
11189
12302
|
if (sink.released.length === 0) return;
|
|
@@ -11196,13 +12309,13 @@ async function awaitReleasedCalls(sink) {
|
|
|
11196
12309
|
}
|
|
11197
12310
|
const quiet = now - sink.releasedQuietSince;
|
|
11198
12311
|
if (quiet >= RELEASED_QUIET_MS) return;
|
|
11199
|
-
await new Promise((
|
|
12312
|
+
await new Promise((resolve6) => setTimeout(resolve6, Math.min(RELEASED_QUIET_MS - quiet, deadline - now)));
|
|
11200
12313
|
}
|
|
11201
12314
|
}
|
|
11202
12315
|
async function settledWithin(pending, ms) {
|
|
11203
12316
|
if (pending.length === 0) return;
|
|
11204
12317
|
let timer;
|
|
11205
|
-
await Promise.race([Promise.allSettled(pending), new Promise((
|
|
12318
|
+
await Promise.race([Promise.allSettled(pending), new Promise((resolve6) => timer = setTimeout(resolve6, ms))]);
|
|
11206
12319
|
clearTimeout(timer);
|
|
11207
12320
|
}
|
|
11208
12321
|
function endStopsWithTestFunction(testInfo, sink) {
|
|
@@ -11235,7 +12348,7 @@ async function pauseInPlace(page, playwrightPause, location) {
|
|
|
11235
12348
|
if (!sink || !testInfo || !location || !context || !contextDebugger(context)) {
|
|
11236
12349
|
return leaveToPlaywright(context, playwrightPause);
|
|
11237
12350
|
}
|
|
11238
|
-
if (!sink.pause && (!canRecordFromHere() || pauseSkipReason(pauseGateFromTestInfo(testInfo)))) {
|
|
12351
|
+
if (!sink.pause && (!canRecordFromHere() || pauseSkipReason(pauseGateFromTestInfo(testInfo, sink.headless)))) {
|
|
11239
12352
|
return leaveToPlaywright(context, playwrightPause);
|
|
11240
12353
|
}
|
|
11241
12354
|
sink.pause ??= pauseState(NO_BREAKPOINTS);
|
|
@@ -11303,7 +12416,7 @@ function wrapLocator(page, locator, originMethod, originArgs) {
|
|
|
11303
12416
|
const callerLocation = sink ? captureCallerLocation() : null;
|
|
11304
12417
|
if (sink) recordLocatorPage(sink, page, target, callerLocation, noteLocatorCall(page, EXPECT_METHOD, true));
|
|
11305
12418
|
const call = knowLocatorCall(sink, page, target, callerLocation, { method: originMethod, args: originArgs });
|
|
11306
|
-
const ending = sink?.pause && !sink.ended ? withEndSignal(EXPECT_METHOD, callArgs, sink.end.signal) : null;
|
|
12419
|
+
const ending = sink?.pause && !sink.ended && takesEndSignal(sink.testInfo?.config?.version) ? withEndSignal(EXPECT_METHOD, callArgs, sink.end.signal) : null;
|
|
11307
12420
|
if (call && ending) call.endable = true;
|
|
11308
12421
|
const expectArgs = ending ?? callArgs;
|
|
11309
12422
|
try {
|
|
@@ -11356,7 +12469,7 @@ function wrapLocator(page, locator, originMethod, originArgs) {
|
|
|
11356
12469
|
raw: `${originMethod}(${JSON.stringify(originArgs)})`
|
|
11357
12470
|
};
|
|
11358
12471
|
const call = knowLocatorCall(sink, page, target, callerLocation, used);
|
|
11359
|
-
const ending = sink.pause && !sink.ended ? withEndSignal(String(prop), callArgs, sink.end.signal) : null;
|
|
12472
|
+
const ending = sink.pause && !sink.ended && takesEndSignal(sink.testInfo?.config?.version) ? withEndSignal(String(prop), callArgs, sink.end.signal) : null;
|
|
11360
12473
|
if (call && ending) call.endable = true;
|
|
11361
12474
|
const alreadyProbed = callerLocation !== null && sink.probedLocations.has(callerLocation);
|
|
11362
12475
|
let seq = -1;
|
|
@@ -11369,7 +12482,10 @@ function wrapLocator(page, locator, originMethod, originArgs) {
|
|
|
11369
12482
|
alternatives: []
|
|
11370
12483
|
});
|
|
11371
12484
|
}
|
|
12485
|
+
const early = !alreadyProbed && EARLY_READ_ACTIONS.has(prop) ? readElementSnapshot(sink, page, target, callerLocation, used) : null;
|
|
11372
12486
|
let result;
|
|
12487
|
+
const startedAt = Date.now();
|
|
12488
|
+
const navigations = PAGE_NAVIGATIONS.get(page) ?? 0;
|
|
11373
12489
|
try {
|
|
11374
12490
|
result = await callMethod(target, prop, ending ?? callArgs);
|
|
11375
12491
|
} catch (error) {
|
|
@@ -11382,7 +12498,8 @@ function wrapLocator(page, locator, originMethod, originArgs) {
|
|
|
11382
12498
|
if (endsNow(sink)) throw skipOf(sink);
|
|
11383
12499
|
if (!alreadyProbed && !call?.picked) {
|
|
11384
12500
|
if (callerLocation) sink.probedLocations.add(callerLocation);
|
|
11385
|
-
|
|
12501
|
+
const earlyFirst = (PAGE_NAVIGATIONS.get(page) ?? 0) !== navigations || Date.now() - startedAt < EARLY_READ_TRUSTED_MS;
|
|
12502
|
+
startElementCapture(sink, page, target, seq, callerLocation, used, early, earlyFirst);
|
|
11386
12503
|
}
|
|
11387
12504
|
return result;
|
|
11388
12505
|
};
|
|
@@ -11444,7 +12561,7 @@ function instrumentPage(page) {
|
|
|
11444
12561
|
page.close = async (...args) => {
|
|
11445
12562
|
let closed = () => {
|
|
11446
12563
|
};
|
|
11447
|
-
CLOSING_PAGES.set(page, new Promise((
|
|
12564
|
+
CLOSING_PAGES.set(page, new Promise((resolve6) => closed = resolve6));
|
|
11448
12565
|
try {
|
|
11449
12566
|
await settleBeforeClose({ page });
|
|
11450
12567
|
return await originalClose(...args);
|
|
@@ -11472,10 +12589,14 @@ function instrumentPage(page) {
|
|
|
11472
12589
|
};
|
|
11473
12590
|
}
|
|
11474
12591
|
if (typeof page.on === "function") {
|
|
12592
|
+
page.on("domcontentloaded", () => readyProbeWorld(page));
|
|
11475
12593
|
page.on("framenavigated", (frame) => {
|
|
11476
12594
|
PROBE_UNSEEDED_PAGES.delete(page);
|
|
11477
12595
|
try {
|
|
11478
|
-
if (frame === page.mainFrame())
|
|
12596
|
+
if (frame === page.mainFrame()) {
|
|
12597
|
+
PAGE_NAVIGATIONS.set(page, (PAGE_NAVIGATIONS.get(page) ?? 0) + 1);
|
|
12598
|
+
noteNavigation(page);
|
|
12599
|
+
}
|
|
11479
12600
|
} catch {
|
|
11480
12601
|
}
|
|
11481
12602
|
});
|
|
@@ -11756,8 +12877,8 @@ async function flushSink(sink, testInfo) {
|
|
|
11756
12877
|
let drainDeadline;
|
|
11757
12878
|
await Promise.race([
|
|
11758
12879
|
Promise.allSettled(sink.capturePromises),
|
|
11759
|
-
new Promise((
|
|
11760
|
-
drainDeadline = setTimeout(
|
|
12880
|
+
new Promise((resolve6) => {
|
|
12881
|
+
drainDeadline = setTimeout(resolve6, 2e3);
|
|
11761
12882
|
})
|
|
11762
12883
|
]);
|
|
11763
12884
|
clearTimeout(drainDeadline);
|
|
@@ -11964,7 +13085,7 @@ var runControl = {
|
|
|
11964
13085
|
return;
|
|
11965
13086
|
}
|
|
11966
13087
|
if (!sink.pause) {
|
|
11967
|
-
const why = sink.testInfo ? pauseSkipReason({ ...pauseGateFromTestInfo(sink.testInfo), channel: true }) : null;
|
|
13088
|
+
const why = sink.testInfo ? pauseSkipReason({ ...pauseGateFromTestInfo(sink.testInfo, sink.headless), channel: true }) : null;
|
|
11968
13089
|
tellHub({ type: "notice", message: `This test does not stop${why ? `: ${why}` : ""}.` });
|
|
11969
13090
|
return;
|
|
11970
13091
|
}
|
|
@@ -11986,7 +13107,7 @@ var runControl = {
|
|
|
11986
13107
|
}
|
|
11987
13108
|
};
|
|
11988
13109
|
function runFile(file) {
|
|
11989
|
-
return
|
|
13110
|
+
return path17.relative(process.cwd(), file).split(path17.sep).join("/") || file;
|
|
11990
13111
|
}
|
|
11991
13112
|
function testBegin(testInfo, cannotPause) {
|
|
11992
13113
|
const title = testInfo.titlePath.slice(1);
|
|
@@ -12026,6 +13147,7 @@ var piwiFixtures = {
|
|
|
12026
13147
|
startResourceLedger(playwright, workerInfo.workerIndex);
|
|
12027
13148
|
observeApiRequests(playwright, apiCallRecorder);
|
|
12028
13149
|
openLiveView(workerInfo);
|
|
13150
|
+
if (liveViewOpen()) trackCalls(playwright);
|
|
12029
13151
|
if (await openRunChannel(workerInfo, runControl)) watchCalls(playwright);
|
|
12030
13152
|
try {
|
|
12031
13153
|
await use();
|
|
@@ -12063,17 +13185,18 @@ var piwiFixtures = {
|
|
|
12063
13185
|
// (attach the collected data) at teardown. Runs for every test without being
|
|
12064
13186
|
// requested, so suites that never destructure `page` are still captured.
|
|
12065
13187
|
piwiCapture: [
|
|
12066
|
-
async ({ screenshot, baseURL }, use, testInfo) => {
|
|
13188
|
+
async ({ screenshot, baseURL, headless }, use, testInfo) => {
|
|
12067
13189
|
boxCaptureFrames();
|
|
12068
13190
|
const conflict = labModeConflict();
|
|
12069
13191
|
if (conflict) throw new Error(conflict);
|
|
12070
13192
|
const sink = createSink();
|
|
12071
13193
|
sink.testInfo = testInfo;
|
|
12072
13194
|
sink.baseURL = baseURL ?? null;
|
|
13195
|
+
sink.headless = headless;
|
|
12073
13196
|
if (keepsFailureScreenshots(screenshot, testInfo.retry)) sink.failureScreenshot = screenshot;
|
|
12074
13197
|
const pauseSet = currentPauseSet();
|
|
12075
13198
|
const channel = runChannelOpen();
|
|
12076
|
-
const cannotPause = pauseSkipReason({ ...pauseGateFromTestInfo(testInfo), channel });
|
|
13199
|
+
const cannotPause = pauseSkipReason({ ...pauseGateFromTestInfo(testInfo, headless), channel });
|
|
12077
13200
|
if (pauseSet.size || channel) {
|
|
12078
13201
|
if (!cannotPause) sink.pause = pauseState(pauseSet, pauseNextTest);
|
|
12079
13202
|
else if (pauseSet.size) logPauseSkipOnce(cannotPause);
|
|
@@ -12105,7 +13228,7 @@ var piwiFixtures = {
|
|
|
12105
13228
|
});
|
|
12106
13229
|
activeResourceLedger()?.testStarted(testInfo);
|
|
12107
13230
|
currentSink = sink;
|
|
12108
|
-
liveViewTestBegan(testInfo.title, () => runningCall(testInfo));
|
|
13231
|
+
liveViewTestBegan(testInfo.title, () => runningCall(testInfo), mayPause(pauseGateFromTestInfo(testInfo)));
|
|
12109
13232
|
const showRunChannel = hideRunChannel();
|
|
12110
13233
|
try {
|
|
12111
13234
|
await armStops(OPEN_CONTEXTS);
|
|
@@ -12123,9 +13246,10 @@ var piwiFixtures = {
|
|
|
12123
13246
|
const applied = sink.probeInterception?.applied() ?? false;
|
|
12124
13247
|
const level = sink.probeItem.level ?? "client";
|
|
12125
13248
|
const handled = level === "server" && applied ? classifyProbeHandled({
|
|
12126
|
-
consoleErrors: sink.consoleEntries
|
|
13249
|
+
consoleErrors: probeConsoleErrors(sink.consoleEntries),
|
|
12127
13250
|
dialogs: sink.dialogs.length,
|
|
12128
13251
|
backendError: sink.probeInterception?.serverError() ?? false,
|
|
13252
|
+
faultIsTheError: faultIsTheError(sink.probeItem.fault),
|
|
12129
13253
|
pageErrors: sink.probePageErrors,
|
|
12130
13254
|
blankPage: sink.probeBlankPage
|
|
12131
13255
|
}) : "n/a";
|