@xenon-device-management/xenon 2.13.2 → 2.14.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 +11 -5
- package/lib/package.json +1 -1
- package/lib/public/assets/{AnnotationOverlay-BXruClDk.js → AnnotationOverlay-B_VNtKBH.js} +1 -1
- package/lib/public/assets/{ApiKeyGate-qTS4LPLC.js → ApiKeyGate-BwuaMjkX.js} +1 -1
- package/lib/public/assets/{BugReportButton-BoGhlI7F.js → BugReportButton-BAt0_pzq.js} +1 -1
- package/lib/public/assets/DeviceMosaicView-BYaQHhTf.js +2 -0
- package/lib/public/assets/{EmptyState-Cr17PdDv.js → EmptyState-BSjhk_IV.js} +1 -1
- package/lib/public/assets/{FieldGroup-Bvn08svv.js → FieldGroup-Cb5QZsE1.js} +1 -1
- package/lib/public/assets/{FilterMenu-DDSO8aDf.js → FilterMenu-BAaSlWbW.js} +1 -1
- package/lib/public/assets/{Menu-B-Qn2X3T.js → Menu-JGGYrSQQ.js} +1 -1
- package/lib/public/assets/{Modal-Cg9V5JYU.js → Modal-CFCp3n9d.js} +1 -1
- package/lib/public/assets/{RecordingPage-BTUs9VTW.js → RecordingPage-CbJj_EBT.js} +1 -1
- package/lib/public/assets/{RecordingsPage-DYcfxITK.js → RecordingsPage-BJeYnygR.js} +2 -2
- package/lib/public/assets/{SegmentedControl-Zy7suhT5.js → SegmentedControl-Bg42Atjy.js} +1 -1
- package/lib/public/assets/{SettingCard-CWYmycoe.js → SettingCard-Njz1pUIJ.js} +1 -1
- package/lib/public/assets/{Table-io9fwa98.js → Table-BcTm5xV2.js} +1 -1
- package/lib/public/assets/{activity-Bdf_uIWl.js → activity-DOVdsfMQ.js} +1 -1
- package/lib/public/assets/{ai-settings-Bce5SJob.js → ai-settings-CsuJroRf.js} +2 -2
- package/lib/public/assets/{api-keys-CPqtVpm3.js → api-keys-BKnNSTw9.js} +1 -1
- package/lib/public/assets/{apps-BgZan0H9.js → apps-D0SC6uw6.js} +2 -2
- package/lib/public/assets/{arrow-left-B27ckB31.js → arrow-left-MMbTmgoE.js} +1 -1
- package/lib/public/assets/{arrow-right-DOF8ITCz.js → arrow-right-BMfnGmxW.js} +1 -1
- package/lib/public/assets/{arrow-up-right-eFG5KTFS.js → arrow-up-right-DuAftMWB.js} +1 -1
- package/lib/public/assets/{auth-shell-DlC3_27j.js → auth-shell-nZC2JNDO.js} +2 -2
- package/lib/public/assets/{builds-page-BuXpPfKo.js → builds-page-hDCgTwkS.js} +1 -1
- package/lib/public/assets/{button-fIW8owMI.js → button-Dda4irn_.js} +1 -1
- package/lib/public/assets/{calendar-WNjmrF-a.js → calendar-BMr0Q62v.js} +1 -1
- package/lib/public/assets/{check-BdTh1Sq7.js → check-CZ9iw9-e.js} +1 -1
- package/lib/public/assets/{chevron-right-B4BKlsqk.js → chevron-right-BRYJJVC9.js} +1 -1
- package/lib/public/assets/{circle-check-zJU80_EF.js → circle-check-CqAJjQ5O.js} +1 -1
- package/lib/public/assets/{circle-x-5Uwquo0h.js → circle-x-DlDJtnVz.js} +1 -1
- package/lib/public/assets/{clock-BtUprDsZ.js → clock-CpuDKysU.js} +1 -1
- package/lib/public/assets/{copy-DGSbZI3u.js → copy-CAQydNFe.js} +1 -1
- package/lib/public/assets/device-explorer-CdO6_nrn.js +282 -0
- package/lib/public/assets/{download-WG6l7BgD.js → download-C9XG6lLv.js} +1 -1
- package/lib/public/assets/{forgot-password-DnudqFFo.js → forgot-password-CGnkRgE9.js} +1 -1
- package/lib/public/assets/h264Stream-VQBphYhg.js +26 -0
- package/lib/public/assets/{index-DI1fGIyj.js → index-BbKEXUFH.js} +5 -5
- package/lib/public/assets/{index-HDQVe1gi.js → index-DJGc1M2f.js} +1 -1
- package/lib/public/assets/{input-BjDRKCea.js → input-Cm0tmSoc.js} +1 -1
- package/lib/public/assets/{line-chart-C0zdgd0v.js → line-chart-D2DyJCml.js} +1 -1
- package/lib/public/assets/{list-checks-BdW5CjM8.js → list-checks-UWf1GY99.js} +1 -1
- package/lib/public/assets/{lock-Dy3L4I8E.js → lock-CCJv9m_I.js} +1 -1
- package/lib/public/assets/{login-BHbY9CLG.js → login-f8vOVW-U.js} +1 -1
- package/lib/public/assets/maintenance-settings-C1PrkuAH.js +6 -0
- package/lib/public/assets/{monitor-8xjkeHHi.js → monitor-B2jDgwm7.js} +1 -1
- package/lib/public/assets/{mouse-pointer-2-CXdKMXQ8.js → mouse-pointer-2-DLI1RtzU.js} +1 -1
- package/lib/public/assets/{network-Cj8JOKON.js → network-ebT86qoi.js} +1 -1
- package/lib/public/assets/{overview-CTg7F0im.js → overview-C0aykVrd.js} +1 -1
- package/lib/public/assets/{page-header--aTyJXq6.js → page-header-BjzgWz-T.js} +1 -1
- package/lib/public/assets/{play-B-FodgHV.js → play-DLG9t8FU.js} +1 -1
- package/lib/public/assets/{plus-CsIk10Cq.js → plus-nBFSMu_X.js} +1 -1
- package/lib/public/assets/{profile-page-DkFKkMfl.js → profile-page-CSl0UYlv.js} +1 -1
- package/lib/public/assets/{reset-password-I2-zqLlI.js → reset-password-LeOuHqmB.js} +1 -1
- package/lib/public/assets/{runbook-page-DJTVCJX7.js → runbook-page-CMMeQml9.js} +1 -1
- package/lib/public/assets/{select-DvCSv25z.js → select-88jHcl-Z.js} +1 -1
- package/lib/public/assets/{selector-detail-redirect-BLVfMJs_.js → selector-detail-redirect-BbXKJjZM.js} +1 -1
- package/lib/public/assets/{selector-health-page-oqadRokn.js → selector-health-page-B4rA1Xkv.js} +2 -2
- package/lib/public/assets/session-detail-page-COL_RZxw.js +4 -0
- package/lib/public/assets/settings-GF6ZMSQ6.js +6 -0
- package/lib/public/assets/{stat-tile-DV9Ix-4O.js → stat-tile-BV98e_Sv.js} +1 -1
- package/lib/public/assets/{tablet-6b_SEN2X.js → tablet-BiAsnFDj.js} +1 -1
- package/lib/public/assets/{teams-BjZyfuHh.js → teams-B4-jrr-1.js} +1 -1
- package/lib/public/assets/{trash-2-CTZpPSdd.js → trash-2-C5RSuX0e.js} +1 -1
- package/lib/public/assets/{upload-P3XzwQUb.js → upload-BG7_rMXK.js} +1 -1
- package/lib/public/assets/{use-builds-data-1cOCMf-z.js → use-builds-data-J-riQYyC.js} +1 -1
- package/lib/public/assets/{use-password-reset-mode-DgF9w088.js → use-password-reset-mode-C8-eH-UG.js} +1 -1
- package/lib/public/assets/{users-DMF9Mk5H.js → users-CFba5TSy.js} +1 -1
- package/lib/public/assets/{users-CiNTKUiz.js → users-Cp0beJm3.js} +1 -1
- package/lib/public/assets/{video-off-CHp1KU_T.js → video-off-DDsEISjV.js} +1 -1
- package/lib/public/assets/webhook-settings-BGu32vSB.js +1 -0
- package/lib/public/assets/{zap-DRz0tDq5.js → zap-l4VaXzq8.js} +1 -1
- package/lib/public/index.html +1 -1
- package/lib/schema.json +19 -21
- package/lib/src/XenonCapabilityManager.js +22 -5
- package/lib/src/app/openapi/control.yaml +71 -20
- package/lib/src/app/openapi/grid.yaml +23 -5
- package/lib/src/app/openapi/platform.yaml +136 -28
- package/lib/src/app/routers/control.js +12 -2
- package/lib/src/app/routers/dashboard.js +29 -1
- package/lib/src/app/routers/grid.js +32 -10
- package/lib/src/app/routers/streamType.js +7 -2
- package/lib/src/app/routers/webhook.js +8 -4
- package/lib/src/config.js +50 -5
- package/lib/src/dashboard/commands.js +185 -141
- package/lib/src/dashboard/event-manager.js +39 -6
- package/lib/src/data-service/appleFamily.js +43 -0
- package/lib/src/data-service/db.js +11 -0
- package/lib/src/data-service/device-service.js +6 -1
- package/lib/src/data-service/device-store.js +62 -2
- package/lib/src/data-service/deviceClaims.js +37 -0
- package/lib/src/data-service/deviceFieldOwners.js +25 -11
- package/lib/src/data-service/deviceSettings.js +79 -0
- package/lib/src/data-service/prisma-store.js +150 -8
- package/lib/src/data-service/web-config-service.js +43 -92
- package/lib/src/device-managers/AndroidDeviceManager.js +9 -31
- package/lib/src/device-managers/ios/WDAClient.js +44 -43
- package/lib/src/device-managers/shellCommands.js +96 -0
- package/lib/src/device-utils.js +96 -40
- package/lib/src/generated/client/edge.js +17 -3
- package/lib/src/generated/client/index-browser.js +14 -0
- package/lib/src/generated/client/index.d.ts +2054 -379
- package/lib/src/generated/client/index.js +17 -3
- package/lib/src/generated/client/package.json +1 -1
- package/lib/src/generated/client/schema.prisma +35 -7
- package/lib/src/generated/client/wasm.js +14 -0
- package/lib/src/interceptors/CommandInterceptor.js +29 -8
- package/lib/src/interceptors/xenonScripts.js +55 -0
- package/lib/src/interfaces/IPluginArgs.js +1 -2
- package/lib/src/plugin.js +15 -1
- package/lib/src/prisma.js +1 -1
- package/lib/src/scripts/database-check.js +124 -0
- package/lib/src/services/AIService.js +99 -0
- package/lib/src/services/CleanupService.js +6 -1
- package/lib/src/services/InterceptorService.js +70 -13
- package/lib/src/services/NetworkConditioningService.js +85 -88
- package/lib/src/services/NotificationService.js +36 -29
- package/lib/src/services/OrphanSweeper.js +5 -0
- package/lib/src/services/PortAllocator.js +5 -0
- package/lib/src/services/ServerManager.js +94 -8
- package/lib/src/services/SessionLifecycleService.js +47 -49
- package/lib/src/services/ShutdownCoordinator.js +33 -0
- package/lib/src/services/TeamService.js +20 -3
- package/lib/src/services/healing/HealingOrchestrator.js +32 -1
- package/lib/src/services/interceptor/AndroidProxyAdapter.js +18 -0
- package/lib/src/services/interceptor/interceptorOptions.js +44 -0
- package/lib/src/services/network/AndroidRadios.js +71 -0
- package/lib/src/services/network/PhoneNetworkLedger.js +184 -0
- package/lib/src/services/network/PhoneNetworkRestore.js +330 -0
- package/lib/src/services/network/adbForPhone.js +60 -0
- package/lib/src/services/network/phoneNetworkLock.js +20 -0
- package/lib/src/services/network/xenonProxy.js +107 -0
- package/lib/src/services/omni-vision/OmniVisionService.js +53 -43
- package/lib/src/services/omni-vision/VisionAssertionService.js +28 -25
- package/lib/src/services/omni-vision/ocrTextMatch.js +105 -0
- package/lib/src/services/omni-vision/screenScale.js +68 -0
- package/lib/src/services/recording/concurrency-gate.js +12 -3
- package/lib/src/services/settings/SelfHealingSwitch.js +70 -0
- package/lib/src/services/settings/labSettings.js +149 -0
- package/lib/src/services/webhookEvents.js +173 -0
- package/lib/test/helpers/fake-adb.js +58 -0
- package/lib/test/helpers/fake-device-table.js +63 -24
- package/lib/test/helpers/scratch-database.js +1 -0
- package/lib/test/unit/TeamService.test.js +3 -0
- package/lib/test/unit/XenonCapabilityManager.spec.js +4 -2
- package/lib/test/unit/apple-family.spec.js +36 -0
- package/lib/test/unit/cleanup-settings-db.spec.js +103 -0
- package/lib/test/unit/cleanup-settings.spec.js +244 -0
- package/lib/test/unit/dashboard-api-paths.spec.js +101 -0
- package/lib/test/unit/dashboard-commands.spec.js +249 -0
- package/lib/test/unit/dashboard-events-team-scope.spec.js +9 -6
- package/lib/test/unit/database-check.spec.js +108 -0
- package/lib/test/unit/default-plugin-args.spec.js +33 -0
- package/lib/test/unit/device-offline-webhook.spec.js +81 -0
- package/lib/test/unit/device-setting-columns.spec.js +66 -0
- package/lib/test/unit/device-settings-loki.spec.js +87 -0
- package/lib/test/unit/device-settings-survive.spec.js +555 -0
- package/lib/test/unit/device-sync-lost-update.spec.js +5 -0
- package/lib/test/unit/emulator-boot.spec.js +91 -0
- package/lib/test/unit/hub-node-gateway-appium-server.spec.js +48 -0
- package/lib/test/unit/hub-node-same-host.spec.js +5 -0
- package/lib/test/unit/iphone-ipad-only.spec.js +132 -0
- package/lib/test/unit/json-logging-option.spec.js +74 -0
- package/lib/test/unit/lab-settings.spec.js +218 -0
- package/lib/test/unit/max-sessions.spec.js +240 -0
- package/lib/test/unit/ocr-text-match.spec.js +44 -0
- package/lib/test/unit/omni-vision-analyze-screen.spec.js +72 -0
- package/lib/test/unit/omni-vision-ios-points.spec.js +161 -0
- package/lib/test/unit/omni-vision-tap-text.spec.js +129 -0
- package/lib/test/unit/omni-vision.spec.js +1 -1
- package/lib/test/unit/partial-config-file.spec.js +14 -0
- package/lib/test/unit/phone-network/boot-clean-up.spec.js +64 -0
- package/lib/test/unit/phone-network/interceptor-options.spec.js +77 -0
- package/lib/test/unit/phone-network/interceptor-proxy-restore.spec.js +145 -0
- package/lib/test/unit/phone-network/network-conditioning.spec.js +145 -0
- package/lib/test/unit/phone-network/phone-network-restore.spec.js +246 -0
- package/lib/test/unit/phone-network/session-end-paths.spec.js +182 -0
- package/lib/test/unit/phone-network/session-start.spec.js +110 -0
- package/lib/test/unit/phone-network/xenon-proxy.spec.js +69 -0
- package/lib/test/unit/plugin.spec.js +3 -3
- package/lib/test/unit/recording-options.spec.js +181 -0
- package/lib/test/unit/self-healing-boot.spec.js +75 -0
- package/lib/test/unit/self-healing-setting.spec.js +272 -0
- package/lib/test/unit/session-failed-webhook.spec.js +170 -0
- package/lib/test/unit/shell-commands.spec.js +133 -0
- package/lib/test/unit/socketServer.scopedEmit.spec.js +2 -0
- package/lib/test/unit/stream-start-pipelines.spec.js +210 -0
- package/lib/test/unit/stream-status-type.spec.js +4 -0
- package/lib/test/unit/vision-assertion.spec.js +118 -0
- package/lib/test/unit/wda-client-shell.spec.js +133 -0
- package/lib/test/unit/web-config-service.spec.js +79 -0
- package/lib/test/unit/webhook-delivery.spec.js +24 -0
- package/lib/test/unit/webhook-payloads.spec.js +273 -0
- package/lib/test/unit/xenon-execute-scripts.spec.js +138 -0
- package/lib/tsconfig.tsbuildinfo +1 -1
- package/lib/web/src/components/webhook-settings/webhookEventVariables.json +30 -0
- package/package.json +1 -1
- package/prisma/migrations/20261004140000_keep_device_settings/migration.sql +19 -0
- package/prisma/schema.prisma +35 -7
- package/schema.json +19 -21
- package/scripts/generate-types-from-schema.js +1 -2
- package/scripts/lib/always-present-plugin-args.js +4 -1
- package/lib/public/assets/DeviceMosaicView-8J0oYqMM.js +0 -2
- package/lib/public/assets/device-explorer-C-KnhMHI.js +0 -279
- package/lib/public/assets/maintenance-settings-zDVlOMVK.js +0 -6
- package/lib/public/assets/recording-group-store-WVM3hoXn.js +0 -26
- package/lib/public/assets/session-detail-page-CGn55Pvl.js +0 -4
- package/lib/public/assets/settings-MsVU2ukE.js +0 -6
- package/lib/public/assets/webhook-settings-DiS8geIm.js +0 -1
|
@@ -73,6 +73,20 @@ describe('An Appium config file for Xenon', function () {
|
|
|
73
73
|
(0, chai_1.expect)((_a = result.reason) !== null && _a !== void 0 ? _a : result.errors).to.be.empty;
|
|
74
74
|
});
|
|
75
75
|
});
|
|
76
|
+
it('is accepted with the network capture option Xenon Control writes', function () {
|
|
77
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
78
|
+
var _a;
|
|
79
|
+
// The server's `interceptor` option is now each session's default
|
|
80
|
+
// (resolveInterceptorOptions); configs that set it must keep starting.
|
|
81
|
+
const result = yield load(`${README_SAMPLE} interceptor:\n enabled: true\n bufferSize: 50\n captureBodies: false\n`);
|
|
82
|
+
(0, chai_1.expect)((_a = result.reason) !== null && _a !== void 0 ? _a : result.errors).to.be.empty;
|
|
83
|
+
(0, chai_1.expect)(result.config.server.plugin.xenon.interceptor).to.deep.equal({
|
|
84
|
+
enabled: true,
|
|
85
|
+
bufferSize: 50,
|
|
86
|
+
captureBodies: false,
|
|
87
|
+
});
|
|
88
|
+
});
|
|
89
|
+
});
|
|
76
90
|
it('is still refused when a value is invalid', function () {
|
|
77
91
|
return __awaiter(this, void 0, void 0, function* () {
|
|
78
92
|
const result = yield load(README_SAMPLE.replace('platform: both', 'platform: windows'));
|
|
@@ -0,0 +1,64 @@
|
|
|
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 sinon_1 = __importDefault(require("sinon"));
|
|
18
|
+
const typedi_1 = require("typedi");
|
|
19
|
+
const ServerManager_1 = require("../../../src/services/ServerManager");
|
|
20
|
+
const PhoneNetworkRestore_1 = require("../../../src/services/network/PhoneNetworkRestore");
|
|
21
|
+
const device_store_1 = require("../../../src/data-service/device-store");
|
|
22
|
+
const container_registration_1 = require("../../helpers/container-registration");
|
|
23
|
+
const ME = 'http://10.0.0.5:4723';
|
|
24
|
+
const NODE_ID = 'node-me';
|
|
25
|
+
/**
|
|
26
|
+
* At its start a server checks the network of the Android phones it drives:
|
|
27
|
+
* never a phone another server on the same machine drives (a hub keeps its
|
|
28
|
+
* nodes' rows), a cloud phone or an iPhone.
|
|
29
|
+
*/
|
|
30
|
+
describe('the phone network check at boot', () => {
|
|
31
|
+
let restoreRegs;
|
|
32
|
+
let cleanUpAtBoot;
|
|
33
|
+
beforeEach(() => {
|
|
34
|
+
restoreRegs = (0, container_registration_1.saveRegistrations)(PhoneNetworkRestore_1.PhoneNetworkRestore);
|
|
35
|
+
cleanUpAtBoot = sinon_1.default.stub().resolves();
|
|
36
|
+
typedi_1.Container.set(PhoneNetworkRestore_1.PhoneNetworkRestore, { cleanUpAtBoot });
|
|
37
|
+
});
|
|
38
|
+
afterEach(() => {
|
|
39
|
+
sinon_1.default.restore();
|
|
40
|
+
restoreRegs();
|
|
41
|
+
});
|
|
42
|
+
it("checks this server's own Android phones, once each", () => __awaiter(void 0, void 0, void 0, function* () {
|
|
43
|
+
sinon_1.default.stub(device_store_1.DeviceStoreFactory, 'getStore').returns({
|
|
44
|
+
getAllDevices: () => __awaiter(void 0, void 0, void 0, function* () {
|
|
45
|
+
return [
|
|
46
|
+
{ udid: 'MINE', platform: 'android', host: ME, nodeId: NODE_ID },
|
|
47
|
+
{ udid: 'MINE', platform: 'android', host: ME, nodeId: NODE_ID },
|
|
48
|
+
{ udid: 'NO-ID', platform: 'android', host: ME },
|
|
49
|
+
{ udid: 'NODE', platform: 'android', host: 'http://10.0.0.5:4724', nodeId: 'node-b' },
|
|
50
|
+
{ udid: 'CLOUD', platform: 'android', host: ME, nodeId: NODE_ID, cloud: { name: 'x' } },
|
|
51
|
+
{ udid: 'IPHONE', platform: 'ios', host: ME, nodeId: NODE_ID },
|
|
52
|
+
];
|
|
53
|
+
}),
|
|
54
|
+
});
|
|
55
|
+
yield typedi_1.Container.get(ServerManager_1.ServerManager).cleanUpPhoneNetworks({ origin: ME, hosts: new Set([ME]) }, NODE_ID);
|
|
56
|
+
(0, chai_1.expect)(cleanUpAtBoot.calledOnce).to.equal(true);
|
|
57
|
+
(0, chai_1.expect)(cleanUpAtBoot.firstCall.args[0]).to.deep.equal(['MINE', 'NO-ID']);
|
|
58
|
+
}));
|
|
59
|
+
it('never fails the boot', () => __awaiter(void 0, void 0, void 0, function* () {
|
|
60
|
+
sinon_1.default.stub(device_store_1.DeviceStoreFactory, 'getStore').throws(new Error('no store'));
|
|
61
|
+
yield typedi_1.Container.get(ServerManager_1.ServerManager).cleanUpPhoneNetworks({ origin: ME, hosts: new Set([ME]) }, NODE_ID);
|
|
62
|
+
(0, chai_1.expect)(cleanUpAtBoot.called).to.equal(false);
|
|
63
|
+
}));
|
|
64
|
+
});
|
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
require("reflect-metadata");
|
|
4
|
+
const chai_1 = require("chai");
|
|
5
|
+
const XenonCapabilityManager_1 = require("../../../src/XenonCapabilityManager");
|
|
6
|
+
const interceptorOptions_1 = require("../../../src/services/interceptor/interceptorOptions");
|
|
7
|
+
const capsOf = (alwaysMatch) => (0, XenonCapabilityManager_1.getXenonCapabilities)({ alwaysMatch, firstMatch: [{}] });
|
|
8
|
+
/**
|
|
9
|
+
* The server's `interceptor` option was declared in schema.json and shown in
|
|
10
|
+
* Xenon Control, but nothing read it: turning it on did nothing. It is now the
|
|
11
|
+
* default for this server's sessions, and a session's own capability wins
|
|
12
|
+
* field by field.
|
|
13
|
+
*/
|
|
14
|
+
describe('resolveInterceptorOptions: the server option is the default, the session decides', () => {
|
|
15
|
+
it('leaves capture off when neither the session nor the server turns it on', () => {
|
|
16
|
+
(0, chai_1.expect)((0, interceptorOptions_1.resolveInterceptorOptions)(capsOf({}), undefined).enabled).to.equal(false);
|
|
17
|
+
(0, chai_1.expect)((0, interceptorOptions_1.resolveInterceptorOptions)(capsOf({}), {}).enabled).to.equal(false);
|
|
18
|
+
});
|
|
19
|
+
it("turns capture on for a session that doesn't say, when the server's option is on", () => {
|
|
20
|
+
const opts = (0, interceptorOptions_1.resolveInterceptorOptions)(capsOf({}), { enabled: true });
|
|
21
|
+
(0, chai_1.expect)(opts.enabled).to.equal(true);
|
|
22
|
+
(0, chai_1.expect)(opts.captureBodies).to.equal(true);
|
|
23
|
+
(0, chai_1.expect)(opts.mocks).to.deep.equal([]);
|
|
24
|
+
});
|
|
25
|
+
it("lets a session turn capture off when the server's option is on", () => {
|
|
26
|
+
const object = (0, interceptorOptions_1.resolveInterceptorOptions)(capsOf({ 'xe:interceptor': { enabled: false } }), {
|
|
27
|
+
enabled: true,
|
|
28
|
+
});
|
|
29
|
+
(0, chai_1.expect)(object.enabled).to.equal(false);
|
|
30
|
+
const flat = (0, interceptorOptions_1.resolveInterceptorOptions)(capsOf({ 'xe:interceptorEnabled': false }), {
|
|
31
|
+
enabled: true,
|
|
32
|
+
});
|
|
33
|
+
(0, chai_1.expect)(flat.enabled).to.equal(false);
|
|
34
|
+
});
|
|
35
|
+
it("lets a session turn capture on when the server's option is off", () => {
|
|
36
|
+
const opts = (0, interceptorOptions_1.resolveInterceptorOptions)(capsOf({ 'xe:interceptor': { enabled: true } }), {
|
|
37
|
+
enabled: false,
|
|
38
|
+
});
|
|
39
|
+
(0, chai_1.expect)(opts.enabled).to.equal(true);
|
|
40
|
+
});
|
|
41
|
+
it("takes bufferSize and captureBodies from the server when the session doesn't set them", () => {
|
|
42
|
+
const opts = (0, interceptorOptions_1.resolveInterceptorOptions)(capsOf({ 'xe:interceptor': { enabled: true } }), {
|
|
43
|
+
bufferSize: 50,
|
|
44
|
+
captureBodies: false,
|
|
45
|
+
});
|
|
46
|
+
(0, chai_1.expect)(opts.bufferSize).to.equal(50);
|
|
47
|
+
(0, chai_1.expect)(opts.captureBodies).to.equal(false);
|
|
48
|
+
});
|
|
49
|
+
it("keeps the session's own bufferSize and captureBodies over the server's", () => {
|
|
50
|
+
const opts = (0, interceptorOptions_1.resolveInterceptorOptions)(capsOf({ 'xe:interceptor': { enabled: true, bufferSize: 7, captureBodies: true } }), { bufferSize: 50, captureBodies: false });
|
|
51
|
+
(0, chai_1.expect)(opts.bufferSize).to.equal(7);
|
|
52
|
+
(0, chai_1.expect)(opts.captureBodies).to.equal(true);
|
|
53
|
+
});
|
|
54
|
+
it('keeps mocks and host filters from the session only', () => {
|
|
55
|
+
const mocks = [{ match: { url: '**/x' }, respondWith: { status: 204 } }];
|
|
56
|
+
const opts = (0, interceptorOptions_1.resolveInterceptorOptions)(capsOf({
|
|
57
|
+
'xe:interceptor': { mocks, includeHosts: ['a.example'], excludeHosts: ['b.example'] },
|
|
58
|
+
}), { enabled: true });
|
|
59
|
+
(0, chai_1.expect)(opts.mocks).to.deep.equal(mocks);
|
|
60
|
+
(0, chai_1.expect)(opts.includeHosts).to.deep.equal(['a.example']);
|
|
61
|
+
(0, chai_1.expect)(opts.excludeHosts).to.deep.equal(['b.example']);
|
|
62
|
+
});
|
|
63
|
+
it('reads a session that sets nothing about capture as unset, not as off', () => {
|
|
64
|
+
const caps = capsOf({});
|
|
65
|
+
(0, chai_1.expect)(caps[XenonCapabilityManager_1.XENON_CAPABILITIES.INTERCEPTOR_ENABLED]).to.equal(undefined);
|
|
66
|
+
(0, chai_1.expect)(caps[XenonCapabilityManager_1.XENON_CAPABILITIES.INTERCEPTOR_CAPTURE_BODIES]).to.equal(undefined);
|
|
67
|
+
});
|
|
68
|
+
it('reads an interceptor object without `enabled` as unset', () => {
|
|
69
|
+
const caps = capsOf({ 'xe:interceptor': { bufferSize: 3 } });
|
|
70
|
+
(0, chai_1.expect)(caps[XenonCapabilityManager_1.XENON_CAPABILITIES.INTERCEPTOR_ENABLED]).to.equal(undefined);
|
|
71
|
+
(0, chai_1.expect)((0, interceptorOptions_1.resolveInterceptorOptions)(caps, { enabled: true }).enabled).to.equal(true);
|
|
72
|
+
});
|
|
73
|
+
it("reads `enabled: 'false'` as off", () => {
|
|
74
|
+
const caps = capsOf({ 'xe:interceptor': { enabled: 'false' } });
|
|
75
|
+
(0, chai_1.expect)(caps[XenonCapabilityManager_1.XENON_CAPABILITIES.INTERCEPTOR_ENABLED]).to.equal(false);
|
|
76
|
+
});
|
|
77
|
+
});
|
|
@@ -0,0 +1,145 @@
|
|
|
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 sinon_1 = __importDefault(require("sinon"));
|
|
18
|
+
const fs_1 = __importDefault(require("fs"));
|
|
19
|
+
const net_1 = __importDefault(require("net"));
|
|
20
|
+
const os_1 = __importDefault(require("os"));
|
|
21
|
+
const path_1 = __importDefault(require("path"));
|
|
22
|
+
const typedi_1 = require("typedi");
|
|
23
|
+
const InterceptorService_1 = require("../../../src/services/InterceptorService");
|
|
24
|
+
const CertManager_1 = require("../../../src/services/interceptor/CertManager");
|
|
25
|
+
const MitmProxyHost_1 = require("../../../src/services/interceptor/MitmProxyHost");
|
|
26
|
+
const PortAllocator_1 = require("../../../src/services/PortAllocator");
|
|
27
|
+
const PhoneNetworkLedger_1 = require("../../../src/services/network/PhoneNetworkLedger");
|
|
28
|
+
const config_1 = require("../../../src/config");
|
|
29
|
+
const container_registration_1 = require("../../helpers/container-registration");
|
|
30
|
+
const scratch_database_1 = require("../../helpers/scratch-database");
|
|
31
|
+
const fake_adb_1 = require("../../helpers/fake-adb");
|
|
32
|
+
const PROXY_PORT = 11150;
|
|
33
|
+
const phone = { udid: 'R5CT', platform: 'android', deviceType: 'real', host: 'h' };
|
|
34
|
+
const ON = { enabled: true, mocks: [], includeHosts: [], excludeHosts: [] };
|
|
35
|
+
/**
|
|
36
|
+
* The interceptor points the whole phone at its proxy. When the session ends
|
|
37
|
+
* it puts back the proxy the phone had (Xenon used to write ":0" over a lab's
|
|
38
|
+
* own proxy), and records the change while it lasts so a restart can undo it.
|
|
39
|
+
*/
|
|
40
|
+
describe('InterceptorService and the phone proxy', function () {
|
|
41
|
+
this.timeout(60000);
|
|
42
|
+
(0, scratch_database_1.useScratchDatabase)();
|
|
43
|
+
let adb;
|
|
44
|
+
(0, fake_adb_1.useFakeAdb)(() => adb);
|
|
45
|
+
let restoreRegs;
|
|
46
|
+
let assetsBefore;
|
|
47
|
+
let assetsDir;
|
|
48
|
+
let ledger;
|
|
49
|
+
let range;
|
|
50
|
+
beforeEach(() => __awaiter(this, void 0, void 0, function* () {
|
|
51
|
+
adb = (0, fake_adb_1.fakeAdb)({ 'shell settings get global http_proxy': 'proxy.corp:3128' });
|
|
52
|
+
restoreRegs = (0, container_registration_1.saveRegistrations)(PortAllocator_1.PortAllocator);
|
|
53
|
+
range = [11100, 11199];
|
|
54
|
+
typedi_1.Container.set(PortAllocator_1.PortAllocator, {
|
|
55
|
+
acquire: () => __awaiter(this, void 0, void 0, function* () { return PROXY_PORT; }),
|
|
56
|
+
releaseForUdid: () => __awaiter(this, void 0, void 0, function* () { return undefined; }),
|
|
57
|
+
rangeOf: () => range,
|
|
58
|
+
});
|
|
59
|
+
sinon_1.default.stub(CertManager_1.CertManager.prototype, 'ensure').resolves({
|
|
60
|
+
certPath: '/nonexistent/ca.pem',
|
|
61
|
+
keyPath: '/nonexistent/ca.key',
|
|
62
|
+
subjectHash: 'abc',
|
|
63
|
+
});
|
|
64
|
+
sinon_1.default.stub(CertManager_1.CertManager.prototype, 'androidCertFilename').returns('abc.0');
|
|
65
|
+
sinon_1.default.stub(MitmProxyHost_1.MitmProxyHost.prototype, 'start').resolves();
|
|
66
|
+
sinon_1.default.stub(MitmProxyHost_1.MitmProxyHost.prototype, 'stop').resolves();
|
|
67
|
+
assetsBefore = config_1.config.sessionAssetsPath;
|
|
68
|
+
assetsDir = fs_1.default.mkdtempSync(path_1.default.join(os_1.default.tmpdir(), 'xenon-icp-'));
|
|
69
|
+
(0, config_1.updateConfig)({ sessionAssetsPath: assetsDir });
|
|
70
|
+
ledger = new PhoneNetworkLedger_1.PhoneNetworkLedger();
|
|
71
|
+
for (const entry of yield ledger.list())
|
|
72
|
+
yield ledger.forget(entry.sessionId);
|
|
73
|
+
}));
|
|
74
|
+
afterEach(() => {
|
|
75
|
+
sinon_1.default.restore();
|
|
76
|
+
restoreRegs();
|
|
77
|
+
(0, config_1.updateConfig)({ sessionAssetsPath: assetsBefore });
|
|
78
|
+
fs_1.default.rmSync(assetsDir, { recursive: true, force: true });
|
|
79
|
+
});
|
|
80
|
+
it('reads the proxy the phone had and writes its own down before setting it', () => __awaiter(this, void 0, void 0, function* () {
|
|
81
|
+
const svc = new InterceptorService_1.InterceptorService();
|
|
82
|
+
yield svc.start('s1', phone, ON);
|
|
83
|
+
const cmds = adb.commands('R5CT');
|
|
84
|
+
const read = cmds.indexOf('shell settings get global http_proxy');
|
|
85
|
+
const set = cmds.indexOf(`shell settings put global http_proxy 127.0.0.1:${PROXY_PORT}`);
|
|
86
|
+
(0, chai_1.expect)(read, cmds.join('\n')).to.be.greaterThan(-1);
|
|
87
|
+
(0, chai_1.expect)(set).to.be.greaterThan(read);
|
|
88
|
+
const [entry] = yield ledger.list();
|
|
89
|
+
(0, chai_1.expect)(entry).to.include({ sessionId: 's1', udid: 'R5CT' });
|
|
90
|
+
(0, chai_1.expect)(entry.proxy).to.deep.equal({
|
|
91
|
+
set: `127.0.0.1:${PROXY_PORT}`,
|
|
92
|
+
previous: 'proxy.corp:3128',
|
|
93
|
+
reversePort: PROXY_PORT,
|
|
94
|
+
});
|
|
95
|
+
yield svc.stop('s1');
|
|
96
|
+
}));
|
|
97
|
+
it("puts the phone's own proxy back at the end, and saves the capture", () => __awaiter(this, void 0, void 0, function* () {
|
|
98
|
+
const svc = new InterceptorService_1.InterceptorService();
|
|
99
|
+
yield svc.start('s1', phone, ON);
|
|
100
|
+
adb.calls.length = 0;
|
|
101
|
+
yield svc.stop('s1');
|
|
102
|
+
(0, chai_1.expect)(adb.commands('R5CT')).to.include('shell settings put global http_proxy proxy.corp:3128');
|
|
103
|
+
(0, chai_1.expect)(adb.commands('R5CT')).not.to.include('shell settings put global http_proxy :0');
|
|
104
|
+
(0, chai_1.expect)(yield ledger.list()).to.deep.equal([]);
|
|
105
|
+
(0, chai_1.expect)(fs_1.default.existsSync(path_1.default.join(assetsDir, 's1', 'interceptor', 'requests.json'))).to.equal(true);
|
|
106
|
+
}));
|
|
107
|
+
it('clears the proxy at the end when the phone had none', () => __awaiter(this, void 0, void 0, function* () {
|
|
108
|
+
adb.answers['shell settings get global http_proxy'] = ':0';
|
|
109
|
+
const svc = new InterceptorService_1.InterceptorService();
|
|
110
|
+
yield svc.start('s1', phone, ON);
|
|
111
|
+
adb.calls.length = 0;
|
|
112
|
+
yield svc.stop('s1');
|
|
113
|
+
(0, chai_1.expect)(adb.commands('R5CT')).to.include('shell settings put global http_proxy :0');
|
|
114
|
+
}));
|
|
115
|
+
it("doesn't put back a proxy an earlier run left pointing at a dead capture port", () => __awaiter(this, void 0, void 0, function* () {
|
|
116
|
+
const closed = net_1.default.createServer();
|
|
117
|
+
yield new Promise((r) => closed.listen(0, '127.0.0.1', () => r()));
|
|
118
|
+
const dead = closed.address().port;
|
|
119
|
+
yield new Promise((r) => closed.close(() => r()));
|
|
120
|
+
range = [Math.min(dead, PROXY_PORT), Math.max(dead, PROXY_PORT)];
|
|
121
|
+
adb.answers['shell settings get global http_proxy'] = `127.0.0.1:${dead}`;
|
|
122
|
+
const svc = new InterceptorService_1.InterceptorService();
|
|
123
|
+
yield svc.start('s1', phone, ON);
|
|
124
|
+
adb.calls.length = 0;
|
|
125
|
+
yield svc.stop('s1');
|
|
126
|
+
(0, chai_1.expect)(adb.commands('R5CT')).to.include('shell settings put global http_proxy :0');
|
|
127
|
+
(0, chai_1.expect)(adb.commands('R5CT')).not.to.include(`shell settings put global http_proxy 127.0.0.1:${dead}`);
|
|
128
|
+
}));
|
|
129
|
+
it('keeps the record when the proxy could not be put back', () => __awaiter(this, void 0, void 0, function* () {
|
|
130
|
+
const svc = new InterceptorService_1.InterceptorService();
|
|
131
|
+
yield svc.start('s1', phone, ON);
|
|
132
|
+
adb.answers['shell settings put global http_proxy proxy.corp:3128'] = new Error('offline');
|
|
133
|
+
yield svc.stop('s1');
|
|
134
|
+
(0, chai_1.expect)((yield ledger.list()).map((e) => e.sessionId)).to.deep.equal(['s1']);
|
|
135
|
+
(0, chai_1.expect)(svc.isActive('s1')).to.equal(false);
|
|
136
|
+
}));
|
|
137
|
+
it('stops once, however many times it is asked', () => __awaiter(this, void 0, void 0, function* () {
|
|
138
|
+
const svc = new InterceptorService_1.InterceptorService();
|
|
139
|
+
yield svc.start('s1', phone, ON);
|
|
140
|
+
adb.calls.length = 0;
|
|
141
|
+
yield Promise.all([svc.stop('s1'), svc.stop('s1')]);
|
|
142
|
+
const puts = adb.commands('R5CT').filter((c) => c.startsWith('shell settings put'));
|
|
143
|
+
(0, chai_1.expect)(puts).to.deep.equal(['shell settings put global http_proxy proxy.corp:3128']);
|
|
144
|
+
}));
|
|
145
|
+
});
|
|
@@ -0,0 +1,145 @@
|
|
|
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 sinon_1 = __importDefault(require("sinon"));
|
|
18
|
+
const NetworkConditioningService_1 = require("../../../src/services/NetworkConditioningService");
|
|
19
|
+
const PhoneNetworkLedger_1 = require("../../../src/services/network/PhoneNetworkLedger");
|
|
20
|
+
const scratch_database_1 = require("../../helpers/scratch-database");
|
|
21
|
+
const fake_adb_1 = require("../../helpers/fake-adb");
|
|
22
|
+
// The live module object, so a spy sees a call made through any import of it.
|
|
23
|
+
// eslint-disable-next-line @typescript-eslint/no-var-requires
|
|
24
|
+
const childProcess = require('child_process');
|
|
25
|
+
const phone = (udid, extra = {}) => (Object.assign({ udid, platform: 'android', deviceType: 'real', host: 'http://127.0.0.1:4723' }, extra));
|
|
26
|
+
const RADIOS_ON = {
|
|
27
|
+
'shell settings get global wifi_on': '1',
|
|
28
|
+
'shell settings get global mobile_data': '1',
|
|
29
|
+
};
|
|
30
|
+
describe('NetworkConditioningService', function () {
|
|
31
|
+
this.timeout(60000);
|
|
32
|
+
(0, scratch_database_1.useScratchDatabase)();
|
|
33
|
+
let adb;
|
|
34
|
+
(0, fake_adb_1.useFakeAdb)(() => adb);
|
|
35
|
+
let ledger;
|
|
36
|
+
beforeEach(() => __awaiter(this, void 0, void 0, function* () {
|
|
37
|
+
adb = (0, fake_adb_1.fakeAdb)(Object.assign({}, RADIOS_ON));
|
|
38
|
+
ledger = new PhoneNetworkLedger_1.PhoneNetworkLedger();
|
|
39
|
+
for (const entry of yield ledger.list())
|
|
40
|
+
yield ledger.forget(entry.sessionId);
|
|
41
|
+
}));
|
|
42
|
+
afterEach(() => sinon_1.default.restore());
|
|
43
|
+
describe('Offline on an Android phone', () => {
|
|
44
|
+
it('turns mobile data and Wi-Fi off through the resolved adb, never a shell', () => __awaiter(this, void 0, void 0, function* () {
|
|
45
|
+
const exec = sinon_1.default.spy(childProcess, 'exec');
|
|
46
|
+
const execFile = sinon_1.default.spy(childProcess, 'execFile');
|
|
47
|
+
const svc = new NetworkConditioningService_1.NetworkConditioningService();
|
|
48
|
+
yield svc.applyProfile('s1', phone('R5CT'), 'Offline');
|
|
49
|
+
(0, chai_1.expect)(adb.commands('R5CT')).to.deep.equal([
|
|
50
|
+
'shell settings get global wifi_on',
|
|
51
|
+
'shell settings get global mobile_data',
|
|
52
|
+
'shell svc data disable',
|
|
53
|
+
'shell svc wifi disable',
|
|
54
|
+
]);
|
|
55
|
+
(0, chai_1.expect)(adb.calls.every((args) => args[0] === '-s' && args[1] === 'R5CT')).to.equal(true);
|
|
56
|
+
(0, chai_1.expect)(exec.called, 'child_process.exec').to.equal(false);
|
|
57
|
+
(0, chai_1.expect)(execFile.called, 'child_process.execFile').to.equal(false);
|
|
58
|
+
}));
|
|
59
|
+
it('writes down what it changed before changing it, so a restart can undo it', () => __awaiter(this, void 0, void 0, function* () {
|
|
60
|
+
adb.answers['shell settings get global mobile_data'] = '0';
|
|
61
|
+
yield new NetworkConditioningService_1.NetworkConditioningService().applyProfile('s1', phone('R5CT'), 'Offline');
|
|
62
|
+
const entries = yield ledger.list();
|
|
63
|
+
(0, chai_1.expect)(entries).to.have.length(1);
|
|
64
|
+
(0, chai_1.expect)(entries[0]).to.include({ sessionId: 's1', udid: 'R5CT' });
|
|
65
|
+
(0, chai_1.expect)(entries[0].offline).to.deep.equal({ wifiOn: true, dataOn: false });
|
|
66
|
+
}));
|
|
67
|
+
it('puts back what was on, and leaves off what was off', () => __awaiter(this, void 0, void 0, function* () {
|
|
68
|
+
adb.answers['shell settings get global mobile_data'] = '0';
|
|
69
|
+
const svc = new NetworkConditioningService_1.NetworkConditioningService();
|
|
70
|
+
yield svc.applyProfile('s1', phone('R5CT'), 'Offline');
|
|
71
|
+
adb.calls.length = 0;
|
|
72
|
+
yield svc.reset('s1');
|
|
73
|
+
(0, chai_1.expect)(adb.commands('R5CT')).to.deep.equal(['shell svc wifi enable']);
|
|
74
|
+
(0, chai_1.expect)(yield ledger.list()).to.deep.equal([]);
|
|
75
|
+
(0, chai_1.expect)(svc.has('s1')).to.equal(false);
|
|
76
|
+
}));
|
|
77
|
+
it('turns both back on when it could not read what they were', () => __awaiter(this, void 0, void 0, function* () {
|
|
78
|
+
adb.answers['shell settings get global wifi_on'] = new Error('device offline');
|
|
79
|
+
adb.answers['shell settings get global mobile_data'] = 'null';
|
|
80
|
+
const svc = new NetworkConditioningService_1.NetworkConditioningService();
|
|
81
|
+
yield svc.applyProfile('s1', phone('R5CT'), 'Offline');
|
|
82
|
+
adb.calls.length = 0;
|
|
83
|
+
yield svc.reset('s1');
|
|
84
|
+
(0, chai_1.expect)(adb.commands('R5CT')).to.deep.equal([
|
|
85
|
+
'shell svc data enable',
|
|
86
|
+
'shell svc wifi enable',
|
|
87
|
+
]);
|
|
88
|
+
}));
|
|
89
|
+
it('keeps the record when the phone cannot be reached at the end', () => __awaiter(this, void 0, void 0, function* () {
|
|
90
|
+
const svc = new NetworkConditioningService_1.NetworkConditioningService();
|
|
91
|
+
yield svc.applyProfile('s1', phone('R5CT'), 'Offline');
|
|
92
|
+
adb.answers['shell svc data enable'] = new Error("device 'R5CT' not found");
|
|
93
|
+
yield svc.reset('s1');
|
|
94
|
+
const entries = yield ledger.list();
|
|
95
|
+
(0, chai_1.expect)(entries.map((e) => e.sessionId)).to.deep.equal(['s1']);
|
|
96
|
+
(0, chai_1.expect)(svc.has('s1')).to.equal(false);
|
|
97
|
+
}));
|
|
98
|
+
it('undoes a session once, however many times it is asked', () => __awaiter(this, void 0, void 0, function* () {
|
|
99
|
+
const svc = new NetworkConditioningService_1.NetworkConditioningService();
|
|
100
|
+
yield svc.applyProfile('s1', phone('R5CT'), 'Offline');
|
|
101
|
+
adb.calls.length = 0;
|
|
102
|
+
yield Promise.all([svc.reset('s1'), svc.reset('s1')]);
|
|
103
|
+
yield svc.reset('s1');
|
|
104
|
+
(0, chai_1.expect)(adb.commands('R5CT')).to.deep.equal([
|
|
105
|
+
'shell svc data enable',
|
|
106
|
+
'shell svc wifi enable',
|
|
107
|
+
]);
|
|
108
|
+
}));
|
|
109
|
+
});
|
|
110
|
+
it("changes nothing on the phone for 3G, at the start or the end; it's a delay", () => __awaiter(this, void 0, void 0, function* () {
|
|
111
|
+
const svc = new NetworkConditioningService_1.NetworkConditioningService();
|
|
112
|
+
yield svc.applyProfile('s1', phone('R5CT'), '3G');
|
|
113
|
+
(0, chai_1.expect)(svc.getLatency('s1')).to.equal(100);
|
|
114
|
+
yield svc.reset('s1');
|
|
115
|
+
(0, chai_1.expect)(adb.calls).to.deep.equal([]);
|
|
116
|
+
(0, chai_1.expect)(svc.getLatency('s1')).to.equal(0);
|
|
117
|
+
(0, chai_1.expect)(yield ledger.list()).to.deep.equal([]);
|
|
118
|
+
}));
|
|
119
|
+
it('turns the network on for Normal, and has nothing to undo at the end', () => __awaiter(this, void 0, void 0, function* () {
|
|
120
|
+
const svc = new NetworkConditioningService_1.NetworkConditioningService();
|
|
121
|
+
yield svc.applyProfile('s1', phone('R5CT'), 'Normal');
|
|
122
|
+
(0, chai_1.expect)(adb.commands('R5CT')).to.deep.equal(['shell svc data enable', 'shell svc wifi enable']);
|
|
123
|
+
adb.calls.length = 0;
|
|
124
|
+
yield svc.reset('s1');
|
|
125
|
+
(0, chai_1.expect)(adb.calls).to.deep.equal([]);
|
|
126
|
+
(0, chai_1.expect)(yield ledger.list()).to.deep.equal([]);
|
|
127
|
+
}));
|
|
128
|
+
it("doesn't run xcrun for an iOS simulator, and says once that only the delay applies", () => __awaiter(this, void 0, void 0, function* () {
|
|
129
|
+
const exec = sinon_1.default.spy(childProcess, 'exec');
|
|
130
|
+
const execFile = sinon_1.default.spy(childProcess, 'execFile');
|
|
131
|
+
const svc = new NetworkConditioningService_1.NetworkConditioningService();
|
|
132
|
+
const said = sinon_1.default.spy(svc.log, 'info');
|
|
133
|
+
const sim = { udid: 'SIM-1', platform: 'ios', deviceType: 'simulator' };
|
|
134
|
+
yield svc.applyProfile('s1', sim, 'Offline');
|
|
135
|
+
yield svc.applyProfile('s2', Object.assign(Object.assign({}, sim), { udid: 'SIM-2' }), 'Edge');
|
|
136
|
+
yield svc.reset('s1');
|
|
137
|
+
yield svc.reset('s2');
|
|
138
|
+
(0, chai_1.expect)(exec.called).to.equal(false);
|
|
139
|
+
(0, chai_1.expect)(execFile.called).to.equal(false);
|
|
140
|
+
(0, chai_1.expect)(adb.calls).to.deep.equal([]);
|
|
141
|
+
const notes = said.args.filter((a) => /only .*delay/i.test(String(a[0])));
|
|
142
|
+
(0, chai_1.expect)(notes, said.args.map((a) => a[0]).join('\n')).to.have.length(1);
|
|
143
|
+
(0, chai_1.expect)(yield ledger.list()).to.deep.equal([]);
|
|
144
|
+
}));
|
|
145
|
+
});
|