@xenon-device-management/xenon 2.0.0 → 2.2.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 +2 -1
- package/lib/package.json +3 -4
- package/lib/public/assets/{AnnotationOverlay-BmTPCAds.js → AnnotationOverlay-XpewX5nn.js} +1 -1
- package/lib/public/assets/{ApiKeyGate-B5eB7f8B.js → ApiKeyGate-BQDv3ZCC.js} +1 -1
- package/lib/public/assets/BugReportButton-C7OmS61B.js +10 -0
- package/lib/public/assets/{DeviceMosaicView-9DUoZnQi.js → DeviceMosaicView-wl3l2Zlt.js} +1 -1
- package/lib/public/assets/{EmptyState-lvyNiXev.js → EmptyState-fPxujqqE.js} +1 -1
- package/lib/public/assets/{FieldGroup-BduMPM9K.js → FieldGroup-5bCutWrs.js} +1 -1
- package/lib/public/assets/{FilterMenu-BSqX6I94.js → FilterMenu-owsBiDT1.js} +1 -1
- package/lib/public/assets/{Menu-Ba5cT7jZ.js → Menu-DFhqzPSt.js} +1 -1
- package/lib/public/assets/{Modal-CceoMY7D.js → Modal-Bw-a-E-e.js} +1 -1
- package/lib/public/assets/{RecordingPage-6T9dOidc.js → RecordingPage-WtByf5bC.js} +1 -1
- package/lib/public/assets/{RecordingsPage-qj6FdZXR.js → RecordingsPage-FpWWFNgT.js} +1 -1
- package/lib/public/assets/{SegmentedControl-CSRuZ8TR.js → SegmentedControl-Jzn9c7o8.js} +1 -1
- package/lib/public/assets/{SettingCard-Bp1kz4By.js → SettingCard-BR7nOhpT.js} +1 -1
- package/lib/public/assets/{Table-LV7zFyZg.js → Table-B_Va3V05.js} +1 -1
- package/lib/public/assets/{activity-D17TUloi.js → activity-DxRFWvkN.js} +1 -1
- package/lib/public/assets/{ai-settings-qvbhY89P.js → ai-settings-CQf-9_V6.js} +1 -1
- package/lib/public/assets/{api-keys-BfpvdG7G.js → api-keys-SoE1Kd7N.js} +1 -1
- package/lib/public/assets/{apps-Dbnzd0fW.js → apps-CgGXTlgn.js} +1 -1
- package/lib/public/assets/{arrow-left-DD1VjtC7.js → arrow-left-uQ6WIwiC.js} +1 -1
- package/lib/public/assets/{arrow-right-D0pnM3ma.js → arrow-right-C9RyfzXS.js} +1 -1
- package/lib/public/assets/{arrow-up-right-BIC8JOV6.js → arrow-up-right-DFHi_8yG.js} +1 -1
- package/lib/public/assets/{auth-shell-DWHPXSnI.js → auth-shell-Bii1t4qc.js} +2 -2
- package/lib/public/assets/{builds-page-ClCNBUv_.js → builds-page-Bnv8Z9hi.js} +1 -1
- package/lib/public/assets/{button-xtAhYR-f.js → button-_Y78wXiP.js} +1 -1
- package/lib/public/assets/{calendar-CupwJ7Bm.js → calendar-D4MqSOc2.js} +1 -1
- package/lib/public/assets/{check-CyuEoKkd.js → check-uGHCXe-L.js} +1 -1
- package/lib/public/assets/{chevron-left-DgX-F9xK.js → chevron-left-Dy1OqpzB.js} +1 -1
- package/lib/public/assets/{chevron-right-BKJgJnU9.js → chevron-right-BUsldEli.js} +1 -1
- package/lib/public/assets/{circle-check-CZdg8mMa.js → circle-check-D3Qq0tKp.js} +1 -1
- package/lib/public/assets/{circle-x-CZH6t9z-.js → circle-x-B4IOD1DE.js} +1 -1
- package/lib/public/assets/{clock-Cd-t7xpe.js → clock-BfYfzvSg.js} +1 -1
- package/lib/public/assets/{copy-C6zerX3I.js → copy-DZwYE5Va.js} +1 -1
- package/lib/public/assets/{device-explorer-B1pI6b_M.js → device-explorer-CRG4Utt5.js} +3 -3
- package/lib/public/assets/device-explorer-D2AqrrLN.css +1 -0
- package/lib/public/assets/{download-x9WP7mcL.js → download-CCDYo_TN.js} +1 -1
- package/lib/public/assets/{forgot-password-B0TEH4N-.js → forgot-password-DuLCmVFb.js} +1 -1
- package/lib/public/assets/{funnel-Cb40MfFN.js → funnel-CtKNPqzb.js} +1 -1
- package/lib/public/assets/{index-BR1wTIPC.js → index-3uxl3o7K.js} +43 -43
- package/lib/public/assets/{index-BvH_ekRy.js → index-Zy1Op5C1.js} +1 -1
- package/lib/public/assets/{input-DejcCiTI.js → input-CzTWvUXZ.js} +1 -1
- package/lib/public/assets/{keycodes-Mavx5iGg.js → keycodes-CI-nYDFM.js} +1 -1
- package/lib/public/assets/{layers-BKqYLiHE.js → layers-YZZGFyTX.js} +1 -1
- package/lib/public/assets/{lock-LFsFhc17.js → lock-L8tzWgDo.js} +1 -1
- package/lib/public/assets/{login-x2pkzt-t.js → login-DBbX-fu8.js} +1 -1
- package/lib/public/assets/{maintenance-settings-CYafcoV-.js → maintenance-settings-AJwkOIsE.js} +1 -1
- package/lib/public/assets/{monitor-_7_uJ8d9.js → monitor-Di_66F2m.js} +1 -1
- package/lib/public/assets/{mouse-pointer-2-BfLuURKN.js → mouse-pointer-2-Cnab2zdg.js} +1 -1
- package/lib/public/assets/{network-Z_zBhpsD.js → network-DiSumY-1.js} +1 -1
- package/lib/public/assets/{overview-BY5uv08S.js → overview-CwE4gXl5.js} +1 -1
- package/lib/public/assets/{page-header-CP2UkykG.js → page-header-CfR4Sm9V.js} +1 -1
- package/lib/public/assets/{plus-BTD0dnQ0.js → plus-DOS8P9CQ.js} +1 -1
- package/lib/public/assets/{profile-page-BhJZuCS7.js → profile-page-BYpHwQIH.js} +1 -1
- package/lib/public/assets/{reset-password-CMN0SO81.js → reset-password-BRVJU_qV.js} +1 -1
- package/lib/public/assets/{runbook-page-CBpPX9_5.js → runbook-page-Bi3JvAFZ.js} +1 -1
- package/lib/public/assets/{select-Uc9E5pye.js → select-Cqx7pEou.js} +1 -1
- package/lib/public/assets/{selector-detail-page-BSyBnRuU.js → selector-detail-page-C0DsncKj.js} +1 -1
- package/lib/public/assets/{selector-health-DzqxNkzm.js → selector-health-CAhpdilF.js} +1 -1
- package/lib/public/assets/{selector-health-page-CyPLjoD1.js → selector-health-page-CrCVeW2m.js} +1 -1
- package/lib/public/assets/{session-detail-page-BZQZN1P-.js → session-detail-page-F_hwYqEt.js} +1 -1
- package/lib/public/assets/{settings-CkSWiZq8.js → settings-C-s5pypL.js} +1 -1
- package/lib/public/assets/{status-pill-outline-BFYni2qo.js → status-pill-outline-DY5uzv6A.js} +1 -1
- package/lib/public/assets/{tablet-BSd-xSZK.js → tablet-Dli7gwyg.js} +1 -1
- package/lib/public/assets/{teams-PpoEQz31.js → teams-ui4IhHNC.js} +1 -1
- package/lib/public/assets/{trash-2-BorJv5_u.js → trash-2-BV4hSmXJ.js} +1 -1
- package/lib/public/assets/{upload-C3i7sAZl.js → upload-DkGy_J-L.js} +1 -1
- package/lib/public/assets/{use-password-reset-mode-CeZQ3EsH.js → use-password-reset-mode-BFtMqQOh.js} +1 -1
- package/lib/public/assets/{users-Drd7ifJx.js → users-BU0nXIVe.js} +1 -1
- package/lib/public/assets/{users-CEJ3fC78.js → users-Nb_rzRmF.js} +1 -1
- package/lib/public/assets/{video-off-CNzn6bPE.js → video-off-CUNzD2kH.js} +1 -1
- package/lib/public/assets/{webhook-settings-BFqX6QXV.js → webhook-settings-Ca3UFXZe.js} +1 -1
- package/lib/public/assets/{zap-4yN8Jdfw.js → zap-B2bH2vKX.js} +1 -1
- package/lib/public/index.html +1 -1
- package/lib/src/InternalHttpClient.js +5 -1
- package/lib/src/XenonCapabilityManager.js +15 -1
- package/lib/src/app/appiumBasePath.js +32 -0
- package/lib/src/app/index.js +12 -0
- package/lib/src/app/insertBeforeRoutes.js +24 -4
- package/lib/src/app/registerCommandAuth.js +152 -33
- package/lib/src/app/routers/control.js +11 -104
- package/lib/src/app/routers/grid.js +28 -8
- package/lib/src/app/routers/nodePhoneControl.js +301 -0
- package/lib/src/app/ws/h264StreamWs.js +43 -12
- package/lib/src/app/ws/logcatWs.js +48 -14
- package/lib/src/app/ws/nodeSocketRelay.js +167 -0
- package/lib/src/app/ws/upgradeRouter.js +215 -0
- package/lib/src/dashboard/event-manager.js +7 -6
- package/lib/src/data-service/device-service.js +152 -49
- package/lib/src/data-service/device-store.js +92 -10
- package/lib/src/data-service/deviceClaims.js +116 -0
- package/lib/src/data-service/deviceFieldOwners.js +32 -10
- package/lib/src/data-service/prisma-store.js +102 -31
- package/lib/src/device-managers/AndroidDeviceManager.js +40 -42
- package/lib/src/device-managers/HealthMonitorService.js +17 -2
- package/lib/src/device-managers/IOSDeviceManager.js +5 -1
- package/lib/src/device-managers/NodeDevices.js +7 -2
- package/lib/src/device-managers/android/AndroidH264StreamService.js +13 -5
- package/lib/src/device-managers/android/AndroidStreamService.js +9 -6
- package/lib/src/device-managers/android/previewHold.js +2 -2
- package/lib/src/device-managers/ios/IOSDiscoveryService.js +19 -8
- package/lib/src/device-managers/ios/IOSStreamService.js +162 -40
- package/lib/src/device-managers/localDeviceHosts.js +86 -0
- package/lib/src/device-managers/ownDeviceRow.js +38 -0
- package/lib/src/device-utils.js +78 -24
- package/lib/src/gateway/createHandoff.js +73 -0
- package/lib/src/gateway/defaultGateway.js +80 -0
- package/lib/src/gateway/forwardToNode.js +147 -0
- package/lib/src/gateway/hubSessionToken.js +356 -0
- package/lib/src/gateway/internalCall.js +90 -0
- package/lib/src/gateway/nodeSessionStatus.js +140 -0
- package/lib/src/gateway/nodeWebDriverUrl.js +139 -0
- package/lib/src/gateway/sessionCreate.js +136 -0
- package/lib/src/gateway/sessionGateway.js +201 -0
- package/lib/src/gateway/sessionLocator.js +100 -0
- package/lib/src/generated/client/edge.js +7 -4
- package/lib/src/generated/client/index-browser.js +4 -1
- package/lib/src/generated/client/index.d.ts +112 -1
- package/lib/src/generated/client/index.js +7 -4
- package/lib/src/generated/client/package.json +1 -1
- package/lib/src/generated/client/schema.prisma +9 -0
- package/lib/src/generated/client/wasm.js +4 -1
- package/lib/src/helpers/index.js +5 -1
- package/lib/src/logger.js +5 -0
- package/lib/src/middleware/authMiddleware.js +80 -0
- package/lib/src/middleware/commandAuth.js +31 -17
- package/lib/src/middleware/csrfMiddleware.js +3 -2
- package/lib/src/middleware/deviceAccessGuard.js +4 -1
- package/lib/src/middleware/sessionListingFilter.js +153 -0
- package/lib/src/middleware/sessionUpgradeGuard.js +203 -0
- package/lib/src/plugin.js +27 -1
- package/lib/src/prisma.js +2 -1
- package/lib/src/services/DeviceReconciler.js +5 -2
- package/lib/src/services/OrphanSweeper.js +12 -19
- package/lib/src/services/PortAllocator.js +75 -16
- package/lib/src/services/ServerManager.js +63 -30
- package/lib/src/services/SessionLifecycleService.js +335 -76
- package/lib/src/services/ShutdownCoordinator.js +7 -1
- package/lib/src/services/SocketServer.js +11 -2
- package/lib/src/services/device-access/LiveSessionOwners.js +53 -0
- package/lib/src/services/device-access/SessionOwnerResolver.js +74 -0
- package/lib/src/services/device-access/ticketActorAccess.js +3 -1
- package/lib/src/services/lease/LeaseOrphanSweeper.js +34 -6
- package/lib/src/services/lease/LeaseService.js +19 -5
- package/lib/src/services/session/nodeCreateCaps.js +55 -0
- package/lib/src/services/session/sessionCredentials.js +7 -59
- package/lib/src/sessions/CloudSession.js +12 -0
- package/lib/src/sessions/LocalSession.js +52 -26
- package/lib/src/sessions/RemoteSession.js +116 -9
- package/lib/src/sessions/SessionManager.js +49 -6
- package/lib/src/sessions/appiumUmbrella.js +32 -0
- package/lib/test/helpers/appium-umbrella.js +84 -0
- package/lib/test/helpers/fake-device-table.js +97 -0
- package/lib/test/helpers/loki-stores.js +41 -0
- package/lib/test/helpers/loopbackRequest.js +77 -0
- package/lib/test/helpers/own-node-id.js +29 -0
- package/lib/test/helpers/scratch-database.js +100 -0
- package/lib/test/helpers/scratch-port-leases.js +119 -0
- package/lib/test/helpers/test-container.js +35 -27
- package/lib/test/integration/androidDevices.spec.js +5 -4
- package/lib/test/integration/ios/01iOSSimulator.spec.js +6 -5
- package/lib/test/integration/ios/02iOSDevices.spec.js +3 -2
- package/lib/test/integration/prisma-device-store-add-devices.spec.js +14 -4
- package/lib/test/setup/scratch-default-database.js +32 -0
- package/lib/test/unit/AndroidDeviceManager.spec.js +56 -1
- package/lib/test/unit/DeviceModel.spec.js +9 -9
- package/lib/test/unit/HealingCascade.test.js +5 -1
- package/lib/test/unit/OrphanSweeper.test.js +17 -13
- package/lib/test/unit/SessionHeartbeatWrite.test.js +6 -7
- package/lib/test/unit/StreamTicketService.spec.js +4 -1
- package/lib/test/unit/TokenEndpoint.spec.js +17 -3
- package/lib/test/unit/aggregate-hotspots-session-scope.spec.js +5 -5
- package/lib/test/unit/android-h264-service.spec.js +24 -0
- package/lib/test/unit/android-stream-stall-eof.spec.js +10 -0
- package/lib/test/unit/apps-team-visibility.spec.js +16 -16
- package/lib/test/unit/auth-me-auth-disabled.test.js +7 -4
- package/lib/test/unit/authMiddleware.appTicket.spec.js +5 -5
- package/lib/test/unit/authMiddleware.bearer.spec.js +4 -1
- package/lib/test/unit/authMiddleware.mcpAudience.spec.js +4 -1
- package/lib/test/unit/authMiddleware.streamTicket.spec.js +19 -7
- package/lib/test/unit/authMiddleware.test.js +3 -0
- package/lib/test/unit/bug-report/route.spec.js +9 -9
- package/lib/test/unit/cliargs-admin-only.spec.js +3 -3
- package/lib/test/unit/clipboard-route.spec.js +22 -8
- package/lib/test/unit/command-auth-appium-server.spec.js +187 -32
- package/lib/test/unit/command-auth.spec.js +31 -31
- package/lib/test/unit/control-device-lookup.spec.js +9 -9
- package/lib/test/unit/control-forward-decision.spec.js +115 -0
- package/lib/test/unit/control-lookup-failure.spec.js +3 -3
- package/lib/test/unit/create-error-message.spec.js +43 -0
- package/lib/test/unit/create-handoff.spec.js +86 -0
- package/lib/test/unit/dashboard-events-team-scope.spec.js +6 -3
- package/lib/test/unit/device-access-guard.spec.js +66 -28
- package/lib/test/unit/device-service.spec.js +4 -0
- package/lib/test/unit/device-store-clear-at-boot.spec.js +48 -0
- package/lib/test/unit/device-sync-lost-update.spec.js +17 -89
- package/lib/test/unit/device-team-guard.spec.js +20 -20
- package/lib/test/unit/device-utils.spec.js +8 -0
- package/lib/test/unit/extract-team-cap.spec.js +65 -0
- package/lib/test/unit/grid-admin-only.spec.js +3 -3
- package/lib/test/unit/healing-tier-gate.spec.js +5 -1
- package/lib/test/unit/health-monitor-own-phones.spec.js +102 -0
- package/lib/test/unit/health-monitor-stream-lookup.spec.js +11 -1
- package/lib/test/unit/helpers.spec.js +27 -0
- package/lib/test/unit/hub-boot-sweep-recovery.spec.js +192 -0
- package/lib/test/unit/hub-create-once.spec.js +190 -0
- package/lib/test/unit/hub-create-session-umbrella.spec.js +160 -0
- package/lib/test/unit/hub-gateway-create-appium-server.spec.js +591 -0
- package/lib/test/unit/hub-node-control-forward.spec.js +608 -0
- package/lib/test/unit/hub-node-gateway-appium-server.spec.js +594 -0
- package/lib/test/unit/hub-node-race.spec.js +426 -0
- package/lib/test/unit/hub-node-same-host.spec.js +356 -0
- package/lib/test/unit/hub-register-removal.spec.js +165 -0
- package/lib/test/unit/hub-remote-session-heartbeat.spec.js +277 -0
- package/lib/test/unit/hub-routed-session-recovery.spec.js +151 -0
- package/lib/test/unit/hub-session-token.spec.js +265 -0
- package/lib/test/unit/insert-before-routes.spec.js +5 -5
- package/lib/test/unit/install-repository-app-team.spec.js +6 -2
- package/lib/test/unit/interceptor/MitmProxyHost.spec.js +23 -10
- package/lib/test/unit/internal-call.spec.js +181 -0
- package/lib/test/unit/ios-discovery-same-host.spec.js +193 -0
- package/lib/test/unit/ios-stream-appium-tunnel.spec.js +317 -0
- package/lib/test/unit/ios-stream-port-leases.spec.js +299 -0
- package/lib/test/unit/ios-wda-status-identity.spec.js +84 -0
- package/lib/test/unit/is-xenon-running.spec.js +69 -0
- package/lib/test/unit/lease/LeaseOrphanSweeper.spec.js +2 -2
- package/lib/test/unit/lease/LeaseService.spec.js +28 -1
- package/lib/test/unit/lease/lease-reap-held-phone.spec.js +154 -0
- package/lib/test/unit/lease/lease-sweeper-expiry.spec.js +107 -0
- package/lib/test/unit/lease/lease-team-visibility.spec.js +2 -2
- package/lib/test/unit/lease/lease-token-log-filter.spec.js +242 -64
- package/lib/test/unit/lease/lease-token-oracle.spec.js +20 -7
- package/lib/test/unit/lease/session-lease-proof.spec.js +8 -3
- package/lib/test/unit/logcat-ws.spec.js +12 -1
- package/lib/test/unit/loopback-request.spec.js +81 -0
- package/lib/test/unit/mark-node-busy-loki.spec.js +53 -0
- package/lib/test/unit/node-create-caps.spec.js +54 -0
- package/lib/test/unit/node-gateway-create-appium-server.spec.js +354 -0
- package/lib/test/unit/node-hub-session-owner.spec.js +223 -0
- package/lib/test/unit/node-phone-control.spec.js +131 -0
- package/lib/test/unit/node-socket-relay.spec.js +357 -0
- package/lib/test/unit/omni-vision.spec.js +7 -3
- package/lib/test/unit/plugin.spec.js +45 -4
- package/lib/test/unit/port-allocator-os-probe.spec.js +151 -0
- package/lib/test/unit/profile-router.test.js +11 -11
- package/lib/test/unit/queue-requester.spec.js +5 -0
- package/lib/test/unit/queue-team-scope-routes.spec.js +17 -16
- package/lib/test/unit/queue-visibility.spec.js +1 -1
- package/lib/test/unit/recording-downloads-visibility.spec.js +2 -2
- package/lib/test/unit/recording-instrumentation.spec.js +5 -0
- package/lib/test/unit/recording-source-ended.spec.js +5 -0
- package/lib/test/unit/recording-store.spec.js +9 -3
- package/lib/test/unit/recordings-library-routes.spec.js +51 -51
- package/lib/test/unit/recordings-write-visibility.spec.js +2 -2
- package/lib/test/unit/remote-session-create.spec.js +130 -0
- package/lib/test/unit/reservation-team-visibility.spec.js +5 -5
- package/lib/test/unit/reset-link.test.js +14 -14
- package/lib/test/unit/session-app-resolution.spec.js +14 -10
- package/lib/test/unit/session-assets.spec.js +9 -9
- package/lib/test/unit/session-attribution-wiring.spec.js +29 -2
- package/lib/test/unit/session-create-layer.spec.js +255 -0
- package/lib/test/unit/session-credential-strip.spec.js +25 -9
- package/lib/test/unit/session-credentials.spec.js +0 -47
- package/lib/test/unit/session-listing-auth.spec.js +345 -0
- package/lib/test/unit/session-locator.spec.js +250 -0
- package/lib/test/unit/session-node-calls.spec.js +174 -0
- package/lib/test/unit/session-owner-resolver.spec.js +107 -0
- package/lib/test/unit/session-recovery-routing.spec.js +118 -0
- package/lib/test/unit/session-upgrade-guard.spec.js +571 -0
- package/lib/test/unit/shutdown-drain-remote.spec.js +58 -0
- package/lib/test/unit/socketServer.bearer.spec.js +3 -1
- package/lib/test/unit/stream-leave-route.spec.js +19 -7
- package/lib/test/unit/stream-own-device-row.spec.js +164 -0
- package/lib/test/unit/stream-stop-ownership.spec.js +17 -5
- package/lib/test/unit/stream-stop-recording.spec.js +17 -5
- package/lib/test/unit/stream-ticket-identity.spec.js +26 -6
- package/lib/test/unit/stream-watchdog-timers.spec.js +46 -0
- package/lib/test/unit/upgrade-router.spec.js +446 -0
- package/lib/test/unit/users-router.test.js +11 -8
- package/lib/test/unit/verify-credential.spec.js +4 -1
- package/lib/test/unit/webdriver-info-route.spec.js +64 -0
- package/lib/test/unit/ws-upgrade-routing-appium-server.spec.js +500 -0
- package/lib/tsconfig.tsbuildinfo +1 -1
- package/package.json +3 -4
- package/prisma/migrations/20260929120000_device_claims/migration.sql +8 -0
- package/prisma/schema.prisma +9 -0
- package/lib/public/assets/BugReportButton-DKYsOdGV.js +0 -10
- package/lib/public/assets/device-explorer-DV8rWRxL.css +0 -1
- package/lib/src/proxy/wd-command-proxy.js +0 -221
|
@@ -0,0 +1,100 @@
|
|
|
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
|
+
exports.useScratchDatabase = useScratchDatabase;
|
|
16
|
+
const child_process_1 = require("child_process");
|
|
17
|
+
const fs_1 = __importDefault(require("fs"));
|
|
18
|
+
const os_1 = __importDefault(require("os"));
|
|
19
|
+
const path_1 = __importDefault(require("path"));
|
|
20
|
+
const sinon_1 = __importDefault(require("sinon"));
|
|
21
|
+
const client_1 = require("../../src/generated/client");
|
|
22
|
+
const prisma_1 = require("../../src/prisma");
|
|
23
|
+
// Every model the `prisma` wrapper exposes (src/prisma.ts MODEL_DELEGATES).
|
|
24
|
+
const MODELS = [
|
|
25
|
+
'build',
|
|
26
|
+
'session',
|
|
27
|
+
'sessionLog',
|
|
28
|
+
'log',
|
|
29
|
+
'profiling',
|
|
30
|
+
'app',
|
|
31
|
+
'device',
|
|
32
|
+
'pendingSession',
|
|
33
|
+
'cLIArgs',
|
|
34
|
+
'webhookConfig',
|
|
35
|
+
'webConfig',
|
|
36
|
+
'locatorEtalon',
|
|
37
|
+
'lease',
|
|
38
|
+
'portLease',
|
|
39
|
+
'apiKey',
|
|
40
|
+
'selectorState',
|
|
41
|
+
'user',
|
|
42
|
+
'userSession',
|
|
43
|
+
'passwordResetToken',
|
|
44
|
+
'team',
|
|
45
|
+
'teamMember',
|
|
46
|
+
'eventLog',
|
|
47
|
+
'project',
|
|
48
|
+
'recording',
|
|
49
|
+
'bookmark',
|
|
50
|
+
'annotation',
|
|
51
|
+
];
|
|
52
|
+
/**
|
|
53
|
+
* Back the whole `prisma` wrapper with a scratch SQLite file for the enclosing
|
|
54
|
+
* `describe`, so the real queries (the device store, the session lifecycle,
|
|
55
|
+
* SessionOwnerResolver) run against a real database that is not the server's.
|
|
56
|
+
* Like useScratchPortLeases, for every model.
|
|
57
|
+
*
|
|
58
|
+
* Call it inside a `describe`, never at the top of a file. The database is
|
|
59
|
+
* migrated once per suite; the stubs are made before each test and removed
|
|
60
|
+
* after it, so a spec's own `sinon.restore()` can't leave the next test on
|
|
61
|
+
* the real database. Tests empty the tables they use.
|
|
62
|
+
*/
|
|
63
|
+
function useScratchDatabase() {
|
|
64
|
+
const ctx = {};
|
|
65
|
+
const sandbox = sinon_1.default.createSandbox();
|
|
66
|
+
let dbPath = '';
|
|
67
|
+
before(function () {
|
|
68
|
+
this.timeout(90000);
|
|
69
|
+
dbPath = path_1.default.join(os_1.default.tmpdir(), `xenon-scratch-${process.pid}-${Date.now()}.db`);
|
|
70
|
+
const url = `file:${dbPath}`;
|
|
71
|
+
(0, child_process_1.execSync)('npx prisma migrate deploy', {
|
|
72
|
+
cwd: path_1.default.resolve(__dirname, '../..'),
|
|
73
|
+
env: Object.assign(Object.assign({}, process.env), { DATABASE_URL: url }),
|
|
74
|
+
stdio: 'pipe',
|
|
75
|
+
});
|
|
76
|
+
ctx.db = new client_1.PrismaClient({ datasources: { db: { url } } });
|
|
77
|
+
});
|
|
78
|
+
beforeEach(() => {
|
|
79
|
+
for (const model of MODELS) {
|
|
80
|
+
const wrapper = prisma_1.prisma[model];
|
|
81
|
+
const delegate = ctx.db[model];
|
|
82
|
+
for (const [name, value] of Object.entries(wrapper)) {
|
|
83
|
+
if (typeof value !== 'function' || typeof delegate[name] !== 'function')
|
|
84
|
+
continue;
|
|
85
|
+
sandbox.stub(wrapper, name).callsFake((...args) => delegate[name](...args));
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
});
|
|
89
|
+
afterEach(() => sandbox.restore());
|
|
90
|
+
after(() => __awaiter(this, void 0, void 0, function* () {
|
|
91
|
+
sandbox.restore();
|
|
92
|
+
if (ctx.db)
|
|
93
|
+
yield ctx.db.$disconnect();
|
|
94
|
+
for (const f of [dbPath, `${dbPath}-journal`]) {
|
|
95
|
+
if (f && fs_1.default.existsSync(f))
|
|
96
|
+
fs_1.default.unlinkSync(f);
|
|
97
|
+
}
|
|
98
|
+
}));
|
|
99
|
+
return ctx;
|
|
100
|
+
}
|
|
@@ -0,0 +1,119 @@
|
|
|
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
|
+
exports.useScratchPortLeases = useScratchPortLeases;
|
|
16
|
+
const child_process_1 = require("child_process");
|
|
17
|
+
const fs_1 = __importDefault(require("fs"));
|
|
18
|
+
const net_1 = __importDefault(require("net"));
|
|
19
|
+
const os_1 = __importDefault(require("os"));
|
|
20
|
+
const path_1 = __importDefault(require("path"));
|
|
21
|
+
const sinon_1 = __importDefault(require("sinon"));
|
|
22
|
+
const client_1 = require("../../src/generated/client");
|
|
23
|
+
const prisma_1 = require("../../src/prisma");
|
|
24
|
+
const METHODS = [
|
|
25
|
+
'findUnique',
|
|
26
|
+
'findFirst',
|
|
27
|
+
'findMany',
|
|
28
|
+
'create',
|
|
29
|
+
'update',
|
|
30
|
+
'upsert',
|
|
31
|
+
'delete',
|
|
32
|
+
'deleteMany',
|
|
33
|
+
];
|
|
34
|
+
/**
|
|
35
|
+
* Below every OS's ephemeral range (macOS 49152-65535, Linux 32768-60999).
|
|
36
|
+
* The blocks used to come from 40000-60000, half of it inside macOS's range,
|
|
37
|
+
* where the suite's own outgoing connections take ports: now and then one
|
|
38
|
+
* held base+1 while a two-port range needed it, and PortAllocator answered
|
|
39
|
+
* "range exhausted" in the full run only.
|
|
40
|
+
*/
|
|
41
|
+
const FIRST_BASE = 20000;
|
|
42
|
+
const BLOCKS = 128;
|
|
43
|
+
const USED_OFFSETS = [...Array(10).keys(), ...Array.from({ length: 10 }, (_, i) => 50 + i)];
|
|
44
|
+
function bindable(port, host) {
|
|
45
|
+
return new Promise((resolve) => {
|
|
46
|
+
const server = net_1.default.createServer();
|
|
47
|
+
server.once('error', (err) =>
|
|
48
|
+
// A host without IPv6 can't have a listener there either.
|
|
49
|
+
resolve(err.code === 'EADDRNOTAVAIL' || err.code === 'EAFNOSUPPORT'));
|
|
50
|
+
server.listen(port, host, () => server.close(() => resolve(true)));
|
|
51
|
+
});
|
|
52
|
+
}
|
|
53
|
+
function blockIsFree(base) {
|
|
54
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
55
|
+
for (const offset of USED_OFFSETS) {
|
|
56
|
+
if (!(yield bindable(base + offset, '0.0.0.0')))
|
|
57
|
+
return false;
|
|
58
|
+
if (!(yield bindable(base + offset, '::')))
|
|
59
|
+
return false;
|
|
60
|
+
}
|
|
61
|
+
return true;
|
|
62
|
+
});
|
|
63
|
+
}
|
|
64
|
+
function freeBlock() {
|
|
65
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
66
|
+
let base = FIRST_BASE;
|
|
67
|
+
for (let attempt = 0; attempt < 20; attempt++) {
|
|
68
|
+
base = FIRST_BASE + Math.floor(Math.random() * BLOCKS) * 100;
|
|
69
|
+
if (yield blockIsFree(base))
|
|
70
|
+
return base;
|
|
71
|
+
}
|
|
72
|
+
return base;
|
|
73
|
+
});
|
|
74
|
+
}
|
|
75
|
+
/**
|
|
76
|
+
* Back `prisma.portLease` with a scratch SQLite file for the enclosing
|
|
77
|
+
* `describe`, so PortAllocator runs its real queries (unique constraint on
|
|
78
|
+
* `port` included) without touching the server's database.
|
|
79
|
+
*
|
|
80
|
+
* Call it inside a `describe`, never at the top of a file. The database is
|
|
81
|
+
* created once per suite; the table is emptied and the stubs are re-made
|
|
82
|
+
* before each test, so a spec's own `sinon.restore()` can't leave the next
|
|
83
|
+
* test pointed at the real database.
|
|
84
|
+
*/
|
|
85
|
+
function useScratchPortLeases() {
|
|
86
|
+
const ctx = {};
|
|
87
|
+
const sandbox = sinon_1.default.createSandbox();
|
|
88
|
+
let dbPath = '';
|
|
89
|
+
before(function () {
|
|
90
|
+
this.timeout(60000);
|
|
91
|
+
dbPath = path_1.default.join(os_1.default.tmpdir(), `xenon-port-leases-${process.pid}-${Date.now()}.db`);
|
|
92
|
+
const url = `file:${dbPath}`;
|
|
93
|
+
(0, child_process_1.execSync)('npx prisma migrate deploy', {
|
|
94
|
+
cwd: path_1.default.resolve(__dirname, '../..'),
|
|
95
|
+
env: Object.assign(Object.assign({}, process.env), { DATABASE_URL: url }),
|
|
96
|
+
stdio: 'pipe',
|
|
97
|
+
});
|
|
98
|
+
ctx.db = new client_1.PrismaClient({ datasources: { db: { url } } });
|
|
99
|
+
});
|
|
100
|
+
beforeEach(() => __awaiter(this, void 0, void 0, function* () {
|
|
101
|
+
yield ctx.db.portLease.deleteMany({});
|
|
102
|
+
ctx.base = yield freeBlock();
|
|
103
|
+
const wrapper = prisma_1.prisma.portLease;
|
|
104
|
+
for (const m of METHODS) {
|
|
105
|
+
sandbox.stub(wrapper, m).callsFake((...args) => ctx.db.portLease[m](...args));
|
|
106
|
+
}
|
|
107
|
+
}));
|
|
108
|
+
afterEach(() => sandbox.restore());
|
|
109
|
+
after(() => __awaiter(this, void 0, void 0, function* () {
|
|
110
|
+
sandbox.restore();
|
|
111
|
+
if (ctx.db)
|
|
112
|
+
yield ctx.db.$disconnect();
|
|
113
|
+
for (const f of [dbPath, `${dbPath}-journal`]) {
|
|
114
|
+
if (f && fs_1.default.existsSync(f))
|
|
115
|
+
fs_1.default.unlinkSync(f);
|
|
116
|
+
}
|
|
117
|
+
}));
|
|
118
|
+
return ctx;
|
|
119
|
+
}
|
|
@@ -23,20 +23,38 @@ const PluginContext_1 = require("../../src/PluginContext");
|
|
|
23
23
|
const IPluginArgs_1 = require("../../src/interfaces/IPluginArgs");
|
|
24
24
|
const uuid_1 = require("uuid");
|
|
25
25
|
const db_1 = require("../../src/data-service/db");
|
|
26
|
-
const device_store_1 = require("../../src/data-service/device-store");
|
|
27
26
|
const AndroidDeviceManager_1 = __importDefault(require("../../src/device-managers/AndroidDeviceManager"));
|
|
28
27
|
const IOSDeviceManager_1 = __importDefault(require("../../src/device-managers/IOSDeviceManager"));
|
|
29
28
|
const device_managers_1 = require("../../src/device-managers");
|
|
30
|
-
const sinon_1 = __importDefault(require("sinon"));
|
|
31
29
|
const IOSDiscoveryService_1 = require("../../src/device-managers/ios/IOSDiscoveryService");
|
|
32
|
-
const
|
|
30
|
+
const container_registration_1 = require("./container-registration");
|
|
33
31
|
/**
|
|
34
|
-
*
|
|
35
|
-
*
|
|
32
|
+
* The classes setupTestContainer() builds. Each call makes new ones, with its
|
|
33
|
+
* own PluginContext; the ones already registered are put back by
|
|
34
|
+
* resetTestContainer().
|
|
35
|
+
*/
|
|
36
|
+
const BUILT = [
|
|
37
|
+
PluginContext_1.PluginContext,
|
|
38
|
+
IOSDiscoveryService_1.IOSDiscoveryService,
|
|
39
|
+
AndroidDeviceManager_1.default,
|
|
40
|
+
IOSDeviceManager_1.default,
|
|
41
|
+
device_managers_1.XenonManager,
|
|
42
|
+
];
|
|
43
|
+
/** What each setupTestContainer() call not yet undone replaced, newest last. */
|
|
44
|
+
const undo = [];
|
|
45
|
+
/**
|
|
46
|
+
* Registers a PluginContext configured for testing, and the device managers
|
|
47
|
+
* built with it. Undo it with resetTestContainer() in the same `describe`.
|
|
48
|
+
*
|
|
49
|
+
* Only the ids it builds are replaced. It used to Container.reset(), which
|
|
50
|
+
* discarded every singleton in the process, including the ones other specs
|
|
51
|
+
* and the server's own modules had built and still held.
|
|
36
52
|
*/
|
|
37
53
|
function setupTestContainer(overrides) {
|
|
38
|
-
|
|
39
|
-
|
|
54
|
+
undo.push((0, container_registration_1.saveRegistrations)('LocalStorage', ...BUILT));
|
|
55
|
+
// Unbuilt again, so the Container.get() calls below make new instances.
|
|
56
|
+
for (const id of BUILT)
|
|
57
|
+
typedi_1.Container.set({ id, type: id });
|
|
40
58
|
const nodeId = (0, uuid_1.v4)();
|
|
41
59
|
const port = 4723;
|
|
42
60
|
const pluginArgs = Object.assign({}, IPluginArgs_1.DefaultPluginArgs, overrides || {});
|
|
@@ -85,28 +103,18 @@ function createTestXenonManager(pluginArgs) {
|
|
|
85
103
|
xenonManager.init();
|
|
86
104
|
return xenonManager;
|
|
87
105
|
}
|
|
106
|
+
/**
|
|
107
|
+
* Puts back every registration the setupTestContainer() calls since the last
|
|
108
|
+
* reset replaced, newest first, and empties the in-memory device database.
|
|
109
|
+
*
|
|
110
|
+
* It changes nothing else. It used to stub device discovery on the
|
|
111
|
+
* AndroidDeviceManager and IOSDiscoveryService prototypes and never restore
|
|
112
|
+
* the stubs, so every spec that ran later got no devices from discovery.
|
|
113
|
+
*/
|
|
88
114
|
function resetTestContainer() {
|
|
89
115
|
return __awaiter(this, void 0, void 0, function* () {
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
// Stub discovery methods to prevent background pollution
|
|
93
|
-
sandbox.stub(AndroidDeviceManager_1.default.prototype, 'getDevices').resolves([]);
|
|
94
|
-
sandbox.stub(IOSDiscoveryService_1.IOSDiscoveryService.prototype, 'getDevices').resolves([]);
|
|
95
|
-
// @ts-ignore - Stubbing internal methods to be extra safe
|
|
96
|
-
sandbox.stub(AndroidDeviceManager_1.default.prototype, 'fetchAndroidDevices').resolves([]);
|
|
97
|
-
// @ts-ignore
|
|
98
|
-
sandbox.stub(IOSDiscoveryService_1.IOSDiscoveryService.prototype, 'fetchLocalIOSDevices').resolves([]);
|
|
99
|
-
// @ts-ignore
|
|
100
|
-
sandbox.stub(IOSDiscoveryService_1.IOSDiscoveryService.prototype, 'fetchLocalSimulators').resolves([]);
|
|
101
|
-
// Clear DeviceStoreFactory static caches to prevent cross-test pollution
|
|
102
|
-
// @ts-ignore - Accessing private static members for test cleanup
|
|
103
|
-
device_store_1.DeviceStoreFactory._deviceStore = undefined;
|
|
104
|
-
// @ts-ignore
|
|
105
|
-
device_store_1.DeviceStoreFactory._pendingSessionStore = undefined;
|
|
106
|
-
// @ts-ignore
|
|
107
|
-
device_store_1.DeviceStoreFactory._cliArgsStore = undefined;
|
|
108
|
-
// @ts-ignore
|
|
109
|
-
device_store_1.DeviceStoreFactory._healEtalonStore = undefined;
|
|
116
|
+
for (const restore of undo.splice(0).reverse())
|
|
117
|
+
restore();
|
|
110
118
|
yield db_1.XenonDatabase.reset();
|
|
111
119
|
});
|
|
112
120
|
}
|
|
@@ -49,6 +49,7 @@ require("reflect-metadata");
|
|
|
49
49
|
const chai_1 = __importStar(require("chai"));
|
|
50
50
|
const db_1 = require("../../src/data-service/db");
|
|
51
51
|
const device_utils_1 = require("../../src/device-utils");
|
|
52
|
+
const localDeviceHosts_1 = require("../../src/device-managers/localDeviceHosts");
|
|
52
53
|
const IPluginArgs_1 = require("../../src/interfaces/IPluginArgs");
|
|
53
54
|
const ip_1 = __importDefault(require("ip"));
|
|
54
55
|
const async_wait_until_1 = __importDefault(require("async-wait-until"));
|
|
@@ -73,7 +74,7 @@ describe('Android Test', () => {
|
|
|
73
74
|
yield (0, device_utils_1.initializeStorage)();
|
|
74
75
|
yield deviceManager.getDevices();
|
|
75
76
|
const hub = pluginArgs.hub;
|
|
76
|
-
yield (0, device_utils_1.updateDeviceList)(pluginArgs
|
|
77
|
+
yield (0, device_utils_1.updateDeviceList)((0, localDeviceHosts_1.localDeviceHosts)(pluginArgs, 4723), hub);
|
|
77
78
|
yield (0, device_utils_1.cleanPendingSessions)(0);
|
|
78
79
|
yield (0, device_service_1.unblockDeviceMatchingFilter)({});
|
|
79
80
|
const capabilities = {
|
|
@@ -95,7 +96,7 @@ describe('Android Test', () => {
|
|
|
95
96
|
it.skip('Allocate second free device and verify both the device state is busy in db', () => __awaiter(void 0, void 0, void 0, function* () {
|
|
96
97
|
yield (0, device_utils_1.initializeStorage)();
|
|
97
98
|
deviceManager = (0, test_container_1.createTestXenonManager)(Object.assign({}, pluginArgs, { platform: 'android' }));
|
|
98
|
-
yield (0, device_utils_1.updateDeviceList)(
|
|
99
|
+
yield (0, device_utils_1.updateDeviceList)((0, localDeviceHosts_1.localDeviceHosts)(pluginArgs, 4723));
|
|
99
100
|
const capabilities = {
|
|
100
101
|
alwaysMatch: {
|
|
101
102
|
platformName: 'android',
|
|
@@ -108,7 +109,7 @@ describe('Android Test', () => {
|
|
|
108
109
|
// wait until there are two devices and both are not offline
|
|
109
110
|
let devices;
|
|
110
111
|
(0, async_wait_until_1.default)(() => __awaiter(void 0, void 0, void 0, function* () {
|
|
111
|
-
yield (0, device_utils_1.updateDeviceList)(
|
|
112
|
+
yield (0, device_utils_1.updateDeviceList)((0, localDeviceHosts_1.localDeviceHosts)(pluginArgs, 4723));
|
|
112
113
|
devices = yield deviceManager.getDevices();
|
|
113
114
|
return devices.length === 2 && devices.every((device) => !device.offline);
|
|
114
115
|
}));
|
|
@@ -120,7 +121,7 @@ describe('Android Test', () => {
|
|
|
120
121
|
yield (0, device_utils_1.initializeStorage)();
|
|
121
122
|
deviceManager = (0, test_container_1.createTestXenonManager)(pluginArgs);
|
|
122
123
|
const hub = pluginArgs.hub;
|
|
123
|
-
yield (0, device_utils_1.updateDeviceList)(pluginArgs
|
|
124
|
+
yield (0, device_utils_1.updateDeviceList)((0, localDeviceHosts_1.localDeviceHosts)(pluginArgs, 4723), hub);
|
|
124
125
|
const capabilities = {
|
|
125
126
|
alwaysMatch: {
|
|
126
127
|
platformName: 'android',
|
|
@@ -22,6 +22,7 @@ Object.defineProperty(exports, "__esModule", { value: true });
|
|
|
22
22
|
require("reflect-metadata");
|
|
23
23
|
const chai_1 = require("chai");
|
|
24
24
|
const device_utils_1 = require("../../../src/device-utils");
|
|
25
|
+
const localDeviceHosts_1 = require("../../../src/device-managers/localDeviceHosts");
|
|
25
26
|
const db_1 = require("../../../src/data-service/db");
|
|
26
27
|
const node_simctl_1 = __importDefault(require("node-simctl"));
|
|
27
28
|
const pluginArgs_1 = require("../../../src/data-service/pluginArgs");
|
|
@@ -56,7 +57,7 @@ function initDeviceFarm(iosDeviceType) {
|
|
|
56
57
|
}));
|
|
57
58
|
(0, chai_1.expect)(deviceManager.getMaxSessionCount()).to.be.eql(1);
|
|
58
59
|
const hub = pluginArgs.hub;
|
|
59
|
-
yield (0, device_utils_1.updateDeviceList)(pluginArgs
|
|
60
|
+
yield (0, device_utils_1.updateDeviceList)((0, localDeviceHosts_1.localDeviceHosts)(pluginArgs, 4723), hub);
|
|
60
61
|
yield markSimulatorsAsBooted();
|
|
61
62
|
yield (0, device_service_1.unblockDeviceMatchingFilter)({});
|
|
62
63
|
yield (0, device_utils_1.cleanPendingSessions)(0);
|
|
@@ -155,7 +156,7 @@ describe('IOS Simulator Test', () => {
|
|
|
155
156
|
yield (0, device_utils_1.initializeStorage)();
|
|
156
157
|
const deviceManager = (0, test_container_1.createTestXenonManager)(pluginArgs);
|
|
157
158
|
const hub = pluginArgs.hub;
|
|
158
|
-
yield (0, device_utils_1.updateDeviceList)(pluginArgs
|
|
159
|
+
yield (0, device_utils_1.updateDeviceList)((0, localDeviceHosts_1.localDeviceHosts)(pluginArgs, 4723), hub);
|
|
159
160
|
yield markSimulatorsAsBooted();
|
|
160
161
|
yield (0, device_service_1.unblockDeviceMatchingFilter)({});
|
|
161
162
|
yield (0, device_utils_1.cleanPendingSessions)(0);
|
|
@@ -184,7 +185,7 @@ describe('IOS Simulator Test', () => {
|
|
|
184
185
|
yield (0, device_utils_1.initializeStorage)();
|
|
185
186
|
const deviceManager = (0, test_container_1.createTestXenonManager)(pluginArgs);
|
|
186
187
|
const hub = pluginArgs.hub;
|
|
187
|
-
yield (0, device_utils_1.updateDeviceList)(pluginArgs
|
|
188
|
+
yield (0, device_utils_1.updateDeviceList)((0, localDeviceHosts_1.localDeviceHosts)(pluginArgs, 4723), hub);
|
|
188
189
|
yield markSimulatorsAsBooted();
|
|
189
190
|
yield (0, device_service_1.unblockDeviceMatchingFilter)({});
|
|
190
191
|
yield (0, device_utils_1.cleanPendingSessions)(0);
|
|
@@ -215,7 +216,7 @@ describe('IOS Simulator Test', () => {
|
|
|
215
216
|
yield (0, device_utils_1.initializeStorage)();
|
|
216
217
|
const deviceManager = (0, test_container_1.createTestXenonManager)(pluginArgs);
|
|
217
218
|
const hub = pluginArgs.hub;
|
|
218
|
-
yield (0, device_utils_1.updateDeviceList)(pluginArgs
|
|
219
|
+
yield (0, device_utils_1.updateDeviceList)((0, localDeviceHosts_1.localDeviceHosts)(pluginArgs, 4723), hub);
|
|
219
220
|
yield markSimulatorsAsBooted();
|
|
220
221
|
yield (0, device_service_1.unblockDeviceMatchingFilter)({});
|
|
221
222
|
yield (0, device_utils_1.cleanPendingSessions)(0);
|
|
@@ -265,7 +266,7 @@ describe('Boot simulator test', () => __awaiter(void 0, void 0, void 0, function
|
|
|
265
266
|
yield (0, device_utils_1.initializeStorage)();
|
|
266
267
|
const deviceManager = (0, test_container_1.createTestXenonManager)(pluginArgs);
|
|
267
268
|
const hub = pluginArgs.hub;
|
|
268
|
-
yield (0, device_utils_1.updateDeviceList)(pluginArgs
|
|
269
|
+
yield (0, device_utils_1.updateDeviceList)((0, localDeviceHosts_1.localDeviceHosts)(pluginArgs, 4723), hub);
|
|
269
270
|
const capabilities = {
|
|
270
271
|
alwaysMatch: {
|
|
271
272
|
platformName: 'iOS',
|
|
@@ -16,6 +16,7 @@ require("reflect-metadata");
|
|
|
16
16
|
const chai_1 = require("chai");
|
|
17
17
|
const ip_1 = __importDefault(require("ip"));
|
|
18
18
|
const device_utils_1 = require("../../../src/device-utils");
|
|
19
|
+
const localDeviceHosts_1 = require("../../../src/device-managers/localDeviceHosts");
|
|
19
20
|
const db_1 = require("../../../src/data-service/db");
|
|
20
21
|
const IPluginArgs_1 = require("../../../src/interfaces/IPluginArgs");
|
|
21
22
|
const device_service_1 = require("../../../src/data-service/device-service");
|
|
@@ -39,7 +40,7 @@ describe('IOS Test', () => {
|
|
|
39
40
|
d.plugin['xenon'].iosDeviceType = 'real';
|
|
40
41
|
});
|
|
41
42
|
const deviceManager = (0, test_container_1.createTestXenonManager)(pluginArgs);
|
|
42
|
-
yield (0, device_utils_1.updateDeviceList)(
|
|
43
|
+
yield (0, device_utils_1.updateDeviceList)((0, localDeviceHosts_1.localDeviceHosts)(pluginArgs, 4723));
|
|
43
44
|
const capabilities = {
|
|
44
45
|
alwaysMatch: {
|
|
45
46
|
platformName: 'iOS',
|
|
@@ -57,7 +58,7 @@ describe('IOS Test', () => {
|
|
|
57
58
|
it('Should throw error if the IPA does not match with device type real', () => __awaiter(void 0, void 0, void 0, function* () {
|
|
58
59
|
yield (0, device_utils_1.initializeStorage)();
|
|
59
60
|
const deviceManager = (0, test_container_1.createTestXenonManager)(pluginArgs);
|
|
60
|
-
yield (0, device_utils_1.updateDeviceList)(
|
|
61
|
+
yield (0, device_utils_1.updateDeviceList)((0, localDeviceHosts_1.localDeviceHosts)(pluginArgs, 4723));
|
|
61
62
|
const capabilities = {
|
|
62
63
|
alwaysMatch: {
|
|
63
64
|
platformName: 'iOS',
|
|
@@ -65,10 +65,20 @@ describe('PrismaDeviceStore.addDevices (integration)', function () {
|
|
|
65
65
|
(0, chai_1.expect)(yield row(NEW)).to.include({ name: 'Pixel 8', busy: false });
|
|
66
66
|
(0, chai_1.expect)(yield row(KNOWN)).to.include({ busy: true });
|
|
67
67
|
}));
|
|
68
|
-
it('
|
|
69
|
-
yield
|
|
70
|
-
|
|
68
|
+
it("as a node's report, records the node's busy apart and never writes the session", () => __awaiter(this, void 0, void 0, function* () {
|
|
69
|
+
yield prisma_1.prisma.device.updateMany({
|
|
70
|
+
where: { udid: KNOWN, host: HOST },
|
|
71
|
+
data: { claimSessionId: 'sess-1', claimedAt: 41 },
|
|
72
|
+
});
|
|
73
|
+
yield store.addDevices([phone(KNOWN, { busy: false, session_id: 'node-sess', sdk: '11' })], {
|
|
74
|
+
nodeReport: true,
|
|
75
|
+
});
|
|
76
|
+
(0, chai_1.expect)(yield row(KNOWN)).to.include({
|
|
77
|
+
sdk: '11',
|
|
78
|
+
nodeBusy: false,
|
|
79
|
+
busy: true,
|
|
80
|
+
session_id: 'sess-1',
|
|
81
|
+
claimSessionId: 'sess-1',
|
|
71
82
|
});
|
|
72
|
-
(0, chai_1.expect)(yield row(KNOWN)).to.include({ busy: false, session_id: 'node-sess' });
|
|
73
83
|
}));
|
|
74
84
|
});
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
// Loaded with --require by `npm run test:all`, before any spec.
|
|
3
|
+
//
|
|
4
|
+
// src/config.ts reads DATABASE_URL once, at import time, and falls back to
|
|
5
|
+
// ~/.cache/xenon/xenon.db. Specs that don't stub the store (EventLogService,
|
|
6
|
+
// MetricsService and others writing in passing) used to write their rows into
|
|
7
|
+
// that file, the database a developer's own server uses. This points the whole
|
|
8
|
+
// run at a freshly migrated scratch file instead, and removes it at exit.
|
|
9
|
+
// Specs that need a database of their own still use useScratchDatabase().
|
|
10
|
+
/* eslint-disable @typescript-eslint/no-var-requires -- plain CommonJS, loaded before ts-node */
|
|
11
|
+
const { execSync } = require('child_process');
|
|
12
|
+
const fs = require('fs');
|
|
13
|
+
const os = require('os');
|
|
14
|
+
const path = require('path');
|
|
15
|
+
const dbPath = path.join(os.tmpdir(), `xenon-test-run-${process.pid}-${Date.now()}.db`);
|
|
16
|
+
const url = `file:${dbPath}`;
|
|
17
|
+
execSync('npx prisma migrate deploy', {
|
|
18
|
+
cwd: path.resolve(__dirname, '../..'),
|
|
19
|
+
env: Object.assign(Object.assign({}, process.env), { DATABASE_URL: url }),
|
|
20
|
+
stdio: 'pipe',
|
|
21
|
+
});
|
|
22
|
+
process.env.DATABASE_URL = url;
|
|
23
|
+
process.on('exit', () => {
|
|
24
|
+
for (const f of [dbPath, `${dbPath}-journal`]) {
|
|
25
|
+
try {
|
|
26
|
+
fs.unlinkSync(f);
|
|
27
|
+
}
|
|
28
|
+
catch (_a) {
|
|
29
|
+
// already gone
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
});
|
|
@@ -14,13 +14,18 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
|
14
14
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
15
15
|
require("reflect-metadata");
|
|
16
16
|
const sinon_1 = __importDefault(require("sinon"));
|
|
17
|
+
const AndroidDeviceManager_1 = require("../../src/device-managers/AndroidDeviceManager");
|
|
17
18
|
const adbkit_1 = __importDefault(require("@devicefarmer/adbkit"));
|
|
18
19
|
const appium_adb_1 = require("appium-adb");
|
|
19
20
|
const chai_1 = require("chai");
|
|
20
21
|
const test_container_1 = require("../helpers/test-container");
|
|
22
|
+
const scratch_database_1 = require("../helpers/scratch-database");
|
|
21
23
|
const GetAdbOriginal_1 = require("./GetAdbOriginal");
|
|
22
24
|
const sandbox = sinon_1.default.createSandbox();
|
|
23
25
|
describe('Android Device Manager', () => {
|
|
26
|
+
// deviceInfo() leases the phone's ports through Prisma. Without a database
|
|
27
|
+
// of its own that was the developer's ~/.cache/xenon/xenon.db.
|
|
28
|
+
(0, scratch_database_1.useScratchDatabase)();
|
|
24
29
|
let adb;
|
|
25
30
|
beforeEach(() => __awaiter(void 0, void 0, void 0, function* () {
|
|
26
31
|
sandbox.restore();
|
|
@@ -37,7 +42,10 @@ describe('Android Device Manager', () => {
|
|
|
37
42
|
});
|
|
38
43
|
}));
|
|
39
44
|
afterEach(function () {
|
|
40
|
-
|
|
45
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
46
|
+
sandbox.restore();
|
|
47
|
+
yield (0, test_container_1.resetTestContainer)();
|
|
48
|
+
});
|
|
41
49
|
});
|
|
42
50
|
it('Android Device List to have added state', () => __awaiter(void 0, void 0, void 0, function* () {
|
|
43
51
|
const androidDevices = (0, test_container_1.createTestAndroidManager)({ platform: 'android' });
|
|
@@ -158,6 +166,31 @@ describe('Android Device Manager', () => {
|
|
|
158
166
|
(0, chai_1.expect)(devices.length).to.be.equal(1);
|
|
159
167
|
(0, chai_1.expect)(devices[0]).to.have.property('udid', 'emulator-7777');
|
|
160
168
|
}));
|
|
169
|
+
// A phone adb reports as plugged (and every phone connected when the server
|
|
170
|
+
// starts) went through onDeviceAdded with no androidDeviceType check: a
|
|
171
|
+
// hub set to emulators only listed a real phone until its next sync pruned
|
|
172
|
+
// it, and could hand it to a session meanwhile.
|
|
173
|
+
describe('a plugged phone', () => {
|
|
174
|
+
const plug = (androidDeviceType, real) => __awaiter(void 0, void 0, void 0, function* () {
|
|
175
|
+
const manager = (0, test_container_1.createTestAndroidManager)({ platform: 'android', androidDeviceType });
|
|
176
|
+
sandbox.stub(manager, 'waitBootComplete').resolves(true);
|
|
177
|
+
sandbox.stub(manager, 'isRealDevice').resolves(real);
|
|
178
|
+
// Stands for the rest of the add: the port lease, the row, the hub.
|
|
179
|
+
const deviceInfo = sandbox.stub(manager, 'deviceInfo').resolves(undefined);
|
|
180
|
+
yield manager.onDeviceAdded(adb, { id: 'plugged-1', type: 'device' });
|
|
181
|
+
return deviceInfo.called;
|
|
182
|
+
});
|
|
183
|
+
it('is ignored when the server doesn’t serve its kind', () => __awaiter(void 0, void 0, void 0, function* () {
|
|
184
|
+
(0, chai_1.expect)(yield plug('simulated', true), 'a real phone, emulators only').to.equal(false);
|
|
185
|
+
(0, chai_1.expect)(yield plug('real', false), 'an emulator, real phones only').to.equal(false);
|
|
186
|
+
}));
|
|
187
|
+
it('is added when the server serves its kind', () => __awaiter(void 0, void 0, void 0, function* () {
|
|
188
|
+
(0, chai_1.expect)(yield plug('simulated', false)).to.equal(true);
|
|
189
|
+
(0, chai_1.expect)(yield plug('real', true)).to.equal(true);
|
|
190
|
+
(0, chai_1.expect)(yield plug('both', true)).to.equal(true);
|
|
191
|
+
(0, chai_1.expect)(yield plug('both', false)).to.equal(true);
|
|
192
|
+
}));
|
|
193
|
+
});
|
|
161
194
|
it('should handle device never completing boot', () => __awaiter(void 0, void 0, void 0, function* () {
|
|
162
195
|
const androidDevices = (0, test_container_1.createTestAndroidManager)({ platform: 'android' });
|
|
163
196
|
// @ts-expect-error - Accessing private member for testing
|
|
@@ -176,3 +209,25 @@ describe('Android Device Manager', () => {
|
|
|
176
209
|
}).to.not.throw();
|
|
177
210
|
}));
|
|
178
211
|
});
|
|
212
|
+
// The one rule discovery and a plugged phone both apply.
|
|
213
|
+
describe('includesAndroidDevice', () => {
|
|
214
|
+
const real = { deviceType: 'real', state: 'device' };
|
|
215
|
+
const emulator = { deviceType: 'emulator', state: 'device' };
|
|
216
|
+
const offlineEmulator = { deviceType: 'emulator', state: 'offline' };
|
|
217
|
+
it('real: real phones only', () => {
|
|
218
|
+
(0, chai_1.expect)((0, AndroidDeviceManager_1.includesAndroidDevice)(real, 'real')).to.equal(true);
|
|
219
|
+
(0, chai_1.expect)((0, AndroidDeviceManager_1.includesAndroidDevice)(emulator, 'real')).to.equal(false);
|
|
220
|
+
});
|
|
221
|
+
it('simulated: emulators only, booted ones when bootedEmulators is on', () => {
|
|
222
|
+
(0, chai_1.expect)((0, AndroidDeviceManager_1.includesAndroidDevice)(real, 'simulated')).to.equal(false);
|
|
223
|
+
(0, chai_1.expect)((0, AndroidDeviceManager_1.includesAndroidDevice)(emulator, 'simulated')).to.equal(true);
|
|
224
|
+
(0, chai_1.expect)((0, AndroidDeviceManager_1.includesAndroidDevice)(offlineEmulator, 'simulated')).to.equal(true);
|
|
225
|
+
(0, chai_1.expect)((0, AndroidDeviceManager_1.includesAndroidDevice)(offlineEmulator, 'simulated', true)).to.equal(false);
|
|
226
|
+
});
|
|
227
|
+
it('both: everything, emulators booted when bootedEmulators is on', () => {
|
|
228
|
+
(0, chai_1.expect)((0, AndroidDeviceManager_1.includesAndroidDevice)(real, 'both')).to.equal(true);
|
|
229
|
+
(0, chai_1.expect)((0, AndroidDeviceManager_1.includesAndroidDevice)(emulator, 'both', true)).to.equal(true);
|
|
230
|
+
(0, chai_1.expect)((0, AndroidDeviceManager_1.includesAndroidDevice)(offlineEmulator, 'both', true)).to.equal(false);
|
|
231
|
+
(0, chai_1.expect)((0, AndroidDeviceManager_1.includesAndroidDevice)(Object.assign(Object.assign({}, real), { state: 'offline' }), 'both', true)).to.equal(true);
|
|
232
|
+
});
|
|
233
|
+
});
|
|
@@ -19,18 +19,20 @@ const device_service_1 = require("../../src/data-service/device-service");
|
|
|
19
19
|
const devices_1 = require("./fixtures/devices");
|
|
20
20
|
const chai_2 = require("chai");
|
|
21
21
|
const device_service_2 = require("../../src/data-service/device-service");
|
|
22
|
-
const sinon_1 = __importDefault(require("sinon"));
|
|
23
|
-
chai_1.default.should();
|
|
24
|
-
const sandbox = sinon_1.default.createSandbox();
|
|
25
22
|
const test_container_1 = require("../helpers/test-container");
|
|
26
|
-
|
|
23
|
+
const loki_stores_1 = require("../helpers/loki-stores");
|
|
24
|
+
chai_1.default.should();
|
|
25
|
+
describe('Model Test', () => {
|
|
26
|
+
// The in-memory store this spec is about. Run on its own, without the
|
|
27
|
+
// NODE_ENV=test that test:all sets, it used the Prisma store instead: the
|
|
28
|
+
// developer's own database, whose tables it wrote to.
|
|
29
|
+
(0, loki_stores_1.useLokiStores)();
|
|
27
30
|
before('Add device collection', () => __awaiter(void 0, void 0, void 0, function* () {
|
|
28
31
|
yield (0, test_container_1.resetTestContainer)();
|
|
29
32
|
(0, chai_2.expect)((yield db_1.XenonDatabase.DeviceModel).chain().find().data().length).to.be.equal(0);
|
|
30
33
|
(0, chai_2.expect)(devices_1.deviceMock.length).to.be.greaterThanOrEqual(1);
|
|
31
34
|
// console.log(`deviceMock length: ${deviceMock.length}`);
|
|
32
35
|
(yield db_1.XenonDatabase.DeviceModel).insert(devices_1.deviceMock);
|
|
33
|
-
(yield db_1.XenonDatabase.db).saveDatabase();
|
|
34
36
|
// console.log(`result: ${result}`);
|
|
35
37
|
// console.log(`device model length: ${deviceModel.find().length}`);
|
|
36
38
|
(yield db_1.XenonDatabase.DeviceModel)
|
|
@@ -39,9 +41,7 @@ describe('Model Test', () => __awaiter(void 0, void 0, void 0, function* () {
|
|
|
39
41
|
.data()
|
|
40
42
|
.length.should.be.equal(devices_1.deviceMock.length);
|
|
41
43
|
}));
|
|
42
|
-
after('clean', () =>
|
|
43
|
-
sandbox.restore();
|
|
44
|
-
});
|
|
44
|
+
after('clean', () => (0, test_container_1.resetTestContainer)());
|
|
45
45
|
it('Should remove device from old pool when new poll call does not have the device', () => __awaiter(void 0, void 0, void 0, function* () {
|
|
46
46
|
const findDevice = (yield db_1.XenonDatabase.DeviceModel)
|
|
47
47
|
.chain()
|
|
@@ -157,4 +157,4 @@ describe('Model Test', () => __awaiter(void 0, void 0, void 0, function* () {
|
|
|
157
157
|
const updatedDeviceUdids = updatedDeviceList.map((device) => device.udid);
|
|
158
158
|
addedDeviceUdids.forEach((udid) => updatedDeviceUdids.includes(udid).should.be.true);
|
|
159
159
|
}));
|
|
160
|
-
})
|
|
160
|
+
});
|
|
@@ -9,12 +9,14 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, ge
|
|
|
9
9
|
});
|
|
10
10
|
};
|
|
11
11
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
12
|
+
require("reflect-metadata");
|
|
12
13
|
const chai_1 = require("chai");
|
|
13
14
|
const HealingOrchestrator_1 = require("../../src/services/healing/HealingOrchestrator");
|
|
14
15
|
const types_1 = require("../../src/services/healing/types");
|
|
15
16
|
const typedi_1 = require("typedi");
|
|
16
17
|
const HealEtalonService_1 = require("../../src/services/healing/HealEtalonService");
|
|
17
18
|
const HealedLocatorGenerator_1 = require("../../src/services/healing/HealedLocatorGenerator");
|
|
19
|
+
const container_registration_1 = require("../helpers/container-registration");
|
|
18
20
|
// Mock Tesseract
|
|
19
21
|
const TesseractMock = {
|
|
20
22
|
recognize: () => __awaiter(void 0, void 0, void 0, function* () {
|
|
@@ -38,8 +40,9 @@ describe('Healing Orchestrator - Cascade Strategy', () => {
|
|
|
38
40
|
let orchestrator;
|
|
39
41
|
let mockEtalonService;
|
|
40
42
|
let mockGenerator;
|
|
43
|
+
let restoreContainer;
|
|
41
44
|
beforeEach(() => {
|
|
42
|
-
|
|
45
|
+
restoreContainer = (0, container_registration_1.saveRegistrations)(HealEtalonService_1.HealEtalonService, HealedLocatorGenerator_1.HealedLocatorGenerator);
|
|
43
46
|
mockEtalonService = {
|
|
44
47
|
getSignature: () => __awaiter(void 0, void 0, void 0, function* () { return null; }),
|
|
45
48
|
saveSignature: () => __awaiter(void 0, void 0, void 0, function* () { }),
|
|
@@ -49,6 +52,7 @@ describe('Healing Orchestrator - Cascade Strategy', () => {
|
|
|
49
52
|
typedi_1.Container.set(HealedLocatorGenerator_1.HealedLocatorGenerator, mockGenerator);
|
|
50
53
|
orchestrator = new HealingOrchestrator_1.HealingOrchestrator(mockEtalonService);
|
|
51
54
|
});
|
|
55
|
+
afterEach(() => restoreContainer());
|
|
52
56
|
it('should cascade to OCR (Tier 3) when Fuzzy XML (Tier 2) fails to find a high-confidence match', () => __awaiter(void 0, void 0, void 0, function* () {
|
|
53
57
|
// 1. Setup a page source that has NO matching text or buttons for "Login"
|
|
54
58
|
// This will cause Fuzzy XML Provider to return null (or score below 0.5 threshold)
|