@wdio/nightwatch-devtools 1.8.1 → 2.0.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 +70 -16
- package/dist/{bidi-WNZOXX5X.js → bidi-AZX3WRGJ.js} +4 -2
- package/dist/{bidi-WNZOXX5X.js.map → bidi-AZX3WRGJ.js.map} +1 -1
- package/dist/{chunk-DM4FCAIJ.js → chunk-TUK7ZLQI.js} +627 -88
- package/dist/chunk-TUK7ZLQI.js.map +1 -0
- package/dist/index.js +247 -59
- package/dist/index.js.map +1 -1
- package/package.json +12 -11
- package/dist/chunk-DM4FCAIJ.js.map +0 -1
|
@@ -452,6 +452,29 @@ function formatActionTitle(action, args, params, command) {
|
|
|
452
452
|
}
|
|
453
453
|
var FILL_METHODS = /* @__PURE__ */ new Set(["fill", "selectOption"]);
|
|
454
454
|
|
|
455
|
+
// ../shared/src/browser-schemes.ts
|
|
456
|
+
var BROWSER_INTERNAL_SCHEMES = /* @__PURE__ */ new Set([
|
|
457
|
+
"about",
|
|
458
|
+
"chrome",
|
|
459
|
+
"chrome-error",
|
|
460
|
+
"chrome-extension",
|
|
461
|
+
"chrome-native",
|
|
462
|
+
"chrome-search",
|
|
463
|
+
"chrome-untrusted",
|
|
464
|
+
"devtools",
|
|
465
|
+
"edge",
|
|
466
|
+
"moz-extension",
|
|
467
|
+
"safari-web-extension"
|
|
468
|
+
]);
|
|
469
|
+
var SCHEME = /^([a-z][a-z0-9+.-]*):/;
|
|
470
|
+
function schemeOf(url) {
|
|
471
|
+
return SCHEME.exec(url.toLowerCase())?.[1];
|
|
472
|
+
}
|
|
473
|
+
function isBrowserInternalUrl(url) {
|
|
474
|
+
const scheme = schemeOf(url);
|
|
475
|
+
return scheme !== void 0 && (BROWSER_INTERNAL_SCHEMES.has(scheme) || scheme.startsWith("chrome-"));
|
|
476
|
+
}
|
|
477
|
+
|
|
455
478
|
// ../shared/src/console.ts
|
|
456
479
|
var CONSOLE_METHODS = ["log", "info", "warn", "error"];
|
|
457
480
|
var ANSI_REGEX = /\x1b\[[?]?[0-9;]*[A-Za-z]/g;
|
|
@@ -1051,6 +1074,46 @@ function imageMime(base64) {
|
|
|
1051
1074
|
return base64.startsWith(PNG_BASE64_MAGIC) ? "image/png" : "image/jpeg";
|
|
1052
1075
|
}
|
|
1053
1076
|
|
|
1077
|
+
// ../shared/src/request-type.ts
|
|
1078
|
+
function getRequestType(url, mimeType) {
|
|
1079
|
+
const contentType = mimeType?.toLowerCase() ?? "";
|
|
1080
|
+
const urlLower = url.toLowerCase();
|
|
1081
|
+
if (contentType.includes("text/html")) {
|
|
1082
|
+
return "document";
|
|
1083
|
+
}
|
|
1084
|
+
if (contentType.includes("text/css")) {
|
|
1085
|
+
return "stylesheet";
|
|
1086
|
+
}
|
|
1087
|
+
if (contentType.includes("javascript") || contentType.includes("ecmascript")) {
|
|
1088
|
+
return "script";
|
|
1089
|
+
}
|
|
1090
|
+
if (contentType.includes("image/")) {
|
|
1091
|
+
return "image";
|
|
1092
|
+
}
|
|
1093
|
+
if (contentType.includes("font/") || contentType.includes("woff")) {
|
|
1094
|
+
return "font";
|
|
1095
|
+
}
|
|
1096
|
+
if (contentType.includes("application/json")) {
|
|
1097
|
+
return "fetch";
|
|
1098
|
+
}
|
|
1099
|
+
if (urlLower.endsWith(".html") || urlLower.endsWith(".htm")) {
|
|
1100
|
+
return "document";
|
|
1101
|
+
}
|
|
1102
|
+
if (urlLower.endsWith(".css")) {
|
|
1103
|
+
return "stylesheet";
|
|
1104
|
+
}
|
|
1105
|
+
if (urlLower.endsWith(".js") || urlLower.endsWith(".mjs")) {
|
|
1106
|
+
return "script";
|
|
1107
|
+
}
|
|
1108
|
+
if (/\.(png|jpg|jpeg|gif|svg|webp|ico)$/.test(urlLower)) {
|
|
1109
|
+
return "image";
|
|
1110
|
+
}
|
|
1111
|
+
if (/\.(woff|woff2|ttf|eot|otf)$/.test(urlLower)) {
|
|
1112
|
+
return "font";
|
|
1113
|
+
}
|
|
1114
|
+
return "xhr";
|
|
1115
|
+
}
|
|
1116
|
+
|
|
1054
1117
|
// ../shared/src/routes.ts
|
|
1055
1118
|
var WS_PATHS = {
|
|
1056
1119
|
/** Adapter session upgrade endpoint. One socket per running adapter. */
|
|
@@ -1147,6 +1210,9 @@ var TEST_STATE = {
|
|
|
1147
1210
|
FAILED: "failed",
|
|
1148
1211
|
SKIPPED: "skipped"
|
|
1149
1212
|
};
|
|
1213
|
+
var NETWORK_CAPTURE_SOURCE = {
|
|
1214
|
+
RESOURCE_TIMING: "resource-timing"
|
|
1215
|
+
};
|
|
1150
1216
|
var SCREENCAST_DEFAULTS = {
|
|
1151
1217
|
enabled: false,
|
|
1152
1218
|
captureFormat: "jpeg",
|
|
@@ -1805,6 +1871,21 @@ function buildActionEvents(commands, pageId, wallTime, testMetadata, snapshotInd
|
|
|
1805
1871
|
|
|
1806
1872
|
// ../trace/src/trace-har.ts
|
|
1807
1873
|
var INLINE_BODY_TEXT_MAX_BYTES = 8 * 1024;
|
|
1874
|
+
var HAR_PHASE_NOT_APPLICABLE = -1;
|
|
1875
|
+
function toHarTimings(timings, duration) {
|
|
1876
|
+
if (!timings) {
|
|
1877
|
+
return { send: 0, wait: Math.max(0, duration), receive: 0 };
|
|
1878
|
+
}
|
|
1879
|
+
return {
|
|
1880
|
+
blocked: timings.blocked ?? HAR_PHASE_NOT_APPLICABLE,
|
|
1881
|
+
dns: timings.dns ?? HAR_PHASE_NOT_APPLICABLE,
|
|
1882
|
+
connect: timings.connect ?? HAR_PHASE_NOT_APPLICABLE,
|
|
1883
|
+
ssl: timings.ssl ?? HAR_PHASE_NOT_APPLICABLE,
|
|
1884
|
+
send: timings.send ?? 0,
|
|
1885
|
+
wait: timings.wait ?? 0,
|
|
1886
|
+
receive: timings.receive ?? 0
|
|
1887
|
+
};
|
|
1888
|
+
}
|
|
1808
1889
|
function toHeaderArray(h) {
|
|
1809
1890
|
if (!h) {
|
|
1810
1891
|
return [];
|
|
@@ -1868,11 +1949,9 @@ function networkRequestToHar(entry, opts = {}) {
|
|
|
1868
1949
|
bodySize: entry.size ?? -1
|
|
1869
1950
|
},
|
|
1870
1951
|
cache: {},
|
|
1871
|
-
timings:
|
|
1872
|
-
|
|
1873
|
-
|
|
1874
|
-
receive: 0
|
|
1875
|
-
}
|
|
1952
|
+
timings: toHarTimings(entry.timings, duration),
|
|
1953
|
+
_resourceType: entry.type,
|
|
1954
|
+
...entry.source ? { _source: entry.source } : {}
|
|
1876
1955
|
}
|
|
1877
1956
|
};
|
|
1878
1957
|
}
|
|
@@ -2764,48 +2843,6 @@ import { createRequire } from "module";
|
|
|
2764
2843
|
|
|
2765
2844
|
// ../core/src/net.ts
|
|
2766
2845
|
import * as net from "net";
|
|
2767
|
-
|
|
2768
|
-
// ../core/src/request-type.ts
|
|
2769
|
-
function getRequestType(url, mimeType) {
|
|
2770
|
-
const contentType = mimeType?.toLowerCase() ?? "";
|
|
2771
|
-
const urlLower = url.toLowerCase();
|
|
2772
|
-
if (contentType.includes("text/html")) {
|
|
2773
|
-
return "document";
|
|
2774
|
-
}
|
|
2775
|
-
if (contentType.includes("text/css")) {
|
|
2776
|
-
return "stylesheet";
|
|
2777
|
-
}
|
|
2778
|
-
if (contentType.includes("javascript") || contentType.includes("ecmascript")) {
|
|
2779
|
-
return "script";
|
|
2780
|
-
}
|
|
2781
|
-
if (contentType.includes("image/")) {
|
|
2782
|
-
return "image";
|
|
2783
|
-
}
|
|
2784
|
-
if (contentType.includes("font/") || contentType.includes("woff")) {
|
|
2785
|
-
return "font";
|
|
2786
|
-
}
|
|
2787
|
-
if (contentType.includes("application/json")) {
|
|
2788
|
-
return "fetch";
|
|
2789
|
-
}
|
|
2790
|
-
if (urlLower.endsWith(".html") || urlLower.endsWith(".htm")) {
|
|
2791
|
-
return "document";
|
|
2792
|
-
}
|
|
2793
|
-
if (urlLower.endsWith(".css")) {
|
|
2794
|
-
return "stylesheet";
|
|
2795
|
-
}
|
|
2796
|
-
if (urlLower.endsWith(".js") || urlLower.endsWith(".mjs")) {
|
|
2797
|
-
return "script";
|
|
2798
|
-
}
|
|
2799
|
-
if (/\.(png|jpg|jpeg|gif|svg|webp|ico)$/.test(urlLower)) {
|
|
2800
|
-
return "image";
|
|
2801
|
-
}
|
|
2802
|
-
if (/\.(woff|woff2|ttf|eot|otf)$/.test(urlLower)) {
|
|
2803
|
-
return "font";
|
|
2804
|
-
}
|
|
2805
|
-
return "xhr";
|
|
2806
|
-
}
|
|
2807
|
-
|
|
2808
|
-
// ../core/src/net.ts
|
|
2809
2846
|
function isPortInUse(port, hostname) {
|
|
2810
2847
|
return new Promise((resolve) => {
|
|
2811
2848
|
const server = net.createServer();
|
|
@@ -2893,34 +2930,56 @@ function bidiResponseTiming(timings, startTime, timestamp) {
|
|
|
2893
2930
|
const endTime = Number(timestamp ?? Date.now());
|
|
2894
2931
|
return { endTime, time: Math.max(0, endTime - startTime) };
|
|
2895
2932
|
}
|
|
2896
|
-
|
|
2933
|
+
var MAX_TRACKED_REQUESTS = 5e3;
|
|
2934
|
+
function remember(rows, id, entry) {
|
|
2935
|
+
rows.set(id, entry);
|
|
2936
|
+
if (rows.size <= MAX_TRACKED_REQUESTS) {
|
|
2937
|
+
return;
|
|
2938
|
+
}
|
|
2939
|
+
for (const [key, row] of rows) {
|
|
2940
|
+
if (row.status !== void 0) {
|
|
2941
|
+
rows.delete(key);
|
|
2942
|
+
return;
|
|
2943
|
+
}
|
|
2944
|
+
}
|
|
2945
|
+
rows.delete(rows.keys().next().value);
|
|
2946
|
+
}
|
|
2947
|
+
function handleBidiRequestSent(rawEvent, rows, sinks, log) {
|
|
2897
2948
|
const event = rawEvent;
|
|
2898
2949
|
try {
|
|
2899
2950
|
const requestId = String(event?.request?.request ?? event?.id ?? "");
|
|
2900
|
-
|
|
2951
|
+
const shape = rawEvent;
|
|
2952
|
+
if (!requestId || shape?.response || shape?.errorText) {
|
|
2901
2953
|
return;
|
|
2902
2954
|
}
|
|
2955
|
+
const previous = rows.get(requestId);
|
|
2903
2956
|
const entry = {
|
|
2904
2957
|
id: requestId,
|
|
2905
2958
|
url: event?.request?.url ?? "",
|
|
2906
2959
|
method: event?.request?.method ?? "GET",
|
|
2907
2960
|
requestHeaders: arrayHeadersToObject(event?.request?.headers),
|
|
2908
|
-
|
|
2909
|
-
|
|
2961
|
+
// A redirect hop re-sends the request under the same id: one row, which
|
|
2962
|
+
// keeps the clock of the hop it follows and reopens for the next answer.
|
|
2963
|
+
timestamp: previous?.timestamp ?? Date.now(),
|
|
2964
|
+
startTime: previous?.startTime ?? Number(event?.timestamp ?? Date.now()),
|
|
2910
2965
|
type: getRequestType(event?.request?.url ?? "")
|
|
2911
2966
|
};
|
|
2912
|
-
|
|
2913
|
-
|
|
2967
|
+
remember(rows, requestId, entry);
|
|
2968
|
+
if (previous) {
|
|
2969
|
+
sinks.replaceNetworkRequest(requestId, entry);
|
|
2970
|
+
} else {
|
|
2971
|
+
sinks.pushNetworkRequest(entry);
|
|
2972
|
+
}
|
|
2914
2973
|
} catch (err) {
|
|
2915
2974
|
log("warn", `beforeRequestSent threw: ${errorMessage(err)}`);
|
|
2916
2975
|
}
|
|
2917
2976
|
}
|
|
2918
|
-
function handleBidiResponseCompleted(rawEvent,
|
|
2977
|
+
function handleBidiResponseCompleted(rawEvent, rows, sinks, log) {
|
|
2919
2978
|
const event = rawEvent;
|
|
2920
2979
|
try {
|
|
2921
2980
|
const requestId = String(event?.request?.request ?? event?.id ?? "");
|
|
2922
|
-
const previous =
|
|
2923
|
-
if (!previous) {
|
|
2981
|
+
const previous = rows.get(requestId);
|
|
2982
|
+
if (!previous || !event?.response) {
|
|
2924
2983
|
return;
|
|
2925
2984
|
}
|
|
2926
2985
|
const { endTime, time } = bidiResponseTiming(
|
|
@@ -2938,22 +2997,75 @@ function handleBidiResponseCompleted(rawEvent, pending, sinks, log) {
|
|
|
2938
2997
|
time,
|
|
2939
2998
|
size: Number(event?.response?.bytesReceived) || void 0
|
|
2940
2999
|
};
|
|
2941
|
-
|
|
3000
|
+
remember(rows, requestId, finalized);
|
|
2942
3001
|
sinks.replaceNetworkRequest(requestId, finalized);
|
|
2943
3002
|
} catch (err) {
|
|
2944
3003
|
log("warn", `responseCompleted threw: ${errorMessage(err)}`);
|
|
2945
3004
|
}
|
|
2946
3005
|
}
|
|
3006
|
+
function handleBidiFetchError(rawEvent, rows, sinks, log) {
|
|
3007
|
+
const event = rawEvent;
|
|
3008
|
+
try {
|
|
3009
|
+
const requestId = String(event?.request?.request ?? event?.id ?? "");
|
|
3010
|
+
const previous = rows.get(requestId);
|
|
3011
|
+
if (!previous || typeof event?.errorText !== "string") {
|
|
3012
|
+
return;
|
|
3013
|
+
}
|
|
3014
|
+
const { endTime, time } = bidiResponseTiming(
|
|
3015
|
+
event?.request?.timings,
|
|
3016
|
+
previous.startTime,
|
|
3017
|
+
event?.timestamp
|
|
3018
|
+
);
|
|
3019
|
+
const errorText = event?.errorText ?? "Failed";
|
|
3020
|
+
const finalized = {
|
|
3021
|
+
...previous,
|
|
3022
|
+
status: 0,
|
|
3023
|
+
statusText: errorText,
|
|
3024
|
+
error: errorText,
|
|
3025
|
+
endTime,
|
|
3026
|
+
time
|
|
3027
|
+
};
|
|
3028
|
+
remember(rows, requestId, finalized);
|
|
3029
|
+
sinks.replaceNetworkRequest(requestId, finalized);
|
|
3030
|
+
} catch (err) {
|
|
3031
|
+
log("warn", `fetchError threw: ${errorMessage(err)}`);
|
|
3032
|
+
}
|
|
3033
|
+
}
|
|
3034
|
+
async function subscribeFetchErrors(inspector, rows, sinks, log) {
|
|
3035
|
+
if (typeof inspector.fetchError === "function") {
|
|
3036
|
+
await inspector.fetchError((e) => handleBidiFetchError(e, rows, sinks, log));
|
|
3037
|
+
return;
|
|
3038
|
+
}
|
|
3039
|
+
const bidi = inspector.bidi;
|
|
3040
|
+
if (typeof bidi?.subscribe !== "function" || !bidi.socket?.on) {
|
|
3041
|
+
return;
|
|
3042
|
+
}
|
|
3043
|
+
await bidi.subscribe("network.fetchError");
|
|
3044
|
+
bidi.socket.on("message", (raw) => {
|
|
3045
|
+
try {
|
|
3046
|
+
const { method, params } = JSON.parse(String(raw));
|
|
3047
|
+
if (method === "network.fetchError") {
|
|
3048
|
+
handleBidiFetchError(params, rows, sinks, log);
|
|
3049
|
+
}
|
|
3050
|
+
} catch {
|
|
3051
|
+
}
|
|
3052
|
+
});
|
|
3053
|
+
}
|
|
2947
3054
|
async function attachNetworkInspector(driver, factory, sinks, log) {
|
|
2948
3055
|
try {
|
|
2949
3056
|
const inspector = await factory(driver);
|
|
2950
|
-
const
|
|
3057
|
+
const rows = /* @__PURE__ */ new Map();
|
|
2951
3058
|
await inspector.beforeRequestSent(
|
|
2952
|
-
(e) => handleBidiRequestSent(e,
|
|
3059
|
+
(e) => handleBidiRequestSent(e, rows, sinks, log)
|
|
2953
3060
|
);
|
|
2954
3061
|
await inspector.responseCompleted(
|
|
2955
|
-
(e) => handleBidiResponseCompleted(e,
|
|
3062
|
+
(e) => handleBidiResponseCompleted(e, rows, sinks, log)
|
|
2956
3063
|
);
|
|
3064
|
+
try {
|
|
3065
|
+
await subscribeFetchErrors(inspector, rows, sinks, log);
|
|
3066
|
+
} catch (err) {
|
|
3067
|
+
log("warn", `BiDi network.fetchError unavailable: ${errorMessage(err)}`);
|
|
3068
|
+
}
|
|
2957
3069
|
log("info", "\u2713 BiDi NetworkInspector attached (request + response)");
|
|
2958
3070
|
return true;
|
|
2959
3071
|
} catch (err) {
|
|
@@ -3111,6 +3223,144 @@ function serializeWebSnapshot(nodes, context, options = {}) {
|
|
|
3111
3223
|
// ../core/src/locators/xml-parsing.ts
|
|
3112
3224
|
import { DOMParser } from "@xmldom/xmldom";
|
|
3113
3225
|
import xpath from "xpath";
|
|
3226
|
+
|
|
3227
|
+
// ../core/src/locators/xpath-index.ts
|
|
3228
|
+
var NAME = /[A-Za-z_][\w.-]*/y;
|
|
3229
|
+
var active = null;
|
|
3230
|
+
function parseSimpleXPath(expr) {
|
|
3231
|
+
if (!expr.startsWith("//")) {
|
|
3232
|
+
return null;
|
|
3233
|
+
}
|
|
3234
|
+
let pos = 2;
|
|
3235
|
+
let tag;
|
|
3236
|
+
if (expr[pos] === "*") {
|
|
3237
|
+
tag = "*";
|
|
3238
|
+
pos++;
|
|
3239
|
+
} else {
|
|
3240
|
+
NAME.lastIndex = pos;
|
|
3241
|
+
const m = NAME.exec(expr);
|
|
3242
|
+
if (!m) {
|
|
3243
|
+
return null;
|
|
3244
|
+
}
|
|
3245
|
+
tag = m[0];
|
|
3246
|
+
pos = NAME.lastIndex;
|
|
3247
|
+
}
|
|
3248
|
+
if (pos === expr.length) {
|
|
3249
|
+
return { tag, conditions: [] };
|
|
3250
|
+
}
|
|
3251
|
+
if (expr[pos] !== "[" || !expr.endsWith("]")) {
|
|
3252
|
+
return null;
|
|
3253
|
+
}
|
|
3254
|
+
const conditions = parseConditions(expr.slice(pos + 1, -1));
|
|
3255
|
+
return conditions ? { tag, conditions } : null;
|
|
3256
|
+
}
|
|
3257
|
+
function parseConditions(body) {
|
|
3258
|
+
const conditions = [];
|
|
3259
|
+
let pos = 0;
|
|
3260
|
+
for (; ; ) {
|
|
3261
|
+
if (body[pos] !== "@") {
|
|
3262
|
+
return null;
|
|
3263
|
+
}
|
|
3264
|
+
NAME.lastIndex = pos + 1;
|
|
3265
|
+
const m = NAME.exec(body);
|
|
3266
|
+
if (!m || body[NAME.lastIndex] !== "=") {
|
|
3267
|
+
return null;
|
|
3268
|
+
}
|
|
3269
|
+
const quote = body[NAME.lastIndex + 1];
|
|
3270
|
+
if (quote !== '"' && quote !== "'") {
|
|
3271
|
+
return null;
|
|
3272
|
+
}
|
|
3273
|
+
const start = NAME.lastIndex + 2;
|
|
3274
|
+
const end = body.indexOf(quote, start);
|
|
3275
|
+
if (end === -1) {
|
|
3276
|
+
return null;
|
|
3277
|
+
}
|
|
3278
|
+
conditions.push({ name: m[0], value: body.slice(start, end) });
|
|
3279
|
+
pos = end + 1;
|
|
3280
|
+
if (pos === body.length) {
|
|
3281
|
+
return conditions;
|
|
3282
|
+
}
|
|
3283
|
+
if (!body.startsWith(" and ", pos)) {
|
|
3284
|
+
return null;
|
|
3285
|
+
}
|
|
3286
|
+
pos += 5;
|
|
3287
|
+
}
|
|
3288
|
+
}
|
|
3289
|
+
function append(map, key, value) {
|
|
3290
|
+
const list = map.get(key);
|
|
3291
|
+
if (list) {
|
|
3292
|
+
list.push(value);
|
|
3293
|
+
} else {
|
|
3294
|
+
map.set(key, [value]);
|
|
3295
|
+
}
|
|
3296
|
+
}
|
|
3297
|
+
function buildIndex(doc) {
|
|
3298
|
+
const index = {
|
|
3299
|
+
doc,
|
|
3300
|
+
all: [],
|
|
3301
|
+
byTag: /* @__PURE__ */ new Map(),
|
|
3302
|
+
byAttr: /* @__PURE__ */ new Map()
|
|
3303
|
+
};
|
|
3304
|
+
const stack = doc.documentElement ? [doc.documentElement] : [];
|
|
3305
|
+
const ordered = [];
|
|
3306
|
+
while (stack.length > 0) {
|
|
3307
|
+
const el = stack.pop();
|
|
3308
|
+
if (el.namespaceURI || el.nodeName.includes(":")) {
|
|
3309
|
+
return null;
|
|
3310
|
+
}
|
|
3311
|
+
ordered.push(el);
|
|
3312
|
+
for (let i = el.childNodes.length - 1; i >= 0; i--) {
|
|
3313
|
+
const child = el.childNodes.item(i);
|
|
3314
|
+
if (child?.nodeType === 1) {
|
|
3315
|
+
stack.push(child);
|
|
3316
|
+
}
|
|
3317
|
+
}
|
|
3318
|
+
}
|
|
3319
|
+
for (const el of ordered) {
|
|
3320
|
+
index.all.push(el);
|
|
3321
|
+
append(index.byTag, el.nodeName, el);
|
|
3322
|
+
for (let i = 0; i < el.attributes.length; i++) {
|
|
3323
|
+
const { name, value } = el.attributes.item(i);
|
|
3324
|
+
if (name === "xmlns" || name.includes(":")) {
|
|
3325
|
+
return null;
|
|
3326
|
+
}
|
|
3327
|
+
let byValue = index.byAttr.get(name);
|
|
3328
|
+
if (!byValue) {
|
|
3329
|
+
byValue = /* @__PURE__ */ new Map();
|
|
3330
|
+
index.byAttr.set(name, byValue);
|
|
3331
|
+
}
|
|
3332
|
+
append(byValue, value, el);
|
|
3333
|
+
}
|
|
3334
|
+
}
|
|
3335
|
+
return index;
|
|
3336
|
+
}
|
|
3337
|
+
function withXPathIndex(doc, walk) {
|
|
3338
|
+
const previous = active;
|
|
3339
|
+
active = doc ? buildIndex(doc) : null;
|
|
3340
|
+
try {
|
|
3341
|
+
return walk();
|
|
3342
|
+
} finally {
|
|
3343
|
+
active = previous;
|
|
3344
|
+
}
|
|
3345
|
+
}
|
|
3346
|
+
function selectIndexed(doc, expr) {
|
|
3347
|
+
if (active?.doc !== doc) {
|
|
3348
|
+
return null;
|
|
3349
|
+
}
|
|
3350
|
+
const query = parseSimpleXPath(expr);
|
|
3351
|
+
if (!query) {
|
|
3352
|
+
return null;
|
|
3353
|
+
}
|
|
3354
|
+
const [first, ...rest] = query.conditions;
|
|
3355
|
+
const candidates = first ? active.byAttr.get(first.name)?.get(first.value) ?? [] : query.tag === "*" ? active.all : active.byTag.get(query.tag) ?? [];
|
|
3356
|
+
return candidates.filter(
|
|
3357
|
+
(el) => (query.tag === "*" || el.nodeName === query.tag) && rest.every(
|
|
3358
|
+
(c) => el.hasAttribute(c.name) && el.getAttribute(c.name) === c.value
|
|
3359
|
+
)
|
|
3360
|
+
);
|
|
3361
|
+
}
|
|
3362
|
+
|
|
3363
|
+
// ../core/src/locators/xml-parsing.ts
|
|
3114
3364
|
function childNodesOf(node) {
|
|
3115
3365
|
const children = [];
|
|
3116
3366
|
if (node.childNodes) {
|
|
@@ -3240,6 +3490,10 @@ function xmlToDOM(sourceXML) {
|
|
|
3240
3490
|
}
|
|
3241
3491
|
function evaluateXPath(doc, xpathExpr) {
|
|
3242
3492
|
try {
|
|
3493
|
+
const indexed = selectIndexed(doc, xpathExpr);
|
|
3494
|
+
if (indexed) {
|
|
3495
|
+
return indexed;
|
|
3496
|
+
}
|
|
3243
3497
|
const nodes = xpath.select(xpathExpr, doc);
|
|
3244
3498
|
if (Array.isArray(nodes)) {
|
|
3245
3499
|
return nodes;
|
|
@@ -4488,7 +4742,7 @@ function generateAllElementLocators(sourceXML, options) {
|
|
|
4488
4742
|
results: [],
|
|
4489
4743
|
parsedDOM
|
|
4490
4744
|
};
|
|
4491
|
-
traverseTree(sourceJSON, ctx);
|
|
4745
|
+
withXPathIndex(parsedDOM, () => traverseTree(sourceJSON, ctx));
|
|
4492
4746
|
return ctx.results;
|
|
4493
4747
|
}
|
|
4494
4748
|
|
|
@@ -4945,6 +5199,207 @@ function webdriverPost(address, sessionId, path10, body) {
|
|
|
4945
5199
|
body
|
|
4946
5200
|
);
|
|
4947
5201
|
}
|
|
5202
|
+
function webdriverExecute(address, sessionId, body, args = []) {
|
|
5203
|
+
return webdriverPost(address, sessionId, "execute/sync", {
|
|
5204
|
+
script: body,
|
|
5205
|
+
args
|
|
5206
|
+
});
|
|
5207
|
+
}
|
|
5208
|
+
|
|
5209
|
+
// ../core/src/appium-interceptor.ts
|
|
5210
|
+
var INTERCEPT_CAPABILITY = "appium:intercept";
|
|
5211
|
+
var APPIUM_OPTIONS = "appium:options";
|
|
5212
|
+
var NATIVE_NETWORK_DRAIN_INTERVAL_MS = 2e3;
|
|
5213
|
+
function flattenInterceptedHeaders(headers) {
|
|
5214
|
+
const flat = {};
|
|
5215
|
+
for (const [name, value] of Object.entries(headers ?? {})) {
|
|
5216
|
+
if (/\s/.test(name)) {
|
|
5217
|
+
continue;
|
|
5218
|
+
}
|
|
5219
|
+
flat[name.toLowerCase()] = Array.isArray(value) ? value.join(", ") : value;
|
|
5220
|
+
}
|
|
5221
|
+
return flat;
|
|
5222
|
+
}
|
|
5223
|
+
function interceptedBodyText(body) {
|
|
5224
|
+
if (body === void 0 || body === null || body === "") {
|
|
5225
|
+
return void 0;
|
|
5226
|
+
}
|
|
5227
|
+
return typeof body === "string" ? body : JSON.stringify(body);
|
|
5228
|
+
}
|
|
5229
|
+
function interceptedToNetworkRequest(entry, id, observedAt) {
|
|
5230
|
+
const responseHeaders = flattenInterceptedHeaders(entry.responseHeaders);
|
|
5231
|
+
const responseBody = interceptedBodyText(entry.responseBody);
|
|
5232
|
+
return {
|
|
5233
|
+
id,
|
|
5234
|
+
url: entry.url,
|
|
5235
|
+
method: entry.method,
|
|
5236
|
+
status: entry.statusCode,
|
|
5237
|
+
type: getRequestType(entry.url, responseHeaders["content-type"]),
|
|
5238
|
+
timestamp: observedAt.timestamp,
|
|
5239
|
+
startTime: observedAt.startTime,
|
|
5240
|
+
endTime: observedAt.startTime,
|
|
5241
|
+
requestHeaders: flattenInterceptedHeaders(entry.requestHeaders),
|
|
5242
|
+
responseHeaders,
|
|
5243
|
+
response: {
|
|
5244
|
+
fromCache: false,
|
|
5245
|
+
headers: responseHeaders,
|
|
5246
|
+
mimeType: responseHeaders["content-type"]?.split(";")[0]?.trim() ?? "",
|
|
5247
|
+
status: entry.statusCode
|
|
5248
|
+
},
|
|
5249
|
+
requestBody: interceptedBodyText(entry.requestBody),
|
|
5250
|
+
responseBody,
|
|
5251
|
+
size: responseBody === void 0 ? void 0 : Buffer.byteLength(responseBody)
|
|
5252
|
+
};
|
|
5253
|
+
}
|
|
5254
|
+
var InterceptorNetworkPoller = class {
|
|
5255
|
+
#execute;
|
|
5256
|
+
#listenerId;
|
|
5257
|
+
#seen = 0;
|
|
5258
|
+
// Drains are chained: two overlapping reads would both list the entries past the same `#seen`.
|
|
5259
|
+
#tail = Promise.resolve([]);
|
|
5260
|
+
constructor(execute) {
|
|
5261
|
+
this.#execute = execute;
|
|
5262
|
+
}
|
|
5263
|
+
async start() {
|
|
5264
|
+
const id = await this.#execute("interceptor: startListening", [{}]);
|
|
5265
|
+
this.#listenerId = typeof id === "string" ? id : void 0;
|
|
5266
|
+
return this.#listenerId !== void 0;
|
|
5267
|
+
}
|
|
5268
|
+
drain() {
|
|
5269
|
+
this.#tail = this.#tail.catch(() => []).then(() => this.#readFresh());
|
|
5270
|
+
return this.#tail;
|
|
5271
|
+
}
|
|
5272
|
+
async #readFresh() {
|
|
5273
|
+
const listenerId = this.#listenerId;
|
|
5274
|
+
if (!listenerId) {
|
|
5275
|
+
return [];
|
|
5276
|
+
}
|
|
5277
|
+
const data = await this.#execute("interceptor: getInterceptedData", [
|
|
5278
|
+
{ id: listenerId }
|
|
5279
|
+
]);
|
|
5280
|
+
if (!Array.isArray(data)) {
|
|
5281
|
+
throw new Error("the plugin returned no intercepted data");
|
|
5282
|
+
}
|
|
5283
|
+
if (data.length <= this.#seen) {
|
|
5284
|
+
return [];
|
|
5285
|
+
}
|
|
5286
|
+
const observedAt = { timestamp: Date.now(), startTime: performance.now() };
|
|
5287
|
+
const fresh = data.slice(this.#seen).map(
|
|
5288
|
+
(entry, offset) => interceptedToNetworkRequest(
|
|
5289
|
+
entry,
|
|
5290
|
+
`interceptor-${listenerId}-${this.#seen + offset}`,
|
|
5291
|
+
observedAt
|
|
5292
|
+
)
|
|
5293
|
+
);
|
|
5294
|
+
this.#seen = data.length;
|
|
5295
|
+
return fresh;
|
|
5296
|
+
}
|
|
5297
|
+
};
|
|
5298
|
+
function asksForIntercept(caps) {
|
|
5299
|
+
if (!caps || typeof caps !== "object") {
|
|
5300
|
+
return false;
|
|
5301
|
+
}
|
|
5302
|
+
const map = caps;
|
|
5303
|
+
const read = (key) => typeof map.get === "function" ? map.get(key) : map[key];
|
|
5304
|
+
if (read(INTERCEPT_CAPABILITY) === true) {
|
|
5305
|
+
return true;
|
|
5306
|
+
}
|
|
5307
|
+
const grouped = read(APPIUM_OPTIONS);
|
|
5308
|
+
return !!grouped && typeof grouped === "object" && grouped.intercept === true;
|
|
5309
|
+
}
|
|
5310
|
+
function requestsInterceptedNetwork(capabilities) {
|
|
5311
|
+
if (asksForIntercept(capabilities)) {
|
|
5312
|
+
return true;
|
|
5313
|
+
}
|
|
5314
|
+
if (!capabilities || typeof capabilities !== "object") {
|
|
5315
|
+
return false;
|
|
5316
|
+
}
|
|
5317
|
+
const { alwaysMatch, firstMatch } = capabilities;
|
|
5318
|
+
return [alwaysMatch, ...Array.isArray(firstMatch) ? firstMatch : []].some(
|
|
5319
|
+
asksForIntercept
|
|
5320
|
+
);
|
|
5321
|
+
}
|
|
5322
|
+
var NativeNetworkCapture = class {
|
|
5323
|
+
#sink;
|
|
5324
|
+
#onLog;
|
|
5325
|
+
#now;
|
|
5326
|
+
#lastDrainAt = Number.NEGATIVE_INFINITY;
|
|
5327
|
+
#poller;
|
|
5328
|
+
// A start superseded by a later one (session replaced mid-handshake) must not install its poller.
|
|
5329
|
+
#generation = 0;
|
|
5330
|
+
constructor(sink, onLog, now = Date.now) {
|
|
5331
|
+
this.#sink = sink;
|
|
5332
|
+
this.#onLog = onLog;
|
|
5333
|
+
this.#now = now;
|
|
5334
|
+
}
|
|
5335
|
+
/** Called per session: a listener dies with its session. */
|
|
5336
|
+
async start(target) {
|
|
5337
|
+
const generation = ++this.#generation;
|
|
5338
|
+
this.#poller = void 0;
|
|
5339
|
+
if (!target?.requested || !isNativeAppSession(target.capabilities)) {
|
|
5340
|
+
return;
|
|
5341
|
+
}
|
|
5342
|
+
if (!target.execute) {
|
|
5343
|
+
this.#onLog(
|
|
5344
|
+
"warn",
|
|
5345
|
+
`${INTERCEPT_CAPABILITY} is set but ${target.unavailableReason ?? "the driver cannot be reached outside the command path"}; native network is not captured`
|
|
5346
|
+
);
|
|
5347
|
+
return;
|
|
5348
|
+
}
|
|
5349
|
+
const poller = new InterceptorNetworkPoller(target.execute);
|
|
5350
|
+
const started = await poller.start().catch((err) => {
|
|
5351
|
+
this.#onLog(
|
|
5352
|
+
"warn",
|
|
5353
|
+
`appium-interceptor listener failed to start: ${errorMessage(err)}`
|
|
5354
|
+
);
|
|
5355
|
+
return false;
|
|
5356
|
+
});
|
|
5357
|
+
if (generation !== this.#generation) {
|
|
5358
|
+
return;
|
|
5359
|
+
}
|
|
5360
|
+
if (!started) {
|
|
5361
|
+
this.#onLog(
|
|
5362
|
+
"warn",
|
|
5363
|
+
`${INTERCEPT_CAPABILITY} is set but the interceptor listener did not start; is Appium running with --use-plugins=appium-interceptor?`
|
|
5364
|
+
);
|
|
5365
|
+
return;
|
|
5366
|
+
}
|
|
5367
|
+
this.#onLog("info", "Native network capture active via appium-interceptor");
|
|
5368
|
+
this.#poller = poller;
|
|
5369
|
+
this.#lastDrainAt = Number.NEGATIVE_INFINITY;
|
|
5370
|
+
}
|
|
5371
|
+
/** Whether a listener is running, so a caller can skip a drain that cannot find anything. */
|
|
5372
|
+
get active() {
|
|
5373
|
+
return this.#poller !== void 0;
|
|
5374
|
+
}
|
|
5375
|
+
/** At session end, so a drain after the driver quit does not reach a dead session. */
|
|
5376
|
+
stop() {
|
|
5377
|
+
this.#generation++;
|
|
5378
|
+
this.#poller = void 0;
|
|
5379
|
+
}
|
|
5380
|
+
/** Lists what the proxy recorded since the last call. A test or session boundary must not pass `throttle`. */
|
|
5381
|
+
async forward(options = {}) {
|
|
5382
|
+
const poller = this.#poller;
|
|
5383
|
+
if (!poller) {
|
|
5384
|
+
return;
|
|
5385
|
+
}
|
|
5386
|
+
const now = this.#now();
|
|
5387
|
+
if (options.throttle && now - this.#lastDrainAt < NATIVE_NETWORK_DRAIN_INTERVAL_MS) {
|
|
5388
|
+
return;
|
|
5389
|
+
}
|
|
5390
|
+
this.#lastDrainAt = now;
|
|
5391
|
+
const requests = await poller.drain().catch((err) => {
|
|
5392
|
+
this.#onLog(
|
|
5393
|
+
"warn",
|
|
5394
|
+
`Reading appium-interceptor data failed: ${errorMessage(err)}`
|
|
5395
|
+
);
|
|
5396
|
+
return [];
|
|
5397
|
+
});
|
|
5398
|
+
if (requests.length > 0) {
|
|
5399
|
+
this.#sink.recordNetworkRequests(requests);
|
|
5400
|
+
}
|
|
5401
|
+
}
|
|
5402
|
+
};
|
|
4948
5403
|
|
|
4949
5404
|
// ../core/src/viewport.ts
|
|
4950
5405
|
var VISUAL_VIEWPORT_SCRIPT = `
|
|
@@ -5284,20 +5739,8 @@ async function loadCollectorSource() {
|
|
|
5284
5739
|
async function loadInjectableScript() {
|
|
5285
5740
|
return `(async function() { ${await loadCollectorSource()} })()`;
|
|
5286
5741
|
}
|
|
5287
|
-
var NON_INSTRUMENTABLE_SCHEMES = [
|
|
5288
|
-
"about:",
|
|
5289
|
-
"data:",
|
|
5290
|
-
"chrome:",
|
|
5291
|
-
"chrome-error:",
|
|
5292
|
-
"edge:",
|
|
5293
|
-
"moz-extension:",
|
|
5294
|
-
"chrome-extension:"
|
|
5295
|
-
];
|
|
5296
5742
|
function isInstrumentableDocument(url) {
|
|
5297
|
-
|
|
5298
|
-
return false;
|
|
5299
|
-
}
|
|
5300
|
-
return !NON_INSTRUMENTABLE_SCHEMES.some((scheme) => url.startsWith(scheme));
|
|
5743
|
+
return Boolean(url) && !isBrowserInternalUrl(url) && schemeOf(url) !== "data";
|
|
5301
5744
|
}
|
|
5302
5745
|
async function drainCollectorWithRecovery(opts) {
|
|
5303
5746
|
const payload = await opts.drain();
|
|
@@ -5329,6 +5772,9 @@ async function drainCollectorWithRecovery(opts) {
|
|
|
5329
5772
|
async function pollUntilReady(check, opts = {}) {
|
|
5330
5773
|
const attempts = opts.attempts ?? 5;
|
|
5331
5774
|
const intervalMs = opts.intervalMs ?? 200;
|
|
5775
|
+
if (opts.checkFirst && await check()) {
|
|
5776
|
+
return true;
|
|
5777
|
+
}
|
|
5332
5778
|
for (let i = 0; i < attempts; i++) {
|
|
5333
5779
|
await new Promise((resolve) => setTimeout(resolve, intervalMs));
|
|
5334
5780
|
if (await check()) {
|
|
@@ -5605,18 +6051,28 @@ function resolveAssertTargetFromArgs(args, fromHandle) {
|
|
|
5605
6051
|
}
|
|
5606
6052
|
|
|
5607
6053
|
// ../core/src/retry-tracker.ts
|
|
5608
|
-
var RetryTracker = class {
|
|
6054
|
+
var RetryTracker = class _RetryTracker {
|
|
5609
6055
|
#lastSig = null;
|
|
6056
|
+
#lastBareSig = null;
|
|
5610
6057
|
#lastId = null;
|
|
5611
6058
|
/** Build the canonical signature used for retry-equality checks. */
|
|
5612
6059
|
static signature(command, args, callSource) {
|
|
5613
6060
|
return JSON.stringify({ command, args, src: callSource ?? null });
|
|
5614
6061
|
}
|
|
6062
|
+
/** The signature without the call site: a framework's own repeat of a call
|
|
6063
|
+
* (a `wait()` poll from a timer) has no user frame on its stack. */
|
|
6064
|
+
static bareSignature(command, args) {
|
|
6065
|
+
return JSON.stringify({ command, args });
|
|
6066
|
+
}
|
|
5615
6067
|
/** True when the incoming signature matches the last captured one AND we
|
|
5616
6068
|
* have an id to replace (otherwise there's nothing to replace yet). */
|
|
5617
6069
|
isRetry(sig) {
|
|
5618
6070
|
return sig === this.#lastSig && this.#lastId !== null;
|
|
5619
6071
|
}
|
|
6072
|
+
/** As `isRetry`, for a repeat that carries no call site of its own. */
|
|
6073
|
+
isRetryIgnoringSource(bareSig) {
|
|
6074
|
+
return bareSig === this.#lastBareSig && this.#lastId !== null;
|
|
6075
|
+
}
|
|
5620
6076
|
/** The id of the last captured command, if any (for the replace-in-place
|
|
5621
6077
|
* flow). */
|
|
5622
6078
|
get lastId() {
|
|
@@ -5625,8 +6081,17 @@ var RetryTracker = class {
|
|
|
5625
6081
|
/** Record a fresh capture — sets both sig and id together. */
|
|
5626
6082
|
recordCapture(sig, id) {
|
|
5627
6083
|
this.#lastSig = sig;
|
|
6084
|
+
this.#lastBareSig = _RetryTracker.bareOf(sig);
|
|
5628
6085
|
this.#lastId = id;
|
|
5629
6086
|
}
|
|
6087
|
+
static bareOf(sig) {
|
|
6088
|
+
try {
|
|
6089
|
+
const { command, args } = JSON.parse(sig);
|
|
6090
|
+
return _RetryTracker.bareSignature(command, args);
|
|
6091
|
+
} catch {
|
|
6092
|
+
return null;
|
|
6093
|
+
}
|
|
6094
|
+
}
|
|
5630
6095
|
/** Record only the id (used by adapters that compute the sig but defer the
|
|
5631
6096
|
* id assignment to after an async capture call). */
|
|
5632
6097
|
setLastId(id) {
|
|
@@ -5637,10 +6102,12 @@ var RetryTracker = class {
|
|
|
5637
6102
|
* commands). Pair with {@link setLastId} once the capture resolves. */
|
|
5638
6103
|
setLastSig(sig) {
|
|
5639
6104
|
this.#lastSig = sig;
|
|
6105
|
+
this.#lastBareSig = _RetryTracker.bareOf(sig);
|
|
5640
6106
|
}
|
|
5641
6107
|
/** Reset at test/scenario boundaries so the next capture is "fresh". */
|
|
5642
6108
|
reset() {
|
|
5643
6109
|
this.#lastSig = null;
|
|
6110
|
+
this.#lastBareSig = null;
|
|
5644
6111
|
this.#lastId = null;
|
|
5645
6112
|
}
|
|
5646
6113
|
};
|
|
@@ -5919,6 +6386,31 @@ var ScreencastRecorderBase = class {
|
|
|
5919
6386
|
}
|
|
5920
6387
|
};
|
|
5921
6388
|
|
|
6389
|
+
// ../core/src/browser-focus.ts
|
|
6390
|
+
import { spawn } from "child_process";
|
|
6391
|
+
var LOCAL_HOSTS = /* @__PURE__ */ new Set(["localhost", "127.0.0.1", "::1", "[::1]"]);
|
|
6392
|
+
function restoreBrowserFocus(target, platform = process.platform, launch = spawn) {
|
|
6393
|
+
const local = !target.host || LOCAL_HOSTS.has(target.host);
|
|
6394
|
+
if (platform !== "darwin" || !local || !/safari/i.test(target.browserName ?? "") || /ios/i.test(target.platformName ?? "")) {
|
|
6395
|
+
return false;
|
|
6396
|
+
}
|
|
6397
|
+
try {
|
|
6398
|
+
const child = launch("open", ["-a", "Safari"], {
|
|
6399
|
+
detached: true,
|
|
6400
|
+
stdio: "ignore"
|
|
6401
|
+
});
|
|
6402
|
+
child.on?.(
|
|
6403
|
+
"error",
|
|
6404
|
+
(err) => target.onError?.(`Could not bring Safari to the front: ${err.message}`)
|
|
6405
|
+
);
|
|
6406
|
+
child.unref?.();
|
|
6407
|
+
return true;
|
|
6408
|
+
} catch (err) {
|
|
6409
|
+
target.onError?.(`Could not bring Safari to the front: ${String(err)}`);
|
|
6410
|
+
return false;
|
|
6411
|
+
}
|
|
6412
|
+
}
|
|
6413
|
+
|
|
5922
6414
|
// ../core/src/session-capturer.ts
|
|
5923
6415
|
import fs10 from "fs/promises";
|
|
5924
6416
|
import { WebSocket } from "ws";
|
|
@@ -6267,26 +6759,24 @@ var SessionCapturerBase = class _SessionCapturerBase {
|
|
|
6267
6759
|
*
|
|
6268
6760
|
* `skipConsoleLogs` / `skipNetworkRequests` opt out when an out-of-band
|
|
6269
6761
|
* channel (BiDi) is already delivering those streams — without the gate
|
|
6270
|
-
* the dashboard would see each entry twice.
|
|
6762
|
+
* the dashboard would see each entry twice. `skipResourceTimingRequests`
|
|
6763
|
+
* drops only the page's Resource Timing entries: a driver-side source
|
|
6764
|
+
* reports the same loads with their headers, while the collector's patched
|
|
6765
|
+
* fetch/XHR rows still carry the bodies nothing else captures.
|
|
6271
6766
|
*/
|
|
6272
6767
|
processTracePayload(payload, opts = {}) {
|
|
6273
6768
|
const { mutations, traceLogs, consoleLogs, networkRequests, metadata } = payload;
|
|
6274
6769
|
if (metadata && typeof metadata === "object") {
|
|
6275
6770
|
this.mergeMetadata(metadata);
|
|
6276
6771
|
}
|
|
6277
|
-
|
|
6278
|
-
|
|
6279
|
-
...entry,
|
|
6280
|
-
source: LOG_SOURCES.BROWSER
|
|
6281
|
-
}));
|
|
6772
|
+
const tagged = opts.skipConsoleLogs ? [] : this.#tagBrowserConsoleLogs(consoleLogs);
|
|
6773
|
+
if (tagged.length > 0) {
|
|
6282
6774
|
this.consoleLogs.push(...tagged);
|
|
6283
6775
|
this.sendUpstream("consoleLogs", tagged);
|
|
6284
6776
|
}
|
|
6285
|
-
|
|
6286
|
-
|
|
6287
|
-
|
|
6288
|
-
this.sendUpstream("networkRequests", reqs);
|
|
6289
|
-
}
|
|
6777
|
+
this.recordNetworkRequests(
|
|
6778
|
+
this.#acceptedNetworkRequests(networkRequests, opts)
|
|
6779
|
+
);
|
|
6290
6780
|
if (Array.isArray(mutations) && mutations.length > 0) {
|
|
6291
6781
|
const batch = mutations;
|
|
6292
6782
|
reattributeDomAnchors(
|
|
@@ -6303,6 +6793,50 @@ var SessionCapturerBase = class _SessionCapturerBase {
|
|
|
6303
6793
|
this.sendUpstream("logs", logs);
|
|
6304
6794
|
}
|
|
6305
6795
|
}
|
|
6796
|
+
#tagBrowserConsoleLogs(consoleLogs) {
|
|
6797
|
+
if (!Array.isArray(consoleLogs)) {
|
|
6798
|
+
return [];
|
|
6799
|
+
}
|
|
6800
|
+
return consoleLogs.map((entry) => ({
|
|
6801
|
+
...entry,
|
|
6802
|
+
source: LOG_SOURCES.BROWSER
|
|
6803
|
+
}));
|
|
6804
|
+
}
|
|
6805
|
+
/** Lists network rows and sends them in one message; a re-sent id replaces its row, as the dashboard does. */
|
|
6806
|
+
recordNetworkRequests(reqs) {
|
|
6807
|
+
if (reqs.length === 0) {
|
|
6808
|
+
return;
|
|
6809
|
+
}
|
|
6810
|
+
for (const req of reqs) {
|
|
6811
|
+
this.#upsertNetworkRequest(req);
|
|
6812
|
+
}
|
|
6813
|
+
this.sendUpstream("networkRequests", reqs);
|
|
6814
|
+
}
|
|
6815
|
+
/** Row index by request id; checked against the row before use, since adapters also write rows by index. */
|
|
6816
|
+
#networkRequestIndex = /* @__PURE__ */ new Map();
|
|
6817
|
+
#upsertNetworkRequest(req) {
|
|
6818
|
+
let at = req.id ? this.#networkRequestIndex.get(req.id) : void 0;
|
|
6819
|
+
if (at !== void 0 && this.networkRequests[at]?.id !== req.id) {
|
|
6820
|
+
at = this.networkRequests.findIndex((r) => r.id === req.id);
|
|
6821
|
+
}
|
|
6822
|
+
if (at !== void 0 && at !== -1) {
|
|
6823
|
+
this.networkRequests[at] = req;
|
|
6824
|
+
this.#networkRequestIndex.set(req.id, at);
|
|
6825
|
+
return;
|
|
6826
|
+
}
|
|
6827
|
+
if (req.id) {
|
|
6828
|
+
this.#networkRequestIndex.set(req.id, this.networkRequests.length);
|
|
6829
|
+
}
|
|
6830
|
+
this.networkRequests.push(req);
|
|
6831
|
+
}
|
|
6832
|
+
#acceptedNetworkRequests(networkRequests, opts) {
|
|
6833
|
+
if (opts.skipNetworkRequests || !Array.isArray(networkRequests)) {
|
|
6834
|
+
return [];
|
|
6835
|
+
}
|
|
6836
|
+
return networkRequests.filter(
|
|
6837
|
+
(req) => !opts.skipResourceTimingRequests || req.source !== NETWORK_CAPTURE_SOURCE.RESOURCE_TIMING
|
|
6838
|
+
);
|
|
6839
|
+
}
|
|
6306
6840
|
/**
|
|
6307
6841
|
* Resolve when the WS reaches OPEN state, or `false` on timeout / error.
|
|
6308
6842
|
* Returns immediately if already open. Used by adapters that need a
|
|
@@ -6713,9 +7247,11 @@ export {
|
|
|
6713
7247
|
mapCommandToAction,
|
|
6714
7248
|
stripAnsi,
|
|
6715
7249
|
mapChromeBrowserLogs,
|
|
7250
|
+
getRequestType,
|
|
6716
7251
|
WS_SCOPE,
|
|
6717
7252
|
REUSE_ENV,
|
|
6718
7253
|
TEST_STATE,
|
|
7254
|
+
NETWORK_CAPTURE_SOURCE,
|
|
6719
7255
|
SCREENCAST_DEFAULTS,
|
|
6720
7256
|
captureActionSnapshot,
|
|
6721
7257
|
toError,
|
|
@@ -6728,6 +7264,9 @@ export {
|
|
|
6728
7264
|
TestAttemptTracker,
|
|
6729
7265
|
webdriverGet,
|
|
6730
7266
|
webdriverPost,
|
|
7267
|
+
webdriverExecute,
|
|
7268
|
+
requestsInterceptedNetwork,
|
|
7269
|
+
NativeNetworkCapture,
|
|
6731
7270
|
resolveViewport,
|
|
6732
7271
|
isUserCodeFrame,
|
|
6733
7272
|
normalizeFilePath,
|
|
@@ -6744,7 +7283,6 @@ export {
|
|
|
6744
7283
|
recordSpecBoundary,
|
|
6745
7284
|
flushRangeLogged,
|
|
6746
7285
|
finalizeTraceExport,
|
|
6747
|
-
getRequestType,
|
|
6748
7286
|
findFreePort,
|
|
6749
7287
|
attachBidiHandlers,
|
|
6750
7288
|
COLLECTOR_READY_EXPRESSION,
|
|
@@ -6762,6 +7300,7 @@ export {
|
|
|
6762
7300
|
resolveAssertTargetFromArgs,
|
|
6763
7301
|
RetryTracker,
|
|
6764
7302
|
ScreencastRecorderBase,
|
|
7303
|
+
restoreBrowserFocus,
|
|
6765
7304
|
SessionCapturerBase,
|
|
6766
7305
|
createSuiteStats,
|
|
6767
7306
|
createTestStats,
|
|
@@ -6773,4 +7312,4 @@ export {
|
|
|
6773
7312
|
findTestDefinitions,
|
|
6774
7313
|
TestReporterBase
|
|
6775
7314
|
};
|
|
6776
|
-
//# sourceMappingURL=chunk-
|
|
7315
|
+
//# sourceMappingURL=chunk-TUK7ZLQI.js.map
|