@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
package/dist/index.js
CHANGED
|
@@ -1,6 +1,8 @@
|
|
|
1
1
|
import {
|
|
2
2
|
CAPTURE_PERFORMANCE_SCRIPT,
|
|
3
3
|
COLLECTOR_READY_EXPRESSION,
|
|
4
|
+
NETWORK_CAPTURE_SOURCE,
|
|
5
|
+
NativeNetworkCapture,
|
|
4
6
|
REUSE_ENV,
|
|
5
7
|
RetryTracker,
|
|
6
8
|
SCREENCAST_DEFAULTS,
|
|
@@ -47,9 +49,11 @@ import {
|
|
|
47
49
|
recordSpecBoundary,
|
|
48
50
|
registerCollectorPreload,
|
|
49
51
|
rememberReadValue,
|
|
52
|
+
requestsInterceptedNetwork,
|
|
50
53
|
resolveAdapterOutputDir,
|
|
51
54
|
resolveAssertTargetFromArgs,
|
|
52
55
|
resolveViewport,
|
|
56
|
+
restoreBrowserFocus,
|
|
53
57
|
safeSerializeAssertArg,
|
|
54
58
|
serializeError,
|
|
55
59
|
stampSuiteEnd,
|
|
@@ -58,9 +62,10 @@ import {
|
|
|
58
62
|
toError,
|
|
59
63
|
tracePolicyModeWarning,
|
|
60
64
|
upsertRichestSnapshot,
|
|
65
|
+
webdriverExecute,
|
|
61
66
|
webdriverGet,
|
|
62
67
|
webdriverPost
|
|
63
|
-
} from "./chunk-
|
|
68
|
+
} from "./chunk-TUK7ZLQI.js";
|
|
64
69
|
|
|
65
70
|
// src/index.ts
|
|
66
71
|
import { fileURLToPath } from "url";
|
|
@@ -546,23 +551,16 @@ async function openDevtoolsBrowser(ctx, url) {
|
|
|
546
551
|
}
|
|
547
552
|
ctx.devtoolsBrowser = await remote({
|
|
548
553
|
logLevel: "info",
|
|
549
|
-
automationProtocol: "devtools",
|
|
550
554
|
capabilities: {
|
|
551
555
|
browserName: "chrome",
|
|
552
556
|
"goog:chromeOptions": {
|
|
557
|
+
excludeSwitches: ["enable-automation"],
|
|
553
558
|
args: [
|
|
554
559
|
"--window-size=1600,1200",
|
|
555
560
|
`--user-data-dir=${ctx.userDataDir}`,
|
|
556
561
|
"--no-first-run",
|
|
557
562
|
"--no-default-browser-check"
|
|
558
563
|
]
|
|
559
|
-
},
|
|
560
|
-
// Dashboard uses the Puppeteer-based 'devtools' protocol; drop the
|
|
561
|
-
// "controlled by automated test software" infobar by ignoring
|
|
562
|
-
// Puppeteer's --enable-automation default. (Runtime-valid capability
|
|
563
|
-
// not present in the type.)
|
|
564
|
-
"wdio:devtoolsOptions": {
|
|
565
|
-
ignoreDefaultArgs: ["--enable-automation"]
|
|
566
564
|
}
|
|
567
565
|
}
|
|
568
566
|
});
|
|
@@ -601,7 +599,7 @@ async function waitForDevtoolsBrowserClose(ctx) {
|
|
|
601
599
|
}
|
|
602
600
|
;
|
|
603
601
|
logger2.setLevel(
|
|
604
|
-
"
|
|
602
|
+
"webdriver",
|
|
605
603
|
"warn"
|
|
606
604
|
);
|
|
607
605
|
let exitBySignal = false;
|
|
@@ -629,7 +627,7 @@ async function waitForDevtoolsBrowserClose(ctx) {
|
|
|
629
627
|
process.removeListener("SIGINT", signalHandler);
|
|
630
628
|
process.removeListener("SIGTERM", signalHandler);
|
|
631
629
|
logger2.setLevel(
|
|
632
|
-
"
|
|
630
|
+
"webdriver",
|
|
633
631
|
"info"
|
|
634
632
|
);
|
|
635
633
|
try {
|
|
@@ -661,7 +659,7 @@ var firstDefined = (obj, ...keys) => {
|
|
|
661
659
|
}
|
|
662
660
|
return void 0;
|
|
663
661
|
};
|
|
664
|
-
function
|
|
662
|
+
function configuredWebDriver(browser) {
|
|
665
663
|
const transportSettings = getPath(browser, "transport", "settings", "webdriver") || getPath(browser, "queue", "transport", "settings", "webdriver") || getPath(
|
|
666
664
|
browser,
|
|
667
665
|
"nightwatchInstance",
|
|
@@ -671,13 +669,24 @@ function resolveWebDriverAddress(browser) {
|
|
|
671
669
|
) || {};
|
|
672
670
|
const opts = getProp(browser, "options") ?? {};
|
|
673
671
|
const nightwatchSettings = getPath(browser, "nightwatchInstance", "settings") || getPath(browser, "globals", "nightwatchInstance", "settings") || {};
|
|
674
|
-
const
|
|
675
|
-
|
|
676
|
-
|
|
677
|
-
|
|
678
|
-
|
|
679
|
-
);
|
|
680
|
-
return {
|
|
672
|
+
const sources = [
|
|
673
|
+
transportSettings,
|
|
674
|
+
getProp(opts, "webdriver"),
|
|
675
|
+
getProp(nightwatchSettings, "webdriver")
|
|
676
|
+
];
|
|
677
|
+
const pick = (...keys) => sources.map((source) => firstDefined(source, ...keys)).find(Boolean);
|
|
678
|
+
return {
|
|
679
|
+
host: pick("host", "server_address"),
|
|
680
|
+
port: pick("port"),
|
|
681
|
+
pathPrefix: pick("default_path_prefix")
|
|
682
|
+
};
|
|
683
|
+
}
|
|
684
|
+
function resolveWebDriverAddress(browser) {
|
|
685
|
+
const { host, port } = configuredWebDriver(browser);
|
|
686
|
+
return {
|
|
687
|
+
driverHost: String(host || "localhost"),
|
|
688
|
+
driverPort: Number(port || 9515)
|
|
689
|
+
};
|
|
681
690
|
}
|
|
682
691
|
function address(browser) {
|
|
683
692
|
const { driverHost, driverPort } = resolveWebDriverAddress(browser);
|
|
@@ -698,7 +707,21 @@ function webdriverPost2(browser, path5, body) {
|
|
|
698
707
|
const id = sessionId(browser);
|
|
699
708
|
return id ? webdriverPost(address(browser), id, path5, body) : Promise.resolve(null);
|
|
700
709
|
}
|
|
701
|
-
function
|
|
710
|
+
function sessionExecutor(browser) {
|
|
711
|
+
const id = sessionId(browser);
|
|
712
|
+
const { host, port, pathPrefix } = configuredWebDriver(browser);
|
|
713
|
+
if (!id || !port) {
|
|
714
|
+
return void 0;
|
|
715
|
+
}
|
|
716
|
+
const at = {
|
|
717
|
+
hostname: String(host || "localhost"),
|
|
718
|
+
port: Number(port),
|
|
719
|
+
path: typeof pathPrefix === "string" ? pathPrefix : void 0,
|
|
720
|
+
onWarn: (message) => log3.warn(message)
|
|
721
|
+
};
|
|
722
|
+
return (script, args) => webdriverExecute(at, id, script, args);
|
|
723
|
+
}
|
|
724
|
+
function webdriverExecute2(browser, body) {
|
|
702
725
|
return webdriverPost2(browser, "execute/sync", { script: body, args: [] });
|
|
703
726
|
}
|
|
704
727
|
|
|
@@ -713,7 +736,7 @@ function captureActionSnapshot2(browser, command, timestamp, runner, native = fa
|
|
|
713
736
|
runner,
|
|
714
737
|
takeScreenshot: () => webdriverGet2(browser, "screenshot"),
|
|
715
738
|
...native ? {} : {
|
|
716
|
-
runScript: (src) =>
|
|
739
|
+
runScript: (src) => webdriverExecute2(browser, `return (${src})`),
|
|
717
740
|
getUrl: () => webdriverGet2(browser, "url"),
|
|
718
741
|
getTitle: () => webdriverGet2(browser, "title")
|
|
719
742
|
}
|
|
@@ -721,6 +744,7 @@ function captureActionSnapshot2(browser, command, timestamp, runner, native = fa
|
|
|
721
744
|
}
|
|
722
745
|
|
|
723
746
|
// src/helpers/perfLogs.ts
|
|
747
|
+
var RESOURCE_TIMING_MATCH_MS = 2e3;
|
|
724
748
|
function applyResponseReceived(p, response) {
|
|
725
749
|
const responseHeaders = {};
|
|
726
750
|
for (const [k, v] of Object.entries(response.headers || {})) {
|
|
@@ -746,7 +770,9 @@ function startPendingRequest(params, entry, pending) {
|
|
|
746
770
|
url: req.url,
|
|
747
771
|
method: req.method,
|
|
748
772
|
requestHeaders: req.headers ?? {},
|
|
749
|
-
|
|
773
|
+
// Epoch, from the event's own wall clock: every consumer dates a row by
|
|
774
|
+
// `timestamp`, and the monotonic `startTime` only diffs within a session.
|
|
775
|
+
timestamp: typeof params.wallTime === "number" ? Math.round(params.wallTime * 1e3) : entry.timestamp,
|
|
750
776
|
startTime
|
|
751
777
|
});
|
|
752
778
|
}
|
|
@@ -804,6 +830,95 @@ function parseNetworkFromPerfLogs(logs) {
|
|
|
804
830
|
}
|
|
805
831
|
return completed;
|
|
806
832
|
}
|
|
833
|
+
function resourceTimingRowsByUrl(rows) {
|
|
834
|
+
const byUrl = /* @__PURE__ */ new Map();
|
|
835
|
+
rows.forEach((row, index) => {
|
|
836
|
+
if (row.source !== NETWORK_CAPTURE_SOURCE.RESOURCE_TIMING) {
|
|
837
|
+
return;
|
|
838
|
+
}
|
|
839
|
+
const indexes = byUrl.get(row.url);
|
|
840
|
+
if (indexes) {
|
|
841
|
+
indexes.push(index);
|
|
842
|
+
} else {
|
|
843
|
+
byUrl.set(row.url, [index]);
|
|
844
|
+
}
|
|
845
|
+
});
|
|
846
|
+
return byUrl;
|
|
847
|
+
}
|
|
848
|
+
function takeNearestResourceTimingRow(indexes, rows, at) {
|
|
849
|
+
let best;
|
|
850
|
+
let bestDelta = RESOURCE_TIMING_MATCH_MS;
|
|
851
|
+
for (const index of indexes) {
|
|
852
|
+
const delta = Math.abs(rows[index].timestamp - at);
|
|
853
|
+
if (delta <= bestDelta) {
|
|
854
|
+
best = index;
|
|
855
|
+
bestDelta = delta;
|
|
856
|
+
}
|
|
857
|
+
}
|
|
858
|
+
if (best !== void 0) {
|
|
859
|
+
indexes.splice(indexes.indexOf(best), 1);
|
|
860
|
+
}
|
|
861
|
+
return best;
|
|
862
|
+
}
|
|
863
|
+
function overlay(row, entry) {
|
|
864
|
+
const merged = {
|
|
865
|
+
...row,
|
|
866
|
+
method: entry.method,
|
|
867
|
+
requestHeaders: entry.requestHeaders,
|
|
868
|
+
type: entry.type ?? row.type,
|
|
869
|
+
...entry.status !== void 0 ? { status: entry.status } : {},
|
|
870
|
+
...entry.statusText !== void 0 ? { statusText: entry.statusText } : {},
|
|
871
|
+
...entry.responseHeaders ? { responseHeaders: entry.responseHeaders } : {},
|
|
872
|
+
...entry.size !== void 0 ? { size: entry.size } : {},
|
|
873
|
+
...entry.error !== void 0 ? { error: entry.error } : {}
|
|
874
|
+
};
|
|
875
|
+
delete merged.source;
|
|
876
|
+
return merged;
|
|
877
|
+
}
|
|
878
|
+
function withoutLoadsTheLogListed(payload, listed) {
|
|
879
|
+
const requests = payload.networkRequests;
|
|
880
|
+
if (!Array.isArray(requests) || listed.length === 0) {
|
|
881
|
+
return payload;
|
|
882
|
+
}
|
|
883
|
+
const rows = requests;
|
|
884
|
+
const rowsByUrl = resourceTimingRowsByUrl(rows);
|
|
885
|
+
const taken = /* @__PURE__ */ new Set();
|
|
886
|
+
for (const load of listed) {
|
|
887
|
+
const index = takeNearestResourceTimingRow(
|
|
888
|
+
rowsByUrl.get(load.url) ?? [],
|
|
889
|
+
rows,
|
|
890
|
+
load.timestamp
|
|
891
|
+
);
|
|
892
|
+
if (index !== void 0) {
|
|
893
|
+
taken.add(index);
|
|
894
|
+
}
|
|
895
|
+
}
|
|
896
|
+
if (taken.size === 0) {
|
|
897
|
+
return payload;
|
|
898
|
+
}
|
|
899
|
+
return {
|
|
900
|
+
...payload,
|
|
901
|
+
networkRequests: rows.filter((_, index) => !taken.has(index))
|
|
902
|
+
};
|
|
903
|
+
}
|
|
904
|
+
function upgradeResourceTimingRows(incoming, existing) {
|
|
905
|
+
const rowsByUrl = resourceTimingRowsByUrl(existing);
|
|
906
|
+
const upgrades = [];
|
|
907
|
+
const fresh = [];
|
|
908
|
+
for (const entry of incoming) {
|
|
909
|
+
const index = takeNearestResourceTimingRow(
|
|
910
|
+
rowsByUrl.get(entry.url) ?? [],
|
|
911
|
+
existing,
|
|
912
|
+
entry.timestamp
|
|
913
|
+
);
|
|
914
|
+
if (index === void 0) {
|
|
915
|
+
fresh.push(entry);
|
|
916
|
+
continue;
|
|
917
|
+
}
|
|
918
|
+
upgrades.push({ index, request: overlay(existing[index], entry) });
|
|
919
|
+
}
|
|
920
|
+
return { upgrades, fresh };
|
|
921
|
+
}
|
|
807
922
|
function dedupeNetworkRequests(incoming, existing) {
|
|
808
923
|
const failedKey = (entry) => {
|
|
809
924
|
try {
|
|
@@ -856,6 +971,9 @@ var SessionCapturer = class extends SessionCapturerBase {
|
|
|
856
971
|
// True once BiDi inspectors are attached — the per-command perf-log network
|
|
857
972
|
// capture path skips when set, so we don't double-emit network requests.
|
|
858
973
|
bidiActive = false;
|
|
974
|
+
/** Whether BiDi has delivered at least one network event; a subscription
|
|
975
|
+
* that delivers nothing is not a source. */
|
|
976
|
+
bidiNetworkObserved = false;
|
|
859
977
|
/** Whether the session was created with `goog:loggingPrefs.performance`, set
|
|
860
978
|
* from the capabilities at session init. A perf log exists only because a
|
|
861
979
|
* session asked for one, so without it every fetch is a round trip that can
|
|
@@ -864,6 +982,10 @@ var SessionCapturer = class extends SessionCapturerBase {
|
|
|
864
982
|
* 21 of them in a two-test run, where XCUITest lists `performance` among its
|
|
865
983
|
* supported types and then serves none of it. */
|
|
866
984
|
perfLogsRequested = false;
|
|
985
|
+
/** Loads the performance log listed before the page's drain could, spent
|
|
986
|
+
* at the next drain that reaches the driver, whatever it finds, and
|
|
987
|
+
* emptied with the session that listed them. */
|
|
988
|
+
loadsListedByLog = [];
|
|
867
989
|
/** True once the collector is registered to run at document-start. Every
|
|
868
990
|
* document then instruments and anchors itself, so the paths that exist to
|
|
869
991
|
* notice a navigation after the fact — re-injection and the settle poll — have
|
|
@@ -876,6 +998,11 @@ var SessionCapturer = class extends SessionCapturerBase {
|
|
|
876
998
|
runner = nightwatchRunnerId(false);
|
|
877
999
|
actionSnapshots = [];
|
|
878
1000
|
snapshotCaptures = [];
|
|
1001
|
+
/** Native app network from appium-interceptor's proxy; started per session. */
|
|
1002
|
+
nativeNetwork = new NativeNetworkCapture(
|
|
1003
|
+
this,
|
|
1004
|
+
(level, message) => log4[level](message)
|
|
1005
|
+
);
|
|
879
1006
|
constructor(devtoolsOptions = {}, browser) {
|
|
880
1007
|
super(devtoolsOptions);
|
|
881
1008
|
this.#browser = browser;
|
|
@@ -1021,7 +1148,7 @@ var SessionCapturer = class extends SessionCapturerBase {
|
|
|
1021
1148
|
/** The current document's own birth time, which is what a DOM anchor is
|
|
1022
1149
|
* stamped with. Undefined when the session cannot answer. */
|
|
1023
1150
|
async documentOrigin(browser) {
|
|
1024
|
-
return
|
|
1151
|
+
return webdriverExecute2(browser, "return performance.timeOrigin;").then((origin) => origin ?? void 0).catch(() => void 0);
|
|
1025
1152
|
}
|
|
1026
1153
|
/**
|
|
1027
1154
|
* Wait for a navigation to actually replace the document, then drain with a
|
|
@@ -1104,7 +1231,7 @@ var SessionCapturer = class extends SessionCapturerBase {
|
|
|
1104
1231
|
return;
|
|
1105
1232
|
}
|
|
1106
1233
|
try {
|
|
1107
|
-
const alreadyPresent = await
|
|
1234
|
+
const alreadyPresent = await webdriverExecute2(
|
|
1108
1235
|
browser,
|
|
1109
1236
|
`return ${COLLECTOR_READY_EXPRESSION};`
|
|
1110
1237
|
);
|
|
@@ -1116,14 +1243,15 @@ var SessionCapturer = class extends SessionCapturerBase {
|
|
|
1116
1243
|
const script = document.createElement('script');
|
|
1117
1244
|
script.textContent = ${JSON.stringify(scriptContent)};
|
|
1118
1245
|
document.head.appendChild(script);
|
|
1119
|
-
return
|
|
1246
|
+
return ${COLLECTOR_READY_EXPRESSION};
|
|
1120
1247
|
`;
|
|
1121
|
-
await
|
|
1122
|
-
const hasCollector = await pollUntilReady(
|
|
1123
|
-
async () => await
|
|
1248
|
+
const injected = await webdriverExecute2(browser, injectionScript);
|
|
1249
|
+
const hasCollector = injected === true || await pollUntilReady(
|
|
1250
|
+
async () => await webdriverExecute2(
|
|
1124
1251
|
browser,
|
|
1125
1252
|
`return ${COLLECTOR_READY_EXPRESSION};`
|
|
1126
|
-
) === true
|
|
1253
|
+
) === true,
|
|
1254
|
+
{ checkFirst: true }
|
|
1127
1255
|
);
|
|
1128
1256
|
if (hasCollector) {
|
|
1129
1257
|
log4.info("\u2713 Script injected and collector ready");
|
|
@@ -1167,20 +1295,8 @@ var SessionCapturer = class extends SessionCapturerBase {
|
|
|
1167
1295
|
return;
|
|
1168
1296
|
}
|
|
1169
1297
|
const networkEntries = parseNetworkFromPerfLogs(logs);
|
|
1170
|
-
if (networkEntries.length
|
|
1171
|
-
|
|
1172
|
-
}
|
|
1173
|
-
const deduped = dedupeNetworkRequests(
|
|
1174
|
-
networkEntries,
|
|
1175
|
-
this.networkRequests
|
|
1176
|
-
);
|
|
1177
|
-
if (deduped.length > 0) {
|
|
1178
|
-
const normalized = deduped.map((d) => ({
|
|
1179
|
-
...d,
|
|
1180
|
-
type: d.type ?? "other"
|
|
1181
|
-
}));
|
|
1182
|
-
this.networkRequests.push(...normalized);
|
|
1183
|
-
this.sendUpstream("networkRequests", normalized);
|
|
1298
|
+
if (networkEntries.length > 0) {
|
|
1299
|
+
this.#ingestPerfLogEntries(networkEntries);
|
|
1184
1300
|
}
|
|
1185
1301
|
} catch (err) {
|
|
1186
1302
|
const msg = errorMessage(err) ?? "";
|
|
@@ -1189,6 +1305,30 @@ var SessionCapturer = class extends SessionCapturerBase {
|
|
|
1189
1305
|
}
|
|
1190
1306
|
}
|
|
1191
1307
|
}
|
|
1308
|
+
/** A log entry for a load the page already reported replaces that row in
|
|
1309
|
+
* place, under the row's own id; the rest are appended once. */
|
|
1310
|
+
#ingestPerfLogEntries(entries) {
|
|
1311
|
+
const { upgrades, fresh } = upgradeResourceTimingRows(
|
|
1312
|
+
entries,
|
|
1313
|
+
this.networkRequests
|
|
1314
|
+
);
|
|
1315
|
+
for (const { index, request } of upgrades) {
|
|
1316
|
+
this.networkRequests[index] = request;
|
|
1317
|
+
}
|
|
1318
|
+
const deduped = dedupeNetworkRequests(
|
|
1319
|
+
fresh,
|
|
1320
|
+
this.networkRequests
|
|
1321
|
+
);
|
|
1322
|
+
const appended = deduped.map((d) => ({ ...d, type: d.type ?? "other" }));
|
|
1323
|
+
this.networkRequests.push(...appended);
|
|
1324
|
+
this.loadsListedByLog.push(
|
|
1325
|
+
...appended.map(({ url, timestamp }) => ({ url, timestamp }))
|
|
1326
|
+
);
|
|
1327
|
+
const sent = [...upgrades.map((upgrade) => upgrade.request), ...appended];
|
|
1328
|
+
if (sent.length > 0) {
|
|
1329
|
+
this.sendUpstream("networkRequests", sent);
|
|
1330
|
+
}
|
|
1331
|
+
}
|
|
1192
1332
|
/** `forceAnchor` forces a full-DOM anchor of the current document before
|
|
1193
1333
|
* draining, so a freshly injected collector's async anchor isn't lost.
|
|
1194
1334
|
* `anchorTimestamp` is the command this drain's anchors were CAUSED by — pass
|
|
@@ -1196,28 +1336,45 @@ var SessionCapturer = class extends SessionCapturerBase {
|
|
|
1196
1336
|
* that command, since it overrides the inference at ingest. */
|
|
1197
1337
|
async captureTrace(browser, forceAnchor = false, anchorTimestamp) {
|
|
1198
1338
|
if (this.isNativeAppSession) {
|
|
1339
|
+
await this.nativeNetwork.forward({ throttle: true });
|
|
1199
1340
|
return;
|
|
1200
1341
|
}
|
|
1201
1342
|
await this.drainCollector(browser, forceAnchor, anchorTimestamp);
|
|
1202
1343
|
await this.captureNetworkFromPerformanceLogs(browser);
|
|
1203
1344
|
}
|
|
1345
|
+
/** At a test or scenario end, before its slice is written: the proxy drain there is never throttled. */
|
|
1346
|
+
async captureTraceAtTestEnd(browser) {
|
|
1347
|
+
if (this.isNativeAppSession) {
|
|
1348
|
+
await this.nativeNetwork.forward();
|
|
1349
|
+
return;
|
|
1350
|
+
}
|
|
1351
|
+
await this.captureTrace(browser, true);
|
|
1352
|
+
}
|
|
1204
1353
|
async drainCollector(browser, forceAnchor, anchorTimestamp) {
|
|
1205
1354
|
try {
|
|
1206
1355
|
const traceData = await drainCollectorWithRecovery({
|
|
1207
|
-
drain: () =>
|
|
1356
|
+
drain: () => webdriverExecute2(
|
|
1208
1357
|
browser,
|
|
1209
1358
|
collectorDrainExpression(forceAnchor)
|
|
1210
1359
|
),
|
|
1211
1360
|
injectIntoCurrentDocument: () => this.injectScript(browser),
|
|
1212
|
-
currentUrl: () =>
|
|
1361
|
+
currentUrl: () => webdriverExecute2(browser, "return location.href;").then(
|
|
1213
1362
|
(u) => u ?? void 0
|
|
1214
1363
|
),
|
|
1215
1364
|
log: (level, message) => log4[level](message)
|
|
1216
1365
|
});
|
|
1366
|
+
const listedByLog = this.loadsListedByLog;
|
|
1367
|
+
this.loadsListedByLog = [];
|
|
1217
1368
|
if (!traceData) {
|
|
1218
1369
|
return;
|
|
1219
1370
|
}
|
|
1220
|
-
this.processTracePayload(
|
|
1371
|
+
this.processTracePayload(
|
|
1372
|
+
withoutLoadsTheLogListed(traceData, listedByLog),
|
|
1373
|
+
{
|
|
1374
|
+
anchorTimestamp,
|
|
1375
|
+
skipResourceTimingRequests: this.bidiNetworkObserved
|
|
1376
|
+
}
|
|
1377
|
+
);
|
|
1221
1378
|
const mutationCount = Array.isArray(
|
|
1222
1379
|
traceData.mutations
|
|
1223
1380
|
) ? traceData.mutations.length : 0;
|
|
@@ -1631,17 +1788,23 @@ function callbackError(result, depth = 0) {
|
|
|
1631
1788
|
return void 0;
|
|
1632
1789
|
}
|
|
1633
1790
|
const r = result;
|
|
1634
|
-
if ("
|
|
1635
|
-
return depth < 2 ? callbackError(r.value, depth + 1) : void 0;
|
|
1636
|
-
}
|
|
1637
|
-
if ("passed" in r || !r.error) {
|
|
1791
|
+
if ("passed" in r) {
|
|
1638
1792
|
return void 0;
|
|
1639
1793
|
}
|
|
1640
|
-
|
|
1641
|
-
|
|
1642
|
-
|
|
1794
|
+
if (r.error instanceof Error) {
|
|
1795
|
+
return r.error;
|
|
1796
|
+
}
|
|
1797
|
+
if (r.error) {
|
|
1798
|
+
const err = new Error(String(r.message ?? r.error));
|
|
1799
|
+
if (typeof r.stack === "string") {
|
|
1800
|
+
err.stack = r.stack;
|
|
1801
|
+
}
|
|
1802
|
+
return err;
|
|
1803
|
+
}
|
|
1804
|
+
if ("value" in r) {
|
|
1805
|
+
return depth < 2 ? callbackError(r.value, depth + 1) : void 0;
|
|
1643
1806
|
}
|
|
1644
|
-
return
|
|
1807
|
+
return void 0;
|
|
1645
1808
|
}
|
|
1646
1809
|
|
|
1647
1810
|
// src/helpers/assertTarget.ts
|
|
@@ -2315,6 +2478,9 @@ var BrowserProxy = class {
|
|
|
2315
2478
|
this.attachScreenshot(browser, lastCommand, methodName);
|
|
2316
2479
|
}
|
|
2317
2480
|
this.anchorDestination(browser, methodName);
|
|
2481
|
+
if (!this.isDomMutating(methodName)) {
|
|
2482
|
+
void this.sessionCapturer.nativeNetwork.forward({ throttle: true });
|
|
2483
|
+
}
|
|
2318
2484
|
}
|
|
2319
2485
|
isDomMutating(methodName) {
|
|
2320
2486
|
return NAVIGATION_COMMANDS.includes(methodName) || [
|
|
@@ -2643,7 +2809,7 @@ function initReporterChain(ctx) {
|
|
|
2643
2809
|
}
|
|
2644
2810
|
function readViewport(browser) {
|
|
2645
2811
|
return resolveViewport(browser.capabilities || {}, {
|
|
2646
|
-
runScript: (body) =>
|
|
2812
|
+
runScript: (body) => webdriverExecute2(browser, body),
|
|
2647
2813
|
getWindowSize: () => webdriverGet2(browser, "window/rect"),
|
|
2648
2814
|
onWarn: (message) => log10.warn(message)
|
|
2649
2815
|
});
|
|
@@ -2681,6 +2847,18 @@ async function broadcastSessionMetadata(ctx, browser) {
|
|
|
2681
2847
|
log10.info(
|
|
2682
2848
|
`\u2713 Browser: ${browserName}${browserVersion ? " " + browserVersion : ""} (session: ${sessionId2})`
|
|
2683
2849
|
);
|
|
2850
|
+
if (ctx.mode === "live" && restoreBrowserFocus({
|
|
2851
|
+
browserName: String(browserName),
|
|
2852
|
+
platformName: String(
|
|
2853
|
+
capabilities.platformName || desiredCapabilities.platformName || ""
|
|
2854
|
+
),
|
|
2855
|
+
host: resolveWebDriverAddress(browser).driverHost,
|
|
2856
|
+
onError: (message) => log10.warn(message)
|
|
2857
|
+
})) {
|
|
2858
|
+
log10.info(
|
|
2859
|
+
"\u2713 Safari brought back to the front (it delivers clicks only there)"
|
|
2860
|
+
);
|
|
2861
|
+
}
|
|
2684
2862
|
const wantsPerfLog = wantsPerformanceLog(
|
|
2685
2863
|
capabilities,
|
|
2686
2864
|
desiredCapabilities
|
|
@@ -2688,7 +2866,7 @@ async function broadcastSessionMetadata(ctx, browser) {
|
|
|
2688
2866
|
ctx.sessionCapturer.perfLogsRequested = wantsPerfLog;
|
|
2689
2867
|
if (!wantsPerfLog && !ctx.bidiEnabled) {
|
|
2690
2868
|
log10.warn(
|
|
2691
|
-
"\u26A0 Network tab will
|
|
2869
|
+
"\u26A0 Network tab will carry the page's own Resource Timing only (no headers, no bodies). Add 'goog:loggingPrefs': { performance: 'ALL' } to your capabilities (or enable bidi:true) for full requests"
|
|
2692
2870
|
);
|
|
2693
2871
|
}
|
|
2694
2872
|
}
|
|
@@ -2701,7 +2879,7 @@ async function tryAttachBidi(ctx, browser) {
|
|
|
2701
2879
|
log10.warn("bidi:true set but browser.driver unavailable \u2014 skipping");
|
|
2702
2880
|
return;
|
|
2703
2881
|
}
|
|
2704
|
-
const { attachBidiHandlers, buildBidiSinks } = await import("./bidi-
|
|
2882
|
+
const { attachBidiHandlers, buildBidiSinks } = await import("./bidi-AZX3WRGJ.js");
|
|
2705
2883
|
const ok = await attachBidiHandlers(
|
|
2706
2884
|
driver,
|
|
2707
2885
|
buildBidiSinks(ctx.sessionCapturer)
|
|
@@ -2725,6 +2903,12 @@ async function armCaptureForSession(ctx, browser) {
|
|
|
2725
2903
|
ctx.armedSessionId = browser.sessionId ?? void 0;
|
|
2726
2904
|
await tryRegisterPreload(ctx, browser);
|
|
2727
2905
|
await tryAttachBidi(ctx, browser);
|
|
2906
|
+
await ctx.sessionCapturer.nativeNetwork.start({
|
|
2907
|
+
requested: requestsInterceptedNetwork(browser.desiredCapabilities),
|
|
2908
|
+
capabilities: browser.capabilities,
|
|
2909
|
+
execute: sessionExecutor(browser),
|
|
2910
|
+
unavailableReason: "the session id or the configured WebDriver port is unknown"
|
|
2911
|
+
});
|
|
2728
2912
|
}
|
|
2729
2913
|
function needsCaptureRearm(ctx, sessionId2) {
|
|
2730
2914
|
return Boolean(ctx.sessionCapturer) && sessionId2 !== void 0 && ctx.armedSessionId !== void 0 && ctx.armedSessionId !== sessionId2;
|
|
@@ -2736,6 +2920,9 @@ async function armReplacedSession(ctx, browser) {
|
|
|
2736
2920
|
}
|
|
2737
2921
|
ctx.sessionCapturer.preloadRegistered = false;
|
|
2738
2922
|
ctx.sessionCapturer.bidiActive = false;
|
|
2923
|
+
ctx.sessionCapturer.bidiNetworkObserved = false;
|
|
2924
|
+
ctx.sessionCapturer.loadsListedByLog = [];
|
|
2925
|
+
ctx.sessionCapturer.nativeNetwork.stop();
|
|
2739
2926
|
log10.info(`Session replaced \u2014 re-arming capture for ${sessionId2}`);
|
|
2740
2927
|
await armCaptureForSession(ctx, browser);
|
|
2741
2928
|
}
|
|
@@ -3222,7 +3409,7 @@ async function captureCucumberScenarioBeforeQuit(ctx, browser, result) {
|
|
|
3222
3409
|
assertCalls
|
|
3223
3410
|
);
|
|
3224
3411
|
}
|
|
3225
|
-
await ctx.sessionCapturer.
|
|
3412
|
+
await ctx.sessionCapturer.captureTraceAtTestEnd(browser);
|
|
3226
3413
|
flushTestSlice(ctx);
|
|
3227
3414
|
await ctx.emitTestArtifacts(
|
|
3228
3415
|
scenario?.uid,
|
|
@@ -3943,7 +4130,7 @@ var NightwatchDevToolsPlugin = class {
|
|
|
3943
4130
|
this.browserProxy.drainNativeAssertCalls()
|
|
3944
4131
|
);
|
|
3945
4132
|
}
|
|
3946
|
-
await this.sessionCapturer.
|
|
4133
|
+
await this.sessionCapturer.captureTraceAtTestEnd(browser);
|
|
3947
4134
|
flushTestSlice(this.#getInternals());
|
|
3948
4135
|
const results = browser.currentTest?.results;
|
|
3949
4136
|
const failed = !!results && ((results.errors ?? 0) > 0 || (results.failed ?? 0) > 0);
|
|
@@ -3989,6 +4176,7 @@ var NightwatchDevToolsPlugin = class {
|
|
|
3989
4176
|
});
|
|
3990
4177
|
}
|
|
3991
4178
|
async after(browser) {
|
|
4179
|
+
this.sessionCapturer?.nativeNetwork.stop();
|
|
3992
4180
|
await this.#screencast.rotation;
|
|
3993
4181
|
await this.#finalizeCurrentScreencast();
|
|
3994
4182
|
try {
|