@xenon-device-management/xenon 2.15.0 → 2.16.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 +38 -26
- package/lib/package.json +2 -1
- package/lib/public/assets/{AnnotationOverlay-Btq3dZRI.js → AnnotationOverlay-2W4JvEyZ.js} +1 -1
- package/lib/public/assets/{ApiKeyGate-C9OoAgg_.js → ApiKeyGate-GmckX_7r.js} +1 -1
- package/lib/public/assets/{DeviceMosaicView-DBC_xczB.js → DeviceMosaicView-DqODzkyo.js} +1 -1
- package/lib/public/assets/{EmptyState-DqWU2-vM.js → EmptyState-7hETKEWb.js} +1 -1
- package/lib/public/assets/{FieldGroup-CveQ3xrA.js → FieldGroup-GOgssFEP.js} +1 -1
- package/lib/public/assets/{FilterMenu-BfKDSfHz.js → FilterMenu-Yvf6RGMd.js} +1 -1
- package/lib/public/assets/{Menu-CtdCybzA.js → Menu-KrPnfdsf.js} +1 -1
- package/lib/public/assets/{Modal-Bsvj1tm2.js → Modal-C6WjZFeI.js} +1 -1
- package/lib/public/assets/{RecordingPage-CRofOiyx.js → RecordingPage-CSRIhZEh.js} +1 -1
- package/lib/public/assets/{RecordingsPage-jNQHb2IR.js → RecordingsPage-CB8ST3nE.js} +1 -1
- package/lib/public/assets/{SegmentedControl-C8HtTha-.js → SegmentedControl-C9o90KId.js} +1 -1
- package/lib/public/assets/{SettingCard-D8K1LZNo.js → SettingCard-CPm3w9l2.js} +1 -1
- package/lib/public/assets/{Table-zNTX92Pn.js → Table-DwIgERRt.js} +1 -1
- package/lib/public/assets/{activity-CJ8LwZq9.js → activity-DrLbHifn.js} +1 -1
- package/lib/public/assets/{ai-settings-CgxQZqrc.js → ai-settings-DU_Ls06P.js} +1 -1
- package/lib/public/assets/{api-keys-ECpo-zTo.js → api-keys-CWYdEzJ_.js} +1 -1
- package/lib/public/assets/{apps-BjGymyf3.js → apps-BqNVdln7.js} +1 -1
- package/lib/public/assets/{arrow-left-BWfCbeHP.js → arrow-left-BtB0_Jg_.js} +1 -1
- package/lib/public/assets/{arrow-right-DC7UGf_n.js → arrow-right-C8NhdtyN.js} +1 -1
- package/lib/public/assets/{arrow-up-right-BOjwybDk.js → arrow-up-right-gMg0ffvv.js} +1 -1
- package/lib/public/assets/{auth-shell-CxMTo9aH.js → auth-shell-CiYZ3N4M.js} +2 -2
- package/lib/public/assets/{builds-page-DOCm4HkA.js → builds-page-EaaqDmLN.js} +1 -1
- package/lib/public/assets/{button-Cg08-EMT.js → button-DDau4Axv.js} +1 -1
- package/lib/public/assets/{calendar-CA1ikLJs.js → calendar-DQ83B1ed.js} +1 -1
- package/lib/public/assets/{check-BMTaH72W.js → check-K1ovTQam.js} +1 -1
- package/lib/public/assets/{chevron-right-B2QOv9Nx.js → chevron-right-Biq00pMt.js} +1 -1
- package/lib/public/assets/{circle-check-CYXBoUqT.js → circle-check-CBv88cH7.js} +1 -1
- package/lib/public/assets/{circle-x-DTwnSAnl.js → circle-x-Bmltailn.js} +1 -1
- package/lib/public/assets/{clock-C8_HViQP.js → clock-BPrld-3h.js} +1 -1
- package/lib/public/assets/{copy-Be7LNMXV.js → copy-t0QB14JI.js} +1 -1
- package/lib/public/assets/{device-explorer-BOqtfGXz.js → device-explorer-BgGZ8AvV.js} +1 -1
- package/lib/public/assets/{download-B56lwxWP.js → download-BroHxNOh.js} +1 -1
- package/lib/public/assets/{forgot-password-CCU6bzLn.js → forgot-password-Bjmf49i6.js} +1 -1
- package/lib/public/assets/{h264Stream-C-OtmIuk.js → h264Stream-DeD__6wa.js} +1 -1
- package/lib/public/assets/{index-CfxsUslp.js → index-72oF89LJ.js} +1 -1
- package/lib/public/assets/{index-B9KQ6LsY.js → index-B0JeuZbg.js} +1 -1
- package/lib/public/assets/index-B0gQkEg0.js +219 -0
- package/lib/public/assets/{input-CZHFjbSg.js → input-DIc4MSC7.js} +1 -1
- package/lib/public/assets/{line-chart-Gl_Dz3X_.js → line-chart-BcWm2vW8.js} +1 -1
- package/lib/public/assets/{list-checks-CCgjeyGY.js → list-checks-B06IJJad.js} +1 -1
- package/lib/public/assets/{lock-CkOwgDcx.js → lock-pVLvnN30.js} +1 -1
- package/lib/public/assets/{login-D5cc4w7M.js → login-CN77vy--.js} +1 -1
- package/lib/public/assets/{maintenance-settings-t7qr3fvX.js → maintenance-settings-CSdzQSkY.js} +1 -1
- package/lib/public/assets/{monitor-DEJixKQO.js → monitor-BtrN5IkI.js} +1 -1
- package/lib/public/assets/{mouse-pointer-2-CtOrk4M8.js → mouse-pointer-2-CNsQfo6w.js} +1 -1
- package/lib/public/assets/{network-Dii9qdvk.js → network-BSfA8ZxG.js} +1 -1
- package/lib/public/assets/{overview-rolAACrO.js → overview-BfTebCK5.js} +1 -1
- package/lib/public/assets/{page-header-COQZxJfR.js → page-header-D8KUX6Qg.js} +1 -1
- package/lib/public/assets/{play-DrN1Se_R.js → play-Cvc8xzic.js} +1 -1
- package/lib/public/assets/{plus-5aq-9uRx.js → plus-C9Zj84UF.js} +1 -1
- package/lib/public/assets/{profile-page-6i5kY6kF.js → profile-page-Bi27STgs.js} +1 -1
- package/lib/public/assets/{reset-password-sp2IBrc7.js → reset-password-CPsOuwSu.js} +1 -1
- package/lib/public/assets/{runbook-page-DJDjeopU.js → runbook-page-DEQ-DBOX.js} +153 -102
- package/lib/public/assets/{select-Bpyn7rWO.js → select-BWpnhWXj.js} +1 -1
- package/lib/public/assets/{selector-detail-redirect-BH74vmKv.js → selector-detail-redirect-BefSCSpe.js} +1 -1
- package/lib/public/assets/{selector-health-page-Cw_g218H.js → selector-health-page-MSk96Zz7.js} +1 -1
- package/lib/public/assets/session-detail-page-DPyBNef_.js +4 -0
- package/lib/public/assets/{settings-C3uyGhyc.js → settings-V6x-cXwy.js} +1 -1
- package/lib/public/assets/{stat-tile-xVqAKIlI.js → stat-tile-Duex-GZD.js} +1 -1
- package/lib/public/assets/{tablet-BxUScAeO.js → tablet-BBcoqBmL.js} +1 -1
- package/lib/public/assets/{teams-B5ljGkY2.js → teams-ts8p5rEh.js} +1 -1
- package/lib/public/assets/{trash-2-B701i8sy.js → trash-2-CZ2ew6AZ.js} +1 -1
- package/lib/public/assets/{upload-C2gr44RS.js → upload-Dv99y_le.js} +1 -1
- package/lib/public/assets/{use-builds-data-NeQOFEoy.js → use-builds-data-DrYC8JCF.js} +1 -1
- package/lib/public/assets/{use-password-reset-mode-DEzJq5ef.js → use-password-reset-mode-C7QkBNkP.js} +1 -1
- package/lib/public/assets/{users-D_vv_yz4.js → users-D7tKMESH.js} +1 -1
- package/lib/public/assets/{users-MY6eNe6s.js → users-gJ5Y01yo.js} +1 -1
- package/lib/public/assets/{video-off-OK1lNZQn.js → video-off-CqZVuZGs.js} +1 -1
- package/lib/public/assets/{webhook-settings-BU-C9lBf.js → webhook-settings-DI5DSW99.js} +1 -1
- package/lib/public/assets/{zap-DO9ry1iz.js → zap-ClcHFxfo.js} +1 -1
- package/lib/public/index.html +1 -1
- package/lib/schema.json +11 -7
- package/lib/src/InternalHttpClient.js +27 -5
- package/lib/src/XenonCapabilityManager.js +10 -3
- package/lib/src/app/openapi/sessions.yaml +7 -5
- package/lib/src/app/routers/auth.js +2 -1
- package/lib/src/app/routers/users.js +2 -1
- package/lib/src/app/ws/nodeSocketRelay.js +77 -11
- package/lib/src/dashboard/commandLogFields.js +42 -0
- package/lib/src/dashboard/event-manager.js +26 -154
- package/lib/src/dashboard/services/failure-analysis-service.js +8 -86
- package/lib/src/dashboard/services/failureCategories.js +264 -0
- package/lib/src/device-managers/AndroidDeviceManager.js +61 -23
- package/lib/src/device-managers/IOSDeviceManager.js +10 -0
- package/lib/src/device-utils.js +14 -6
- package/lib/src/gateway/forwardToNode.js +4 -11
- package/lib/src/gateway/hubSessionToken.js +27 -4
- package/lib/src/gateway/nodeWebDriverUrl.js +3 -5
- package/lib/src/helpers/outboundProxy.js +219 -0
- package/lib/src/index.js +21 -0
- package/lib/src/interceptors/CommandInterceptor.js +13 -15
- package/lib/src/interceptors/xenonScripts.js +28 -7
- package/lib/src/logger.js +3 -1
- package/lib/src/plugin.js +8 -3
- package/lib/src/scripts/database-check.js +3 -3
- package/lib/src/scripts/initialize-database.js +35 -33
- package/lib/src/scripts/run-migrations.js +1079 -27
- package/lib/src/services/ApiKeyService.js +16 -5
- package/lib/src/services/InterceptorService.js +6 -2
- package/lib/src/services/OrphanSweeper.js +6 -0
- package/lib/src/services/SelectorStateService.js +11 -7
- package/lib/src/services/SelectorVerificationJob.js +6 -2
- package/lib/src/services/SessionLifecycleService.js +32 -18
- package/lib/src/services/SocketClient.js +11 -0
- package/lib/src/services/SocketServer.js +361 -42
- package/lib/src/services/TeamService.js +5 -1
- package/lib/src/services/TracingService.js +171 -60
- package/lib/src/services/UserService.js +14 -3
- package/lib/src/services/UserSessionService.js +16 -4
- package/lib/src/services/healing/HealingOrchestrator.js +3 -1
- package/lib/src/services/identity/identityChanges.js +41 -0
- package/lib/src/services/logcat/SessionDeviceLogs.js +97 -10
- package/lib/src/services/logcat/deviceLogBook.js +5 -4
- package/lib/src/services/logcat/iosDriverLog.js +178 -0
- package/lib/src/services/selector-health/SelectorVisibilityResolver.js +121 -0
- package/lib/src/services/token/JwtKeyService.js +8 -2
- package/lib/src/services/token/mcpTokenTtl.js +27 -0
- package/lib/src/sessions/LocalSession.js +29 -1
- package/lib/src/sessions/RemoteSession.js +12 -2
- package/lib/src/sessions/SessionManager.js +5 -8
- package/lib/test/helpers/driver-log.js +38 -0
- package/lib/test/helpers/fake-http-proxy.js +106 -0
- package/lib/test/helpers/otlp-probe.js +107 -0
- package/lib/test/integration/androidDevices.spec.js +1 -1
- package/lib/test/integration/ios/01iOSSimulator.spec.js +1 -1
- package/lib/test/integration/ios/02iOSDevices.spec.js +1 -1
- package/lib/test/integration/selector-state-flow.spec.js +5 -5
- package/lib/test/unit/XenonCapabilityManager.spec.js +23 -0
- package/lib/test/unit/android-adb-retry.spec.js +112 -0
- package/lib/test/unit/command-log-network-capture.spec.js +191 -0
- package/lib/test/unit/dashboard-events-device-logs.spec.js +59 -11
- package/lib/test/unit/dashboard-events-team-scope.spec.js +6 -3
- package/lib/test/unit/database-check.spec.js +1 -0
- package/lib/test/unit/device-utils.spec.js +13 -4
- package/lib/test/unit/event-log-session-data.spec.js +11 -4
- package/lib/test/unit/failure-analysis.spec.js +22 -8
- package/lib/test/unit/failure-categories.spec.js +24 -22
- package/lib/test/unit/failure-categorization.spec.js +393 -0
- package/lib/test/unit/healing-tier-gate.spec.js +3 -2
- package/lib/test/unit/hub-node-gateway-appium-server.spec.js +72 -1
- package/lib/test/unit/ios-driver-log.spec.js +168 -0
- package/lib/test/unit/ios-simulator-session-readiness.spec.js +187 -0
- package/lib/test/unit/ios-simulator-session-teardown.spec.js +158 -0
- package/lib/test/unit/logcat-ws.spec.js +78 -37
- package/lib/test/unit/logger-json-levels.spec.js +44 -0
- package/lib/test/unit/mcp-token-ttl.spec.js +38 -0
- package/lib/test/unit/observability-example-loki.spec.js +25 -0
- package/lib/test/unit/openapi-for-site.spec.js +80 -0
- package/lib/test/unit/outbound-proxy.spec.js +320 -0
- package/lib/test/unit/port-exhaustion.spec.js +2 -1
- package/lib/test/unit/proxy-option.spec.js +358 -0
- package/lib/test/unit/run-migrations-database.spec.js +807 -0
- package/lib/test/unit/run-migrations-fail-fast.test.js +41 -27
- package/lib/test/unit/run-migrations.test.js +864 -0
- package/lib/test/unit/selector-health-actions.spec.js +3 -1
- package/lib/test/unit/selector-state-service.spec.js +72 -23
- package/lib/test/unit/selector-verification-clean-build.spec.js +1 -1
- package/lib/test/unit/selector-verification-job.spec.js +15 -6
- package/lib/test/unit/selector-verification-missed-regression.spec.js +2 -2
- package/lib/test/unit/selector-visibility-resolver.spec.js +209 -0
- package/lib/test/unit/session-device-logs-ios.spec.js +180 -0
- package/lib/test/unit/session-device-logs.spec.js +11 -2
- package/lib/test/unit/session-end-failure-analysis.spec.js +5 -5
- package/lib/test/unit/session-log-indexes.spec.js +1 -1
- package/lib/test/unit/socket-team-scope.integration.spec.js +749 -0
- package/lib/test/unit/socketServer.scopedEmit.spec.js +237 -5
- package/lib/test/unit/tracing-export-routing.spec.js +137 -0
- package/lib/test/unit/tracing-session-spans.spec.js +337 -0
- package/lib/test/unit/unexpected-shutdown-reason.spec.js +2 -2
- package/lib/tsconfig.tsbuildinfo +1 -1
- package/package.json +2 -1
- package/schema.json +11 -7
- package/scripts/dev/readme-screenshots.js +276 -23
- package/scripts/dev/screenshot-fixtures/logcat.js +157 -0
- package/scripts/dev/screenshot-fixtures/phone-screen.html +469 -0
- package/scripts/dev/screenshot-fixtures/session.js +242 -0
- package/scripts/export-openapi.js +32 -0
- package/scripts/lib/openapi-for-site.js +79 -0
- package/lib/public/assets/index-BOxe43p1.js +0 -219
- package/lib/public/assets/session-detail-page-AZJE6XXE.js +0 -4
- package/lib/src/scripts/prepare-prisma.js +0 -89
|
@@ -0,0 +1,180 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
|
|
3
|
+
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
|
|
4
|
+
return new (P || (P = Promise))(function (resolve, reject) {
|
|
5
|
+
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
|
|
6
|
+
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
|
|
7
|
+
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
|
|
8
|
+
step((generator = generator.apply(thisArg, _arguments || [])).next());
|
|
9
|
+
});
|
|
10
|
+
};
|
|
11
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
12
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
13
|
+
};
|
|
14
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
15
|
+
require("reflect-metadata");
|
|
16
|
+
const chai_1 = require("chai");
|
|
17
|
+
const supertest_1 = __importDefault(require("supertest"));
|
|
18
|
+
const SessionDeviceLogs_1 = require("../../src/services/logcat/SessionDeviceLogs");
|
|
19
|
+
const deviceLogBook_1 = require("../../src/services/logcat/deviceLogBook");
|
|
20
|
+
const scratch_database_1 = require("../helpers/scratch-database");
|
|
21
|
+
const selector_health_fixture_1 = require("../helpers/selector-health-fixture");
|
|
22
|
+
// The driver's own log class (test/helpers/driver-log.ts): an iPhone's and a
|
|
23
|
+
// simulator's `logs.syslog` extend it, so what Xenon reads is what the driver keeps.
|
|
24
|
+
const driver_log_1 = require("../helpers/driver-log");
|
|
25
|
+
// How the Device logs tab reads an iPhone line's level (web log-derive.ts).
|
|
26
|
+
const DASHBOARD_SYSLOG = /<(Error|Fault|Warning)>/;
|
|
27
|
+
class TestDeviceLogs extends SessionDeviceLogs_1.SessionDeviceLogs {
|
|
28
|
+
context() {
|
|
29
|
+
return { pluginArgs: { bindHostOrIp: '127.0.0.1' }, port: 4723, nodeId: 'this-server' };
|
|
30
|
+
}
|
|
31
|
+
flushIntervalMs() {
|
|
32
|
+
return 60000;
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
const IPHONE = {
|
|
36
|
+
udid: '00008110-000A',
|
|
37
|
+
platform: 'ios',
|
|
38
|
+
realDevice: true,
|
|
39
|
+
host: 'http://127.0.0.1:4723',
|
|
40
|
+
nodeId: 'this-server',
|
|
41
|
+
};
|
|
42
|
+
const SIMULATOR = Object.assign(Object.assign({}, IPHONE), { udid: 'SIM-1', realDevice: false });
|
|
43
|
+
const syslog = (n, level = 'Notice') => `Oct 5 20:31:${String(n % 60).padStart(2, '0')} iPhone Shop[4127] <${level}>: line ${n}`;
|
|
44
|
+
function deviceLogs(sessionId) {
|
|
45
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
46
|
+
const res = yield (0, supertest_1.default)((0, selector_health_fixture_1.selectorHealthApp)(selector_health_fixture_1.ADMIN)).get(`/session/${sessionId}/logs/device`);
|
|
47
|
+
(0, chai_1.expect)(res.status, res.text).to.equal(200);
|
|
48
|
+
return res.body;
|
|
49
|
+
});
|
|
50
|
+
}
|
|
51
|
+
describe("An iPhone or simulator session's Device logs", function () {
|
|
52
|
+
this.timeout(60000);
|
|
53
|
+
const scratch = (0, scratch_database_1.useScratchDatabase)();
|
|
54
|
+
let logs;
|
|
55
|
+
let sessionId;
|
|
56
|
+
let driverLog;
|
|
57
|
+
let count = 0;
|
|
58
|
+
beforeEach(() => __awaiter(this, void 0, void 0, function* () {
|
|
59
|
+
count += 1;
|
|
60
|
+
sessionId = `dl-ios-${count}`;
|
|
61
|
+
yield scratch.db.session.create({
|
|
62
|
+
data: {
|
|
63
|
+
id: sessionId,
|
|
64
|
+
device_udid: IPHONE.udid,
|
|
65
|
+
device_platform: 'ios',
|
|
66
|
+
device_version: '26.0',
|
|
67
|
+
desired_capabilities: '{}',
|
|
68
|
+
session_capabilities: '{}',
|
|
69
|
+
node_id: 'this-server',
|
|
70
|
+
has_live_video: false,
|
|
71
|
+
},
|
|
72
|
+
});
|
|
73
|
+
logs = new TestDeviceLogs();
|
|
74
|
+
driverLog = new driver_log_1.TestDriverLog();
|
|
75
|
+
}));
|
|
76
|
+
afterEach(() => __awaiter(this, void 0, void 0, function* () {
|
|
77
|
+
yield logs.stop(sessionId);
|
|
78
|
+
}));
|
|
79
|
+
it('keeps the lines from the create and from the run, each once, in order', () => __awaiter(this, void 0, void 0, function* () {
|
|
80
|
+
// Logged during the create, before Xenon could listen: in the driver's buffer.
|
|
81
|
+
for (let i = 0; i < 3; i++)
|
|
82
|
+
driverLog.line(syslog(i));
|
|
83
|
+
yield logs.start({ sessionId, device: IPHONE, since: Date.now() - 60000, driverLog });
|
|
84
|
+
for (let i = 3; i < 250; i++)
|
|
85
|
+
driverLog.line(syslog(i));
|
|
86
|
+
yield logs.stop(sessionId);
|
|
87
|
+
(0, chai_1.expect)(driverLog.listenerCount('output')).to.equal(0);
|
|
88
|
+
driverLog.line(syslog(999)); // after the session: not its line
|
|
89
|
+
const rows = yield deviceLogs(sessionId);
|
|
90
|
+
(0, chai_1.expect)(rows.map((r) => r.message)).to.deep.equal(Array.from({ length: 250 }, (_, i) => syslog(i)));
|
|
91
|
+
}));
|
|
92
|
+
it("leaves the driver's lines to the test that asks for them", () => __awaiter(this, void 0, void 0, function* () {
|
|
93
|
+
driverLog.line(syslog(1));
|
|
94
|
+
yield logs.start({ sessionId, device: IPHONE, since: Date.now() - 60000, driverLog });
|
|
95
|
+
driverLog.line(syslog(2));
|
|
96
|
+
yield logs.stop(sessionId);
|
|
97
|
+
const forTheTest = yield driverLog.getLogs();
|
|
98
|
+
(0, chai_1.expect)(forTheTest.map((e) => e.message)).to.deep.equal([
|
|
99
|
+
syslog(1),
|
|
100
|
+
syslog(2),
|
|
101
|
+
]);
|
|
102
|
+
}));
|
|
103
|
+
it("keeps each line's text, so Errors only finds the phone's errors, at the time it arrived", () => __awaiter(this, void 0, void 0, function* () {
|
|
104
|
+
yield logs.start({ sessionId, device: IPHONE, since: Date.now() - 60000, driverLog });
|
|
105
|
+
const before = Date.now();
|
|
106
|
+
driverLog.line(syslog(1));
|
|
107
|
+
driverLog.line(syslog(2, 'Error'));
|
|
108
|
+
driverLog.line(syslog(3, 'Fault'));
|
|
109
|
+
const after = Date.now();
|
|
110
|
+
yield logs.stop(sessionId);
|
|
111
|
+
const rows = yield deviceLogs(sessionId);
|
|
112
|
+
(0, chai_1.expect)(rows.map((r) => { var _a, _b; return (_b = (_a = DASHBOARD_SYSLOG.exec(r.message)) === null || _a === void 0 ? void 0 : _a[1]) !== null && _b !== void 0 ? _b : null; })).to.deep.equal([
|
|
113
|
+
null,
|
|
114
|
+
'Error',
|
|
115
|
+
'Fault',
|
|
116
|
+
]);
|
|
117
|
+
for (const r of rows) {
|
|
118
|
+
const t = new Date(r.timestamp).getTime();
|
|
119
|
+
(0, chai_1.expect)(t).to.be.within(before, after);
|
|
120
|
+
}
|
|
121
|
+
}));
|
|
122
|
+
it("records a simulator's compact lines, without `log stream`'s banner", () => __awaiter(this, void 0, void 0, function* () {
|
|
123
|
+
driverLog.line('Filtering the log data using "subsystem != \\"com.apple.CoreTelephony\\""');
|
|
124
|
+
driverLog.line('Timestamp Ty Process[PID:TID]');
|
|
125
|
+
yield logs.start({ sessionId, device: SIMULATOR, since: Date.now() - 60000, driverLog });
|
|
126
|
+
driverLog.line('2026-10-05 20:31:40.123 F Shop[4127:8812] Terminating app');
|
|
127
|
+
driverLog.line('\tThread 0 Crashed:');
|
|
128
|
+
yield logs.stop(sessionId);
|
|
129
|
+
(0, chai_1.expect)((yield deviceLogs(sessionId)).map((r) => r.message)).to.deep.equal([
|
|
130
|
+
'2026-10-05 20:31:40.123 F Shop[4127:8812] Terminating app',
|
|
131
|
+
'\tThread 0 Crashed:',
|
|
132
|
+
]);
|
|
133
|
+
}));
|
|
134
|
+
it("keeps the app's own lines, the errors inside it, the app manager's lines and faults, and says so first", () => __awaiter(this, void 0, void 0, function* () {
|
|
135
|
+
const line = (proc, text, level = 'Notice') => `Oct 5 23:48:33 Rabindras-iPhone ${proc} <${level}>: ${text}`;
|
|
136
|
+
// From the create, before Xenon listens: the app's first line comes
|
|
137
|
+
// before the phone announces its pid.
|
|
138
|
+
driverLog.line(line('Shop[4127]', 'first line'));
|
|
139
|
+
driverLog.line(line('runningboardd(RunningBoard)[34]', 'Acquiring [app<com.example.shop(UUID)>:4127]'));
|
|
140
|
+
yield logs.start({
|
|
141
|
+
sessionId,
|
|
142
|
+
device: IPHONE,
|
|
143
|
+
since: Date.now() - 60000,
|
|
144
|
+
driverLog,
|
|
145
|
+
appUnderTest: 'com.example.shop',
|
|
146
|
+
});
|
|
147
|
+
driverLog.line(line('duetexpertd[412]', 'chatter'));
|
|
148
|
+
driverLog.line(line('Shop(Networking)[4127]', 'request failed', 'Error'));
|
|
149
|
+
driverLog.line(line('wifid[61]', 'link lost', 'Fault'));
|
|
150
|
+
yield logs.stop(sessionId);
|
|
151
|
+
(0, chai_1.expect)((yield deviceLogs(sessionId)).map((r) => r.message)).to.deep.equal([
|
|
152
|
+
"Xenon: Kept here: what the app under test (com.example.shop) logs itself, every error inside it, what the phone says about its launch, state, crashes and end, and faults. The rest of the phone's log is left out.",
|
|
153
|
+
line('Shop[4127]', 'first line'),
|
|
154
|
+
line('runningboardd(RunningBoard)[34]', 'Acquiring [app<com.example.shop(UUID)>:4127]'),
|
|
155
|
+
line('Shop(Networking)[4127]', 'request failed', 'Error'),
|
|
156
|
+
line('wifid[61]', 'link lost', 'Fault'),
|
|
157
|
+
]);
|
|
158
|
+
}));
|
|
159
|
+
it('records nothing for a session that skips log capture', () => __awaiter(this, void 0, void 0, function* () {
|
|
160
|
+
yield logs.start({ sessionId, device: IPHONE, since: Date.now(), driverLog: undefined });
|
|
161
|
+
(0, chai_1.expect)(logs.isRecording(sessionId)).to.equal(false);
|
|
162
|
+
(0, chai_1.expect)(yield deviceLogs(sessionId)).to.deep.equal([]);
|
|
163
|
+
}));
|
|
164
|
+
it(`holds to the same limit as Android, ${deviceLogBook_1.DEVICE_LOG_LINE_LIMIT} lines then errors`, () => __awaiter(this, void 0, void 0, function* () {
|
|
165
|
+
yield logs.start({ sessionId, device: IPHONE, since: Date.now() - 60000, driverLog });
|
|
166
|
+
for (let i = 0; i < deviceLogBook_1.DEVICE_LOG_LINE_LIMIT + 100; i++) {
|
|
167
|
+
driverLog.line(syslog(i, i >= deviceLogBook_1.DEVICE_LOG_LINE_LIMIT && i % 2 === 0 ? 'Error' : 'Notice'));
|
|
168
|
+
}
|
|
169
|
+
yield logs.stop(sessionId);
|
|
170
|
+
const rows = yield deviceLogs(sessionId);
|
|
171
|
+
(0, chai_1.expect)(rows).to.have.length(deviceLogBook_1.DEVICE_LOG_LINE_LIMIT + 1 + 50 + 1);
|
|
172
|
+
(0, chai_1.expect)(rows[deviceLogBook_1.DEVICE_LOG_LINE_LIMIT].message).to.match(/^Xenon: This session reached 10,000/);
|
|
173
|
+
(0, chai_1.expect)(rows[rows.length - 1].message).to.match(/^Xenon: 50 device log lines were left out/);
|
|
174
|
+
}));
|
|
175
|
+
it("doesn't record a node's iPhone, whose driver is the node's", () => {
|
|
176
|
+
(0, chai_1.expect)(logs.appliesTo(IPHONE)).to.equal(true);
|
|
177
|
+
(0, chai_1.expect)(logs.appliesTo(SIMULATOR)).to.equal(true);
|
|
178
|
+
(0, chai_1.expect)(logs.appliesTo(Object.assign(Object.assign({}, IPHONE), { nodeId: 'a-node' }))).to.equal(false);
|
|
179
|
+
});
|
|
180
|
+
});
|
|
@@ -311,10 +311,12 @@ describe("An Android session's Device logs", function () {
|
|
|
311
311
|
(0, chai_1.expect)(errors.every((r) => levelOf(r.message) === 'E')).to.equal(true);
|
|
312
312
|
(0, chai_1.expect)(rows[rows.length - 1].message).to.equal("Xenon: 1,000 device log lines were left out to keep this session's log within its limit.");
|
|
313
313
|
}));
|
|
314
|
-
it("only records this server's own
|
|
314
|
+
it("only records this server's own phones", () => {
|
|
315
315
|
(0, chai_1.expect)(logs.appliesTo(PHONE)).to.equal(true);
|
|
316
316
|
(0, chai_1.expect)(logs.appliesTo(Object.assign(Object.assign({}, PHONE), { nodeId: 'a-node' }))).to.equal(false);
|
|
317
|
-
|
|
317
|
+
// An iPhone is recorded from its driver's log (session-device-logs-ios.spec.ts).
|
|
318
|
+
(0, chai_1.expect)(logs.appliesTo(Object.assign(Object.assign({}, PHONE), { platform: 'ios' }))).to.equal(true);
|
|
319
|
+
(0, chai_1.expect)(logs.appliesTo(Object.assign(Object.assign({}, PHONE), { platform: 'windows' }))).to.equal(false);
|
|
318
320
|
(0, chai_1.expect)(logs.appliesTo(Object.assign(Object.assign({}, PHONE), { cloud: true }))).to.equal(false);
|
|
319
321
|
(0, chai_1.expect)(logs.appliesTo(undefined)).to.equal(false);
|
|
320
322
|
});
|
|
@@ -328,6 +330,13 @@ describe("An Android session's Device logs", function () {
|
|
|
328
330
|
yield logs.stop(sessionId);
|
|
329
331
|
(0, chai_1.expect)(textOf(yield deviceLogs(sessionId))).to.deep.equal(['during']);
|
|
330
332
|
}));
|
|
333
|
+
it('says in the log when the session turned it off', () => __awaiter(this, void 0, void 0, function* () {
|
|
334
|
+
yield logs.noteOff(sessionId);
|
|
335
|
+
(0, chai_1.expect)((yield deviceLogs(sessionId)).map((r) => r.message)).to.deep.equal([
|
|
336
|
+
"Xenon: This session's device log wasn't kept: the test turned it off.",
|
|
337
|
+
]);
|
|
338
|
+
(0, chai_1.expect)(logcat.procs).to.have.length(0);
|
|
339
|
+
}));
|
|
331
340
|
it('stops however often it is told to', () => __awaiter(this, void 0, void 0, function* () {
|
|
332
341
|
yield logs.start({ sessionId, device: PHONE, since: Date.now() });
|
|
333
342
|
yield logs.stop(sessionId);
|
|
@@ -100,10 +100,10 @@ describe('ending a failed session', () => {
|
|
|
100
100
|
notify: false,
|
|
101
101
|
});
|
|
102
102
|
it('returns while the AI is still explaining the failure', () => __awaiter(void 0, void 0, void 0, function* () {
|
|
103
|
-
(0, chai_1.expect)(yield within(
|
|
103
|
+
(0, chai_1.expect)(yield within(10000, end())).to.equal('ended');
|
|
104
104
|
// The AI is still asked, once the session has ended.
|
|
105
105
|
const analyzeFailure = AIService_1.AI_SERVICE.analyzeFailure;
|
|
106
|
-
for (let i = 0; i <
|
|
106
|
+
for (let i = 0; i < 500 && !analyzeFailure.called; i++) {
|
|
107
107
|
yield new Promise((r) => setTimeout(r, 20));
|
|
108
108
|
}
|
|
109
109
|
(0, chai_1.expect)(analyzeFailure.calledOnce).to.equal(true);
|
|
@@ -113,17 +113,17 @@ describe('ending a failed session', () => {
|
|
|
113
113
|
});
|
|
114
114
|
}));
|
|
115
115
|
it('has filed the session under its category when it returns', () => __awaiter(void 0, void 0, void 0, function* () {
|
|
116
|
-
yield within(
|
|
116
|
+
yield within(10000, end());
|
|
117
117
|
const saved = yield row();
|
|
118
118
|
(0, chai_1.expect)(saved.status).to.equal(SessionStatus_1.SessionStatus.FAILED);
|
|
119
119
|
(0, chai_1.expect)(saved.failure_category).to.equal('ELEMENT_NOT_FOUND');
|
|
120
120
|
(0, chai_1.expect)(saved.ai_analysis).to.equal(null);
|
|
121
121
|
}));
|
|
122
122
|
it("saves the AI's explanation when it comes", () => __awaiter(void 0, void 0, void 0, function* () {
|
|
123
|
-
yield within(
|
|
123
|
+
yield within(10000, end());
|
|
124
124
|
explain.resolve('Root Cause: the Login button was covered by a dialog.');
|
|
125
125
|
let saved = yield row();
|
|
126
|
-
for (let i = 0; i <
|
|
126
|
+
for (let i = 0; i < 500 && !saved.ai_analysis; i++) {
|
|
127
127
|
yield new Promise((r) => setTimeout(r, 20));
|
|
128
128
|
saved = yield row();
|
|
129
129
|
}
|
|
@@ -199,7 +199,7 @@ describe('The selector verification reads SessionLog through an index', function
|
|
|
199
199
|
});
|
|
200
200
|
}));
|
|
201
201
|
it("reads a selector's finds without a scan", () => __awaiter(this, void 0, void 0, function* () {
|
|
202
|
-
const socket = {
|
|
202
|
+
const socket = { emitToDashboardForSelector: () => __awaiter(this, void 0, void 0, function* () { return undefined; }) };
|
|
203
203
|
const ran = yield sqlOf(scratch, () => new SelectorVerificationJob_1.SelectorVerificationJob(scratch.db, socket).run());
|
|
204
204
|
const steps = yield stepsOn(scratch, 'SessionLog', ran);
|
|
205
205
|
(0, chai_1.expect)(steps, 'no SessionLog statement was captured').to.not.be.empty;
|