@xenon-device-management/xenon 2.13.2 → 2.15.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 +17 -8
- package/lib/package.json +5 -5
- package/lib/public/assets/{AnnotationOverlay-BXruClDk.js → AnnotationOverlay-Btq3dZRI.js} +1 -1
- package/lib/public/assets/{ApiKeyGate-qTS4LPLC.js → ApiKeyGate-C9OoAgg_.js} +1 -1
- package/lib/public/assets/DeviceMosaicView-DBC_xczB.js +2 -0
- package/lib/public/assets/{EmptyState-Cr17PdDv.js → EmptyState-DqWU2-vM.js} +1 -1
- package/lib/public/assets/{FieldGroup-Bvn08svv.js → FieldGroup-CveQ3xrA.js} +1 -1
- package/lib/public/assets/{FilterMenu-DDSO8aDf.js → FilterMenu-BfKDSfHz.js} +1 -1
- package/lib/public/assets/{Menu-B-Qn2X3T.js → Menu-CtdCybzA.js} +1 -1
- package/lib/public/assets/{Modal-Cg9V5JYU.js → Modal-Bsvj1tm2.js} +1 -1
- package/lib/public/assets/{RecordingPage-BTUs9VTW.js → RecordingPage-CRofOiyx.js} +1 -1
- package/lib/public/assets/{RecordingsPage-DYcfxITK.js → RecordingsPage-jNQHb2IR.js} +2 -2
- package/lib/public/assets/{SegmentedControl-Zy7suhT5.js → SegmentedControl-C8HtTha-.js} +1 -1
- package/lib/public/assets/{SettingCard-CWYmycoe.js → SettingCard-D8K1LZNo.js} +1 -1
- package/lib/public/assets/{Table-io9fwa98.js → Table-zNTX92Pn.js} +1 -1
- package/lib/public/assets/{activity-Bdf_uIWl.js → activity-CJ8LwZq9.js} +1 -1
- package/lib/public/assets/ai-settings-CgxQZqrc.js +16 -0
- package/lib/public/assets/{api-keys-CPqtVpm3.js → api-keys-ECpo-zTo.js} +1 -1
- package/lib/public/assets/{apps-BgZan0H9.js → apps-BjGymyf3.js} +2 -2
- package/lib/public/assets/{arrow-left-B27ckB31.js → arrow-left-BWfCbeHP.js} +1 -1
- package/lib/public/assets/{arrow-right-DOF8ITCz.js → arrow-right-DC7UGf_n.js} +1 -1
- package/lib/public/assets/{arrow-up-right-eFG5KTFS.js → arrow-up-right-BOjwybDk.js} +1 -1
- package/lib/public/assets/{auth-shell-DlC3_27j.js → auth-shell-CxMTo9aH.js} +2 -2
- package/lib/public/assets/{builds-page-BuXpPfKo.js → builds-page-DOCm4HkA.js} +1 -1
- package/lib/public/assets/{button-fIW8owMI.js → button-Cg08-EMT.js} +1 -1
- package/lib/public/assets/{calendar-WNjmrF-a.js → calendar-CA1ikLJs.js} +1 -1
- package/lib/public/assets/{check-BdTh1Sq7.js → check-BMTaH72W.js} +1 -1
- package/lib/public/assets/{chevron-right-B4BKlsqk.js → chevron-right-B2QOv9Nx.js} +1 -1
- package/lib/public/assets/{circle-check-zJU80_EF.js → circle-check-CYXBoUqT.js} +1 -1
- package/lib/public/assets/{circle-x-5Uwquo0h.js → circle-x-DTwnSAnl.js} +1 -1
- package/lib/public/assets/{clock-BtUprDsZ.js → clock-C8_HViQP.js} +1 -1
- package/lib/public/assets/{copy-DGSbZI3u.js → copy-Be7LNMXV.js} +1 -1
- package/lib/public/assets/derive-UnLRp4r3.js +1 -0
- package/lib/public/assets/device-explorer-BOqtfGXz.js +282 -0
- package/lib/public/assets/{download-WG6l7BgD.js → download-B56lwxWP.js} +1 -1
- package/lib/public/assets/forgot-password-CCU6bzLn.js +1 -0
- package/lib/public/assets/h264Stream-C-OtmIuk.js +26 -0
- package/lib/public/assets/index-B9KQ6LsY.js +15 -0
- package/lib/public/assets/{index-DI1fGIyj.js → index-BOxe43p1.js} +5 -5
- package/lib/public/assets/{index-HDQVe1gi.js → index-CfxsUslp.js} +1 -1
- package/lib/public/assets/{input-BjDRKCea.js → input-CZHFjbSg.js} +1 -1
- package/lib/public/assets/{line-chart-C0zdgd0v.js → line-chart-Gl_Dz3X_.js} +1 -1
- package/lib/public/assets/{list-checks-BdW5CjM8.js → list-checks-CCgjeyGY.js} +1 -1
- package/lib/public/assets/{lock-Dy3L4I8E.js → lock-CkOwgDcx.js} +1 -1
- package/lib/public/assets/{login-BHbY9CLG.js → login-D5cc4w7M.js} +1 -1
- package/lib/public/assets/maintenance-settings-t7qr3fvX.js +6 -0
- package/lib/public/assets/{monitor-8xjkeHHi.js → monitor-DEJixKQO.js} +1 -1
- package/lib/public/assets/{mouse-pointer-2-CXdKMXQ8.js → mouse-pointer-2-CtOrk4M8.js} +1 -1
- package/lib/public/assets/{network-Cj8JOKON.js → network-Dii9qdvk.js} +1 -1
- package/lib/public/assets/{overview-CTg7F0im.js → overview-rolAACrO.js} +1 -1
- package/lib/public/assets/{page-header--aTyJXq6.js → page-header-COQZxJfR.js} +1 -1
- package/lib/public/assets/{play-B-FodgHV.js → play-DrN1Se_R.js} +1 -1
- package/lib/public/assets/{plus-CsIk10Cq.js → plus-5aq-9uRx.js} +1 -1
- package/lib/public/assets/{profile-page-DkFKkMfl.js → profile-page-6i5kY6kF.js} +1 -1
- package/lib/public/assets/{reset-password-I2-zqLlI.js → reset-password-sp2IBrc7.js} +1 -1
- package/lib/public/assets/runbook-page-DJDjeopU.js +253 -0
- package/lib/public/assets/{select-DvCSv25z.js → select-Bpyn7rWO.js} +1 -1
- package/lib/public/assets/{selector-detail-redirect-BLVfMJs_.js → selector-detail-redirect-BH74vmKv.js} +1 -1
- package/lib/public/assets/{selector-health-page-oqadRokn.js → selector-health-page-Cw_g218H.js} +2 -2
- package/lib/public/assets/session-detail-page-AZJE6XXE.js +4 -0
- package/lib/public/assets/settings-C3uyGhyc.js +6 -0
- package/lib/public/assets/{settings-Dep_3kbA.css → settings-K8UHWMdI.css} +1 -1
- package/lib/public/assets/{stat-tile-DV9Ix-4O.js → stat-tile-xVqAKIlI.js} +1 -1
- package/lib/public/assets/{tablet-6b_SEN2X.js → tablet-BxUScAeO.js} +1 -1
- package/lib/public/assets/{teams-BjZyfuHh.js → teams-B5ljGkY2.js} +1 -1
- package/lib/public/assets/{trash-2-CTZpPSdd.js → trash-2-B701i8sy.js} +1 -1
- package/lib/public/assets/{upload-P3XzwQUb.js → upload-C2gr44RS.js} +1 -1
- package/lib/public/assets/{use-builds-data-1cOCMf-z.js → use-builds-data-NeQOFEoy.js} +1 -1
- package/lib/public/assets/{use-password-reset-mode-DgF9w088.js → use-password-reset-mode-DEzJq5ef.js} +1 -1
- package/lib/public/assets/{users-DMF9Mk5H.js → users-D_vv_yz4.js} +1 -1
- package/lib/public/assets/{users-CiNTKUiz.js → users-MY6eNe6s.js} +1 -1
- package/lib/public/assets/{video-off-CHp1KU_T.js → video-off-OK1lNZQn.js} +1 -1
- package/lib/public/assets/webhook-settings-BU-C9lBf.js +1 -0
- package/lib/public/assets/{zap-DRz0tDq5.js → zap-DO9ry1iz.js} +1 -1
- package/lib/public/index.html +1 -1
- package/lib/schema.json +23 -25
- package/lib/src/XenonCapabilityManager.js +22 -5
- package/lib/src/app/dashboardPluginLink.js +117 -0
- package/lib/src/app/index.js +13 -34
- package/lib/src/app/openapi/control.yaml +73 -20
- package/lib/src/app/openapi/grid.yaml +31 -7
- package/lib/src/app/openapi/identity.yaml +103 -31
- package/lib/src/app/openapi/platform.yaml +165 -39
- package/lib/src/app/openapi/sessions.yaml +10 -3
- package/lib/src/app/routers/apikeys.js +14 -2
- package/lib/src/app/routers/auth.js +29 -5
- package/lib/src/app/routers/control.js +20 -2
- package/lib/src/app/routers/dashboard.js +69 -26
- package/lib/src/app/routers/grid.js +37 -13
- package/lib/src/app/routers/profile.js +21 -0
- package/lib/src/app/routers/streamType.js +7 -2
- package/lib/src/app/routers/users.js +10 -5
- package/lib/src/app/routers/webhook.js +8 -4
- package/lib/src/app/swagger.js +2 -0
- package/lib/src/app/ws/h264StreamWs.js +10 -1
- package/lib/src/config.js +51 -5
- package/lib/src/dashboard/commands.js +185 -141
- package/lib/src/dashboard/event-manager.js +88 -67
- package/lib/src/dashboard/services/failure-analysis-service.js +136 -45
- package/lib/src/dashboard/sessionCommandSummary.js +39 -0
- 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 +76 -3
- 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 +169 -15
- package/lib/src/data-service/web-config-service.js +50 -92
- package/lib/src/device-managers/AndroidDeviceManager.js +9 -31
- package/lib/src/device-managers/android/AndroidH264StreamService.js +69 -20
- package/lib/src/device-managers/android/H264Multiplexer.js +38 -4
- 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 +114 -40
- package/lib/src/gateway/defaultGateway.js +1 -1
- package/lib/src/gateway/forwardToNode.js +2 -0
- package/lib/src/gateway/healReport.js +87 -0
- package/lib/src/gateway/sessionGateway.js +12 -2
- package/lib/src/generated/client/edge.js +18 -3
- package/lib/src/generated/client/index-browser.js +15 -0
- package/lib/src/generated/client/index.d.ts +2086 -379
- package/lib/src/generated/client/index.js +18 -3
- package/lib/src/generated/client/package.json +1 -1
- package/lib/src/generated/client/schema.prisma +38 -7
- package/lib/src/generated/client/wasm.js +15 -0
- package/lib/src/interceptors/CommandInterceptor.js +271 -188
- package/lib/src/interceptors/xenonScripts.js +55 -0
- package/lib/src/interfaces/IPluginArgs.js +1 -2
- package/lib/src/middleware/authMiddleware.js +15 -3
- package/lib/src/middleware/commandCaller.js +3 -3
- package/lib/src/middleware/verifyCredential.js +58 -1
- package/lib/src/plugin.js +23 -4
- package/lib/src/prisma.js +1 -1
- package/lib/src/scripts/database-check.js +124 -0
- package/lib/src/services/AICommandService.js +9 -2
- package/lib/src/services/AIService.js +281 -32
- package/lib/src/services/CleanupService.js +6 -1
- package/lib/src/services/EmailService.js +29 -0
- 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 +7 -0
- package/lib/src/services/PortAllocator.js +5 -0
- package/lib/src/services/SelectorVerificationJob.js +15 -4
- package/lib/src/services/ServerManager.js +105 -9
- package/lib/src/services/SessionLifecycleService.js +133 -75
- package/lib/src/services/ShutdownCoordinator.js +33 -0
- package/lib/src/services/SocketServer.js +19 -3
- package/lib/src/services/TeamService.js +20 -3
- package/lib/src/services/bug-report/video-slice.js +4 -1
- package/lib/src/services/device-access/leaseOverride.js +7 -5
- package/lib/src/services/healing/FuzzyXmlHealingProvider.js +51 -0
- package/lib/src/services/healing/HealEtalonService.js +3 -2
- package/lib/src/services/healing/HealingOrchestrator.js +85 -71
- package/lib/src/services/healing/OcrHealingProvider.js +41 -64
- package/lib/src/services/healing/ResilioTreeHealingProvider.js +41 -50
- package/lib/src/services/healing/VisualAiHealingProvider.js +1 -1
- package/lib/src/services/healing/fingerprintIdentity.js +126 -0
- package/lib/src/services/healing/resilioPath.js +274 -0
- package/lib/src/services/interceptor/AndroidProxyAdapter.js +18 -0
- package/lib/src/services/interceptor/interceptorOptions.js +44 -0
- package/lib/src/services/logcat/SessionDeviceLogs.js +271 -0
- package/lib/src/services/logcat/deviceLogBook.js +163 -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/ocr/ocrData.js +118 -0
- package/lib/src/services/ocr/vendor/LICENSE-tessdata.txt +202 -0
- package/lib/src/services/ocr/vendor/eng.traineddata.gz +0 -0
- package/lib/src/services/omni-vision/OmniVisionService.js +132 -95
- 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/passwordResetLink.js +50 -9
- package/lib/src/services/passwordResetMode.js +3 -1
- package/lib/src/services/publicUrl.js +45 -0
- package/lib/src/services/recording/concurrency-gate.js +12 -3
- package/lib/src/services/session/sessionIdentity.js +5 -3
- package/lib/src/services/session/shutdownReason.js +26 -0
- package/lib/src/services/settings/SelfHealingSwitch.js +70 -0
- package/lib/src/services/settings/aiEngineSettings.js +219 -0
- package/lib/src/services/settings/labSettings.js +149 -0
- package/lib/src/services/token/mintedLifetime.js +51 -0
- package/lib/src/services/webhookEvents.js +173 -0
- package/lib/src/sessions/SessionManager.js +8 -2
- package/lib/src/sessions/sessionMemory.js +25 -0
- package/lib/test/helpers/fake-adb.js +58 -0
- package/lib/test/helpers/fake-ai-provider.js +188 -0
- package/lib/test/helpers/fake-device-table.js +63 -24
- package/lib/test/helpers/loki-stores.js +33 -0
- package/lib/test/helpers/page-sources.js +164 -0
- package/lib/test/helpers/scratch-database.js +58 -13
- package/lib/test/integration/audit-ingest.spec.js +10 -10
- package/lib/test/integration/auth-flow.spec.js +9 -14
- package/lib/test/integration/auth-rate-limit.spec.js +6 -3
- package/lib/test/integration/bug-report-route.spec.js +22 -2
- package/lib/test/integration/forgot-password-rate-limit.spec.js +6 -3
- package/lib/test/integration/forgot-password.spec.js +17 -18
- package/lib/test/integration/legacy-deprecation-headers.spec.js +5 -2
- package/lib/test/integration/migration.spec.js +6 -1
- package/lib/test/integration/node-pair-auth-rest.spec.js +5 -9
- package/lib/test/integration/ports-router.spec.js +4 -4
- package/lib/test/integration/prisma-device-store-add-devices.spec.js +2 -3
- package/lib/test/integration/prisma-device-store-team-filter.spec.js +2 -5
- package/lib/test/integration/profile-tokens.spec.js +10 -16
- package/lib/test/integration/recordings-router.spec.js +16 -16
- package/lib/test/integration/reset-revokes-sessions.spec.js +4 -9
- package/lib/test/integration/role-matrix.spec.js +4 -9
- package/lib/test/integration/sdk-leases-router.spec.js +9 -9
- package/lib/test/integration/sdk-version.spec.js +2 -2
- package/lib/test/integration/selector-health-endpoint.spec.js +5 -5
- package/lib/test/integration/team-membership-migration.spec.js +73 -12
- package/lib/test/integration/team-visibility-apps.spec.js +15 -22
- package/lib/test/integration/team-visibility-control.spec.js +96 -69
- package/lib/test/integration/team-visibility-dashboard.spec.js +7 -23
- package/lib/test/integration/team-visibility-grid.spec.js +16 -15
- package/lib/test/integration/team-visibility-leases.spec.js +8 -11
- package/lib/test/integration/team-visibility-recordings.spec.js +6 -19
- package/lib/test/integration/team-visibility-sessions.spec.js +6 -19
- package/lib/test/integration/users-flow.spec.js +7 -12
- package/lib/test/integration/users-lockout.spec.js +5 -3
- package/lib/test/unit/EmailService.test.js +66 -0
- package/lib/test/unit/ResilioTreeHealingProvider.test.js +46 -19
- package/lib/test/unit/TeamService.test.js +3 -0
- package/lib/test/unit/XenonCapabilityManager.spec.js +4 -2
- package/lib/test/unit/ai-call-time-limit.spec.js +130 -0
- package/lib/test/unit/ai-engine-settings.spec.js +338 -0
- package/lib/test/unit/ai-provider-switch.spec.js +130 -0
- package/lib/test/unit/ai-rate-limit.spec.js +126 -0
- package/lib/test/unit/android-h264-viewers.spec.js +272 -0
- package/lib/test/unit/android-preview-hold.spec.js +3 -2
- package/lib/test/unit/apikeys-router.spec.js +84 -0
- package/lib/test/unit/apple-family.spec.js +36 -0
- package/lib/test/unit/authMiddleware.bearer.spec.js +9 -0
- package/lib/test/unit/authMiddleware.credentialLifetime.spec.js +149 -0
- package/lib/test/unit/bug-report/video-slice.spec.js +19 -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-device-logs.spec.js +207 -0
- package/lib/test/unit/dashboard-events-team-scope.spec.js +9 -6
- package/lib/test/unit/dashboard-plugin-link.spec.js +213 -0
- 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-log-book.spec.js +180 -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/enable-dashboard-option.spec.js +57 -0
- package/lib/test/unit/event-log-session-data.spec.js +244 -0
- package/lib/test/unit/failure-analysis.spec.js +182 -0
- package/lib/test/unit/failure-categories.spec.js +128 -0
- package/lib/test/unit/fingerprint-identity.spec.js +433 -0
- package/lib/test/unit/h264-multiplexer.spec.js +46 -0
- package/lib/test/unit/heal-report.spec.js +71 -0
- package/lib/test/unit/healing-instrumentation.spec.js +6 -1
- package/lib/test/unit/healing-tier-gate.spec.js +73 -29
- package/lib/test/unit/healing-tiers-every-session.spec.js +219 -0
- package/lib/test/unit/hub-node-gateway-appium-server.spec.js +219 -0
- package/lib/test/unit/hub-node-same-host.spec.js +5 -0
- package/lib/test/unit/interceptor-visual-elements.spec.js +192 -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/lease/lease-override.spec.js +23 -6
- package/lib/test/unit/lease/session-lease-proof.spec.js +16 -4
- package/lib/test/unit/max-sessions.spec.js +240 -0
- package/lib/test/unit/ocr-healing-tier-real-ocr.spec.js +113 -0
- package/lib/test/unit/ocr-healing-tier.spec.js +122 -0
- package/lib/test/unit/ocr-offline.spec.js +213 -0
- package/lib/test/unit/ocr-text-match.spec.js +44 -0
- package/lib/test/unit/omni-virtual-element-store.spec.js +301 -0
- package/lib/test/unit/omni-vision-analyze-screen.spec.js +72 -0
- package/lib/test/unit/omni-vision-ios-points.spec.js +159 -0
- package/lib/test/unit/omni-vision-tap-text.spec.js +129 -0
- package/lib/test/unit/omni-vision.spec.js +6 -8
- package/lib/test/unit/package-engines.spec.js +27 -0
- package/lib/test/unit/partial-config-file.spec.js +14 -0
- package/lib/test/unit/passwordResetMode.test.js +10 -2
- 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/profile-router.test.js +122 -1
- package/lib/test/unit/queue-requester.spec.js +1 -1
- package/lib/test/unit/recording-options.spec.js +181 -0
- package/lib/test/unit/reset-link-address.spec.js +266 -0
- package/lib/test/unit/reset-link.test.js +10 -4
- package/lib/test/unit/resilio-path-healing.spec.js +270 -0
- package/lib/test/unit/selector-learning-every-session.spec.js +179 -0
- package/lib/test/unit/selector-verification-clean-build.spec.js +130 -0
- package/lib/test/unit/selector-verification-job.spec.js +13 -13
- package/lib/test/unit/self-healing-boot.spec.js +80 -0
- package/lib/test/unit/self-healing-setting.spec.js +273 -0
- package/lib/test/unit/session-app-resolution.spec.js +5 -1
- package/lib/test/unit/session-attribution-wiring.spec.js +41 -13
- package/lib/test/unit/session-create-credential-owner.spec.js +242 -0
- package/lib/test/unit/session-credential-strip.spec.js +1 -1
- package/lib/test/unit/session-device-logs.spec.js +347 -0
- package/lib/test/unit/session-end-failure-analysis.spec.js +133 -0
- package/lib/test/unit/session-failed-webhook.spec.js +170 -0
- package/lib/test/unit/session-log-indexes.spec.js +64 -3
- package/lib/test/unit/shell-commands.spec.js +133 -0
- package/lib/test/unit/socketServer.scopedEmit.spec.js +2 -0
- package/lib/test/unit/socketServer.sessionCookie.spec.js +2 -1
- package/lib/test/unit/stream-start-pipelines.spec.js +264 -0
- package/lib/test/unit/stream-status-type.spec.js +4 -0
- package/lib/test/unit/token-mint-limits.spec.js +165 -0
- package/lib/test/unit/unexpected-shutdown-reason.spec.js +163 -0
- package/lib/test/unit/verify-credential.spec.js +33 -0
- package/lib/test/unit/vision-assertion.spec.js +119 -0
- package/lib/test/unit/wda-client-shell.spec.js +133 -0
- package/lib/test/unit/web-config-service.spec.js +98 -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 +5 -5
- package/prisma/migrations/20261004140000_keep_device_settings/migration.sql +19 -0
- package/prisma/migrations/20261004174413_locator_etalon_path/migration.sql +2 -0
- package/prisma/schema.prisma +38 -7
- package/schema.json +23 -25
- package/scripts/generate-types-from-schema.js +1 -2
- package/scripts/lib/always-present-plugin-args.js +4 -1
- package/lib/public/assets/BugReportButton-BoGhlI7F.js +0 -15
- package/lib/public/assets/DeviceMosaicView-8J0oYqMM.js +0 -2
- package/lib/public/assets/ai-settings-Bce5SJob.js +0 -21
- package/lib/public/assets/device-explorer-C-KnhMHI.js +0 -279
- package/lib/public/assets/forgot-password-DnudqFFo.js +0 -1
- 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/runbook-page-DJTVCJX7.js +0 -124
- 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
- /package/lib/public/assets/{BugReportButton-CPBjfiv4.css → index-CPBjfiv4.css} +0 -0
|
@@ -0,0 +1,126 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* What a stored fingerprint (`LocatorSignature.attributes`) says about which
|
|
4
|
+
* element it is, as opposed to where the element was (x, y, width, height).
|
|
5
|
+
*
|
|
6
|
+
* Through 2.14 every learnt fingerprint held only the rect and the type: the
|
|
7
|
+
* attribute reads failed (CommandInterceptor.triggerLearning). Fuzzy XML, which
|
|
8
|
+
* weighs position above everything else, then healed a selector to whatever
|
|
9
|
+
* element of that type sat at that spot. A fingerprint with no identity isn't
|
|
10
|
+
* used for position, and is learnt again.
|
|
11
|
+
*/
|
|
12
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
13
|
+
exports.FINGERPRINT_IDENTITY_ATTRIBUTES = void 0;
|
|
14
|
+
exports.meaningful = meaningful;
|
|
15
|
+
exports.hasIdentity = hasIdentity;
|
|
16
|
+
exports.attributesToLearn = attributesToLearn;
|
|
17
|
+
exports.resourceIdName = resourceIdName;
|
|
18
|
+
exports.stableIdentifierOf = stableIdentifierOf;
|
|
19
|
+
exports.sameIdentifier = sameIdentifier;
|
|
20
|
+
exports.sameText = sameText;
|
|
21
|
+
/**
|
|
22
|
+
* The attributes that say which element a fingerprint is. Not `value`: on a
|
|
23
|
+
* text field it is what the test typed, which fingerprints no longer keep.
|
|
24
|
+
*/
|
|
25
|
+
exports.FINGERPRINT_IDENTITY_ATTRIBUTES = [
|
|
26
|
+
'resource-id',
|
|
27
|
+
'content-desc',
|
|
28
|
+
'accessibility-id',
|
|
29
|
+
'text',
|
|
30
|
+
'name',
|
|
31
|
+
'label',
|
|
32
|
+
'id',
|
|
33
|
+
'hint',
|
|
34
|
+
];
|
|
35
|
+
/**
|
|
36
|
+
* A value that says something: a string with more than spaces in it, and not
|
|
37
|
+
* "null", which is what UiAutomator2 answers for an attribute the element
|
|
38
|
+
* hasn't set (its getAttribute is String(value)).
|
|
39
|
+
*/
|
|
40
|
+
function meaningful(value) {
|
|
41
|
+
return typeof value === 'string' && value.trim() !== '' && value.trim() !== 'null';
|
|
42
|
+
}
|
|
43
|
+
/** Whether a fingerprint says anything about which element it is. */
|
|
44
|
+
function hasIdentity(attributes) {
|
|
45
|
+
return exports.FINGERPRINT_IDENTITY_ATTRIBUTES.some((name) => meaningful(attributes === null || attributes === void 0 ? void 0 : attributes[name]));
|
|
46
|
+
}
|
|
47
|
+
/**
|
|
48
|
+
* The attributes learning asks a driver for: those it has. UiAutomator2
|
|
49
|
+
* throws for `id` and `label`, and its `name` is the text again; WebDriverAgent
|
|
50
|
+
* throws for anything but its own, so five of the old list were five failed
|
|
51
|
+
* round trips per selector learnt.
|
|
52
|
+
*/
|
|
53
|
+
function attributesToLearn(platformName) {
|
|
54
|
+
const platform = String(platformName !== null && platformName !== void 0 ? platformName : '').toLowerCase();
|
|
55
|
+
if (platform === 'android')
|
|
56
|
+
return ['resource-id', 'content-desc', 'text', 'hint'];
|
|
57
|
+
if (platform === 'ios' || platform === 'tvos')
|
|
58
|
+
return ['name', 'label'];
|
|
59
|
+
return ['resource-id', 'content-desc', 'text', 'hint', 'name', 'label'];
|
|
60
|
+
}
|
|
61
|
+
/**
|
|
62
|
+
* An Android resource-id without its package: `com.shop:id/pay` is `pay`.
|
|
63
|
+
* Fuzzy XML's veto asks whether two ids are the same this way: ids of one
|
|
64
|
+
* app share the package, and of a debug build another one. Its score still
|
|
65
|
+
* compares whole ids, where the shared package earns a share of the weight.
|
|
66
|
+
*/
|
|
67
|
+
function resourceIdName(resourceId) {
|
|
68
|
+
const at = resourceId.lastIndexOf(':id/');
|
|
69
|
+
if (at >= 0)
|
|
70
|
+
return resourceId.slice(at + 4);
|
|
71
|
+
return resourceId.slice(resourceId.lastIndexOf('/') + 1);
|
|
72
|
+
}
|
|
73
|
+
/**
|
|
74
|
+
* The identifier a developer gave the element, which stays when its text
|
|
75
|
+
* changes: an Android resource-id, or an iOS accessibility identifier (`name`
|
|
76
|
+
* when it isn't just the label; an element without one has its label as name).
|
|
77
|
+
*/
|
|
78
|
+
function stableIdentifierOf(attributes) {
|
|
79
|
+
const resourceId = attributes === null || attributes === void 0 ? void 0 : attributes['resource-id'];
|
|
80
|
+
if (meaningful(resourceId))
|
|
81
|
+
return { attribute: 'resource-id', value: resourceId };
|
|
82
|
+
const name = attributes === null || attributes === void 0 ? void 0 : attributes.name;
|
|
83
|
+
if (meaningful(name) && name !== (attributes === null || attributes === void 0 ? void 0 : attributes.label))
|
|
84
|
+
return { attribute: 'name', value: name };
|
|
85
|
+
return null;
|
|
86
|
+
}
|
|
87
|
+
// Built at run time: the compiler's target predates Unicode property escapes
|
|
88
|
+
// in regular expression literals. Letters, their combining marks and digits,
|
|
89
|
+
// in any script.
|
|
90
|
+
const NOT_A_WORD = new RegExp('[^\\p{L}\\p{M}\\p{N}]+', 'gu');
|
|
91
|
+
const CAMEL_TURN = new RegExp('([\\p{Ll}\\p{N}])(\\p{Lu})', 'gu');
|
|
92
|
+
/** An identifier's words: split where camelCase turns and at anything but a letter or digit. */
|
|
93
|
+
function identifierWords(identifier) {
|
|
94
|
+
return identifier
|
|
95
|
+
.normalize('NFC')
|
|
96
|
+
.replace(CAMEL_TURN, '$1 $2')
|
|
97
|
+
.toLowerCase()
|
|
98
|
+
.split(NOT_A_WORD)
|
|
99
|
+
.filter(Boolean);
|
|
100
|
+
}
|
|
101
|
+
/**
|
|
102
|
+
* Whether two identifiers name the same element: they have the same words,
|
|
103
|
+
* however written (btn-pay, pay_btn, payBtn). Any other pair names another
|
|
104
|
+
* element, and the text decides: an id that gains words is as often another
|
|
105
|
+
* element (pay_later, undo_delete) as a rename (checkout_pay), and letters
|
|
106
|
+
* alone can't tell follow from unfollow, or checkout.cancel from
|
|
107
|
+
* checkout.pay (64% of their letter pairs are shared). Two with no letter or
|
|
108
|
+
* digit between them can't be told apart, and count as the same.
|
|
109
|
+
*/
|
|
110
|
+
function sameIdentifier(a, b) {
|
|
111
|
+
const [wordsA, wordsB] = [new Set(identifierWords(a)), new Set(identifierWords(b))];
|
|
112
|
+
if (wordsA.size !== wordsB.size)
|
|
113
|
+
return false;
|
|
114
|
+
return [...wordsA].every((w) => wordsB.has(w));
|
|
115
|
+
}
|
|
116
|
+
/**
|
|
117
|
+
* Whether two texts read the same: case, punctuation and spacing aside, with
|
|
118
|
+
* accents composed (NFC). Combining marks count: Hindi दें and दो differ only
|
|
119
|
+
* by theirs. Nothing looser: "Log in" and "Log out", "Enable" and "Disable"
|
|
120
|
+
* are as alike by their letters as a reworded label is.
|
|
121
|
+
*/
|
|
122
|
+
function sameText(a, b) {
|
|
123
|
+
const read = (t) => t.normalize('NFC').toLowerCase().replace(NOT_A_WORD, ' ').trim();
|
|
124
|
+
const [textA, textB] = [read(a), read(b)];
|
|
125
|
+
return textA !== '' && textA === textB;
|
|
126
|
+
}
|
|
@@ -0,0 +1,274 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __rest = (this && this.__rest) || function (s, e) {
|
|
3
|
+
var t = {};
|
|
4
|
+
for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p) && e.indexOf(p) < 0)
|
|
5
|
+
t[p] = s[p];
|
|
6
|
+
if (s != null && typeof Object.getOwnPropertySymbols === "function")
|
|
7
|
+
for (var i = 0, p = Object.getOwnPropertySymbols(s); i < p.length; i++) {
|
|
8
|
+
if (e.indexOf(p[i]) < 0 && Object.prototype.propertyIsEnumerable.call(s, p[i]))
|
|
9
|
+
t[p[i]] = s[p[i]];
|
|
10
|
+
}
|
|
11
|
+
return t;
|
|
12
|
+
};
|
|
13
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
14
|
+
exports.MIN_LEAD = exports.MIN_SCORE = exports.RESILIO_PATH_FORMAT = void 0;
|
|
15
|
+
exports.resilioPathOf = resilioPathOf;
|
|
16
|
+
exports.isResilioPath = isResilioPath;
|
|
17
|
+
exports.findLearntElement = findLearntElement;
|
|
18
|
+
exports.nearestElement = nearestElement;
|
|
19
|
+
exports.locatorsSelectingOnly = locatorsSelectingOnly;
|
|
20
|
+
exports.contradictsSelector = contradictsSelector;
|
|
21
|
+
const xmldom_1 = require("@xmldom/xmldom");
|
|
22
|
+
const resiliotree_1 = require("resiliotree");
|
|
23
|
+
const xpath_1 = require("xpath");
|
|
24
|
+
/**
|
|
25
|
+
* The Resilio healing tier's view of a page source: an element's path from
|
|
26
|
+
* the root of the driver's element tree, as resiliotree compares paths.
|
|
27
|
+
*
|
|
28
|
+
* The page source is parsed as the XML it is. Through 2.14 it went through
|
|
29
|
+
* resiliotree's HTML parser, which lowercases every tag
|
|
30
|
+
* (`android.widget.button`, which the drivers' XPath doesn't match) and nests
|
|
31
|
+
* every element written `<x/>` around its next siblings.
|
|
32
|
+
*
|
|
33
|
+
* A path is stored with the selector's fingerprint (`LocatorEtalon.path`).
|
|
34
|
+
* Each node keeps what resiliotree compares (tag, id, labels, index, other
|
|
35
|
+
* attributes), not its children or text, so a path is a few kilobytes.
|
|
36
|
+
*/
|
|
37
|
+
/** Marks a path made here. A path without it (an HTML parse, before 2.15) is not used. */
|
|
38
|
+
exports.RESILIO_PATH_FORMAT = 'page-source-1';
|
|
39
|
+
/**
|
|
40
|
+
* How sure Resilio must be. An element that kept its id scores about 0.99
|
|
41
|
+
* after moving anywhere in the tree; once its id changed, or it is gone, the
|
|
42
|
+
* best element scores about 0.55, and a neighbour of the same type comes
|
|
43
|
+
* within a few hundredths of it. Below this, or with a second element as
|
|
44
|
+
* close as MIN_LEAD, the next tier decides.
|
|
45
|
+
*/
|
|
46
|
+
exports.MIN_SCORE = 0.8;
|
|
47
|
+
exports.MIN_LEAD = 0.05;
|
|
48
|
+
function platformOf(pageSource) {
|
|
49
|
+
if (/<XCUIElementType/.test(pageSource))
|
|
50
|
+
return 'ios';
|
|
51
|
+
if (/<hierarchy[\s>]/.test(pageSource))
|
|
52
|
+
return 'android';
|
|
53
|
+
return 'other';
|
|
54
|
+
}
|
|
55
|
+
const isElement = (n) => !!n && n.nodeType === 1;
|
|
56
|
+
function childElements(el) {
|
|
57
|
+
const out = [];
|
|
58
|
+
for (let c = el.firstChild; c; c = c.nextSibling)
|
|
59
|
+
if (isElement(c))
|
|
60
|
+
out.push(c);
|
|
61
|
+
return out;
|
|
62
|
+
}
|
|
63
|
+
/**
|
|
64
|
+
* One element as a resiliotree node, without children. The platform decides
|
|
65
|
+
* what its id is: Android's resource-id, iOS's name, else `id`.
|
|
66
|
+
*/
|
|
67
|
+
function nodeBuilderFor(el, index, platform) {
|
|
68
|
+
const attributes = new Map();
|
|
69
|
+
for (let i = 0; i < el.attributes.length; i++) {
|
|
70
|
+
attributes.set(el.attributes[i].name, el.attributes[i].value);
|
|
71
|
+
}
|
|
72
|
+
const builder = new resiliotree_1.NodeBuilder().setTag(el.tagName).setIndex(index);
|
|
73
|
+
if (platform === 'android')
|
|
74
|
+
builder.setAndroidAttributes(attributes);
|
|
75
|
+
else if (platform === 'ios')
|
|
76
|
+
builder.setIOSAttributes(attributes);
|
|
77
|
+
else
|
|
78
|
+
builder.setAttributes(attributes);
|
|
79
|
+
return builder;
|
|
80
|
+
}
|
|
81
|
+
function parse(pageSource) {
|
|
82
|
+
return new xmldom_1.DOMParser({
|
|
83
|
+
errorHandler: { warning: () => { }, error: () => { }, fatalError: () => { } },
|
|
84
|
+
}).parseFromString(pageSource, 'text/xml');
|
|
85
|
+
}
|
|
86
|
+
/** The page source as a resiliotree tree, each node knowing its element. */
|
|
87
|
+
function treeOf(pageSource) {
|
|
88
|
+
const doc = parse(pageSource);
|
|
89
|
+
const top = doc === null || doc === void 0 ? void 0 : doc.documentElement;
|
|
90
|
+
if (!isElement(top))
|
|
91
|
+
return null;
|
|
92
|
+
const platform = platformOf(pageSource);
|
|
93
|
+
const elementOf = new Map();
|
|
94
|
+
const build = (el, index) => {
|
|
95
|
+
const builder = nodeBuilderFor(el, index, platform);
|
|
96
|
+
childElements(el).forEach((child, i) => builder.addChild(build(child, i)));
|
|
97
|
+
const node = builder.build();
|
|
98
|
+
elementOf.set(node, el);
|
|
99
|
+
return node;
|
|
100
|
+
};
|
|
101
|
+
return { root: build(top, 0), elementOf };
|
|
102
|
+
}
|
|
103
|
+
function compact(node) {
|
|
104
|
+
const _a = node.toJSON(), { children: _children, innerText: _text } = _a, rest = __rest(_a, ["children", "innerText"]);
|
|
105
|
+
return rest;
|
|
106
|
+
}
|
|
107
|
+
/**
|
|
108
|
+
* The path of an element of a parsed page source (an xmldom Element, as the
|
|
109
|
+
* Fuzzy XML and Resilio tiers find them), from the root of the page source.
|
|
110
|
+
* `pageSource` decides the platform. Null for anything that isn't such an
|
|
111
|
+
* element.
|
|
112
|
+
*/
|
|
113
|
+
function resilioPathOf(element, pageSource) {
|
|
114
|
+
if (!isElement(element))
|
|
115
|
+
return null;
|
|
116
|
+
const platform = platformOf(pageSource);
|
|
117
|
+
const lineage = [];
|
|
118
|
+
for (let el = element; isElement(el); el = el.parentNode)
|
|
119
|
+
lineage.unshift(el);
|
|
120
|
+
const nodes = lineage.map((el) => {
|
|
121
|
+
const parent = el.parentNode;
|
|
122
|
+
const index = isElement(parent) ? childElements(parent).indexOf(el) : 0;
|
|
123
|
+
return compact(nodeBuilderFor(el, index, platform).build());
|
|
124
|
+
});
|
|
125
|
+
return { format: exports.RESILIO_PATH_FORMAT, nodes };
|
|
126
|
+
}
|
|
127
|
+
/** Whether `path` is one made here (not an older HTML parse). */
|
|
128
|
+
function isResilioPath(path) {
|
|
129
|
+
return (!!path &&
|
|
130
|
+
typeof path === 'object' &&
|
|
131
|
+
path.format === exports.RESILIO_PATH_FORMAT &&
|
|
132
|
+
Array.isArray(path.nodes) &&
|
|
133
|
+
path.nodes.length > 0);
|
|
134
|
+
}
|
|
135
|
+
/** Attributes that say which element an element is, not where it is or what state it is in. */
|
|
136
|
+
const IDENTITY_ATTRIBUTES = new Set([
|
|
137
|
+
'resource-id',
|
|
138
|
+
'content-desc',
|
|
139
|
+
'text',
|
|
140
|
+
'name',
|
|
141
|
+
'label',
|
|
142
|
+
'value',
|
|
143
|
+
'id',
|
|
144
|
+
'hint',
|
|
145
|
+
'accessibility-id',
|
|
146
|
+
]);
|
|
147
|
+
/**
|
|
148
|
+
* The element of the page source that the driver found, from its tag and the
|
|
149
|
+
* attributes Xenon read from it (`name`/`value` pairs: resource-id, text,
|
|
150
|
+
* name, label, ..., and x, y, width, height). The element of that tag with
|
|
151
|
+
* the most of them, among those that share at least one identity attribute or
|
|
152
|
+
* the whole rect with it: the page source is read after the find answered,
|
|
153
|
+
* so the screen may have changed, and a size alone names nothing. Null when
|
|
154
|
+
* none qualifies, or two qualify equally well.
|
|
155
|
+
*/
|
|
156
|
+
function findLearntElement(pageSource, nodeName, attributes) {
|
|
157
|
+
const doc = parse(pageSource);
|
|
158
|
+
if (!isElement(doc === null || doc === void 0 ? void 0 : doc.documentElement))
|
|
159
|
+
return null;
|
|
160
|
+
const tag = (nodeName || '').toLowerCase();
|
|
161
|
+
const anyTag = !tag || tag === 'xcuielementtypeany' || tag === 'unknown';
|
|
162
|
+
const wanted = new Map(attributes.map((a) => [a.name.toLowerCase(), String(a.value)]));
|
|
163
|
+
const rect = ['x', 'y', 'width', 'height'].map((k) => wanted.get(k));
|
|
164
|
+
const hasRect = rect.every((v) => v !== undefined);
|
|
165
|
+
/** How many of the attributes `el` shares, or 0 when it shares no identity and not the rect. */
|
|
166
|
+
const matchesOf = (el) => {
|
|
167
|
+
let all = 0;
|
|
168
|
+
let identity = 0;
|
|
169
|
+
for (const [name, value] of wanted) {
|
|
170
|
+
if (el.getAttribute(name) !== value)
|
|
171
|
+
continue;
|
|
172
|
+
all++;
|
|
173
|
+
if (IDENTITY_ATTRIBUTES.has(name))
|
|
174
|
+
identity++;
|
|
175
|
+
}
|
|
176
|
+
// Android writes a rect as bounds="[x1,y1][x2,y2]"; iOS as x, y, width, height.
|
|
177
|
+
let wholeRect = hasRect && ['x', 'y', 'width', 'height'].every((k, i) => el.getAttribute(k) === rect[i]);
|
|
178
|
+
const bounds = el.getAttribute('bounds');
|
|
179
|
+
if (bounds && hasRect) {
|
|
180
|
+
const [x, y, w, h] = rect.map(Number);
|
|
181
|
+
if (bounds === `[${x},${y}][${x + w},${y + h}]`) {
|
|
182
|
+
all++;
|
|
183
|
+
wholeRect = true;
|
|
184
|
+
}
|
|
185
|
+
}
|
|
186
|
+
return identity > 0 || wholeRect ? all : 0;
|
|
187
|
+
};
|
|
188
|
+
let best = null;
|
|
189
|
+
let bestCount = 0;
|
|
190
|
+
let tie = false;
|
|
191
|
+
const walk = (el) => {
|
|
192
|
+
if (anyTag || el.tagName.toLowerCase() === tag) {
|
|
193
|
+
const n = matchesOf(el);
|
|
194
|
+
if (n > bestCount) {
|
|
195
|
+
best = el;
|
|
196
|
+
bestCount = n;
|
|
197
|
+
tie = false;
|
|
198
|
+
}
|
|
199
|
+
else if (n > 0 && n === bestCount) {
|
|
200
|
+
tie = true;
|
|
201
|
+
}
|
|
202
|
+
}
|
|
203
|
+
childElements(el).forEach(walk);
|
|
204
|
+
};
|
|
205
|
+
walk(doc.documentElement);
|
|
206
|
+
return tie ? null : best;
|
|
207
|
+
}
|
|
208
|
+
/**
|
|
209
|
+
* The element of `pageSource` at the end of the path most like `path`, and
|
|
210
|
+
* how alike they are (0 to 1). Null when no element scores MIN_SCORE, or
|
|
211
|
+
* another comes within MIN_LEAD of the best: Resilio then has no answer.
|
|
212
|
+
*/
|
|
213
|
+
function nearestElement(path, pageSource) {
|
|
214
|
+
const tree = treeOf(pageSource);
|
|
215
|
+
if (!tree)
|
|
216
|
+
return null;
|
|
217
|
+
const finder = new resiliotree_1.PathFinder(new resiliotree_1.LCSPathDistance(), new resiliotree_1.HeuristicNodeDistance());
|
|
218
|
+
// resiliotree scores an element once for each leaf below it, so an element
|
|
219
|
+
// holding others (a list row) comes back several times at one score. Every
|
|
220
|
+
// result, best first, and the best of each element.
|
|
221
|
+
const scored = finder.find(resiliotree_1.Path.fromJSON(path), tree.root, Number.MAX_SAFE_INTEGER);
|
|
222
|
+
const best = scored[0];
|
|
223
|
+
if (!best || best.score < exports.MIN_SCORE)
|
|
224
|
+
return null;
|
|
225
|
+
const second = scored.find((s) => s.value !== best.value);
|
|
226
|
+
if (second && best.score - second.score < exports.MIN_LEAD)
|
|
227
|
+
return null;
|
|
228
|
+
const element = tree.elementOf.get(best.value);
|
|
229
|
+
return element ? { element, score: best.score } : null;
|
|
230
|
+
}
|
|
231
|
+
/**
|
|
232
|
+
* The locators among `candidates` that select `element` and nothing else in
|
|
233
|
+
* its page source. A driver answers a find with the first match, so a
|
|
234
|
+
* locator that also matches an element before it would hand back that one.
|
|
235
|
+
*/
|
|
236
|
+
function locatorsSelectingOnly(element, candidates) {
|
|
237
|
+
const doc = element.ownerDocument;
|
|
238
|
+
return candidates.filter((xpath) => {
|
|
239
|
+
try {
|
|
240
|
+
const found = (0, xpath_1.select)(xpath, doc);
|
|
241
|
+
return Array.isArray(found) && found.length === 1 && found[0] === element;
|
|
242
|
+
}
|
|
243
|
+
catch (_a) {
|
|
244
|
+
return false;
|
|
245
|
+
}
|
|
246
|
+
});
|
|
247
|
+
}
|
|
248
|
+
/** The text-like attributes a selector can state that the element it means must have. */
|
|
249
|
+
const STATED_ATTRIBUTES = 'text|content-desc|label|name|value';
|
|
250
|
+
/**
|
|
251
|
+
* Whether `element` has, for an attribute the XPath `selector` states (the
|
|
252
|
+
* text, description, label, name or value), something other than what it
|
|
253
|
+
* says: `@text='OK'` on an element reading "Delete". Resilio compares paths,
|
|
254
|
+
* in which a changed text weighs little, so a dialog's OK button that now
|
|
255
|
+
* reads Delete would otherwise be healed to. A stated attribute may belong to
|
|
256
|
+
* another element of the XPath (an ancestor, a sibling); Resilio then
|
|
257
|
+
* declines a heal it could have made, and the next tier decides.
|
|
258
|
+
*/
|
|
259
|
+
function contradictsSelector(element, strategy, selector) {
|
|
260
|
+
if (strategy !== 'xpath' || typeof selector !== 'string')
|
|
261
|
+
return false;
|
|
262
|
+
const valueOf = (name) => { var _a; return (_a = element.getAttribute(name)) !== null && _a !== void 0 ? _a : ''; };
|
|
263
|
+
const equals = new RegExp(`@(${STATED_ATTRIBUTES})\\s*=\\s*(['"])(.*?)\\2`, 'g');
|
|
264
|
+
for (const [, name, , value] of selector.matchAll(equals)) {
|
|
265
|
+
if (valueOf(name) !== value)
|
|
266
|
+
return true;
|
|
267
|
+
}
|
|
268
|
+
const contains = new RegExp(`contains\\(\\s*@(${STATED_ATTRIBUTES})\\s*,\\s*(['"])(.*?)\\2\\s*\\)`, 'g');
|
|
269
|
+
for (const [, name, , value] of selector.matchAll(contains)) {
|
|
270
|
+
if (!valueOf(name).includes(value))
|
|
271
|
+
return true;
|
|
272
|
+
}
|
|
273
|
+
return false;
|
|
274
|
+
}
|
|
@@ -10,6 +10,7 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, ge
|
|
|
10
10
|
};
|
|
11
11
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
12
12
|
exports.AndroidProxyAdapter = void 0;
|
|
13
|
+
const xenonProxy_1 = require("../network/xenonProxy");
|
|
13
14
|
class AndroidProxyAdapter {
|
|
14
15
|
constructor(getAdb) {
|
|
15
16
|
this.getAdb = getAdb;
|
|
@@ -38,6 +39,23 @@ class AndroidProxyAdapter {
|
|
|
38
39
|
yield adb.adbExec(['-s', udid, 'shell', 'settings', 'put', 'global', 'http_proxy', ':0']);
|
|
39
40
|
});
|
|
40
41
|
}
|
|
42
|
+
/** The phone's global HTTP proxy as stored: `host:port`, or `null` / `:0` / '' for none. */
|
|
43
|
+
readProxy(udid) {
|
|
44
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
45
|
+
const adb = yield this.getAdb(udid);
|
|
46
|
+
const out = yield adb.adbExec(['-s', udid, 'shell', 'settings', 'get', 'global', 'http_proxy']);
|
|
47
|
+
return String(out !== null && out !== void 0 ? out : '').trim();
|
|
48
|
+
});
|
|
49
|
+
}
|
|
50
|
+
/** Puts `previous` back as the phone's proxy, or clears it when there was none. */
|
|
51
|
+
restoreProxy(udid, previous) {
|
|
52
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
53
|
+
if (previous === null || (0, xenonProxy_1.isUnsetProxy)(previous))
|
|
54
|
+
return this.clearProxy(udid);
|
|
55
|
+
const adb = yield this.getAdb(udid);
|
|
56
|
+
yield adb.adbExec(['-s', udid, 'shell', 'settings', 'put', 'global', 'http_proxy', previous]);
|
|
57
|
+
});
|
|
58
|
+
}
|
|
41
59
|
// Forwards a port on the device back to a port on the host over the adb transport
|
|
42
60
|
// (USB or wireless adb). The device sees `127.0.0.1:devicePort` and that traffic
|
|
43
61
|
// reaches the host's `hostPort`. This is the standard way to make a host-side
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.resolveInterceptorOptions = resolveInterceptorOptions;
|
|
4
|
+
const XenonCapabilityManager_1 = require("../../XenonCapabilityManager");
|
|
5
|
+
/**
|
|
6
|
+
* The network capture a session gets: its own capability (any of the shapes
|
|
7
|
+
* getXenonCapabilities reads) wins field by field, and the server's
|
|
8
|
+
* `interceptor` option fills in what the session leaves unset.
|
|
9
|
+
*
|
|
10
|
+
* - `enabled`: the session's, when it says true or false; else the server's;
|
|
11
|
+
* else off.
|
|
12
|
+
* - `bufferSize`, `captureBodies`: the session's, else the server's, else the
|
|
13
|
+
* service's defaults (1000, true).
|
|
14
|
+
* - `mocks`, `includeHosts`, `excludeHosts`: the session's only.
|
|
15
|
+
*
|
|
16
|
+
* `sessionCaps` is getXenonCapabilities' output, where a field the session
|
|
17
|
+
* didn't set is `undefined`.
|
|
18
|
+
*/
|
|
19
|
+
function resolveInterceptorOptions(sessionCaps, server) {
|
|
20
|
+
var _a;
|
|
21
|
+
const own = (key) => sessionCaps[key];
|
|
22
|
+
const enabled = own(XenonCapabilityManager_1.XENON_CAPABILITIES.INTERCEPTOR_ENABLED);
|
|
23
|
+
const bufferSize = own(XenonCapabilityManager_1.XENON_CAPABILITIES.INTERCEPTOR_BUFFER_SIZE);
|
|
24
|
+
const captureBodies = own(XenonCapabilityManager_1.XENON_CAPABILITIES.INTERCEPTOR_CAPTURE_BODIES);
|
|
25
|
+
return {
|
|
26
|
+
enabled: typeof enabled === 'boolean' ? enabled : (server === null || server === void 0 ? void 0 : server.enabled) === true,
|
|
27
|
+
bufferSize: (_a = positive(bufferSize)) !== null && _a !== void 0 ? _a : positive(server === null || server === void 0 ? void 0 : server.bufferSize),
|
|
28
|
+
captureBodies: typeof captureBodies === 'boolean'
|
|
29
|
+
? captureBodies
|
|
30
|
+
: typeof (server === null || server === void 0 ? void 0 : server.captureBodies) === 'boolean'
|
|
31
|
+
? server.captureBodies
|
|
32
|
+
: true,
|
|
33
|
+
mocks: listOr(own(XenonCapabilityManager_1.XENON_CAPABILITIES.INTERCEPTOR_MOCKS)),
|
|
34
|
+
includeHosts: listOr(own(XenonCapabilityManager_1.XENON_CAPABILITIES.INTERCEPTOR_INCLUDE_HOSTS)),
|
|
35
|
+
excludeHosts: listOr(own(XenonCapabilityManager_1.XENON_CAPABILITIES.INTERCEPTOR_EXCLUDE_HOSTS)),
|
|
36
|
+
};
|
|
37
|
+
}
|
|
38
|
+
function positive(value) {
|
|
39
|
+
const n = typeof value === 'string' && value.trim() !== '' ? Number(value) : value;
|
|
40
|
+
return typeof n === 'number' && Number.isFinite(n) && n > 0 ? n : undefined;
|
|
41
|
+
}
|
|
42
|
+
function listOr(value) {
|
|
43
|
+
return Array.isArray(value) ? value : [];
|
|
44
|
+
}
|